Table of Contents
Zamek szyfrowy z tarczą telefoniczną
Projekt ten ma na celu pokazanie jak powinno się realizować mały projekt.
Założenia
Bardzo ważna jeżeli nie najważniejsza część, zauważyłem że bardzo często wręcz pomijana przez kolegów. Jak można pracować nie wiedząc o się chce zrobić? W założeniach powinny się znaleźć:
- Cel
- Wymagania
- Dostępne środki (koszty i czas)
Wypisując założenia warto zaczynać od najważniejszych zagadnień. Planując projekt trzeba pamiętać o własnych umiejętnościach, dostępnych narzędziach i zasobach. Urządzenie które chcemy zrealizować musi mieć wstępną specyfikację, im dokładniejsza tym lepiej. Budując urządzenie należy przestrzegać technicznej zasady: jak najlepiej jak najmniejszym kosztem (optymalnie).
W moim przypadku założenia wyglądają tak: Projekt zamka szyfrowego
- Chcę zrealizować zamek szyfrowy
- Wprowadzanie kombinacji jest realizowane za pomocą tarczy telefonicznej
- Urządzenie powinno posiadać wyświetlacz
- Koszty: żadne: wykorzystanie dostępnych części, budowa na płytce prototypowej
- Czas wykonania: 2 dni
- Funkcja edukacyjna
Projektowanie
Kiedy już wiadomo co się chce zrobić trzeba zastanowić się jak to zrobić.
Wyświetlacz
Informacja o stanie w jakim znajduje się urządzenie będzie przekazywana za pomocą prostego wyświetlacza 7 segmentowego. Ponieważ będzie to zamek zastosuje również dwukolorową diodę (czerwony/zielony). Pierwszym krokiem jest wysterowanie wyświetlaczem. Ponieważ sterowanie takim wyświetlaczem jest bardzo proste, wyprowadzenia odpowiednich segmentów podłączyłem w sposób zupełnie losowy do portu C. Następnie na mikrokontrolerze uruchomiłem prosty program ustawiający po kolei wszystkie piny portu C co 1 sekundę. Na papierze narysowałem schemat wyświetlacza i obliczyłem kombinacje liczb dla wszystkich potrzebnych znaków ( 0,1,2,3,4,5,6,7,8,9, ,-,=, <animacja obrotu>).
// Tablica znaków 7 seg const char chartab[] = {252,144,234,186,150, 62,126,152,254,190, 0, 2,114, 8,128+8,16+8+128,32+8+16+128,64+32+8+16+128,4+64+32+8+16+128};
W ostatniej linijce tablicy widać jak zostały utworzone kolejne znaki: poprzez dodawanie liczb odpowiadających konkretnym segmentom. Powyższy kod jest poprawny tylko dla pewnej nieokreślonej konfiguracji połączeń, każdy musi stożyć tablicę odpowiadającą jego połączeniom. Uprościłem również sposób ograniczania prądu diod w wyświetlaczu, rezystory ograniczające prąd są wpięte pomiędzy katody a masę. Rozwiązanie takie powoduje zmianę jasności segmentów w zależności od tego ile ich jest używanych równocześnie.
Tarcza
Tarcze telefoniczną dostałem od kolegi. Posiada ona 3 wyprowadzenia. Po odkręceniu tylnej obudowy ukazał się skomplikowany układ mechaniczny. Jeden z przewodów był przewodem wspólnym dla 2 styków. Tarcza generuje 2 sygnały:
- Impulsy wybieranego numeru
- Ciągły sygnał aktywny w momencie obracania się tarczy
Początkowo planowałem wykorzystanie obu sygnałów, jednak zrezygnowałem z drogiego ponieważ same impulsy wystarczą do poprawnego odczytu stanu tarczy. Elektrycznie przewód wspólny jest podłączony do dodatniej szyny zasilania natomiast przewód sygnału impulsów jest podciągnięty pod masę przez rezystor 10k. Zastosowałem również kondensator 100nF podłączony pomiędzy linią sygnału a masą w celu debouncingu styków tarczy.
Stany
Następnym krokiem jest zaprojektowanie zachowania programu, rozrysowałem stany w jakich może się znajdować program.
Urządzenie po resecie i inicjalizacji peryferiów wchodzi do punktu Reset gdzie są zerowane wszystkie znaczące zmienne, wyświetlacz i dioda. Następnie urządzenie może znajdować się w stanach:
- Idle: stan bezczynności - na wyświetlaczu miga znak zachęty.
- Rotating - występuje w momencie obracania się tarczy, na wyświetlaczu pojawia się zliczana liczba
- Locked - po zliczeniu implulsów liczba zostaje wczytana
- Ridle - bezczynność ale z oczekiwaniem na kolejne cyfry kombinacji
- Locked pattern - wszystkie cyfry kombinacji zostały wprowadzone
- Fail - cyfry kombinacji nie zgadzają się z zaszytym wzorem
- Success - cyfry kombinacji zgadzają się z wzorem
Schemat pozwala zaplanować logiczne przejścia i możliwe stany urządzenia.
Schemat ideowy
Na schemacie nie zaznaczyłem kondensatorów na szynie zasilania.
Program
#include "avr/interrupt.h" #include "util/delay.h" #include "avr/io.h" //PORTY #define DISPP PORTC #define DISPD DDRC #define LED_DIR DDRD #define LED PORTD #define forever for(;;) // Tablica znaków 7 seg const char chartab[] = { 252, 144, 234, 186, 150, 62, 126, 152, 254, 190, 0, 2, 114, 8, 128 + 8, 16 + 8 + 128, 32 + 8 + 16 + 128, 64 + 32 + 8 + 16 + 128, 4 + 64 + 32 + 8 + 16 + 128 }; //STAN enum STATUS { st_reset, st_idle, st_ridle, st_rotating, st_locked, st_lockpattern, st_fail, st_success }; volatile enum STATUS status = st_reset; //GLOBALS const uint8_t codetab[6] = { 1, 2, 3, 4, 5, 6 }; volatile uint8_t counter = 0; uint8_t numpos = 0; uint8_t numtab[6]; //TIMER volatile uint8_t divider = 0; volatile uint8_t blinker = 0; volatile uint8_t timeout = 0; //Przerwanie timera ISR(TIMER1_COMPA_vect) { if (divider++ == 10) { blinker ^= 1; divider = 0; } if (timeout < 0xFF) timeout++; } //INT //Przerwanie zewnętrzne INT1 ISR (SIG_INTERRUPT1) { //Tylko w tych 3 stanach sygnał z tarczy jest oczekiwany if ((status == st_rotating) || (status == st_idle) || (status == st_ridle)) { status = st_rotating; timeout = 0; // resetujemy counter++; // zliczamy impulsy } } int main() { int i; //IO Settings LED_DIR = (1 << 5) | (1 << 6); LED = 0; DISPD = 0xFF; //Ustawiamy timer TCCR1B |= (1 << WGM12); // Timer1 w trybie CTC TIMSK |= (1 << OCIE1A); // Włączam przerwanie CTC OCR1A = 868; // Ustawiam wartość do porównania TCCR1B |= ((1 << CS10) | (1 << CS12)); // Ustawiam preskaler Fcpu/1024 //Tylko zbocze rosnące aktywuje INT1 MCUCR |= (1 << ISC11) | (1 << ISC10); //Aktywacja przerwań sei(); //Main loop forever { switch (status) { case st_reset: //kasujemy timeout = 0; counter = 0; numpos = 0; LED &= ~((1 << 5) | (1 << 6)); //aktywuje przerwanie INT1 GICR |= (1 << INT1); status = st_idle; break; case st_idle: //nic sie nie dzieje //prompt DISPP = chartab[11 - ((blinker ? 1 : 0))]; //tutaj można dopisać jakis kod do uspienia MCU dla oszczednosci energii // break; case st_rotating: // impulsy sa teraz zliczane przez przerwanie //wyswietlam liczbe (modulo bo zawijam do 0) DISPP = chartab[counter % 10]; //nowych impulsow dawno nie bylo wiec numer jest wczytywany if (timeout > 10) status = st_locked; break; case st_locked: //numer wpisany numtab[numpos++] = counter; counter = 0; timeout = 0; //keeping number for a while _delay_ms(150); //Czy to juz wszystkie cyfry? if (numpos >= 6) status = st_lockpattern; else status = st_ridle; break; case st_ridle: //oczekiwanie na kolejna liczbe //Po pewnym czasie wyswietlacz zaczyna migać sygnalizujac uplywajacy czas if ((timeout > 50) && (divider % 5)) LED |= (1 << 5); else LED &= ~(1 << 5); //Wyswietlam ile cyfr wpisano DISPP = chartab[12 + numpos]; //jezeli za dlugo nie wpisujemy kodu to resetujemy wszystko if (timeout > 100) status = st_reset; break; case st_lockpattern: //kod wpisany //czyszcze wyswietlacz blokuje INT GICR &= ~(1 << INT1); _delay_ms(150); status = st_success; //sprawdzam for (i = 0; i < 6; i++) if (numtab[i] != codetab[i]) { status = st_fail; break; } break; DISPP = 0; case st_fail: //porazka //migam szybko czerwona dioda if (divider % 5) LED |= (1 << 5); else LED &= ~(1 << 5); if (timeout > 50) status = st_reset; break; case st_success: //sukces //migam zielona dioda if (blinker == 0) LED |= (1 << 6); else LED &= ~(1 << 6); //tutaj mozna zalaczyc jakis przekaznik otwierajacy np drzwi // //otwarcie zamka trwa tylko chwile if (timeout > 100) status = st_reset; break; } } }
Upgrade
W projekcie można by zmienić kilka rzeczy. Po pierwsze zamek nie otwiera niczego, w kodzie jest komentarz w którym momencie można włączyć np. przekaźnik. Ponieważ zawieszenie się programu (które może zostać spowodowane np. zakłóceniami) spowodowałoby odcięcie możliwości otwarcia zatrzasku sterowanego przez ten zamek szyfrowy dobrym pomysłem byłoby zaimplementowanie watchdoga. Porogram nie jest optymalny, niektóre zmienne można zupełnie wyeliminować (czy zauważysz które?).