Autor: wieloiksiasty
Redakcja: Dondu
Zegarek to jeden z najlepszych pomysłów na prezent. Estetyczny i praktyczny zegarek zawsze znajduję się w widocznym miejscu, a jeśli jest wystarczająco efektowny i starannie wykonany zawsze wywołuje efekt WOW oraz zapewnia doskonałą reklamę konstruktora. Taki właśnie jest mój projekt.
Mój zegarek to prosta konstrukcja oparta na mikrokontrolerze Atmel ATMega8A-PU, wspieranym przez zegar RTC DS1307, termometr DS1820 i rejestr przesuwny 74HC164. Całość mieści się na pojedynczej płytce drukowanej, przyczepionej do efektownej imitacji dynamitu. Układ może działać jako zegar, termometr, stoper oraz minutnik. Dzięki temu jest bardzo wszechstronny i nadaje się zarówno do kuchni, jak i do warsztatu. Koniec lania wody :-D.
Efekt końcowy |
Mój zegarek to prosta konstrukcja oparta na mikrokontrolerze Atmel ATMega8A-PU, wspieranym przez zegar RTC DS1307, termometr DS1820 i rejestr przesuwny 74HC164. Całość mieści się na pojedynczej płytce drukowanej, przyczepionej do efektownej imitacji dynamitu. Układ może działać jako zegar, termometr, stoper oraz minutnik. Dzięki temu jest bardzo wszechstronny i nadaje się zarówno do kuchni, jak i do warsztatu. Koniec lania wody :-D.
Hardware
Schemat układu narysowany w programie Eagle 6.4.0 jest widoczny poniżej oraz załączony w plikach do pobrania w dalszej części artykułu.Schemat układu (EAGLE 6.4.0) Pełna rozdzielczość: tutaj |
Centralnym punktem układu jest mikrokontroler ATMega8A-PU, taktowany wewnętrznym generatorem RC. Oznacza to, że równie dobrze do tego celu nadaje się ATMega8L i inne z tej serii. Wokół mikrokontrolera znajduje się standardowy osprzęt czyli kondensator i rezystor podciągający RESET do VCC. Kondensator jest tylko jeden, ale został umieszczony pod mikrokontrolerem, pomiędzy dwoma pinami zasilającymi i doskonale spełnia swoją rolę.
Pomiarem czasu i temperatury zajmują się układy DS1307 i DS1820. Zdecydowałem się na zewnętrzny zegar RTC, ponieważ gwarantuje on stabilne działanie oraz maksymalnie długi czas podtrzymywania na jednej baterii. Poza tym uprościło to kod i przyspieszyło prace. Przewagę DS1820 nad innymi termometrami stanowiła przede wszystkim jego obecność w moich zapasach. Analizując schemat można zauważyć, że brakuje rezystorów podciągających linie SDA i SCL na magistrali I²C (TWI). Jest to mój błąd, który jednak okazał się zupełnie niegroźny, gdyż wewnętrzny pull-up mikrokontrolera z powodzeniem zastępuje zewnętrzne podciąganie. Przy wprowadzaniu poprawek gospodarowanie miejsca na PCB okazało się bardzo trudne dlatego na ostatecznej wersji schematu również ich nie ma. Pomimo tego układ działa bez problemów i całkowicie stabilnie.
Rolę interface użytkownika pełnią cztery przyciski, buzzer z generatorem, cztery diody LED sterowane jednym tranzystorem (kropki pomiędzy wyświetlaczami) oraz trzy wyświetlacze 7-segmentowe ze wspólną anodą. Podczas projektowania okazało się, że brakuje pinów do realizacji multipleksowania, dlatego na płytce znalazł się również rejestr przesuwny 74HC164. Dzięki niemu udało się uniknąć kolejnych kompromisów i kombinacji oraz uprościć sterowanie wyświetlaczami. Objętość kodu również nie ucierpiała, obsługa rejestru przesuwnego nie jest trudniejsza niż przełączanie kolejnych pinów. Oprócz tego na płytce znajduje się gniazdo programowania oraz prosty stabilizator napięcia z układem 7805 (na układzie widocznym na zdjęciach został zastąpiony zworą).
Zaprojektowanie płytki drukowanej było podwójnie trudne, ponieważ rozmieszczenie najważniejszych elementów (wyświetlaczy, diód, złączy, przycisków) zostało ustalone na samym początku i nie mogłem ich potem zmieniać. Udało się zaprojektwać dość zwartą płytkę jednostronną, prawie wszystkie elementy są przewlekane (poza dwoma kondensatorami). Oprócz elementów na płytce znajduje się również imponująca ilość zworek, na szczęście większość z nich została ukryta pod wyświetlaczami, dlatego nie szpecą płytki.
Mozaika ścieżek płytki PCB |
Zaprojektowanie płytki drukowanej było podwójnie trudne, ponieważ rozmieszczenie najważniejszych elementów (wyświetlaczy, diód, złączy, przycisków) zostało ustalone na samym początku i nie mogłem ich potem zmieniać. Udało się zaprojektwać dość zwartą płytkę jednostronną, prawie wszystkie elementy są przewlekane (poza dwoma kondensatorami). Oprócz elementów na płytce znajduje się również imponująca ilość zworek, na szczęście większość z nich została ukryta pod wyświetlaczami, dlatego nie szpecą płytki.
Płytka została wykonana na standardowym jednostronnym laminacie metodą termotransferu. Ścieżki zabezpieczone są warstwą kalafonii, a na górnej stronie znajduje się nadruk, również wykonany metodą termotransferu. Po obu stronach płytki znajdują się uchwyty na taśmę klejącą, którą układ jest przymocowany do imitacji bomby. Ukończona płytka prezentuje się imponująco.
Skończona płytka drukowana |
Skończona płytka drukowana |
Skończona płytka drukowana |
Software
Software zegara został napisany w C++, z użyciem IDE Eclipse i kompilatora avr-gcc. Użycie C++ zamiast czystego C, może wydawać się nadużyciem, ale w kodzie nie używałem klas i programowania obiektowego, a jedynie niektóre przydatne sztuczki, na które pozwala C++, np. przeładowywanie funkcji. Rozmiar kodu nie cierpi znacznie (czasami nawet powstaje mniejszy kod), a przystosowanie istniejących już fragmentów kodu na potrzeby publikacji uznałem za zbyt czasochłonne. Cały projekt jest podzielony na części, które znajdują się w osobnych plikach.
Kod jest dość rozwlekły i nie wymaga specjalnego tłumaczenia. Komentarze zawarte w kodzie oraz poniższy tekst z pewnością wystarczą do zrozumienia działania kodu.
Program dla ATmega8 (i pokrewnych ATmega8A, ATmega8L, ...) działających z zegarem 8MHz, dla fusebitów ustawionych następująco:
Kod jest dość rozwlekły i nie wymaga specjalnego tłumaczenia. Komentarze zawarte w kodzie oraz poniższy tekst z pewnością wystarczą do zrozumienia działania kodu.
Program dla ATmega8 (i pokrewnych ATmega8A, ATmega8L, ...) działających z zegarem 8MHz, dla fusebitów ustawionych następująco:
- Low: E4
- High: D9
Devastator/DS1820/
Pliki ds.h & ds.cpp zawierają funkcje niezbędne do obsługi termometru. W kodzie znajduje tylko niezbędne minimum. Dwie funkcje pozwalają na inicjalizację konwersji temperatury oraz odczyt. Funkcja odczytująca temperaturę od razu zamienia ją na konwencjonalną 16-bitową zmienną ze znakiem wg algorytmu dla DS1820, więc zmiana termometru na wersję DS18B20 wymaga przepisania tej funkcji na nowo.
Pliki onewire.h & onewire.c to biblioteka do obsługi interface 1-wire za pomocą dowolnego pinu I/O.
Devastator/RTC/
Pliki rtc.h & rtc.cpp zawierają obsługę układu DS1307 oraz rzeczy niezbędne do zarządzania czasem. Tutaj znajduje się między innymi deklaracja struktury do przechowywania czasu oraz instrukcje konwersji pomiędzy systemem dziesiętnym, który jest używany w programie, a systemem BCD, który działa w układzie DS1307.
Pliki twi.h & twi.c to biblioteka do obsługi wbudowanego w mikrokontroler sprzętowego interface TWI (I²C).
Devastator/
Pliki user.h & user.cpp zawierają wszystko co potrzebne do realizacji interface użytkownika. Tutaj znajduję się obsługa wyświetlacza, przycisków, kropki oraz buzzera. W pliku user.h są również opisane funkcje poszczególnych pinów.
Plik main.cpp zawiera globalne zmienne now, alarm oraz alarm_on, które przechowują zawsze aktualny czas, czas uruchomienia budzika i flagę aktywowania budzika, poza tym znajdują się w niej trzy elementy:
Funkcja main – zawiera inicjalizację wszystkich potrzebnych peryferiów oraz timera2, który steruje głównym przerwaniem programu. Na końcu znajduje się pętla nieskończona, która wykonuje się w czasie bezczynności.
Przerwanie przy porównaniu Timer2 – to główna część całego programu. Przerwanie jest wywoływane z częstotliwością 1kHz. Na początku znajdują się deklaracje zmiennych używanych w całym przerwaniu. Są to:
- Zawartość wyświetlacza.
- Wciśnięty przycis oraz przycisk wciśnięty podczas ostatniej iteracji.
- Wybrany przez użytkownika tryb oraz tryb wybrany w ostatniej iteracji.
- Zmienna buzzera.
- Czas przytrzymania przycisku 4, potrzebny do wejścia w ustawienia.
Następnie znajdują się zadania wykonywane przed właściwą częścią. Tutaj realizowana jest obsługa przycisków, sztuczny generator milisekund oraz wybór trybu działania układu.
Zastosowanie sztucznego generatora milisekund okazało się konieczne, ponieważ układ DS1307 zapewnia tylko sekundy. Precyzja takiego rozwiązania może budzić wątpliwości, tym bardziej, że mikrokontroler jest taktowany wbudowanym generatorem RC, ale wszystkie wątpliwości okazują się bez znaczenia, gdy zauważymy, że czas reakcji na naciśnięcie przycisku wynosi zazwyczaj około 20-40ms i na dodatek nie jest stały.
Kolejnym elementem jest realizacja poszczególnych funkcji działania układu. Umieszczenie wszystkich potrzebnych instrukcji w jednym miejscu, jedna po drugiej, pozwala na wykonywanie zadań wszystkich elementów jednocześnie, pomimo że wyświetlona może być tylko jedna z funkcjonalności. Dzięki temu można jednocześnie uruchomić stoper, minutnik oraz wyświetlić aktualną godzinę.
Zegar odpowiada za wyświetlanie aktualnej godziny, daty (przycisk 1), temperatury (przycisk 2) oraz włączania, wyłączania budzika (przycisk 3). W tej części jest również realizowana obsługa budzika oraz odczyt temperatury na zawołanie (odczyt na bieżąco trwa zbyt długo i mógłby powodować samo nagrzenie się termometru /self-heating/). Tutaj również znajduje się procedura budzenia o zadanym czasie.
Stoper jest bardziej skomplikowany. Ta część rozpoczyna się od deklaracji flag stopwatchStarted oraz stopwatchFreezen (zamrożenie wyświetlacza) i zmiennych stopwatchStartTime (moment startu), stopwatchOffset (czas wyświetlany podczas wejścia w tryb pauzy), stopwatchOutput (czas aktualnie przekazywany na wyświetlacz). Następnie następuje obsługa przycisków, obliczanie czasu do wyświetlenia oraz przekazanie danych do interface użytkownika (wyświetlacz, kropka, buzzer). Dodatkowo, kiedy nie ma potrzeby wyświetlania godzin, dane przesuwane są o dwa miejsca w prawo, a zwolnione miejsce zajmowane jest przez milisekundy.
Minutnik działa na zasadzie odliczania czasu do zera i jest to funkcja najbardziej adekwatna to wyglądu zewnętrznego urządzenia :-D. Minutnik działa odwrotnie do stopera, a ponadto dodany jest tryb ustawiania czasu od którego rozpoczyna się odliczanie.
Następna jest procedura obsługi specjalnego trybu ustawień. Tutaj ustawiany jest aktualny czas i data oraz godzina zadziałania budzika. Tryb ustawień aktywowany jest 3 sekundowym przytrzymaniem przycisku 4 oraz jako jedyny samodzielnie obsługuje przycisk 4 (gdy już zostanie aktywowany), ponieważ odbywa się tam zapis ustawień do DS1307 i EEPROM.
Przerwanie kończy procedura końcowa. Znajduje się tam obsługa wyświetlacza, aktualizacja zmiennych last i last_mode oraz obsługa buzzera.
Ostatnim elementem programu jest przerwanie zewnętrzne INT0 pochodzące od układu DS1307, które generuje sygnał o częstotliwości 1Hz. W tym miejscu aktualizowana jest globalna struktura przechowująca zawsze akutalny czas.
Podczas prac okazało się, że komunikacja z układem DS1307 jest zbyt czasochłonna i powoduje miganie wyświetlacza. Dlatego dodałem atrybut ISR_NOBLOCK i podzieliłem przerwanie na dwie części. Pierwsza odpowiada za komunikację z układem DS1307 i podczas jej działania nie są blokowane przerwania. Druga część odpowiada za aktualizację globalnej zmiennej now oraz synchronizację softwarowego generatora milisekund i jest umieszczona w bloku z zablokowanymi przerwaniami. W przekazywaniu danych pośredniczy bufor buffer. Kopiowanie danych z bufora do miejsca przeznaczenia jest na tyle szybkie, że nie powoduje zaburzeń w pracy układu.
Podczas prac okazało się, że komunikacja z układem DS1307 jest zbyt czasochłonna i powoduje miganie wyświetlacza. Dlatego dodałem atrybut ISR_NOBLOCK i podzieliłem przerwanie na dwie części. Pierwsza odpowiada za komunikację z układem DS1307 i podczas jej działania nie są blokowane przerwania. Druga część odpowiada za aktualizację globalnej zmiennej now oraz synchronizację softwarowego generatora milisekund i jest umieszczona w bloku z zablokowanymi przerwaniami. W przekazywaniu danych pośredniczy bufor buffer. Kopiowanie danych z bufora do miejsca przeznaczenia jest na tyle szybkie, że nie powoduje zaburzeń w pracy układu.
Część artystyczna
Każdy projekt, który ma opuścić warsztat, trzeba jakoś wykończyć. W końcu sama płytka nie jest ani ładna, ani praktyczna. Najoczywistszym sposobem na wykończenie, jest obudowa. Niestety gotowe obudowy, które można kupić w elektroniku, nie są zbyt estetyczne. Samodzielne wykonanie akceptowalnej obudowy zdecydowanie przekraczało moje możliwości, dlatego zdecydowałem się na coś bardziej oryginalnego. Przyczepiłem płytkę do prostej imitacji dynamitu, a całość natychmiast zyskała efektowny i mocno filmowy wygląd.
Z ręczników kuchennych wydobyłem rdzeń, przebiłem go patyczkiem od szaszłyków) ok. 2,5cm od końca. W rurze umieściłem tekturowe kółko, korzystając z pomocy patyczka (dzięki patyczkowi denko nie wpadnie do środka). Miejsce styku tektury i rury zalałem klejem. Po wyschnięciu wyciągnąłem patyczek. W środku umieściłem ciasno zwinięte gazety i powtórzyłem procedurę wklejania denka z drugiej strony tuby.
Całość owinąłem taśmą izolacyjną, zostawiając jak najwięcej taśmy na zakładkę. Zakładki zawinąłem do środka i ustabilizowałem kolejnym tekturowym kółkiem.
Imitacja dynamitu została pomalowana farbą w sprayu z użyciem szablonu. Niestety okazało się, że farba nie chce do końca wyschnąć na taśmie izolacyjnej. Nie mam pojęcia dlaczego. Lekko wilgotna farba nie przeszkadza, dopóki nie trzeba będzie wytrzeć kurzu. Niestety nie udało mi się znaleźć lepszej alternatywy.
Po przeschnięciu farby całość skleiłem duct tapem (taśma zbrojona) w plaster miodu. Można również uformować piramidkę. Dla lepszego efektu dodałem jeszcze zwinięte w spiralkę druciki w kolorowych izolacjach.
Z ręczników kuchennych wydobyłem rdzeń, przebiłem go patyczkiem od szaszłyków) ok. 2,5cm od końca. W rurze umieściłem tekturowe kółko, korzystając z pomocy patyczka (dzięki patyczkowi denko nie wpadnie do środka). Miejsce styku tektury i rury zalałem klejem. Po wyschnięciu wyciągnąłem patyczek. W środku umieściłem ciasno zwinięte gazety i powtórzyłem procedurę wklejania denka z drugiej strony tuby.
Całość owinąłem taśmą izolacyjną, zostawiając jak najwięcej taśmy na zakładkę. Zakładki zawinąłem do środka i ustabilizowałem kolejnym tekturowym kółkiem.
Imitacja dynamitu została pomalowana farbą w sprayu z użyciem szablonu. Niestety okazało się, że farba nie chce do końca wyschnąć na taśmie izolacyjnej. Nie mam pojęcia dlaczego. Lekko wilgotna farba nie przeszkadza, dopóki nie trzeba będzie wytrzeć kurzu. Niestety nie udało mi się znaleźć lepszej alternatywy.
Po przeschnięciu farby całość skleiłem duct tapem (taśma zbrojona) w plaster miodu. Można również uformować piramidkę. Dla lepszego efektu dodałem jeszcze zwinięte w spiralkę druciki w kolorowych izolacjach.
Efekt końcowy |
Działanie i obsługa układu
Działanie układu prezentuje film:
Ze względu na brak bezpośrednich oznaczeń i dodatkowego wyświetlacza informującego o wybranej funkcji obsługa może wydawać się trudna, ale to tylko pozory. Zegar ma trzy tryby działania, w których każdy przycisk ma trochę inną rolę. Najwygodniejsza będzie tabelka:
Zegar | Stoper | Minutnik | Ustawienia (tryb specjalny) | |
Przycisk 1 | Data | Start/pauza | Start/pauza | Dodaj jeden |
Przycisk 2 | Termometr | Reset | Reset | Odejmij jeden |
Przycisk 3 | Budzik ON/OFF i podgląd | Hold | Ustaw czas | Następna wartość |
Przycisk 4 | Następny tryb | Następny tryb | Następny tryb | Zapisz i wyjdź |
Wyjaśnienia może wymagać kilka rzeczy. Po pierwsze hold to funkcja pozwalająca zamrozić czas na wyświetlaczu bez zatrzymywania stopera. W trybie minutnika w miejscu funkcji hold znajduje się wejście w podtryb ustawiania czasu startowego. Obsługuje się go w taki sam sposób jak specjalny tryb ustawień, ale wyjście następuje samoczynnie po przejściu przez wszystkie trzy wartości za pomocą przycisku 3. Dobrze widać to na filmie.
Tryb ustawień jest zrealizowany inaczej niż pozostałe. Za pomocą przycisku 3 przełączamy wybraną wartość w lewo. W sumie są tam trzy ustawienia: data, budzik oraz godzina. Godzina jest na końcu ponieważ ustawiamy ją na końcu, chcąc zsynchronizować się z innym zegarem. Kropka na dole wyświetlaczy pozwala zorientować się, gdzie się znajdujemy.
Ostatnią rzeczą, która wymaga wyjaśnienia jest sposób sygnalizacji aktywowania budzika. Kropka na dole skrajnie prawego wyświetlacza świeci się, gdy budzik jest aktywowany.
Errata
Osoby, które dogłębnie przeanalizują projekt zauważą pewne różnice pomiędzy tym co jest w plikach, a tym co na zdjęciach. Te różnice to błędy, które odkryłem, gdy było już za późno. Wszystkie które znalazłem zostały już poprawione.
Literatura
- Język C dla mikrokontrolerów AVR. Od podstaw do zaawansowanych aplikacji - Tomasz Francuz, Helion 2011
- Mikrokontrolery - Jak zacząć? - Dondu i inni, czyli nieniejszą stronę
- Forum elektroda.pl
Załączniki do pobrania
W załącznikach znajduje się kompletny projekt urządzenia:- schemat i płytka PCB: Bombowy-zegar-schemat-PCB-Eagle.zip (kopia)
- PCB w formie do wytrawienia: Devastator.pdf (kopia)
- pliki źródłowe programu: Bombowy-zegar-program.zip (kopia)
- skompilowany plik .hex, który możesz wgrać do mikrokontrolera bez kompilowania plików źródłowych ustawiając fusebity na wewnętrzny zegar RC 8MHz w sposób opisany w niniejszym artykule w części zatytułowanej Software.
Uwagi redakcji
Dwie istotne uwagi techniczne:
1. brak rezystorów ograniczających prąd diod LED (segmentów) wyświetlacza. To bardzo istotny problem mogący skutkować uszkodzeniem mikrokontrolera: Mikrokontroler vs prądy pinów
W związku z tym, po wprowadzeniu rezystorów nastąpiłoby znaczące przyciemnienie wyświetlanych cyfr, co powinno zostać rozwiązane poprzez dodanie bufora np. ULN2803.
EDIT: 18 stycznia 2014r. Wyjaśnienie przyczyny wykreślenia uwagi w komentarzach poniżej.
Pomimo tych uwag technicznych nasza ocena jest wysoka, ze względu na spory program, który naszym zdaniem może stanowić bazę dla podobnych projektów.
Autor odniósł się do naszych uwag w dyskusji w komentarzu do niniejszego artykułu tutaj.
Pliki źródłowe
Poniżej pliki źródłowe z załącznika.
Pliki dot. DS1820
ds.h
//Obsługa DS1820 //Plik ds.h #include <avr/io.h> #include <util/delay.h> #include "onewire.h" #ifndef DS18B20_H #define DS18B20_H //Zainicjuj konwersję temperatury uint8_t dsConvertT(); //Odczytaj temperaturę z czujnika int16_t dsRead(); #endif
ds.ccp
//Obsługa DS1820 //Plik ds.cpp #include "ds.h" //-------------------------------------------------- //Zainicjuj konwersję temperatury uint8_t dsConvertT() { if (!oneWireReset()) return 0; oneWireWriteByte(0xcc); //Wybierz wszystkie oneWireWriteByte(0x44); //Konwertuj return -1; } //-------------------------------------------------- //Zainicjuj konwersję temperatury int16_t dsRead() { uint16_t i, tmp; if (!oneWireReset()) return 0; oneWireWriteByte(0xcc); //Wybierz wszystkie oneWireWriteByte(0xbe); //Odczytaj i = oneWireReadByte(); //Odczytaj pierwszy bajt (wartość) tmp = oneWireReadByte(); //Odczytaj drugi bajt (znak) //Konwersja temperatury i *= 10; i /= 2; if(tmp != 0) i *= -1; return i; }
onewire.h
//Interface 1-wire //Plik onewire.h #include <avr/io.h> #include <util/delay.h> #ifndef ONEWIRE_H_ #define ONEWIRE_H_ //Interface realizowany na PC0 #define SET_ONEWIRE_PORT PORTC |= _BV(0) #define CLR_ONEWIRE_PORT PORTC &= ~_BV(0) #define IS_SET_ONEWIRE_PIN PINC & _BV(0) #define SET_OUT_ONEWIRE_DDR DDRC |= _BV(0) #define SET_IN_ONEWIRE_DDR DDRC &= ~_BV(0) //Sprawdź stan magistrali uint8_t oneWireReset(); //Zapisz bajt void oneWireWriteByte(uint8_t byte); //Odczytaj bajt uint8_t oneWireReadByte(); #endif
onewire.ccp
//Interface 1-wire //Plik onewire.cpp #include "onewire.h" //-------------------------------------------------- //Sprawdź stan magistrali uint8_t oneWireReset() { CLR_ONEWIRE_PORT; if (!(IS_SET_ONEWIRE_PIN)) return 0; SET_OUT_ONEWIRE_DDR; _delay_us(500); SET_IN_ONEWIRE_DDR; _delay_us(70); if(!(IS_SET_ONEWIRE_PIN)) { _delay_us(500); return(1); } _delay_us(500); return(0); } //-------------------------------------------------- //Zapisz bajt void oneWireWriteByte(uint8_t byte) { uint8_t i; CLR_ONEWIRE_PORT; for (i=0; i<8; i++) { SET_OUT_ONEWIRE_DDR; if (byte & 0x01) { _delay_us(7); SET_IN_ONEWIRE_DDR; _delay_us(70); } else { _delay_us(70); SET_IN_ONEWIRE_DDR; _delay_us(7); } byte >>= 1; } } //-------------------------------------------------- //Odczytaj bajt uint8_t oneWireReadByte() { uint8_t i, byte = 0; SET_IN_ONEWIRE_DDR; for (i=0; i<8; i++) { SET_OUT_ONEWIRE_DDR; _delay_us(7); SET_IN_ONEWIRE_DDR; _delay_us(7); byte >>= 1; if(IS_SET_ONEWIRE_PIN) byte |= 0x80; _delay_us(70); } return byte; }
Pliki dot. RTC
rtc.h
//Obsługa RTC DS1307 //Plik rtc.h #include "twi.h" #include <avr/io.h> #ifndef RTC_H_ #define RTC_H_ //Przydatne makra #define set(reg, num) ((reg) |= (1 << (num))) #define clear(reg, num) ((reg) &= ~(1 << (num))) //Struktura do przechowywania czasu struct time { volatile uint16_t msec; uint8_t sec; uint8_t min; uint8_t hour; uint8_t wday; uint8_t day; uint8_t month; uint8_t year; }; //Ta sama struktura nadaje się do czasy w formacie BCD typedef time BCDTime; //Zapisz/odczytaj w formacie BCD void rtcSetBCDTime(BCDTime data); //Zapisz czas BCDTime rtcGetBCDTime(); //Odczytaj czas //Zapisz/odczytaj w formacie dziesiętnym void rtcSetTime(time data); //Zapisz czas time rtcGetTime(); //Odczytaj czas //Sprawdź czy nie doszło do resetu bool checkReset(); //Konwersja pomiędzy formatami czasu uint8_t binToBCD(uint8_t data); BCDTime binToBCD(time data); uint8_t BCDToBin(uint8_t data); time BCDToBin(BCDTime data); //Inicjalizacja komunikacji z DS1307 void rtcInit(); //Okrojone funkje do dodawania i odejmowania czasu void rtcDiff(time* min, time* sub); //Odejmowanie void rtcSum(time* first, time* second); //Dodawanie #endif /* RTC_H_ */
rtc.ccp
//Obsługa RTC DS1307 //Plik rtc.cpp #include "rtc.h" //-------------------------------------------------- //Zapisz czas do DS1307 void rtcSetBCDTime(BCDTime data) { twiStart(); //początek twiAddress(208);//adres DSa twiWrite(0); //adres zapisu twiWrite(data.sec); //zapis twiWrite(data.min); twiWrite(data.hour); twiWrite(data.wday); twiWrite(data.day); twiWrite(data.month); twiWrite(data.year); twiStop(); //zakończenie } //-------------------------------------------------- //Odczytaj czas z DS1307 BCDTime rtcGetBCDTime() { BCDTime data; twiStart(); //początek twiAddress(208);//adres DSa twiWrite(0); //adres odczytu twiStart(); //początek odczytu twiAddress(209);//adres odczytu z DSa data.sec = twiReadACK(); //odczyt data.min = twiReadACK(); data.hour = twiReadACK(); data.wday = twiReadACK(); data.day = twiReadACK(); data.month = twiReadACK(); data.year = twiReadNACK(); twiStop(); //zakończenie return data; } //-------------------------------------------------- //Zapisz czas void rtcSetTime(time data) { rtcSetBCDTime(binToBCD(data)); } //-------------------------------------------------- //Odczytaj czas time rtcGetTime() { return BCDToBin(rtcGetBCDTime()); } //-------------------------------------------------- //Sprawdź czy nie doszło do resetu bool checkReset() { twiStart(); //początek twiAddress(208);//adres DSa twiWrite(0); //adres odczytu twiStart(); //początek odczytu twiAddress(209);//adres odczytu z DSa uint8_t data = twiReadNACK(); //odczyt twiStop(); //zakończenie if(data == 0x80) return true; else return false; } //-------------------------------------------------- //Konwersja uint8_t binToBCD(uint8_t data) { uint8_t result; result = (data / 10) << 4; result += (data % 10); return result; } BCDTime binToBCD(time data) { BCDTime result; result.sec = binToBCD(data.sec); result.min = binToBCD(data.min); result.hour = binToBCD(data.hour); result.wday = binToBCD(data.wday); result.day = binToBCD(data.day); result.month = binToBCD(data.month); result.year = binToBCD(data.year); return result; } uint8_t BCDToBin(uint8_t data) { uint8_t result; result = data & 0x0f; result += ((data >> 4) /*& 0x03*/) * 10; return result; } time BCDToBin(BCDTime data) { time result; result.sec = BCDToBin(data.sec); result.min = BCDToBin(data.min); result.hour = BCDToBin(data.hour); result.wday = BCDToBin(data.wday); result.day = BCDToBin(data.day); result.month = BCDToBin(data.month); result.year = BCDToBin(data.year); return result; } //-------------------------------------------------- //Inicjalizacja komunikacji z DS1307 void rtcInit() { //Pull-up na magistali set(PORTC, PC5); set(PORTC, PC4); //Prędkość transmisji twiInit(400000); //Wstępna konfiguracja DS1307 bool reset = checkReset(); if(reset == true) { time czas; czas = rtcGetTime(); rtcSetTime(czas); } twiStart(); twiAddress(208); twiWrite(7); twiWrite(0b00010000); //generator 1Hz twiStop(); //Konfiguracja przerwania od czasu set(PORTD, PD2); //Pull-up na INT0 MCUCR |= (1 << ISC00) | (1 << ISC01); //Przerwanie przy rosnącym zboczu GICR |= (1 << INT0); //Włącz przerwanie } //-------------------------------------------------- //Okrojone funkje do dodawania i odejmowania czasu //Odejmowanie void rtcDiff(time* min, time* sub) { //Zmienne przechowujące przeniesienia uint8_t carry_sec = 0, carry_min = 0, carry_hour = 0; //Kolejne odejmowania z przeniesieniem //Milisekundy if(min->msec < sub->msec) { carry_sec++; min->msec += 1000; } min->msec -= sub->msec; //Sekundy if(min->sec < (sub->sec + carry_sec)) { carry_min++; min->sec += 60; } min->sec -= (sub->sec + carry_sec); //Minuty if(min->min < (sub->min + carry_min)) { carry_hour++; min->min += 60; } min->min -= (sub->min + carry_min); //Godziny if(min->hour < (sub->hour + carry_hour)) { min->hour += 24; } min->hour -= (sub->hour + carry_hour); //TODO można ew. rozszerzyć odejmowanie na pozostałe pola //Nie jest to konieczne do poprawnego działania } //Dodawanie void rtcSum(time* element_a, time* element_b) { //Zmienne przechowujące przeniesienia uint8_t carry_sec = 0, carry_min = 0, carry_hour = 0; //Kolejne dodawania z przeniesieniem //Milisekundy if((element_a->msec + element_b->msec) > 999) { carry_sec++; element_a->msec = (element_a->msec + element_b->msec) - 1000; } else element_a->msec += element_b->msec; //Sekundy if((element_a->sec + element_b->sec + carry_sec) > 59) { carry_min++; element_a->sec = (element_a->sec + element_b->sec + carry_sec) - 60; } else element_a->sec += element_b->sec + carry_sec; //Minuty if((element_a->min + element_b->min + carry_min) > 59) { carry_hour++; element_a->min = (element_a->min + element_b->min + carry_min) - 60; } else element_a->min += element_b->min + carry_min; //Godziny if((element_a->hour + element_b->hour + carry_hour) > 23) element_a->hour = (element_a->hour + element_b->hour + carry_hour) - 24; else element_a->hour += element_b->hour + carry_hour; //TODO można ew. rozszerzyć dodawnie na pozostałe pola //Nie jest to konieczne do poprawnego działania }
twi.h
//Obsługa TWI //Plik twi.h #ifndef TWI_H_ #define TWI_H_ #include <avr/io.h> //Inicjalizacja z ustawieniem prędkości transmisji void twiInit(uint32_t speed); //Wyślij start/stop void twiStart(); void twiStop(); //Wyślij adres/dane void twiAddress(uint8_t data); void twiWrite(uint8_t data); //Odczytaj dane z lub bez NACK uint8_t twiReadACK(); uint8_t twiReadNACK(); #endif
twi.ccp
//Obsługa TWI //Plik twi.cpp #include "twi.h" //Inicjalizacja z ustawieniem prędkości transmisji void twiInit(uint32_t speed) { //Włącz TWI TWCR = (1<<TWEA) | (1<<TWEN); //Ustaw prędkość transmisji uint8_t prescaler = 0; speed = (F_CPU/speed/100-16)/2; while(speed > 255) { prescaler++; speed=speed/4; } TWSR = (TWSR & ((1<<TWPS1) | (1<<TWPS0))) | prescaler; TWBR = speed; } //Wyślij start void twiStart() { TWCR = (1<<TWINT)|(1<<TWSTA)| (1<<TWEN); //wyślij while (!(TWCR & (1<<TWINT))); //czekaj } //Wyślij stop void twiStop() { TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO); //wyślij while(TWCR & (1<<TWSTO)); //czekaj } //Wyślij adres void twiAddress(uint8_t data) { TWDR = data; //wyślij TWCR = (1<<TWINT) | (1<<TWEN); while (!(TWCR & (1<<TWINT))); //czekaj } //Wyślij dane void twiWrite(uint8_t data) { TWDR = data; //wyślij TWCR = (1<<TWINT) | (1<<TWEN); while (!(TWCR & (1<<TWINT))); //czekaj } //Odczytaj dane uint8_t twiReadACK() { TWCR = (1<<TWEA) | (1<<TWINT) | (1<<TWEN); //odbierz while (!(TWCR & (1<<TWINT))); //czekaj return TWDR; //zwróć wynik } //Odczytaj i wyg. NACK uint8_t twiReadNACK() { TWCR = (1<<TWINT) | (1<<TWEN); //odbierz while (!(TWCR & (1<<TWINT))); //czekaj return TWDR; //zwróć wynik }
Pliki główne
user.h
//Projekt -Devastator- //Plik user.h //Autor wieloiksiasty //Obsługa użytkownika #include <avr/io.h> #include <util/delay.h> #ifndef USER_H_ #define USER_H_ //Przydatne makra #define set(reg, num) ((reg) |= (1 << (num))) #define clear(reg, num) ((reg) &= ~(1 << (num))) #define on(reg, num) ((reg) &= ~(1 << (num))) #define off(reg, num) ((reg) |= (1 << (num))) //-------------------------------------------------- //Wyświetlacz LED //Definicje segmentów #define LEDA_DDR DDRB #define LEDA_PORT PORTB #define LEDA_NUM PB0 #define LEDB_DDR DDRD #define LEDB_PORT PORTD #define LEDB_NUM PD7 #define LEDC_DDR DDRD #define LEDC_PORT PORTD #define LEDC_NUM PD4 #define LEDD_DDR DDRB #define LEDD_PORT PORTB #define LEDD_NUM PB6 #define LEDE_DDR DDRB #define LEDE_PORT PORTB #define LEDE_NUM PB7 #define LEDF_DDR DDRD #define LEDF_PORT PORTD #define LEDF_NUM PD6 #define LEDG_DDR DDRD #define LEDG_PORT PORTD #define LEDG_NUM PD5 #define LEDDT_DDR DDRD //Kropka #define LEDDT_PORT PORTD #define LEDDT_NUM PD3 //Definicje wyprowadzeń rejestru przesuwnego #define DSP_DATA_DDR DDRD //Data #define DSP_DATA_PORT PORTD #define DSP_DATA_NUM PD1 #define DSP_CLK_DDR DDRD //Zegar #define DSP_CLK_PORT PORTD #define DSP_CLK_NUM PD0 //Inicjalizacja wyświetlacza void ledInit(); //Obsługa wyświetlacza void ledProcess(uint8_t content[], uint8_t dot); //-------------------------------------------------- //Kropka #define KROPKA_DDR DDRC #define KROPKA_PORT PORTC #define KROPKA_NUM PC3 //Inicjalizacja kropki void kropkaInit(); //Zaświeć/gaś kropkę void kropkaOn(); void kropkaOff(); //-------------------------------------------------- //Przyciski //Definicje #define DB_TIME 10 //Stała filtracji drgań //Więcej dłużej/dokładniej //Mniej szybciej/bardziej pobieżnie #define SW1_DDR DDRB #define SW1_PORT PORTB #define SW1_PIN PINB #define SW1_NUM PB4 #define SW2_DDR DDRB #define SW2_PORT PORTB #define SW2_PIN PINB #define SW2_NUM PB3 #define SW3_DDR DDRB #define SW3_PORT PORTB #define SW3_PIN PINB #define SW3_NUM PB2 #define SW4_DDR DDRB #define SW4_PORT PORTB #define SW4_PIN PINB #define SW4_NUM PB5 //Przydatne makro #define readPin(pin, num) (~pin & (1<<num)) //Inicjalizacja przycisków void swInit(); //Obsługa przycisków //Zwraca wciśnięty przycisk uint8_t swProcess(); //-------------------------------------------------- //Buzzer #define BUZZER_DDR DDRB #define BUZZER_PORT PORTB #define BUZZER_NUM PB1 //Inicjalizacja buzzera inline void buzzerInit() {set(BUZZER_DDR, BUZZER_NUM);} //Włącz/wyłącz buzzer inline void buzzerOn() {set(BUZZER_PORT, BUZZER_NUM);} inline void buzzerOff() {clear(BUZZER_PORT, BUZZER_NUM);} #endif /* USER_H_ */
user.ccp
//Projekt -Devastator- //Plik user.cpp //Autor wieloiksiasty //Obsługa użytkownika #include "user.h" //-------------------------------------------------- //Wyświetlacz LED //Inicjalizacja wyświetlacza LED void ledInit() { //Segmenty //Kierunek set(LEDA_DDR, LEDA_NUM); set(LEDB_DDR, LEDB_NUM); set(LEDC_DDR, LEDC_NUM); set(LEDD_DDR, LEDD_NUM); set(LEDE_DDR, LEDE_NUM); set(LEDF_DDR, LEDF_NUM); set(LEDG_DDR, LEDG_NUM); set(LEDDT_DDR, LEDDT_NUM); //Stan początkowy set(LEDA_PORT, LEDA_NUM); set(LEDB_PORT, LEDB_NUM); set(LEDC_PORT, LEDC_NUM); set(LEDD_PORT, LEDD_NUM); set(LEDE_PORT, LEDE_NUM); set(LEDF_PORT, LEDF_NUM); set(LEDG_PORT, LEDG_NUM); set(LEDDT_PORT, LEDDT_NUM); //Rejestr przesuwny set(DSP_DATA_DDR, DSP_DATA_NUM); set(DSP_CLK_DDR, DSP_CLK_NUM); clear(DSP_DATA_PORT, DSP_DATA_NUM); set(DSP_DATA_PORT, DSP_DATA_NUM); //Zerowanie wyjść rejestru for(uint8_t i = 0; i < 8; i++) { set(DSP_CLK_PORT, DSP_CLK_NUM); clear(DSP_CLK_PORT, DSP_CLK_NUM); } } //Obsługa wyświetlacza LED void ledProcess(uint8_t content[], uint8_t dot) { volatile static uint8_t current = 5; //Zgaszenie wyświetlacza w celu uniknięcia poświaty off(LEDA_PORT, LEDA_NUM); off(LEDB_PORT, LEDB_NUM); off(LEDC_PORT, LEDC_NUM); off(LEDD_PORT, LEDD_NUM); off(LEDE_PORT, LEDE_NUM); off(LEDF_PORT, LEDF_NUM); off(LEDG_PORT, LEDG_NUM); off(LEDDT_PORT, LEDDT_NUM); //Wystarczy przejść na następny if(current < 5) { set(DSP_DATA_PORT, DSP_DATA_NUM); set(DSP_CLK_PORT, DSP_CLK_NUM); clear(DSP_CLK_PORT, DSP_CLK_NUM); current++; } //Jeśli nie, wróć do początku else { clear(DSP_DATA_PORT, DSP_DATA_NUM); set(DSP_CLK_PORT, DSP_CLK_NUM); clear(DSP_CLK_PORT, DSP_CLK_NUM); current = 0; } //--------------------------- //Wyświetlenie kolejnej cyfry if(dot == current) on(LEDDT_PORT, LEDDT_NUM); else off(LEDDT_PORT, LEDDT_NUM); if(content[current] == 1) { off(LEDA_PORT, LEDA_NUM); on(LEDB_PORT, LEDB_NUM); on(LEDC_PORT, LEDC_NUM); off(LEDD_PORT, LEDD_NUM); off(LEDE_PORT, LEDE_NUM); off(LEDF_PORT, LEDF_NUM); off(LEDG_PORT, LEDG_NUM); } if(content[current] == 2) { on(LEDA_PORT, LEDA_NUM); on(LEDB_PORT, LEDB_NUM); off(LEDC_PORT, LEDC_NUM); on(LEDD_PORT, LEDD_NUM); on(LEDE_PORT, LEDE_NUM); off(LEDF_PORT, LEDF_NUM); on(LEDG_PORT, LEDG_NUM); } if(content[current] == 3) { on(LEDA_PORT, LEDA_NUM); on(LEDB_PORT, LEDB_NUM); on(LEDC_PORT, LEDC_NUM); on(LEDD_PORT, LEDD_NUM); off(LEDE_PORT, LEDE_NUM); off(LEDF_PORT, LEDF_NUM); on(LEDG_PORT, LEDG_NUM); } if(content[current] == 4) { off(LEDA_PORT, LEDA_NUM); on(LEDB_PORT, LEDB_NUM); on(LEDC_PORT, LEDC_NUM); off(LEDD_PORT, LEDD_NUM); off(LEDE_PORT, LEDE_NUM); on(LEDF_PORT, LEDF_NUM); on(LEDG_PORT, LEDG_NUM); } if(content[current] == 5) { on(LEDA_PORT, LEDA_NUM); off(LEDB_PORT, LEDB_NUM); on(LEDC_PORT, LEDC_NUM); on(LEDD_PORT, LEDD_NUM); off(LEDE_PORT, LEDE_NUM); on(LEDF_PORT, LEDF_NUM); on(LEDG_PORT, LEDG_NUM); } if(content[current] == 6) { on(LEDA_PORT, LEDA_NUM); off(LEDB_PORT, LEDB_NUM); on(LEDC_PORT, LEDC_NUM); on(LEDD_PORT, LEDD_NUM); on(LEDE_PORT, LEDE_NUM); on(LEDF_PORT, LEDF_NUM); on(LEDG_PORT, LEDG_NUM); } if(content[current] == 7) { on(LEDA_PORT, LEDA_NUM); on(LEDB_PORT, LEDB_NUM); on(LEDC_PORT, LEDC_NUM); off(LEDD_PORT, LEDD_NUM); off(LEDE_PORT, LEDE_NUM); off(LEDF_PORT, LEDF_NUM); off(LEDG_PORT, LEDG_NUM); } if(content[current] == 8) { on(LEDA_PORT, LEDA_NUM); on(LEDB_PORT, LEDB_NUM); on(LEDC_PORT, LEDC_NUM); on(LEDD_PORT, LEDD_NUM); on(LEDE_PORT, LEDE_NUM); on(LEDF_PORT, LEDF_NUM); on(LEDG_PORT, LEDG_NUM); } if(content[current] == 9) { on(LEDA_PORT, LEDA_NUM); on(LEDB_PORT, LEDB_NUM); on(LEDC_PORT, LEDC_NUM); on(LEDD_PORT, LEDD_NUM); off(LEDE_PORT, LEDE_NUM); on(LEDF_PORT, LEDF_NUM); on(LEDG_PORT, LEDG_NUM); } if(content[current] == 0) { on(LEDA_PORT, LEDA_NUM); on(LEDB_PORT, LEDB_NUM); on(LEDC_PORT, LEDC_NUM); on(LEDD_PORT, LEDD_NUM); on(LEDE_PORT, LEDE_NUM); on(LEDF_PORT, LEDF_NUM); off(LEDG_PORT, LEDG_NUM); } if(content[current] == '-') { off(LEDA_PORT, LEDA_NUM); off(LEDB_PORT, LEDB_NUM); off(LEDC_PORT, LEDC_NUM); off(LEDD_PORT, LEDD_NUM); off(LEDE_PORT, LEDE_NUM); off(LEDF_PORT, LEDF_NUM); on(LEDG_PORT, LEDG_NUM); } } //-------------------------------------------------- //Kropka //Inicjalizacja void kropkaInit() { set(KROPKA_DDR, KROPKA_NUM); set(KROPKA_PORT, KROPKA_NUM); } //Zaświeć void kropkaOn() { clear(KROPKA_PORT, KROPKA_NUM); } //Zgaś void kropkaOff() { set(KROPKA_PORT, KROPKA_NUM); } //-------------------------------------------------- //Przyciski //Inicjalizacja void swInit() { //Ustaw odczyt clear(SW1_DDR, SW1_NUM); clear(SW2_DDR, SW2_NUM); clear(SW3_DDR, SW3_NUM); clear(SW4_DDR, SW4_NUM); //Włącz pull_up set(SW1_PORT, SW1_NUM); set(SW2_PORT, SW2_NUM); set(SW3_PORT, SW3_NUM); set(SW4_PORT, SW4_NUM); } //Obsługa uint8_t swProcess() { static uint8_t sw1, sw2, sw3, sw4; //Odczytaj i zrób deboucing if(readPin(SW1_PIN, SW1_NUM) != 0 && sw1 < DB_TIME) sw1++; if(readPin(SW1_PIN, SW1_NUM) == 0) sw1=0; if(readPin(SW2_PIN, SW2_NUM) != 0 && sw2 < DB_TIME) sw2++; if(readPin(SW2_PIN, SW2_NUM) == 0) sw2=0; if(readPin(SW3_PIN, SW3_NUM) != 0 && sw3 < DB_TIME) sw3++; if(readPin(SW3_PIN, SW3_NUM) == 0) sw3=0; if(readPin(SW4_PIN, SW4_NUM) != 0 && sw4 < DB_TIME) sw4++; if(readPin(SW4_PIN, SW4_NUM) == 0) sw4=0; //Jeśli przeszedł przez debouncing zwróć if(sw1 >= DB_TIME) return 1; if(sw2 >= DB_TIME) return 2; if(sw3 >= DB_TIME) return 3; if(sw4 >= DB_TIME) return 4; //Brak wciśniętych => zwróc zero return 0; }
main.ccp
//Projekt -Devastator- //Plik main.cpp //Autor wieloiksiasty //---------- //Nagłówki #include <avr/io.h> #include <avr/interrupt.h> #include <avr/eeprom.h> #include <util/delay.h> #include <util/atomic.h> #include <string.h> #include "RTC/twi.h" #include "RTC/rtc.h" #include "user.h" #include "DS1820/ds.h" //Glogalna struktura czasu time now; time alarm; bool alarm_on; //Dane budzika w EEPROM EEMEM time eealarm_data; EEMEM bool eealarm_on; //---------- //Funkcja main int main() { //Konfiguracja peryferiów: ledInit(); //Wyświetlacza swInit(); //Przycisków kropkaInit(); //Kropki rtcInit(); //Zegara RTC buzzerInit(); //Głośniczka //Załadowanie danych budzika z EEPROM eeprom_read_block(&alarm, &eealarm_data, sizeof(time)); eeprom_read_block(&alarm_on, &eealarm_on, sizeof(bool)); //Konfiguracja timer2 do pętli głównej TCCR2 |= (1 << WGM21); TCCR2 |= (1 << CS20) | (1 << CS21); //Tryb CTC, preskaler 32, ok. 1kHz OCR2 = 249; TIMSK |= (1 << OCIE2); //Przerwanie przy porównaniu kropkaOn(); //Zaświeć kropkę sei(); //Włącz przerwania while(1) { //Loopend :-) //Taki weekend dla mikrokontrolera :-D } } //---------- //Pzerwanie Timer2 ok. 1kHz //Pętla główna całego programu ISR(TIMER2_COMP_vect) { //-------------------------------------------------- //Zmienna statyczne static uint8_t content[6]; //Zawartość wyświetlacza static uint8_t button = 0; //Aktualny przycisk static uint8_t last = 0; //Ostatni przycisk static uint8_t mode = 0; //Aktualny tryb static uint8_t last_mode = 0; //Ostatni tryb static uint16_t buzzer = 0; //Odliczanie budzika (post działania) static bool budzenie = false; //Flaga zadziałania budzika static uint16_t hold_time = 0; //Odliczanie 3 sek. do wejścia w ust. uint8_t dot; //Którą kropkę zapalić? //Sygnalizacja aktywacji budzika if(alarm_on == true) dot = 0; else dot = 0xff; //-------------------------------------------------- //Pre- działania button = swProcess(); //Wczytaj przycisk if(now.msec < 999) //Generator milisekund now.msec++; //Przełączanie funcji if(button == 4 && button != last && mode != 255) { mode++; if(mode > 2) mode = 0; } //Wchodzenie w ustawienia godziny if(button == 4) { if(hold_time <= 3000) hold_time++; if(hold_time > 3000) mode = 255; } else hold_time=0; //Piszczenie przy naciśnięciu przycisku if(button != last && button != 0) buzzer = 100; //Sprawdzenie czy nie należy uruchomić budzika if(now.hour == alarm.hour && now.min == alarm.min && now.sec == alarm.sec && alarm_on == true) budzenie = true; //Aktywny budzik wymusz tryb zegara if(budzenie == true) mode = 0; //-------------------------------------------------- //Funkcja 0 => Zegar if(mode == 0) { //---------- //Obsługa budzenia if(budzenie == true) { if(button == 0) //Nic nie jest wciśnięte { //Sygnały akustyczne if(now.msec == 1) buzzer = 500; if(now.msec < 500) { kropkaOn(); content[0] = now.sec%10; //Wyświetl godzinę content[1] = now.sec/10; content[2] = now.min%10; content[3] = now.min/10; content[4] = now.hour%10; content[5] = now.hour/10; } else { kropkaOff(); content[0] = 0xFF; //Sygnały wizualne, miganie wyświetlacza content[1] = 0xFF; content[2] = 0xFF; content[3] = 0xFF; content[4] = 0xFF; content[5] = 0xFF; } } //Wciśnięcie czegokolwiek dezaktywuje budzenie else budzenie = false; } //---------- //Normalne działanie else { //---------- //Obsługa przycisków if(button == 0) //Nic nie jest wciśnięte { if(now.msec < 500) kropkaOn(); else kropkaOff(); content[0] = now.sec%10; //Wyświetl godzinę content[1] = now.sec/10; content[2] = now.min%10; content[3] = now.min/10; content[4] = now.hour%10; content[5] = now.hour/10; } else if(button == 1) //Przycisk 1 { kropkaOff(); content[0] = now.year%10; //Wyświetl datę content[1] = now.year/10; content[2] = now.month%10; content[3] = now.month/10; content[4] = now.day%10; content[5] = now.day/10; } else if(button == 2) //Przycisk 2, temperatura { static uint16_t temp; static bool readed = false; //Przy pierwszej iteracji zapoczątkuj konwersję if(last != 2) dsConvertT(); //W następnych odczytuj temp = dsRead(); kropkaOff(); //Wyłącz dwukropki content[0] = 0; content[1] = (temp/1)%10; //Wyświetl temperaturę content[2] = (temp/10)%10; content[3] = (temp/100)%10; //Zadbaj o znak - jeśli konieczne if(temp < 0) content[4] = '-'; else content[4] = 0xFF; content[5] = 0xFF; dot = 2; //Przecinek na 2 pozycji } else if(button == 3 && last != button) // Przycisk 3 { if(alarm_on == true) //Włącz, wyłącz budzik alarm_on = false; else alarm_on = true; //Zapisz wynik do eeprom eeprom_write_block(&alarm_on, &eealarm_on, sizeof(bool)); kropkaOn(); content[0] = alarm.sec%10; //Wyświetl godzinę budzika content[1] = alarm.sec/10; content[2] = alarm.min%10; content[3] = alarm.min/10; content[4] = alarm.hour%10; content[5] = alarm.hour/10; } } } //-------------------------------------------------- //Funkcja 1 - stoper //---------- //Zmienne stopera static bool stopwatchStarted = false; static bool stopwatchFreezen; static time stopwatchStartTime; static time stopwatchOffset; static time stopwatchOutput; stopwatchFreezen = false; //---------- //Obsługa przycisków if(mode == 1) { //Przycisk 1 - start/pause if(button == 1 && button != last) { stopwatchStarted ^= true; if(stopwatchStarted == true) //Przy starcie załaduj czas startu stopwatchStartTime = now; else stopwatchOffset = stopwatchOutput; //Przy pauzie załaduj offset } //Przycisk 2 - reset else if(button == 2 && button != last) { stopwatchStarted = false; //Wyzeruj wszystko memset(&stopwatchOutput, 0, sizeof(time)); memset(&stopwatchOffset, 0, sizeof(time)); } //Przycisk 3 - lap - zamrożenie wyświetlacza else if(button == 3) stopwatchFreezen = true; } //---------- //Obliczanie czasu do wyświetlenia if(stopwatchStarted == true) { //Oblicz czas od ostatniego startu stopwatchOutput = now; rtcDiff(&stopwatchOutput, &stopwatchStartTime); //Dodaj offset rtcSum(&stopwatchOutput, &stopwatchOffset); } //---------- //Obsługa wyjścia stopera if(mode == 1) //Włączona funkcja stopera { //Miganie kropkami if(stopwatchOutput.msec < 500 || stopwatchFreezen == true || stopwatchStarted == false) kropkaOn(); else kropkaOff(); //Sygnały dzwiękowe stopera if(stopwatchOutput.msec == 0 && stopwatchStarted == true) buzzer = 100; if(stopwatchOutput.msec == 0 && stopwatchOutput.sec == 0 && stopwatchStarted == true) buzzer = 500; //Przekazywanie danych do wyświetlenia if(stopwatchFreezen == false) { if(stopwatchOutput.hour > 0) { content[0] = stopwatchOutput.sec%10; //Wyświetl czas content[1] = stopwatchOutput.sec/10; content[2] = stopwatchOutput.min%10; content[3] = stopwatchOutput.min/10; content[4] = stopwatchOutput.hour%10; content[5] = stopwatchOutput.hour/10; } else { content[0] = (stopwatchOutput.msec/10)%10; //Wyświetl czas, content[1] = stopwatchOutput.msec/100; //nie pokazuj zera w godzinach tylko m. sekundy content[2] = stopwatchOutput.sec%10; content[3] = stopwatchOutput.sec/10; content[4] = stopwatchOutput.min%10; content[5] = stopwatchOutput.min/10; } } } //-------------------------------------------------- //Funckja 2 - minutnik/timer //Zmienne statyczne minutnika static time timerOutput; static time timerUserSet; static time timerTarget; static bool timerStarted = false; static uint8_t timerHighlighted = 3; //Tryb normalny na start //Jeśli wybrano minutnik if(mode == 2) { //Tryb normalny if(timerHighlighted >= 3) { //Start/stop if(button == 1 && button != last) { timerStarted ^= true; timerTarget = timerOutput; rtcSum(&timerTarget, &now); //Oblicz czas docelowy } //Reset if(button == 2 && button != last) { timerStarted = false; //Wyzeruj timerOutput = timerUserSet; } //Wejdź w ustawianie czasu if(button == 3 && button != last) { timerStarted = false; timerOutput = timerUserSet; //Załaduj wstępne dane timerHighlighted = 0; } if(timerStarted == true) { //Oblicz czas do wyświetlenia timerOutput = timerTarget; rtcDiff(&timerOutput, &now); //Zasygnalizuj koniec czasu i zatrzymaj if(timerOutput.msec == 0 && timerOutput.sec == 0 && timerOutput.min == 0 && timerOutput.hour == 0) { timerStarted = false; buzzer = 2000; } } //Wyświetlanie if (timerOutput.hour == 0) { //Jeśli nie trzeba godzin pokaż milisekundy content[0] = (timerOutput.msec/10)%10; content[1] = (timerOutput.msec/100)%10; content[2] = (timerOutput.sec/1)%10; content[3] = (timerOutput.sec/10)%10; content[4] = (timerOutput.min/1)%10; content[5] = (timerOutput.min/10)%10; } else { content[0] = (timerOutput.sec/1)%10; content[1] = (timerOutput.sec/10)%10; content[2] = (timerOutput.min/1)%10; content[3] = (timerOutput.min/10)%10; content[4] = (timerOutput.hour/1)%10; content[5] = (timerOutput.hour/10)%10; } //Buzzer //Gdy mniej niż 10s pikaj częściej if(timerOutput.hour == 0 && timerOutput.min == 0 && timerOutput.sec <= 10 && timerStarted == true) { if(timerOutput.msec % 200 == 0) //co 200ms buzzer = 50; } //Gdy więcej niż 10s pikaj co 1s else if(timerOutput.msec % 1000 == 0 && timerStarted == true) buzzer = 100; //Miganie kropką if(timerOutput.msec > 500) kropkaOn(); else kropkaOff(); if(timerStarted == false) kropkaOn(); } //Tryb ustawiania czasu else if(timerHighlighted < 3) { kropkaOn(); //Przcisk 1 - inkrementacja podświetlonej wartości if(button == 1 && button != last) { //Sekundy if(timerHighlighted == 0) { timerUserSet.sec++; if(timerUserSet.sec >= 60) timerUserSet.sec = 0; } //Minuty if(timerHighlighted == 1) { timerUserSet.min++; if(timerUserSet.min >= 60) timerUserSet.min = 0; } //Godziny if(timerHighlighted == 2) { timerUserSet.hour++; if(timerUserSet.hour >= 24) timerUserSet.hour = 0; } } //Przycisk 2 - dekrementacja podświetlonego if(button == 2 && button != last) { //Sekundy if(timerHighlighted == 0) { if(timerUserSet.sec == 0) timerUserSet.sec = 60; timerUserSet.sec--; } //Minuty if(timerHighlighted == 1) { if(timerUserSet.min == 0) timerUserSet.min = 60; timerUserSet.min--; } //Godziny if(timerHighlighted == 2) { if(timerUserSet.hour == 0) timerUserSet.hour = 24; timerUserSet.hour--; } } //Zmiana podświetlenia na następne lub wyjście if(button == 3 && button != last) { timerHighlighted++; timerOutput = timerUserSet; } //Wyświetlanie content[0] = (timerUserSet.sec/1)%10; content[1] = (timerUserSet.sec/10)%10; content[2] = (timerUserSet.min/1)%10; content[3] = (timerUserSet.min/10)%10; content[4] = (timerUserSet.hour/1)%10; content[5] = (timerUserSet.hour/10)%10; //Miganie podświetlonego if(now.msec < 500) { if(timerHighlighted == 0) { content[0] = 0xFF; content[1] = 0xFF; } else if(timerHighlighted == 1) { content[2] = 0xFF; content[3] = 0xFF; } else if(timerHighlighted == 2) { content[4] = 0xFF; content[5] = 0xFF; } } } } //-------------------------------------------------- //Funckja 255 - ustawienia godziny, daty i budzika static uint8_t highlighted = 5; if(mode == 255) { //Wstępna konfiguracja kropkaOn(); static time new_time; //Załadowanie wstępnych danych if(last_mode != mode) { new_time = now; new_time.msec = 0; new_time.sec = 0; highlighted = 8; } //-------------------------------------------------- //Obsługa przycisków //---------- //Zmień podświetlenie if(button == 3 && last != button) { if(highlighted == 0) highlighted = 9; highlighted--; } //---------- //Dodaj wartość do podświetlonego else if(button == 1 && last != button) { //Sekundy if(highlighted == 0) { new_time.sec++; if(new_time.sec >= 60) new_time.sec = 0; } //Minuty else if(highlighted == 1) { new_time.min++; if(new_time.min >= 60) new_time.min = 0; } //Godziny else if(highlighted == 2) { new_time.hour++; if(new_time.hour >= 60) new_time.hour = 0; } //Dni else if(highlighted == 8) { new_time.day++; if(new_time.day >= 32) new_time.day = 0; } //Miesiące else if(highlighted == 7) { new_time.month++; if(new_time.month >= 13) new_time.month = 0; } //Lata else if(highlighted == 6) { new_time.year++; if(new_time.year >= 100) new_time.year = 0; } //Budzik sekundy else if(highlighted == 3) { alarm.sec++; if(alarm.sec >= 60) alarm.sec = 0; } //Budzik minuty else if(highlighted == 4) { alarm.min++; if(alarm.min >= 60) alarm.min = 0; } //Budzik godziny else if(highlighted == 5) { alarm.hour++; if(alarm.hour >= 24) alarm.hour = 0; } } else if(button == 2 && last != button) //Odejmin wartość od podświetlonego { //Sekundy if(highlighted == 0) { if(new_time.sec == 0) new_time.sec = 60; new_time.sec--; } //Minuty else if(highlighted == 1) { if(new_time.min == 0) new_time.min = 60; new_time.min--; } //Godziny else if(highlighted == 2) { if(new_time.hour == 0) new_time.hour = 60; new_time.hour--; } //Dni else if(highlighted == 8) { if(new_time.day == 0) new_time.day = 32; new_time.day--; } //Miesiące else if(highlighted == 7) { if(new_time.month == 0) new_time.month = 13; new_time.month--; } //Lata else if(highlighted == 6) { if(new_time.year == 0) new_time.year = 100; new_time.year--; } //Budzik sekundy else if(highlighted == 3) { if(alarm.sec == 0) alarm.sec = 60; alarm.sec--; } //Budzik minuty else if(highlighted == 4) { if(alarm.min == 0) alarm.min = 60; alarm.min--; } //Budzik godziny else if(highlighted == 5) { if(alarm.hour == 0) alarm.hour = 24; alarm.hour--; } } else if(button == 4 && last != button) //Zapisz do DSa i wyjdź z trybu { rtcSetTime(new_time); eeprom_write_block(&alarm, &eealarm_data, sizeof(time)); mode = 0; } //Wyświetlanie if(highlighted <= 2) //Podświetlone sek/min/godz { content[0] = new_time.sec%10; //Wyświetl czas content[1] = new_time.sec/10; content[2] = new_time.min%10; content[3] = new_time.min/10; content[4] = new_time.hour%10; content[5] = new_time.hour/10; } else if(highlighted >= 3 && highlighted <= 5) //Budzik { content[0] = alarm.sec%10; //Wyświetl budzik content[1] = alarm.sec/10; content[2] = alarm.min%10; content[3] = alarm.min/10; content[4] = alarm.hour%10; content[5] = alarm.hour/10; } else if(highlighted >= 6 && highlighted <= 8) //Podświetlone dni/mies/lata { content[0] = new_time.year%10; //Wyświetl datę content[1] = new_time.year/10; content[2] = new_time.month%10; content[3] = new_time.month/10; content[4] = new_time.day%10; content[5] = new_time.day/10; } if(now.msec < 500) //Miganie podświetlonego { if(highlighted == 0 || highlighted == 3 || highlighted == 6) { content[0] = 0xff; content[1] = 0xff; } if(highlighted == 1 || highlighted == 4 || highlighted == 7) { content[2] = 0xff; content[3] = 0xff; } if(highlighted == 2 || highlighted == 5 || highlighted == 8) { content[4] = 0xff; content[5] = 0xff; } } dot = (highlighted/3); //Kropka pomaga w orientacji } //-------------------------------------------------- //Post- działania //Obsługa wyświetlacza ledProcess(content, dot); //Załaduj ostatni przycisk i ostatni tryb last = button; last_mode = mode; //Opóźnione odliczanie buzzera if(buzzer > 0) { buzzer--; buzzerOn(); } else buzzerOff(); } //---------- //Pzerwanie zewnętrzne INT0 1Hz //Pętla główna aktualizacji czasu //Nieblokowanie pozwala na elimicję migotania wyśw. ISR(INT0_vect, ISR_NOBLOCK) { time buffer; //Odczyt może być przerywany buffer = rtcGetTime(); ATOMIC_BLOCK(ATOMIC_FORCEON) //Bezpośrednia aktualizacja { //jest operacją atomową now = buffer; now.msec = 0; } }
Projekt ciekawy. Obawiam sie, jednak, że w kontekscie ostatnich zamachów terrorystycznych w Rosji moze budzic u niektorych niesmak :(
OdpowiedzUsuńFaktycznie przykry zbieg okoliczności ... artykuł jednak został opracowany ponad tydzień wcześniej, a sam projekt jeszcze wcześniej.
UsuńPrzecież prawdziwa bomba nie wygląda jak bomba. To tylko doskonały pomysł na efektowną, humorystyczną i estetyczną alternatywę obudowy - bomba z kreskówki.
Usuń... doskonały, co widać po ilości lików na FB.
UsuńI tak projekt przerósł twórcę, gdy pokazałem prototyp przedpremierowo kilkunastu osobom z klasy.Wszyscy dostali głupawki jak małe dzieci, które dostały nową zabawkę :-D
OdpowiedzUsuńChciałbym zamieścić odpowiedź do "Uwag redakcji"
OdpowiedzUsuń1. Brak rezystorów nie jest rozwiązaniem w pełni zgodnym ze sztuką. To prawda. Warto jednak zauważyć, że takie rozwiązanie w praktyce działa. Oczywiście zastosowanie takiego a nie innego rozwiązania nie jest podyktowane moją ignorancją lub niewiedzą. Jest to spowodowane ściskiem na płytce drukowanej. Używając tylko elementów przewlekanych (całość miała być prosta do samodzielnego wykonania) niemożliwe okazało się dodanie tak dużej ilości elementów. 8 rezystorów i układ scalony to naprawdę dużo.
Kompromis - ulubione słowo każdego konstruktora.
2. Absolutnie nie zgadzam się z punktem 2. Celowo zastosowałem rejestr przesuwny bez zatrzasku. Znacząco uprasza to obsługę i zwalnia jeden pin mikrokontrolera. Proszę zauważyć, że do rejestru nie jest ładowany cały bajt przy każdym przełączeniu wyświetlacza, co mogłoby powodować powidok. W każdym cyklu ładowany jest tylko jeden bit. Na początku ładujemy zero, a następnie same jedynki przesuwając zero w prawo o jeden krok. Eliminuje to problem o którym wspomniała redakcja, powidok nie występuje.
Jak zawsze, ocena w pewnej mierze jest subiektywna. Ponieważ problem rezystorów dyskutowaliśmy, wspólnie doszliśmy do wniosku, że jest to poważny błąd. Projekty konkursowe celowo są umieszczane w całości (tzn. źródła + schematy) - chodzi o walor edukacyjny dla nowych adeptów i fanów mikrokontrolerów - stąd też musimy zwracać uwagę na ich poprawność. Stąd to, że coś działa nie jest wystarczającym dowodem na poprawność - LED bez rezystorów się po prostu nie łączy i tyle (wyjątkiem jest dostępność źródła prądowego) co Dondu opisał w jednym z artykułów na temat LEDów. Trudno się też zgodzić z argumentem, że na płytce nie ma miejsca dla rezystorów. IMHO zostało go sporo - a zawsze można było zastosować rezystory w SMD (nie mówię o np. 0402, ale 1206 są całkiem spore i wygodne do lutowania), można było zastosować drabinki rezystorowe w THT.
UsuńCo do rejestru przesuwnego - tu znowu istotny jest walor edukacyjny - na 164 da się to zrobić, ale w najlepszym przypadku ograniczy duty cylce wyświetlacza (konieczność wyłączenia wyświetlacza na czas przesuwania bitów), co skutkuje zmniejszoną jasnością. W tym projekcie to nie problem ale w ogólności jednak tak. Piszesz o konieczności wykorzystania dodatkowego pinu IO w przypadku zastosowania 595 - zgoda, lecz zauważ, że zostały ci wolne piny. Gdyby ich nie było, to takie zastosowanie 164 i sposób sterowania pewnie uznalibyśmy za plus i pomysłowość autora (no chyba, że ktoś by przyjął, że skoro zabrakło pinu to znaczy, że źle dobrany jest procesor:) ).
Znowu - nasze uwagi to nie tyle zastrzeżenia co do twojego projektu, lecz uwagi dla osób początkujących, które przeglądając wasze projekty się uczą. Chcielibyśmy, aby nabierali dobrych nawyków, stąd nasze komentarze.Jak widać po naszych ocenach, wymienione uchybienia nie spowodowały istotnego obniżenia naszej wysokiej oceny dla projektu.
Także nie tylko projektowanie jest sztuką kompromisu, ale ocena też - w tym przypadku pomiędzy pomysłowością, walorami estetycznymi projektu i jego generalnym wysokim poziomem wykonania, a celem jaki nam przyświeca - czyli edukacją i pokazywaniem poprawnych (co nie znaczy jedynie słusznych) dróg rozwiązywania problemów.
Myślę, że Dondu się ze mną zgodzi?
Tak, całkowicie się z Tobą Tmf zgadzam. Tak właśnie podchodzimy do każdego artykułu, a musimy tak robić, bo bardzo często na forum spotykamy początkujących, którzy z braku wiedz i doświadczenia ufają w znalezione w sieci schematy nie widząc, że są na nich przyjęte jakieś kompromisy (o których piszesz Tomku - autorze) i w konsekwencji niszczą swoje projekty lub zastosowane w nich elementy elektroniczne.
UsuńW artykule pod naszymi uwagami dodałem informację i link do niniejszej dyskusji.
Co do rezystorów to rozumiem wasze podejście. Jesteście tu od nauki. Moje rozwiązanie to kompromis, może trochę naciągany, ale jednak konieczny (zakładałem całkowity brak elementów SMD, został jeden).
OdpowiedzUsuńJeśli chodzi o rejestr przesuwny to przecież na czas zmiany wyświetlacz i tak trzeba wyłączyć wyświetlaną cyfrę przed przełączeniem, bo na wyświetlaczu zostaną duchy poprzedniej lub następnej cyfry.
Rozumiem, że podajecie rozwiązania wzorcowe. Takie jest wasze zadanie, trzymajcie tak dalej :-)
"Jeśli chodzi o rejestr przesuwny to przecież na czas zmiany wyświetlacz i tak trzeba wyłączyć wyświetlaną cyfrę przed przełączeniem, bo na wyświetlaczu zostaną duchy poprzedniej lub następnej cyfry."
UsuńNiezupełnie. W rejestrze z zatrzaskiem, np wspomnianym 74HC595 stan wyjść zmieni się dopiero po podaniu stanu wysokiego na pin zatrzasku. W trakcie samego ładowania zmiana nie nastąpi. Tak czy siak, bardzo fajny projekt. Gratulacje!
No właśnie nie! Przecież jeśli w momencie przełączania nadal będzie zaświecona poprzednia cyfra to przez ułamek sekundy pojawi się ona na następnym wyświetlaczu. Lub odwrotnie jeśli załadujemy następną cyfrę przed przełączeniem to pojawi się ona na chwilę na poprzednim. Z zatrzaskiem, czy bez to nie ma znaczenia. Będzie tak nawet bez żadnego rejestru przesuwnego.
UsuńNie wspominając już o tym, że kiedy ładujemy tylko jeden bit to zatrzask nic nie zmienia.
Nie jest tak jak piszesz. Dla rejestru 164, który nie posiada zatrzasku przed wsuwaniem bitów musisz wyłączyć wyświetlacz, żeby nie pokazywał śmieci. Włączyć możesz go ponownie dopiero po wsunięciu nowych danych. W efekcie LED przez dłuższy czas jest wyłączony. Jeśli masz zatrzask (np. 595), to LED wyświetla stare dane, ty w tym czasie wsuwasz nowe, bez wyłączenia wyświetlacza. Po ich wsunięciu gasisz LED, aktywujesz przepisanie danych do zatrzasku i zapalasz LED. Cała operacja trwa jeden takt zegara, a nie 9. W efekcie LED przez większą część czasu jest włączony i dzieki temu jaśniej świeci, co umożliwia multipleksowanie większej liczby wyświetlaczy na raz.
UsuńTłumaczę, że mój program nie wsuwa "danych", ale tylko jeden bit, więc zatrzask nie ma racji bytu. Nie wsuwam danych, tylko przemieszczam to co już jest o jedno miejsce w prawo. Przecież załadowanie 1 bitu odbywa się natychmiast.
UsuńPrzeanalizuj plik user.cpp linie 65-85.
Co do rezystorów w przypadku diod, to z własnego doświadczenia wiem że czasami się przydają.:)
UsuńJeśli chodzi o rejestry to zatrzaskowy 595 jest lepszy, ale w tym przypadku rację ma autor projektu. Bez patrzenia w kod już na schemacie widzimy, że autor nie używa rejestru nie do zapalania poszczególnych segmentów, ale do przełączania wyświetlaczy poprzez zwykłe "shiftowanie" w tym przypadku zera bo sterujemy tranzystorami PNP. Zastosowanie zatrzasku w tym przypadku (nie zauważalnie) zmniejszyło by czas świecenia wyświetlaczy o konieczność właśnie aktywacji wyjść. Nie chce mi się skupiać w kod bo nie programuję AVRów ale w tym przypadku poświaty unikniemy ustawiając jedynki na piny mikrokontrolera sterujące segmentami wyświetlaczy w czasie przesyłania przełączania tranzystorów, który jednak był by dłuższy przy zastosowaniu *595.
Swoją drogą w przypadku takiego sterowania wyświetlaczami można by się pokusić o zastosowanie jakiegoś licznika Johnsona i przełączanie wyświetlaczy tylko jednym pinem uC.
W całej dyskusji chyba zaszło małe nieporozumienie. W projekcie autora, istotnie rejestr 164 jest ok - nie steruje on segmentami, lecz jedynie tranzystorem sterującym wyświetlaczem. W takim zastosowaniu jest ok i duchy nie będą powstawały. Pewna wada polega na konieczności wyłączenia wyświetlacza (wyłączenia wszystkich segmentów) przed zmianą rejestru 164. Wada niewielka.
UsuńMyślę, że nasza dyskusją dotyczyła jakby bardziej ogólnego podejścia do sterowania wyświetlaczem z wykorzystaniem SPI - idea takiego sterowania bierze się z konieczności ograniczenia liczby wykorzystanych pinów IO. Dowolnie długi wyświetlacz możemy sterować przy pomocy kilku sygnałów - MOSI, SCK i opcjonalnego LE oraz CLR. Na prezentowanym schemacie wykorzystanych jest 10 sygnałów, co jest uzasadnione brakiem konieczności zastosowania dodatkowego scalaka. A czy 164 można było wyeliminować? Pozornie brakuje nam 2 wolnych pinów IO - ale łatwo można by je uzyskać zmieniając podłączenie switchy.
Ja dodam jeszcze tylko, że ten element nie wpłynął znacząco na ocenę końcową artykułu, a nawet gdybyśmy ocenili projekt perfekcyjnie na 5 i tak nie wyprzedziłby zwycięzcy Świąteczny konkurs DIY (grudzień 2013r.).
UsuńHello!
OdpowiedzUsuńI would like to build this structure, it would be so much the question that you do not have LEDs to the resistance? Such a display is used: http://www.hqelektronika.hu/product/da08_11srwa
Thank Lajos Varga from Hungary.
Hello.
UsuńYes, You are right. Resistors should be added. This is an competition article. For this reason this project has awarded fewer number of points. We wrote about it at the end of article and in our (Dondu & tmf) comments.
Regards from Poland!
Hello!
UsuńThank you for your response for each segment do resistance / 48 pieces /? Or just the anodes / 6 pieces /?
At eight microcontroller pins marked by labels from A to G and DT.
UsuńSimilar example: One wire LED display
Thank you very much!
UsuńThat's what I thought. Trying to plan a panel of resistors, but I do not know the eagle. I sprint layout I'm using!
Thank you again!
Louis Greetings.
When you have completed this watch, take a pictures and paste here links to them - we will add them to this article. You may do the same with your schematic :)
UsuńGood luck!
Köszönöm majd úgy teszek!! :)
OdpowiedzUsuńOld polish adage:
UsuńPolak i Węgier dwa bratanki i do szabli i do szklanki!
Lengyel, Magyar – két jó barát, együtt harcol, s issza borát!
Egy pár alkotásom!!! https://www.youtube.com/user/lajos19690207
OdpowiedzUsuńVery nice :)
UsuńLook here: MIDI controller with touch pannel with Arduino
I looked really cool!
OdpowiedzUsuńHello!
OdpowiedzUsuńAll I would ask that the displays are common anode??
The term common-cathode in!
So what now???
Hi!
UsuńThe author made a mistake in the content of the article. I corrected on common anode.
Hello!
OdpowiedzUsuńThank you! When I'm ready I'll send pictures, videos!
We'll be waiting!
Usuń5-4-3-2-1-0 !!!
OdpowiedzUsuńhttps://www.youtube.com/watch?v=rn_SZFrPneI
Hello!
OdpowiedzUsuńalso has its displays. But I can not watch! There is something wrong! Good to hex which is putting together?? Help me if you can!
Thank you, Louis.
Ten komentarz został usunięty przez autora.
OdpowiedzUsuńThe epromot also be programmed.
OdpowiedzUsuńFailed to start! It works perfectly!
OdpowiedzUsuńA small initial difficulties.
Thank you!
In the morning I wrote to the author, to answer your questions. But now I see that everything is OK. :)
UsuńPerfect!
OdpowiedzUsuńhttps://www.youtube.com/watch?v=vn8UyrrGhUI
Possibly a hex that could turn a relay??
ATMEGA8A output is free!
Thank you!
"Possibly a hex that could turn a relay?? "
UsuńSorry, but no.
Ok!
OdpowiedzUsuńThank you!
Such has been!
OdpowiedzUsuńhttps://www.youtube.com/watch?v=R_tpWqKKVqw&list=UUQAJh2tARDxFKUXh6t3tnvQ
Wonderful !!! :-)
UsuńCould you add link to this page into description of your videos?
I do not quite understand what you mean!
UsuńLook into "About" of this movie: http://www.youtube.com/watch?v=Muaa3-Mgbmc
UsuńThere is a link to this page.
Így gondolod? https://www.youtube.com/watch?v=R_tpWqKKVqw&list=UUQAJh2tARDxFKUXh6t3tnvQ
UsuńIt can not be me! (
OdpowiedzUsuńhttps://www.youtube.com/watch?v=R_tpWqKKVqw&list=UUQAJh2tARDxFKUXh6t3tnvQ
OdpowiedzUsuńhttps://www.facebook.com/l.php?u=https%3A%2F%2Fwww.youtube.com%2Fembed%2Fvn8UyrrGhUI&h=mAQGlCUmh
OdpowiedzUsuńHello! I can not put up the link!
OdpowiedzUsuńThe program could not be modified to seconds of the clock go backwards??
Like that! https://www.youtube.com/watch?v=pkIXVboSMro
Thank you, Louis.
Witam, mam problem ze znalezieniem wyświetlaczy segmentowych oraz DS1307. Mógłby ktoś podać zamienniki? Chciałem go ukończyć...
OdpowiedzUsuńIts possible to get a hex file?
OdpowiedzUsuńYes, the HEX file (for 8MHz) is in the ZIP file: Bombowy-zegar-program.zip
UsuńYou can find it in this article.
Ten zegar jest mistrzowski. Przymierzam się do niego już z pół roku tylko materiałów brak... ale w końcu się uda :) Dzięki za inspirację (i dokumentację).
OdpowiedzUsuńCzy któryś z kolegów byłby łaskaw przesłać wzór prasowanki pcb w pdf na maila? dawidsobolewski@o2.pl z góry wielkie dzięki.
OdpowiedzUsuńProszę kolegów, lub szanownego Dondu o przesłanie mi prasowanki pcb czarno białej w formacie pdf i spisu elementów.....jestem cienki z eagle, a projekt strasznie mnie zaciekawił i zrobił bym go.Wielkie dzięki z góry mój adres dawidsobolewski@o2.pl
OdpowiedzUsuńDodałem do artykułu w części "Do pobrania".
UsuńA co trzeba by zmienić w kodzie aby dodać obsługę jeszcze jednego czujnika temp? Powiedzmy że byłyby te czujniki przełączane za pomocą dodatkowego przycisku na pinach PC1 lub PC2.
OdpowiedzUsuńZ góry dziękuję za odpowiedź :)
Mam problem z czujnikiem temperatury. Wskazuje 58*, myślałem, że to wada DS1820 więc kupiłem nowy ale z kolejnym jest to samo. Wszystko działa jak należy poza termometrem. Jakieś pomysły?
OdpowiedzUsuńwitam czy kolega rozwiązał problem z termometrem mam to samo losowo pokazuje 13, 60,70st???
OdpowiedzUsuńJuż wiem czujnik nie może być ds 18b20 tylko ds 1820!! i jest ok.
OdpowiedzUsuńDziwne bo miałem i 1820 i 18b20 i w obu to samo.
UsuńKoledzy podpowiedzcie, bo wykonałem ten zegar ale nie mam pod ręką DS1307 i po włączeniu bez niego nic nie ma na wyświetlaczach, jakby nic nie działało. Czy tak ma być może ktoś sprawdzić u siebie ?
OdpowiedzUsuńNie analizowałem programu, ale DS1307 jest podstawą tego projektu, więc musiałbyś zapewne usunąć nieco kodu, który być może przeszkadza przy braku DS1307.
UsuńDzięki Dondu, dziś doszedł do mnie DS1307 i po włożeniu w podstawkę wszystko ruszyło.
UsuńWitam,
Usuńzrobiłem ten zegarek,wydaje mi się że wszystkie funkcje działają tylko stoi dwukropek nie miga, zmieniałem Mega8, Ds1307, kwarc, nic nie pomaga, co to może być?, proszę o poradę
Pozdrawiam
Załóż może temat na forum a tutaj wklej link, byśmy do niego trafili.
UsuńWitam.
OdpowiedzUsuńPatrząc na zaprojektowaną płytkę zastanawiam się czy gniazdo isp a konkretnie nóżka vcc i mosi są ze sobą zwarte? nie znam sie na tym tak dobrze dlatego pytam. i kolejne pytanie jesli nie są zwarte to jak dostarczamy napięcie przez programator do mikroprocesora? którą ścieżką? czy podczas programowania musi byc w takim razie uklad zasiloniony z innego zrodla niz z programatora?
Witam. Autor projektu założył, że w czasie programowania układ jest zasilany z własnego źródła napięcia. Dlatego też nie podłączył pinu Vcc w gnieździe ISP. Mógł oczywiście to zrobić, bo większość programatorów ma możliwość rozłączenia napięcia zasilania (nie powinno się łączyć równolegle dwóch różnych źródeł zasilania, bo nigdy nie mają dokładnie tego samego napięcia), ale ponieważ jest mu to zbędne i dodatkowo upraszcza płytkę PCB, to za pewne dlatego nie wykonał takiego połączenia.
UsuńWitam.
OdpowiedzUsuńNapotkałem się z problemem, mianowicie nie działają mi jednocześnie 2 wyświetlacze 7-segmentowe z 3. Nie jestem w stanie określić co może być przyczyną... Proszę o pomoc. Link do zdjęcia : http://pokazywarka.pl/vcwbl7/
Wstępnie wygląda to na problemy z montażem lub uszkodzenia ścieżek na PCB.
Usuńtylko, że na dwóch wyświetlaczach nie ma nic a na srodkowym normalnie pokazuje. takze sciezki mysle ze sa ok. co ew. moze byc jeszcze przyczna?
UsuńDyskusję kontynuujemy na forum: tutaj
UsuńSzkoda, że ustawianie budzika wiąże się z ustawianiem zegara! za każdym razem zmiana czasu budzenia=ustawianie czasu zegara=zmęczenie i znudzenie. Bardzo fajny projekt, ale z dwoma wadami: Pierwsza opisana wyżej, a teraz druga. Nieznaczne nagrzewanie się Atmega powoduje podniesienie odczytywanej temperatury.
OdpowiedzUsuńGaspar.
Przecież masz kod źródłowy programu - popraw go wg własnego uznania. W czym problem? Układ w zamkniętej obudowie istotnie może nieznacznie wpływać na odczyt temperatury - dodaj wentylację, lub odsuń na PCB DS1820 i problem rozwiązany.
UsuńDoes anybody contact the author of the project?
OdpowiedzUsuńUnfortunately not.
UsuńMoże komuś pomoże:
OdpowiedzUsuńZbudowałem ten projekt, zamiast 74HC164 zastosowałem 74HC595 bo akurat miałem pod ręką, dodałem rezystory do segmentów. Działał pięknie, dopóki nie zmieniłem zasilacza laboratoryjnego na jakiś chiński, który zamiast 5V dawał 6.5V i zegarek się zawiesił. Myślałem że spaliło któryś scalak, ale zarówno Atmega jak i RTC są w porządku, ale z jakiegoś powodu scalak nie generował sygnału 1Hz. Gdy sterowałem nim (DS1307) z Arduino, poprawnie wystawiał prostokąt, a w Atmedze nie chciał.
Doszedłem do tego, że w rtcInit() wywoływana jest funkcja checkReset() sprawdza najstarszy bit odczytany z adresu 0x00 (adres sekund) i na jego podstawie decyduje o resecie. Reset polega na tym, że odczytuje na sztywno godzinę i zapisuje ją ponownie w to samo miejsce. Jednak w adresie sekund dalej zapisywana jest najstarsza jedynka, a więc wracamy do punktu wyjścia. Należy w funkcji rtcSetBCDTime() poprawić i do zapisu sekund dołożyć maskę & 0x7F.
Czy jest ktoś w stanie napisać program tak aby działał z czujnikiem DS18B20.Ciężko kupić obecnie wersje 1820
OdpowiedzUsuń