Запуск функции аппаратного прерывания на NodeMCU ESP8266, исключение (0) после четырех итераций, невозможно отключить функцию прерывания
Я пытался проверить стабильность и согласованность аппаратных прерываний на моем NodeMCU ESP8266. Я использую Arduino IDE. Этот скетч не должен требовать каких-либо внешних схем, и я думаю, но не уверен, что проблема в каком-то переполнении значения. ВОПРОС 1. Кто-нибудь может указать, где проблема, вызывающая исключение (0)?
Функции hw_timer_* описаны в справочном документе ESP8266 NONOS SDK в формате pdf. . Объявления hw_timer_* не были найдены средой разработки Arduino IDE после того, как я настроил ее с помощью URL менеджера платы здесь, поэтому Я скопировал hw_timer.h и hw_timer.c из репозитория Expressif github в ту же папку, что и мой файл .ino. Функции прерывания запускаются сейчас, но после четырех итераций я получаю исключение & дамп стека, а затем плата перезагружается с этим первым сообщением:
ets Jan 8 2013,rst cause:4, boot mode:(3,6)
Код моего скетча здесь. Вам не нужно никаких булавок или чего-то еще.
EDIT: я добавил volatile
перед тремя переменными, на которые ссылаются в прерывании и в других fns.
#include "user_interface.h";
#include "hw_timer.h";
#include "hw_timer.c";
// интервал прерывания, используемый для моего hw_timer
#define timerInterval 100
// количество отсчетов циклов, которые мы собираем перед анализом
#define totalSamples 500
volatile uint32_t cycleSamples[totalSamples];
volatile uint16_t cycleSampleCount = 0;
// вычислить мин./макс./среднее
uint32_t elapsedCycles; // количество циклов, прошедших между двумя выборками
float elapsedSum = 0; // циклы - это большие числа... на случай, если мы превысим 2^32, мы используем число с плавающей запятой
uint32_t elapsedMax = 0;
uint32_t elapsedMin = 0xFFFFFFFF;
uint32_t elapsedCount = 0;
float elapsedMean;
// для расчета стандартного отклонения
float diffSumSquared = 0;
float variance = 0;
float stdDev = 0;
// устанавливается в true таймером fn, когда он собрал образцы
volatile bool analyzeSamples = false;
// Функция обратного вызова аппаратного таймера НЕ ДОЛЖНА быть определена с помощью ICACHE_FLASH_ATTR.
// Функции обратного вызова прерывания должны быть в IRAM, потому что флэш-память может быть в середине
// других операций, когда они происходят. Сделайте это, добавив атрибут ICACHE_RAM_ATTR.
// в определении функции
// @см. https://arduino-esp8266.readthedocs.io/en/latest/reference.html#interrupts
ICACHE_RAM_ATTR void my_timer_callback(void) {
if (analyzeSamples) {
// если эта fn собрала все образцы, мы должны пропустить это, пока она не закончит их анализ
// и сбрасывает это
return;
}
if (cycleSampleCount >= totalSamples) {
// мы не можем отключить этот таймер
analyzeSamples = true;
return;
}
// взглянем на счетчик циклов, чтобы проверить точность синхронизации
cycleSamples[cycleSampleCount] = ESP.getCycleCount();
cycleSampleCount++;
}
void setup() {
Serial.begin(115200);
for(uint8_t i=5; i>0; i--) {
Serial.printf("counting %d\n", i);
delay(1000);
}
// ЗАПУСКАЕМ ТАЙМЕР ВЫБОРКИ, ИСПОЛЬЗУЯ АППАРАТНЫЕ ПРЕРЫВАНИЯ
// использовать NMI_SOURCE для наивысшего приоритета и самого быстрого/самого точного времени
hw_timer_init(NMI_SOURCE, 1);
hw_timer_set_func(my_timer_callback);
// Если в качестве источника ISR для автозагрузки таймера используется NM, параметр val hw_timer_arm не может быть меньше 100
hw_timer_arm(timerInterval);
}
void loop() {
// TODO нам нужно сделать что-то недетерминированное здесь, чтобы увидеть, не испортит ли это время прерываний
if (analyzeSamples) {
// это не работает, чтобы попытаться отключить прерывание fn
// hw_timer_set_func(0);
// это тоже не
//hw_timer_arm(0);
Serial.println("Sampling complete, analyzing...");
elapsedSum = 0; // циклы - это большие числа... на случай, если мы превысим 2^32, мы используем число с плавающей запятой
elapsedMax = 0;
elapsedMin = 0xFFFFFFFF;
elapsedCount = 0;
// зацикливаемся с 1-го (т.е. второго) элемента
for(uint32_t i = 1; i<totalSamples; i++) {
elapsedCycles = cycleSamples[i] - cycleSamples[i-1];
Serial.printf("%d %d\n", i, elapsedCycles);
elapsedSum += (float)elapsedCycles;
elapsedMax = max(elapsedMax, elapsedCycles);
elapsedMin = min(elapsedMin, elapsedCycles);
elapsedCount++;
}
// вычислить среднее значение
elapsedMean = elapsedSum / (float)elapsedCount;
Serial.print("Average elapsed cycles: ");
Serial.println(elapsedMean);
// сообщить мин/макс
Serial.print("Elapsed cycles max: ");
Serial.println(elapsedMax);
Serial.print("Elapsed cycles min: ");
Serial.println(elapsedMin);
// рассчитать дисперсию/стандартное отклонение
diffSumSquared = 0;
for(uint32_t i = 1; i<totalSamples; i++) {
elapsedCycles = cycleSamples[i] - cycleSamples[i-1];
diffSumSquared += sq(elapsedCycles - elapsedMean);
}
variance = diffSumSquared / elapsedCount;
Serial.print("Variance: ");
Serial.println(variance);
stdDev = sqrt(variance);
Serial.print("Std Dev: ");
Serial.println(stdDev);
// сбрасываем сэмплы на ноль и пробуем снова
Serial.println("Gonna reset...");
ets_intr_lock( ); // закрыть прерывание
for(uint32_t i = 0; i<totalSamples; i++) {
cycleSamples[i] = 0;
}
cycleSampleCount = 0;
analyzeSamples = false;
interrupts();
ets_intr_unlock(); // открытое прерывание
system_soft_wdt_restart();
Serial.println("Reset complete.");
// стопСтопСтоп = true;
} else {
Serial.println("not yet!");
}
Serial.println("sleeping 1...");
delay(1000); // спать 1000 мс
}
Это подводит меня к ВОПРОСУ 2: Как снять с охраны/отменить hw_timer? Как вы можете видеть в этом коде, я пытался установить FN на ноль и/или ноль, и я я также пытался установить hw_timer_arm(0).
Похоже, что исключение и дамп стека постоянно происходят после отладочного сообщения sleep 1...
, которое соответствует концу функции loop(). Некоторые типичные результаты отладки:
// полезный и удачный вывод выше вплоть до этого момента
497 8000
498 8000
499 8000
Average elapsed cycles: 8000.00
Elapsed cycles max: 8263
Elapsed cycles min: 7738
Variance: 396.13
Std Dev: 19.90
Gonna reset...
Reset complete.
sleeping 1...
Exception (0):
epc1=0x40201030 epc2=0x00000000 epc3=0x400043a3 excvaddr=0x00000000 depc=0x00000000
>>>stack>>>
ctx: sys
sp: 3ffedf20 end: 3fffffb0 offset: 0190
3ffee0b0: 00000000 00000000 00000000 4010480d
3ffee0c0: 40105513 00000000 00000000 00000000
3ffee0d0: 00000010 00000000 00000000 40000f68
3ffee0e0: 00000004 00000000 40000f58 00000000
3ffee0f0: 60000200 3fffeed0 00000002 7c097c08
// гигантский дамп стека продолжается....
Мы будем очень признательны за любую помощь.
@S. Imp, 👍0
Обсуждение1 ответ
Лучший ответ:
Итак, эти документы говорят, что причина RST 4 является сторожевым таймером сброс. Очевидно, в arduino есть сторожевой таймер, чтобы проверить и убедиться, что ваша программа не работает. т застрял в бесконечном цикле. В этой ссылке есть несколько советов о том, как избежать этого сброса. Вообще говоря, вам нужно убедиться, что ваш цикл не тратит слишком много времени на выполнение какой-либо одной операции или обработку слишком большого количества прерываний, иначе он может начать перегружаться, и тогда код Arduino не сможет справиться со всеми домашними делами, которые он должен выполнять. сделать.
Что касается отключения функции аппаратного таймера, я опубликовал проблему в репозитории ESP8266 на github, и кто-то предложил что выглядит как полезный ответ, который я не тестировал. Они предлагают:
Вызов hw_timer_init со вторым аргументом = 0; затем в вашем ISR вызовите hw_timer_arm(), если вы хотите, чтобы он продолжал работать, и не вызывайте его, если хотите «снять с охраны».
- Разница между этими двумя платами NodeMCU?
- Как определить размер Flash?
- В ESP-12E NodeMCU, какой выход PIN A0?
- Esp8266 Vin контакт
- Преобразование byte* в int в Arduino
- Каково использование зарезервированных контактов и контактов SDD2, SDD3 NodeMCU?
- NodeMCU (Arduino IDE) «DynamicJsonBuffer» не был объявлен в этой области
- NodeMCU поддерживает внедрение ключей?
Я не рассматривал ваш код подробно, но вы должны объявить любую глобальную переменную, которую можно изменить внутри ISR, как
volatile
, чего вы не сделали., @StarCat@StarCat спасибо за это. Я добавил
volatile
перед каждой из трех переменных, упомянутых как в прерывании fn, так и в других fns. Он по-прежнему падает и сбрасывает стек примерно после четырех итераций., @S. Imp