Перейти к содержанию
    

WinAVR, _delay_ms(), строки и оптимизация.

Програмка для 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);
    }
}

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Функции из 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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

У функции _delay_ms() есть ограничение на максимальное время задержки. Посмотрите её исходник (или заголовочный файл) - там это написано было.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

... похоже на то, что у вас первая строка не успевает передасться полностью к началу передачи второй. Либо скорость уарта настолько низкая, что двух секунд (даже болльше) не хватает, либо прерывания 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 не присваивается значение нужной строки, а он остается равен нулю и с этого, нулевого, адреса забираются случайные байты для передачи.

 

Я не профессионал, ни в микроконтроллерах, ни в программировании. Время от времени делаю простенькие устройства для себя. Поэтому подозреваю, что где-то сморозил глупость, а где не пойму.

 

________________________

У функции _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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Может, это снобизм, но принципиально не использую ф-цию _delay_ms().

Использую службу времени, организованную на одном из таймеров.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Посмотрите, укладывается ли полученный бинарник во флеш (суммарный размер секций data + text):

avr-size --format=Berkeley <файл elf>

, значение в колонке dec в байтах

GCC (а может avr-libc) любит такую пакость делать: он не следит за размерами. Вы можете вылезть за размеры оперативки или флеша, а он хоть бы хны. Я потом авр-дудкой прошиваю, и тоже, зараза, молчит. (Неясно, что он там прошивает: то, что влезает, или циклически затирает.)

Дело в том, что _delay_ms() без включенной оптимизации тянет за собой математическую либу с плавающей запятой, а весит она оееей.

Изменено пользователем artymen

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

GCC (а может avr-libc) любит такую пакость делать: он не следит за размерами.
Фигня полная. Прекрасно следит. А если у вас самописный линкерный скрипт и в нем указан нереальный размер памяти - так кто же вам злобный буратина?

 

 

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

 

Может, это снобизм, но принципиально не использую ф-цию _delay_ms().
Конечно снобизм. Все хорошо к месту. И задержки на 10-20 тактов ядра вы на службе времени не организуете.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Конечно снобизм. Все хорошо к месту. И задержки на 10-20 тактов ядра вы на службе времени не организуете.

Речь-то про ms, а не mks

Смысла делать миллисекундные задержки "на NOP-ах" я тоже никакого не вижу.

Лично я при таких задержках отпускаю процессор заниматься другими делами.

Хоть это и несколько сложнее, чем просто тупождать.

Задержки решил сделать через _delay_ms(2000).

OMG! :maniac:

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Фигня полная. Прекрасно следит. А если у вас самописный линкерный скрипт и в нем указан нереальный размер памяти - так кто же вам злобный буратина?
Линкерный скрипт дефолтовый. Работаю в Eclipse c AVR-Eclipse Plugin. Возможно, плагин кривой, что выбираемый мною девайс не передается линковщику для задания нужной конфигурации. Как бы то ни было, это не мои грабли, я всего лишь указал на баг.

Laksus - ваша ошибка в том, что StringLCD объявлен без volatile.
Кстати, да... Но в данном случае это не должно играть роли, ведь оптимизации нету. Более того, когда она есть, работает... :unsure:
Изменено пользователем artymen

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Маленькое замечание по поводу кода:

 

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

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

 

 

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

 

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

 

Например так:

StringLCD= Hello_1;  // подготовка данных для передачи
UDR= Address_1;     //  старт передачи

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Возможно, плагин кривой, что выбираемый мною девайс не передается линковщику для задания нужной конфигурации. Как бы то ни было, это не мои грабли, я всего лишь указал на баг.
О. уже плагин. А вовсе и не gcc и не avr-libc.

Речь-то про ms, а не mks

Смысла делать миллисекундные задержки "на NOP-ах" я тоже никакого не вижу.

Лично я при таких задержках отпускаю процессор заниматься другими делами.

Хоть это и несколько сложнее, чем просто тупождать.

Да какая разница. У меня есть инициализация LCD. Одного-единственного. Она выполняется один раз при включении. В конструкторе. ОС на этот момент не запущена, отпускать проц некуда. Так накой мне городить службу времени на таймерах, если тупой цикл занимает гораздо меньше места, а индикатору (не говоря уж о пользователе - он не заметит разницы) не критично затягивание задержек? Мне нужно при старте включить подтяжку, дать паузу ~1 мс на заряд паразитных емкостей и прочитать состояние ноги. Опять на таймерах городить? Но процессору все равно больше нечем заниматься пока не прочитает состояние той ноги, и точность (в плюс) задержки не критична нисколько.

 

 

При посылке ПЕРВОГО байта прерывание USART_UDRE_vect возникает СРАЗУ ЖЕ,
Отчего же? Флаг установится, (точнее он и перед этим стоял), а прерывание возникнет после его разрешения в строке UCSRB|= (1<<UDRIE);//и отправим нужную строку

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

При уровне оптимизации -Os, компилятор выкидывает строку StringLCD=Hello_1,

(видно при отладке в протеусе)

Изменено пользователем Marian

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

... Так накой мне городить службу времени на таймерах

...

процессору все равно больше нечем заниматься ...

Вы прямо-таки сформулировали мои мысли, прочитав я на какое-то мгновение засомневался, не цитата ли из моего сообщения, я хотел отправить очень похожее (по сути), уже набрал, но потом решил отложить. Абсолютно согласен, проц некуда отпускать, а кроме паразитных емкостей мне еще надо ждать установки 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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Просто оптимизатор считает, что нефиг присваивать одной и той же переменной несколько значений подряд.

Вам же подсказали квалификатор volatile. Он как раз и позволяет исключить излишний интеллект оптимизатора. По сути все действия с такой переменной вообще не оптимизируются. Почему он не помог непонятно. Может.. не туда добавили? :)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Отчего же? Флаг установится, (точнее он и перед этим стоял), а прерывание возникнет после его разрешения в строке UCSRB|= (1<<UDRIE);//и отправим нужную строку

 

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

StringLCD= Hello_1;

, а стоит безликая

init();

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

 

Я хотел лишь сказать то что, в любом случае, необходимо первым делом подготовить данные для отправки, и лишь потом производить любые действия по их передаче - так можно будет избежать трудноотлавливаемых проблем в далеком/недалеком будущем...........

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
К сожалению, ваш контент содержит запрещённые слова. Пожалуйста, отредактируйте контент, чтобы удалить выделенные ниже слова.
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...