AVR Раздел по микроконтроллерам компании Atmel - AVR / ATtiny / ATmega / ATMega128 / ATxmega, вопросы по программированию в AVR studio и все, относящееся к AVR... |
24.10.2023, 12:03
|
|
Временная регистрация
Регистрация: 07.03.2019
Сообщений: 87
Сказал спасибо: 11
Сказали Спасибо 0 раз(а) в 0 сообщении(ях)
|
Таймер на AVR ATMEGA128
Доброго времени суток!
Прошу помощи. Занялся изучением мк. Попытался написать таймер обратного отсчета.
Суть следующая написал исходник где переменные должны меняться по достижению определенного значения. Важный момент, таймер МК настроен на децесекунды, и итерации кнопкой проходят по 10 децесекунд, то есть +1 секунда
Но когда таймер получает первую минуту, то при дальнейшей итерации - увеличение минут не происходит, а продолжается наращивание секунд, уверен что ошибся в написании функции. ПРошу помощи как лучше реализовать данную функцию.
Видео поведения таймера прикладываю
Код:
|
#include ‹avr/io.h›
#define F_CPU 8000000UL
#include ‹avr/io.h›
#include ‹util/delay.h›
#include ‹avr/interrupt.h›
#include "n5110.h"
//-------------------------------------------------------------------------------------------------------------------------
unsigned int d_min = 0, min = 0, d_sec = 0, sec = 0, dsec = 0, z = 0, desec = 0; // Создаю переменные для разрядов на дисплее (тысячи, сотни, десятки, еденицы)
//-------------------------------------------------------------------------------------------------------------------------
void chislo (unsigned int tiki) // Создаю функцию для дробления 5х значного числа на разряды
{
d_min = tiki/10000; // десятки минут
min = tiki/1000; // минуты
d_sec = tiki%1000/100; // сотни
sec = tiki%100/10; // десятки
dsec = tiki%10; // единицы
if (dsec==10){sec++; dsec=0;}
if (sec==10){d_sec++; sec=0;}
if (d_sec==6){min++; d_sec=0;}
if (min==10){d_min++; min=0;}
}
//-------------------------------------------------------------------------------------------------------------------------
void timer1_ini(void) //настройка таймера счетчика первого
{
TCNT1 = 0; // обнуляем счетный регистр
OCR1A = 3125; // Указываем число для сравнения в регистр сравнения
TIMSK |= (1‹‹OCIE1A); // Включаем бит разрешения сравнения с OCR1A
}
//-------------------------------------------------------------------------------------------------------------------------
void timer1_start(void) //Запуск первого таймера счетчика с предделителем на 256 и разрешением сравнения
{
TCCR1B |= (1‹‹WGM12)|(1‹‹CS12);
}
//-------------------------------------------------------------------------------------------------------------------------
void timer1_stop(void) //Остановка первого таймера
{
TCCR1B &= ~((1‹‹WGM12)|(1‹‹CS12));
}
//-------------------------------------------------------------------------------------------------------------------------
ISR(TIMER1_COMPA_vect) // Создаю макрос тиканья таймера
{
if (--desec‹=0)
{
desec = 0;
PORTF |= (1‹‹4);
TCCR1B &=~(1‹‹CS12);
}
}
//-------------------------------------------------------------------------------------------------------------------------
void button_on(void) // Активирую порты D и Е на выход, что бы использовать кнопки
{
DDRD = 0x00;
PORTD |= (1‹‹6) | (1‹‹7);
DDRE |= (0‹‹6) | (0‹‹7);
PORTE |= (1‹‹6) | (1‹‹7);
}
//-------------------------------------------------------------------------------------------------------------------------
void rele(void) // Активирую Порт F на выход, для того что бы активировать реле
{
DDRF |= (1‹‹4);
PORTF = 0x00;
}
//-------------------------------------------------------------------------------------------------------------------------
void displey_port(void) // Активирую Порт B на выход, для того что бы активировать реле
{
DDRB = 0xff;
PORTB = 0x00;
}
//-------------------------------------------------------------------------------------------------------------------------
int main(void)
{
timer1_ini();
timer1_start();
timer1_stop();
button_on();
rele();
displey_port();
sei(); // глобальные прерывания включены
Lcd_init();
Lcd_clear();
Lcd_update();
LcdContrast(62);
while (1)
{
chislo(desec);
if (~PIND&(1‹‹6))
{
desec+=10;
if(desec›=66526) desec = 66526;
_delay_ms(150);
}
if (~PIND&(1‹‹7))
{
desec-=10;
if(desec‹=0) desec = 0;
_delay_ms(150);
}
if (~PINE&(1‹‹6))
{
timer1_start();
PORTB &= ~(1‹‹4);
_delay_ms(150);
}
if (~PINE&(1‹‹7))
{
timer1_stop();
PORTB |= (1‹‹4);
_delay_ms(150);
}
Lcd_clear();
char buff[20];
itoa(d_min, buff, 10);
Lcd_print(0, 1, FONT_2X,(unsigned char *)buff);
itoa(min, buff, 10);
Lcd_print(2, 1, FONT_2X,(unsigned char *)buff);
Lcd_prints(4, 1, FONT_2X,(unsigned char *)PSTR(":"));
itoa(d_sec, buff, 10);
Lcd_print(6, 1, FONT_2X,(unsigned char *)buff);
itoa(sec, buff, 10);
Lcd_print(8, 1, FONT_2X,(unsigned char *)buff);
Lcd_prints(10, 1, FONT_2X,(unsigned char *)PSTR(":"));
itoa(dsec, buff, 10);
Lcd_print(12, 1, FONT_2X,(unsigned char *)buff);
Lcd_update();
}
} |
Последний раз редактировалось Devil Byte; 24.10.2023 в 12:11.
|
|
|
|
24.10.2023, 13:53
|
|
Гражданин KAZUS.RU
Регистрация: 17.06.2008
Адрес: Украина
Сообщений: 731
Сказал спасибо: 363
Сказали Спасибо 807 раз(а) в 379 сообщении(ях)
|
Re: Таймер на AVR ATMEGA128
Сообщение от Devil Byte
|
уверен что ошибся в написании функции
|
Такие функции отлаживаются в симуляторе Atmel ( или Microchip ) Studio.
Симулятор можно выбрать в свойствах проекта:
Для этого убрать все, что не касается непосредственно функции, и разбираться с ней.
Например, задать такое desec, в котором заранее известно количество часов/минут/секунд. Затем менять desec и анализировать в симуляторе работу функции ПОШАГОВО, и найти, где она работает некорректно.
Например, main() для отладки функции chislo(...) может выглядеть так:
PHP код:
|
int main (void)
{
desec = ‹что-то-предопределенное›;
chislo(desec); // убедиться, что функция chislo() работает корректно
itoa(d_min, buff, 10); // убедиться, что в буфере действительно ожидаемое значение
desec = ‹еще-что-то-предопределенное›;
chislo(desec); // убедиться, что функция chislo() работает корректно
itoa(d_min, buff, 10); // убедиться, что в буфере действительно ожидаемое значение
// и т.д.
}
|
Потом уже переходить к железу и вовлекать периферию, таймеры, кнопки и т.д.
P.S.
навскидку:
1) desec объявить volatile, раз уж оно изменяется в обработчике прерывания
2) как отработает if (--desec‹=0) в обработчике прерывания, если desec будет нулевым?
3) пока достаточно
|
|
|
|
24.10.2023, 14:14
|
|
Временная регистрация
Регистрация: 07.03.2019
Сообщений: 87
Сказал спасибо: 11
Сказали Спасибо 0 раз(а) в 0 сообщении(ях)
|
Re: Таймер на AVR ATMEGA128
Сообщение от j-Roger
|
3) пока достаточно
|
Извините, у меня практики неделя, я занимаюсь сам. и пока очень далек..
может стоит сделать так
например мне конвертировать время исходя из того что в секунде 10 децесекунд, в минуте 60 секунд и так далее.
просто я не понимаю как мне это написать и обрабатывать это сразу в переменных?
|
|
|
|
24.10.2023, 14:32
|
|
Супер-модератор
Регистрация: 13.03.2004
Адрес: Minsk
Сообщений: 2,381
Сказал спасибо: 1,962
Сказали Спасибо 1,328 раз(а) в 578 сообщении(ях)
|
Re: Таймер на AVR ATMEGA128
Не вчитывался особо, но сходу видно следующее
1) desec у вас int, то есть 16 бит. 66526 это бОльше 16 бит. Компилятор должен был вас там предупредить. 0xFFFF это 65535
2) если счет идет в dsec, то зачем в chislo - где вы раскладываете двоичное с минуты-секунды - проверки типа
Код:
|
if (dsec==10){sec++; dsec=0;} |
dsec вычислялось как остаток от деления на 10... не может оно быть == 10!
__________________
[ жизнь приятна и красива, если выпить литр пива ]
Последний раз редактировалось nml; 24.10.2023 в 15:52.
|
|
|
|
24.10.2023, 15:03
|
|
Временная регистрация
Регистрация: 07.03.2019
Сообщений: 87
Сказал спасибо: 11
Сказали Спасибо 0 раз(а) в 0 сообщении(ях)
|
Re: Таймер на AVR ATMEGA128
Сообщение от nml
|
dsec вычислялось как остаток от деления на 10... не может оно быть == 10!
|
Почему не может? просто она становится равна 10 и тут же обнуляется
|
|
|
|
24.10.2023, 15:53
|
|
Супер-модератор
Регистрация: 13.03.2004
Адрес: Minsk
Сообщений: 2,381
Сказал спасибо: 1,962
Сказали Спасибо 1,328 раз(а) в 578 сообщении(ях)
|
Re: Таймер на AVR ATMEGA128
Сообщение от Devil Byte
|
просто она становится равна 10
|
Поясните, где она может стать 10. Остаток от деления на 10 не может быть больше 9.
__________________
[ жизнь приятна и красива, если выпить литр пива ]
|
|
|
|
25.10.2023, 05:26
|
|
Гражданин KAZUS.RU
Регистрация: 17.06.2008
Адрес: Украина
Сообщений: 731
Сказал спасибо: 363
Сказали Спасибо 807 раз(а) в 379 сообщении(ях)
|
Re: Таймер на AVR ATMEGA128
Если еще актуально.
Сравнить с оригинальным исходником и вставить оттуда то, чего не хватает. Или же наоборот -
отредактировать оригинал так, чтобы там оказались изменения из файла (во вложении, расширение изменено на *.txt )
main_.txt
Содержимое файла из вложения ( не спрятан под спойлер; с экрана не копи-пастить во избежание )
PHP код:
|
#include ‹avr/io.h›
#include ‹avr/sleep.h›
#include ‹avr/cpufunc.h›
#include ‹avr/interrupt.h›
//--------------------------------------------------------
// Переменные (десятки минут, единицы минут, десятки секунд,
// единицы секунд, десятые доли секунды)
unsigned char d_min = 0, min = 0, d_sec = 0, sec = 0, dsec = 0;
// Счетчик десятых долей секунды
volatile unsigned int desec = 0;
//--------------------------------------------------------
// Пересчет тиков в минуты, секунды и десятые доли секунд
// Для ограничения сверху 59 мин 59 сек величина tiki не должна превышать 59990
void chislo (unsigned int tiki)
{
unsigned int seconds_total = tiki / 10; // всего секунд в tiki
dsec = tiki % 10; // десятые доли секунды (0..9)
unsigned char minutes = seconds_total / 60; // минуты (0..99)
unsigned char seconds = seconds_total % 60; // секунды (0..59)
d_min = minutes / 10; // десятки минут (0..9)
min = minutes % 10; // единицы минут (0..9)
d_sec = seconds / 10; // десятки секунд (0..5)
sec = seconds % 10; // единицы секунд (0..9)
}
ISR(TIMER1_COMPA_vect)
{
if (--desec == 0) {
// остановить таймер
// ..............
// вставить из оригинального кода то, что требуется
// ..............
}
}
char buff[20];
int main(void)
{
// временная переменная для сохранения состояния прерываний
unsigned char keep_interrupt_status;
// временная переменная для счетчика тиков
unsigned int safe_desec;
desec = 590; // 590 = 59 секунд для примера
// ..............
// вставить из оригинального кода то, что требуется
// ..............
while (1) {
keep_interrupt_status = SREG; // сохранить состояние прерываний
cli(); // запрет прерываний
safe_desec = desec; // АТОМАРНО копируем счетчик тиков во временную переменную
SREG = keep_interrupt_status; // восстановить состояние прерываний
chislo(safe_desec);
// ..............
// вставить из оригинального кода то, что требуется
// ..............
// Формирование строки для вывода на дисплей
buff[0] = d_min + '0'; // десятки минут (0..9) ASCII
buff[1] = min + '0'; // единицы минут (0..9) ASCII
buff[2] = ':'; // разделитель
buff[3] = d_sec + '0'; // десятки секунд (0..5) ASCII
buff[4] = sec + '0'; // единицы секунд (0..9) ASCII
buff[5] = ':'; // разделитель
buff[6] = dsec + '0'; // десятые доли секунды (0..9) ASCII
buff[7] = '\0'; // NUL-терминатор строки
// ASCIIZ-строка готова к выводу на дисплей
// ..............
// вставить из оригинального кода то, что требуется
// ..............
}
}
|
не забываем касательно переменной desec:
0) обеспечивать АТОМАРНЫЙ доступ для ее чтения и записи
1) удерживать ее в пределах 0..59990 ( 99 мин 59 сек ) любым способом )))
P.S.
Файл из вложения, если его или его содержимое вставить в проект, можно пошагово погонять прямо в симуляторе Atmel Studio ( в конфигурации Debug ) в таком как есть виде - никакая периферия не используется, поэтому достаточно в главном цикле поиграться с переменной desec ( счетчиком тиков ) и понаблюдать за содержимым текстового буфера buff.
Успехов!
Последний раз редактировалось j-Roger; 25.10.2023 в 07:57.
Причина: Форматирование
|
|
|
|
26.10.2023, 11:34
|
|
Временная регистрация
Регистрация: 07.03.2019
Сообщений: 87
Сказал спасибо: 11
Сказали Спасибо 0 раз(а) в 0 сообщении(ях)
|
Re: Таймер на AVR ATMEGA128
j-Roger, вставил и все работает! буду разбираться в чем я ошибался. Спасибо!
Последний раз редактировалось mike-y-k; 26.10.2023 в 12:44.
Причина: 7.15
|
|
|
|
26.10.2023, 15:02
|
|
Гражданин KAZUS.RU
Регистрация: 17.06.2008
Адрес: Украина
Сообщений: 731
Сказал спасибо: 363
Сказали Спасибо 807 раз(а) в 379 сообщении(ях)
|
Re: Таймер на AVR ATMEGA128
Вкратце упомянем о том, что в частности для avr-gcc есть библиотечные средства для атомарного доступа.
Находится это все в файле ‹util/atomic.h›, который хорошо откомментирован.
С учетом вышесказанного и вышенаписанного, блок кода
PHP код:
|
...
keep_interrupt_status = SREG; // сохранить состояние прерываний
cli(); // запрет прерываний
safe_desec = desec; // АТОМАРНО копируем счетчик тиков во временную переменную
SREG = keep_interrupt_status; // восстановить состояние прерываний
...
|
можно с тем же результатом переписать так ( и попутно выбросить переменную для сохранения состояния прерываний ) :
PHP код:
|
#include ‹util/atomic.h›
...
ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
{
safe_desec = desec; // АТОМАРНО копируем счетчик тиков во временную переменную
}
chislo(safe_desec);
...
|
|
|
|
|
18.01.2024, 11:31
|
|
Временная регистрация
Регистрация: 07.03.2019
Сообщений: 87
Сказал спасибо: 11
Сказали Спасибо 0 раз(а) в 0 сообщении(ях)
|
Re: Таймер на AVR ATMEGA128
j-Roger, Приветствую! Подскажи пожалуйста - как добавить в код возможность увеличивать нужные доли времени, типа, секунды, десятки секунд и т.д?
Нужно создать функцию выбора переменной с определенным значением для каждого промежутка времени?? или есть проще решение? ну или создать счетчик нажатия отдельной кнопки, и например если счетчик равен 1, iterator =10 , и соответственно в в кнопке увеличения написать milis+=iterator; Или оператор switch использовать ?
Нажмите, чтобы открыть спойлер
PHP код:
|
//------------------------------------------------------------------------------------------------------------------------- // Создаю переменные для получения времени volatile uint32_t dmilliseconds = 0; volatile uint32_t seconds_on_tiki = 0; volatile uint32_t milliseconds = 0; volatile uint32_t seconds = 0; volatile uint32_t tens_of_seconds = 0; volatile uint32_t minutes = 0; volatile uint32_t tens_of_minutes = 0; volatile uint32_t time_in_sec = 0; volatile uint32_t time_in_thensec = 0; volatile uint32_t time_in_min = 0; volatile uint32_t time_in_thenmin = 0; unsigned int milis = 0; volatile uint8_t timer_state = 0; //------------------------------------------------------------------------------------------------------------------------- void time (unsigned int tiki) // Создаю функцию для дробления 5х значного числа на разряды { seconds_on_tiki = tiki / 10; // всего секунд в tiki dmilliseconds = tiki % 10; // десятые доли секунды (0..9) minutes = seconds_on_tiki / 60; // минуты (0..99) seconds = seconds_on_tiki % 60; // секунды (0..59) tens_of_minutes = minutes /10; //Десятки минут time_in_min = minutes %10; // минуты tens_of_seconds = seconds/10; time_in_sec = seconds %10; }
void display_timer_state(void) { if (timer_state == 1) { // Timer is ON Lcd_print(0, 3, FONT_1X, (unsigned char *)PSTR("timer ON"));
} else { // Timer is paused Lcd_print(0, 3, FONT_1X, (unsigned char *)PSTR("timer pause")); } } //------------------------------------------------------------------------------------------------------------------------- void timer1_ini(void) //настройка таймера счетчика первого { TCNT1 = 0; // обнуляем счетный регистр OCR1A = 3125; // Указываем число для сравнения в регистр сравнения TIMSK |= (1‹‹OCIE1A); // Включаем бит разрешения сравнения с OCR1A } //------------------------------------------------------------------------------------------------------------------------- void timer1_start(void) //Запуск первого таймера счетчика с предделителем на 256 и разрешением сравнения { TCCR1B |= (1‹‹WGM12)|(1‹‹CS12); } //------------------------------------------------------------------------------------------------------------------------- void timer1_stop(void) //Остановка первого таймера { TCCR1B &= ~((1‹‹WGM12)|(1‹‹CS12)); } //------------------------------------------------------------------------------------------------------------------------- ISR(TIMER1_COMPA_vect) // Создаю макрос тиканья таймера { if (--milis==0) { TCCR1B &=~(1‹‹CS12); milis = 0; //PORTD |= (1‹‹0);
} } //------------------------------------------------------------------------------------------------------------------------- void button_on(void) // Активирую порты D и Е на выход, что бы использовать кнопки { DDRD = 0x00; PORTD |= (1‹‹0) | (1‹‹1) | (1‹‹2) | (1‹‹3); } //------------------------------------------------------------------------------------------------------------------------- void displey_start(void) // Активирую Порт B на выход, для того что бы активировать дисплей { DDRB &= ~((1‹‹PINB0) | (1‹‹PINB1) | (1‹‹PINB2) | (1‹‹PINB3) | (1‹‹PINB4) | (1‹‹PINB5)); PORTB = 0x00; Lcd_init(); Lcd_clear(); Lcd_update(); LcdContrast(62); }
//------------------------------------------------------------------------------------------------------------------------- char buff[20]; int main(void) { // временная переменная для сохранения состояния прерываний unsigned char keep_interrupt_status; // временная переменная для счетчика тиков unsigned int safe_desec; button_on(); displey_start(); timer1_ini(); timer1_start(); // глобальные прерывания включены timer1_stop(); sei(); while (1) { keep_interrupt_status = SREG; // сохранить состояние прерываний cli(); // запрет прерываний safe_desec = milis; // АТОМАРНО копируем счетчик тиков во временную переменную SREG = keep_interrupt_status; // восстановить состояние прерываний
time(safe_desec);
// Формирование строки для вывода на дисплей buff[0] = tens_of_minutes + '0'; // десятки минут (0..9) ASCII buff[1] = time_in_min + '0'; // единицы минут (0..9) ASCII buff[2] = ':'; // разделитель buff[3] = tens_of_seconds + '0'; // десятки секунд (0..5) ASCII buff[4] = time_in_sec + '0'; // единицы секунд (0..9) ASCII buff[5] = ':'; // разделитель buff[6] = dmilliseconds + '0'; // десятые доли секунды (0..9) ASCII buff[7] = '\0'; // NUL-терминатор строки if (~PIND&(1‹‹0)) { milis+=600;
_delay_ms(150); } if (~PIND&(1‹‹1)) { time(milis); // Обновляем переменные перед запуском таймера if (tens_of_minutes != 0 || time_in_min != 0 || tens_of_seconds != 0 || time_in_sec != 0 || dmilliseconds != 0) { // Проверяем, что хотя бы одна из переменных не равна нулю milis-=1; } if(milis‹=0) milis = 0; _delay_ms(150); } if (~PIND&(1‹‹2)) { time(milis); // Обновляем переменные перед запуском таймера if (tens_of_minutes != 0 || time_in_min != 0 || tens_of_seconds != 0 || time_in_sec != 0 || dmilliseconds != 0) { // Проверяем, что хотя бы одна из переменных не равна нулю timer1_start(); } _delay_ms(150); } if (~PIND&(1‹‹3)) { timer1_stop(); _delay_ms(150); }
Lcd_clear(); char buff[20]; itoa(tens_of_minutes, buff, 10); Lcd_print(0, 0, FONT_1X,(unsigned char *)buff); itoa(time_in_min, buff, 10); Lcd_print(1, 0, FONT_1X,(unsigned char *)buff); Lcd_prints(2, 0, FONT_1X,(unsigned char *)PSTR(":")); itoa(tens_of_seconds, buff, 10); Lcd_print(3, 0, FONT_1X,(unsigned char *)buff); itoa(time_in_sec, buff, 10); Lcd_print(4, 0, FONT_1X,(unsigned char *)buff); Lcd_prints(5, 0, FONT_1X,(unsigned char *)PSTR(":")); itoa(dmilliseconds, buff, 10); Lcd_print(7, 0, FONT_1X,(unsigned char *)buff); Lcd_update(); _delay_ms(150);
} }
|
еще хочется перейти на не блокирующие задержки. Может стоит использовать нулевой таймер счетчик? например так
Нажмите, чтобы открыть спойлер
PHP код:
|
ISR(INT0_vect) { // Изменение значения переменной iterator в зависимости от текущего значения
if (count==1) iterator = 10; if (count==2) iterator = 100; if (count==3) iterator = 600; if (count==4) iterator = 6000; if (count›=5) {count = 0; iterator = 1;} }
|
Последний раз редактировалось Devil Byte; 18.01.2024 в 21:55.
|
|
|
|
Ваши права в разделе
|
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения
HTML код Выкл.
|
|
|
Часовой пояс GMT +4, время: 22:28.
|
|