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

Рассмотрим этот простой подход, чтобы сделать легкую работу в трех разных режимах: включении, выключении и мигании:

attachInterrupt(buttonVector, switchMode, FALLING);

void loop() {
  switch(mode) {
    case 0: turnOff(); break;
    case 1: turnOn(); break;
    case 2: blinkOnce(); break;
  }
}

теперь, если switchMode выглядит примерно так:

unsigned long last_interrupt_time = 0;
unsigned long interrupt_time = 0;
void switchMode()
{
  interrupt_time = millis();
  // debounce measures
  if (interrupt_time - last_interrupt_time > 200) 
  {
    mode = (mode + 1) % 3;
  }
  last_interrupt_time = interrupt_time;
}

затем, как только кнопка будет нажата, режим будет изменен, и если бы мы были в режиме мигания (mode == 2), мы подождем пока не будет мигать (предположим, что это 2 секунды), и только тогда режим фактически переключается в режим «выключения». Теперь, могу ли я остановить все, что было в процессе текущего цикла, сломать его и мгновенно «выключить»?

PS Of'course могут быть некоторые обходные пути, такие как

void switchMode()
{
  interrupt_time = millis();
  // debounce measures
  if (interrupt_time - last_interrupt_time > 200) 
  {
    mode = (mode + 1) % 3;
    if(mode == 0)
      turnOff();
    if(mode == 1)
      turnOn();
  }
  last_interrupt_time = interrupt_time;
}

но есть ли что-то более элегантное и общее? (обратите внимание, что если есть 4 режима: включено, выключено, мигает, blink_slowly, чем это не будет работать)

2 голоса | спросил YakovL 16 J000000Sunday17 2017, 01:12:54

3 ответа


0

Нет, вы не можете. Прерывание не может повлиять на поток существующей процедуры, за исключением передачи данных или состояния ему через переменную.

В многопоточных терминах речь идет о семафоре - общем флаге, который устанавливает один поток или контекст (прерывание), а другой поток или контекст (основной цикл) читает и отвечает к.

Если вы хотите быстро ответить на это, вы должны написать свой основной цикл таким образом, чтобы можно было быстро ответить на него.

ответил Majenko 16 J000000Sunday17 2017, 01:26:00
2

Это возможно. Вы можете использовать setjmp () /longjmp () комбинация, или вы можете просто манипулировать стеком в порядке сборки перед возвратом из прерывания. Это, однако, делает программу очень трудно рассуждать, что может легко привести к внедрению очень сложно найти ошибки. Это, безусловно, not хорошее решение для вашего конкретной проблемы.

Простое решение вашей реальной проблемы - сделать свою петлю без блокировки , и это достигается с помощью методики, описанной в Мигает без задержки Учебник Arduino. Ваш случай немного больше сложный, так как ваш светодиод может быть в трех режимах. Я предлагаю вам рассмотреть вместо этого он может фактически находиться в режимах four , говоря, что, хотя он мигает, состояния ВКЛ и ВЫКЛ - разные режимы:

 static enum { OFF, BLINK_OFF, BLINK_ON, ON } mode;

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

 void switchMode()
{
    // Debounce.
    static uint32_t last_interrupt_time;
    uint32_t interrupt_time = millis();
    if (interrupt_time - last_interrupt_time < DEBOUNCE_TIME)
        return;
    last_interrupt_time = interrupt_time;

    // Switch modes.
    switch (mode) {
        case OFF:  // OFF -> blink
            digitalWrite(LED_PIN, HIGH);
            mode = BLINK_ON;
            break;
        case BLINK_OFF:  // blink -> ON
        case BLINK_ON:   // ditto
            digitalWrite(LED_PIN, HIGH);
            mode = ON;
            break;
        case ON:  // ON -> OFF
            mode = OFF;
            break;
    }
}

И loop() должен иметь дело только с миганием:

 void loop()
{
    // This only has to handle the timing of the blinking.
    uint32_t now = millis();
    static uint32_t last_toggle_time;
    switch (mode) {
        case BLINK_OFF:
            if (now - last_toggle_time > TIME_OFF) {
                digitalWrite(LED_PIN, HIGH);
                mode = BLINK_ON;
                last_toggle_time = now;
            }
            break;
        case BLINK_ON:
            if (now - last_toggle_time > TIME_ON) {
                digitalWrite(LED_PIN, LOW);
                mode = BLINK_OFF;
                last_toggle_time = now;
            }
            break;
        default:
            // Nothing to do in the other modes.
            break;
    }
}

Другой вариант заключается в том, чтобы обработчик прерываний устанавливал только флаг нажмите кнопку. Затем loop() будет заботиться обо всех режимах изменения. Это может быть хорошим вариантом, если вы хотите «конечный автомат» ( управление кодом mode), содержащимся в одной функции.

ответил Edgar Bonet 16 J000000Sunday17 2017, 21:14:40
1
  

, но есть ли что-то более элегантное и общее?

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

самым простым было бы запретить прерывание в начале блока switch /case. «Режим» всегда будет атомарным.

второй будет двойным чтением в «режиме».

  do {
    mode_shadow = mode; //save mode
    switch (mode_shadow) {
      case ...
    }
  } while (mode_shadow != mode); //continue to execute the switch block if mode has changed

это классический случай, обеспечивающий атомарность без запрета прерываний.

третий возможный подход заключается в том, чтобы сделать блок переключателя /случая зависящим от «режима» на каждом этапе выполнения. это часто используется в высоконадежных вычислениях, где вы не уверены, действительно ли часть данных, которую вы читаете, действительно действительна. здесь вы можете обеспечить это двумя способами: 1) утвердительно: оператор выполняется только в том случае, если «режим» соответствует ожиданиям. или 2) исключение: все утверждения не выполняются, как только «режим» не отвечает ожиданиям. Это выведет вас из любой ветви блока переключателя /корпуса. атомальность в этом случае не гарантируется, но это происходит только в течение короткого периода времени.

последнее, что я хотел бы упомянуть, было бы типичным для ОС, где поддерживается «глобальная» переменная, чтобы каждая задача обращалась к этой глобальной переменной, чтобы знать, что происходит. RTOS делает это через сообщения, например. Не так, как в режиме реального времени, как подход № 1 /# 2, но он довольно общий.

ответил dannyf 16 J000000Sunday17 2017, 02:52:38

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132