|
WinAVR, _delay_ms(), строки и оптимизация. |
|
|
|
Nov 5 2010, 19:17
|
Частый гость
 
Группа: Участник
Сообщений: 146
Регистрация: 16-05-05
Пользователь №: 5 069

|
Програмка для ATmega32, WinAVR-20100110. Инициализируется USART для передачи. После этого хочу отправить через USART несколько групп символов. С паузой между группами в несколько секунд. Задержки решил сделать через _delay_ms(2000). Оказалось, что если оптимизация "OPT = s", то правильно передается только последняя строка, а вместо остальных строк передается мусор. Насколько я понял указатель ставится на адрес ноль, а не на адрес строки. Если отключить оптимизацию ("OPT = 0"), то все строки передаются правильно. Если вместо _delay_ms() использую самодельную задержку delay_sec(uint8_t T){for..{for..{for..{for..{asm("nop")}}}}}, то все строки передаются нормально и при "OPT = s" Большой проблемы нет, так как с самдельной задержкой вроде бы работает. Но хотелось бы узнать причину, чтобы не получить проблем в дальнейшем. _______ Как правильно использовать _delay_ms(), чтобы работало и при оптимизации? Код //код . . .
char *StringLCD; char Hello_1[]= "11111111111111111111111111111111"; char Hello_2[]= "22222222222222222222222222222222";
//------------------------------------------------------ int main(void) { init();
UDR= Address_1;// пошлем адрес StringLCD= Hello_1; UCSRB|= (1<<UDRIE);//и отправим нужную строку _delay_ms(2000);
UDR= Address_1;// пошлем адрес StringLCD= Hello_2; UCSRB|= (1<<UDRIE);//и отправим нужную строку _delay_ms(2000);
for(;;); return 0; } //------------------------------------------------------ ISR(USART_UDRE_vect) { static uint8_t j= 0; if(j< 32) { UDR= StringLCD[j++]; } else { j=0; UCSRB&= ~(1<<UDRIE); } }
|
|
|
|
|
Nov 5 2010, 19:46
|

Участник

Группа: Участник
Сообщений: 66
Регистрация: 6-11-09
Из: г. Омск
Пользователь №: 53 464

|
Функции из util/delay.h имеют одну пакость, из доков: Цитата Note: In order for these functions to work as intended, compiler optimizations must be enabled, and the delay time must be an expression that is a known constant at compile-time. If these requirements are not met, the resulting delay will be much longer (and basically unpredictable), and applications that otherwise do not use floating-point calculations will experience severe code bloat by the floating-point library routines linked into the application. При отключенной оптимизации ("OPT = 0") задержка получается бОльшей, чем надо, и более того, непредсказуемой. А вообще похоже на то, что у вас первая строка не успевает передасться полностью к началу передачи второй. Либо скорость уарта настолько низкая, что двух секунд (даже болльше) не хватает, либо прерывания UDRE задерживает остальной код (другие прерывания, установка/снятие флага разрешения прерывания и т.п.). По-хорошему, так конечно не делается. Перед второй отправкой нужно дождаться завершения первой, например так: Код ... while (j != 0); UDR= Address_1;// пошлем адрес StringLCD= Hello_2; ... , предварительно сделав j глобальной переменной.
Сообщение отредактировал artymen - Nov 5 2010, 19:51
--------------------
"Сознание своего несовершенства приближает к совершенству" Гёте
|
|
|
|
|
Nov 5 2010, 22:52
|
Частый гость
 
Группа: Участник
Сообщений: 146
Регистрация: 16-05-05
Пользователь №: 5 069

|
Цитата(artymen @ Nov 5 2010, 22:46)  ... похоже на то, что у вас первая строка не успевает передасться полностью к началу передачи второй. Либо скорость уарта настолько низкая, что двух секунд (даже болльше) не хватает, либо прерывания UDRE задерживает остальной код (другие прерывания, установка/снятие флага разрешения прерывания и т.п.). По-хорошему, так конечно не делается. Перед второй отправкой нужно дождаться завершения первой, например так: ... Нет, скорость передачи 9600, это примерно один байт за миллисекунду. В группе 33 байта, итого порядка 33 мсек. А пауза между группами 2000 мсек. Других прерываний кроме ISR(USART_UDRE_vect) нет, и другого кода тоже нет. Я все убрал, чтобы выделить непонятный для меня глюк в чистом виде. Попробовал ставить проверку глобального счетчика передаваемых байт while(j), а задержку уменьшил до 111 мсек. Если ставлю задержку в виде for(..){.. for(..){ for(..) { asm("nop");}}}, то в реальном устройстве циферки мерехтят (100 пар групп, передается примерно за 30 сек), в симуляторе тоже все нормально. А если меняю задержку на _delay_ms();, то в реальном идет в течении 30 сек мусор, а затем приходит последняя строка. То есть каждая из предыдущих двухсот передач тоже состоит из 33 знаков идущих непрерывно, затем пауза 100 мсек, первый знак каждой группы == Address_1, так как я его шлю отдельно, а затем указателю StringLCD не присваивается значение нужной строки, а он остается равен нулю и с этого, нулевого, адреса забираются случайные байты для передачи. Я не профессионал, ни в микроконтроллерах, ни в программировании. Время от времени делаю простенькие устройства для себя. Поэтому подозреваю, что где-то сморозил глупость, а где не пойму. ________________________ Цитата(Genadi Zawidowski @ Nov 5 2010, 22:48)  У функции _delay_ms() есть ограничение на максимальное время задержки. Посмотрите её исходник (или заголовочный файл) - там это написано было. цитата из "C:\WinAVR\avr\include\util\delay.h" " The maximal possible delay is 262.14 ms / F_CPU in MHz. When the user request delay which exceed the maximum possible one, _delay_ms() provides a decreased resolution functionality. In this mode _delay_ms() will work with a resolution of 1/10 ms, providing delays up to 6.5535 seconds (independent from CPU frequency). The user will not be informed about decreased resolution." То есть до задержки 6,5 сек отрабатываются при любой частоте кварца. Мне нужно 2...3 секунды. Так что вписываюсь в ограничение. _______
Сообщение отредактировал Laksus - Nov 5 2010, 22:59
|
|
|
|
|
Nov 6 2010, 05:12
|

Участник

Группа: Участник
Сообщений: 66
Регистрация: 6-11-09
Из: г. Омск
Пользователь №: 53 464

|
Посмотрите, укладывается ли полученный бинарник во флеш (суммарный размер секций data + text): Код avr-size --format=Berkeley <файл elf> , значение в колонке dec в байтах GCC (а может avr-libc) любит такую пакость делать: он не следит за размерами. Вы можете вылезть за размеры оперативки или флеша, а он хоть бы хны. Я потом авр-дудкой прошиваю, и тоже, зараза, молчит. (Неясно, что он там прошивает: то, что влезает, или циклически затирает.) Дело в том, что _delay_ms() без включенной оптимизации тянет за собой математическую либу с плавающей запятой, а весит она оееей.
Сообщение отредактировал artymen - Nov 6 2010, 05:13
--------------------
"Сознание своего несовершенства приближает к совершенству" Гёте
|
|
|
|
|
Nov 6 2010, 06:44
|

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

|
Цитата(artymen @ Nov 6 2010, 08:12)  GCC (а может avr-libc) любит такую пакость делать: он не следит за размерами. Фигня полная. Прекрасно следит. А если у вас самописный линкерный скрипт и в нем указан нереальный размер памяти - так кто же вам злобный буратина? Laksus - ваша ошибка в том, что StringLCD объявлен без volatile. Цитата(ASZ @ Nov 6 2010, 02:09)  Может, это снобизм, но принципиально не использую ф-цию _delay_ms(). Конечно снобизм. Все хорошо к месту. И задержки на 10-20 тактов ядра вы на службе времени не организуете.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Nov 6 2010, 07:37
|

Беспросветный оптимист
     
Группа: Свой
Сообщений: 4 640
Регистрация: 26-12-07
Из: Н.Новгород
Пользователь №: 33 646

|
Цитата(Сергей Борщ @ Nov 6 2010, 09:44)  Конечно снобизм. Все хорошо к месту. И задержки на 10-20 тактов ядра вы на службе времени не организуете. Речь-то про ms, а не mks Смысла делать миллисекундные задержки "на NOP-ах" я тоже никакого не вижу. Лично я при таких задержках отпускаю процессор заниматься другими делами. Хоть это и несколько сложнее, чем просто тупождать. Цитата Задержки решил сделать через _delay_ms(2000). OMG!
--------------------
Программирование делится на системное и бессистемное. ©Моё :) — а для кого-то БГ — это Bill Gilbert =)
|
|
|
|
|
Nov 6 2010, 10:32
|

Участник

Группа: Участник
Сообщений: 66
Регистрация: 6-11-09
Из: г. Омск
Пользователь №: 53 464

|
Цитата Фигня полная. Прекрасно следит. А если у вас самописный линкерный скрипт и в нем указан нереальный размер памяти - так кто же вам злобный буратина? Линкерный скрипт дефолтовый. Работаю в Eclipse c AVR-Eclipse Plugin. Возможно, плагин кривой, что выбираемый мною девайс не передается линковщику для задания нужной конфигурации. Как бы то ни было, это не мои грабли, я всего лишь указал на баг. Цитата Laksus - ваша ошибка в том, что StringLCD объявлен без volatile. Кстати, да... Но в данном случае это не должно играть роли, ведь оптимизации нету. Более того, когда она есть, работает...
Сообщение отредактировал artymen - Nov 6 2010, 10:37
--------------------
"Сознание своего несовершенства приближает к совершенству" Гёте
|
|
|
|
|
Nov 8 2010, 05:07
|
Местный
  
Группа: Участник
Сообщений: 298
Регистрация: 26-01-09
Из: Пермь
Пользователь №: 43 940

|
Маленькое замечание по поводу кода: Код UDR= Address_1;// пошлем адрес StringLCD= Hello_1; .....
ISR(USART_UDRE_vect) { ......... } При посылке ПЕРВОГО байта прерывание USART_UDRE_vect возникает СРАЗУ ЖЕ, то есть при первом заходе в прерывание указатель StringLCD НЕ ИНИЦИАЛИЗИРОВАН и указывает на мусор. В любом случае первым делом необходимо подготовить данные для передачи (в вашем случае - инициализация указателя), и только после этого начинать передачу массива данных Например так: Код StringLCD= Hello_1; // подготовка данных для передачи UDR= Address_1; // старт передачи
|
|
|
|
|
Nov 8 2010, 09:08
|

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

|
Цитата(artymen @ Nov 6 2010, 12:32)  Возможно, плагин кривой, что выбираемый мною девайс не передается линковщику для задания нужной конфигурации. Как бы то ни было, это не мои грабли, я всего лишь указал на баг. О. уже плагин. А вовсе и не gcc и не avr-libc. Цитата(MrYuran @ Nov 6 2010, 09:37)  Речь-то про ms, а не mks Смысла делать миллисекундные задержки "на NOP-ах" я тоже никакого не вижу. Лично я при таких задержках отпускаю процессор заниматься другими делами. Хоть это и несколько сложнее, чем просто тупождать. Да какая разница. У меня есть инициализация LCD. Одного-единственного. Она выполняется один раз при включении. В конструкторе. ОС на этот момент не запущена, отпускать проц некуда. Так накой мне городить службу времени на таймерах, если тупой цикл занимает гораздо меньше места, а индикатору (не говоря уж о пользователе - он не заметит разницы) не критично затягивание задержек? Мне нужно при старте включить подтяжку, дать паузу ~1 мс на заряд паразитных емкостей и прочитать состояние ноги. Опять на таймерах городить? Но процессору все равно больше нечем заниматься пока не прочитает состояние той ноги, и точность (в плюс) задержки не критична нисколько. Цитата(alexeyv @ Nov 8 2010, 07:07)  При посылке ПЕРВОГО байта прерывание USART_UDRE_vect возникает СРАЗУ ЖЕ, Отчего же? Флаг установится, (точнее он и перед этим стоял), а прерывание возникнет после его разрешения в строке UCSRB|= (1<<UDRIE);//и отправим нужную строку
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Nov 8 2010, 22:16
|
Частый гость
 
Группа: Участник
Сообщений: 146
Регистрация: 16-05-05
Пользователь №: 5 069

|
Цитата(Сергей Борщ @ Nov 8 2010, 12:08)  ... Так накой мне городить службу времени на таймерах ... процессору все равно больше нечем заниматься ... Вы прямо-таки сформулировали мои мысли, прочитав я на какое-то мгновение засомневался, не цитата ли из моего сообщения, я хотел отправить очень похожее (по сути), уже набрал, но потом решил отложить. Абсолютно согласен, проц некуда отпускать, а кроме паразитных емкостей мне еще надо ждать установки RC-цепочек. ___ volatile не помогает, ничего не меняется. ___ Причина не в _delay_ms(2000), если по прерываниям таймера наращивать счетчик (Count++), а паузу между выводимыми строчками сделать через ожидание пока счетчик натикает нужное число while(Count < 20); , или if(Count==20)..., то все равно не работает так как казалось бы должно. Просто оптимизатор считает, что нефиг присваивать одной и той же переменной несколько значений подряд. Если присваивать в прерывании таймера, тогда все нормально. Но так как мне очень не хочется путаться с пусковым приветом в прерывании, где будет вся работа программы, то я лучше использую таки _delay_ms(2000), а для передачи текста использую не указатели, а функцию strcpy(). Ее оптимизатор не выкидывает. Все работает так как мне хотелось. Насколько я понял для правильного выхода из возникшей ситуации мне надо бы глубоко изучить внутреннюю кухню gcc. А через strcpy() может и неправильно (быстродействие, размер кода), но так как быстродействия мне не надо, и код (предположительно) влезет с запасом, то меня устраивает и такое половинчатое решение. Код char StringLCD[33]; . . . int main(void) { init();
UDR= Address_LCD;// пошлем адрес strcpy(StringLCD, Hello_1); UCSRB|= (1<<UDRIE);//и отправим строку1 _delay_ms(2000);
UDR= Address_LCD;// пошлем адрес strcpy(StringLCD, Hello_2); UCSRB|= (1<<UDRIE);//и отправим строку2 . . . for(;;); Спасибо ответившим.
Сообщение отредактировал Laksus - Nov 8 2010, 22:23
|
|
|
|
|
Nov 9 2010, 04:23
|
Местный
  
Группа: Участник
Сообщений: 298
Регистрация: 26-01-09
Из: Пермь
Пользователь №: 43 940

|
Цитата(Сергей Борщ @ Nov 8 2010, 14:08)  Отчего же? Флаг установится, (точнее он и перед этим стоял), а прерывание возникнет после его разрешения в строке UCSRB|= (1<<UDRIE);//и отправим нужную строку В приведенном коде не видно что прерывание НЕ разрешено в UCSRB, до Код StringLCD= Hello_1; , а стоит безликая Код init(); поэтому значение UCSRB на момент выполнения "UDR=...." неизвестно. Я хотел лишь сказать то что, в любом случае, необходимо первым делом подготовить данные для отправки, и лишь потом производить любые действия по их передаче - так можно будет избежать трудноотлавливаемых проблем в далеком/недалеком будущем...........
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|