Альтернатива опросу флага прерывания из основного цикла?

Я использую ISR, который написан как можно более минимальным:

volatile bool interrupt1{};

void ISR1() {
    interrupt1 = true;
}

Прерывание обрабатывается путем опроса переменной прерывания1 в loop():

if (interrupt1 == true) {

// Делаем что-нибудь

interrupt1 = false;
}

Несмотря на то, что сигнал прерывания, поступающий на gpio, не опрашивается, описанный выше метод вместо этого опрашивает переменную прерывания из loop().

В качестве альтернативы вышеизложенному, можно ли использовать какую-то логику push-уведомлений на стороне программного обеспечения при каждом возникновении прерывания вместо постоянного опроса переменной прерывания? Я не решаюсь выполнить всю задачу с помощью ISR, поскольку он выполняет много задач.

Какие у меня есть варианты?

, 👍2

Обсуждение

if (interrupt1 == true) true избыточен... вместо этого используйте if (interrupt1), @jsotola

Летучее bool прерывание1 {}; - для чего нужны фигурные скобки?, @Nick Gammon

@NickGammon Это пустой список инициализации с скобками, я думаю, он добавлен вслепую или из-за рекомендаций по исходному коду., @the busybee

@Nick Gammon Инициализация униформы, @Erik

@jsotola Я знаю, что это излишне, но в данном случае я предпочитаю многословие, @Erik

В зависимости от того, что вам нужно сделать, вы можете достичь предела возможностей Arduino, и вам может быть лучше использовать RTOS и потоки (а также очереди, блокировки или мьютексы, чтобы дождаться выполнения условия). На некоторых платформах Arduino построен на базе RTOS, и существуют библиотеки, позволяющие это реализовать. Понятия не имею, относится ли это к Teensy (хотя это, возможно, зависит от модели)., @jcaron


2 ответа


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

10

Если задача довольно короткая и требует много времени, лучший вариант — поместите его в обработчик прерываний.

Если задача большая и не слишком срочная (например: она вполне может подождать миллисекунду или около того), ваш подход к опросу флага внутри loop() лучший, при условии, что основная программа неблокируется.

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

Изменить: для краткого обсуждения подхода к верхней и нижней половине, см. первые абзацы этой статьи о ядре Linux. развитие.

,

Спасибо за ваш ответ. Последняя часть особенно интересна. Возможно, мне удастся реорганизовать обязанности, чтобы найти лучший компромисс между расчетом времени и тяжелой работой, так сказать…, @Erik

Может быть сложно подумать о том, какая часть задачи является «срочной». Обработчик ISR должен позаботиться обо всем, что _пойдёт не так_, если вы подождете (например, обо всем, что необходимо для подготовки оборудования к другому событию, если оно произойдет до того, как вы закончите с этим)., @Glenn Willen


3

Поскольку вы пометили его Teensy: вы также можете уменьшить приоритет прерывания, чтобы его можно было прервать прерываниями с более высоким приоритетом. Таким образом, вы можете реализовать прерывания большей продолжительности, не блокируя важные функции системы.

Вот пример использования таймеров GPT1 и GPT2, работающих с разными приоритетами:

#include "Arduino.h"
#include "TeensyTimerTool.h"

using namespace TeensyTimerTool;

PeriodicTimer loPrioTimer(GPT1);
PeriodicTimer hiPrioTimer(GPT2);

void onLowPrioTimer() // какая-то длинная задача, выполняемая из прерывания с низким приоритетом
{
    Serial.printf("  Start low prio isr @t=%d ms\n", millis());

    for (int i = 0; i < 10; i++)
    {
        Serial.printf("    lowPriority task %d\n",i);
        delay(10);
    }

    Serial.println("  Stop low prio isr");
}

void onHiPrioTimer() // высокий приоритет, прерывает isr с низким приоритетом
{
    Serial.printf("high priority @t=%d ms\n", millis());
}

void setup()
{
    while (!Serial) {}

    loPrioTimer.begin(onLowPrioTimer, 500ms); // GPT1
    hiPrioTimer.begin(onHiPrioTimer, 50ms);   // GPT2

    NVIC_SET_PRIORITY(IRQ_GPT1, 128); // самый низкий приоритет
    NVIC_SET_PRIORITY(IRQ_GPT2, 16);  // высокий приоритет
}

void loop()
{
}

который печатает:

high priority @t=1057 ms
high priority @t=1107 ms
high priority @t=1157 ms
high priority @t=1207 ms
high priority @t=1257 ms
high priority @t=1307 ms
high priority @t=1357 ms
high priority @t=1407 ms
high priority @t=1457 ms
high priority @t=1507 ms
  Start low prio isr @t=1507 ms
    lowPriority task 0
    lowPriority task 1
    lowPriority task 2
    lowPriority task 3
    lowPriority task 4
high priority @t=1557 ms
    lowPriority task 5
    lowPriority task 6
    lowPriority task 7
    lowPriority task 8
    lowPriority task 9
high priority @t=1607 ms
  Stop low prio isr
high priority @t=1657 ms
high priority @t=1707 ms
high priority @t=1757 ms
high priority @t=1807 ms
high priority @t=1857 ms
high priority @t=1907 ms
high priority @t=1957 ms
high priority @t=2007 ms
  Start low prio isr @t=2007 ms
    lowPriority task 0
    lowPriority task 1
    lowPriority task 2
    lowPriority task 3
    lowPriority task 4
high priority @t=2057 ms
    lowPriority task 5
    lowPriority task 6
    lowPriority task 7
    lowPriority task 8
    lowPriority task 9
high priority @t=2107 ms
  Stop low prio isr
high priority @t=2157 ms
high priority @t=2207 ms
high priority @t=2257 ms
high priority @t=2307 ms

Это показывает, что таймер с высоким приоритетом (работающий с интервалом 50 мс) перехватывает ISR с низким приоритетом, который запускается каждые 500 мс в течение примерно 100 мс.

,

Спасибо за ваш ответ. Я никогда не использовал приоритет прерывания. Хотите уточнить?, @Erik

@Эрик добавил пример, @luni64

Очень интересно, спасибо!, @Erik

Что делать, если произойдет прерывание того же типа? Например, если бы он что-то считал, не пропустил бы ли он приращение счета (вероятно, это не реентерабельный ISR)?, @Peter Mortensen

Какая скорость передачи данных использовалась для «Serial»?, @Peter Mortensen

Если прерывание того же типа произойдет во время обслуживания первого, флаг прерывания будет установлен снова, и isr будет вызван снова (если вы не удалите флаг в конце ISR). Однако это не может обрабатывать более одного запроса на прерывание во время работы isr. Вот некоторая информация о системе вложенных прерываний ARM: https://microcontrollerslab.com/nested-vectored-interrupt-controller-nvic-arm-cortex-m/, @luni64

Скорость передачи данных: не существует такого понятия, как скорость передачи данных для «usb-Serial», известного как CDC. Данные всегда будут передаваться с полной пропускной способностью USB. (До 20 МБ/с на T4x). Кроме того, Serial.begin() — это всего лишь функция совместимости. Раньше здесь было пусто. В наши дни он обеспечивает некоторое ожидание кода подключения, я предпочитаю простой while(!Serial){}, @luni64