|
|
  |
WinAVR, задержки > 250мс, Help, me!!! |
|
|
|
Nov 16 2008, 17:28
|
Частый гость
 
Группа: Свой
Сообщений: 106
Регистрация: 13-05-05
Пользователь №: 4 977

|
Помаленьку осваиваю AVR, пишу в WinAVR. Всякие простейшие "моргалки", вроде освоил, решил светофор сбацать. Вроде элементарно, но в WinAVR столкнулся с проблемами с задержками > 250 ms. Версия WinAVR - 20070525, все проверялось в Proteus 7.2SP6. Вот, что получилось: Код //Электронный светофор, =AVR. // cpu: ATMega8 // speed: 1 mhz
#include <avr/io.h> #include <util/delay.h>
#define F_CPU 1000000UL
int main(void) { //Начало программы
// Инициализация портов PORTB=0x20; DDRB=0x1F; PORTC=0x00; DDRC=0x00; PORTD=0x00; DDRD=0x00; while (1) //Бесконечный цикл { PORTB |= _BV(PB0); // Уст. пин 0 порта B в "1" - вкл. красный PORTB |= _BV(PB3); // Уст. пин 3 порта B в "1" - вкл. зеленый для пеших _delay_ms(250); // Пауза 3 сек. - пока горит красный _delay_ms(250); _delay_ms(250); _delay_ms(250); _delay_ms(250); _delay_ms(250); _delay_ms(250); _delay_ms(250); _delay_ms(250); _delay_ms(250); PORTB |= _BV(PB1); // Уст. пин 1 порта B в "1"- вкл. желтый _delay_ms(250); _delay_ms(250); PORTB &= ~_BV(PB0); // Уст. пин 0 порта B в "0" - выкл. красный PORTB &= ~_BV(PB3); // Уст. пин 3 порта B в "0" - выкл. зеленый для пеших _delay_ms(250); // Пауза 1 сек. - пока горит желтый _delay_ms(250); _delay_ms(250); _delay_ms(250); PORTB &= ~_BV(PB1); // Уст. пин 1 порта B в "0"- выкл. желтый PORTB |= _BV(PB2); // Уст. пин 1 порта B в "1"- вкл. зеленый PORTB |= _BV(PB4); // Уст. пин 3 порта B в "1" - вкл. красный для пеших _delay_ms(250); // Пауза 3.5 сек. - пока горит зеленый _delay_ms(250); _delay_ms(250); _delay_ms(250); _delay_ms(250); _delay_ms(250); _delay_ms(250); _delay_ms(250); _delay_ms(250); _delay_ms(250); _delay_ms(250); _delay_ms(250); _delay_ms(250); _delay_ms(250); PORTB &= ~_BV(PB2); // Уст. пин 2 порта B в "0"- выкл. зеленый PORTB |= _BV(PB1); // Уст. пин 1 порта B в "1"- вкл. желтый PORTB &= ~_BV(PB4); // Уст. пин 3 порта B в "0" - выкл. красный для пеших _delay_ms(250); // Пауза 1 сек. - пока горит желтый _delay_ms(250); _delay_ms(250); _delay_ms(250); PORTB &= ~_BV(PB1); // Уст. пин 1 порта B в "0"- выкл. желтый } // } Т.е., что бы получить требуемые задержки, как я думал, типа _delay_ms(3000), _delay_ms(1000) и _delay_ms(4500), прошлось делать "порно" в виде последовательных команд _delay_ms(250); То же самое - в CVAVR - все ОК: Код /***************************************************** Project : TrafficLights Version : 1 alpha Date : 16.11.2008 Author : Freeware, for evaluation and non-commercial use only Chip type : ATmega8 Program type : Application Clock frequency : 8,000000 MHz Memory model : Small External RAM size : 0 Data Stack size : 256 *****************************************************/
#include <mega8.h> #include <delay.h>
void main(void) {
// Input/Output Ports initialization PORTB=0x20; DDRB=0x1F; PORTC=0x00; DDRC=0x00; PORTD=0x00; DDRD=0x00;
while (1) { // Place your code here PORTB.0 = 1; // Уст. пин 0 порта B в "1" - вкл. красный delay_ms(3000); // Пауза 3 сек. - пока горит красный PORTB.0 = 0; // Уст. пин 0 порта B в "0" - выкл. красный PORTB.1 = 1; // Уст. пин 1 порта B в "1"- вкл. желтый delay_ms(1000); // Пауза 1 сек. - пока горит желтый PORTB.1 = 0; // Уст. пин 1 порта B в "0"- выкл. желтый PORTB.2 = 1; // Уст. пин 1 порта B в "1"- вкл. зеленый delay_ms(4500); // Пауза 1 сек. - пока горит зеленый PORTB.2 = 0; // Уст. пин 2 порта B в "0"- выкл. зеленый PORTB.1 = 1; // Уст. пин 1 порта B в "1"- вкл. желтый delay_ms(1000); // Пауза 1 сек. - пока горит желтый PORTB.1 = 0; // Уст. пин 1 порта B в "0"- выкл. желтый }; } Здесь - все четко - в Preteus'е все переключается(правильные задержки) как надо! В чем косяк в случае с WinAVR?!! Как решать проблему?!!
|
|
|
|
|
Nov 17 2008, 13:01
|
Частый гость
 
Группа: Свой
Сообщений: 106
Регистрация: 13-05-05
Пользователь №: 4 977

|
Цитата(ReAl @ Nov 17 2008, 00:41)  1) почитать документацию на _delay_ms() используемой версии, увидеть в ограничение макс. времени в зависимости от частоты процессора 2) взять хоть немного более свежую версию (WinAVR-0071221 сгодится) - там упор уже в 6,5секунды независимо от частоты. 3) написать свою delay_msec(unsigned msec) { while(msec--) _delay_ms(1); } 1) The maximal possible delay is 262.14 ms / F_CPU in MHz. Т.е. При тактовой 1МГц получим не более 262 мс всего. 2) ОК, попробую или предлагаемую 0071221 или последнюю доступную с оффсайта. 3) уже понял и пример нашел, и своя идея созрела, попробуем. Просто хотелось минимума кода. Цитата(Aesthete Animus @ Nov 17 2008, 01:17)  ... 4) не использовать задержки Каким образом, можно пример для моего случая? Цитата(_Pasha @ Nov 17 2008, 03:37)  5) А использовать таймеры и прерывания. И проц не вешается, и Вам с пользой - быстрее освоите потроха. Была такая мысля, но я пока "чайник" - чуть попозже, как освою прерывания/таймеры/счетчики.
|
|
|
|
|
Nov 17 2008, 14:52
|

Местный
  
Группа: Свой
Сообщений: 222
Регистрация: 9-06-07
Пользователь №: 28 317

|
Цитата(Alex_NEMO @ Nov 17 2008, 16:01)  Каким образом, можно пример для моего случая? Использовать таймеры - именно это я имел ввиду. Все остальное от лукавого. [spoiler="Сделать можно например так"] Код #include <avr/io.h> #include <avr/interrupt.h>
volatile uint16_t timer_ms;
// Вызывается каждую милисекунду SIGNAL(SIG_OUTPUT_COMPARE2) { if (timer_ms) --timer_ms; }
void inline delay_set(uint16_t ms) { cli(); timer_ms = ms; sei(); }
void inline delay_wait() { while (timer_ms); }
int main(void) { // Предполагается, что F_CPU = 16МГц TCCR2 = (1 << WGM21)|(1 << CS22); // CTC режим, прескаллер 64 OCR2 = 249; TIMSK = (1 << OCIE2); sei();
delay_set(1024); delay_wait();
for (;;) {} } [/spoiler]
|
|
|
|
|
Nov 17 2008, 16:28
|
Частый гость
 
Группа: Свой
Сообщений: 106
Регистрация: 13-05-05
Пользователь №: 4 977

|
Цитата(Aesthete Animus @ Nov 17 2008, 17:52)  Использовать таймеры - именно это я имел ввиду. Все остальное от лукавого. Спасибо, бум разбираться/пробовать!
|
|
|
|
|
Nov 18 2008, 15:54
|
Частый гость
 
Группа: Свой
Сообщений: 106
Регистрация: 13-05-05
Пользователь №: 4 977

|
Цитата(ReAl @ Nov 17 2008, 00:41)  2) взять хоть немного более свежую версию (WinAVR-0071221 сгодится) - там упор уже в 6,5секунды независимо от частоты. Да, установка WinAVR-20071221 - помогла, спасибо! Ещё в ходе эксперементов родилась такая подпрограмка: Код float delay_s(float t) { uint8_t i, j; j = t * 100; for(i=0;i<j;i++) _delay_ms(10); } вызывал так: delay_s(5); или delay_s(3.5); На глаз, все правильно считает! Только один вопрс - при компиляции выскакивает один Warning: control reaches end of non-void function- чем ему закрывающая скобка не нравится в моей ф-ции? Или это "особенности" компилера из разряда "странностей"?!!
|
|
|
|
|
Nov 18 2008, 16:17
|

Местный
  
Группа: Свой
Сообщений: 222
Регистрация: 9-06-07
Пользователь №: 28 317

|
Цитата(Alex_NEMO @ Nov 18 2008, 18:54)  Только один вопрс - при компиляции выскакивает один Warning: control reaches end of non-void function- чем ему закрывающая скобка не нравится в моей ф-ции? Или это "особенности" компилера из разряда "странностей"?!!  ..Потому что функция ваша имеет возращаемый тип (не void функция), но return-а в ней нет А вообще, во-первых, не используйте числа с плавающей точкой без очень сильной на то необходимости. А во вторых, ну не поместится в однобайтовую переменную i число, скажем, 350!
|
|
|
|
|
Nov 18 2008, 16:52
|
Частый гость
 
Группа: Свой
Сообщений: 106
Регистрация: 13-05-05
Пользователь №: 4 977

|
Цитата(Aesthete Animus @ Nov 18 2008, 19:17)  ..Потому что функция ваша имеет возращаемый тип (не void функция), но return-а в ней нет А вообще, во-первых, не используйте числа с плавающей точкой без очень сильной на то необходимости. А во вторых, ну не поместится в однобайтовую переменную i число, скажем, 350! Понял, спасибо за помощь, понял свои ошибки! А float использовал только для "универсальности" (напр., для получения 3,5 сек.). Итого, получилось: Код float delay_s(float t) { uint16_t i, j; j = t * 100; for(i=0;i<j;i++) _delay_ms(10); return 0; } Теперь - все ОК!
|
|
|
|
|
Nov 18 2008, 18:04
|
;
     
Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509

|
Цитата(Alex_NEMO @ Nov 18 2008, 20:52)  А float использовал только для "универсальности" (напр., для получения 3,5 сек.). Ну так и используйте uint32_t на крайняк - если даже использовать микросекундное (!) разрешение - получите больше часа максимальную задержку. Стока не живут даже в коллайдере А плавучка - это же страшно ресурсоемко и медленно на данной архитектуре. ЗЫ: можно сразу идти дальше и завести себе тип timer_t, разрядность которого можно менять в зависимости от требований проекта. Код /*Нужное откомментить*/ // typedef uint8_t timer_t // typedef uint16_t timer_t // typedef uint32_t timer_t А дальше - очень рекомендую написать это простенькое прерывание в посте Aesthete Animus и по аналогии - чтением тиков таймера можно управлять независимо несколькими десятками светофоров.
|
|
|
|
|
Nov 18 2008, 18:27
|

Нечётный пользователь.
     
Группа: Свой
Сообщений: 2 033
Регистрация: 26-05-05
Из: Бровари, Україна
Пользователь №: 5 417

|
Против uint32_t ничего не имею  Однако плавучка тоже имеет право на жизнь (вкупе с uint16_t или uint32_t - не важно). Дело в том, что если времена не передаются извне или вычисляются в программе, а жёстко заданы на момент компиляции, то всю плавучку можно спрятать в compile-time (собственно, util/delay.h так и делает). Зато просто удобно писать плавающее число. delay_s.h Код #ifndef DELAY_S_H #define DELAY_S_H
#include <util/delay.h>
static inline void delay_s(float s) { uint16_t i = s * 100; if(i) do _delay_ms(10); while(--i); } #endif d.c Код #include "delay_s.h"
void foo() { delay_s(3.5); } avr-gcc -Os -DF_CPU=8000000UL -mmcu=atmega48 d.c Код foo: ldi r18,lo8(350) ; вот оно uint16_t i = s * 100 - две команды загрузки и всё ldi r19,hi8(350) .L2: ldi r24,lo8(20000) ldi r25,hi8(20000) /* #APP */ 1: sbiw r24,1 brne 1b /* #NOAPP */ subi r18,lo8(-(-1)) sbci r19,hi8(-(-1)) brne .L2 ret Другое дело, что это же (и тоже с плавучкой) можно сделать и через задержку в циклах с макросом SEC_TO_CYCLES(), который принимает плавающее число, множит/делит c учётом F_CPU (всё во время компиляции!) и выдаёт uint16_t или uint32_t неких попугайных циклов, принимаемых нужной функцией. Но это достаточно вкусовой вопрос. p.s. Сам я первое, что пишу для новой платы - это таймерное прерывание, свободно бегущий таймер и отработку таймаутов ( ну это вот if(timeout) --timeout; как в приведенном примере прерывания), иногда таких таймаутов несколько для разных задач, какие-то в тиках, какие-то в уже секундах. Но "случаи бывают всякие" и я бы не стал с порога отвергать задержки по месту и аккуратно применённый float, который заканчивает свою жизнь в compile-time.
--------------------
Ну, я пошёл… Если что – звоните…
|
|
|
|
|
Nov 18 2008, 18:40
|

Местный
  
Группа: Свой
Сообщений: 222
Регистрация: 9-06-07
Пользователь №: 28 317

|
Цитата(ReAl @ Nov 18 2008, 21:27)  Против uint32_t ничего не имею  Однако плавучка тоже имеет право на жизнь (вкупе с uint16_t или uint32_t - не важно). Но "случаи бывают всякие" и я бы не стал с порога отвергать задержки по месту и аккуратно применённый float, который заканчивает свою жизнь в compile-time. Дадада  Согласен. Но только так - когда флоат существует только в исходнике. Любые попытки задавать длину задержки в рантайме приведут к неприятным последствиям. Что касается задержек, то при всей моей неприязни к ним, я не знаю, как к примеру выдерживать интервалы для софтового 1-wire, кроме как с их помощью. Но и там они все задаются во время компиляции.
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|