attachInterrupt VS analogReadMilliVolts

Модуль ESP32-S3


Обвязка модуля:

Считаю герцы так, всё работает отлично (attachInterrupt висит на 11 и 12 пинах)

void IRAM_ATTR ISR(){
   HZ = (1000000.0/(micros()-TIME_TIK)); // измеряем частоту
   TIME_TIK = micros();
}
void HERTZ(TFT_eSprite *MAIN, char PARAMETER)
{
  if (PARAMETER == 'L')
  {
    pinMode(45, INPUT);         // 45 пин высокий импеданс
    pinMode(46, OUTPUT);        // выводим на 46 пин 0
    digitalWrite(46,0);         // устанавливая его в 0(включаем нижний резистор делителя)
    digitalWrite(45,0);
    attachInterrupt(12, ISR, FALLING);
  } 
  else if (PARAMETER == 'R')
  {
    pinMode(46, INPUT);         // 45 пин высокий импеданс
    pinMode(45, OUTPUT);        // выводим на 46 пин 0
    digitalWrite(46,0);
    digitalWrite(45,0);         // устанавливая его в 0(включаем нижний резистор делителя)
    attachInterrupt(11, ISR, FALLING);
  }
  Serial.println(HZ);
}

пока я не вызываю функцию GET_VOLT на эти пины 11 и 12:

float GET_VOLT(uint8_t PIN)
{
  int32_t VOLTS = 0;
  for(uint8_t i = 0; i <= 4; ++i)
    VOLTS += analogReadMilliVolts(PIN);
  return VOLTS / 5000.0f;
}

void OHMETER(TFT_eSprite *MAIN)
{
  pinMode(46, OUTPUT);        // пин выход
  pinMode(45, OUTPUT);        // пин выход
  digitalWrite(46,1);
  digitalWrite(45,0); 
  
  // измеряем напряжение на верхнем конце делителя
  //**************************************************************
  float R1 =  GET_VOLT(10);
  // измеряем напряжение на верхнем щупе
  //**************************************************************
  float R2 =  GET_VOLT(12);
  // измеряем напряжение на нижнем щупе
  //**************************************************************
  float R3 =  GET_VOLT(11);
  Serial.print(R1);
  Serial.print(R2);
  Serial.println(R3);
}

После этого прерывание attachInterrupt перестаёт работать.

Если в коде закомментировать

float R2 =  GET_VOLT(12);
float R3 =  GET_VOLT(11);

эти строчки, то attachInterrupt работает исправно.

Пояснение: функции HERTZ и OHMETER одновременно не вызываются, по очереди, сначала HERTZ, потом OHMETER потом снова HERTZ и после этого attachInterrupt перестаёт работать.

А если перед вызовом VOLT/HERZ , делать detach, после снова attach ?

Пробовал, тоже самое:

detachInterrupt(11);
detachInterrupt(12);

Вообще attachInterrupt перестаёт работать, как только затрагиваешь её пины.

pinMode(11, INPUT);   
pinMode(12, INPUT);

например, такой код тоже заставит перестать работать attachInterrupt,
несмотря на его повторный вызов.

attachInterrupt(11, ISR, FALLING);
attachInterrupt(12, ISR, FALLING);

Видимо пока работа идет с пинами на них «нельзя повесить» прерывание.
Попробуй как в блинк без делей сделать задержку на время работы ацп.

Не понял, что Вы имеете в виду?

Я сам не сильно во всех «тонкостях» разбираюсь, но на сколько знаю - преобразования в ацп проходят далеко не за один такт. Иными словами - Вы запускаете преобразование ацп, оно запускается и начинает выполняться. В это время Вы пытаетесь перевести пин на считывание прерывания, но преобразование то еще не закончено - вот пин и не переводится, и в тоже время в следующем цикле Вы опять запускаете преобразование ацп. Я не знаю как сам ацп реагирует на повторный запуск ацп (а может быть оно только закончилось - кто его знает?), но тем не менее - на выходе получаются данные только с ацп, так и не успев перевести пин на чтение прерывания.

Я далеко не писатель романтических романов, надеюсь доходчиво объяснил свои мысли?

Не знаком я с esp32, пытался найти даташит или какую информацию - но не нашел. Попробуй с задержки в 10-30 мс, думаю должно помочь.

Если я Вас правильно понял, то добавил паузу на 200 мс между вызовами так:

void loop()
{ 
  if (millis() - TIME <= 200)   
    return;
  TIME = millis(); 

  if (SELECT_DISPLAY == 0)
     VOLT(&MAIN, 'L');
  if (SELECT_DISPLAY == 1)
     VOLT(&MAIN, 'R');
  if (SELECT_DISPLAY == 2)
     OHMETER(&MAIN);
}

SELECT_DISPLAY изменяет значение от нажатия кнопки. Результатов нет.

Я прочитал много статей по поводу attachInterrupt и в примерах, attachInterrupt везде указывается в setup что меня наводит на мысль, возможно после назначения пинов под прерывания, больше не подразумевается их использование под другие задачи.

Интересно мыслишь…
Подкину дровишек для раздумья:
Зачем тогда нужен detach ?

А что, это противоречит высказыванию выше?
hint: после “detach” это уже не будет “после назначения пинов под прерывания”.

У меня нет ESP32S
Проверил на обычной, на тестовом скетче проблем не увидел.
После AnalogRead (MilliVolts) прерывание не работает, но после очередного pinMode(BTN_PIN, INPUT); снова начинает работать.

Тестовый скетч ОБЫЧНАЯ ESP32
volatile uint8_t isrFlag = 0;
static constexpr byte BTN_PIN = 32; // Подтянутый через внешний R к 3,3 В (При нажатии замыкается на землю)

void IRAM_ATTR btnISR(){
   isrFlag = 1;
}

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  Serial.println("Hello, ESP32!");
  pinMode(BTN_PIN, INPUT);
  attachInterrupt(BTN_PIN, btnISR, FALLING);
}

void loop() {
  static uint16_t btnCnt = 0;
  static char mode = 'i'; // 'i' 'o' 'a' 'm' Вход Выход Аналог Аналог мВ

  // Пееключение режимов 'i' 'o' 'a' 'm' Вход Выход Аналог Аналог мВ
  if(Serial.available()){
    byte ser = Serial.read();
    if(ser == 'i'){
      pinMode(BTN_PIN, INPUT);
      mode = 'i';
      Serial.print("Mode = "); Serial.println(mode);
    }
    if(ser == 'o'){
      pinMode(BTN_PIN, OUTPUT);
      mode = 'o';
      Serial.print("Mode = "); Serial.println(mode);
    }
    if(ser == 'a'){
      mode = 'a';
      Serial.print("Mode = "); Serial.println(mode);
    }
    if(ser == 'm'){
      mode = 'm';
      Serial.print("Mode = "); Serial.println(mode);
    }    
  }

  // Проверка, что работает analogRead
  if (mode == 'a'){
    int a = analogRead(BTN_PIN);
    Serial.print("A = "); Serial.println(a);
  }

  if (mode == 'm'){
    int a = analogReadMilliVolts(BTN_PIN);
    Serial.print("M = "); Serial.println(a);
  }  
  
  // Проверка, что работает кнопка digitalRead
  if (mode == 'i' && digitalRead(BTN_PIN) == LOW)
    Serial.println(btnCnt++);
  
  // Проверка, что работает прерывание
  if(isrFlag != 0){
    isrFlag = 0;
    Serial.println("ISR");
  }

  delay(500);
}

Спасибо за пример, понял свою ошибку:

M = 0
ISR
M = 0
ISR
M = 0
ISR
M = 0
ISR
M = 0

Как видим из лога, срабатывает вначале analogReadMilliVolts а потом ISR из-за чего между срабатываем ISR проходит “большое время” и герцы не считаются.
для подсчёта герц нужно хотя бы так:

M = 0
ISR
ISR
M = 0
ISR
ISR
M = 0
ISR
ISR
M = 0
ISR
ISR
M = 0