В данном проекте мы разработаем и соберем цифровые часы на микроконтроллере. В качестве камня будем использовать микроконтроллер ATMEGA16 и 6 семи сегментных индикатора (далее просто индикаторы или дисплеи). Код программы, разработанный под ATMEGA16, может с легкостью использоваться на других камнях, таких как ATMEGA32, ATMEGA8515 и другие.
Итак, наши цифровые часы работают следующим образом. Когда напряжение подано индикаторы выставляются в нули следующего вида «00:00:00» соответственно часы, минуты и секунды. Для установки времени будем использовать два кнопочных выключателя. Кнопка S1 необходима для установки минут, при ее нажатии происходит инкремент переменной содержащей минуты на 1, при достижении значения в 59, происходит ее сброс и отсчет начинается сначала, т.е. с нуля.
Аналогичным образом работает кнопка S2, только используется для установки переменной отвечающей за часы.
На рисунке ниже представлена наша схема, особенностью схемы является то, что управление всеми индикаторами осуществляется при помощи одного порта PORTB. С его помощью поочередно осуществляется запись одной цифры в индикатор. За поочередное включение,а именно за динамическую индикацию отвечает PORTC. Что такое динамическая индикация было сказано ранее. Важно отметить что все дисплеи с общим катодом, поэтому для включения того или иного дисплея нужно подать на его общий вывод сигнал низкого состояния, или просто «0».
Важно! Для правильной работы цифровых часов нужно чтобы внутренний генератор камня был настроен на частоту в 4 Мгц.
Ниже представлен код для часов на микроконтроллере ATMEGA16. Программа была написана с использованием среды AVR Studio 5. Еще раз повторю, что данные часы работают от внутреннего тактового генератора. Который имеет определённую погрешность, поэтому период одной секунды может меняться. И вешать их на стену или нет и сверять по ним время решайте сами.
#define F_CPU 4000000UL #include <avr/delay.h> #include <avr/io.h> #include <avr/interrupt.h> #define SegDataPort PORTB #define SegDataPin PINB #define SegDataDDR DDRB #define SegCntrlPort PORTC #define SegCntrlPin PINC #define SegCntrlDDR DDRC /*Global Variables Declarations*/ unsigned char hours = 0; unsigned char minutes = 0; unsigned char seconds = 0; /*Function Declarations*/ /*****************************************************************************/ /*Decimal Digit (0-9) to Seven Segment Values Encoder*/ unsigned char DigitTo7SegEncoder(unsigned char digit, unsigned char common); /*Timer Counter 1 Compare Match A Interrupt Service Routine/Interrupt Han-dler*/ ISR(TIMER1_COMPA_vect); /*Main Program*/ /*****************************************************************************/ int main(void) { SegDataDDR = 0xFF; SegCntrlDDR = 0x3F; SegCntrlPort = 0xFF; TCCR1B = (1<<CS12|1<<WGM12); OCR1A = 15625-1; TIMSK = 1<<OCIE1A; sei(); while(1) { /* Set Minutes when SegCntrl Pin 6 Switch is Pressed*/ if((SegCntrlPin & 0x40) == 0 ) { _delay_ms(200); if(minutes < 59) minutes++; else minutes = 0; } /* Set Hours when SegCntrl Pin 7 Switch is Pressed*/ if((SegCntrlPin & 0x80) == 0 ) { _delay_ms(200); if(hours < 23) hours++; else hours = 0; } SegDataPort = DigitTo7SegEncoder(seconds%10,1); SegCntrlPort = ~0x01; SegDataPort = DigitTo7SegEncoder(seconds/10,1); SegCntrlPort = ~0x02; SegDataPort = DigitTo7SegEncoder(minutes%10,1); SegCntrlPort = ~0x04; SegDataPort = DigitTo7SegEncoder(minutes/10,1); SegCntrlPort = ~0x08; SegDataPort = DigitTo7SegEncoder(hours%10,1); SegCntrlPort = ~0x10; SegDataPort = DigitTo7SegEncoder(hours/10,1); SegCntrlPort = ~0x20; } return 0; } /* * Function Description: * Encode a Decimal Digit 0-9 to its Seven Segment Equivalent. * * Function Arguments: * digit - Decimal Digit to be Encoded * common - Common Anode (0), Common Cathode(1) * SegVal - Encoded Seven Segment Value * * Connections: * Encoded SegVal is return in the other G-F-E-D-C-B-A that is A is the least * significant bit (bit 0) and G bit 6. */ unsigned char DigitTo7SegEncoder(unsigned char digit, unsigned char common) { unsigned char SegVal; switch(digit) { case 0: if(common == 1) SegVal = 0b00111111; else SegVal = ~0b00111111; break; case 1: if(common == 1) SegVal = 0b00000110; else SegVal = ~0b00000110; break; case 2: if(common == 1) SegVal = 0b01011011; else SegVal = ~0b01011011; break; case 3: if(common == 1) SegVal = 0b01001111; else SegVal = ~0b01001111; break; case 4: if(common == 1) SegVal = 0b01100110; else SegVal = ~0b01100110; break; case 5: if(common == 1) SegVal = 0b01101101; else SegVal = ~0b01101101; break; case 6: if(common == 1) SegVal = 0b01111101; else SegVal = ~0b01111101; break; case 7: if(common == 1) SegVal = 0b00000111; else SegVal = ~0b00000111; break; case 8: if(common == 1) SegVal = 0b01111111; else SegVal = ~0b01111111; break; case 9: if(common == 1) SegVal = 0b01101111; else SegVal = ~0b01101111; } return SegVal; } /*Timer Counter 1 Compare Match A Interrupt Service Routine/Interrupt Han-dler*/ ISR(TIMER1_COMPA_vect) { seconds++; if(seconds == 60) { seconds = 0; minutes++; } if(minutes == 60) { minutes = 0; hours++; } if(hours > 23) hours = 0; }
Комментарии
Странный вопрос))Можно использовать либо внешний генератор на кварце либо внутренний. Кварц намного стабильнее чем внутренний.
Можно конечно! Только фьюз выставить соответствующий .
Думаю, что можно - частота работы схемы 4 Мгц, а этот МК тянет до 8, в реальности даже больше... Правда, у меня есть некоторые сомнения по поводу самой схемы - нет токоограничител ьных резисторов и транзисторных ключей...
Поправочка - в цепи питания виден резистор 1К, но вряд ли он полноценно заменит все вышеперечисленн ое, скорее всего будет заметна разность яркости индикаторов, да и номинал самого резистора великоват ...
Даже меньше - прямое падение напряжения на ключах МК и индикаторе будет составлять не менее половины питания, как мне видится. В теории можно обойтись без всех резисторов, если точно подобрать напряжение питания (2,5-3,5В) примерно. Лучше всего подбирать лабораторным БП с ограничением тока по минимально приемлемому свечению индикатора, и потом изготовить БП с точно таким напряжением. Важно! БП должен быть стабилен! Даже незначительное повышение напряжения вызовет резкий прирост тока и может вывести из строя и МК и индикатор. Возможно придется заменить МК на более низковольтный AtMega16l-8AU, но это вряд ли.
Лучше все же в цепи выводов 1-7 МК установить резисторы 470 ом, в цепи выводов 22-27 транзисторные ключи (с вывода МК через резистор 1К-10К к базе, коллектор к индикатору, а эммитер к 0 раз индикатор ОК ) соответственно и транзистор должен быть n-p-n. Резистор 1К из питания исключить. Установить керамический конденсатор 0.1 мкф и оксидный 100-500 мкф в питание.