Teoria już za nami - czas na kodzik!
Na dobry początek rozważmy rozwiązanie wykorzystujące mechanizm przerwań i timer. Sygnał tacho podepniemy pod zewnętrzne przerwanie INT2 (PD3 w mojej Atmedze), a timer skonfigurujemy tak, by generował przerwanie co 1 sekundę. W obsłudze przerwania INT2 będziemy zliczać impulsy, a co sekundę, dzięki timerowi policzymy, ile obrotów wykonał nasz wentylator. Wiemy, że na pełen obrót składają się 2 impulsy, dlatego sumę impulsów podzielimy przez 2.
Żeby mieć jako takie pojęcie o tym, co się dzieje na pokładzie mikrokontrolera, wyślemy ilość obrotów na minutę USARTem przez bluetooth'a wprost do aplikacji terminala odpalonej na telefonie z Androidem. Informację o prędkości będziemy wysyłać również co sekundę. Skąd będziemy wiedzieć kiedy dokładnie? Ustawimy flagę send w obsłudze przerwania!
Do dzieła!
Pokazywanie postów oznaczonych etykietą przerwania. Pokaż wszystkie posty
Pokazywanie postów oznaczonych etykietą przerwania. Pokaż wszystkie posty
piątek, 6 maja 2016
środa, 4 maja 2016
Fun with Fan part 3
Gospodarowanie dostępnymi zasobami
Trzeba dążyć do tego, by wykorzystać jak najwięcej z tego, co mają nam do zaoferowania producenci sprzętu, który wpadnie nam w ręce. Maglowany przeze mnie wiatraczek a.k.a. wentylator oprócz dwóch linii zasilających (czerwonej + i czarnej -) posiada jeszcze linię oznaczoną kolorem żółtym. Szybki rzut oka w Internety wystarcza, by dowiedzieć się, że jest to wyjście tacho. Linia ta jest częścią sprzężenia zwrotnego dla naszego proca - zwiększając napięcie zasilania spodziewa się, że wentylator będzie kręcił się coraz szybciej. W myśl zasady - kontrola najwyższą formą zaufania - za pomocą żółtej linii jest w stanie określić prędkość, z jaką obraca się wirnik wentylatora i w zależności od sytuacji odpalać takie albo inne eventy. Np. gdy wie, że prędkość jest niska, a temperatura wzrosła - może podnieść nieco napięcie zasilania - spodziewając się wzrostu prędkości obrotowej. Gdy ten wzrost nie nastąpi - nasz procesor może zacząć bić na alarm - bo coś złego dzieje się z tak ważnym elementem każdego komputera - chłodzeniem (PC zaczyna piszczeć tym swoim głośniczkiem...).Pomysł na mikro-projekt
Co jako było nie było programista Atmegi mogę zrobić z tym fantem? Ano podpiąć dodatkową linię do mikrokontrolera i wykorzystać jakoś te informacje. Co mnie interesuje? Interesuje mnie stan tej linii w czasie. Pierwsza myśl - podłączę tacho do dowolnej nóżki mikrokontrolera i będę sobie sprawdzał jej stan. Dobre, ale są lepsze rozwiązania. Przerwanie! Przecież mogę podpiąć ten sygnał pod przerwanie - główna pętla programu już nie będzie musiała sprawdzać stanu nóżki. OK - do tego jeszcze potrzebny nam jest jakiś pomiar czasu. Z żółtej linii odczytamy ilość wykonanych przez wirnik obrotów - do zmierzenia prędkości potrzebny będzie też czas. Zapniemy jakiś zegarek? Stoper? Blisko - wykorzystamy Timer! Lepiej! Wykorzystamy mechanizm przechwytywania sygnału wejściowego (Input Capture Unit).
Zejdźmy na poziom sygnałów
Przechwytywany sygnał musimy podpiąć do jednego z dwóch (w przypadku Atmegi 162) wejść ICP. Dla niepoznaki są to ICP1 na pinie PE0 i ICP3 na pinie PD3. Pin PD3 to także wejście zewnętrznego przerwania INT1 - warto o tym pamiętać wybierając miejsce podłączenia sygnału tacho - chcąc wypróbować podejście z przerwaniem warto skorzystać z pinu PD3 - nie trzeba będzie zmieniać konfiguracji połączenia przy zmianie podejścia programowego - chcąc zaoszczędzić dobrą linię przerwania - warto wybrać port E.
Nie warto ryzykować - wentylator działa z napięciem 12V - Atmega 5V - sięgamy po transoptor!
Input Capture Unit
Jak zwykle w przypadku obcowania z zewnętrznymi sygnałami, musimy określić, na jaki stan nasza potężna jednostka ICU ma reagować. W momencie, gdy na wejściu ICPn pojawi się oczekiwany przez nas stan, nastąpi przechwycenie! W momencie przechwycenia do rejestru ICRn jest wpisywana aktualna wartość rejestru TCNTn wykorzystywanego licznika (krótko mówiąc timestamp). Warto tak skonfigurować mikrokontroler, by w tym momencie wystąpiło przerwanie - przecież musimy na bieżąco reagować! Mało tego - musimy reagować szybko, bo przy następnym oczekiwanym stanie logicznym interesujące nas rejestry zostaną nadpisane, a cenne dane utracone. W tym miejscu warto jeszcze wspomnieć o istnieniu mechanizmu redukowania szumów (Noise Canceler), który może usprawnić nasze pomiary, gdy na linii występują zakłócenia.
I z tą dozą teorii zostawiam Cię Czytelniku, w niedalekiej przyszłości pojawi się adekwatny kodzik!
piątek, 29 kwietnia 2016
Fun with Fan part 2
Hardware poskładany, czas przysporzyć trochę problemów software'owcom.
Nasz program powinien przyspieszyć obroty wiatraczka w momencie, gdy temperatura wzrośnie ponad zdefiniowane maksimum i wrócić do jałowych obrotów w momencie, gdy spadnie poniżej założonego minimum. Proste. Zatem do dzieła.
Zdefiniujmy nasze wartości graniczne:
Oraz zdefiniujmy graniczne obroty wentylatora. Operujemy na 8-bitowym timerze w ramach naszego PWM, zatem maksymalną prędkość określimy na 255, minimalną na 80 - wartość dobrana eksperymentalnie - możnaby się też pokusić o wyliczenia... 80 jest spoko.
Teraz konfiguracja PWM i termometru (podłączamy go jak ostatnio). Włączamy przerwania.
W nieskończonej pętli będziemy odczytywać temperaturę, modyfikować prędkość wentylatora, a następnie odpowiadającą jej wartość wpisywać do rejestru OCR0. Skomplikowane działanie odejmowania wymuszone jest konfiguracją PWM (clear on match). Odwracając logikę konfiguracji możemy pozbyć się odejmowania. Zostawiam tak, jako przykład akademicki ;)
I tyle - teraz tylko testować, dobierać parametry i na produkcję!
PS kodzik jak zwykle na githubie.
Nasz program powinien przyspieszyć obroty wiatraczka w momencie, gdy temperatura wzrośnie ponad zdefiniowane maksimum i wrócić do jałowych obrotów w momencie, gdy spadnie poniżej założonego minimum. Proste. Zatem do dzieła.
Zdefiniujmy nasze wartości graniczne:
Oraz zdefiniujmy graniczne obroty wentylatora. Operujemy na 8-bitowym timerze w ramach naszego PWM, zatem maksymalną prędkość określimy na 255, minimalną na 80 - wartość dobrana eksperymentalnie - możnaby się też pokusić o wyliczenia... 80 jest spoko.
Teraz konfiguracja PWM i termometru (podłączamy go jak ostatnio). Włączamy przerwania.
W nieskończonej pętli będziemy odczytywać temperaturę, modyfikować prędkość wentylatora, a następnie odpowiadającą jej wartość wpisywać do rejestru OCR0. Skomplikowane działanie odejmowania wymuszone jest konfiguracją PWM (clear on match). Odwracając logikę konfiguracji możemy pozbyć się odejmowania. Zostawiam tak, jako przykład akademicki ;)
I tyle - teraz tylko testować, dobierać parametry i na produkcję!
PS kodzik jak zwykle na githubie.
czwartek, 21 kwietnia 2016
Wyślij SMS o treści...
Wyrównujemy szanse
Dodajmy kolejną możliwość do naszej komunikacyjnej piaskownicy. Za pośrednictwem modułu jesteśmy w stanie przekazać wiadomość SMS do Atmegi. Wyrównajmy szanse - niech teraz Atmega będzie w stanie wysłać SMSa do nas.
W tym celu musimy skonfigurować połączenie z modułem GSM jak poprzednio. Uruchomić moduł, odczekać 15 sekund potrzebne na zalogowanie się do sieci i wysłać - niespodzianka - sekwencję komend AT, które sprawią, że przez sieć GSM poleci SMS od Atmegi wprost do telefonu o wskazanym przez nas numerze telefonu - najlepiej, żeby był to nasz telefon - przynajmniej będziemy wiedzieć, że działa, a karta SIM włożona do modułu GSM nie posłuży nam do wysyłania niechcianych wiadomości do losowych ludzi...
Procedura wysyłania SMSa
Ustalamy tryb tekstowyAT+CMGF=1Po otrzymaniu informacji
OKPodajemy numer telefonu odbiorcy
AT+CMGS=000000000 lub AT+CMGS=+48000000000
Następnie oczekujemy na znak zachęty:
>Zachęceni podajemy treść SMSa:
Daj sie poznac!Koniec treści SMSa komunikujemy kombinacją Ctrl+Z... czyli za pomocą kombinacji:
\x1AMożemy jeszcze zaczekać na potwierdzenie nadania wiadomości
+CMGS: ...Po otrzymaniu tak rozpoczynającego się ciągu znaków, możemy uznać, że SMS został wysłany - nasz trud skończony.
W optymistycznym przypadku...
...nie będziemy musieli się martwić o nieprzewidziane sytuacje. Jednak warto być świadomym, że każda nasza komenda może zostać niezrozumiana przez moduł GSM, o czym w najlepszym przypadku poinformuje nas pięknym komunikatem ERROR, który przyjdzie do nas zamiast oczekiwanej wiadomości pozytywnej. Dlatego komunikując się z modułem GSM za pomocą komend AT warto określać stan aplikacji - będzie o wiele łatwiej zarządzać kodzikiem i decydować, co w danym momencie powinniśmy zrobić - będziemy mieć informację na temat stanu, w jakim się aktualnie znajdujemy i wiadomości, jaka aktualnie przyszła - sporo wiedzy - warto ją spożytkować w należyty sposób - nie zawsze mamy aż tyle informacji o stanie systemu...Kodzik
Aby wysłać SMS o treści podanej w kodzie programu powinniśmy wprowadzić aplikację w stan "GSM_Init". W celu odebrania SMSa od modułu GSM, wprowadzamy aplikację w stan "SMS_ReadInit". Zmiana stanu aplikacji może nastąpić na przykład pod wpływem odczytu określonej temperatury (jeżeli podepniemy do układu termometr) lub pod wpływem przerwania wywołanego przez zwarcie lub rozwarcie zewnętrznego układu - kiedy dokładnie - zostawiam Tobie!
czwartek, 14 kwietnia 2016
- SMS Przyszedł! - Czego chce?
Połączenie
Uruchomienie modułu to dopiero początek. Teraz musimy się z nim dogadać. Na chwilę obecną Fibocom G510 jest połączony z mikrokontrolerem jedynie linią POWER_ON. Do pełni szczęścia potrzebne nam jest jeszcze połączenie USART - 2 kolejne linie. Zatem standardowo - łączymy linię RxD modułu z linią TxD mikrokontrolera, a linię TxD modułu z linią RxD mikrokontrolera. Jak pamiętamy z posta o USART, potrzebna jest też znajomość parametrów transmisji. Domyślnie port UART1 modułu G510 skonfigurowany jest następująco:- 8 data bits
- 1 stop bit
- no parity
- none flow control
- auto baud rate detect
Ta ostatnia opcja jest o tyle ciekawa, że to my możemy zdecydować, z jaką prędkością odbywać się będzie nasza komunikacja. Osobiście wybrałem 115200 bps. Radośnie zakładam, że karta SIM jest już na swoim miejscu, a moduł znajduje się tam, gdzie jest zasięg sieci, do której będzie próbował się logować.
Wait a minute...
Połączenie utworzone, czas włączyć moduł i wyciągnąć z niego interesujące nas informacje. Na dzień dobry musimy uzbroić się w ok. 15 sekund cierpliwości - w tym czasie moduł powinien zalogować się do sieci. Sukces tego procesu zostanie oznajmiony poprzez zmianę częstotliwości migania diody podpiętej do linii LPG. To opóźnienie nie jest wymagane w momencie odczytu SMSów obecnych na karcie SIM, aczkolwiek myślę, że warto o nim wspomnieć - choćby po to, by zapamiętać na przyszłość - kiedy to zasięg sieci będzie krokiem wymaganym (choćby po to, by odebrać tego pierwszego SMSa :))
Porozmawiajmy!
G510 komunikuje się z nami za pośrednictwem komend AT. W celu sprawdzenia komunikacji, powinniśmy wysłać do modułu proste "AT" i oczekiwać czegoś w rodzaju "OK" lub "AT OK" - w zależności, czy moduł ma włączone echo, czy nie. Echo może się przydać do kontroli wysyłanych wiadomości - tj. przetwarzając odpowiedź, najpierw sprawdzamy, czy zapytanie, na które odpowiada to to samo, na które oczekujemy. Oprócz tego w ten sposób można sprawdzić poprawność konfiguracji połączenia. Przy pomocy komend AT opisanych w odrębnym dokumencie możemy dowiedzieć się wielu ciekawych rzeczy o stanie modułu. Skupmy się jednak na tych, które pozwolą nam "odebrać SMSa".
Odczyt SMSa z karty SIM
Na początek ustawmy format wiadomości - interesuje nas tekst. zatem:
AT+CMGF=1
oczekujemy odpowiedzi
OK
Następnie wskazujemy, skąd chcemy odczytać SMSa
AT+CPMS="SM"
w odpowiedzi uzyskamy informację zaczynającą się od
+CPMS ...
Teraz właściwe żądanie odczytu SMSa. Do tego celu potrzebujemy id interesującej nas wiadomości. Załóżmy, że przed nadejściem naszego SMSa karta była pusta, zatem chcąc odczytać pierwszą wiadomość:
AT+CMGR=1 lub AT+MMGR=1
Różnica między tymi komendami jest taka, że ta pierwsza zmienia status wiadomości na karcie - tj. ze statusu "REC UNREAD" (otrzymana nieprzeczytana) na status "REC READ" (otrzymana przeczytana). W odpowiedzi otrzymamy strukturę zawierającą informację na temat otrzymanego SMSa (w zależności od parametru ustawionego komendą AT+CSDH) - m. in. numer, z jakiego został wysłany, czas otrzymania i w końcu - treść SMSa.
+CMGR ...
Do nas należy decyzja, co zrobimy z otrzymanymi danymi. Wypadałoby wyłuskać treść SMSa. Co może nieść ze sobą ta treść? Na przykład informację o tym, że powinniśmy włączyć którąś diodę podpiętą do mikrokontrolera albo załączyć przekaźnik, który załączy nam ogrzewanie w domu albo... co tylko sobie wymyślisz :)
Chcąc wykorzystać bardziej informacje zawarte w komendzie zwrotnej +CMGR - powinniśmy pokusić się o utworzenie struktury danych, która przechowa nam to, co dla nas ważne.
Po odczytaniu SMSa możemy go usunąć z karty SIM komendą:
AT+CMGD=1
Która usunie nam wiadomość o identyfikatorze 1 (czyli tą, którą przed chwilą odczytaliśmy). Spodziewamy się odpowiedzi:
OK
Usuwanie SMSów zaraz po ich przetworzeniu odejmuje nam jeden problem - karta SIM nie zapełni się zbyt szybko i nasz układ będzie "responsywny". Od konkretnego problemu zależy, czy będziemy chcieli trzymać SMSy na karcie SIM i jak długo... Jednakowoż uważam, że istnieją lepsze sposoby archiwizacji danych, niż trzymanie ich na karcie SIM.
środa, 6 kwietnia 2016
Ważna linia cd.(DS1822-PAR)
Po co mi ta wiedza?
Swego czasu stałem się szczęśliwym posiadaczem czujnika temperatury DS1822-PAR. Czujnik ten jest o tyle ciekawy, że wg noty katalogowej posiada tylko 2 nóżki, do których możemy cokolwiek podpiąć: nóżkę danych (1-Wire) i nóżkę masy (GND). A zasilanie?! No właśnie - i tu jest moc, siła i potęga tego czujnika. Końcówka PAR pochodzi od angielskiego parasite (pasożyt) i nie jest to wcale przesadzone - czujnik czerpie zasilanie z linii danych - wiedzie na niej pasożytnicze życie - jak jemioła na drzewach na przykład...Co to oznacza dla konstruktora-programisty?
Nie mniej - nie więcej tyle, że trzeba to zasilanie czujnikowi dostarczyć - w przeciwnym wypadku, niczego ciekawego się od niego nie dowiemy - a zwłaszcza, jaka temperatura panuje w naszym otoczeniu. To od czego zacząć? Jak zwykle - od czytania dokumentacji.![]() |
| Sposób podpięcia czujnika do mikrokontrolera (źródło: datasheet) |
Najlepszą dokumentacją jest kodzik!
Podłączyliśmy termometr do linii danych, czas te dane z niego wyciągnąć! Bit po bicie... Nasz mikrokontroler będzie działał jako master w tym tandemie, w związku z tym potrzebna mu będzie wiedza, jak wysłać sygnał reset na magistralę:
Kolejnym krokiem - odebranie pojedynczego bitu z magistrali:
Zatem chcąc odebrać bajt:
Jak już potrafimy odczytać, nauczmy się wysyłać: I znowu - bajt:
Na wykresie zaznaczone są pomiary temperatury odnotowane podczas "złapania" czujnika DS1822-PAR w 2 palce (między kciuk a wskazujący). Czujnik złapałem w 21 sekundzie eksperymentu, puściłem w 49. Z wykresu wynika, że czujnik szybciej reaguje na wzrost temperatury niż na jej spadek - posiada pewną bezwładność.
Całość dostępna oczywiście na githubie.
Zatem chcąc odebrać bajt:
Jak już potrafimy odczytać, nauczmy się wysyłać: I znowu - bajt:
To jaka jest temperatura otoczenia?
Teraz wystarczy tylko poprosić termometr o podanie temperatury. W tym celu będziemy musieli wysłać mu kilka komend - 0xCC (skip ROM), 0x44 (convert T) oraz 0xBE (read Scratchpad). Szczegółowy opis znajdziecie w dokumentacji. Tam też znajdziecie tabelki z tajemną wiedzą na temat kolejności wysyłania poszczególnych komend. A jak to będzie wyglądać w naszym przypadku? Dane pochodzące z termometru pochodzą z 'brudnopisu' (scratchpad) czujnika. Temperatura zapisana jest na 2 bajtach. W celu wyliczenia prawidłowej wartości, musimy dokonać małej arytmetyki na tych wartościach - ponownie posługując się wiedzą z dokumentacji.![]() |
| Sposób przechowywania temperatury przez czujnik. Źródło: datasheet. |
Zatem co musimy zrobić? Wziąć MS Byte, przesunąć go bitowo o 8 miejsc w prawo, zapisać do 16 bitowej zmiennej, do tego dopisać LS Byte i podzielić całość przez 16 (ujemne potęgi 2 na najmłodszych bitach LS Byte -> 2^4 = 16).
I po co to wszystko?
A z otrzymaną wartością temperatury możemy zrobić, co tylko chcemy - na przykład wysłać ją za pośrednictwem USART do innego urządzenia, a uzyskane dane przedstawić na wykresie:czwartek, 31 marca 2016
Czy w moim AVR jest miejsce na czasownik?
W jakim stanie się znajduje?
Program napisany na mikrokontrolerze wykonuje się w pętli nieskończonej. Podczas jednego przebiegu pętli możemy wyróżnić różne stany, w jakich znajduje się nasza aplikacja. Myśląc o kodziku jako zbiorze stanów, będzie nam łatwiej zarządzać i rozwijać to, co nam głowa do Atmel Studio przyniesie. Weźmy na warsztat jeden z ostatnich kawałków kodu...
Logika biznesowa - Red!
Skomplikujmy nieco ten przykład. Załóżmy, że pewne urządzenie podpięte do naszej Atmegi za pośrednictwem USART powinno zmieniać stan naszych diod. Wiemy jak skonfigurować komunikację. Skąd będziemy wiedzieć, kiedy ma się zmienić stan? Ustalmy protokół: 'A' będzie załączać diodę przy pinie PB0, 'a' - wyłączać. Analogicznie dla diody podpiętej do pinu PB1 'B' ją załączy, a 'b' wyłączy.Najprostszy pomysł - Green!
Jak mogłaby wyglądać obsługa przerwania USART0_RXC_vect? Na przykład tak:ISR(USART0_RXC_vect)
{
char temp;
temp = UDR0;
if (temp == 'a')
{
PORTB |= (1 << PB0);
}
else if (temp == 'A')
{
PORTB &= ~(1 << PB0);
}
else if (temp == 'b')
{
PORTB |= (1 << PB1);
}
else if (temp == 'B')
{
PORTB &= ~(1 << PB1);
}
}
Działa? Wyśmienicie! Wygląda? Byle jak. Poza tym możemy tu wpaść w pułapkę 'rozwleczenia' przerwania. Jego czas wykonania może stać się niebezpiecznie długie i negatywnie wpłynąć na główny kod programu. Mało tego - traci nam się informacja, co właściwie oznaczały te literki? Pomijam już fakt, że można dać tu switcha.
Zastanówmy się, co powinno wydarzyć się w obsłudze przerwania, na jakiej informacji nam zależy?
Chcemy się dowiedzieć, jaką informację przekazało nam urządzenie podłączone przez USART. Czy pozostałą część kodu interesuje znak, jaki się tu pojawił? Nie. Ważna jest akcja, jaka powinna zostać wykonana po otrzymaniu konkretnego znaku. Przetłumaczmy ją zatem na coś bardziej zrozumiałego.
Refactor!
ISR(USART0_RXC_vect)
{
char messsage;
messsage = UDR0;
switch (messsage)
{
case TurnLed0Off:
state = Led0Off;
break;
case TurnLed0On:
state = Led0On;
break;
case TurnLed1Off:
state = Led1Off;
break;
case TurnLed1On:
state = Led1On;
break;
default:
state = Idle;
break;
}
}
Co się stało?
Nazwaliśmy to, co właściwie wpada do naszego systemu, a oprócz tego wprowadziliśmy zmienną state, do której przypisujemy jakieś wartości. Określają one, w jakim stanie ma się znaleźć mikrokontroler - a dokładniej, jaką akcję powinien wykonać w momencie otrzymania konkretnej wiadomości. Skąd wziąć te wartości? Użyjmy enuma! I umieśćmy gdzieś na początku kodu.typedef enum
{
Idle = 0,
Led0On,
Led0Off,
Led1On,
Led1Off
} States;
typedef enum
{
TurnLed0On = 'A',
TurnLed0Off = 'a',
TurnLed1On = 'B',
TurnLed1Off = 'b'
} Messages;
Jeszcze tylko zmienna state widoczna w obsłudze przerwania...
volatile States state;
...którą na dzień dobry ustawimy w stan Idle
state = Idle;
I obsługa poszczególnych stanów w kodzie programu:
while (1)
{
switch (state)
{
case Idle:
break;
case Led0Off:
PORTB |= (1 << PB0);
state = Idle;
break;
case Led0On:
PORTB &= ~(1 << PB0);
state = Idle;
break;
case Led1Off:
PORTB |= (1 << PB0);
state = Idle;
break;
case Led1On:
PORTB &= ~(1 << PB0);
state = Idle;
break;
default:
break;
}
}
W ten sposób otrzymaliśmy działający kodzik gotowy na implementację kolejnych bardziej lub mniej zawiłych zasad i reguł. Zwróć uwagę, że obsługa danego stanu kończy się przejściem w stan bezczynności (Idle).
Rozpatrzyliśmy dość trywialny przypadek oparty o diody i 4 komendy. Czy to podejście można zastosować do implementacji 'rozmowy' z jakimś modułem zewnętrznym? WiFi? GSM?
czwartek, 10 marca 2016
Ja Panu nie przerywałem!
Ciężki los mikrokontrolera
Oczekiwanie w pętli nieskończonej na interesujące nas wydarzenie nie wygląda najlepiej. Mało tego, może nieco poirytować nasz mikrokontroler. Sprawdzanie co jakiś czas czy się coś gdzieś nie zmieniło nie należy do najciekawszych zadań. Można w tym czasie policzyć jakąś całkę... Poza tym - czasy wszędobylskiego pollingu w dobie Reacta dawno minęły... chyba?Na szczęście nie musimy postępować w ten sposób. Możemy skorzystać z wbudowanego mechanizmu przerwań. Co nam to daje? Ano wyzbycie się z kodu takich czy innych ifów. Póki co musimy zarządzać tym sprawdzającym stan przycisku. Niewiele, do ogarnięcia. Ale w przyszłości będziemy musieli obsłużyć bardziej złożone akcje (np. naciśnięcie kolejnego przycisku, czy obsługa komunikacji). Łatwo można sobie wyobrazić, jak bardzo nieczytelny stanie się kodzik, gdy w każdym przebiegu pętli będziemy sprawdzać po kolei, czy aby coś się nie zmieniło w naszym układzie... Pomijając fakt czytelności, kod nie zdobędzie nagrody najbardziej wydajnego na Świecie.
Czym zatem są wspomniane przerwania?
Przerwanie to takie zdarzenie w cyklu pracy procesora, które sprawia, że aktualnie wykonywany program jest przerywany (z zachowaniem potrzebnych rejestrów), a sterowanie przekazane jest do miejsca w pamięci programu, gdzie umieszczono kod odpowiedzialny za obsługę przerwania. Po wykonaniu tego kodu, sterowanie wraca do miejsca w kodzie programu, gdzie wystąpiło przerwanie. Proste? To tak, jakby pisać sobie w najlepsze kodzik, a ktoś by przyszedł i poprosił nas, żeby na coś zerknąć... Więc zerkamy a potem heja! do miejsca, w którym skończyliśmy.
Datasheet Twoim najlepszym przyjacielem
Atmega posiada wbudowany moduł obsługi przerwań. Datasheet zawiera opis wszystkich, z którymi możemy prędzej czy później mieć styczność. Często będę odsyłał Was do tego pełnego wiedzy wszelkiej pdf'a. gdyż tam jest źródło prawdy - nie na blogach, tylko w dokumencie publikowanym przez producenta. Posty mogą się przedawnić, nota katalogowa nie powinna :)Atmega162 posiada 2 piny, dzięki którym możemy wyzwolić zewnętrzne przerwanie - na schemacie opisane INT0 i INT1. Podpinając przycisk do jednego z nich i odpowiednio konfigurując odpowiednie rejestry, możemy w elegancki sposób dokonać refaktoru naszego kodu.
Rejestry
Chcąc skorzystać z zewnętrznego przerwania INT0 musimy skonfigurować rejestry:GICR |= (1 << INT0);Oraz MCUCR, gdy chcemy reagować na konkretną zmianę stanu na pinie INT0 (stan niski, jakakolwiek zmiana stanu, zbocze narastające - zmiana z 0 na 1, czy zbocze opadające - zmiana z 1 na 0) . Pozostawienie w nim wartości domyślnych (0) sprawi, że nasz mikrokontroler "wpadnie" w przerwanie, gdy na pinie pojawi się stan niski. Dodatkowo, po skonfigurowaniu przerwań, musimy wykonać makro, które poinformuje mikrokontroler, że chcemy z nich korzystać
Kodzik!
Jeszcze tylko obsługa przerwania i mamy gotowy kodzik - po naciśnieciu przycisku, zmień stan LEDów na przeciwny:#include <avr/io.h>
#include <avr/interrupt.h>
int main(void)
{
// set pd2 (INT0) as input
DDRD &= (1 << PD2);
// pull-up for pd2
PORTD |= (1 << PD2);
// set pb0 and pb1 pins as output
DDRB |= (1 << PB0) | (1 << PB1);
// turn off led connected to pb0
PORTB |= (1 << PB0);
// turn on led connected to pb1
PORTB &= ~(1 << PB1);
// enable INT0 interrupt
GICR |= (1 << INT0);
// enable global interrupts
sei();
while (1)
{
}
}
ISR(INT0_vect)
{
PORTB ^= (1 << PB0);
PORTB ^= (1 << PB1);
}
Warto pamiętać
Kod, który powinien wykonać się w ramach obsługi przerwania powinien być jak najkrótszy! Wszak ma to być przerwanie, nie zmiana kontekstu :) Jest to jedna z dobrych praktyk kodzenia na niskim poziomie.I jeszcze notka wyjaśniająca - chwilowo jestem szczęśliwym człowiekiem cieszącym się benfitami płynącymi z przebywania na urlopie - jak tylko wrócę w okolice mojego zestawu deweloperskiego (atmega + programator) dorzucę conieco na githuba. Stay tuned!
Subskrybuj:
Posty (Atom)


