[111] Budujemy ze zrozumieniem - elektroniczny budzik cz. 5
![[111] Budujemy ze zrozumieniem - elektroniczny budzik cz. 5](/_next/image?url=https%3A%2F%2Farduino.pl%2Fimgproxy%2Fkgoe_Bix-qtKaKXT4C3iw8AfzNX3VYlQn-gV2bRZSDI%2Ff%3Awebp%2Fw%3A1200%2FbG9jYWw6Ly8vaW1hZ2VzLzAtNjgzLzFlYTM1LzU0NTFiLzAtNjgzMWVhMzU1NDUxYjQyMDA2NjczNy5wbmc%3D.webp&w=3840&q=75)
Czas zamienić zegar w budzik. Jak się można łatwo domyślić, potrzebne będą:
Dodatkowy zestaw zmiennych, w których będzie siedzieć czas alarmu.
Jakiś wskaźnik jego aktywności.
Emiter pisków.
Sposób regulacji takiego już bardziej rozbudowanego zegara.
Zacznijmy więc od rozmnożenia zmiennych oraz deklaracji portów dodatkowej diody świecącej i piszczka. Ten ostatni siedzi sobie na płytce edukacyjnej TME, więc go zaraz zatrudnimy. W tym momencie nadszedł czas na pewną refleksję. Podczas rozbudowywania aplikacji bywa, że nasze dobre pomysły stają się niedobre. Nie dlatego, że takimi były od początku, a z powodu takiego, że pewne rzeczy w nowym kontekście lepiej robić inaczej. Coś takiego przytrafiło się i tutaj, więc wykorzystam to na chwałę edukacji. Chodzi o traktowanie danych związanych przechowywaniem informacji o czasie.
Dotąd używałem idei BCD, czyli każdy bajt przenosił jedną cyfrę zegara: jednostki i dziesiątki minut oraz takież godzin. Zaletą tego sposobu była prosta konwersja tych wartości na grafiki, które je reprezentowały. Wadą – wszelkie operacje obliczeniowe. A pamiętajmy, że w przerwaniach należy dążyć do minimalizacji obciążeń obliczeniowych spowodowanych potrzebą używania bardziej zaawansowanej matematyki. Czasem lepiej stworzyć łańcuch prostych warunków niż używać na przykład funkcji dzielenia.
Lepiej nie znaczy, że tak trzeba absolutnie. Jak wykazał nasz oscyloskopowy test, zapasu mocy jest od groma, więc po przemyśleniu postanowiłem zmienić ideę przechowywania czasu na taką, w której minuty i godziny stanowią wartości rzeczywiste, przechowywane w pojedynczych bajtach. Żadna nie przekroczy sześćdziesięciu, więc osiem bitów wystarczy aż za dość. Dzięki tej zmianie dostaniemy wadę i dużo zalet. Zacznijmy od wady.
void ladujCzas() {
if (godziny < 10) { // Wyświetl spację, jeśli godzina < 10...
cyfra1 = 10; // Pod adresem dziesiątym znajduje się spacja.
} else { // Wyświetl wartość dziesiątek godzin, jeśli godzina > 9
cyfra1 = godziny / 10; // Załaduj do bufora wyświetlacza dziesiątki godzin.
} // Pozostałe cyfry ładują się już w sposób prosty.
cyfra2 = godziny % 10; // Załaduj do bufora wyświetlacza jednostki godzin.
cyfra3 = minuty / 10; // Załaduj do bufora wyświetlacza dziesiątki minut.
cyfra4 = minuty % 10; // Załaduj do bufora wyświetlacza jednostki minut.
}
Wyłuskanie adresu tablicy znaków nie będzie takie oczywiste, ale już to robiliśmy w pierwotnej wersji zegara. A więc – zaczynając od tyłu – jednostki minut to reszta z dzielenia przez dziesięć wartości minut, dziesiątki – stanowią wynik dzielenia całkowitoliczbowego tychże. Podobnie z godzinami, z tym że tutaj zastawiam pułapkę na wartość niższą od dziesięciu i w tym wypadku adresem w tablicy dla dziesiątek godzin będzie na sztywno ustawiony kształt spacji, siedzący nomen omen pod adresem numer 10.
I to jest cała komplikacja. A teraz zalety: spójrzmy na procedury zwiększające i zmniejszające czas.
void zwiekszCzas() { // Procedura zwiększająca czas z uwzględnieniem przeładowań jednostek.
minuty++; // Zwiększ licznik minut.
if (minuty == 60) { // Jeśli osiągnął 60...
minuty = 0; // Zeruj go oraz...
godziny++; // Zwiększ licznik godzin.
if (godziny == 24) { // Jeśli osiągnął 24...
godziny = 0; // Zeruj go
}
}
}
void zmniejszCzas() { // Procedura zmniejszająca czas z uwzględnieniem przeładowań jednostek.
minuty--; // Zmniejsz licznik minut.
if (minuty == 255) { // Jeśli osiągnął 255...
minuty = 59; // Załaduj wartość równą 59 oraz...
godziny--; // Zmniejsz licznik godzin.
if (godziny == 255) { // Jeśli osiągnął 255...
godziny = 23; // Załaduj wartość równą 23
}
}
}
Pamiętajmy, że pierwsza używana jest zarówno podczas edycji czasu klawiaturką, jak i pracy samego zegara, gdy to wywoływana jest co minutę. Te procedury zmalały o połowę, bo zwiększamy wartości odpowiednio do 60 i 24, a nie 10, 6, 10, 2 i dodatkowo jeszcze trzeba było sprawdzić, czy przy dwójce na początku nie tkwi czwórka zaraz obok. Tak więc redukcja kodu i jego czytelność wzrosła nawet więcej niż dwukrotnie.
Zaraz zobaczymy, że upraszcza to także regulację alarmu i przede wszystkim porównywanie ze sobą czasu i alarmu, co jest niezbędne, by budzik o wybranej porze zaczął pikać.
Drugim elementem, nieco komplikującym program, jest dość rewolucyjny koncept obsługi zegara za pomocą tylko dwóch przycisków. Najpierw przeczytajmy jak to działa w praktyce, a potem wrócimy do programu.
byte cyfra1 = 11; // Bufor kolejnych pozycji wyświetlacza.
byte cyfra2 = 11;
byte cyfra3 = 11;
byte cyfra4 = 11;
while (digitalRead(minus) == LOW) { // Po resecie pikaj w pętli nieskończonej, aż zostanie wciśnięty przycisk minus.
zapikajPoczwornie();
delay(500);
}
Po włączeniu prądu, czyli po resecie, bynajmniej nie ujrzymy zer i, jak to bywa na amerykańskich filmach, zaśpimy, bo jak wiadomo, zresetowany budzik nie zadziała dopóki go nie nastawimy. Postanowiłem wdrożyć brutalny sposób informowania o awarii zasilania – start zegara obwieszczany jest wyświetleniem kresek i nieskończonym pikaniem. Dzięki temu, jeśli z jakiegoś powodu zegar się zresetuje albo zniknie na chwilę zasilanie, po jego powrocie zostaniemy obudzeni – choćby w środku nocy – i poproszeni o nastawienie zegara i budzika.
// Edycja czasu =============================================================================================================================================
if (digitalRead(minus) == HIGH) { // Jeśli wciśnięto przycisk minus...
delay(2000); // Zaczekaj dwie sekundy.
if (digitalRead(minus) == HIGH) { // Jeśli wciąż przycisk będzie wciśnięty...
trybEdycji = HIGH; // Ustaw flagę trybu edycji.
digitalWrite(dwukropek, HIGH); // Włącz dwukropek.
digitalWrite(buzzer, HIGH); // Zapikaj pojedynczo.
delay(opoznienieDlugieAlarm);
digitalWrite(buzzer, LOW);
zerujSekundy(); // Zeruj licznik sekund i ramek.
autoKoniec = 0; // Zeruj licznik sekund do automatycznego opuszczania procedury ustawiania czasu.
while (autoKoniec < czasAutoKonca) { // Automatycznie opuść procedurę po trzech sekundach bezczynności.
if (digitalRead(plus) == HIGH) { // Obsługa pstryczka plus w trybie ustawiania czasu. Jeśli go wciśnięto...
regulujCzasPlus(); // Zeruj licznik sekund do automatycznego opuszczania procedury ustawiania czasu, zwiększ czas i wyświetl go.
delay(opoznienieDlugie); // Opóźnienie przed autorepetycją.
while (digitalRead(plus) == HIGH) { // Dopóki przycisk plus jest trzymany...
regulujCzasPlus(); // Zeruj licznik sekund do automatycznego opuszczania procedury ustawiania czasu, zwiększ czas i wyświetl go.
delay(opoznienieKrotkie); // Opóźnienie autorepetycji.
}
delay(opoznienieDlugie); // Opóźnienie przed wyjściem z procedury.
}
if (digitalRead(minus) == HIGH) { // Obsługa pstryczka minus w trybie ustawiania czasu.
regulujCzasMinus();
delay(opoznienieDlugie);
while (digitalRead(minus) == HIGH) {
regulujCzasMinus();
delay(opoznienieKrotkie);
}
delay(opoznienieDlugie);
}
}
digitalWrite(buzzer, HIGH); // Zapikaj pojedynczo.
delay(opoznienieDlugieAlarm);
digitalWrite(buzzer, LOW);
zerujSekundy(); // Zeruj licznik sekund i ramek.
trybEdycji = LOW; // Zeruj flagę trybu edycji.
}
}
Jak to zrobić, mając tylko dwa przyciski? Przycisk minus zmniejsza wartości, przycisk plus – zwiększa. Przypomnę – po chwili ich trzymania włącza się szalona autorepetycja, ale to działa nadzwyczaj wygodnie.
No dobrze, ale jak zakończyć nastawy, gdy już czas będzie ustawiony zgodnie z obowiązującym? Po prostu trzeba nic nie robić przez trzy sekundy. Zegar zapika w charakterystyczny, dłuższy sposób, dwukropek ożyje i to wszystko. Prawda, że genialne? A gdybyśmy się pomylili? Albo ponownie chcieli wyregulować zegarek? Należy przez chwilę przytrzymać wciśnięty minus, by znów przejść w jego edycję. Dlaczego trzeba chwili? Żeby przypadkiem nie wejść w tę opcję, gdy będziemy chcieli ustawić alarm. Każdorazowe wejście w ustawienie zegara zeruje także liczniki sekund i ramek. Dlatego postanowiłem tę funkcję zabezpieczyć potrzebą trzymania minus przez dwie sekundy.
// Edycja alarmu - włączanie i ustawianie ===================================================================================================================
if (digitalRead(plus) == HIGH && przelacznikTrybuAlarmu == HIGH) { // Jeśli wciśnięto przycisk plus i można edytować alarm...
przelacznikTrybuAlarmu = LOW; // Przestaw przełącznik.
trybEdycji = HIGH; // Ustaw flagę trybu edycji.
digitalWrite(dwukropek, HIGH); // Włącz dwukropek.
digitalWrite(alarm, HIGH); // Włącz diodę alarmu.
zapikajPodwojnie(); // Zapikaj dwukrotnie.
ladujAlarm(); // Ładuj czas alarmu do buforów wyświetlacza.
autoKoniec = 0; // Zeruj licznik sekund do automatycznego opuszczania procedury ustawiania alarmu.
while (autoKoniec < czasAutoKonca) { // Automatycznie opuść procedurę po trzech sekundach bezczynności.
if (digitalRead(plus) == HIGH) { // Obsługa pstryczka plus w trybie alarmu. Jeśli go wciśnięto...
zwiekszAlarm(); // Zwiększ czas alarmu.
delay(opoznienieDlugie); // Opóźnienie przed autorepetycją.
while (digitalRead(plus) == HIGH) { // Dopóki przycisk plus jest trzymany...
zwiekszAlarm(); // Zwiększaj czas alarmu.
delay(opoznienieKrotkie); // Opóźnienie autorepetycji.
}
delay(opoznienieDlugie); // Opóźnienie przed wyjściem z procedury.
}
if (digitalRead(minus) == HIGH) { // Obsługa pstryczka minus w trybie alarmu.
zmniejszAlarm();
delay(opoznienieDlugie);
while (digitalRead(minus) == HIGH) {
zmniejszAlarm();
delay(opoznienieKrotkie);
}
delay(opoznienieDlugie);
}
}
cyfra1 = 12; // Załaduj komunikat "AL On"
cyfra2 = 13;
cyfra3 = 0;
cyfra4 = 15;
zapikajPodwojnie(); // Zapikaj dwukrotnie.
delay(1000); // Zaczekaj sekundę.
ladujCzas(); // Ładuj do bufora wyświetlacza zawartość zegara.
trybEdycji = LOW; // Zeruj flagę trybu edycji.
}
Jak więc nastawić alarm? Tak samo, tylko zamiast minus wciskamy plus. Tu już nie ma opóźnienia i od razu wejdziemy w możliwość nastawiania alarmu. Zamiast długiego pisku usłyszymy dwa krótkie, po czym będziemy mogli ustalić godzinę budzenia. I podobnie, żeby ją zatwierdzić, po prostu nic nie róbmy przez trzy sekundy. Mało tego, oprócz ponownego usłyszenia dwóch krótkich pisków na chwilę wyświetli nam się napis AL On - żebyśmy nie mieli wątpliwości cośmy właśnie uczynili. No i włączy się dioda alarmu.
// Edycja alarmu - wyłączanie ===============================================================================================================================
if (digitalRead(plus) == HIGH && przelacznikTrybuAlarmu == LOW) { // Jeśli wciśnięto przycisk plus i nie można edytować alarmu...
przelacznikTrybuAlarmu = HIGH; // Przestaw przełącznik.
trybEdycji = HIGH; // Ustaw flagę trybu edycji.
digitalWrite(dwukropek, HIGH); // Włącz dwukropek.
digitalWrite(alarm, LOW); // Wyłącz diodę alarmu.
cyfra1 = 16; // Załaduj komunikat "No AL"
cyfra2 = 14;
cyfra3 = 12;
cyfra4 = 13;
digitalWrite(buzzer, HIGH); // Zapikaj pojedynczo.
delay(opoznienieKrotkieAlarm);
digitalWrite(buzzer, LOW);
delay(1000); // Zaczekaj sekundę.
ladujCzas(); // Ładuj do bufora wyświetlacza zawartość zegara.
while (digitalRead(minus) == HIGH || digitalRead(plus) == HIGH) {} // Czekaj na puszczenie przycisków.
trybEdycji = LOW; // Zeruj flagę trybu edycji.
}
No fajnie, ale jeśli jutro będzie sobota i będziemy chcieli spać do oporu? I tu mamy niezwykle sprytny sposób: spróbujmy znowu nastawić alarm – i co? Nie da się! Na wyświetlaczu ukaże się napis No AL, piszczek wyda pojedynczy, krótki pisk, a dioda alarmu zgaśnie. Innymi słowy, w parzystych wejściach w ustawianie alarmu będziemy go ustawiać i włączać, w nieparzystych – tylko wyłączać.
A jeśli pasuje nam godzina nastawienia alarmu, ale był on wyłączony? Po prostu wejdźmy w jego edycję, ale nie zmieniajmy wartości – po trzech sekundach zostanie zatwierdzona jaka jest.
// Obsługa alarmu ===========================================================================================================================================
while (wlaczAlarm == HIGH) { // Jeśli nadeszła pora alarmu...
zapikajPoczwornie(); // Zapikaj poczwórnie.
delay(500); // Zaczekaj pół sekundy.
if (digitalRead(plus) == HIGH) { // Powtarzaj dopóki nie zostanie wciśnięty przycisk kasowania alarmu.
wlaczAlarm = LOW; // Zeruj flagę alarmu.
}
}
No i ostatnia rzecz: gdy przyjdzie pora, alarm będzie pikał nie przez minutę, a w nieskończoność. Żeby obudzić największego śpiocha. Jedynym sposobem jego wyłączenia będzie przytrzymanie nieco dłużej przycisku plus. Dłużej – żeby nie zrobić tego odruchowo, nakrywając się mocniej pierzyną :) Skoro już wiemy, jak to działa, czas na dogłębną tym razem analizę programu – ale o tym napiszę w kolejnym artykule, wrzucając tutaj jeszcze listing całego szkicu.
#include <TimerOne.h> // Biblioteka obsługi przerwań.
const byte segmentA = 5; // Adresy portów kolejnych segmentów: od A do G
const byte segmentB = 6;
const byte segmentC = 9;
const byte segmentD = 10;
const byte segmentE = 11;
const byte segmentF = 12;
const byte segmentG = 13;
const byte wspolne1 = A0; // Adresy portów wpólnych wyprowadzeń kolejnych wyświetlaczy.
const byte wspolne2 = A1;
const byte wspolne3 = A2;
const byte wspolne4 = A3;
const byte dwukropek = 3; // Adres diody dwukropka.
const byte alarm = 8; // Adres diody alarmu.
const byte buzzer = 2; // Adres buzzera.
const byte plus = 7; // Adresy portów klawiatury.
const byte minus = 4;
const byte opoznienieDlugie = 100; // Współczynniki autorepetycji klawiatury.
const byte opoznienieKrotkie = 5;
const byte opoznienieKrotkieAlarm = 50;
const byte opoznienieDlugieAlarm = 250;
const byte czasAutoKonca = 3; // Czas w sekundach opuszczania trybu edycji
const byte tablica[17] = {
B1000000, // 0, Tablica zawierająca wzorce znaków wyświetlacza siedmiosegmentowego.
B1111001, // 1, Schemat: G-F-E-D-C-B-A
B0100100, // 2
B0110000, // 3
B0011001, // 4
B0010010, // 5
B0000010, // 6
B1111000, // 7
B0000000, // 8
B0010000, // 9
B1111111, // spacja
B0111111, // minus
B0001000, // A
B1000111, // L
B0100011, // o
B0101011, // n
B1001000 // N
};
int licznikPrzerwan = 0; // Lcznik ramek odmierzających 2,5 ms
byte aktywnaCyfra = 1; // Numer aktywnej cyfry na wyświetlaczu dla multipleksera.
byte sekundy = 0; // Licznik sekund.
byte minuty = 0; // Licznik minut.
byte godziny = 0; // Licznik godzin.
byte minutyAlarmu = 0; // Licznik minut alarmu.
byte godzinyAlarmu = 6; // Licznik godzin alarmu.
byte cyfra1 = 11; // Bufor kolejnych pozycji wyświetlacza.
byte cyfra2 = 11;
byte cyfra3 = 11;
byte cyfra4 = 11;
byte autoKoniec = czasAutoKonca; // Licznik w sekundach do automatycznego opuszczania procedur ustawiania czasu i alarmu.
bool trybEdycji = LOW; // Flaga aktywności edycji czasu lub alarmu.
bool przelacznikTrybuAlarmu = HIGH; // Flaga przełączania edycji alarmu między jego włączeniem z regulacją i wyłączeniem.
bool wlaczAlarm = LOW; // Flaga trwania alarmu.
void setup() {
pinMode(segmentA, OUTPUT);
pinMode(segmentB, OUTPUT);
pinMode(segmentC, OUTPUT);
pinMode(segmentD, OUTPUT);
pinMode(segmentE, OUTPUT);
pinMode(segmentF, OUTPUT);
pinMode(segmentG, OUTPUT);
pinMode(wspolne1, OUTPUT);
pinMode(wspolne2, OUTPUT);
pinMode(wspolne3, OUTPUT);
pinMode(wspolne4, OUTPUT);
pinMode(dwukropek, OUTPUT);
pinMode(alarm, OUTPUT);
pinMode(buzzer, OUTPUT);
pinMode(plus, INPUT_PULLUP);
pinMode(minus, INPUT_PULLUP);
digitalWrite(dwukropek, HIGH); // Włącz dwukropek.
Timer1.initialize(2500); // Ustaw licznik na 2500 mikrosekund.
Timer1.attachInterrupt(przerwanie); // Włącz przerwanie z określeniem miejsca lądowania.
while (digitalRead(minus) == LOW) { // Po resecie pikaj w pętli nieskończonej, aż zostanie wciśnięty przycisk minus.
zapikajPoczwornie();
delay(500);
}
ladujCzas(); // Wyświetl czas zamiast kresek.
}
void loop() {
// Edycja czasu =============================================================================================================================================
if (digitalRead(minus) == HIGH) { // Jeśli wciśnięto przycisk minus...
delay(2000); // Zaczekaj dwie sekundy.
if (digitalRead(minus) == HIGH) { // Jeśli wciąż przycisk będzie wciśnięty...
trybEdycji = HIGH; // Ustaw flagę trybu edycji.
digitalWrite(dwukropek, HIGH); // Włącz dwukropek.
digitalWrite(buzzer, HIGH); // Zapikaj pojedynczo.
delay(opoznienieDlugieAlarm);
digitalWrite(buzzer, LOW);
zerujSekundy(); // Zeruj licznik sekund i ramek.
autoKoniec = 0; // Zeruj licznik sekund do automatycznego opuszczania procedury ustawiania czasu.
while (autoKoniec < czasAutoKonca) { // Automatycznie opuść procedurę po trzech sekundach bezczynności.
if (digitalRead(plus) == HIGH) { // Obsługa pstryczka plus w trybie ustawiania czasu. Jeśli go wciśnięto...
regulujCzasPlus(); // Zeruj licznik sekund do automatycznego opuszczania procedury ustawiania czasu, zwiększ czas i wyświetl go.
delay(opoznienieDlugie); // Opóźnienie przed autorepetycją.
while (digitalRead(plus) == HIGH) { // Dopóki przycisk plus jest trzymany...
regulujCzasPlus(); // Zeruj licznik sekund do automatycznego opuszczania procedury ustawiania czasu, zwiększ czas i wyświetl go.
delay(opoznienieKrotkie); // Opóźnienie autorepetycji.
}
delay(opoznienieDlugie); // Opóźnienie przed wyjściem z procedury.
}
if (digitalRead(minus) == HIGH) { // Obsługa pstryczka minus w trybie ustawiania czasu.
regulujCzasMinus();
delay(opoznienieDlugie);
while (digitalRead(minus) == HIGH) {
regulujCzasMinus();
delay(opoznienieKrotkie);
}
delay(opoznienieDlugie);
}
}
digitalWrite(buzzer, HIGH); // Zapikaj pojedynczo.
delay(opoznienieDlugieAlarm);
digitalWrite(buzzer, LOW);
zerujSekundy(); // Zeruj licznik sekund i ramek.
trybEdycji = LOW; // Zeruj flagę trybu edycji.
}
}
// Edycja alarmu - włączanie i ustawianie ===================================================================================================================
if (digitalRead(plus) == HIGH && przelacznikTrybuAlarmu == HIGH) { // Jeśli wciśnięto przycisk plus i można edytować alarm...
przelacznikTrybuAlarmu = LOW; // Przestaw przełącznik.
trybEdycji = HIGH; // Ustaw flagę trybu edycji.
digitalWrite(dwukropek, HIGH); // Włącz dwukropek.
digitalWrite(alarm, HIGH); // Włącz diodę alarmu.
zapikajPodwojnie(); // Zapikaj dwukrotnie.
ladujAlarm(); // Ładuj czas alarmu do buforów wyświetlacza.
autoKoniec = 0; // Zeruj licznik sekund do automatycznego opuszczania procedury ustawiania alarmu.
while (autoKoniec < czasAutoKonca) { // Automatycznie opuść procedurę po trzech sekundach bezczynności.
if (digitalRead(plus) == HIGH) { // Obsługa pstryczka plus w trybie alarmu. Jeśli go wciśnięto...
zwiekszAlarm(); // Zwiększ czas alarmu.
delay(opoznienieDlugie); // Opóźnienie przed autorepetycją.
while (digitalRead(plus) == HIGH) { // Dopóki przycisk plus jest trzymany...
zwiekszAlarm(); // Zwiększaj czas alarmu.
delay(opoznienieKrotkie); // Opóźnienie autorepetycji.
}
delay(opoznienieDlugie); // Opóźnienie przed wyjściem z procedury.
}
if (digitalRead(minus) == HIGH) { // Obsługa pstryczka minus w trybie alarmu.
zmniejszAlarm();
delay(opoznienieDlugie);
while (digitalRead(minus) == HIGH) {
zmniejszAlarm();
delay(opoznienieKrotkie);
}
delay(opoznienieDlugie);
}
}
cyfra1 = 12; // Załaduj komunikat "AL On"
cyfra2 = 13;
cyfra3 = 0;
cyfra4 = 15;
zapikajPodwojnie(); // Zapikaj dwukrotnie.
delay(1000); // Zaczekaj sekundę.
ladujCzas(); // Ładuj do bufora wyświetlacza zawartość zegara.
trybEdycji = LOW; // Zeruj flagę trybu edycji.
}
// Edycja alarmu - wyłączanie ===============================================================================================================================
if (digitalRead(plus) == HIGH && przelacznikTrybuAlarmu == LOW) { // Jeśli wciśnięto przycisk plus i nie można edytować alarmu...
przelacznikTrybuAlarmu = HIGH; // Przestaw przełącznik.
trybEdycji = HIGH; // Ustaw flagę trybu edycji.
digitalWrite(dwukropek, HIGH); // Włącz dwukropek.
digitalWrite(alarm, LOW); // Wyłącz diodę alarmu.
cyfra1 = 16; // Załaduj komunikat "No AL"
cyfra2 = 14;
cyfra3 = 12;
cyfra4 = 13;
digitalWrite(buzzer, HIGH); // Zapikaj pojedynczo.
delay(opoznienieKrotkieAlarm);
digitalWrite(buzzer, LOW);
delay(1000); // Zaczekaj sekundę.
ladujCzas(); // Ładuj do bufora wyświetlacza zawartość zegara.
while (digitalRead(minus) == HIGH || digitalRead(plus) == HIGH) {} // Czekaj na puszczenie przycisków.
trybEdycji = LOW; // Zeruj flagę trybu edycji.
}
// Obsługa alarmu ===========================================================================================================================================
while (wlaczAlarm == HIGH) { // Jeśli nadeszła pora alarmu...
zapikajPoczwornie(); // Zapikaj poczwornie.
delay(500); // Zaczekaj pół sekundy.
if (digitalRead(plus) == HIGH) { // Powtarzaj dopóki nie zostanie wciśnięty przycisk kasowania alarmu.
wlaczAlarm = LOW; // Zeruj flagę alarmu.
}
}
}
// Obsługa przerwań ===========================================================================================================================================
void przerwanie() { // Tutaj lądujemy za każdym razem, gdy wewnętrzny timer odliczy 2500 mikrosekund.
licznikPrzerwan++; // Zwiększ licznik ramek odmierzających 2,5 ms
if (licznikPrzerwan == 400) { // Jeśli osiągnął 400 (2,5 ms * 400 = 1 sekunda)...
licznikPrzerwan = 0; // Zeruj go oraz...
digitalWrite(dwukropek, !digitalRead(dwukropek) || trybEdycji); // Zmień stan dwukropka, albo go ustaw, jeśli jesteś w trybie edycji.
sekundy++; // Zwiększaj liczbę sekund.
autoKoniec++; // Zwiększ licznik do automatycznego opuszczania procedur ustawiania czasu i alarmu.
if (sekundy == 60) { // Jeśli osiągnął 60...
sekundy = 0; // Zeruj go oraz...
zwiekszCzas(); // Zwiększ czas.
if (trybEdycji == LOW) { // Gdy akurat nie ustawiamy czasu lub alarmu...
ladujCzas(); // Ładuj czas do buforów wyświetlacza.
}
if (minutyAlarmu == minuty && godzinyAlarmu == godziny && digitalRead(alarm) == HIGH) { // Gdy czas na alarm...
wlaczAlarm = HIGH; // Ustaw flagę alarmu.
}
}
}
digitalWrite(wspolne1, LOW); // Wygaś wszystkie cyfry.
digitalWrite(wspolne2, LOW);
digitalWrite(wspolne3, LOW);
digitalWrite(wspolne4, LOW);
switch (aktywnaCyfra) { // Załaduj kształty aktywnej cyfry z tablicy...
case 1: // Na pierwszą pozycję wyświetlacza.
digitalWrite(segmentA, bitRead(tablica[cyfra1], 0));
digitalWrite(segmentB, bitRead(tablica[cyfra1], 1));
digitalWrite(segmentC, bitRead(tablica[cyfra1], 2));
digitalWrite(segmentD, bitRead(tablica[cyfra1], 3));
digitalWrite(segmentE, bitRead(tablica[cyfra1], 4));
digitalWrite(segmentF, bitRead(tablica[cyfra1], 5));
digitalWrite(segmentG, bitRead(tablica[cyfra1], 6));
digitalWrite(wspolne1, HIGH); // Włącz aktywną cyfrę.
aktywnaCyfra = 2; // Ustaw numer aktywnej cyfry dla kolejnego wejścia w przerwanie.
break;
case 2: // Na drugą pozycję wyświetlacza.
digitalWrite(segmentA, bitRead(tablica[cyfra2], 0));
digitalWrite(segmentB, bitRead(tablica[cyfra2], 1));
digitalWrite(segmentC, bitRead(tablica[cyfra2], 2));
digitalWrite(segmentD, bitRead(tablica[cyfra2], 3));
digitalWrite(segmentE, bitRead(tablica[cyfra2], 4));
digitalWrite(segmentF, bitRead(tablica[cyfra2], 5));
digitalWrite(segmentG, bitRead(tablica[cyfra2], 6));
digitalWrite(wspolne2, HIGH);
aktywnaCyfra = 3;
break;
case 3: // Na trzecią pozycję wyświetlacza.
digitalWrite(segmentA, bitRead(tablica[cyfra3], 0));
digitalWrite(segmentB, bitRead(tablica[cyfra3], 1));
digitalWrite(segmentC, bitRead(tablica[cyfra3], 2));
digitalWrite(segmentD, bitRead(tablica[cyfra3], 3));
digitalWrite(segmentE, bitRead(tablica[cyfra3], 4));
digitalWrite(segmentF, bitRead(tablica[cyfra3], 5));
digitalWrite(segmentG, bitRead(tablica[cyfra3], 6));
digitalWrite(wspolne3, HIGH);
aktywnaCyfra = 4;
break;
case 4: // Na czwarta pozycję wyświetlacza.
digitalWrite(segmentA, bitRead(tablica[cyfra4], 0));
digitalWrite(segmentB, bitRead(tablica[cyfra4], 1));
digitalWrite(segmentC, bitRead(tablica[cyfra4], 2));
digitalWrite(segmentD, bitRead(tablica[cyfra4], 3));
digitalWrite(segmentE, bitRead(tablica[cyfra4], 4));
digitalWrite(segmentF, bitRead(tablica[cyfra4], 5));
digitalWrite(segmentG, bitRead(tablica[cyfra4], 6));
digitalWrite(wspolne4, HIGH);
aktywnaCyfra = 1;
break;
}
}
// Podprogramy ================================================================================================================================================
void zwiekszCzas() { // Procedura zwiększająca czas z uwzględnieniem przeładowań jednostek.
minuty++; // Zwiększ licznik minut.
if (minuty == 60) { // Jeśli osiągnął 60...
minuty = 0; // Zeruj go oraz...
godziny++; // Zwiększ licznik godzin.
if (godziny == 24) { // Jeśli osiągnął 24...
godziny = 0; // Zeruj go
}
}
}
void zmniejszCzas() { // Procedura zmniejszająca czas z uwzględnieniem przeładowań jednostek.
minuty--; // Zmniejsz licznik minut.
if (minuty == 255) { // Jeśli osiągnął 255...
minuty = 59; // Załaduj wartość równą 59 oraz...
godziny--; // Zmniejsz licznik godzin.
if (godziny == 255) { // Jeśli osiągnął 255...
godziny = 23; // Załaduj wartość równą 23
}
}
}
void zwiekszAlarm() { // Procedura zwiększająca czas alarmu z uwzględnieniem przeładowań jednostek.
autoKoniec = 0; // Zeruj licznik sekund do automatycznego opuszczania proceduy ustawiania alarmu.
minutyAlarmu++; // Zwiększ licznik minut alarmu.
if (minutyAlarmu == 60) { // Jeśli osiągnął 60...
minutyAlarmu = 0; // Zeruj go oraz...
godzinyAlarmu++; // Zwiększ licznik godzin alarmu.
if (godzinyAlarmu == 24) { // Jeśli osiągnął 24...
godzinyAlarmu = 0; // Zeruj go
}
}
ladujAlarm(); // Ładuj czas alarmu do buforów wyświetlacza.
}
void zmniejszAlarm() { // Procedura zmniejszająca czas alarmu z uwzględnieniem przeładowań jednostek.
autoKoniec = 0; // Zeruj licznik sekund do automatycznego opuszczania proceduy ustawiania alarmu.
minutyAlarmu--; // Zmniejsz licznik minut alarmu.
if (minutyAlarmu == 255) { // Jeśli osiągnął 255...
minutyAlarmu = 59; // Załaduj wartość równą 59 oraz...
godzinyAlarmu--; // Zmniejsz licznik godzin alarmu.
if (godzinyAlarmu == 255) { // Jeśli osiągnął 255...
godzinyAlarmu = 23; // Załaduj wartość równą 23
}
}
ladujAlarm(); // Ładuj czas alarmu do buforów wyświetlacza.
}
void ladujCzas() {
if (godziny < 10) { // Wyświetl spację, jeśli godzina < 10
cyfra1 = 10; // Pod adresem dziesiątym znajduje się spacja.
} else { // Wyświetl wartość dziesiątek godzin, jeśli godzina > 9
cyfra1 = godziny / 10; // Załaduj do bufora wyświetlacza dziesiątki godzin.
} // Pozostałe cyfry ładują się już w sposób prosty.
cyfra2 = godziny % 10; // Załaduj do bufora wyświetlacza jednostki godzin.
cyfra3 = minuty / 10; // Załaduj do bufora wyświetlacza dziesiątki minut.
cyfra4 = minuty % 10; // Załaduj do bufora wyświetlacza jednostki minut.
}
void ladujAlarm() {
if (godzinyAlarmu < 10) { // Wyświetl spację, jeśli godzina alarmu < 10
cyfra1 = 10; // Pod adresem dziesiątym znajduje się spacja.
} else { // Wyświetl wartość dziesiątek godzin alarmu, jeśli godzina alarmu > 9
cyfra1 = godzinyAlarmu / 10; // Załaduj do bufora wyświetlacza dziesiątki godzin alarmu.
} // Pozostałe cyfry ładują się już w sposób prosty.
cyfra2 = godzinyAlarmu % 10; // Załaduj do bufora wyświetlacza jednostki godzin alarmu.
cyfra3 = minutyAlarmu / 10; // Załaduj do bufora wyświetlacza dziesiątki minut alarmu.
cyfra4 = minutyAlarmu % 10; // Załaduj do bufora wyświetlacza jednostki minut alarmu.
}
void koniecRegulacjiCzasu() {
licznikPrzerwan = 0; // Zeruj licznik przerwań.
sekundy = 0; // Zeruj sekundnik.
delay(opoznienieDlugie); // Opóźnienie przed wyjściem z procedury.
}
void zapikajPodwojnie() { // Zapikaj podwójnie.
digitalWrite(buzzer, HIGH);
delay(opoznienieKrotkieAlarm);
digitalWrite(buzzer, LOW);
delay(opoznienieKrotkieAlarm);
digitalWrite(buzzer, HIGH);
delay(opoznienieKrotkieAlarm);
digitalWrite(buzzer, LOW);
delay(opoznienieKrotkieAlarm);
}
void zapikajPoczwornie() { // Zapikaj poczwórnie.
zapikajPodwojnie();
zapikajPodwojnie();
}
void regulujCzasPlus() {
autoKoniec = 0; // Zeruj licznik sekund do automatycznego opuszczania proceduy ustawiania czasu.
zwiekszCzas(); // Zwiększ czas.
ladujCzas(); // Wyświetl czas.
}
void regulujCzasMinus() {
autoKoniec = 0;
zmniejszCzas();
ladujCzas();
}
void zerujSekundy() {
licznikPrzerwan = 0; // Zeruj licznik przerwań.
sekundy = 0; // Zeruj sekundnik.
}