Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: WinAVR, _delay_ms(), строки и оптимизация.
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > AVR
Laksus
Програмка для 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);
    }
}
artymen
Функции из 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 глобальной переменной.
Genadi Zawidowski
У функции _delay_ms() есть ограничение на максимальное время задержки. Посмотрите её исходник (или заголовочный файл) - там это написано было.
Laksus
Цитата(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 секунды. Так что вписываюсь в ограничение.
_______
ASZ
Может, это снобизм, но принципиально не использую ф-цию _delay_ms().
Использую службу времени, организованную на одном из таймеров.
artymen
Посмотрите, укладывается ли полученный бинарник во флеш (суммарный размер секций data + text):
Код
avr-size --format=Berkeley <файл elf>
, значение в колонке dec в байтах
GCC (а может avr-libc) любит такую пакость делать: он не следит за размерами. Вы можете вылезть за размеры оперативки или флеша, а он хоть бы хны. Я потом авр-дудкой прошиваю, и тоже, зараза, молчит. (Неясно, что он там прошивает: то, что влезает, или циклически затирает.)
Дело в том, что _delay_ms() без включенной оптимизации тянет за собой математическую либу с плавающей запятой, а весит она оееей.
Сергей Борщ
Цитата(artymen @ Nov 6 2010, 08:12) *
GCC (а может avr-libc) любит такую пакость делать: он не следит за размерами.
Фигня полная. Прекрасно следит. А если у вас самописный линкерный скрипт и в нем указан нереальный размер памяти - так кто же вам злобный буратина?


Laksus - ваша ошибка в том, что StringLCD объявлен без volatile.

Цитата(ASZ @ Nov 6 2010, 02:09) *
Может, это снобизм, но принципиально не использую ф-цию _delay_ms().
Конечно снобизм. Все хорошо к месту. И задержки на 10-20 тактов ядра вы на службе времени не организуете.
MrYuran
Цитата(Сергей Борщ @ Nov 6 2010, 09:44) *
Конечно снобизм. Все хорошо к месту. И задержки на 10-20 тактов ядра вы на службе времени не организуете.

Речь-то про ms, а не mks
Смысла делать миллисекундные задержки "на NOP-ах" я тоже никакого не вижу.
Лично я при таких задержках отпускаю процессор заниматься другими делами.
Хоть это и несколько сложнее, чем просто тупождать.
Цитата
Задержки решил сделать через _delay_ms(2000).

OMG! maniac.gif
artymen
Цитата
Фигня полная. Прекрасно следит. А если у вас самописный линкерный скрипт и в нем указан нереальный размер памяти - так кто же вам злобный буратина?
Линкерный скрипт дефолтовый. Работаю в Eclipse c AVR-Eclipse Plugin. Возможно, плагин кривой, что выбираемый мною девайс не передается линковщику для задания нужной конфигурации. Как бы то ни было, это не мои грабли, я всего лишь указал на баг.
Цитата
Laksus - ваша ошибка в том, что StringLCD объявлен без volatile.
Кстати, да... Но в данном случае это не должно играть роли, ведь оптимизации нету. Более того, когда она есть, работает... unsure.gif
alexeyv
Маленькое замечание по поводу кода:

Код
UDR= Address_1;// пошлем адрес
StringLCD= Hello_1;
.....

ISR(USART_UDRE_vect)
{
  .........
}



При посылке ПЕРВОГО байта прерывание USART_UDRE_vect возникает СРАЗУ ЖЕ, то есть при первом заходе в прерывание указатель StringLCD НЕ ИНИЦИАЛИЗИРОВАН и указывает на мусор.

В любом случае первым делом необходимо подготовить данные для передачи (в вашем случае - инициализация указателя), и только после этого начинать передачу массива данных

Например так:
Код
StringLCD= Hello_1;  // подготовка данных для передачи
UDR= Address_1;     //  старт передачи
Сергей Борщ
Цитата(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);//и отправим нужную строку
Marian
При уровне оптимизации -Os, компилятор выкидывает строку StringLCD=Hello_1,
(видно при отладке в протеусе)
Laksus
Цитата(Сергей Борщ @ 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(;;);


Спасибо ответившим.
Qwertty
Цитата(Laksus @ Nov 9 2010, 02:16) *
Просто оптимизатор считает, что нефиг присваивать одной и той же переменной несколько значений подряд.

Вам же подсказали квалификатор volatile. Он как раз и позволяет исключить излишний интеллект оптимизатора. По сути все действия с такой переменной вообще не оптимизируются. Почему он не помог непонятно. Может.. не туда добавили? smile.gif
alexeyv
Цитата(Сергей Борщ @ Nov 8 2010, 14:08) *
Отчего же? Флаг установится, (точнее он и перед этим стоял), а прерывание возникнет после его разрешения в строке UCSRB|= (1<<UDRIE);//и отправим нужную строку


В приведенном коде не видно что прерывание НЕ разрешено в UCSRB, до
Код
StringLCD= Hello_1;

, а стоит безликая
Код
init();

поэтому значение UCSRB на момент выполнения "UDR=...." неизвестно.

Я хотел лишь сказать то что, в любом случае, необходимо первым делом подготовить данные для отправки, и лишь потом производить любые действия по их передаче - так можно будет избежать трудноотлавливаемых проблем в далеком/недалеком будущем...........
Сергей Борщ
Цитата(Laksus @ Nov 9 2010, 00:16) *
___
volatile не помогает, ничего не меняется.
___
Покажите, как вы его применили. Сдается, что вы вместо volatile-указателя на char объявили указатель на volatile char. Потому и не помогло.
alexeyv - насчет безликого init() согласен. Но раз дальше идет разрешение прерывания UDRIE, то логично предположить что оно было запрещено до этого. Иначе бы мы просто постоянно крутились в его обработчике.
Laksus
Цитата(Сергей Борщ @ Nov 10 2010, 11:33) *
... Сдается, что вы вместо volatile-указателя на char объявили указатель на volatile char. Потому и не помогло. ...

Так точно! Я ж эту volatile пытался влепить в кучу мест (примерно как мартышка очки), и только после звездочки не догадался (она ж у меня вплотную к переменной пририсована, вот как-то в голову и не пришло, .что туды можно что-то добавить.)
Код
    char *volatile StringLCD;//  работает так как хотел


Большое спасибо за помощь.
Хоть в данном случае большой проблемы и не было, но в дальнейшем незнание разницы между volatile-указателем и укзателем на volatle еще не раз бы о себе напомнило. (Хотя я пока еще толком не понял этой разницы, но гугль говорит, что на эти грабли настапают многие. Есть много обсуждений этого вопроса, почитаю, надеюсь пойму.)

Сергей Борщ
Цитата(Laksus @ Nov 10 2010, 13:15) *
но в дальнейшем незнание разницы между volatile-указателем и укзателем на volatle еще не раз бы о себе напомнило. (Хотя я пока еще толком не понял этой разницы
volatile-указатель: "указатель в любой момент может измениться сам по себе и указать на какое-то другое место".
Указатель на volatile - "то, на что указывает указатель может измениться само по себе".
Принцип тот же, что и с const -указателем - "этот указатель всегда указывает на одно и то же место, ты не можешь присвоить ему другое значение и заставить указывать в другое место".
Указатель на const - "может указывать куда угодно, но то, на что он указывает менять нельзя".
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.