|
Обычная переменная для ISR и volatile для всех остальных, как лучше реализовать? |
|
|
|
Jan 24 2009, 15:32
|

Любитель
    
Группа: Свой
Сообщений: 1 864
Регистрация: 20-08-06
Из: Тольятти
Пользователь №: 19 695

|
Что-то вот задумался над одной мелочью, в принципе. Есть переменная таймера, которая объявлена как volatile, так как модифицируется в обработчике прерывания. Всё бы хорошо, да только хотелось бы, чтобы эта переменная была volatile для всех, кроме самого обработчика прерываний, так как лишние сохранения/загрузки в нём - это совершенно лишнее.  Есть мысля сделать так: Код union { dword isr_timer; volatile dword timer; } и в ISR юзать isr_timer, а в других местах - просто timer. Что скажете?
|
|
|
|
3 страниц
1 2 3 >
|
 |
Ответов
(1 - 38)
|
Jan 24 2009, 16:43
|
Участник

Группа: Свой
Сообщений: 46
Регистрация: 4-02-05
Из: Москва
Пользователь №: 2 430

|
ISR сделай в отдельном файле и используй как dword timer;
а в основном объяви как extern volatile dword timer;
Сообщение отредактировал kimstik - Jan 24 2009, 16:54
|
|
|
|
|
Jan 24 2009, 19:39
|

Гуру
     
Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095

|
Цитата(singlskv @ Jan 24 2009, 20:55)  А я вот чего-то совсем не припоминаю из стандарта почему исходный вариант неправильный ? Он не неправильный. Он несколько... некрасивый. Как вы организуете доступ к этому безымянному unionу из другого файла? Вы вынесете его в заголовочный файл с extern, и при этом сделаете видимым не-volatile пременную. Никакой гарантии, что случайно не используете эту переменную вне прерывания. Если есть возможность железно ограничить область видимости не-volatile переменной обработчиком прерывания, сделав ее локальной - почему не использовать ее? Цитата(sonycman @ Jan 24 2009, 19:47)  А я почему-то подумал, что extern должен быть с точно таким-же типом, что и оригинальная переменная... Думаю, что он может накладывать дополнительные ограничения (volatile, const) на переменную, но не снимать их. Искать в стандарте лень, никогда не использовал такой финт.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Jan 24 2009, 19:51
|

Любитель
    
Группа: Свой
Сообщений: 1 864
Регистрация: 20-08-06
Из: Тольятти
Пользователь №: 19 695

|
Цитата(Сергей Борщ @ Jan 24 2009, 22:33)  Заведите внутри обработчика прерывания локальную переменную, считайте в нее timer и работайте дальше с ней. В конце, если надо, сохраните локальную переменную обратно в timer. Зачем какие-то велосипеды с выносом в другой файл? Этот счётчик необходим для класса таймеров рассредоточенных по всему проекту. Поэтому его вынос обязателен. Последовал совету kimstik. Накладывание доп. ограничений (volatile) на внешнюю переменную для внутренних нужд - это прикольно  Можно ещё и const добавить для подстраховки...
|
|
|
|
|
Jan 24 2009, 19:53
|
дятел
    
Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065

|
Цитата(Сергей Борщ @ Jan 24 2009, 22:39)  Он не неправильный. Он несколько... некрасивый. Как вы организуете доступ к этому безымянному unionу из другого файла? Вы вынесете его в заголовочный файл с extern, и при этом сделаете видимым не-volatile пременную. Никакой гарантии, что случайно не используете эту переменную вне прерывания. Если есть возможность железно ограничить область видимости не-volatile переменной обработчиком прерывания, сделав ее локальной - почему не использовать ее? Безымянный глобальный union это вообще не хорошо, компилятор должен варнинг давать, поэтому конечно нужно дать имя ему. Ну а если уже дали имя и напутали к какой переменной обращаться, то сами себе злобные Буратины... А сам бы я тоже локальную просто завел... чисто по привычке.
|
|
|
|
|
Jan 24 2009, 19:55
|

Любитель
    
Группа: Свой
Сообщений: 1 864
Регистрация: 20-08-06
Из: Тольятти
Пользователь №: 19 695

|
Цитата(singlskv @ Jan 24 2009, 23:53)  Безымянный глобальный union это вообще не хорошо, компилятор должен варнинг давать, поэтому конечно нужно дать имя ему. Компилер заставил поставить static. А имя - зачем оно? Лишняя писанина только... Объединение не должно быть в области видимости снаружи. Оно только для нужд прерывания. А снаружи - только volatile dword... Да ладно, это не актуально более
|
|
|
|
|
Jan 24 2009, 20:39
|

Нечётный пользователь.
     
Группа: Свой
Сообщений: 2 033
Регистрация: 26-05-05
Из: Бровари, Україна
Пользователь №: 5 417

|
Цитата(sonycman @ Jan 24 2009, 19:47)  А я почему-то подумал, что extern должен быть с точно таким-же типом, что и оригинальная переменная...  Ну вообще говоря "желательно". Прокатить такое может только если в файл с обработчиком ISR не включать тот h-файл, в котором объявлено переменную для остальных - я же предпочитаю объявлеия включать везде для проверки (если вдруг поменяю в .h uint16_t на uint32_t, а в .c забуду - компилятор выругатся. Но тогда vv.h Код extern volatile unsigned u; vv.c Код #include <vv.h> unsigned u; Компиляция: Код vv.c:2: error: conflicting type qualifiers for 'u' ./vv.h:1: error: previous declaration of 'u' was here Я как правило кеширую переменную в обработчике прерывания во веменную и сохраняю при выходе. Можно и так, набросал вот  Код template <typename T, volatile T& var> class cache_volatile { public: cache_volatile() : t(var) {} ~cache_volatile() { var = t; }
T t; };
volatile uint8_t timer;
ISR(TIMER0_OVF_vect) { cache_volatile<uint8_t, timer> tim; if( --tim.t == 0 ) { tim.t = 127; } if( PINB & 0x02) return; // тут тоже запишется назад изменённое значение if( tim.t & 0x01 ) PINB |= 0x01; } Код __vector_9: push __zero_reg__ push __tmp_reg__ in __tmp_reg__,__SREG__ push __tmp_reg__ clr __zero_reg__ push r24
lds r24,timer subi r24,lo8(-(-1)) brne .L2 ldi r24,lo8(127) .L2: sbic 54-0x20,1 rjmp .L4 sbrc r24,0 sbi 54-0x20,0 .L4: sts timer,r24
pop r24 pop __tmp_reg__ out __SREG__,__tmp_reg__ pop __tmp_reg__ pop __zero_reg__ reti
--------------------
Ну, я пошёл… Если что – звоните…
|
|
|
|
|
Jan 24 2009, 22:12
|

Любитель
    
Группа: Свой
Сообщений: 1 864
Регистрация: 20-08-06
Из: Тольятти
Пользователь №: 19 695

|
Хм, спасибо! Если будут проблемы с extern, то придётся именно так и поступать. Эх, пора мне, наверное, шаблоны поизучать - а то аж искры из глаз, пока силился понять, что там вы с ними "замутили" Можно, конечно, и без них обойтись...
|
|
|
|
|
Jan 25 2009, 11:43
|
Участник

Группа: Свой
Сообщений: 46
Регистрация: 4-02-05
Из: Москва
Пользователь №: 2 430

|
Я стараюсь union реже использовать - это одна из самых непереносимых и компилерозависимых вещей. Не все компилеры понимают безымяный union. Длинные бестолковые имена. Куча не всегда понятных переменных. А то что в хедер для проверки не вставить объявления - это цена вопроса. Вы же сами просили решения(хака)? Значит вам за него и отвечать.
|
|
|
|
|
Jan 25 2009, 17:31
|
Участник

Группа: Свой
Сообщений: 46
Регистрация: 4-02-05
Из: Москва
Пользователь №: 2 430

|
точно не помню  но нарывался уже на непонимание анонимного union, и вроде как даже не в одном месте. И это был С без плюсов. zltigo: вам виднее,и спорить не хочется. мне лично две длинные переменные вместо одной короткой - лишняя сущность (по оккаму). Тем не менее хочется отметить что вы везунчик - у вас есть выбор как минимум из 3х подходов реализации задачи
|
|
|
|
|
Jan 25 2009, 19:50
|
дятел
    
Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065

|
Цитата(sonycman @ Jan 25 2009, 21:48)  Но разве безымянные объединения не являются фичей исключительно C++? Не только. Насколько я ничего не помню, в С безымянные структуры могут быть, но они должны обязательно включаться в конечном итоге в именованные структуры или объединения. типа: Код struct { union { int a; char c[2]; }; int b; } w; только с безымянными объединениями нужно в любом случае быть очень аккуратными...
|
|
|
|
|
Jan 25 2009, 21:08
|
дятел
    
Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065

|
И раз уж зашла речь о безымянных обединениях и структурах, вот такой пример: Код volatile union { struct { int a; union { int a; int b; }; }; int b; int c; } t;
volatile int A, B, C;
int main() { t.a = 1; t.b = 2; t.c = 3;
A = t.a; B = t.b; C = t.c;
return 0; } Чему будут равны A,B,C в конце main ?
|
|
|
|
|
Jan 25 2009, 21:40
|
дятел
    
Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065

|
Цитата(zltigo @ Jan 26 2009, 00:19)  Для начала эта мусорная структура не должна откомпилироваться. во-первых многие компиляторы сожрут и не подавятся... я пробовал. хотя согласен что я слегка перестарался... а что скажут компиляторы на вот такую структуру: Код volatile union { struct { int b; union { int a; int b; }; }; int c; } t; ? только попробуйте сначала реально скомпилить прежде чем говорить что не скомпилит.
|
|
|
|
|
Jan 25 2009, 21:51
|

Гуру
     
Группа: Свой
Сообщений: 13 372
Регистрация: 27-11-04
Из: Riga, Latvia
Пользователь №: 1 244

|
Цитата(singlskv @ Jan 26 2009, 00:40)  только попробуйте сначала реально скомпилить прежде чем говорить что не скомпилит. 1. Multiple declaration for 'b' 2. "b" has already been declared in the current scope 3. Duplicate name 'b' not allowed in struct or union Что было видно и без попыток скомпилировать сие тремя компиляторами. Начиная с самого древнего и кривого BCC.
--------------------
Feci, quod potui, faciant meliora potentes
|
|
|
|
|
Jan 25 2009, 21:53
|
Участник

Группа: Свой
Сообщений: 46
Регистрация: 4-02-05
Из: Москва
Пользователь №: 2 430

|
живодеры!! вот увидете - перестанет компилятор вам компилить вообще после этого - пожалеете
|
|
|
|
|
Jan 25 2009, 22:10
|
дятел
    
Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065

|
Цитата(zltigo @ Jan 26 2009, 00:51)  Что было видно и без попыток скомпилировать сие тремя компиляторами. Начиная с самого древнего и кривого BCC. вот это: Код volatile union { struct { int b; union { int a; int b; }; }; int c; } t; VisualStudio 2005 прожевывает не поперхнувшись...
|
|
|
|
|
Jan 25 2009, 22:33
|

Гуру
     
Группа: Свой
Сообщений: 13 372
Регистрация: 27-11-04
Из: Riga, Latvia
Пользователь №: 1 244

|
Цитата(singlskv @ Jan 26 2009, 01:10)  VisualStudio 2005 прожевывает не поперхнувшись...  Ну фиг с ней - у меня по любому такое написать не получится. Хотя иногда достаточно, на первый взгляд, запутанные union-ы пишу. Код #pragma pack( push, 1 )
typedef struct Cons_shdr_s { BYTE type; BYTE cmd; union{ BYTE obj; BYTE obj_1; }; union{ BYTE lin; BYTE lin_1; BYTE lin_cons; };
}Cons_shdr;
typedef struct Cons_bhdr_s { BYTE obj_2; BYTE lin_2; }Cons_bhdr;
typedef struct Cons_frame_s {
union{ Cons_shdr; ulong hdr; }; union{ union{ BYTE nlen; char text[CONS_TXT_SIZE]; BYTE time[4]; }; struct{ union{ Cons_bhdr; ushort ident; }; union{ BYTE nlen; char text[CONS_TXT_SIZE]; }; }b; }; }Cons_frame; #pragma pack( pop ) Комментариии, правда,удалены  .
--------------------
Feci, quod potui, faciant meliora potentes
|
|
|
|
|
Jan 25 2009, 22:39
|
дятел
    
Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065

|
Цитата(zltigo @ Jan 26 2009, 01:33)  Ну фиг с ней - у меня по любому такое написать не получится. Хотя иногда достаточно, на первый взгляд, запутанные union-ы пишу. Комментариии, правда,удалены  . Это просто было в качестве примера что неименнованные структуры и объединения нужно очень осторожно применять, можно случайно нарваться... Структурки у Вас веселые...
|
|
|
|
|
Jan 26 2009, 08:20
|

Гуру
     
Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095

|
Цитата(777777 @ Jan 26 2009, 09:51)  А ты не пробовал объявлять ее вообще без volatile? Шикарный совет. "Как, вы еще не получали граблями в этом месте? Вот вам!  " Цитата(777777 @ Jan 26 2009, 09:51)  Вообще-то это требуется крайне редко, Вообще-то это требуется всегда, когда переменная изменяется неизвестным компилятору образом, например в прерывании, другом потоке или аппаратно отражает состояние внешних по отношению к ядру устройств. Цитата(777777 @ Jan 26 2009, 09:51)  Компилятор не может сделать никаких предположений о содержимом переменной и будет вынужден ее прочитать. Зато _вы_ можете сделать предположение за компилятор.  . Может он. Чтобы не мог - существует volatile. Если в конкретном случае без volatile вдруг не сделал, то при малейшем изменении исходника или версии компилятора сможет сделать и вот вам граблями в лоб - получите. Даже если чтения разнесены в разные функции и файлы - все больше компиляторов поддерживают многофайловую компиляцию. Хотите приключений - делайте, но не советуйте этого другим. Это примерно как "лазил я однажды в телевизор и нифига меня током не ударило. Так что незачем при ремонте его отключать от сети".
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Jan 26 2009, 11:38
|

Любитель
    
Группа: Свой
Сообщений: 1 864
Регистрация: 20-08-06
Из: Тольятти
Пользователь №: 19 695

|
Цитата(777777 @ Jan 26 2009, 11:51)  А ты не пробовал объявлять ее вообще без volatile? Но ведь это сразу создаст проблемы. Допустим, я захочу подождать, пока таймер не "протикает" несколько раз: Код extern dword sys_tick;
Wait(dword mksec) { dword new_value, old_value = sys_tick; do{ new_value = sys_tick; }while ((new_value - old_value) < mksec); } Без volatile цикл while просто повиснет, так как new_value просто не будет считываться - компилер, будучи уверенным, что она не изменяется (нет присваивания), выкинул операцию считывания из кода...
|
|
|
|
|
Jan 26 2009, 11:55
|
;
     
Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509

|
Цитата(sonycman @ Jan 26 2009, 14:38)  Код do
{
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) new_value = sys_tick; }
while ((new_value - old_value) < mksec); Пусть sys_tick volatile, проехали предыдущий вопрос. Если мы говорим конкретно об AVR, то дороговатая операция получается. Я бы для такого случая завел бы какой-нить байтовый кеш таймерок. Тогда не надо атомарного доступа. Портируемость AVR->ARM стремится к нулю
|
|
|
|
|
Jan 26 2009, 14:40
|
;
     
Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509

|
Цитата(sonycman @ Jan 26 2009, 18:02)  Тогда страдает или разрешение таймера, или величина макс. выдержки. Я применял это там, где тик был медленный = 1мс. В таких условиях в мониторе событий крутился опрос "быстрого" однобайтового таймера и этот тик (в предельном случае-вместе с вызовом обработчика) генерился там же. Т.е. преследовались цели: разгрузить прерывание, минимизировать джиттер прерывания от таймера из-за частых запретов/разрешений, разделить быстрые и медленные процессы. Понятно, что на ARM "мельчить" объектами меньше чем 32 бита не только не полезно, но и вредно. И никаких извратов там не надо. И, следуя вполне нормальному желанию портировать код, завел тип timer_t - размерностью какой надо в конкретном случае. Извиняюсь если написал много банальщины. Цитата(Сергей Борщ @ Jan 26 2009, 17:24)  В варианте для AVR считывать sys_tick в цикле и сравнивать с предыдущим значением. По остальным тезисам - согласен, а тут - слишком искусственная ситуация, ибо такие циклы встречаются довольно редко. Согласитесь, чаще встречается конструкция типа Код void action (void)
{ static timer_t local_timer =0; timer_t tmp; tmp = get_tick(); if ((local_timer - tmp) < My_Time_To_DO) return; local_timer = tmp; Do_Be_Do_Be_Do();
**********
return; }
|
|
|
|
|
Jan 26 2009, 15:20
|

Гуру
     
Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095

|
Цитата(_Pasha @ Jan 26 2009, 16:40)  Согласитесь, чаще встречается конструкция типа Да. Так вот Код timer_t get_tick() { timer_t Tmp1 = sys_tick; timer_t Tmp2 = sys_tick; while((Tmp2 >> 8) != (Tmp1 >> 8)) { Tmp1 = Tmp2; Tmp2 = sys_tick } return Tmp2; }
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Jan 26 2009, 17:45
|

Профессионал
    
Группа: Участник
Сообщений: 1 091
Регистрация: 25-07-07
Из: Саратов
Пользователь №: 29 357

|
Цитата(Сергей Борщ @ Jan 26 2009, 11:20)  Вообще-то это требуется всегда, когда переменная изменяется неизвестным компилятору образом, например в прерывании, другом потоке или аппаратно отражает состояние внешних по отношению к ядру устройств. А также если глобальная переменная используется в нескольких функциях. Транслируя каждую функцию по отдельности, компилятор не знает, как изменяется такая переменная. Однако вы же не делаете ее volatile. Такое объявление приводит лишь к тому, что если например она используется в функции дважды, то ее значение будет считываться из памяти каждый раз, а при отсутствии volatile (и наличии оптимизатора) - только раз. Цитата(Сергей Борщ @ Jan 26 2009, 11:20)  Зато _вы_ можете сделать предположение за компилятор.  . Может он. Чтобы не мог - существует volatile. Если в конкретном случае без volatile вдруг не сделал, то при малейшем изменении исходника или версии компилятора сможет сделать и вот вам граблями в лоб - получите. Ну например, в прерывании таймера инкрементируется некая переменная, а в прерывании какого-нибудь передатчика ее значение куда-то передается. Что может знать компилятор о ее содержимом? И "малейшего" изменения исходников (а тем более версии компилятора) недостаточно для изменения ее поведения. Нужно "существенное" изменение, например, если мы попытаемся ввести какую-нибудь паузу, как написал ниже sonycman - но паузы и задержки это и есть то, для чего придуман volatile. Но с другой стороны, введение задержек в микропроцессорной системе таким способом - явный признак дурного стиля, это снижает производительность системы. Для задержек как раз и существуют таймеры.
|
|
|
|
|
Jan 26 2009, 18:56
|

Любитель
    
Группа: Свой
Сообщений: 1 864
Регистрация: 20-08-06
Из: Тольятти
Пользователь №: 19 695

|
Цитата(777777 @ Jan 26 2009, 21:45)  Но с другой стороны, введение задержек в микропроцессорной системе таким способом - явный признак дурного стиля, это снижает производительность системы. Для задержек как раз и существуют таймеры. Это был просто пример. Естественно в реальной системе мёртво стопорить процессор при ожидании глупо. Хотя иногда и необходимо, и такие функции существуют. Но вернёмся к теме - вы всё же считаете, что volatile не обязателен для случая с инкрементом счётчика в ISR и чтения его в основной программе?
|
|
|
|
|
Jan 26 2009, 19:18
|

Гуру
     
Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095

|
Цитата(777777 @ Jan 26 2009, 19:45)  А также если глобальная переменная используется в нескольких функциях. Транслируя каждую функцию по отдельности, компилятор не знает, как изменяется такая переменная. Знает. Почитайте что такое точки последовательности, побочные эффекты. Функция исполняется от начала и до конца. Одна. Компилятор ничего не знает ни о прерываниях, ни о многопоточности. Задача прерывания - обеспечить неизменность состояния среды исполнения (state of the execution environment). Все, что изменяет это состояние является побочными эффектами (side-effects). Одним из побочных эффектов является обращение к volatile-переменной. Прочтите стандарт, чтобы не писать глупости. И компилятор имеет право встроить функцию. Так что "Транслируя каждую функцию по отдельности" бывает далеко не всегда.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
3 чел. читают эту тему (гостей: 3, скрытых пользователей: 0)
Пользователей: 0
|
|
|