Часы на микроконтроллере AVR

13/12/2010 - 20:46
 
 
 
   В качестве примера использования событийной системы на таблице я выбрал такой популярный девайс как часы на микроконтроллере. Чтобы пример был понятен как можно более широкому кругу людей функционал часов ограничивается отображением и установкой времени. Программа носит учебный характер и не претендует на оптимальность.





Схема 

Часы на микроконтроллере AVR - схема

Написание программы

   Итак, попытаюсь сформулировать, как я кодил эту программу. 
   Программа учебная, а значит, все должно быть по минимуму. Отображение часов и минут, двоеточие, моргающее раз в секунду, и возможность это самое время выставить. 
   Из аппаратных средств нужен микроконтроллер 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;
}
 
И последним этапом было написание кода обработчиков и отладка программы. 
Вот собственно и все. 

Comments   

# Indigo 2010-12-16 16:06
Пользовательски е задержки на __delay_cycles, по крайней мере в IAR, уже лет 10 делают во float... Посчитайте насколько точнее для _WaitUs(500) для кварца 14745600 Гц.


#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 ))
Reply | Reply with quote | Quote
# Pashgan 2010-12-19 19:33
Спасибо. Возьму на заметку.
Reply | Reply with quote | Quote
# Олег 2010-12-27 23:26
А на сколько увеличится код программы при использовании float в IAR ?
Уж пусть будет погрешность иногда.
Reply | Reply with quote | Quote
# САБ 2010-12-28 09:00
При включенной оптимизации подобные константные вычисления производятся на этапе компиляции.
Reply | Reply with quote | Quote
# foxit 2010-12-16 19:40
неплохо бы добавить коррекцию часов
Reply | Reply with quote | Quote
# Pashgan 2010-12-19 19:34
Это в следующей версии. Если доделаю. :-) Вообще собираюсь еще продолжение по событийной системе написать. Скорее всего там в качестве примера будет кодовый замок.
Reply | Reply with quote | Quote
# foxit 2010-12-19 20:19
буду ждать
Reply | Reply with quote | Quote
# Anton_ 2011-01-26 13:55
Я "прикрутил" событийную систему к ПИК24 и к ARM Кейл. Массив адресов функций в V1.0 и таблицу в V2.0 (с использованием состяний) я разместил в памяти данных. Насколько это повлияет на работоспособнос ть и надёжность работы алгоритма?
Reply | Reply with quote | Quote
# JoJo 2011-01-26 21:36
По идее это не должно никак повлиять на работоспособнос ть. Даже наоборот, алгоритм будет работать быстрее.
Надежность, по моему мнению, уменьшится. Ведь участок ОЗУ с таблицей можно случайно затереть.
Reply | Reply with quote | Quote
# гость 2011-02-21 06:33
Спасибо автору. Понравилась реализация, не до конца пока все понял, но наметил проект на событийной системе, думаю в нем и разберусь.
Reply | Reply with quote | Quote
# svs 2011-04-12 14:46
осваиваю протеус- для таких программ- хорош! Вопрос- в статье одна схема, в протеусе другая. Или это получается из одного исходного документа??
Reply | Reply with quote | Quote
# NihuRsebe 2011-05-21 10:10
Респект автору... Переделал свою прогу. Изменил таблицу, у меня не одна функция прописывается, а 5. Те что не нужны - Emptyfunc. Это увеличило гибкость системы..
Reply | Reply with quote | Quote
# Lion_A 2011-10-24 18:42
А взглянуть можно?
Reply | Reply with quote | Quote
# ignat 2011-11-24 03:23
ого молодец
Reply | Reply with quote | Quote
# kipalex73 2011-11-27 07:29
День Добрый!
Только начал заниматься, вроде бы и понятно. но на деле не совсем, а именно хотелось бы к этому проекту пристроить еще и термометр на ds18b20. Поковырялся вроде и компилируется, но размер выходного файла увеличивается на несколько байт. Начал разбираться и в конец запутался, для начала бы хотелось бы вывести надпись Temp справа от Clock т.е в позиции 10 , 11 (x,y). Пишу
Code:void GUI_Temp(void)
{
LCD_Goto(10, 11);
LCD_SendString("Temp");
}

Компилируется без ошибок, но на экране все по прежнему только Clock, подскажите плиз куда глянуть?
Reply | Reply with quote | Quote
# Дмитрий 2013-01-09 08:47
Здравствуйте Pashgan писать программы на си стал не так давно.Читал и переделывал ваши разработки. Осваиваю IAR. Попытался встроить код температурных датчиков в код часов, используя Ваши библиотеки, но часы не запускаются.Про грамма получилась объемистая. Подскажите пожалуйста в чем ошибка.
Reply | Reply with quote | Quote
# ASDFG123 2013-01-12 09:12
Quoting Дмитрий:
Здравствуйте Pashgan писать программы на си стал не так давно.Читал и переделывал ваши разработки. Осваиваю IAR. Попытался встроить код температурных датчиков в код часов, используя Ваши библиотеки, но часы не запускаются.Программа получилась объемистая. Подскажите пожалуйста в чем ошибка.

на форуме есть конструкция, я выкладывал, все работает, и достаточно точно http://chipenable.ru/index.php/forum/8-detskie-voprosy/3201-chasy-na-s-assinkhronym-tajmerom-atmega-8-ds18b20-7-semisegmentye-indikatory.html?start=10
Reply | Reply with quote | Quote
# Дмитрий 2013-01-13 13:47
Большое спасибо!
Reply | Reply with quote | Quote
# Дмитрий 2013-01-09 08:56
Quoting Дмитрий:
Здравствуйте Pashgan писать программы на си стал не так давно.Читал и переделывал ваши разработки. Осваиваю IAR. Попытался встроить код температурных датчиков в код часов, используя Ваши библиотеки, но часы не запускаются.Программа получилась объемистая. Подскажите пожалуйста в чем ошибка.
Я новичок в написании сообщений, поэтому подскажите, куда отправлять свой код.
Reply | Reply with quote | Quote
# Pashgan 2013-01-09 18:45
Сделай тему на форуме и туда выложи архив своего проекта.
Reply | Reply with quote | Quote

Add comment