|
volatile всё-таки нужен или нет?, всё работает, но страшно |
|
|
|
Mar 9 2013, 02:18
|

I WANT TO BELIEVE
     
Группа: Свой
Сообщений: 2 617
Регистрация: 9-03-08
Пользователь №: 35 751

|
Имеется программный ШИМ: Код typedef struct { uint8_t counter; uint8_t top; uint8_t compare_val; uint8_t pin; uint8_t enabled; volatile uint8_t *port; } pwm_channel;
static pwm_channel channels[PWM_MAXCHANNELS];
ISR(TIMER1_COMPA_vect) { uint8_t i; for ( i = 0; i < chnls_cnt; i++ ) { if ( ! channels[i].enabled ) continue; channels[i].counter++; if ( channels[i].counter == channels[i].compare_val ) { *channels[i].port |= 1 << channels[i].pin; } else { if ( channels[i].counter >= channels[i].top ) { channels[i].counter = 0; *channels[i].port &= ~(1 << channels[i].pin); } } } } При этом channels[i].enabled, channels[i].top и channels[i].compare_val меняются посредством вызова функции spwm_configchannel(), нужно ли делать их volatile и почему всё работает и без этого? Верно ли утверждение, что все глобальные переменные, доступ к которым осуществляется как в прерывании, так и в основном цикле нужно обязательно объявлять с volatile? Я в принципе так всегда и делал, но вот тут вдруг всё работает даже с -O3 P.S. скурил Nine ways to break your systems code using volatile.. страшно. Щас ещё баръеры памяти расставлять побегу )
--------------------
The truth is out there...
|
|
|
|
2 страниц
1 2 >
|
 |
Ответов
(1 - 14)
|
Mar 9 2013, 08:20
|
    
Группа: Свой
Сообщений: 1 928
Регистрация: 11-07-06
Пользователь №: 18 731

|
Цитата(sigmaN @ Mar 9 2013, 06:18)  При этом channels[i].enabled, channels[i].top и channels[i].compare_val меняются посредством вызова функции spwm_configchannel(), нужно ли делать их volatile и почему всё работает и без этого? Верно ли утверждение, что все глобальные переменные, доступ к которым осуществляется как в прерывании, так и в основном цикле нужно обязательно объявлять с volatile? volatile должны быть объявлены глобальные переменные, которые и _изменяются_ в прерывании, и используются вне прерывания. channels[i].enabled, channels[i].top и channels[i].compare_val в прерывании не изменяются, и им volatile'ность не требуется. Используется ли вне прерывания channels[i].counter++, не понятно. Тем не менее, прерывание TIMER1_COMPA при исполнении spwm_configchannel() может привести к сбою формирования ШИМ, если не подстелить соломки. (н-р, из приведённого кода не очевидно, что в spwm_configchannel() после "channels[i].enabled = 0" выполняется что-то, устанавливающее пин в определённое состояние, вроде "*channels[i].port &= ~(1 << channels[i].pin);", или что не приключится пропуск такта ШИМ при изменении channels[i].compare_val, или что ...; но, возможно, оно Вам и не актуально)
|
|
|
|
|
Mar 9 2013, 09:01
|

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

|
QUOTE (sigmaN @ Mar 9 2013, 04:18)  Верно ли утверждение, что все глобальные переменные, доступ к которым осуществляется как в прерывании, так и в основном цикле нужно обязательно объявлять с volatile? Да, верно. QUOTE (xemul @ Mar 9 2013, 10:20)  volatile должны быть объявлены глобальные переменные, которые и _изменяются_ в прерывании, и используются вне прерывания. channels[i].enabled, channels[i].top и channels[i].compare_val в прерывании не изменяются, и им volatile'ность не требуется. А вот это неверно. Если оставить их без volatile, то компилятор может в основном цикле просто закешировать их значение в регистрах и никогда не складывать в память. А может вообще выкинуть все операции с этими переменными, ибо в основном цикле есть только запись в эти переменные, но нет чтения. Следовательно, результат этой записи никому не нужен и считать его тоже не нужно. QUOTE (sigmaN @ Mar 9 2013, 04:18)  но вот тут вдруг всё работает даже с -O3 Если хотите после обновления компилятора или изменения какой-нибудь галочки в его настройках или изменения исходника долго и с удовольствием искать, почему же все вдруг перестало работать - оставьте так. Если же будете добавлять volatile, то в местах, где есть несколько доступов к одной переменной вроде вот этого: CODE channels[i].counter++; if ( channels[i].counter == channels[i].compare_val ) используйте временную не-volatile переменную: CODE uint8_t counter_cache = channels[i].counter++; if ( counter_cache == channels[i].compare_val )
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Mar 9 2013, 10:56
|

I WANT TO BELIEVE
     
Группа: Свой
Сообщений: 2 617
Регистрация: 9-03-08
Пользователь №: 35 751

|
Цитата Используется ли вне прерывания channels[i].counter++, не понятно. Тем не менее прерывание TIMER1_COMPA при исполнении spwm_configchannel() может привести к сбою формирования ШИМ, если не подстелить соломки. да да, знаю. В моём случае никаких проблем это не вызывает. Цитата Если хотите после обновления компилятора или изменения какой-нибудь галочки в его настройках или изменения исходника долго и с удовольствием искать, почему же все вдруг перестало работать - оставьте так. Вот да! Я так и думал! Цитата Если же будете добавлять volatile, то в местах, где есть несколько доступов к одной переменной вроде вот этого: используйте временную не-volatile переменную: Хороший совет. Спасибо!
--------------------
The truth is out there...
|
|
|
|
|
Mar 9 2013, 11:12
|

Гуру
     
Группа: Модераторы
Сообщений: 10 983
Регистрация: 23-11-05
Пользователь №: 11 287

|
Цитата(Сергей Борщ @ Mar 9 2013, 11:01)  Если же будете добавлять volatile, то в местах, где есть несколько доступов к одной переменной вроде вот этого: Код channels[i].counter++; if ( channels[i].counter == channels[i].compare_val ) используйте временную не-volatile переменную: Код uint8_t counter_cache = channels[i].counter++; if ( counter_cache == channels[i].compare_val ) Это чтобы при прерывании между обращениями к переменной не пришлось работать с разными её значениями?
|
|
|
|
|
Mar 9 2013, 11:24
|
;
     
Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509

|
Немного о другом. Зачем в теле цикла инкрементировать счетчик для каждого канала, если он для всех один? У Вас есть варианты, когда каналы совсем независимые? channels[i].top разный? ps CODE typedef struct { uint8_t compare_val; uint8_t pin; uint8_t enabled; volatile uint8_t *port; } pwm_channel;
static volatile pwm_channel channels[PWM_MAXCHANNELS]; static volatile uint8_t counter, top;
ISR(TIMER1_COMPA_vect) { uint8_t i; if(++counter > top) counter = 0; for ( i = 0; i < chnls_cnt; i++ ) { if ( ! channels[i].enabled ) continue; if(counter == channels[i].compare_val) *channels[i].port |= 1 << channels[i].pin; else if (!counter) *channels[i].port &= ~(1 << channels[i].pin); } }
Сообщение отредактировал _Pasha - Mar 9 2013, 12:01
|
|
|
|
|
Mar 9 2013, 12:01
|

I WANT TO BELIEVE
     
Группа: Свой
Сообщений: 2 617
Регистрация: 9-03-08
Пользователь №: 35 751

|
Цитата Это чтобы при прерывании между обращениями к переменной не пришлось работать с разными её значениями? но ведь в примере мы уже в прерывании. Это чтобы полностью не связывать руки оптимизатору. Программист то точно знает, что после channels[i].counter++; эта переменная не изменится и её можно закешировать для дальнейшего использования в цикле. Цитата У Вас есть варианты, когда каналы совсем независимые? channels[i].top разный? Да. Каналы абсолютно независимы. И top и compare_val задаются индивидуально. Это пасхалка, чтоб музычку сыграть на форсунках )))
--------------------
The truth is out there...
|
|
|
|
|
Mar 11 2013, 08:36
|
Гуру
     
Группа: Свой
Сообщений: 3 123
Регистрация: 7-04-07
Из: Химки
Пользователь №: 26 847

|
Цитата(xemul @ Mar 9 2013, 14:20)  Тогда необходимо объявлять volatile любую переменную, которая в одной единице компиляции только читается, а в другой - только пишется. Нет. Компилятор производит межпроцедурный анализ, что бы определить, какие переменные и где читаются и пишутся. Если компилятор не может произвести такой анализ (ну например у него нет режима оптимизации полной программы, такого как LTO), то он будет придерживаться пессимистических предположений, и считать что все (ну почти все) глобальные переменные могут измениться при вызове неизвестной ему функции. Но сами места вызовов он все же видит. Т.е. он считает, что если никаких функций не вызывалось, то и измениться ничего глобального не могло (я несколько упрощаю). С прерываниями все гораздо хуже - компилятор не знает, когда они происходят, так что либо он должен считать, что где угодно может произойти прерывание, которое может изменить что угодно (а это сразу ставит крест на любых оптимизациях с участием не локальных переменных), либо отдать это на откуп программисту (с модификатором volatile)
|
|
|
|
|
Mar 11 2013, 10:42
|
    
Группа: Свой
Сообщений: 1 928
Регистрация: 11-07-06
Пользователь №: 18 731

|
Цитата(XVR @ Mar 11 2013, 12:36)  Нет... Вообще-то, примерно это я и написал (и даже перечислил, то, что не требует объявления volatile, т.к. волатильно _не_изменяется_; даже то, что волатильно изменяется, но опять же не требует volatile). Отквоченная Вами фраза - доказательство (точнее, в данном случае, опровержение другого утверждения) от противного, ПМСМ, довольно очевидное.
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|