Cześć i czołem koleżanki i koledzy elektronicy!
W artykule postaram się przybliżyć swój pierwszy projekt – Zegarek. Zdaję sobie sprawę, że nie brzmi to nazbyt imponująco. Niemniej, chyba większość adeptów trudnej sztuki elektroniki zaczyna swoją przygodę właśnie od zegarka. Jest to z jednej strony bardzo prosty układ, jednak z drugiej strony może przysporzyć wiele zgryzot i wątpliwości. Zatem, wierzę, że artykuł przyda się początkującym.
Zapraszam do lektury!
1. Opis układu
Mózgiem, no a przynajmniej sercem układu jest procesor ATMEGA328-PU (taki sam jak w Arduino UNO). Czas pokazuję na czterech multipleksowanych 7 segmentowych wyświetlaczach. Dane do wyświetlaczy przesyłam przez ekspander PCF8574. Natomiast czas odmierzam w zegarze czasu rzeczywistego DS1037 z podtrzymywaniem bateryjnym, dzięki czemu nawet po wyłączeniu układu, czas jest nadal mierzony. Użytkownik za pomocą trzech przycisków: setup; up; down ustawia czas. Zasilanie całego układu zrealizowałem przez cztery baterie AA (1.5v każda) oraz stabilizator liniowy 7805.
Lista potrzebnych elementów:
Zegar:
- 1x DS1307
- 1x koszyk na baterię 3V do DS1307
- 1x kwarc 32.768KHz
- 1x bateria CR2032 3V
W ofercie sklepu http://nettigo.pl można nabyć te 3 elementy na jednej eleganckiej płytce – Moduł RTC – DS1307.
Scalaki:
- 1x PCF8574
- 1x ATMEGA328PU-P lub z bootloaderem ATmega 328 + UNO bootloader
Rezystory, kondensatory
- 2x kondensator ceramiczny 22pF
- 2x kondensator elektrolityczny 100uF
- 4x rezystor 10k om
- 2x rezystor 4,7k om
- 1x rezystor 220 om
Można nabyć zestaw: Zestaw rezystorów i kondensatorów
I reszta:
- 4x wyświetlacz 7 seg LED ze wspólną anodą
- 4x tranzystor BC327
- 1x dioda LED np. czerwona
- 1x kwarc 16MHz
- 1x koszyk na baterię 4x AA
- 4x bateria AA 1.5V
- 1x 7805
- przewody
- płytki prototypowe
2. DS1307
Ten niepozorny scalak, to bardzo popularny zegar czasu rzeczywistego, który komunikuję się ze światem zewnętrznym via I2C przy użyciu pinów SDA oraz SCL.
- X1, X2 – kwarc 32.768 KHz,
- VBAT – podtrzymywanie bateryjne (min 2.0 – max 3.5V),
- GND – masa,
- VCC– zasilanie (min 4.5 – max 5.5V),
- SQW/OUT – wyjście sygnału prostokątnego, jeśli nie używany, to może wisieć w powietrzu,
- SCL – sygnał zegara magistrali I2C,
- SDA – sygnał danych magistrali I2C.
Zatem, pod nóżki X1 i X2 podłączamy kwarc 32.768KHz, jeszcze tylko z płytki Arduino bierzemy napięcie VCC, GND, sygnał SDA oraz SCL i możemy cieszyć się naszym cackiem.
Można również podpiąć baterie(min 2.0 – max 3.5V) na nóżkę VBAT i nasz DS1307 będzie pilnować upływ czasu nawet po odłączeniu zasilania VCC. Jak tylko ustawimy datę oraz czas, to od tego momentu z uporem lepszej sprawy bez wytchnienia układ będzie odmierzać milisekundę za milisekundą. Bez ustanku… A w zasadzie do wyczerpania baterii ;-)
Tak wygląda układ na płytce prototypowej z wykorzystaniem Arduino.
Dla porządku dodamy do sygnałów SCL oraz SDL rezystory pull-up o wartości 4,7KΩ, dzięki czemu na magistrali będziemy mieli stan wysoki gdy żadne urządzenie nie będzie nadawać.
Po tym jak tylko uda nam się cało i szczęśliwie złożyć prototyp, pozostaje jeszcze dopisanie kodu. Zatem, zakasujemy rękawy i ściągamy bibliotekę RTClib, która dostarcza funkcjonalność układu DS1307 opakowanego w zgrabny interfejs. Po ściągnięciu biblioteki dodajemy ją do katalogu – My DocumentsArduinolibraries
Przykład użycia biblioteki:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
#include <Wire.h> #include"RTClib.h" RTC_DS1307 RTC; void setup() { Serial.begin(9600); Wire.begin(); RTC.begin(); if(!RTC.isrunning()) { Serial.println("RTC is NOT running!"); // following line sets the RTC to the date & time this sketch was compiled RTC.adjust(DateTime(__DATE__,__TIME__)); } } void loop() { DateTimenow=RTC.now(); delay(3000); } |
Jak widzimy korzystanie z DS1307 jest banalnie proste, w funkcji setup() odpalamy komunikację, a w loop() cyklicznie odczytujemy czas za pomocą metody now(), która zwraca ładnie opakowaną datę oraz godzinę w strukturze DateTime.
3. PCF8574
Szerzej znany jako ekspander, główną jego funkcją jest dodanie 8 wyjść/wejść cyfrowych. Komunikuję się tak jak poprzednio opisywany DS1307, czyli przez magistralę I2C.
- A0, A1, A2 – adres układu,
- P0,..,P7 – 8 cyfrowych pinów input/output (I/O) – dlatego nazywa się ekspanderem ;-)
- VCC – zasilanie (min 2.5 – max 6V),
- GND – masa,
- SCL – sygnał zegara magistrali I2C,
- SDA – sygnał danych magistrali I2C,
- /INT – zanegowany sygnał przerwania.
Można jednocześnie używać wielu ekspanderów dlatego został stworzony mechanizm, który pozawala je między sobą jednoznacznie rozróżniać. Do tego celu służy adres – w układach PCF8574 adres ma 7 bitów długości, pierwsze 3 bity A0, A1 oraz A2 nadaje użytkownik a pozostałe 4 są nadawane fabrycznie na stałe. W zależności czy korzystamy z układu PCF8574 czy z PCF8574A fabrycznie nastawione są inne adresy, i tak:
PCF8574:
0 |
1 |
0 |
0 |
A2 |
A1 |
A0 |
Czyli szesnastkowo (hex) zakres adresu to: 0x20 – 0x27
PCF8574A:
0 |
1 |
1 |
1 |
A2 |
A1 |
A0 |
Czyli szesnastkowo (hex) zakres adresu to: 0x38 – 0x3F
Podłączanie do arduino:
Cyfrowymi pinami ekspandera P0-P7 możemy posługiwać się dokładnie tak samo jak cyfrowymi pinami Arduino. My będziemy je używali do ustawiania stanu wyświetlaczy – ale o tym za chwilę.
A jeśli chcielibyśmy nadać inny adres ekspanderowi to możemy dowolnie konfigurować połączenia pinów adresowych z masą GND bądź zasilaniem VCC – uzyskując tym samym 8 kombinacji dla PCF8574 oraz 8 kombinacji dla PCF8574A, dzięki czemu możemy użyć na raz 16 ekspanderów co da nam bagatela dodatkowych 128 (16*8) pinów cyfrowych!
Obsługa programowa ekspandera jest równie prosta jak układu DS1307. Wystarczy ściągnąć bibliotekę o zdumiewającej nazwie PCF8574, dodać do katalogu z bibliotekami i od tej pory za pomocą kilku linijek kodu z łatwością możemy używać PCF8574 do odczytywania, bądź zapisywania danych na cyfrowe piny.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
#include <PCF8574.h> #include <Wire.h> PCF8574 expander; void setup() { expander.begin(0x20); expander.pinMode(4, OUTPUT); } void loop() { expander.digitalWrite(4, LOW); delay(1000); expander.digitalWrite(4, HIGH); delay(1000); } |
Jak widzimy na początku należy ustawić adres ekspandera u nas 0x20, a potem skonfigurować piny wedle wykorzystania na INPUT bądź OUTPUT, a potem czytamy metodą digitalRead a ustawiamy stan metodą digitalWrite. Twórca biblioteki dodał kilka dodatkowych metod takich jak:
- Clear – ustawia wszystkie piny na stan LOW,
- Set – ustawia wszystkie piny na stan HIGH,
- Write – ustawia cały bajt na 8 pinach.
Bardziej dociekliwych uprzejmie odsyłam do dokumentacji biblioteki ;-)
4. Wyświetlacz
Do pokazywania godziny będziemy używać czterech wyświetlaczy 7 segmentowych. Te cuda wyglądają tak:
Jak widzimy, wyświetlacz siedmio-segmentowy posiada siedem segmentów (o rety!), ale żeby utrudnić sprawę w podstawce jest 10 nóżek. Ha! Po krótkiej (bądź długiej, zależy kto ma jaką lotność) chwili dostrzegamy, że są dwa typy wyświetlaczy – ze wspólną katodą, gdzie na te dwie dodatkowe nóżki podłączam GND, oraz ze wspólną anodą, gdzie podłączamy VCC. No ale 7 segmentów + 2 GND/VCC to daje nam 9 pinów, a jest przecież 10. Ta ostatnia to tzw. dp – czyli kropka ;-)
Uzbrojeni w tę wiedzę możemy śmiało przystąpić do wyświetlania czego nasza dusza zapragnie – czyli liczb od 0 do 9 :D
Warto jeszcze zwrócić uwagę, na to, że wyświetlaczem ze wspólną katodą sterujemy za pomocą stanu wysokiego, tzn. że jeśli chcemy zapalić którąś belkę to musimy wysłać na jej pin stan wysoki (HIGH), a z kolei wyświetlaczem ze wspólną anodą sterujemy stanem niskim (LOW).
Teraz dla pogłębienia wiedzy podłączymy sobie pod nasz ekspander jeden wyświetlacz ze wspólną anodą:
Gwoli jasności mamy połączony ekspander z wyświetlaczem w następujący sposób:
P0 – e P2 – c P4 – b P6 – f
P1 – d P3 – dp P5 – a P7 – g
A teraz będziemy wyświetlać po kolei cyfry od 0 do 9. Super, nie? ;-)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
#include <PCF8574.h> #include <Wire.h> PCF8574 expander; #define NOS 10 //Liczba możliwych do wyświetlenia cyfr byte displayArray[NOS] = { B00011000, // 0 B01111011, // 1 B00101100, // 2 B00101001, // 3 B01001011, // 4 B10001001, // 5 B10001000, // 6 B00111011, // 7 B00001000, // 8 B00001001 // 9 // bafgxcde }; void setup() { Serial.begin(9600); expander.begin(0x20); for (byte i=0; i<8; i++) expander.pinMode(i, OUTPUT); } void loop() { for ( int iter = 0; iter < NOS; ++iter ) { expander.write(displayArray[iter]); delay(500); } } |
Wszystko pięknie, ładnie ale my przecież chcemy koniec, końców wyświetlać czas, przecież nie damy rady tego zrobić na jednym wyświetlaczu… Załóżmy, że chcemy pokazywać godzinę oraz minuty, zatem potrzebujemy 4 wyświetlaczy. Ale czy to oznacza, że potrzebujemy 4 ekspanderów? Trochę dużo tego wszystkiego wyjdzie. Może istnieje jakieś lepsze (tzn. mądrzejsze) rozwiązanie. Hehe gdyby nie istniało to bym Was drodzy Czytelnicy nie zanudzał tymi rozważaniami ;-)
Otóż, należy zastosować multipleksowanie – czyli jak podpowiada wikipedia, zastosować dwa lub większą liczbę kanałów komunikacyjnych w jednym medium transmisyjnym. Mądrze brzmi, ale części z Was szanownych czytelników niczym młodziutkiej Cerro do króla Vridanka na ich pierwszej schadzce ciśnie się na usta pytanie: “Niebrzydka rzecz, ale czy ma jakieś zastosowanie praktyczne?”.
Otóż, o dziwo ma! Dobrze przedstawia to następujący obrazek:
Sterujemy wyświetlaczami za pomocą tranzystorów PNP – BC327, które podpinamy przez rezystory do procesora. Ustawienie sygnału LOW na bazie tranzystora powoduje przepływ prądu – dzięki czemu włączymy dany wyświetlacz. Jeśli będziemy dostatecznie szybko się przełączać to ludzkie oko nie zarejestruje migania.
Nasz prototyp wygląda tak:
BTW proszę nie zapominać o rezystorach pull-up na magistrali I2C – nie będę ich już dodawał do schematu bo i tak jest już dostatecznie nieczytelny…
A teraz, żeby sprawdzić czy układ dobrze hula, napiszmy kod, który będzie wyświetlał liczby od 0 do 9999. A co! Jak szaleć to szaleć ;-)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
#include <PCF8574.h> #include <Wire.h> const int DELAY_TIME = 5; PCF8574 expander; #define NOS 10 //Liczba możliwych do wyświetlenia cyfr byte displayArray[NOS] = { B00011000, // 0 B01111011, // 1 B00101100, // 2 B00101001, // 3 B01001011, // 4 B10001001, // 5 B10001000, // 6 B00111011, // 7 B00001000, // 8 B00001001 // 9 // bafgxcde }; int tran1 = 2; int tran2 = 3; int tran3 = 4; int tran4 = 5; int iter1 = 0; int iter2 = 0; int iter3 = 0; int iter4 = 0; void setup() { Serial.begin(9600); expander.begin(0x20); for (byte i=0; i<8; i++) expander.pinMode(i, OUTPUT); pinMode(tran1, OUTPUT); pinMode(tran2, OUTPUT); pinMode(tran3, OUTPUT); pinMode(tran4, OUTPUT); } void turnOff() { digitalWrite(tran1, HIGH); digitalWrite(tran2, HIGH); digitalWrite(tran3, HIGH); digitalWrite(tran4, HIGH); } void loop() { //tran 1 turnOff(); expander.write(displayArray[iter1]); digitalWrite(tran4, LOW); delay(DELAY_TIME); //tran 2 turnOff(); expander.write(displayArray[iter2]); digitalWrite(tran3, LOW); delay(DELAY_TIME); //tran 3 turnOff(); expander.write(displayArray[iter3]); digitalWrite(tran2, LOW); delay(DELAY_TIME); //tran 4 turnOff(); expander.write(displayArray[iter4]); digitalWrite(tran1, LOW); delay(DELAY_TIME); iter1++; if ( iter1 == 10 ) iter1 = 0; iter2++; if ( iter2 == 10 ) iter2 = 0; iter3++; if ( iter3 == 10 ) iter3 = 0; iter4++; if ( iter4 == 10 ) { iter1 = 0; iter2 = 0; iter3 = 0; iter4 = 0; } } |
5. Zegar = DS1307 + PCF8574 + 4x 7 seg LCD
Dochodzimy do wiekopomnego momentu, gdzie użyjemy naszego DS1307 opisanego na samym początku artykułu do odczytywania czasu. Co w sumie sprowadzi się do dodania na płytkę prototypową DS1307 i połączenia go z magistralą I2C.
Teraz będziemy sobie wyświetlać czas odczytany z zegarka DS1307.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
#include <PCF8574.h> #include <Wire.h> #include <RTClib.h> PCF8574 expander; RTC_DS1307 RTC; const int DELAY_TIME = 5; #define NOS 10 //Liczba możliwych do wyświetlenia cyfr byte displayArray[NOS] = { B00011000, // 0 B01111011, // 1 B00101100, // 2 B00101001, // 3 B01001011, // 4 B10001001, // 5 B10001000, // 6 B00111011, // 7 B00001000, // 8 B00001001 // 9 // bafgxcde }; int tran1 = 2; int tran2 = 3; int tran3 = 4; int tran4 = 5; void setup() { Serial.begin(9600); expander.begin(0x20); for (byte i=0; i<8; i++) expander.pinMode(i, OUTPUT); pinMode(tran1, OUTPUT); pinMode(tran2, OUTPUT); pinMode(tran3, OUTPUT); pinMode(tran4, OUTPUT); Wire.begin(); RTC.begin(); if ( !RTC.isrunning() ) Serial.println("RTC is NOT running!"); // following line sets the RTC to the date & time this sketch was compiled RTC.adjust(DateTime(__DATE__, __TIME__)); } void turnOff() { digitalWrite(tran1, HIGH); digitalWrite(tran2, HIGH); digitalWrite(tran3, HIGH); digitalWrite(tran4, HIGH); } void loop() { DateTime now = RTC.now(); int hour = now.hour(); int min = now.minute(); //tran 1 turnOff(); expander.write(displayArray[hour / 10]); digitalWrite(tran1, LOW); delay(DELAY_TIME); //tran 2 turnOff(); expander.write(displayArray[hour % 10]); digitalWrite(tran2, LOW); delay(DELAY_TIME); //tran 3 turnOff(); expander.write(displayArray[min / 10]); digitalWrite(tran3, LOW); delay(DELAY_TIME); //tran 4 turnOff(); expander.write(displayArray[min % 10]); digitalWrite(tran4, LOW); delay(DELAY_TIME); } |
Jak widać używanie układów współpracujących z magistralą I2C zasadniczo ułatwia i programowanie i składanie układu.
7. Przyciski
Fajnie jakby można było ustawić sobie dowolny czas – prawda? No, to dodajmy trzy takie oto maleństwa.
Pierwszy przycisk będzie odpowiedzialny za wejście w tryb ustawiania (setup) i załóżmy, że trzeba będzie chwilę przytrzymać wciśnięty przycisk aby ten tryb ustawiania się włączył. Tym przyciskiem będziemy również akceptować ustawienia. Drugim przyciskiem (up) będziemy mogli zwiększać wartość. A trzecim przyciskiem (down) będziemy zmniejszali wartość.
Warto zapoznać się z tzw. “bounce” przycisków.
A i bym zapomniał, po wejściu w tryb setup zmieniania wartość będzie mrugać – tak żeby było weselej ;-)
Na początek schemat:
By nam przyjemnie się programowało przyciski, to użyjemy biblioteki OneButton, która między innymi rozwiązuje problem wcześniej wspomnianego “bounce’u”. W sumie nie ma co się rozpisywać nad kodem, zatem jakby ktoś miał pytanie/wątpliwość proszę pytać w komentarzach ;-)
Kodu jest dużo, więc postanowiłem go zwinąć.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 |
#include <PCF8574.h> #include <Timer.h> #include <Wire.h> #include <RTClib.h> #include <OneButton.h> const int DELAY_TIME = 5; const byte MODE_TIME = 0x00; const byte MODE_SETUP = 0x01; const byte MODE_SETUP_HOUR = 0x02; const byte MODE_SETUP_MINUTE = 0x03; const byte OFF_SEGEMENT = 10; byte displayArray[10] = { B00011000, // 0 B01111011, // 1 B00101100, // 2 B00101001, // 3 B01001011, // 4 B10001001, // 5 B10001000, // 6 B00111011, // 7 B00001000, // 8 B00001001, // 9 B11111111 //OFF // bafgxcde }; RTC_DS1307 RTC; PCF8574 expander; byte _counter = 0x00; byte _hour = 0x00; byte _minute = 0x00; byte _tran1 = 0x02; byte _tran2 = 0x03; byte _tran3 = 0x04; byte _tran4 = 0x05; byte _countBlinkingHour = 0; byte _countBlinkingMinute = 0; bool _blinkingHour = false; bool _blinkingMinute = false; byte _mode = MODE_TIME; OneButton btnSetup(6, true); OneButton btnUp(7, true); OneButton btnDown(8, true); void setup() { Serial.begin(9600); expander.begin(0x20); for (byte i=0; i<8; i++) expander.pinMode(i, OUTPUT); pinMode(_tran1, OUTPUT); pinMode(_tran2, OUTPUT); pinMode(_tran3, OUTPUT); pinMode(_tran4, OUTPUT); pinMode(A0, OUTPUT); btnSetup.setClickTicks(DELAY_TIME); btnUp.setClickTicks(DELAY_TIME); btnDown.setClickTicks(DELAY_TIME); btnSetup.attachClick(pressedSetupBtn); btnUp.attachClick(pressedUpBtn); btnDown.attachClick(pressedDownBtn); Wire.begin(); RTC.begin(); if (! RTC.isrunning()) { Serial.println("RTC is NOT running!"); // following line sets the RTC to the date & time this sketch was compiled RTC.adjust(DateTime(__DATE__, __TIME__)); } } void turnOff() { digitalWrite(_tran1, HIGH); digitalWrite(_tran2, HIGH); digitalWrite(_tran3, HIGH); digitalWrite(_tran4, HIGH); } void print7Seg(byte pin, byte val) { turnOff(); if ( (pin % 2) == 1 ) expander.write(displayArray1[val]); else expander.write(displayArray2[val]); digitalWrite(pin, LOW); } void printTime() { if ( _blinkingHour ) _countBlinkingHour++; else _countBlinkingHour = 0; if ( _countBlinkingHour > 20 ) _countBlinkingHour = 0; if ( _countBlinkingHour > 10 ) { print7Seg(_tran1, OFF_SEGEMENT); delay(DELAY_TIME); print7Seg(_tran2, OFF_SEGEMENT); delay(DELAY_TIME); } else { print7Seg(_tran1, _hour / 10); delay(DELAY_TIME); print7Seg(_tran2, _hour % 10); delay(DELAY_TIME); } if ( _blinkingMinute ) _countBlinkingMinute++; else _countBlinkingMinute = 0; if ( _countBlinkingMinute > 20 ) _countBlinkingMinute = 0; if ( _countBlinkingMinute > 10 ) { print7Seg(_tran3, OFF_SEGEMENT); delay(DELAY_TIME); print7Seg(_tran4, OFF_SEGEMENT); delay(DELAY_TIME); } else { print7Seg(_tran3, _minute / 10); delay(DELAY_TIME); print7Seg(_tran4, _minute % 10); delay(DELAY_TIME); } } void pressedSetupBtn() { if ( _mode == MODE_TIME ) { _mode = MODE_SETUP; _counter = _hour; } else if ( _mode == MODE_SETUP_HOUR ) { _mode = MODE_SETUP_MINUTE; _counter = _minute; } else if ( _mode == MODE_SETUP_MINUTE ) { _mode = MODE_TIME; _counter = 0x00; RTC.adjust(DateTime(0, 0, 0, _hour, _minute, 0)); } } void pressedUpBtn() { _counter++; if ( _mode == MODE_SETUP_HOUR ) { if ( _counter > 23 ) _counter = 0; } else if ( _mode == MODE_SETUP_MINUTE ) { if ( _counter > 59 ) _counter = 0; } } void pressedDownBtn() { _counter--; if ( _mode == MODE_SETUP_HOUR ) { if ( _counter > 24 ) _counter = 23; } else if ( _mode == MODE_SETUP_MINUTE ) { if ( _counter > 60 ) _counter = 59; } } void loop() { btnSetup.tick(); btnUp.tick(); btnDown.tick(); if ( _mode == MODE_TIME ) { _blinkingHour = false; _blinkingMinute = false; DateTime now = RTC.now(); _hour = now.hour(); _minute = now.minute(); } else if ( _mode == MODE_SETUP ) { _mode = MODE_SETUP_HOUR; } else if ( _mode == MODE_SETUP_HOUR ) { _blinkingHour = true; _blinkingMinute = false; _hour = _counter; } else if ( _mode == MODE_SETUP_MINUTE ) { _blinkingHour = false; _blinkingMinute = true; _minute = _counter; } printTime(); } |
7. ATMEGA328P standalone
Przeniesiemy teraz nasz procesor ATMEGA328 na płytkę prototypową.
Jeśli chcemy użyć nowego procesora, będziemy musieli wgrać do niego bootloader arduino. Tutaj jest ładny na ten temat tutorial: http://arduino.cc/en/Tutorial/ArduinoToBreadboard. A jeśli ktoś nie ma czasu bądź ochoty się z tym samemu męczyć, to można nabyć w sklepie nettigo.pl od razu procesor z bootloaderem http://nettigo.pl/products/175
Po wgraniu bootloadera możemy zająć się wgrywaniem kodu. Płytka tak powinna wyglądać:
Grubo, prawda? ;D Zostaje jeszcze do odcięcia zasilanie i możemy odłożyć arduino na półkę.
8. Stabilizator 7805
Zawsze kiedyś przychodzi moment, gdy coś się kończy i coś się zaczyna.
Odcinamy ostatnie przewody łączące nas z Arduino i od tej pory nasz układ będzie wieść życie wolne i niezależne! Oh! Aż mi się łza zakręciła z wzruszenia…
Nie jestem specjalistą stabilizatorów i prawdopodobnie zrobiłem jakiegoś babola, u mnie to w sumie działa ale nie jestem przekonany czy 6V to jest dobre wejście na zasilanie…
W każdym razie, na nogę input dajemy zasilanie z zakresu +5V (możliwe, że powinno być powyżej 7.5V) a z nogi output ciągniemy stabilizowane 5V, warto jeszcze dodać na wejściu i wyjściu po kondensatorze elektrolitycznym, aby zlikwidować tętnienia.
No i już prawie na sam koniec pokażę całość:
Przy podłączaniu kondensatorów elektrolitycznych należy zwrócić szczególną uwagę, aby podłączyć + do +, i – do -. Dłuższa noga kondensatora to +, a z boku na obudowie widać pasek z minusami.
9. PCB
Jako, że cały ten projekt potraktowałem edukacyjnie, to również z tego samego powodu stworzyłem projekt w eaglu, razem z płytką PCB, którą wytrawiłem i nawet zdaje się, że działa ;-)
A tak wygląda płytka:
Zamieszczam też link do:
Między kodem z artykułu a z linka są drobne różnice.
10. Koniec
Jeśli ktoś dotarł do tego miejsca, to mam dla niego jedną radę: zajmij się kolego czymś pożyteczniejszym niż czytaniem pierdół po internecie…
A tak na poważnie, to dzięki za wytrwałość. Czekam na pytania, sugestie oraz komentarze.
Pozdrawiam,
—
malebranchi
PS: Serdeczne podziękowania Kasi, za poprawki edytorskie :*
Wielki kawał naprawdę dobrej roboty! Coś czuję we krwi, że główną masz gwarantowaną :)
Świetny artykuł. Jasno wszystko wyjaśnione i mega się napracowałeś. Główna murowana.
Idealnie wcelowałeś się z tym postem jeżeli chodzi o mnie – mam chęć zrobienia podobnego zegara, przetarłeś mi właśnie ścieżkę! : )
Szkoda, że osoby dające oceny niższe niż 5, nie piszą czego im zabrakło…
Wydaje mi się, że na samym początku ktoś znudzony dał “1”, a później sypały się już same piąteczki :)
No no… dobra robota :) Ja też chcę wykonać zegar ale na rejestrach przesównych 74HC595 bo mam ich sporą ilość :D Ile zajmuje kod po wrzuceniu na procek?
chylę czoła, świetny artykuł.
a wiesz może jak kalibrować taki układ zegarowy?
bo mój zegar, też oparty na ds1307 spóźnia około minuty na 3 dni…
chodzi mi o jakieś rozwiązanie “niesoftłerowe” :)
Kiedyś tez tak miale z innym zegarem zegarem to jest często wina 32.768KHz one maja rozbieżne parametry, po kilku próbach z różnymi egzemplarzami zegarek chodził punktualnie (no możne kilka min na rok było źle ,ale to już wybaczyłem)
A gdzie filmik ? :D
Fajny artykuł, wkrótce też się za zegar zabiorę. Mam jedno zastrzeżenie (wiem, że obeznani w temacie nie zrobią takiego błędu, ale całkowicie zieloni mogą sobie coś spalić), otóż na schemacie z podpiętym ATmega328 sprawdź czy tam napewno dobrze zasilanie z pierwszej płytki dałeś (z tego co widzę to na linię + dałeś + i GND ;)). Wiem, że się może czepiam ale za godzinę mam egzamin, cały ze stresu chodzę i żeby się odprężyć zacząłem analizować dokładnie wszystkie schematy, no i się błędu dopatrzyłem ;)
Ale artykuł i tak świetny.
Dzięki serdeczne za łechcące mą próżność miłe słowa pochwały ;-)
krzxsiek,
Też w sumie myślałem o 74HC595, ale że akurat miałem pod ręką ekspander to użyłem ekspandera, nie mniej faktycznie lepszym pomysłem jest użycie 74HC595, choć pewnie ciut trudniejsze ;-)
A kod z tego co pamiętam około 8200 bajtów.
bocianu,
Niestety ale mój zegarek póki co tak długo nie działał, żebym zauważył rozkalibrowanie. Jedyne co mi przychodzi do głowy, to jakość kwarcu, może też jego odległość od nóżek X1, X2 ma znaczenie. W każdym razie włączę teraz swój i zobaczę co będzie po kilku dniach.
Kanaron,
Dzięki! Już poprawione.
Pozdrawiam,
—
malebranchi
z 74HC595 wydaje mi się nawet prostsze :) http://arduino-for-beginners.blogspot.com/2010/12/digital-clock-with-7-segments-led-and.html
Kupiłem sobie na ebayu zapasik 74HC595 10szt. za 6zł. :D
Ogólnie i tak są tanie bo coś ok. 1zł. a w mniejszych obudowach taniej nawet.
W każdym razie im więcej sposobów wykonania tym lepiej bo zawsze ktoś wybierze dla siebie łatwiejszy i z części które ma pod ręką :)
Tutaj jest też dobry tutorial (ale pewnie już tam sam trafiłeś):
http://arduino.cc/en/Tutorial/ShiftOut
A pisząc: “ciut trudniejsze” – miałem na myśli, że jednak to kolejna magistrala do ogranięcia, a na PCB kolejne ścieżki do upchnięcia…
Choć 74HC595 ma kilka zalet, jak sam zauważyłeś jest zdecydowanie tańszy, a chyba też jest szybszy, więc zdecydowanie warto się nim zainteresować ;-)
Pozdrawiam,
—
malebranchi
Jestem pod wielkim wrażeniem Twojego artykułu i tego w jaki sposób przedstawiłeś swój projekt. Szczere gratulacje i oby tak dalej! ;) Pozdrawiam!
Tutaj jest fajny projekt z DS1307: http://www.jarzebski.pl/blog/2013/04/25/arduino-monitoruje-linuksa-monitino-uno.html :D
Super sprawa :) W bardzo prosty i przejrzysty sposób opisane krok po kroku jak wykonać zegar. Cieszę się, że na stronie Majsterkowa zaczęły w końcu pojawiać się też artykuły dla laików. Oby tak dalej :) Czekam na kolejną porcję elektronicznych zagwostek :)
Ciekawe by to było gdyby zegar miał kilka wyjść do sterowania urządzeń, np. do akwarium: jeden kanał – oświetlenie, a drugi podawanie CO2.
Największą sztuką to zrobić dobry interfejs do programowania. Tak by można ustawiać niezależnie kanały, a także zależnie, np. włącz 1godzinę przed kanałem nr 2. Plus zegar astronomiczny, żeby skorelować się ze słońcem.
Na razie to zegar jest na poziomie MC1201 i MC1204, które montowałem 30 lat temu.
Nie jestem specjalistą z zakresu ichtiologi ani biologi ba nawet trawnika porządnie nie umiem skosić ale podawanie do akwarium CO2 wydaje mi się idiotycznym pomysłem…
Jak ten DS1307 trzyma czas? Ja mam taki na i2c na gotowej płytce z własnym kwarcem i lipa. Po miesiącu rozjechał się aż o minutę. Jak dla mnie to o minutę za dużo. Rozumiem że puści 1min/12miesięcy….. a tak to niestety zabawka.
Szymon: I tu się mylisz kolego, jeżeli masz akwarium roślinne to jak najbardziej podaje się CO2.Trzeba tylko puszczać przez drewno lipowe i oczywiście trzeba ilość CO@ kontrolować (ph)
Do pełni szczęścia brakuje tylko synchronizacji z zegarem atomowym w Niemczech.
bocianu, Szymon,
Przyjrzałem się problemowi trzymania czasu, i wygląda na to, że nie ma prostego rozwiązania. Głównym problemem jest kwarc, który niestety ale jak to element fizyczny, ma swoją niedokładność – i właśnie z tego bierze się błąd zegarka.
Dobrze jest to tutaj opisane:
http://mikrokontrolery.blogspot.com/2011/04/stopery-timery-itp.html
Żeby poprawić dokładność, można kupić lepszy zegar RTC – np. DS3231(na ebay koszt ok. 20 PLN). Przykład użycia: http://www.l8ter.com/?p=417
Innym całkiem sensownym rozwiązaniem jest synchronizacja radiowa via układ DCF-77. Można też synchronizować co jakiś czas ręcznie – co według mnie jest najlepszym rozwiązaniem :P
Pozdrawiam,
—
malebranchi
Czy ktoś mógłby w pierwszym kodzie zamienić “voidsetup” i “voidloop” na “void setup” i “void loop”?
Poprawione!
—
malebranchi
Nie łatwiej zamiast zapisywać adres urządzenia w hex’ie, zrobić to w bin’ie?
Np. zamiast 0x20: B0100000.
To już zależy komu co bardziej pasuje. Kompilator i tak widzi to jako jedno i to samo. Można nawet wpisywać dziesiętnie, Kwestia wprawy i lenistwa. W HEX’ie jest mniej pisania i łatwiej nie pomylić się z ilością zer.
Witam
Mam pewną zagwozdkę z tym co jest na schemacie a z tym co jest na PCB.
Dokładnie chodzi o podłączenie wyświetlaczy 7 segmentowych, podam na przykładzie jednej z nóżek, powiedzmy górnej lewej, na schemacie wyraźnie widać że każdy z wyświetlaczy ma tą nóżkę podłączona ze sobą, natomiast na PCB jest tak że pierwsza jest połączona z ostatnią później znowu z pierwszą i znowu z ostatnią. Jak to działa.
Cześć Piotrze,
Cieszmy mnie bardzo Twoje pytanie, bo to oznacza, że ktoś jednak dokładnie wczytał się w artykuł ;-)
A przechodząc do pytania, to – faktycznie połączenie wyświetlaczy na PCB a na schematach płytki prototypowej różni się. Wynika to z prowadzenia ścieżek na PCB, gdzie ścieżki nie mogą się krzyżować, a nie chciałem robić przelotek (prowadzić ścieżek na górnej warstwie płytki). W zasadzie po takim połączeniu powstają dwa „rodzaje” wyświetlaczy, chodzi mi o to, że inne wartości trzeba wysłać na PINy poszczególnych wyświetlaczy (zwróć proszę uwagę, że to wszystko jest połączone ze sobą, aż do ekspandera) – u mnie w kodzie nazywa się to „displayArray1” oraz „displayArray2”.
Jeśli masz jeszcze jakieś pytania to proszę śmiało pisz.
Pozdrawiam,
—
malebranchi
Dziękuję za odpowiedź.
Bardzo pomysłowe rozwiązanie, a już myślałem że dzieją się tutaj jakieś czary :)
Upraszcza to w znacznym stopniu ścieżki na PCB, pomysł warty zapamiętania !!! Gratuluje udanego projektu.
Wszystko było by cacy tylko autor nie wspomniał że przy dobranych elementach nie uzyskuje się pełnego świecenia segmentów.
Przy zwiększeniu czasu oczekiwania na rozjaśnienie diody z kolei uzyskujemy mruganie.
Pozdrawiam.
P.S chyba że tylko ja widzę że segmenty świecą ciemniej :D
Pingback: Pilot IR do Nikon'a, czyli tworzymy film poklatkowy » Majsterkowo.pl
Pingback: Pilot IR do Nikon'a, czyli tworzymy film poklatkowy » Majsterkowo.pl
Dzięki tobie zorientowałem się, jak sterować wyświetlaczami 7-seg ze wspólną anodą, wielkie dzięki :>
małe sprostowanie, gnd z baterii powinno się podłączyć bezpośrednio do gnd stabilizatora, ponieważ jak przypadkowo odłączymy kabel łączący stabilizator z gnd baterii, stabilizator zachowywuję się jak zworka. Mam nadzieję że zrozumieliście. Łatwo to sprawdzić miernikiem :D
Dzień dobry.
Realizuję opisany projekt i natrafiłem na przeszkody.
W pierwszym szkicu komputer pokazuje mi błąd w linii 4 (RTC_DS1307RTC;)
Jak rozwiązać ten problem?
Będę wdzięczny za sugestie.
Cześć Makus,
Przepraszam, że dopiero teraz odpisuję, ale za często tu nie zaglądam…
W kodzie pojawił się błąd (już poprawiłem) – nazwa klasy RTC_DS1307 skleiła się z nazwą instancji RTC – tam powinna być spacja.
Tzn. tak to powinno wyglądać:
RTC_DS1307 RTC;
Pozdrawiam,
—
malebranchi
Fajna sprawa właśnie analizuję cały układ i zastanawiam się czy układ będzie działał na atmega 8 ?
milczas,
Nie znajduję powodu dla którego układ miałby nie zadziałać na Atmega8. Proponuję abyś na początku najpierw zbudował sobie zegarek na płytce prototypowej.
Pozdrawiam,
—
malebranchi
Świetny projekt. Jedyne co bym zmienił, to wyrzucił stabilizator, który i tak raczej nie daje 5V na wyjściu ze względu na zbyt niskie napięcie zasiania, a zamiast 4 baterii dał bym 3 które by dały 4,5V co powinno też być akceptowalne przez ten układ. Po mojemu ten LM7805 to strata energii, a napięcie z baterii + jakiś kondensator filtrujący da wystarczająco stabilne zasilanie do momentu wyczerpania baterii oczywiście, ale wtedy to już nic nie pomoże.:)
Witam
Zegarek zmontowany dzięki dobremu opisowi wykorzystałem wsad dorobiłem ustawianie czasu za pomocą pilota IR automatyczne przyciemnianie.
Chciałbym jeszcze wyłączać pierwsze 0 tzn aby przy godzinie jedno cyfrowej np 9:00 nie wyświetlał 09:00 .
Nie bardzo wiem jak to wykonać. Pomoc mile widziana :-)).
bo trzeba sie utrzyć ‘c’ a nie aby kopiuj wklej tuma…e
wiem że post już dawno był wstawiony ale mam pytanie : otóż czy zamiast układu pcf8574 może być pcf8754p ?
A ja mam pytanie z innej beczki: otóż mam swój własny zegar (póki co tylko zegar, docelowo sterownik do akwarium) i chciałbym móc zmieniać czas za pomocą przycisków. Czy to zimowy/letni czy po prostu skorygować RTC (DS1307). Dopiero zaczynam przygodę z Arduino i nie mogę znaleźć jakiegoś gotowego, przejrzyście przedstawionego przykładu. Przyciski w tym projekcie spełniają moje założenia, jednak nie bardzo wiem jak je zaimplementować. Pomoże ktoś? ;)
Mógłby ktoś wstawić PDFa z PCB gotowego do wydruku? niestety nie mogę w eaglu otworzyć tego pliku, wyskakuje mi jakiś błąd. Z góry dziękuję.
za umieszczenie , odwrotenie tego uchwytu na baterie juz powinienes dosta -1 ,
z góry widac ze nie bedzie jak wsadzic bateri o czym sie mozna przekonac ze mam racje po zdjeciu nizej gdzie nie wlutowałęś tego nawet :)
Jestem newbie
gdy wklepałem kod używający przyciski otrzymałem błąd że }; jest nieprawidłowym znakiem końca
usunąłem ; po czym otrzymałem komunikat że RTC was not declaired, poprawiłem
teraz otrzymuję komunikat: a function-definition is not allowed here before ‘{‘ token
Czy mogę prosić o pomoc?
Pewnie głupie pytanie ale dopiero zaczynam ;) Możesz wytłumaczyć jak odbywa się przesłanie kodu z arduino do atmegi??
Zerknij tutaj:
– https://majsterkowo.pl/programowanie-mikrokontrolerow-za-pomoca-programatora-usbasp/
– https://majsterkowo.pl/progamowanie-mikrokontolerow-na-przykladzie-attiny2313/
– https://majsterkowo.pl/programowanie-mikrokontrolerow-avr-przez-pl2303-przy-uzyciu-arduino-ide/
Ślicznie dzięki Łukasz ;) Mam jeszcze pytanie trochę z innej beczki czy coś stoi na przeszkodzie żeby podłączyć pod zegar wyświetlacz led 16×64??
Dawid,
W gruncie rzeczy, przecież to nie ma z punktu wiedzenia hardware
żadnego znaczenia na czym wyświetlacz dane. Oczywiście musisz
zmodyfikować kod programu, tak by używał Twojego wyświetlacza.
—
http://malebranchi.waw.pl
Dawid,
W gruncie rzeczy, przecież to nie ma z punktu wiedzenia hardware żadnego znaczenia na czym wyświetlacz dane. Oczywiście musisz zmodyfikować kod programu, tak by używał Twojego wyświetlacza.
—
http://malebranchi.waw.pl
Witam
Od niedawna zacząłem zabawę z arduino i mam mały problem z powyższym sketchem ponieważ wykrywa problem z zdefiniowaniem „displayArray1” oraz „displayArray2”. Czy ktoś może mi z tym pomóc.
Z góry dziękuję.
Wstaw tabele:{
B00011000, // 0
B01111011, // 1
B00101100, // 2
B00101001, // 3
B01001011, // 4
B10001001, // 5
B10001000, // 6
B00111011, // 7
B00001000, // 8
B00001001 , // 9
B11111111, //OFF
};
I podłącz wyśiwtlacz tak jak na rysunku z jednym wyświetlaczem….
Witam jakiś czas siedze nad kodem 6 cyfr z sekundnikiem, niestety poległem.
Czy możecie pomóc w tym temacie?
Zainspirowany projektem postanowiłem go sobie wykonać. Nawet zamówiłem elementy do jego wykonania. Mają mi przyjść cyfry led o wysokości około 60mm. Jeden segment składa się z trzech diod. I dopiero teraz mnie oświeciło że te 5v może to za małe by zasilić
moje wyświetlacze. Pomyślałam więc że doprowadze wyższe zasilanie przez tranzystory. Ale w projekcie są typu pnp więc nie wiem czy da rade to zrobić przy pomocy tego elementu. Jak mam zmienić układ projektu by wykożystać te duże wyświetlacze. A może wystarczy tego napięcia i by odpalić segment. Podaje link do specyfikacji wyświetlacza.
http://www.artronic.com.pl/o_produkcie.php?id=505. Proszę o poradę i pozdrawiam.
Sprawdziłem żeby wszystkie segmenty się zapaliły z odpowiednią jasnością w tym wyświetlaczu potrzebuje 7v.
Jakiś dziwny ten wyświetlacz. Nie spotkałem się jeszcze aby było kilka diod świecących w szeregu dla jednego segmentu. Jak go dostaniesz to sprawdź przy jakim napięciu świeci segment. Jak przy 3V świeci to jest ok i nie trzeba nic zmieniać.
Co do projektu to stosowanie baterii, stabilizatora i wyświetlaczy LED to słaby pomysł. Chwila moment i wyssie baterie. Tak na oko to pobór prądu będzie około 100mA jak nie więcej. Dla stabilizatora 7805 minimalny spadek napięcia między wejściem a wyjściem to 2V. Lepiej by było zastosować jakiś zasilacz np 12V i dać radiator na stabilizatorze.
Witam, a mógłby ktoś wstawić zegarek na LCD z ustawianiem czasu tak jak w powyższym projekcie? Albo przerobić istniejący projekt pod wyświetlacz LCD tak żeby ustawianie czasu zostało?
Moi mili konstruktorzy powiedzcie mi czy bez baterii a tylko na 5V będzie działał poprawnie. Ja rozumiem że za każdym włączeniem trzeba by było ustawiać zegar ale każde włączenie to stan o:0:0
Pozdrawiam Maciej
Może ktoś wstawić bibliotekę Timer.h?
Autor nie wspomniał że tym sposobem przy tych wartościach rezystorów nie uzyskamy pełnego świecenia segmentów..
Trzeba by było zastosować dłuższy czas oczekiwania ale wtedy mamy efekt mrugania.
Pozdrawiam.
SUPER ARTYKUL. MAM JEDNO PHYTANIE (JESTEM ZIELONY). MAM JUZ TEN DRUGI MIKROKONTROLER Z BOTLOADEREM. JAK WGRAĆ ZA POMOCĄ ARDUINO NA NIEGO KOD ??
“Po wgraniu bootloadera możemy zająć się wgrywaniem kodu. Płytka tak powinna wyglądać:”
E possibile perdere 4 kg in 2 settimane
stihi.ru
Cześć, Czy mogę prosić o pomoc? jestem przy punkcie 2 i wyskakuje mi błąd przy kompilowaniu: “‘DateTimenow’ was not declared in this scope” ?
Hej, wystarczyło przejrzeć kod zamieszczony w dalszej części artykułu….
DateTime now = RTC.now();
W przyszłości polecam zaglądać także do wnętrza biblioteki :) tam wszystko jest napisane.
7. Przyciski. szkic zawarty w tym punkcie zwraca błąd przy kompilacji:
Arduino:1.8.11 (Windows 7), Płytka:”Arduino Nano, ATmega328P (Old Bootloader)”
C:\Users\Piscor\Desktop\zegar_dupa\zegar_dupa.ino: In function ‘void print7Seg(byte, byte)’:
zegar_dupa:104:20: error: ‘displayArray1’ was not declared in this scope
expander.write(displayArray1[val]);
^
C:\Users\Piscor\Desktop\zegar_dupa\zegar_dupa.ino:104:20: note: suggested alternative: ‘displayArray’
expander.write(displayArray1[val]);
^
displayArray
zegar_dupa:106:20: error: ‘displayArray2’ was not declared in this scope
expander.write(displayArray2[val]);
^
C:\Users\Piscor\Desktop\zegar_dupa\zegar_dupa.ino:106:20: note: suggested alternative: ‘displayArray’
expander.write(displayArray2[val]);
^
displayArray
Znaleziono wiele bibliotek w “Wire.h”
Wykorzystane: C:\Program
Niewykorzystane: C:\Program
exit status 1
‘displayArray1’ was not declared in this scope
Zaczynam dopiero przygodę z Arduino i na sam początek nie chciałbym się zrazić. Hardware mam złożony poprawnie. Czy mogę prosić o pomoc ?