Часы на микроконтроллере AVR
13/12/2010 - 20:46
Pavel Bobkov
В качестве примера использования событийной системы на таблице я выбрал такой популярный девайс как часы на микроконтроллере. Чтобы пример был понятен как можно более широкому кругу людей функционал часов ограничивается отображением и установкой времени. Программа носит учебный характер и не претендует на оптимальность.
Схема
Написание программы
Итак, попытаюсь сформулировать, как я кодил эту программу.
Программа учебная, а значит, все должно быть по минимуму. Отображение часов и минут, двоеточие, моргающее раз в секунду, и возможность это самое время выставить.
Из аппаратных средств нужен микроконтроллер AVR – atmega8535, символьный lcd и кнопки - для навигации по одноуровнему меню часов и установки времени. Трех штук - Enter, Up, Down вполне хватит. Кнопкой Cancel пренебрежем.
Кнопки будут опрашиваться в прерывании таймера Т0, а с помощью таймера Т1 будут формироваться секундные интервалы. Можно, конечно, использовать таймер Т2 с внешним часовым кварцем, но не суть.. точность сейчас не главное.
В прерываниях таймеров в очередь будут помещаться события. Событий будет четыре — по одному на каждую кнопку + секундный тик.
У часов будет три состояния — отображение времени, установка часов, установка минут.
Исходя из всего этого, можно расписать логику работы часов в виде таблице переходов.
Чтобы описать таблицу в Си коде, нужно определить коды состояний и событий, а также написать обработчики. Коды состояний и событий я вынес в заголовочный файл list_event.h, он используется в некоторых программных модулях.
//коды событий
#define EVENT_NULL 0
#define EVENT_KEY_UP 1
#define EVENT_KEY_DOWN 2
#define EVENT_KEY_ENTER 3
#define EVENT_SYS_TIMER 4
//коды состояний
#define STATE_NO_CHANGE 0
#define STATE_NORMAL 1
#define STATE_SET_HOUR 2
#define STATE_SET_MINUTE 3
Обработчики я поместил в модуль intrface.h, interface.c. На начальном этапе их можно заменить пустой функцией EmptyFunc (она есть в заготовке событийной системы) или создать функции-обработчики без тела, как делал я.
В модуле interface нет комментариев, но там все довольно просто - функции делают в точности то, что описано в последнем столбце таблицы переходов. Думаю, разберетесь.
//interface.h прототипы функций
void GUI_General(void);
void GUI_SelectHour(void);
void GUI_SelectMinute(void);
void GUI_IncHour(void);
void GUI_DecHour(void);
void GUI_IncMinute(void);
void GUI_DecMinute(void);
void GUI_SetTime(void);
void GUI_ChangeTime(void);
Таблицу переходов закодил в заготовке событийной системы — event-system.c
__flash struct ROW_TABLE table[] = {
//STATE EVENT NEXT STATE STATE_FUNC
{STATE_NORMAL, EVENT_KEY_ENTER, STATE_SET_HOUR, GUI_SelectHour},
{STATE_NORMAL, EVENT_SYS_TIMER, STATE_NO_CHANGE, GUI_ChangeTime},
{STATE_SET_HOUR, EVENT_KEY_ENTER, STATE_SET_MINUTE, GUI_SelectMinute},
{STATE_SET_HOUR, EVENT_KEY_UP, STATE_NO_CHANGE, GUI_IncHour},
{STATE_SET_HOUR, EVENT_KEY_DOWN, STATE_NO_CHANGE, GUI_DecHour},
{STATE_SET_MINUTE, EVENT_KEY_ENTER, STATE_NORMAL, GUI_General},
{STATE_SET_MINUTE, EVENT_KEY_UP, STATE_NO_CHANGE, GUI_IncMinute},
{STATE_SET_MINUTE, EVENT_KEY_DOWN, STATE_NO_CHANGE, GUI_DecMinute},
{ 0, 0, 0, EmptyFunc}
};
Дальше.
Сделал модуль timer.c, timer.h. Там три функции — функция инициализации таймеров Т0 и Т1 и функции прерываний. Т0 вызывает прерывания каждые 10мс, Т1 — каждую секунду.
void TIM_Init(void)
{
…..
}
//опрос кнопок
#pragma vector = TIMER0_COMP_vect
__interrupt void Timer0Comp(void)
{
BUT_Debrief(); //функция опроса кнопок из модуля button.c
}
//секундный таймер
#pragma vector = TIMER1_COMPA_vect
__interrupt void Timer1CompA(void)
{
ES_PlaceEvent(EVENT_SYS_TIMER);
}
Остальные модули buttons.c, bcd.c, lcd_lib.c у меня уже были готовы — остались от проекта «термометр на микроконтроллере». Я подключил их к проекту и настроил заголовочные файлы:
в button.h — пины, к которым подключены кнопки и порт
в lcd_lib.h – пины, к которым подключен lcd, порт, частоту кварца, опрос флага занятости и тип контроллера.
в bcd.h – переопределил функцию вывода на lcd
Далее, включил в main заголовочные файлы необходимых модулей и вписал функции инициализации — таймеров, дисплея, событийной системы и кнопок.
//модуль main
#include <ioavr.h>
#include <intrinsics.h>
#include "lcd_lib.h"
#include "buttons.h"
#include "event-system.h"
#include "interface.h"
#include "timer.h"
int main( void )
{
unsigned char event = 0;
LCD_Init();
BUT_Init();
TIM_Init();
ES_Init();
GUI_General();
__enable_interrupt();
while(1){
event = ES_GetEvent();
if (event){
ES_Dispatch(event);
}
}
return 0;
}
И последним этапом было написание кода обработчиков и отладка программы.
Вот собственно и все.
Tagged under
Comments
#define _WaitMs(q_mS) __delay_cycles \
((unsigned long int) \
(((((float)F_CP U)* \
((float)q_mS))/1000.00)+0.5))
#define _WaitUs(q_uS) __delay_cycles \
((unsigned long int) \
(((((float)F_CP U)* \
((float)q_uS))/ 1000000.00)+0.5 ))
Уж пусть будет погрешность иногда.
Надежность, по моему мнению, уменьшится. Ведь участок ОЗУ с таблицей можно случайно затереть.
Только начал заниматься, вроде бы и понятно. но на деле не совсем, а именно хотелось бы к этому проекту пристроить еще и термометр на ds18b20. Поковырялся вроде и компилируется, но размер выходного файла увеличивается на несколько байт. Начал разбираться и в конец запутался, для начала бы хотелось бы вывести надпись Temp справа от Clock т.е в позиции 10 , 11 (x,y). Пишу
Code:
void GUI_Temp(void)
{
LCD_Goto(10, 11);
LCD_SendString("Temp");
}
Компилируется без ошибок, но на экране все по прежнему только Clock, подскажите плиз куда глянуть?
на форуме есть конструкция, я выкладывал, все работает, и достаточно точно http://chipenable.ru/index.php/forum/8-detskie-voprosy/3201-chasy-na-s-assinkhronym-tajmerom-atmega-8-ds18b20-7-semisegmentye-indikatory.html?start=10
RSS feed for comments to this post