- napięcie zasilania 3…5 V
- średni prąd obciążenia 70 mA
Budowa i działanie
Schemat urządzenia pokazano na rysunku 1. Zaprojektowano dość prosty system mikroprocesorowy zbudowany z wykorzystaniem popularnego mikrokontrolera firmy Atmel typu ATmega48 taktowanego wewnętrznym, wysokostabilnym oscylatorem o częstotliwości 1 MHz, którego zadaniem jest realizacja wszystkich założonych funkcjonalności. Mikrokontroler ten realizuje tutaj dwa podstawowe zadania: obsługę programowego zegara czasu rzeczywistego RTC oraz obsługę wyświetlacza LED.
W celu realizacji zegara czasu rzeczywistego, w jego najprostszej formie, wykorzystano układ czasowo-licznikowy Timer2, wbudowany w mikrokontroler, pracujący w trybie asynchronicznym i taktowany przy udziale zewnętrznego rezonatora kwarcowego o częstotliwości 32768 Hz. Odpowiednio skonfigurowany Timer2 generuje co 1 sekundę przerwanie od przepełnienia licznika, co wykorzystano do programowej realizacji prostego zegara czasu rzeczywistego. Sposób konfiguracji układu czasowo-licznikowego Timer2 w celu realizacji zegara czasu rzeczywistego pokazano na listingu 1.
rzeczywistego
void megaRTCinit(void){
//Timer2 taktowany za pomocą kwarcu 32768Hz podłączonego do wypr. TOSC1/TOSC2
ASSR |= (1<<AS2);
//Preskaler = 128, przerwanie przepełnienia co 1s (32768/128/256)
TCCR2B = (1<<CS22)|(1<<CS20);
while(ASSR & ((1<<TCN2UB)|(1<<OCR2AUB)|(1<<OCR2BUB)|(1<<TCR2AUB)| (1<<TCR2BUB))); //Oczekiwanie na aktualizację rejestrów
//Skasowanie ewentualnych flag przerwań (przez wpisanie jedynek do bitów flag)
TIFR2 = ((1<<OCF2B)|(1<<OCF2A)|(1<<TOV2));
//Uruchomienie przerwania od przepełnienia licznika TCNT2
TIMSK2 = (1<<TOIE2);
}
Co ważne, dla przejrzystości realizacji założonej funkcjonalności (zegara czasu rzeczywistego i stopera) wprowadzono 2 zmienne strukturalne, których zastosowanie upraszcza kod wynikowy i jednocześnie czyni go bardziej przejrzystym. Deklaracje wspomnianych zmiennych pokazano na listingu 2.
//Definicja typu odpowiedzialnego za obsługę zegara
typedef struct{
volatile uint8_t Flag, Hour, Minute, Second;
} clockType;
//Definicja typu odpowiedzialnego za obsługę stopera
typedef struct{
volatile uint8_t Activity, Flag, Minute, Second;
} timerType;
//Zmienne globalne modułu
extern clockType Clock; //Struktura przechowująca dane zegara
extern timerType Timer; //Struktura przechowująca dane timera
Dalej, na listingu 3, pokazano ciało funkcji obsługi przerwania od przepełnienia zawartości licznika Timer2 (wywoływane co 1 s), której zadaniem jest realizacja zegara czasu rzeczywistego i prostego stopera.
//Przerwanie od przepełnienia licznika Timer2 wywoływane 1 raz na sekundę (Timer2
//taktowany kwarcem zegarkowym 32768Hz)
ISR(TIMER2_OVF_vect){
//Obsługa programowego zegara
Clock.Flag = 1;
if(++Clock.Second == 60){
Clock.Second = 0;
if(++Clock.Minute == 60){
Clock.Minute = 0;
if(++Clock.Hour == 24) Clock.Hour = 0;
}
}
//Obsługa programowego timera
if(Timer.Activity){
Timer.Flag = 1;
if(++Timer.Second == 60){
Timer.Second = 0;
if(++Timer.Minute == 100) Timer.Minute = 0;
}
}
}
Pora na przedstawienie drugiego, kluczowego mechanizmu naszego urządzenia. Mowa o obsłudze 7-segmentowego wyświetlacza LED, która realizowana jest w ramach typowego multipleksowania kolejnych cyfr tegoż wyświetlacza. Jest to zwyczajowe rozwiązanie problemu tego typu pozbawione jakiejkolwiek i niepotrzebnej tutaj finezji. Katody poszczególnych wyświetlaczy LED podłączono bezpośrednio do portu PORTD mikrokontrolera, zaś wspólne anody, poprzez tranzystory sterujące (T1…T4), do portu PORTC tegoż układu. Następnie skonfigurowano układ czasowo-licznikowy Timer0 wbudowany w strukturę mikrokontrolera w taki sposób, by generował stosowne przerwanie 240 razy na sekundę (czyli 60 razy na każdą cyfrę wyświetlacza LED), w ramach którego wyświetlane są kolejne cyfry zegara. Aby uruchomić mechanizm multipleksowania, niezbędne jest odpowiednie skonfigurowanie układu czasowo-licznikowego Timer0, które pokazano na listingu 4.
void initMultiplex(void){
//Porty wspólnych anod i katod, jako wyjściowe ze stanami nieaktywnymi
//na wyjściach
SEG_BLANK;
SEG_AS_OUTPUT;
COM_BLANK;
COM_AS_OUTPUT;
//Konfiguracja układu Timer0 w celu generowania przerwania do obsługi
//multipleksowania wyświetlacza LED (240 Hz)
TCCR0A = (1<<WGM01); //Tryb CTC
TCCR0B = (1<<CS01)|(1<<CS00); //Preskaler = 64
OCR0A = 64; //240 Hz (przerwanie co 4.167ms)
//Uruchomienie przerwania Output Compare Match A (od porównania)
TIMSK0 = (1<<OCIE0A);
}
Do kompletu potrzebne są nam jeszcze definicje dwóch tablic upraszczających mechanizm multipleksowania. Pierwsza z nich, DIGITS[], zawiera definicje poszczególnych cyfr wyświetlacza 7-segmentowego (czyli stanów logicznych na porcie katod wyświetlacza), zaś druga, COMS[], zawiera definicje dla portu sterującego wspólnymi anodami wyświetlaczy LED (czyli stanów logicznych na porcie wspólnych anod wyświetlacza). Na koniec, na listingu 5, przedstawię ciało funkcji obsługi przerwania układu czasowo-licznikowego Timer0, realizującą mechanizm multipleksowania wyświetlaczy LED.
//Przerwanie obsługi wyświetlacza LED wywoływane co 4,167 ms (60 razy na sekundę dla każdej z cyfr)
ISR(TIMER0_COMPA_vect){
static uint8_t Nr; //Numer kolejnej cyfry przeznaczonej do wyświetlenia
//Timer programowy 4ms służący do obsługi migania cyfr wyświetlacza LED
static uint8_t timer4ms;
uint8_t currentDigit = Digit[Nr]; //Optymalizacja zmiennej volatile
COM_BLANK; //Wyłączenie wspólnych anod wyświetlaczy LED
//Sprawdzamy, czy dla danej cyfry uruchomiono funkcję migania (ustawiony najstarszy bit zmiennej)
if(currentDigit & BLINKING_BIT)
{
if(++timer4ms & 0x40) SEG_PORT = pgm_read_byte(&DIGITS[currentDigit & (~BLINKING_BIT)]); else SEG_BLANK;
//Migamy co 266ms
}
//Pobranie kolejnej cyfry na port katod
else SEG_PORT = pgm_read_byte(&DIGITS[currentDigit & (~BLINKING_BIT)]);
//Obsługa migania dwukropkiem
if((++timer4ms & 0x80)) SEG_PORT &= ~(1<<SEG_CL);
//Włączenie odpowiedniej wspólnej anody (aktywny stan „0”)
COM_PORT &= pgm_read_byte(&COMS[Nr]);
Nr = (Nr+1) & 0x03;
}
Jak widać, zaimplementowano dodatkową funkcjonalność w postaci obsługi migania dowolnej cyfry wyświetlacza LED (gdy ustawiony jest najstarszy bit tejże cyfry), co wykorzystywane jest w trybie edycji nastaw zegara czasu rzeczywistego.
Na koniec, już zupełnie dla porządku, należy dodać, że mikrokontroler sterujący odpowiada również za obsługę prostego interfejsu użytkownika w postaci dwóch przycisków umownie oznaczonych jako OK i SET, przy czym, co istotne, obsługiwane jest zarówno krótkie, jak i długie wciśnięcie każdego z przycisków.
Montaż
Schemat płytki PCB oraz schemat montażowy urządzenia Clock pokazano na rysunkach 2 i 3.
Zaprojektowano bardzo zwarty obwód drukowany, o wielkości zbliżonej do wielkości zastosowanego wyświetlacza LED, zbudowany ze zdecydowaną przewagą elementów SMD umieszczonych po obu stronach laminatu. Montaż urządzenia rozpoczynamy od warstwy BOTTOM, gdzie w pierwszej kolejności przylutowujemy mikrokontroler sterujący. Następnie lutujemy rezonator kwarcowy SMD, tranzystory sterujące a na koniec elementy bierne. Dalej przechodzimy na warstwę TOP, gdzie montujemy elementy C1, T4 i R13, po czym przylutowujemy wyświetlacz LED.
CKSEL3...0: 0010
SUT1...0: 10
CKDIV8: 0
CKOUT: 1
DWEN: 1
Na koniec przylutowujemy mikroprzełączniki OK i SET. Poprawnie zmontowany układ powinien działać tuż po włączeniu zasilania. Zmontowaną płytkę od strony BOTTOM pokazuje fotografia 4.
Obsługa
Zgodnie z tym, co napisano wcześniej, interfejs użytkownika naszego urządzenia składa się z dwóch mikroprzełączników umownie nazwanych OK i SET, przy czym obsługiwane jest zarówno krótkie, jak i długie (powyżej 0,5 s) naciśnięcie każdego z elementów sterujących.
Diagram obrazujący sposób obsługi urządzenia przedstawiono na rysunku 5.
Robert Wołgajew, EP
- R1: 47 kΩ
- R2…R5, R7…R9: 270 Ω
- R6: 130 Ω
- R10…R13: 2,2 kΩ
- C1: 100 nF
- U1: ATmega48 (TQFP32)
- T1…T4: BC807 (SOT23)
- LED: wyświetlacz typu LED-AF5643FS
- SET, OK: mikroswitch TACT 6 mm
- Q1: rezonator kwarcowy SMD 32768 Hz