volatile

Ключевое слово volatile, известное как классификатор, обычно используется перед типом данными переменной, чтобы изменить способ, в котором компилятор и последующая программа рассматривают переменную.

Объявление переменной volatile является директивой для компилятора. Компилятор - это программное обеспечение, которое переводит ваш код C/C++ в машинный код, который является настоящей инструкцией для чипа Atmega в Arduino.

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

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

В Arduino единственное место, где это может произойти, это разделы кода, связанные с прерываниями, называемые подпрограммой обработки прерываний.

Int или long volatile

Если переменная volatile больше байта (например, 16-битное int или 32-битная длина), то микроконтроллер не может прочитать его за один такт, потому что это 8-битный микроконтроллер.

Это означает, что в то время как ваша основная часть кода (например, цикл) считывает первые 8 битов переменной, прерывание может уже изменить вторые 8 битов. Это даст случайные значения для переменной.

Способы устранения

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

1. Функция noInterrupts

Пример кода

// Переключает светодиод, когда вывод прерывания меняет состояние.
int pin = 13;
volatile byte state = LOW;

void setup() {
	pinMode(pin, OUTPUT);
	attachInterrupt(digitalPinToInterrupt(2), blink, CHANGE);
}

void loop() {
	digitalWrite(pin, state);
}

void blink() {
	state = !state;
}

2. Используйте макрос ATOMIC_BLOCK.

Атомарные операции - это одиночные операции MCU - наименьшая возможная единица.

Пример кода

#include <util/atomic.h> // Эта библиотека включает в себя ATOMIC_BLOCK.

volatile int input_from_interrupt;

ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
	// Код с заблокированными прерываниями 
	// (последовательные атомарные операции не прерываются).
	int result = input_from_interrupt;
}