реклама на сайте
подробности

 
 
2 страниц V   1 2 >  
Reply to this topicStart new topic
> WinAVR, _delay_ms(), строки и оптимизация.
Laksus
сообщение Nov 5 2010, 19:17
Сообщение #1


Частый гость
**

Группа: Участник
Сообщений: 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);
    }
}
Go to the top of the page
 
+Quote Post
artymen
сообщение Nov 5 2010, 19:46
Сообщение #2


Участник
*

Группа: Участник
Сообщений: 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


--------------------
"Сознание своего несовершенства приближает к совершенству"
Гёте
Go to the top of the page
 
+Quote Post
Genadi Zawidowsk...
сообщение Nov 5 2010, 19:48
Сообщение #3


Профессионал
*****

Группа: Участник
Сообщений: 1 620
Регистрация: 22-06-07
Из: Санкт-Петербург, Россия
Пользователь №: 28 634



У функции _delay_ms() есть ограничение на максимальное время задержки. Посмотрите её исходник (или заголовочный файл) - там это написано было.
Go to the top of the page
 
+Quote Post
Laksus
сообщение Nov 5 2010, 22:52
Сообщение #4


Частый гость
**

Группа: Участник
Сообщений: 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
Go to the top of the page
 
+Quote Post
ASZ
сообщение Nov 5 2010, 23:09
Сообщение #5


Местный
***

Группа: Свой
Сообщений: 302
Регистрация: 24-07-06
Из: Донецк, Украина
Пользователь №: 19 042



Может, это снобизм, но принципиально не использую ф-цию _delay_ms().
Использую службу времени, организованную на одном из таймеров.
Go to the top of the page
 
+Quote Post
artymen
сообщение Nov 6 2010, 05:12
Сообщение #6


Участник
*

Группа: Участник
Сообщений: 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


--------------------
"Сознание своего несовершенства приближает к совершенству"
Гёте
Go to the top of the page
 
+Quote Post
Сергей Борщ
сообщение Nov 6 2010, 06:44
Сообщение #7


Гуру
******

Группа: Модераторы
Сообщений: 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)
Go to the top of the page
 
+Quote Post
MrYuran
сообщение Nov 6 2010, 07:37
Сообщение #8


Беспросветный оптимист
******

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



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

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

OMG! maniac.gif


--------------------
Программирование делится на системное и бессистемное. ©Моё :)
— а для кого-то БГ — это Bill Gilbert =)
Go to the top of the page
 
+Quote Post
artymen
сообщение Nov 6 2010, 10:32
Сообщение #9


Участник
*

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



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

Сообщение отредактировал artymen - Nov 6 2010, 10:37


--------------------
"Сознание своего несовершенства приближает к совершенству"
Гёте
Go to the top of the page
 
+Quote Post
alexeyv
сообщение Nov 8 2010, 05:07
Сообщение #10


Местный
***

Группа: Участник
Сообщений: 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;     //  старт передачи
Go to the top of the page
 
+Quote Post
Сергей Борщ
сообщение Nov 8 2010, 09:08
Сообщение #11


Гуру
******

Группа: Модераторы
Сообщений: 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)
Go to the top of the page
 
+Quote Post
Marian
сообщение Nov 8 2010, 14:56
Сообщение #12


Частый гость
**

Группа: Участник
Сообщений: 148
Регистрация: 23-02-07
Пользователь №: 25 618



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

Сообщение отредактировал Marian - Nov 8 2010, 15:34
Go to the top of the page
 
+Quote Post
Laksus
сообщение Nov 8 2010, 22:16
Сообщение #13


Частый гость
**

Группа: Участник
Сообщений: 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
Go to the top of the page
 
+Quote Post
Qwertty
сообщение Nov 8 2010, 23:06
Сообщение #14


Местный
***

Группа: Свой
Сообщений: 408
Регистрация: 21-10-06
Из: Санкт-Петербург
Пользователь №: 21 527



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

Вам же подсказали квалификатор volatile. Он как раз и позволяет исключить излишний интеллект оптимизатора. По сути все действия с такой переменной вообще не оптимизируются. Почему он не помог непонятно. Может.. не туда добавили? smile.gif
Go to the top of the page
 
+Quote Post
alexeyv
сообщение Nov 9 2010, 04:23
Сообщение #15


Местный
***

Группа: Участник
Сообщений: 298
Регистрация: 26-01-09
Из: Пермь
Пользователь №: 43 940



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


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

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

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

Я хотел лишь сказать то что, в любом случае, необходимо первым делом подготовить данные для отправки, и лишь потом производить любые действия по их передаче - так можно будет избежать трудноотлавливаемых проблем в далеком/недалеком будущем...........
Go to the top of the page
 
+Quote Post

2 страниц V   1 2 >
Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 19th July 2025 - 00:06
Рейтинг@Mail.ru


Страница сгенерированна за 0.01509 секунд с 7
ELECTRONIX ©2004-2016