asteroid7 писал(а):Не соглашусь с avreal в высказывании (ошибки там нет).
...
И да. Не сочтите за пиар. Компилятор IAR делает в таких местах предупреждение.
GCC тоже
предупреждение, именно это я и имел ввиду, когда говорил, что
ошибки там нет.
Компилятор не знает, есть ли зависимость от порядка чтения этих переменных, как, например, для половинок таймера TCNT1H/TCNT1L. volatile говорит ему только о том, что он а) обязан зачитать, даже если "только что" читал, б) не должен менять порядок обращений. И тут он предупрежадет о том, что
не знает, какой порядок
должен быть, скомпилировал как пришлось, а программист пусть разбирается. Может, порядок и не важен. Точка.
asteroid7 писал(а):Нет синтаксической ошибки. Да. Согласен.
Но! Есть очень гадкая логическая ошибка!
Попытаюсь обосновать:
8-и битный компилятор (это тот, который за "такт/раз" умеет считывать только один байт) используя много байтовые глобальные переменные может считать и обработать на следующем "такте/разе" уже искажённые данные.
...
Выхода два. Считать во временную переменную или запретить все прерывания на момент обработки
Выход один - запретить прерывания. На время обработки или на время считывания во временную переменную — не важно. Второе время меньше, значит, вероятность глюка при незапрещённых прерываниях меньше, значит, глюк не будет отловлен на столе и будет заработана беда на задницу после установки изделия на объект.
Логические ошибки
программиста компилятор вылавливать не обязан. Это раз.
Два — описываемая Вами проблема будет и при обращении к
одной переменной (а не к нескольким, как для обсуждаемого предупреждения). И компилятор даже предупреждения не выдаст. Компилятор не знает — запрещены ли прерывания (например, ещё в функции уровнем выше) вообще, запрещено ли именно то прерывание, которое может помешать и т.д. и т.п. И вообще он не знает, что данный volatile зависит именно от прерывания. Может, то аппаратный регистр. Вон для упомянутых выше половинок TCNT аппаратура обеспечивает защёлкивание двухбайтовой переменной, нужно только в правильном порядке читать (там для не-xmega опять может вылезть беда, если прерывание обращается к другому словному регистру AVR, но это, опять-таки, компилятору не ведомо).
Проблема считывания/записи "многобайтных" переменных (проблема критических секций) это нечто параллельное и независимое. И не специфически-8-битное, так как и у 32-битного процессора бывают 64-битные переменные, и вообще нужным значением может быть структура из нескольких полей, когерентность которых важна. Описываемая бяка будет и с uint8_t;
Код: Выделить всё
volatile uint8_t a;
volatile uint8_t b;
void wait_ab() { while( a != b ) ; }
будет то же самое предупреждение о порядке считывание volatile-переменных. Уйти от него можно при помощи
Код: Выделить всё
volatile uint8_t a;
volatile uint8_t b;
void wait_ab()
{
uint8_t temp;
do {
temp = a;
} while( temp != b);
}
Критические секции, ATOMIC_BLOCKS() и тому подобное - это само собой, volatile не спасает от проблем многоциклового доступа. Для типов, которые не читаются/не пишутся за один раз, атомарность надо обеспечивать вручную. Кстати, поэтому во многих компиляторах в signal.h определён тип sig_atomic_t, это максимальный размер целого, для которого гарантируется атомарное обращение.
Лень в виде мании величия: «ты гений, зачем стараться?». В виде комплекса: «всё равно не выйдет, зачем упираться?». Как логика: «если достаточно, зачем знать и уметь больше?». Цель одна: остановить. Не любит тепло работающих мышц и шум работающего мозга.