Po krótkiej przerwie przyszedł czas, na ostatnią już część nieco przydługiego cyklu o zegarku. Troszkę mnie poniosło i rozpisałem się o nim na 5 artykułów, a chciałbym Wam jeszcze pokazać przynajmniej jeden mój INNY projekt, wiec dziś zepnę się w sobie i postaram jak najmniej przynudzać.
Ja sam tytuł wskazuje zegar miał być z bajerami, no to będzie. Na razie mamy samą matrycę, no jak już wspomniałem w poprzednim wpisie za moment podłączymy moduł zegara czasu rzeczywistego. Mimo, że to prosty układzik, zdecydowałem się na gotowy moduł, który wygląda tak:
Drabinkę goldpinów przylutowałem sam, dlatego tak krzywo :D
Używać będziemy tylko 4 z nich:
- SCL -> Arduino pin ANALOG5
- SDA -> Arduino pin ANALOG4
- GND -> Arduino GND
- VCC -> Arduino 5V
Jak już wspominałem w poprzedniej części układ DS1307 komunikuje się ze światem przy pomocy dwukierunkowej magistrali I2C, która ze względów patentowych nazywa się w Arduino TWI (Two Wire Interface). Mamy gotową bibliotekę do komunikacji poprzez ten interfejs, która nazywa sie Wire.h. A do komunikacji z samym układem zegara wykorzystamy bibliotekę RTClib, pobraną ze strony: https://github.com/adafruit/RTClib. (Bibliotekę trzeba pobrać, i rozpakować do katalogu …/Moje Dokumenty/Arduino/libraries/ i zrestartować Arduino IDE)
No to kodujemy, czyli to co tygryski lubią najbardziej ;)
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 |
#include <SPI.h> #include "avr/pgmspace.h" #include <Wire.h> #include <RTClib.h> // wybór układu zegara czasu rzeczywistego RTC_DS1307 RTC; // przypisanie pinów #define ZATRZASK 4 // deklaracja rozmiaru matrycy #define ILOSC_WIERSZY 5 #define ILOSC_BAJTOW 2 #define ROZMIAR_EKRANU ILOSC_WIERSZY*ILOSC_BAJTOW // rozmiar pamięci ekranu (w bajtach) // definicja struktury danych pamięci obrazu matrycy struct t_Matryca { char dane[ILOSC_WIERSZY][ILOSC_BAJTOW]; }; t_Matryca m_Ekran; // deklaracja zmiennej m_Ekran przechowującej zawartość ekranu char m_Wiersz = 0; // numer aktualnie wyświetlanego wiersza (iterator) // definicje znaków prog_uchar znaki[10][ILOSC_WIERSZY] PROGMEM = { {7,5,5,5,7}, // 0 {1,1,1,1,1}, // 1 {7,1,7,4,7}, // 2 {7,1,7,1,7}, // 3 {5,5,7,1,1}, // 4 {7,4,7,1,7}, // 5 {7,4,7,5,7}, // 6 {7,1,1,1,1}, // 7 {7,5,7,5,7}, // 8 {7,5,7,1,7}, // 9 }; // i definicje obrazków prog_uchar o_test[ROZMIAR_EKRANU] PROGMEM = {238,110,72,132,76,68,72,36,78,196}; // napis TEST // ****************** funkcje obsługi ekranu // funkcja wyświetla na ekranie zawartość tablicy podanej jako parametr void mPoka(prog_uchar *zawartosc){ memcpy_P(&m_Ekran,zawartosc,ROZMIAR_EKRANU); //kopiowanie zawartości zmiennej do pamięci ekranu } // funkcja odwraca wszystkie bity matrycy void mNegatyw(){ for(char wiersz=0;wiersz<ILOSC_WIERSZY;wiersz++){ for(char bajt=0;bajt<ILOSC_BAJTOW;bajt++) { m_Ekran.dane[wiersz][bajt]=~m_Ekran.dane[wiersz][bajt]; } } } // funkcja czyści matrycę (wypełnia zerami) void mCzysc(){ for(char wiersz=0;wiersz<ILOSC_WIERSZY;wiersz++){ for(char bajt=0;bajt<ILOSC_BAJTOW;bajt++) { m_Ekran.dane[wiersz][bajt]=0; } } } //wyświetla znak z tablicy znaków na zadanej pozycji wyświetlacza (0-3) void mZnak(char znak, char pozycja){ // tablica do której zaczytamy pojedynczy znak z pamięci PROGMEM char t_znak[ILOSC_WIERSZY]; // obliczamy który bajt matrycy musimy modyfikowac dla zadanej pozycji znaku char bajt = pozycja / 2; // w zależności od tego czy pozycja jest parzysta czy nie // liczymy o ile przesuwamy znak w lewo (1 lub 5 bitów) char przesuw = (pozycja % 2)?1:5; // kopiujemy znak z pamieci PROGMEM do naszej zmiennej memcpy_P(t_znak,znaki[znak],ILOSC_WIERSZY); // i dla kolejnych wierszy for(char wiersz=0;wiersz<ILOSC_WIERSZY;wiersz++){ // najpier czyścimy odpowiednie 3 bity m_Ekran.dane[wiersz][bajt] &= ~(7<<przesuw); // i wpisujemy odpowiednio przesuniety wiersz znaku m_Ekran.dane[wiersz][bajt] |= t_znak[wiersz]<<przesuw; } } // funkcja w zależności od parametru (true/false) zapala lub gasi kropkę sekunową void mKropka(bool swieci){ if (swieci) { m_Ekran.dane[4][0]|=1; // wiersz 4, kolumna 0 - tam jest przylutowana } else { m_Ekran.dane[4][0]&=~1; } } // funkcja wyświetla liczbe z przedzialu 0-99 na lewym (bajt=0) lub prawym (bajt=1) module matrycy // ostatni parametr odpowiada za wyświetlanie wiodących zer na polu dziesiątek void mLiczba(char bajt,char liczba,bool dzero=false){ liczba = liczba%100; // zabezpieczenie przed przekroczeniem 99 char dziesiatki = liczba/10; char jednosci = liczba%10; if (dziesiatki!=0 || dzero) { mZnak(dziesiatki,bajt*2); } mZnak(jednosci,(bajt*2)+1); } // funkcja wyświetla aktualny czas void mCzas(){ DateTime now = RTC.now(); mLiczba(0,(char)now.hour()); mLiczba(1,(char)now.minute(),true); mKropka(now.second()%2); } // funkcja wyświetla datę void mData(){ DateTime now = RTC.now(); mLiczba(0,(char)now.day()); mLiczba(1,(char)now.month()); mKropka(1); } // funkcje ekranu startowego void mStart(){ mPoka(o_test); // wyświetlamy napis test for (int i=0;i<24;i++) { // dla bajery na starcie przez chwilę migotamy napisem delay(i*i); // dla porażającego efektu - ze zmienną szybkością :) mNegatyw(); }; delay(1000); // czekamy sekunde mCzysc(); // czyścimy ekran przed przejściem do funkcji loop(); } // **************** kod główny void setup() { pinMode(ZATRZASK, OUTPUT); SPI.begin(); // initjalizacja biblioteki SPI // definicja przerwania zegarowego cli(); // zatrzymaj wykonywanie jakichkolwiek przerwań TCCR2A = 0;// zerujemy rejestr kontrolny A TCCR2B = 0;// zerujemy rejestr kontrolny B TCNT2 = 0;// zerujemy licznik OCR2A = 124;// = (16000000) / (500*256) - 1 TCCR2A |= (1 << WGM21); // ustawiamy timer2 w tryb obsługi przerwań (CTC) TCCR2B |= (1 << CS11); // ustawiam dzielnik na 256 TIMSK2 |= (1 << OCIE2A); // wzkazujemy że przerwanie ma być wywołane dla rejestro OCR2A sei();//zezwalamy na wykonywanie przerwań Wire.begin(); RTC.begin(); // inicjacja biblioteki RTC if (! RTC.isrunning()) { // jeżeli układ RTC nie pracuje - ustawiany jest aktualny czas (czas kompilacji); RTC.adjust(DateTime(__DATE__, __TIME__)); } mStart(); // ekran startowy }; // funkcja obsługi przerwania timera 2 // w której odświeżamy wiersz matrycy ISR(TIMER2_COMPA_vect){ digitalWrite(ZATRZASK,LOW); SPI.transfer(~(1<<3+m_Wiersz)); SPI.transfer(m_Ekran.dane[m_Wiersz][1]); SPI.transfer(m_Ekran.dane[m_Wiersz][0]); digitalWrite(ZATRZASK,HIGH); m_Wiersz++; // przesunięcie do kolejnego wiersza if (m_Wiersz==ILOSC_WIERSZY) m_Wiersz=0; // sprawdzenie zakresu } void loop() { mCzas(); delay(10); // czekamy setną część sekundy } |
Jak widać obsługa układu zegara czasu rzeczywistego jest banalnie prosta. Myślę, że nie wymaga chyba żadnego dodatkowego omówienia. Może tylko wspomnę, że czas jest na sztywno ustawiany w trakcie kompilacji poleceniem:
1 |
RTC.adjust(DateTime(__DATE__, __TIME__)); |
Gdyby zegar się rozregulował, albo co bardziej prawdopodobne skończyła się bateryjka i będziemy zmuszeni znów ustawić prawidłowy czas – programujemy układ od nowa. Mało to wygodne – wiem, może w wersji 1.1 pomyślę o jakimś sprytnym ustawianiu czasu bez użycia komputera.
No to sprawę czasy mamy niejako z głowy, to teraz pozostał czas na tytułowe BAJERY.
Jak pewnie zauważyliście, prócz funkcji wyświetlającej godzinę, mamy także w kodzie funkcję wyświetlania daty, której do tej pory nie wykorzystaliśmy. Początkowo pomyślałem sobie, że będę wyświetlał datę co minutę na jedną sekundę, a to bez sensu. Znając życie zawsze jak chciałbym sprawdzić godzinę to akurat wyświetlałaby się data, i odwrotnie. Więc trzeba zrobić jakiś “kontroler”.
Jako że zegar ma docelowo stać na dosyć wysokim regale, przycisk też byłby mało wygodny. Sięgnąłem do “szuflad” i znalazłem:
Ultradźwiękowy czujnik odległości. Wykorzystam go sobie jako kontroler :D Wystarczy stanąć przed regałem i “pomachać do zegarka”, aby pokazał datę.
Jak to działa?
Jak prosty sonar. Po podaniu stanu wysokiego (trwającego przynajmniej 10us) na pin oznaczony Trig (TRIGGER), układ wystrzeliwuje przed siebie ultradźwiękową wiązkę o częstotliwości 40 kHz, po czym przełącza stan pinu ECHO na stan wysoki. Po “usłyszeniu” przez układ echa odbitego od przeszkody sygnału, stan ten przełączany jest na niski. Zatem czas trwania impulsu na wyjściu ECHO, jest proporcjonalny do odległości od przeszkody. Według danych producenta (http://www.robot-electronics.co.uk/htm/srf05tech.htm) wystarczy długość impulsu podzielić przez 58 aby otrzymać odległość w centymetrach.
Cała procedura pomiaru będzie zatem wyglądać mniej więcej tak:
1 2 3 4 5 6 7 8 9 10 11 |
int zmierzOdleglosc() { digitalWrite(TRIGPIN, LOW); // Dla pewności ustawiamy na moment trigger w stan niski; delayMicroseconds(2); digitalWrite(TRIGPIN, HIGH); // i na 10us w stan wysoki delayMicroseconds(10); digitalWrite(TRIGPIN, LOW); // znów na niski int odleglosc = pulseIn(ECHOPIN, HIGH,29000); // i mierzymy długość impulsu return odleglosc/58; // zwracamy odległość w centymetrach } |
Pojawiła się nam tutaj nowa funkcja:
unsigned long pulseIn(numer_pinu, poziom_impulsu, timeout);
Funkcja zwraca w mikrosekundach długość trwania impulsu na pinie podanym jako pierwszy parametr. Drugi parametr może przyjmować wartości HIGH/LOW i mówi funkcji czy mierzymy długość trwania stanu wysokiego/niskiego. Timeout to czas (w mikrosekundach) po jakim funkcja przestaje nasłuchiwać i zwraca zero.
Dlaczego w naszym przykładzie ustawiłem timeout na 29ms? Ponieważ czujnik SFR-5 ma sprzętowy timeout po 30ms po którym sam “wygasi” wysoki sygnał na pinie ECHO, więc nie ma sensu nasłuchiwać dłużej.
Zanim przejdziemy do podłączania czujnika do zegara, jeszcze jeden “bajer”. W przepastnych czeluściach szuflad znalazłem też takie cudo, które oczywiście zaraz podłączymy do zegara:
To czujnik temperatury i wilgotności powietrza. Kosztował jakieś 8 peelenów i niestety okazał się być nieco “niestabilny”. Czasem wariuje. Jakieś 80% czasu działa, pozostałe 20% czasu pokazuje bzdury. Może trafił mi się jakiś wadliwy. Przy okazji kolejnych zakupów wymienię na mniej chiński :D
Do jego obsługi możemy wykorzystać gotową bibliotekę DHT11.h (http://playground.arduino.cc/Main/DHT11Lib), a sama procedura obsługi jest znów niezwykle prosta. Podam najprostszy przykład:
1 2 3 4 5 6 7 8 9 10 11 12 |
#include <dht11.h> dht11 DHT11; #define DHT11PIN 9 void loop() { DHT11.read(DHT11PIN); int temperatura = DHT11.temperature; int wilgotnosc = DHT11.humidity; // tu sobie obsługujemy odczytane wartości ... // i czekamy pomiędzy kolejnymi odczytami delay(2000); // wg producenta przynajmniej 2 sekundy } |
Oczywiście, my nie będziemy odczytywać w głównej pętli loop, tylko napiszemy sobie specjalną funkcję. No to chyba najwyższy czas podłączyć bajery do Arduno i rozbudować kod o obsługę czujników:
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 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 |
#include <SPI.h> #include "avr/pgmspace.h" #include <Wire.h> #include <RTClib.h> #include <dht11.h> // deklaracja obiektu obsługującego czujnik dht11 DHT11; // wybór układu zegara czasu rzeczywistego RTC_DS1307 RTC; // przypisanie pinów #define ZATRZASK 10 //SRF-05 #define ECHOPIN 3 #define TRIGPIN 4 #define SRF_ZASIEG 50 // odległość w cm dla jakiej zadziała czujnik #define SRF_INTERWAL 200 // co ile ms odpalamy sonar //DHT11 #define DHT11PIN 9 #define DHT_INTERWAL 5000 // co ile ms mierzymy temperature i wilgotnosc // deklaracja rozmiaru matrycy #define ILOSC_WIERSZY 5 #define ILOSC_BAJTOW 2 #define ROZMIAR_EKRANU ILOSC_WIERSZY*ILOSC_BAJTOW // rozmiar pamięci ekranu (w bajtach) // definicja struktury danych pamięci obrazu matrycy struct t_Matryca { char dane[ILOSC_WIERSZY][ILOSC_BAJTOW]; }; t_Matryca m_Ekran; // deklaracja zmiennej m_Ekran przechowującej zawartość ekranu char m_Wiersz = 0; // numer aktualnie wyświetlanego wiersza (iterator) // definicje znaków prog_uchar znaki[15][ILOSC_WIERSZY] PROGMEM = { {7,5,5,5,7}, // 0 {1,1,1,1,1}, // 1 {7,1,7,4,7}, // 2 {7,1,7,1,7}, // 3 {5,5,7,1,1}, // 4 {7,4,7,1,7}, // 5 {7,4,7,5,7}, // 6 {7,1,1,1,1}, // 7 {7,5,7,5,7}, // 8 {7,5,7,1,7}, // 9 {0,0,0,0,0}, // spacja [10] {7,4,4,4,7}, // C [11] {7,5,7,0,0}, // ° [12] {5,5,7,5,5}, // H [13] {5,1,2,4,5}, // % [14] }; // i definicje obrazków prog_uchar o_init[10] PROGMEM = {130,0,0,0,178,0,170,0,170,0}; prog_uchar o_czas[10] PROGMEM = {110,70,130,168,132,228,136,162,110,172}; prog_uchar o_data[10] PROGMEM = {196,228,170,74,174,78,170,74,202,74}; prog_uchar o_temp[10] PROGMEM = {238,172,72,234,78,172,72,168,78,168}; // ****************** funkcje obsługi czujników unsigned long dht_timer; // zmienne do odmierzania czasu dla czujników unsigned long srf_timer; // przechowuja czas ostatniego pomiaru int odleglosc=0; // ostatnio zmierzona odległość sonaru void SRFpomiar() { srf_timer = millis(); // zapamietujemy czas ostaniego pomiaru digitalWrite(TRIGPIN, LOW); // Dla pewności ustawiamy na moment trigger w stan niski; delayMicroseconds(2); digitalWrite(TRIGPIN, HIGH); // i na 10us w stan wysoki delayMicroseconds(100); digitalWrite(TRIGPIN, LOW); // znów na niski int SRFecho = pulseIn(ECHOPIN,HIGH,29000); // i mierzymy długość impulsu odleglosc = SRFecho/58; // wyliczamy odległość w centymetrach } void DHTpomiar() { dht_timer = millis(); // zapamietujemy czas ostaniego pomiaru DHT11.read(DHT11PIN); } // ****************** funkcje obsługi ekranu // funkcja wyświetla na ekranie zawartość tablicy podanej jako parametr void mPoka(prog_uchar *zawartosc){ memcpy_P(&m_Ekran,zawartosc,ROZMIAR_EKRANU); //kopiowanie zawartości zmiennej do pamięci ekranu } // funkcja odwraca wszystkie bity matrycy void mNegatyw(){ for(char wiersz=0;wiersz<ILOSC_WIERSZY;wiersz++){ for(char bajt=0;bajt<ILOSC_BAJTOW;bajt++) { m_Ekran.dane[wiersz][bajt]=~m_Ekran.dane[wiersz][bajt]; } } } // funkcja czyści matrycę (wypełnia zerami) void mCzysc(){ for(char wiersz=0;wiersz<ILOSC_WIERSZY;wiersz++){ for(char bajt=0;bajt<ILOSC_BAJTOW;bajt++) { m_Ekran.dane[wiersz][bajt]=0; } } } //wyświetla znak z tablicy znaków na zadanej pozycji wyświetlacza (0-3) void mZnak(char znak, char pozycja){ // tablica do której zaczytamy pojedynczy znak z pamięci PROGMEM char t_znak[ILOSC_WIERSZY]; // obliczamy który bajt matrycy musimy modyfikowac dla zadanej pozycji znaku char bajt = pozycja / 2; // w zależności od tego czy pozycja jest parzysta czy nie // liczymy o ile przesuwamy znak w lewo (1 lub 5 bitów) char przesuw = (pozycja % 2)?1:5; // kopiujemy znak z pamieci PROGMEM do naszej zmiennej memcpy_P(t_znak,znaki[znak],ILOSC_WIERSZY); // i dla kolejnych wierszy for(char wiersz=0;wiersz<ILOSC_WIERSZY;wiersz++){ // najpier czyścimy odpowiednie 3 bity m_Ekran.dane[wiersz][bajt] &= ~(7<<przesuw); // i wpisujemy odpowiednio przesuniety wiersz znaku m_Ekran.dane[wiersz][bajt] |= t_znak[wiersz]<<przesuw; } } // funkcja w zależności od parametru (true/false) zapala lub gasi kropkę sekunową void mKropka(bool swieci){ if (swieci) { m_Ekran.dane[4][0]|=1; // wiersz 4, kolumna 0 - tam jest przylutowana } else { m_Ekran.dane[4][0]&=~1; } } // funkcja wyświetla liczbe z przedzialu 0-99 na lewym (bajt=0) lub prawym (bajt=1) module matrycy // ostatni parametr odpowiada za wyświetlanie wiodących zer na polu dziesiątek void mLiczba(char bajt,char liczba,bool dzero=false){ liczba = liczba%100; // zabezpieczenie przed przekroczeniem 99 char dziesiatki = liczba/10; char jednosci = liczba%10; if (dziesiatki!=0 || dzero) { mZnak(dziesiatki,bajt*2); } else { mZnak(10,bajt*2); // 10 - spacja } mZnak(jednosci,(bajt*2)+1); } // funkcja wyświetla aktualny czas void mCzas(){ DateTime now = RTC.now(); mLiczba(0,(char)now.hour()); mLiczba(1,(char)now.minute(),true); mKropka(now.second()%2); } // funkcja wyświetla datę void mData(){ DateTime now = RTC.now(); mLiczba(0,(char)now.day()); mLiczba(1,(char)now.month(),true); mKropka(1); } // funkcja wyświetla temperature void mTemp(){ mLiczba(0,(char)DHT11.temperature); mZnak(12,2); // znak stopni mZnak(11,3); // znak C mKropka(0); } // funkcja wyświetla wilgotność void mWilg(){ mLiczba(0,(char)DHT11.humidity); mZnak(14,2); // znak % mZnak(13,3); // znak H mKropka(0); } // funkcje ekranu startowego void mStart(){ DHTpomiar(); // startujemy czujnik temperatury (czasem potrzebuje czasu zeby załapać) mPoka(o_init); // wyświetlamy napis ini for (int i=0;i<100;i++) { // liczymy do 100 dla bajerki mLiczba(1,i,false); delay(10); }; mPoka(o_czas); // wyświetlamy na 2 sek. napis czas delay(2000); mCzysc(); // czyścimy ekran przed przejściem do funkcji loop(); } // funkcja sekwencji czujników void mBajery(){ mPoka(o_data); // wyświetlamy napis data delay(1000); mData(); delay(3000); mPoka(o_temp); // wyświetlamy napis temp delay(1000); mTemp(); // wyświetlamy temperaturę delay(3000); mWilg(); // wyświetlamy wilgotność delay(3000); mPoka(o_czas); // wyświetlamy napis czas delay(1000); } // **************** kod główny void setup() { pinMode(ZATRZASK, OUTPUT); pinMode(ECHOPIN, INPUT); pinMode(TRIGPIN, OUTPUT); SPI.begin(); // initjalizacja biblioteki SPI Wire.begin();// initjalizacja biblioteki wire RTC.begin(); // inicjacja biblioteki RTC // definicja przerwania zegarowego cli(); // zatrzymaj wykonywanie jakichkolwiek przerwań TCCR2A = 0;// zerujemy rejestr kontrolny A TCCR2B = 0;// zerujemy rejestr kontrolny B TCNT2 = 0;// zerujemy licznik OCR2A = 124;// = (16000000) / (500*256) - 1 TCCR2A |= (1 << WGM21); // ustawiamy timer2 w tryb obsługi przerwań (CTC) TCCR2B |= (1 << CS11); // ustawiam dzielnik na 256 TIMSK2 |= (1 << OCIE2A); // wzkazujemy że przerwanie ma być wywołane dla rejestro OCR2A sei();//zezwalamy na wykonywanie przerwań if (! RTC.isrunning()) { // jeżeli układ RTC nie pracuje - ustawiany jest aktualny czas (czas kompilacji); RTC.adjust(DateTime(__DATE__, __TIME__)); }; mStart(); // ekran startowy dht_timer = millis(); // startujemy liczniki czujników srf_timer = millis(); }; // funkcja obsługi przerwania timera 2 // w której odświeżamy wiersz matrycy ISR(TIMER2_COMPA_vect){ digitalWrite(ZATRZASK,LOW); SPI.transfer(~(1<<3+m_Wiersz)); SPI.transfer(m_Ekran.dane[m_Wiersz][1]); SPI.transfer(m_Ekran.dane[m_Wiersz][0]); digitalWrite(ZATRZASK,HIGH); m_Wiersz++; // przesunięcie do kolejnego wiersza if (m_Wiersz==ILOSC_WIERSZY) m_Wiersz=0; // sprawdzenie zakresu } void loop() { if (millis()-dht_timer>=DHT_INTERWAL) { // jeżeli nadszedł czas pomiaru temperatury DHTpomiar(); // to mierz } if (millis()-srf_timer>=SRF_INTERWAL) { // jeżeli nadszedł czas pomiaru odległości SRFpomiar(); // to mierz } if (odleglosc!=0 && odleglosc<SRF_ZASIEG) { // jak obiekt w zasiegu sonaru mBajery(); // pokaż datę i temperature } mCzas(); // pokaż czas delay(10); // czekamy setną część sekundy } |
No i mamy gotowy zegar! Efekt wygląda tak:
I w zasadzie to już prawie koniec. Tyle, że żeby postawić mój projekt w ostatecznej wersji na półce musiałbym tam wpakować moje jedyne Arduino! Co to to nie. Zatem musiałem polutować sobie osobną płytkę pod atmegę z gotowymi gniazdami na peryferia i modułem zasilania. Efekt poniżej:
Jedynym wolnym prockiem jakim dysponowałem była Atmega328PU. Prawie taki sam jak w Arduino Uno. Ale okazało się, że Arduino IDE nie chciało współpracować i już przy próbie wgrania bootloadera powiedziało, że nie zgadza mu sie sygnatura układu. Znów poszukałem po internetach i okazało się, że wystarczy na chwilę podmienić w pliku avrdude.conf wpis:
1 2 |
# ATmega328P signature = 0x1e 0x95 0x0F; |
na
1 2 |
# ATmega328P signature = 0x1e 0x95 0x14; |
i wszystko elegancko się wgrywa i działa bez zarzutu. Zaprogramowałem układ, powpinałem czujniki, poskładałem całość w prowizoryczną kupkę, podłączyłem do prądu i efekt ostateczny wygląda tak:
GOTOWE.
Do zrobienia jeszcze:
- wymienić czujnik temperatury na lepszy
- jakaś zgrabniejsza obudowa
- dodać obsługę drugiego czujnika temperatury (zewnętrznego za okno)
- dodać możliwość ustawianiu czasu i daty bez użycia programatora.
Gratuluję wszystkim którzy dzielnie dotrwali do końca tego cyklu. Mam nadzieję, że tych, którzy nie mieli wcześniej do czynienia z podobnymi projektami zainteresowałem i przekonałem, że nie taki diabeł straszny. A tych dla których nie było to nic nowego, zachęciłem do zrobienia ambitniejszych projektów. Dzięki za wszystkie komentarze i konstruktywną krytykę. Do znów :)
Kolejny solidnie napisany post ;)
Dodać zgrabniejszą obudowę? na filmiku i fotkach nie widzę żadnej obudowy ;p
ajtam ajtam – jest taka czerwona szybka i filcowe nóżki od mebli :D
Super projekt! Oby takich więcej!
Błąd w przykładzie użycia biblioteki DHT11 – dwa razy odczytujesz temperaturę.
A gdzie dokładnie?? Bo jeżeli chodzi Ci o odczyt w funkcji mStart – to jest on tam dany celowo. Czujnik szybciej “załapuje” jak go odpytam przy starcie urządzenia.
już znalazłem – faktycznie był :D dziękuje za spotrzegawczość !
spoko ;)
tez uzywam tej biblioteki… tylko ja buduje system zdalnych bezprzewodowych czujnikow temp/wilg z prezentacja w cosm (albo na czyms swoim – to jeszcze nie skonczone)
a jesli chodzi o pomysly – zrobilem kawalek kodu do zalaczania oswietlenia zewnetrznego domu ale o wschodzie i zachodzie slonca – aplikacja wylicza ten czas ze wspolrzednych geograficznych ;) i tez z uzyciem RTC. nie wiem tylko jeszce co zrobie przy zmianie czasu… niby podaje sie tam strefe czasowa, ale chyba sie samo nie zmieni ;)
niemniej nawet fajnie dziala :D
Dobry projekt. Pomyśl nad jakąś dobrą obudową, żeby się to ładnie prezentowało w przyszłości.
Naprawdę świetne poradniki + pomysł z wykorzystaniem matrycy LED.
Robię podobny projekt tylko, że z wykorzystaniem sześciu 7 segmentowych wyświetlaczy. Mam pytanie: RTC spóźnia Ci się? Bo ja nie mogę sobie z tym poradzić. Dziennie spóźnia mi się ok. 10-20 sek, więc w ciągu 3-5 dni już spóźnia mi się średnio minutę. Dla mnie ta dokładność jest nie do przyjęcia. Najpierw myślałem, że to wina baterii, ale układ jest zasilany bez przerwy Vcc.
W tej chwili nie widzę żadnych zauważalnych opóźnień. Ale zegar chodził maksymalnie około 3 dni bez wyłączania. Zobaczymy jak się będzie sprawował jak pochodzi dłużej. Będę meldował.
A układ RTC robiłeś sam, czy też masz gotowca?
Ten sam gotowiec co Ty – z DS1307 Z.
to poczekamy pare dni i zobaczymy – po napisaniu artykułu zegar wylądował z powrotem na regale i zanim nie dotrze nowy czujnik temp, nie zamierzam go zdejmować. Zdam raport czy trzyma czas.
Podobnie jak u Ciebie i u mnie są opóźnienia.
Około minuty na 3 dni. Ciekawe co jest przyczyną? Kwarc?
Może jakiś fachowiec się wypowie?
Ciekawe czy sa się to jakoś kompensować?
Programowo na pewno, ale sprzętowo?
bocianu mógł byś udostępnić wykaz wszystkich elementów do “atmegi” bo schemat dojdę po zdjęciach z góry dzięki =]
odwołuje, już znalazłem ;]
Szacuneczek dla umiejętności! Super gadżet. Dla mnie to czarna magia i podziwiam takich majsterkowiczów.
Pozdrawiam
Witam. Macie może jakiś pomysł jak zrobić zmianę czasu (np na letni) w DS1307
nie programując na nowo?
Wspaniałe, po przeczytaniu wszystkich artykułów uświadamiam sobie, że jestem niezdolnym i niekreatywnym kretynem;D 5/5