Урок 23
Часть 7
Собираем часы на DS1307 и LED индикаторе
В прошлой части нашего занятия мы занимались в основном проблемой отображения поочерёдно всех показаний на индикаторе в нужное время и в нужном виде. Это нам, как мы помним, удалось.
Теперь перед нами стоит ещё одна нелёгкая задача — возможность редактировать значения в регистрах микросхемы реального времени DS1307. Редактировать мы будем с помощью кнопок, вернее даже с помощью одной кнопки, так как у нас больше и ножек портов-то собственно не осталось. Ну и хорошо, что так, заодно и однокнопочный интерфейс изучим немного.
Посмотрим фрагмент схемы, где у нас подключена кнопка, остальное у нас ничего не меняется (нажмите на картинку для увеличения изображения)
Мы видим, что кнопка наша подключена к ножке PB5.
Чтобы нам определить долгое нажатие, нам либо нужно будет какой-то длинный цикл организовывать, что очень расточительно, либо подключить оставшийся последний нулевой таймер, ну и третий вариант — переходить на другой контроллер, в котором больше пинов. Ну будем стараться всё-таки оставить Atmega8 и задействовать таймер.
Первый таймер у нас занят динамической индикацией, второй таймер занят ШИМом, остается только нулевой. Можно конечно сделать другой счетчик в первом таймере, может в будущем мы так и будем делать, но нам всё же интересно попробовать и нулевой таймер.
У нулевого восьмибитного таймера существует только один режим — прерывание вызывается только по переполнению, причём это переполнение никак не регулируется и считает таймер только до 255. Но я думаю, нам этого хватит, точность нам не нужна.
Настраивается у таймера практически только делитель
Создадим ещё одну библиотеку специально для кнопки, так как чем меньше кнопок. тем больше кода. Состоять она будет из пары файлов — button.c и button.h
Подключим её в main.h
#include «DS18B20.h»
#include «button.h»
Также для порядка давайте все манипуляции в main() с настройкой портов оформим в функцию port_ini, которую создадим в самом начале главного модуля
//———————————————
void port_ini(void)
{
DDRD = 0xFF;
DDRB = 0b00011111;//3 ножки оставляем на вход для кнопок
PORTD = 0b11111111;
DDRC &= ~(1<<DDRC3);
PORTC &= ~(1<<PORTC3);
PORTB = 0b00100000;
}
//———————————————
А вместо всего этого в main() добавим вызов данной функции
timer_ini();
port_ini();
init_PWM_timer();
В файле button.c добавим инициализацию нашего таймера, взяв за основу готовую функцию нашего первого таймера и немного подправив её
#include «button.h»
//———————————————
void init_button_timer(void)
{
TIMSK |= (1<<TOIE0); //устанавливаем бит разрешения прерывания 0-ого счетчика
TCCR0 |= (1<<CS02)|(1<<CS00); // устанавливаем предделитель 1024
}
Также пропишем прототип в хедер-файле для данной функции и вызовем её в main()
init_PWM_timer();
init_button_timer();
ADC_Init();
Теперь в button.c добавим обработчик прерывания от таймера
ISR (TIMER0_OVF_vect)
{
}
Добавим переменную в main.h
unsigned int adc_value,adc_counter,adc_tmp;
unsigned int button_cnt;
Подхватим её в button.c
#include «button.h»
//———————————————
extern unsigned int button_cnt;
И проинициализируем её в main()
unsigned char clockmode=MODETIMEVIEW;//обычный режим показаний дисплея
button_cnt = 0; //измеритель времени нажатия кнопки
А в button.c в обработчике таймера будем её наращивать циклически, пока просто чтобы проверить, работает ли таймер
ISR (TIMER0_OVF_vect)
{
if(button_cnt<10000) button_cnt++;
else button_cnt=0;
}
Всё отображение пока в бесконечном цикле в main() закомментируем (строки где встречается ledprint()), а вместо этого выведем значение нашего счётчика
ledprint(button_cnt,MODETIMEVIEW);
Попробуем собрать код и прошить контроллер.
Всё работает, циферки бегут, кому интересно посмотреть, как они бегут, посмотрите видеоверсию урока, размещённую внизу страницы.
Вернём всё в бесконечном цикле на место, убрав комментарий и отображение счётчика.
Добавим ещё в главном модуле режимы для редактирования. Пока добавим только два, а остальные допишем позже
#define MODEDAYVIEW 103
//———————————————
#define MODENONEEDIT 0
#define MODEMINEDIT 1
Самый первый режим — это режим обычный, когда часы просто ходят и мы ничего не редактируем, а второй, когда редактируем значение минут в регистре микросхемы.
То же самое напишем в button.c
#include «button.h»
//———————————————
#define MODENONEEDIT 0
#define MODEMINEDIT 1
Также нам нужно будет определённый режим проинициализировать при старте, для этого должна быть ещё и переменная, которая должна быть ещё и видна на только в одном главном модуле. Поэтому объявим её глобально в main.h
unsigned int button_cnt;
unsigned char clockeditmode;
И подхватим в button.c
extern unsigned int button_cnt;
extern unsigned char clockeditmode;
Ну и проинициализируем в main()
unsigned char clockmode=MODETIMEVIEW;//обычный режим показаний дисплея
clockeditmode=MODENONEEDIT;
Теперь в обработчике перывания от таймера в button.c мы попытаемся узнать, не нажата ли кнопка.
Пока мы будем кнопку отслеживать при условии, что мы ничего не редактируем, а просто часы у нас ходят и мы решили отредактировать что-то
ISR (TIMER0_OVF_vect)
{
if(clockeditmode==MODENONEEDIT)
{
}
Для того, чтобы нам поудобнее писалось и читалось, добавим дефайны для ножек порта, к которому подключена кнопка в файл button.h
#include «main.h»
//—————————————-
#define BUTTONPORT PORTB
#define BUTTONPORT1 PORTB5
#define BUTTONPIN PINB
#define BUTTONPIN1 PINB5
#define BUTTONDDR DDRB
#define BUTTONDDR1 DDRB5
Добавим код отслеживания нажатой кнопки в обработчик прерывания от таймера в button.c
if(clockeditmode==MODENONEEDIT)
{
if((!(BUTTONPIN&(1<<BUTTONPIN1)))&&(buttonstat==0))//Кнопка 1 нажата
{
}
}
И теперь мы, если у нас кнопка будет нажата, то мы будем счётчик наращивать, а если нет, то обнулять
if((!(BUTTONPIN&(1<<BUTTONPIN1)))&&(buttonstat==0))//Кнопка 1 нажата
{
button_cnt++;
}
else button_cnt=0;
Ниже код удалим.
Также в button.c настроим ножку для кнопки на вход, добавив для этого функцию
void init_button_port(void)
{
BUTTONDDR &= ~(1<<BUTTONDDR1);/ножка кнопоки на вход
BUTTONPORT |= (1<<BUTTONPORT1);//подтянем резисторы к лапкам кнопок
}
Добавим для данной функции прототип в хедер-файле и вызовем её в main()
init_PWM_timer();
init_button_port();
init_button_timer();
Проверим код на работоспособность, собрав его и прошив контроллер.
Теперь наш счётчик набирает значение только по нажатию и удержанию кнопки. Ну думаю 60 нам хватит, чтобы считать нажатие долгим.
Продолжим код в обработчике от таймера в файле button.c, не выходя из условия, когда мы ничего не редактируем
else button_cnt=0;
if(button_cnt>60) clockeditmode=MODEMINEDIT;
Опять проверим наш код, собрав его и прошив контроллер.
У нас всё работает. Если мы держим кнопку и отпустим её, когда она ещё не досчитает до 60, то значение сбросится, а если дождёмся. когда досчитает, то у нас значение счетчика остается, равным 61, и код уже больше на кнопку не реагирует.
Теперь нам нужно по кнопке не только менять режим, но ещё и управлять отображением на дисплее, чтобы было видно, что мы собрались редактировать что-то. Например, если мы редактируем минуты, то у нас два правых разряда должны мигать. Мигание нам также будет обеспечивать выход SQW микросхемы.
Пока потренеруемся и попробуем просто помигать самым младшим разрядом. Для этого мы будем время от времени подавать единицу на его анод, хотя у нас он и анод, то тем не менее за счёт транзисторной инверсии единица у нас будет тушить разряд. Допишем код в led.c в функции обработчика таймера
if(n_count==0)
{
PORTB&=~(1<<PORTB0);PORTB|=(1<<PORTB1)|(1<<PORTB2)|(1<<PORTB4);
if (!(PINC&0b00001000)) PORTB|=(1<<PORTB0);
Соберём код, прошьём контроллер — у нас мигает последний разряд. Уже хорошо.
А чтобы нам сделать зависимость от режима редактирования, нам нужно будет объявить нашу глобальную переменную и дефайны в led.c
#define MODEDAYVIEW 103
//———————————————
#define MODENONEEDIT 0
#define MODEHOUREDIT 1
. . . . . .
extern unsigned char clockeditmode;
Ну и расширим теперь наше условие в обработчике
if ((clockeditmode==MODEMINEDIT)&&(!(PINC&0b00001000))) PORTB|=(1<<PORTB0);
Собрав код и прошив контроллер, мы с вами можем убедиться, что разряд у нас теперь замигает только тогда, когда мы нажмём на кнопку и подержим её до 60.
Таким же образом мы заставим замигать следующий разряд
if(n_count==1)
{
PORTB&=~(1<<PORTB1);PORTB|=(1<<PORTB0)|(1<<PORTB2)|(1<<PORTB4);segchar(R2);
if ((clockeditmode==MODEMINEDIT)&&(!(PINC&0b00001000))) PORTB|=(1<<PORTB1);
Опять проверим, также собрав код и прошив контроллер. У нас теперь по долгому нажатию мигают два младших разряда.
Дальше мы продолжим работать с кнопкой в следующей части занятия, работы у нас ещё много.
Предыдущая часть Программирование МК AVR Следующая часть
Программатор, модуль RTC DS1307 с микросхемой памяти и индикатор можно приобрести здесь:
Программатор (продавец надёжный) USBASP USBISP 2.0
Модуль RTC DS1307 с микросхемой памяти
Семисегментный чертырехразрядный индикатор красный с общим анодом 10 шт
Смотреть ВИДЕОУРОК (нажмите на картинку)
Функцию init_button_port() можно было не писать, тк ножка PB5 объявлена на вход и подтянута уже в port_init(), но можно оставить на случай, если возникнет потребность привязать кнопку к другой ножке. Спасибо за интересный материал.
Добавим код отслеживания нажатой кнопки в обработчик прерывания от таймера в button.c
if(clockeditmode==MODENONEEDIT)
{
if((!(BUTTONPIN&(1<<BUTTONPIN1)))&&(buttonstat==0))//Кнопка 1 нажата
{
}
}
В этой части кода лишнее &&(buttonstat==0)).
Добрый день! Вам можно ознакомится со стратьем Шалыто «Конечные автоматы» нагугливается легко первая статья при поиске.