Запуск функции аппаратного прерывания на 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
// гигантский дамп стека продолжается....

Мы будем очень признательны за любую помощь.

, 👍0

Обсуждение

Я не рассматривал ваш код подробно, но вы должны объявить любую глобальную переменную, которую можно изменить внутри ISR, как volatile, чего вы не сделали., @StarCat

@StarCat спасибо за это. Я добавил volatile перед каждой из трех переменных, упомянутых как в прерывании fn, так и в других fns. Он по-прежнему падает и сбрасывает стек примерно после четырех итераций., @S. Imp


1 ответ


Лучший ответ:

1

Итак, эти документы говорят, что причина RST 4 является сторожевым таймером сброс. Очевидно, в arduino есть сторожевой таймер, чтобы проверить и убедиться, что ваша программа не работает. т застрял в бесконечном цикле. В этой ссылке есть несколько советов о том, как избежать этого сброса. Вообще говоря, вам нужно убедиться, что ваш цикл не тратит слишком много времени на выполнение какой-либо одной операции или обработку слишком большого количества прерываний, иначе он может начать перегружаться, и тогда код Arduino не сможет справиться со всеми домашними делами, которые он должен выполнять. сделать.

Что касается отключения функции аппаратного таймера, я опубликовал проблему в репозитории ESP8266 на github, и кто-то предложил что выглядит как полезный ответ, который я не тестировал. Они предлагают:

Вызов hw_timer_init со вторым аргументом = 0; затем в вашем ISR вызовите hw_timer_arm(), если вы хотите, чтобы он продолжал работать, и не вызывайте его, если хотите «снять с охраны».

,