Альтернатива опросу флага прерывания из основного цикла?
Я использую ISR, который написан как можно более минимальным:
volatile bool interrupt1{};
void ISR1() {
interrupt1 = true;
}
Прерывание обрабатывается путем опроса переменной прерывания1 в loop()
:
if (interrupt1 == true) {
// Делаем что-нибудь
interrupt1 = false;
}
Несмотря на то, что сигнал прерывания, поступающий на gpio, не опрашивается, описанный выше метод вместо этого опрашивает переменную прерывания из loop()
.
В качестве альтернативы вышеизложенному, можно ли использовать какую-то логику push-уведомлений на стороне программного обеспечения при каждом возникновении прерывания вместо постоянного опроса переменной прерывания? Я не решаюсь выполнить всю задачу с помощью ISR, поскольку он выполняет много задач.
Какие у меня есть варианты?
@Erik, 👍2
Обсуждение2 ответа
Лучший ответ:
Если задача довольно короткая и требует много времени, лучший вариант — поместите его в обработчик прерываний.
Если задача большая и не слишком срочная (например: она вполне может подождать
миллисекунду или около того), ваш подход к опросу флага внутри loop()
лучший, при условии, что основная программа неблокируется.
Если задача большая и срочная... возможно, у вас возникла трудная проблема. Вы можете попробовать оба подхода и посмотреть, какой из них работает лучше всего. Или вы можете попробуйте разделить задачу на верхнюю половину (короткую и срочную) и нижнюю половина (долго, но не так срочно). Обработчик прерывания затем выполнит верхнюю половину и установить флаг, тогда как основной цикл опрашивает флаг и выполните нижнюю половину.
Изменить: для краткого обсуждения подхода к верхней и нижней половине, см. первые абзацы этой статьи о ядре Linux. развитие.
Спасибо за ваш ответ. Последняя часть особенно интересна. Возможно, мне удастся реорганизовать обязанности, чтобы найти лучший компромисс между расчетом времени и тяжелой работой, так сказать…, @Erik
Может быть сложно подумать о том, какая часть задачи является «срочной». Обработчик ISR должен позаботиться обо всем, что _пойдёт не так_, если вы подождете (например, обо всем, что необходимо для подготовки оборудования к другому событию, если оно произойдет до того, как вы закончите с этим)., @Glenn Willen
Поскольку вы пометили его 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
- Почему необходимо использовать ключевое слово volatile для глобальных переменных при обработке прерываний в ардуино?
- Какие Arduino поддерживают ATOMIC_BLOCK?
- C++ Undefined reference to 'Class:Function()'
- Прерывания внутри класса, связанные с функцией класса
- Быстрее TimerOne с Teensy 4.0 (600 МГц)
- Невозможно отобразить строку chr с помощью Wire.read() и u8g2.drawStr().
- Передать шаблонную функцию в качестве параметра attachInterrupt
- Используйте ISR внутри библиотеки более элегантно
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