Почему мой ISR дает невозможные значения?

У меня очень простой ISR:

 const uint32_t PATTERN_A = 0xFF00FF00;
const uint32_t PATTERN_B = 0x00FF00FF;
volatile uint32_t count = 0;    
volatile uint32_t toggle = PATTERN_A;

// My ISR
void callback()
{
    count++;
    toggle = (count % 2) ? PATTERN_A : PATTERN_B;
}

Идея состоит в том, что переменная toggle принимает значения 0xFF00FF00 или 0xFF00FF00 в зависимости от четности count. Это единственное место, где изменяется toggle, но он используется (только для чтения) в эскизе.

Я не знаю почему, но toggle получает всевозможные случайные значения во время выполнения.

Что здесь происходит?

1 голос | спросил user31481 20 FebruaryEurope/MoscowbTue, 20 Feb 2018 13:32:22 +0300000000pmTue, 20 Feb 2018 13:32:22 +030018 2018, 13:32:22

1 ответ


4

Проблема состоит из нескольких причин.

Задания не являются атомарными

Операция atomic - это то, что полностью выполняется или нет. Противоположность - это некоторая операция, которая может быть нарушена во время выполнения, оставляя вещь в несогласованном состоянии.

Вот как count++ работает на уровне машины в Arduino:

void callback()
{
    count++;
  fa:   80 91 0a 02     lds r24, 0x020A
  fe:   90 91 0b 02     lds r25, 0x020B
 102:   a0 91 0c 02     lds r26, 0x020C
 106:   b0 91 0d 02     lds r27, 0x020D
 10a:   01 96           adiw    r24, 0x01   ; 1
 10c:   a1 1d           adc r26, r1
 10e:   b1 1d           adc r27, r1
 110:   80 93 0a 02     sts 0x020A, r24
 114:   90 93 0b 02     sts 0x020B, r25
 118:   a0 93 0c 02     sts 0x020C, r26
 11c:   b0 93 0d 02     sts 0x020D, r27

Первые четыре команды загружают четыре байта count в четыре отдельных регистра, с трудом увеличивают его на единицу, а затем сохраняют обратно в ОЗУ.

Теперь toggle представляет собой переменную, представляющую интерес, и вы хотите использовать ее внутри loop, например:

void loop()
{
    uint32_t x = toggle;
    ...
}

И на уровне машины вы получите

    uint32_t x = toggle;
 414:   60 91 04 01     lds r22, 0x0104
 418:   70 91 05 01     lds r23, 0x0105
 41c:   80 91 06 01     lds r24, 0x0106
 420:   90 91 07 01     lds r25, 0x0107

Здесь мы просто копируем переменную toggle в наш x. Как вы можете видеть, копия требует четырех машинных инструкций для завершения.

Теперь, что произойдет, если прерывание произойдет во время копирования (или использования) toggle?

Прерывание прерывания назначается, перейдите в ISR (callback) и измените toggle. По возвращении копирование продолжается, смешивая байты со старым и новым значением.

Вы должны использовать volatile

Любая глобальная переменная, используемая внутри ISR, должна быть объявлена ​​volatile. Это подсказка для компилятора, что переменная может неожиданно измениться. Поэтому переменная volatile должна всегда считываться заново, отбрасывая любое кешированное значение.

Критический раздел

Правило для использования значений, измененных ISR, прост:

  1. Всегда копируйте глобальные переменные ISR в свою временную переменную.
  2. Перед тем, как справиться, отключите прерывания и снова включите его после завершения копирования. Это предотвратит выполнение ISR, пока вы скопируете значения.

Demo

Эта небольшая демонстрация показывает вам все это в действии.

Эскиз собирает все значения из toggle (не отключая прерывания) в течение одной минуты и после этого печатает таблицу, показывающую, что она получила .

/*
 * This sketch show how to deal with variables shared between ISR and main thread. 
 * 
 * By Look Alterno 2018
 */

#include "TimerOne.h"

volatile uint32_t count = 0;
const uint32_t PATTERN_A = 0xFF00FF00;
const uint32_t PATTERN_B = 0x00FF00FF;

volatile uint32_t toggle = PATTERN_A;

#define MAX_CASES 16
struct {
    unsigned long value;
    int count;
} found[MAX_CASES];
int freeFound = 0;

#define PRINT_INTERVAL 60000L

void setup() {
    Serial.begin(9600);

    while(!Serial);

    Serial.println("");
    Serial.println("Test assignment under interrupts");
    Serial.println("");

    Serial.print("Value A=");
    Serial.println(PATTERN_A, HEX);
    Serial.print("Value B=");
    Serial.println(PATTERN_B, HEX);

    Timer1.initialize(500);
    Timer1.pwm(9, 512);
    Timer1.attachInterrupt(callback);

    pinMode(A0, INPUT);
    randomSeed(analogRead(A0));
}

void callback()
{
    count++;
    toggle = (count % 2) ? PATTERN_A : PATTERN_B;
}

void loop()
{
  static unsigned long prev = 0;  

  //  A random delay to simulate some real, variable work being done.
    delayMicroseconds(random(250));

  // This is the critical section.
//  noInterrupts();
    uint32_t x = toggle;
//  interrupts();

  // Now, let's see what value we got.
    if(x != PATTERN_A && x != PATTERN_B) {
        insert(x);
    }

  // Print values found 
  unsigned long now = millis();
  if (now - prev > PRINT_INTERVAL) {
    printFound();    
    prev = now;
  }
}

/*
 * Add a value to a table for counting.
 */
void insert(unsigned long x)
{
    int index = 0;

    while(index < freeFound && found[index].value != x) {
        index++;
    }

    if(index < MAX_CASES) {
        if(index == freeFound) {
            found[index].value = x;
            found[index].count = 1;
            freeFound++;
      Serial.print(freeFound);
      Serial.print(". Add=");
      Serial.println(x, HEX);
        } else {
            found[index].count++;
        }
    } else {
        Serial.print("*** Table overflow. value=");
        Serial.print(x, HEX);
        Serial.println(" ***");
    }
}

/*
 * Print the table of found values so far.
 */
void printFound() {
  Serial.println("");
  Serial.println("Values found");
  Serial.println("");
  Serial.println("Value - Count");
  for (int i=0; i < freeFound; i++) {
    Serial.print(found[i].value, HEX); Serial.print(" - "); Serial.println(found[i].count); 
  }
}

Если вы раскомментируете noInterrupts и interrupts, все прекрасно работает.

Другой процессор

Более мощные процессоры имеют атомарные назначения, но, следуя этим методам, ваш код переносится на разных платформах, несмотря ни на что. Подумайте о защитном программировании

ответил 20 FebruaryEurope/MoscowbTue, 20 Feb 2018 13:32:22 +0300000000pmTue, 20 Feb 2018 13:32:22 +030018 2018, 13:32:22

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

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

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