AVR Урок 23. Собираем часы на DS1307 и LED индикаторе. Часть 7

 

 

 

 

Урок 23

Часть 7

 

Собираем часы на DS1307 и LED индикаторе

 

 

В прошлой части нашего занятия мы занимались в основном проблемой отображения поочерёдно всех показаний на индикаторе в нужное время и в нужном виде. Это нам, как мы помним, удалось.

Теперь перед нами стоит ещё одна нелёгкая задача — возможность редактировать значения в регистрах микросхемы реального времени DS1307. Редактировать мы будем с помощью кнопок, вернее даже с помощью одной кнопки, так как у нас больше и ножек портов-то собственно не осталось. Ну и хорошо, что так, заодно и однокнопочный интерфейс изучим немного.

Посмотрим фрагмент схемы, где у нас подключена кнопка, остальное у нас ничего не меняется (нажмите на картинку для увеличения изображения)

 

image36_0500

 

Мы видим, что кнопка наша подключена к ножке PB5.

Чтобы нам определить долгое нажатие, нам либо нужно будет какой-то длинный цикл организовывать, что очень расточительно, либо подключить оставшийся последний нулевой таймер, ну и третий вариант — переходить на другой контроллер, в котором больше пинов. Ну будем стараться всё-таки оставить Atmega8 и задействовать таймер.

Первый таймер у нас занят динамической индикацией, второй таймер занят ШИМом, остается только нулевой. Можно конечно сделать другой счетчик в первом таймере, может в будущем мы так и будем делать, но нам всё же интересно попробовать и нулевой таймер.

У нулевого восьмибитного таймера существует только один режим — прерывание вызывается только по переполнению, причём это переполнение никак не регулируется и считает таймер только до 255. Но я думаю, нам этого хватит, точность нам не нужна.

Настраивается у таймера практически только делитель

 

image37

 

Создадим ещё одну библиотеку специально для кнопки, так как чем меньше кнопок. тем больше кода. Состоять она будет из пары файлов — 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 шт

 

 

Смотреть ВИДЕОУРОК (нажмите на картинку)

 

AVR Собираем часы на DS1307 и LED индикаторе

Один комментарий на “AVR Урок 23. Собираем часы на DS1307 и LED индикаторе. Часть 7
  1. Сергей:

    Функцию init_button_port() можно было не писать, тк ножка PB5 объявлена на вход и подтянута уже в port_init(),  но можно оставить на случай, если возникнет потребность привязать кнопку к другой ножке. Спасибо за интересный материал. 

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

*