Представляем очередную схему часов на микроконтроллере Atmega16.  Для этого заблаговременно выпаял индикатор из 7-ми сегментов из нерабочей микроволновки. После чего, направился к компьютеру искать в сети схему устройства.

И вот, планомерно изучая, огромные мануалы технических текстов с характеристиками часов с многообразием полезных функциональных возможностей, среди которых: будильник, календарь, таймер и температурный датчик задаюсь вопросом. Необходимо ли мне исключительно отображение временного интервала? В моих настенных часах это уже есть, не хватает лишь подсветки.

Пришел к выводу, что функций демонстрирования временного интервала мне вполне достаточно. В этой связи схему часов на микроконтроллере, как и саму программу, следует упростить.

Во имя оптимизации схемы микросхемы, отображающие показатели времени я не брал, а выполнил все на едином микроконтроллере. Рассмотрев даташиты, мой выбор стал на ATmega16 выполненный в корпусе TQFP, он собственно у меня и имелся.

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

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

В итоге получил схему:

Принципиальная схема на часов на микроконтроллере Atmega16

Принципиальная схема часов на микроконтроллере Atmega16

 

Индикатор, снабженный единым анодом подключенным прямиком к порту микроконтроллера. Резистор каждого сегмента выдает 220 Ом, чуть меньше либо немного больше в нашем случае не критично, ночью светятся довольно ярко (Проще говоря, подойдут и менее яркие, но меньше 100 Ом ставить не советую, так как на каждую опору порта придется всего 35 мА, а по даташиту должно быть не менее 40 мА). В расчетах я ошибся. Надо существенно больше. Скорректировал по 560 Ом, что превысило требование по даташиту. Но часы работают.

На самом транзисторе воплощен детектор входящего напряжения. Он, к слову, как и кнопки, подсоединен на протяжении прерываний их всего 3.

Часы функционируют при помощи Тimer2 работающего асинхронным способом. В данном режиме таймер берет свой такт от кварца на 32768 Гц установленного извне и не прекращает работу даже в ждущем режиме, этот принцип мы и возьмём за основу при программировании устройства для работы на резервном питании. Вот собственно и все.

Семисегментный индикатор выполнен с общим анодом. Его аноды непосредственно законнектины с портом «C» микроконтроллера. На каждый сегмент был установлен резистор на 220 Ом, можно конечно и по меньше при этом свечение сегментов будет ярче. Здесь самое главное не переусердствовать, а то можно запросто спалить сам порт микронтроллера. Поэтому важно смотреть какой максимальный ток может выдержать порт.

Транзистор Q1 выполняет своего рода детектор напряжения. Также как и кнопки он подключается к линии внешних прерываний, в Atmega16 их всего 3.

Код программы часов на микроконтроллере


// ANTONLUBA 2013
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <util/delay.h>
#include "bits_macros.h"

typedef struct{ 
    unsigned char second;
    unsigned char minute;
//    unsigned char hour;
//    unsigned char date;
//    unsigned char month;
//    unsigned int year;
}time;

time t;

uint8_t segment[] = 
                //pgfedcba
                {
                0b00111111,     //0
                0b00000110,     //1
                0b01011011,     //2
                0b01001111,     //3
                0b01100110,     //4
                0b01101101,     //5
                0b01111101,     //6
                0b00000111,     //7
                0b01111111,     //8
                0b01101111      //9
                };

#define SEG_PORT PORTA  
#define SEG_DDR DDRA
#define SEG_CONN 0b01111111

#define DRV_PORT PORTC  
#define DRV_DDR DDRC
#define DRV_CONN 0b00011111

uint8_t digits[5];
uint8_t current_digit;

void BIN2BCD(uint8_t *buffer, unsigned int n)
{
        buffer[1]=0;
        while(n>=10) {buffer[1]++;n-=10;}
        buffer[0]=n;
}

uint8_t power_on = 1;           //питание есть
uint8_t m_pressed = 0, h_pressed=0; //нажаты кнопки
uint8_t button_delay;           //счетчик цикла задержки
int main(void)
{ 
    // Ждем стабилизации внешнего резонатора.
    _delay_ms(500);

    // Конфигурируем порты семисегментника
        SEG_PORT=0xFF;
        SEG_DDR=SEG_CONN;

        DRV_PORT=0x00;
        DRV_DDR=DRV_CONN;

        PORTD|= 0b1100;         //Это подтяжка для кнопок
        DDRD=0;

        SetBit(PORTB,2);        //Это подтяжка для датчика питания
        DDRB=0;
        //таймер0 для динамической индикации
        TCCR0 = (1<<CS01)|(1<<CS00);//3
        
        //внешние прерывания
        MCUCR |= (0<<ISC11)|(1<<ISC10)|(0<<ISC01)|(1<<ISC00);//прерывания INT0 и INT1 по изменению
        MCUCSR |= (1<<ISC2); //прерывание 2 по нарастанию
        GICR |= (1<<INT1)|(1<<INT0)|(1<<INT2); //включить внешние прерывания

    // Отключаем прерывания Таймера 2.
    TIMSK &= ~(_BV(TOIE2) | _BV(OCIE2));

    // Переводим Таймер 2 в асинхронный режим (тактирование от
    // часового кварцевого резонатора).
    ASSR |= _BV(AS2);

    TCNT2 = 0x00;
    TCCR2 = 0x05; //Устанавливаем коэффициент деления равным 128.
    OCR2  = 0x00;

    // Ждем готовности таймера.
    while (ASSR & (_BV(TCN2UB) | _BV(OCR2UB) | _BV(TCR2UB)));

    // Разрешаем прерывание от Таймера 2.
    TIMSK |= _BV(TOIE2);
        // И таймера0
        TIMSK |= (1<<TOIE0);

    // Разрешаем прерывания глобально.
    sei();

    while(1)
    {
        if (power_on)
        {       
                BIN2BCD(&*digits, t.minute);
                BIN2BCD(&*digits+2, t.hour);
        }
        sleep_enable();
        sleep_cpu();
        sleep_disable();

        // Не понял, что это, но без него работает
       // TCCR2=0x05;           // Write dummy value to Control register
        // Ждем готовности таймера 2.
       // while (ASSR & (_BV(TCN2UB) | _BV(OCR2UB) | _BV(TCR2UB)));
    }

    return 0;
}


ISR(TIMER2_OVF_vect) //overflow interrupt vector
{       
        //Это просто взято из аппноута, кроме суток, месяцев и лет
    if (++t.second==60)     //keep track of hours minutes
    {
        t.second=0;
        
                if (++t.minute==60)
        {
            t.minute=0;

            if (++t.hour==24)
            {
                t.hour=0;
            }
        }
    } 
}

ISR(INT0_vect)
{
h_pressed=(BitIsClear(PIND, 2)); //нажатие кнопки часов
}

ISR(INT1_vect)
{
m_pressed=(BitIsClear(PIND, 3)); //нажатие кнопки минут
}

ISR(INT2_vect)
{
ClearBit(GICR,INT2);            //отключим прерывание INT2

InvBit(MCUCSR,ISC2);            //переключим на ожидание спада
power_on=(BitIsSet(MCUCSR,ISC2));//установим переменную

if (power_on)           //если питание есть
        {
        DRV_DDR=DRV_CONN;       //включаем аноды
        TCCR0 |= (1<<CS01)|(1<<CS00);//включаем таймер0
        }
else                    //если питания нет
        {
        DRV_DDR=0;      //отключаем аноды
        TCCR0 &= ~((1<<CS01)|(1<<CS00)); //выкл таймер0
        }

SetBit(GICR,INT2);      //вкл прерывание INT2

}

ISR(TIMER0_OVF_vect)
{
        if (power_on) // если питание включено
        {
                DRV_PORT=0;     //выкл все разряды
                if (current_digit==4)   //если точечки
                {
                        if (BitIsSet(t.second,0)) //включить-выключить точечки
                        {
                                SEG_PORT=0b01111001;
                        }
                        else
                        {
                                SEG_PORT=0b01111111;
                        }
                }
                else            //если цифра
                {
                        SEG_PORT=~segment[digits[current_digit]]; //вывести цифру
                }
                SetBit(DRV_PORT, current_digit);    //включить текущий разряд
                if (++current_digit==5)         //если прошли все разряды                               
                {
                        current_digit=0;        //сначала
                        
                        if (++button_delay==20) // с задержкой
                        {
                                button_delay=0;
                                if (h_pressed)  //если нажата кнопка часов
                                {
                                        if (++t.hour==24)//корректировать часы
                        {
                                t.hour=0;
                        }
                                }
                                if (m_pressed) //если нажата кнопка минут
                                {
                                        if (++t.minute==60) //корректировать минуты
                                {
                                t.minute=0;
                                        }
                                }
                        }
                }
        }
}
Автор
Staticvoid

Администратор сайта http://www.radiodetector.ru

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

Популярные записи

Подключение кнопки к микроконтроллеру AVR

В это примере подключим и научимся обрабатывать события нажатия кнопок при помощи микроконтроллера AVR....

Распиновка наиболее популярных микроконтроллеров AVR

Ниже представлена распиновка наиболее ходовых микроконтроллеров семейства AVR.  Описание выводов, их...

Измерение температуры при помощи датчика Lm35 и ATmega 16

Всем привет! Хочу поделится одной простой схемкой для измерения температуры на микроконтроллере ATmega 16 и...

Простой звонок на микронтроллере Attiny13

В этом примере рассмотрим схему звонка на микроконтроллере Attiny13. Такой звонок можно использовать где...