Как управлять двумя тревогами с помощью одного прерывания?

У меня есть регистратор данных, использующий RTC DS3231 в качестве генератора прерываний. Я могу заставить работать прерывания, но вот моя проблема. Я хотел бы принять 2 отдельных измерения от 2 разных датчиков (один для влажности, температуры и другой для ускорений), один каждые 30 минут и один каждый час, дело в том, что, поскольку я использую один и тот же контакт (только один контакт от DS3231) для обоих этих аварийных сигналов я не могу использовать простой счетчик в своей функции прерывания, иначе одна мера не будет принята, когда сработают оба аварийных сигнала...

Например, я только что провел быстрый тест с одним сигналом тревоги каждую минуту, когда секунды равны 00, и другим сигналом тревоги каждую минуту, когда секунды равны 20. Проблема, как я уже сказал, заключается в том, что я не могу увеличивать счетчики для каждого, поскольку я использую одно и то же прерывание. функция (из-за одного контакта). Вот моя функция цикла и функция обработки прерываний:

void loop() {
  delay(1000);
  sleepNow();
  if (alarm == true){
    handleAlarm(); // сброс возможности срабатывания тревоги
    if (counter_dtphl == COUNT_DTPHL){
      take_measure_DTPHL();
      counter_dtphl = 0;
    }
    else if (counter_accel == COUNT_ACCEL){
      take_measure_ACCEL();
      counter_accel = 0;
    }
  }
}

void wakeUpNow() {  
  Serial.println("In wake up now");
  delay(100);
  alarm = true;
  //Serial.println(счетчик);
  sleep_disable();//Отключаем спящий режим
  byte sec = get_date(SEC);
  Serial.println(sec);
  if (sec == 0){
  counter_dtphl +=1;
  }
  else if (sec == 30){
  counter_accel +=1;
  }
  detachInterrupt(digitalPinToInterrupt(interruptPin)); //Удаляет прерывание от контакта 2;
}

void handleAlarm() {
  alarm = false; 
  rtcObject.LatchAlarmsTriggeredFlags();
}

COUNT_ACCEL и COUNT_DTPHL — константы, имеющие значение в минутах, соответствующее триггеру меры.

Кто-нибудь знает, как справиться с этой ситуацией? Для ускорения мне нужно сохранить данные в течение 30 секунд. Одно из решений, которое я пробовал, — увеличивать счетчик в соответствии со временем в секундах, которое соответствует каждой минуте, но я не могу поместить его в функцию wakeUpNow, поскольку там должна выполняться только быстрая обработка (это функция, выполняемая прерыванием).

Спасибо за ваше время!

, 👍1

Обсуждение

что еще делает скетч? зачем вам RTC в качестве таймера? вы можете делать что-то каждые 30 минут с помощью millis(), @Juraj

зачем вам два прерывания для действия, которое происходит каждые 30 минут?, @jsotola

Здравствуйте, мне нужен RTC в качестве таймера, потому что регистратор данных будет встроенной системой, а потребление является ключевым фактором для этого проекта. Таким образом, я не могу позволить ему работать с помощью простого millis(), потому что я все время выключаю карту и пробуждаю ее с помощью аварийного прерывания DS3231., @JamesONeil


4 ответа


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

3

Учтите, что рекомендуется оставлять в прерывании как можно меньше кода. В этом случае, поскольку рассматриваемое время намного больше, чем частота вызова функции loop(), будет достаточно установить один флаг, указывающий, что произошло прерывание.

Каждый раз в вашей функции loop() проверяется этот флаг. Если TRUE, сразу установите его в FALSE, чтобы не забыть сделать это позже. Затем, оставаясь в рамках оператора IF, предполагая, что RTC DS3231 настроен на создание прерывания каждые 30 минут, возьмите «30-минутный образец». Затем, все еще находясь в области действия оператора IF, увеличьте значение счетчика. Убедитесь, что этот счетчик определен как глобальная переменная, чтобы он сохранялся между вызовами функции loop(). Если этот счетчик достиг значения 2, сбросьте этот счетчик на ноль и возьмите "60-минутный образец".

Рассмотрите следующий псевдокод:

// Глобальная переменная, определенная вне функций для отслеживания состояния & время.
bool state_interrupts;
int counter_interrupts;

void setup()
{
  // Инициализируем RTC для генерации прерывания каждые 30 минут.
  <RTC initialization code.>
  // Вызываем прерывание присоединения, чтобы идентифицировать имя функции прерывания "wakeUpNow".
  attachInterrupt(...)
  // Инициализировать состояние & прилавок;
  interrupts_state = false;
  interrupts_counter = 0; 
}

void loop()
{
  // Проверяем, произошло ли прерывание.
  if(interrupts_state)
  {
    interrupts_state = false;
    interrupts_counter++;
    // Образец 30-минутного датчика.
    <Sample 30 minute sensor code.>
    if(interrupts_counter >= 2)
    {
      interrupts_counter = 0;
      // Образец 60-минутного датчика.
      <Sample 60 minute sensor code.>
    }
  }
}

void wakeUpNow()
{
    interrupts_state = true;
}
,

Привет и спасибо за ваш ответ. Я уже пробовал то, что вы говорите, но это не работает для моих нужд. Мне нужно 2 разных измерения от 2 разных датчиков. Один раз в 30 минут, другой каждый час. Дело в том, что каждый час между звонками будет конфликт, так как 30 мин это полчаса. Поэтому я не могу использовать счетчик таким образом, @JamesONeil

@JamesONeil, ты уверен, что не слишком об этом думаешь? Или есть ограничение проблемы, о котором вы нам не сказали? Да, 2 разных измерения от 2 датчиков. Прерывание НИЧЕГО НЕ ПРОБИРАЕТ!!! Прерывание только устанавливает флаг, указывающий, что прошло 30 минут. ВАШ КОД в функции Loop должен отслеживать количество прерываний. Таким образом, каждый раз, когда устанавливается флаг, он производит выборку 30-минутного датчика, а каждый раз также производит выборку 60-минутного датчика. ПОВТОРЯЮ, ПРЕРЫВАНИЕ НЕ ОБРАБАТЫВАЕТ НИКАКИЕ ДАТЧИКИ!!! Хорошей практикой является делать как можно меньше прерываний!, @st2000

@JamesONeil, поскольку вы приложили усилия для редактирования / улучшения своего вопроса, я добавил псевдокод в свой ответ, чтобы упростить понимание предлагаемого решения., @st2000

Привет ! Извините за поздний ответ, мне удалось заставить его работать так, как я хотел :) Спасибо за ваш псевдокод, это также хороший способ решить мою проблему!, @JamesONeil

«Инициализируйте RTC, чтобы генерировать прерывание каждые 30 минут». не похоже, что это вариант из таблицы битов «A2M2» в https://datasheets.maximintegrated.com/en/ds/DS3231.pdf — похоже, что вы можете запускать только каждую минуту или когда минуты совпадают конкретное число. Если вы выберете «30», вы пропустите «00» и наоборот. Вы можете запускать каждую минуту и считать минуты interruptState = (++elapsedMinutes % 30 == 0);, или читать минуты и действовать соответственно., @Dave X


1

После прерывания прочтите время из RTC, а затем выполните правильные действия в зависимости от результата.

,

2

Типичное решение проблемы такого типа:

  1. В ISR установите флаг, указывающий, что произошло прерывание RTC.
  2. В цикле проверьте флаг и, если он установлен: а) Считать тревожные флаги с RTC, б) Проверьте сигналы тревоги, верните значение из LatchAlarmsTriggeredFlags() и выполните соответствующее действие.

Чего не следует делать, так это писать много кода в ISR. Особенно код, который также управляется прерываниями или зависит от прерываний, таких как Wire, Serial, millis(), delay() и т. д.

Ура!

,

0

Попробуйте это: логический х = истина; Это до настройки логическое выполнено = ложь;

х = !х; Сделайте это в функции прерывания. сделано = верно; это в твоем цикле! if done == true{ // Определяем, были ли вы здесь раньше если X !true выполнить обе функции еще сделай 30 секунд Я использую !X, так как это быстрее. сделано = ложь;} Это должно дать вам представление, я на самом деле не кодировал это, но я все время использую код, подобный этому. Моя цель — сделать не более 1-2 инструкций в подпрограмме обслуживания прерываний (ISR).

,