wtorek, 10 maja 2016

Nowy projekt w Atmel Studio

Jakieś 2 miesiące temu pomyślałem, że obejdzie się bez sięgania do podstaw. Dzisiaj pomyślałem - a co, jeśli ktoś przeczyta moje wpisy, zapała nieograniczoną chęcią stworzenia własnego małego urządzenia i cały Jego zapał pryśnie, gdy w Atmel Studio po utworzeniu projektu dostanie w twarz takim oto okienkiem:


By uniknąć zaprzepaszczonych szans - dziś specjalnie dla Ciebie, początkujący czytelniku kilka słów na temat konfiguracji programatora AVR Dragon w Atmel Studio.

Zatem od początku. Naszym celem jest zaprogramowanie mikrokontrolera Atmega 162 skompilowanym kodem pochodzącym z pliku main.c. Zakładam, że Dragon jest podpięty przewodem USB do komputera i taśmą z wyprowadzeniami JTAG do mikrokontrolera.

Pierwszy krok - tworzymy projekt. Dla pierwszego w solucji:


Dla każdego następnego prawy przycisk myszy na solucji i:


Po wybraniu opcji (New) Project pojawi nam się okno:


(gdzie dla pierwszego projektu będziemy mieli dodatkowo możliwość nazwania naszej nowej solucji).
W otwartym okienku wybieramy typ projektu - Executable project - możemy wybrać język c lub c++, podajemy nazwę i klikamy OK. Następnie zostaniemy zapytani o docelowy mikrokontroler:


Możemy użyć szukajki z prawego górnego rogu i dzięki niej znaleźć interesujący nas model mikrokontrolera. Zaznaczamy i OK. Atmel Studio utworzy plik main.c (jeżeli wybraliśmy język C) i dorzuci tam kawałek boiler plate'a z informacją o autorze, a w funkcji main umieści nieskończoną pętlę. Zestaw w sam raz na dobry początek. Wgrajmy ten skomplikowany kodzik do mikrokontrolera! Na początek - upewnijmy się, że wybrany przez nas projekt jest domyślnym (startowym) w solucji. Prawy klawisz na nazwę projektu i do dzieła:


Domyślny projekt jest zaznaczony pogrubioną czcionką. Następnie klikając w:


Uruchamiamy procedurę budująco-programującą. I tu zonk:


Konfigurując projekt nie wybraliśmy domyślnego programatora. Możemy to zrobić teraz. Continue. Szczęśliwie AS zostawia nas w miejscu, gdzie powinniśmy skonfigurować programator. Z tajemniczego dropdowna wybieramy naszego Dragona z interfacem JTAG:


Naszym oczom ukaże się multum opcji do wyboru. Ustawiamy jeszcze JTAG clock. Częstotliwość nie powinna być większa niż 1/5 częstotliwości taktowania mikrokontrolera. W naszym przypadku 8 MHz, zatem nie przekraczamy 1.6 MHzm czyli 1 MHz jest jak najbardziej poprawną wartością. Zapisujemy zmiany (Ctrl+S albo dyskietka z menu) i próbujemy jeszcze raz.


Nasz skompilowany kodzik trafia wprost do naszej Atmegi, o czym zostaniemy poinformowani w okienku Output dla Build:



Z ciekawych rzeczy - dowiemy się ile pamięci programu i ile pamięci danych zajął nasz kodzik w mikrokontrolerze (zaznaczony fragment). Możemy zrobić pierwszego commita i naduszać dalej z kodzikiem:)

piątek, 6 maja 2016

Fun with Fan part 4

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!


ś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.

czwartek, 28 kwietnia 2016

Fun with Fan

Kodzik jest jak klocki LEGO

PWM - temat tak wdzięczny, że postanowiłem poświęcić mu jeszcze kilka postów. Wprowadzając pojęcie PWM do moich rzeczybezinternetu wspomniałem, jak można go wykorzystać do niecnych celów. Teraz, gdy już mamy wiedzę na temat podpinania termometru do naszej Atmegi, możemy zacząć składać klocki w większe... budowle.

Masz problem?!

Zatem znajdźmy sobie problem. Potrzebne będą: temperatura, sterowanie napięciem zasilania, urządzenie mogące być sterowane zasilaniem. Przykładowy problem - przegrzewający się mikrokontroler. Możemy mierzyć jego temperaturę, a w momencie, gdy ta przekroczy założoną granicę - możemy ochłodzić nieco atmosferę. Zatem do dzieła!

Analiza

Do chłodzenia całego układu posłuży nam wiatraczek wymontowany ze starego komputera - zasilany stałym napięciem 12V - można go nabyć w każdym sklepie z wiatraczkami (czy wentylatorami) - w tym przypadki wiatraczek jest odpowiednim słowem. I tu pojawia się pierwszy problem - jak wysterować urządzenie zasilane 12V z naszej Atmegi, gdzie możemu liczyć na 5V max, przy ograniczonym prądzie. Rozwiązań jest wiele. Zaprezentuję 2. Oba będą zawierać pewien rodzaj izolacji galwanicznej - o tej też już na pewno wiesz!

Część wspólna

Zacznijmy od skonfigurowania PWM. Posłużmy się posiadaną wiedzą:


Termometr

Potrzebny nam będzie element sterujący PWM. Nie będziemy przecież stać z termometrem nad mikrokontrolerem i zmieniać prędkości wentylatora. Podepnijmy nasz pasożytniczy DS1820PAR i wykorzystajmy wcześniejszy kodzik do odczytu temperatury

Podłączenie LED do wyjścia PB0 (OC0) i zmiana wartości wpisywanej do rejestru OCR0 powodowała jej przygaszanie lub rozjaśnianie. Możemy wnioskować, że w czasie zmieniało się napięcie. Zamieńmy teraz diodę na wentylatorek. Brakuje nam dobrych 7V, żeby kręcić nim na pełnych obrotach, a 12V nasza Atmega nie wytrzyma - czas na izolację

Transoptor


Zadziała? Zadziała. Ale bez szaleństw. Transoptor stanie się częścią obwodu. Napięcie, które powinno odłożyć się na wentylatorze (wpływając na jego prędkość), odłoży nam się również na tranzystorze transoptora, a prąd płynący przez układ sprawi, że wspomniany tranzystor zacznie się grzać. Nie rozwiążemy pierwszego problemu, a na domiar złego stworzymy sobie nowy.

Tranzystor MOSFET

I to nie byle jaki - a tranzystor Mocy! 4 maja już niebawem, a wszyscy wiemy, że Mocy nie należy lekceważyć... W odróżnieniu od opisywanych wcześniej tranzystorów bipolarnych - nóżki tego tranzystora posiadają inne nazwy: gate, drain i source. Wiedza potrzebna na teraz: do bramy możemy doprowadzić wyjście PWM, source połączmy z masą, a między drain i napięcie zasilania wepnijmy wentylatorek.


Dlaczego możemy połączyć Atmegę w ten sposób? Ze względu na budowę tranzystora. I dane zamieszczone w nocie katalogowej. Z niej dowiemy się również, że nasze 5V może nie wystarczyć, by rozkręcić wentylatorek na maxa - aczkolwiek prędkość, jaką osiągnie powinna w zupełności wystarczyć.

Pozostaje tylko dodać trochę logiki biznesowej...

piątek, 22 kwietnia 2016

Daj się poznać lvl fotografia

Nie działa i nie wiadomo dlaczego

Projekty elektroniczne mają to do siebie, że nie wszystko działa tak, jakbyśmy tego oczekiwali... A na koniec okazuje się, że cały czas krążyliśmy wokół rozwiązania. Na takie krążenie natknąłem się i ja... Obecnie walczę z uruchomieniem silnika krokowego. Jako że rezultaty nie są najciekawsze pod względem ekhm działania, dziś mniej techniczny post!


Daj się poznać lvl fotografia

Czymże jest wspominane tyle razy moje środowisko deweloperskie - ano mniej więcej czymś takim.


Na zdjęciu od góry:

  • część dolna ściągi z datasheetu (wyprowadzenia Atmegi)
  • płytka stykowa
  • moduł zasilający (mam dostępne 3.3V i 5V DC dzięki niemu!)
  • złącze programatora (JTAG ofc!)
  • żółta i czerwona LED
  • Atmega162, 
  • Przycisk (wpięty do przerwania)
  • Po lewej moduł bluetooth na płytce z mojego projektu inżynierskiego
  • Moduł Fibocom G510
  • Termometr (między modułami GSM i Bluetooth)
  • Programator AVR Dragon

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 tekstowy
AT+CMGF=1
Po otrzymaniu informacji
OK
Podajemy 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:
\x1A
Moż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.

wtorek, 12 kwietnia 2016

SMS Init

Żenujący żart prowadzącego

Znamy już podstawy teoretyczne współpracy z modułem GSM Fibocom G510, pora na odrobinę praktyki. Btw. znacie różnicę między teorią a praktyką? Teoria jest wtedy, gdy wszystko wiemy, ale nic nie działa. Praktyka - wszystko działa, ale nikt nie wie dlaczego. Jest jeszcze ciąg dalszy dotyczący politechniki - tam łączy się teoria z praktyką - nic nie działa i nikt nie wie dlaczego...

Wróćmy do tematu

Nasz moduł GSM nie jest typowym urządzeniem typu "daj mi zasilanie, a pozwolę Ci ze mną rozmawiać". Żeby moduł pozostał włączony po dostarczeniu zasilania, musimy mu to jednoznacznie określić. W tym celu na pin POWER_ON modułu musimy podać stan niski przez okres co najmniej 800 milisekund - jak niski sygnał pozostanie tam ciut dłużej - nic złego się nie stanie - dla pewności, wystawię 0 na czas 1 sekundy.


Co się stanie, gdy czas trwania sygnału niskiego będzie za krótki? To zadziała trochę jak odpalanie zimnego silnika motocykla bez ssania - trochę popracuje i zgaśnie... Zatem na moment zobaczymy, że nasz moduł żyje, by potem znów obserwować smutne zgaszone diody


Jak możemy zrealizować włączanie? Na przykład podpinając tę nóżkę na stałe do masy - w momencie pojawienia się napięcia zasilania w układzie, po upływie niecałej sekundy nasz moduł zacznie się budzić do życia (logować do sieci GSM). Nuda. Skoro dostajemy możliwość sterowania stanem modułu, dlaczego tego nie wykorzystać? Wystarczy poświęcić jedną linię sygnałową mikrokontrolera (pin) i podpiąć bezpośrednio do modułu (jeżeli zapewnimy kompatybilność napięć) lub wykorzystać tranzystor albo pójść drogą super bezpiecznej izolacji galwanicznej - transoptor. W swoim mikrokontrolerze poświęciłem linię PC7 - to ona będzie odpowiedzialna za włączanie i wyłączanie modułu GSM.

Kodzik

piątek, 8 kwietnia 2016

GSM Fibocom G510

What's next?

Ograniczenia wynikające z utrzymaniem połączenia z mikrokontrolerem za pośrednictwem modułu Bluetooth zmuszą nas prędzej czy później do rozważenia rozwiązania o większym zasięgu. Skoro producenci telefonów komórkowych potrafią jakoś podpiąć kartę SIM do obecnego w telefonie mikrokontrolera, dlaczego my nie moglibyśmy zrobić tego samego? Bezpośrednie łączenie pól karty SIM z Atmegą nie jest najlepszym pomysłem - zastosujmy do tego interfejs!

GSM

Moduł GSM Fibocom G510 jest takim właśnie interfejsem. Z jednej strony podpinamy go do Atmegi (USART - a jakże!), z drugiej strony - montujemy antenę i wkładamy kartę SIM (przed włożeniem karty do slotu warto wyłączyć blokadę SIM - korzystając z telefonu komórkowego). Po podłączeniu do prądu nasza karta powinna się zalogować w sieci - tym samym umożliwić nam komunikację z naszym układem wszędzie tam, gdzie jest zasięg.


Możliwości są ograniczone tylko naszą wyobraźnią. 

Moduł możemy wykorzystać do nawiązywania połączeń, wysyłania SMSów, korzystania z transmisji danych - nasze Rzeczy mogą uzyskać dostęp do Internetu - a Internet... do nich. Zanim jednak przejdziemy do rozmowy z modułem - zobaczmy, w jaki sposób możemy się z nim porozumieć...

Zasilanie

Sięgnijmy po raz kolejny do datasheetu. I tu niestety porażka (przynajmniej w moim przypadku). Producent podaje przedział napięcia od 3.3V do ok. 4V. Podpowiem - na 3.3 nie ma szans - moduł owszem - uruchomi się, ale nie zaloguje się do sieci. Musimy dostarczyć mu rekomendowane przez producenta 4.0-4.1V, co może skutkować pewną akrobatyką, jeżeli chodzi o sposób, w jaki takie zasilanie dostarczymy do układu. Możemy bawić się w konwertery napięcia - by Atmega zasilana napięciem 3.3V była w stanie "dogadać się" z modułem, ale to tylko zwiększa koszt rozwiązania. Warto pomyśleć nad układem, który pracuje na wspólnym napięciu. W tym celu powinniśmy wykorzystać "konfigurowalny" stabilizator napięcia, albo posłużyć się prawami fizyki i wymusić spadek napięcia przed wejściem do układu.
Warto też zwrócić uwagę, że w celu uruchomienia modułu musimy podać stan niski na wejście POWER_ON. Więc zanim zaczniecie panikować, że układ nie działa, upewnijcie się, że takowy stan występuje. Ma to swoje zalety - możemy wyłączać moduł, by oszczędzać energię.

LPG

LED - najprostszy "Hello World" w elektronicznym świecie.

Sugerowany przez producenta sposób podpięcia diody do sygnału LPG (źródło: dokumentacja)


Moduł posiada wyprowadzenie LPG - warto podpiąć do niego diodę, gdyż jest to pierwsza linia sygnałowa, która podpowie nam, w jakim stanie znajduje się obecnie moduł. Sygnał ten możemy poprowadzić też bezpośrednio do naszego mikrokontrolera, by już na poziomie kodu móc śledzić stan modułu. O czym nam opowie sygnał LPG? Sięgnijmy do noty katalogowej.

Stan modułu sygnalizowany przez diodę podpiętą do sygnału LPG (źródło: dokumentacja)

USART

Na pokładzie modułu znajdują się 3 układy USART. Ciekawostką jest fakt, że jeden z nich można wykorzystać do programowania samego modułu. Producent na egzemplarzach oznaczonych OPEN CPU zaimplementował możliwość wgrania swojego małego programu. Na potrzeby niewielkich projektów - mega feature! Skupmy się jednak na komunikacji... A ta przebiega standardowo - musimy skrzyżować linie RxD i TxD modułu z odpowiadającymi liniami mikrokontrolera.


Język

Jak większość modułów tego typu i w tym przypadku do komunikacji wykorzystamy modemowe komendy tekstowe AT. Lista obsługiwanych komend i przykładowe "procedury" - tj. określona kolejność ich wysyłania, w celu uzyskania konkretnego skutku - znajdziemy w dokumentacji. W kolejnych postach opiszę sposób obsługi SMSów (wysyłanie i odbieranie).


Im łatwiej tym lepiej

Moduł możemy zakupić "nagi" - tj. gotowy do lutowania powierzchniowego - jednak na potrzeby prototypowania i niejako zabawy :) warto dorzucić kilka złotych i zakupić układ z wlutowanymi niezbędnymi do prawidłowego działania elementami (gniazdem antenowym, stabilizatorem napięcia, slotem na kartę SIM) z wyprowadzeniami USART na goldpinach. Jak już przyjdzie "co do czego" - zaprojektujemy własny układ i poprowadzimy wszystkie wymagane sygnały.

W oczekiwaniu na podłączenie do zasialnia...



ś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)
Jak widać - w tym przypadku określenie 1-Wire jest pewnym niedomówieniem :) warto jednak pamiętać, że 1-Wire określa tylko ilość linii, jaka jest wykorzystywana do przesyłania danych. Korzystając z pasożytniczego czujnika będziemy potrzebować jeszcze jednego pinu mikrokontrolera, który będzie sterował tranzystorem dostarczającym zasilanie na linię danych.

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:

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:

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.

sobota, 2 kwietnia 2016

Ważna Linia

USART to potężne narzędzie i daje nam naprawdę duże możliwości komunikacji. Jak łatwo się domyślić, nie wszystkie urządzenia implementują ten standard - głównie dlatego, że wymaga zbyt wiele miejsca na elektronikę, która byłaby odpowiedzialna za komunikację. Na szczęście istnieją prostsze i mniej wymagające sposoby 'dogadania się' z elementami z zewnątrz mikrokontrolera.

Tniemy koszty

USART do prawidłowego działania wymagał co najmniej 3 linii - 2 sygnałowych i jednej linii odniesienia. Jak uprościć ten schemat? Wyrzućmy jedną linię! Proste. Zostawmy linię odniesienia - nadal musi istnieć wspólne 'źródło prawdy' (kiedy jest 0), a 2 linie sygnałowe zastąpmy 1. Czyli zamiast używania uszu i ust do komunikacji, używamy aparatu, który będzie jednym i drugim... na zmianę. Pamiętacie z dzieciństwa zabawę z kubkami i sznurkiem? Nawet nie wiedzieliście, że korzystacie z rozwiązania 1-Wire - opisanego np. w nocie katalogowej jednego z najbardziej popularnych termometrów wśród miłośników domowej automatyzacji.

Jakoś się dogadamy...

Podobnie jak w przypadku USART, potrzebny jest protokół. Mamy do dyspozycji 1 linię, która w danym momencie może być:
wyjściem w stanie wysokim
wyjściem w stanie niskim
wejściem
i jeszcze ten 'dany moment' - czyli urządzenia połączone ze sobą mają pojęcie o czasie! To już coś.

Jak rozmawiamy? Wiadomo - zera i jedynki układające się w bajty, a bajt to już liczba albo znak - a to już potrafimy wysłać przez USART do innego urządzenia...  Ale po kolei.

Cała komunikacja polega na nasłuchiwaniu, w jakim stanie znajduje się magistrala lub naduszaniu na nią 'swojej woli'. I tak chcąc wysłać bit, urządzenie powinno wystawić stan niski na linii, następnie po czasie 10 us wystawić stan wysoki, jeśli chce wysłać 1, a następnie - w obu przypadkach - odczekać 100us, następnie znów wystawić stan wysoki na linii.

Chcąc odczytać bit, urządzenie powinno na 2us zmienić kierunek swojego portu komunikacyjnego, następnie po 15us sprawdzić stan linii.

8 bitów to bajt!

Skoro wiemy jak odczytać i nadać bit - odczytajmy i nadajmy bajt! - ośmiokrotnie wywołując funkcje dotyczące bitów, dbając o to, żeby dostarczać kolejne bity w przypadku nadawania i zapisywać w przypadku odbierania.

1 mówi, reszta słucha

Wiemy jak korzystać z linii, ale skąd możemy mieć pewność, że nikt inny akurat z tej linii nie korzysta? Standard przewiduje także procedurę resetu. Polega ona na wprowadzeniu magistrali w stan niski na 480us, następnie 480us w stanie wysokim. Magistrali? Tak magistrali! 1-Wire to magistrala! Możemy do niej wpiąć wiele urządzeń - każde z nich będzie miało swój własny adres i możliwa będzie komunikacja - przeważnie typu master-slave. Np. mikrokontroler jako master odpytywać będzie czujniki temperatury DS1822 działające w trybie slave - w ten sposób zbierze dane z całego domu, przetworzy je i wyśle do centralnego serwera, który w niecny sposób wykorzysta tę tajemną wiedzę (i na przykład podkręci ogrzewanie...).

A gdzie kodzik!?

Czeka na porządne przetestowanie na moim dev-środowisku na biurku :)

czwartek, 31 marca 2016

#dajsiepoznacHeartbeat

Uczestnictwo w #dajsiepoznac spowodowało, że oprócz prowadzenia jednego projektu w tzw. międzyczasie napisałem prostą aplikację w React'ie. Oprócz tego, że jest ona moją piaskownicą do zabawy z node'm, czy nowinkami w stylu flux'a, pokazuje listę opublikowanych blogów z #dajsiepoznac:
Kolor zielony to blogi, na których ostatni post pojawił się w przeciągu ostatnich 6 dni (<7)
Kolor pomarańczowy, to blogi z ostatnią aktywnością zaobserwowaną 7-14 dni temu
Kolor czerwony, to blogi z postami starszymi, niż 14 dni
Kolor niebieski - nieaktywne blogi.
Liczba blogów w danym kolorze widoczna jest na górze listy.
Każdy element listy ma 2 przyciski - pod jednym z nich znajduje się RSS bloga, pod drugim link.

#dajsiepoznacHeartbeat podejrzeć można tutaj: http://dsp-heartbeat.azurewebsites.net/dist/



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...

czasownik «część mowy obejmująca wyrazy oznaczające czynność lub stan; też: wyraz należący do tej części mowy»  - SJP

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?


piątek, 25 marca 2016

Pamiętaj!

Problem

Prędzej czy później w naszej przygodzie z programowaniem mikrokontrolerów dostrzeżemy pewien mankament - stan mikrokontrolera jest utrzymywany tylko wtedy, gdy dostępne jest zasilanie. Dość niefortunna przypadłość - zwłaszcza wtedy, gdy bardzo nam zależy, by jednak pewien stan się zachowywał, mimo braku prądu. Co to może być? Na przykład podana przez nas godzina, o której załączać ma się światło albo piec w naszym "trochę mądrzejszym" domu. Załóżmy, że chcemy mieć możliwość zmiany tej godziny bez konieczności przeprogramowania całego układu, dlatego zahardkodowana wartość nie wchodzi w grę.

Analiza

Jak zatem przechowamy naszą godzinę? Przyda nam się do tego Memory - człowiek potrafi zapamiętać wiele rzeczy, dlaczego mikrokontroler nie mógłby robić czegoś podobnego? Pamięć ta powinna być "nieulotna" - tj. rzeczy przechowywane w niej nie powinny zanikać przy braku zasilania. Dodajmy do naszego Memory określenie Read-Only. W ten sposób na potrzeby zapamiętania godziny złożyliśmy sobie własny ROM. Nadal poruszamy się w świecie programowania Atmegi - przydałoby się móc zaprogramować naszą pamięć - bez kompleksów dorzucamy Programmable. Ale przecież chcieliśmy zmieniać naszą godzinę załączania pieca! Zatem raz zapisaną w pamięci, powinniśmy móc wykasować. Do kasowania użyjmy elektroniki! Electrically-Erasable powinno wystarczyć. Zbierzmy wszystko razem - co nam wyszło? EEPROM.

Eureka!

Dobra nasza! Atmega162 wyposażona jest w 512 bajtów pamięci EEPROM! Bez problemu znajdzie się tam miejsce dla naszej godziny. Mało tego, korzystanie z niej jest dziecinnie proste - wystarczy jak w przypadku omawianych wcześniej "ficzerów" skorzystać z dostępnych rejestrów. Na naszej głowie będzie również zadbanie o prawidłową adresację. Nie powinno to być trudne - adres, pod jakim w pamięci EEPROM znajdzie się nasza dana może być już przechowany w pamięci programu. Niestety - są też pewne ograniczenia - w tym przypadku oprócz rozmiaru, lmitowana jest też ilość cykli zapisów/odczytu. Atmel dla Atmegi przewidział co najmniej 100000 takich cyklów. Dużo? Mało? Matematykę zostawiam Wam.

Kodzik!

Do obsługi pamięci wykorzystamy poniższy kodzik:

Zanim przystąpimy do procedury zapisu, powinniśmy znać adres, pod jaki zapiszemy naszą daną i jej wartość. Bogatsi w tę wiedzę rozpoczynamy sekwencję zapisu. Przede wszystkim musimy zadbać, że nasz zapis nie wpłynie negatywnie na trwający zapis - musimy zaczekać, sprawdzając stan bitu EEWE w rejestrze EECR. Następnie do rejestru EEAR wpisujemy adres, a do EEDR wartość naszej danej, którą chcemy umieścić w pamięci. Potem wystarczy ustawić bit EEMWE i EEWE w rejestrze EECR i nasza dana ląduje w pamięci.

void EEPROM_write(unsigned int address, uint8_t data)
{
   while(EECR & (1<<EEWE)); // poczekaj na zakończenie poprzedniego zapisu

   EEAR = address; // ustawienie adresu bajtu do zapisu
   EEDR = data; // dane do zapisu
   EECR |= (1<<EEMWE); // master write enable
   EECR |= (1<<EEWE); // eeprom write enable
}


Żeby odczytać interesującą nas wartość z pamięci, musimy znać adres, pod jakim wcześniej została tam umieszczona. Po raz kolejny musimy się upewnić, że nie przerwiemy procedury zapisu. Następnie ustawiamy wartość rejestru EEAR - (widzicie problem? Gdybyśmy nie zaczekali na koniec zapisu, właśnie podmienilibyśmy adres, pod którym miałaby wylądować zapisywana wartość). Jeszcze tylko ustawić bit EERE w EECR  i z rejestru EEDR (kolejny współdzielony między zapisem i odczytem) możemy odczytać naszą wartosć.

uint8_t EEPROM_read(unsigned int address)
{
   while(EECR & (1<<EEWE)); // poczekaj na zakończenie poprzedniego zapisu

   EEAR = address; // ustawienie adresu bajtu do odczytu
   EECR |= (1<<EERE); // eeprom read enable
   return EEDR; // zwrócenie odczytanego bajtu
}

Porozmawiajmy - USART - Konfiguracja

Nasz mikrokontroler wie już jak poinformować świat, że coś się zmieniło w jego rejestrach. Wie też, jak odczytać sygnały, które mu chcemy przekazać. Czas najwyższy nauczyć go jak rozmawiać z urządzeniami jego pokroju.

Mowa jest złotem

Żeby się porozumiewać, człowiek przez lata ukształtował coś, z czego wszyscy korzystamy - język. W dużym uproszczeniu medium transmisyjnym w większości przypadków jest powietrze - to w nim rozchodzi się dźwięk - głoski wyrażane przez nasze aparaty mowy, które po pewnym czasie trafiają do uszu naszego rozmówcy. Sposób sprawdzony, znany od tysiącleci, posiadający wiele implementacji. Więc na co czekamy? Przełóżmy to na "elektroniczne".

Gotowe rozwiązania

Potrzebujemy medium transmisyjnego - będzie nim przewodnik - np. przewód albo miedziana ścieżka na płytce. Głoski zastąpimy poziomami napięcia - wysokim i niskim. Dwa stany w zupełności wystarczą. Aparat gębowy zastąpimy linią nadającą, a ucho linią odbierającą. Analogią dla ośrodka mowy i słuchu naszego mózgu będzie kodzik wykonywany przez procesor - to do niego będzie należała decyzja, co mówić i czego słuchać.

Do you speak high-low?

Możemy sobie już wyobrazić 2 urządzenia, które "krzyżując" swoje linie mogłyby ze sobą rozmawiać. Mogłyby, ale się zapewne nie dogadają. Potrzebują do tego jeszcze punkt odniesienia, względem którego będą weryfikować, czy to co do nich dotarło to jeszcze stan niski, czy już stan wysoki. Dajmy im w tym celu jeszcze jedną linię. Czy nasze urządzenia będą w stanie się dogadać? Tak, jeżeli tylko nauczymy je tego samego "języka", a przynajmniej kilku zasad, których będziemy przestrzegać w czasie komunikacji. I tu pojawia się określenie protokół.

Źródło: atmega162 datasheet
Załóżmy, że nasze urządzenia będą rozmawiać "ramkami", czyli zestawem stanów wysokich i niskich w określonym porządku. Ramkę rozpoczną bitem startu (i będą wiedziały jak go "wystawić"), następnie prześlą dane, po nich bit parzystości, a na koniec bit stopu. Następnie przejdą w stan nasłuchiwania. Ważne jest, by obie strony wiedziały, jakiego formatu ramki powinny się spodziewać. Dodajmy do tego deklarację prędkości, z jaką będzie przeprowadzana transmisja.

USART

W ten sposób zdefiniowaliśmy elementy potrzebne do wykorzystania układu transmisji danych obecnego na pokładzie prostej Atmegi (znajdziemy go też w Arduino, a nawet w Malince). Moduł ten zwalnia nas z konieczności implementacji całego protokołu transmisji. Mało tego, programiści Atmela oddali do naszej dyspozycji m.in. makra ustawiające parametry komunikacji i przerwania.
Jak się nietrudno domyślić - po jednej stronie komunikacyjnego układu stać będzie nasz mikrokontroler, po drugiej... cokolwiek mogące obsłużyć protokół UART/USART. Co to może być? Moduł Bluetooth, który stanie się dla nas przepustką do świata bez przewodów, inna Atmega albo nasz domowy PC wyposażony w port COM (w tym przypadku przyda się konwerter napięć - zanim podepniesz 2 rzeczy ze sobą, sprawdź, jakie poziomy logiczne obsługują, i czy aby na pewno rozmawiają na tym samym poziomie - 12V nie zadziała kojąco na mikrokontroler zasilany napięciem 3.3V - i tłumaczenie, że chciałem tylko porozmawiać nic nie da...).

Kodzik!

Wiemy już jak działa komunikacja. Czas poinstruować naszego procka.

#include <avr/io.h>
#include <avr/interrupt.h>
#define F_CPU 8000000UL
#define USART_BAUDRATE 19200// 115200
#define UBRR_VALUE (((F_CPU / (USART_BAUDRATE * 16UL))) - 1) // 25

void USART0Init(void)
{
    // Set baud rate
    UBRR0L = (uint8_t)UBRR_VALUE;
    UBRR0H = (uint8_t)(UBRR_VALUE>>8);

    // Set frame format to 8 data bits, no parity, 1 stop bit
    UCSR0C=(1<<URSEL0)|(1<<2)|(1<<1);

    //enable reception and RC complete interrupt
    UCSR0B |= (1<<RXEN0)|(1<<RXCIE0)|(1<<TXEN0);
}

void UsartFlush(void)
{
    UCSR0B &= ~(1<<RXEN0);
    UCSR0B |= (1<<RXEN0);
}

void UsartWrite(char* text)
{
    while(*text)
    {
        while(!(UCSR0A & (1<<UDRE0)))
        {
            //Do nothing
        }

        //Now write the data to USART buffer
        UDR0=*text++;
    }
}


int main(void)
{
    USART0Init();

    // set pb0 and pb1 pins as output
    DDRB |= (1 << PB0) | (1 << PB1);

    // turn off leds connected to pb
    PORTB |= (1 << PB0) | (1 << PB1);

    // initialize interrupts
    sei();
   
    while (1)
    {
    }
}

// onUsart
ISR(USART0_RXC_vect)
{
    char temp;
 
    temp = UDR0;
   
    if (temp == '0')
    {
        // switch led state connected to pb0
        PORTB ^= (1 << PB0);
    }
    else if (temp == '1')
    {
        // switch led state connected to pb1
        PORTB ^= (1 << PB1);
    }
}




piątek, 18 marca 2016

Kodzenie na fazie - PWM dla odważnych

Powrót do przeszłości

Post o Fast PWM przypomniał mi pewną rzecz, którą popełniłem w ramach pracy inżynierskiej.

Tematem pracy był "Moduł sterowania Inteligentnego Domu sterowany z urządzenia z systemem Android za pośrednictwem Bluetooth.", co sprowadziło się do podpięcia modułu Bluetooth do płytki STM32 Discovery, napisania prostej aplikacji na Androida i zaprojektowaniu układu wykonawczego dla całego modułu. I gdzie tu miejsce na Fast PWM?

Moduł umożliwia sterowanie fazowe oświetleniem - co dla użytkownika oznacza rozjaśnianie i przyciemnianie żarówki :) I to wszystko jest możliwe z poziomu telefonu z Androidem - wypas!

I przy odrobinie (nie)szczęścia może nas porazić prąd... Zatem pokrętło ostrożności ustawiamy na max i do dzieła!

Sterowanie fazowe oświetleniem

Sterowanie fazowe oświetleniem polega na „wycinaniu” z sinusoidalnego przebiegu napięcia odpowiedniego fragmentu. Układ wykrywa moment przejścia sinusoidy przez zero i odlicza czas, po jakim zostanie załączony triak. Im większy czas, tym moc dostarczona do urządzenia zewnętrznego będzie mniejsza. Na poniższym wykresie zaznaczono 2 sytuacje – pierwsza, gdy czas oczekiwania na załączenie triaka jest bliski ½ T (dla zasilania sieciowego 230V 50Hz jest to 20ms), do urządzenia zewnętrznego podawana jest mała moc – ok. 10%, dla drugiego przypadku – przy opóźnieniu załączenia bliskim 0 – moc dostarczona do urządzenia jest zbliżona do maksymalnej.

Sterowanie fazowe. Źródło: Elektronika Praktyczna


Realizacja sprzętowa

Układ wykonawczy (po prawej 2 zestawy triak-optotriak odpowiedzialne za sterowanie oświetleniem)


Schemat układu wykrywającego "przejście sinusoidy przez zero"


Do złącz ACIN1 i ACIN2 doprowadzone jest napięcie zasilania 12 VAC z zewnętrznego transformatora. Na anodę diody transoptora OK6 podawane jest napięcie wyprostowane za pomocą mostka Graetza B2. W momencie, gdy sygnał osiąga wartość 0, tranzystor transoptora nie przewodzi – sygnał ZERO wyzwala przerwanie w układzie sterującym.
Schemat układu sterującego oświetleniem

A kodzik?

Sygnał OSWIETL1 podawany na anodę optotriaka U$1 pochodzi z mikrokontrolera. Jest aktywny w momencie, gdy triak T3 powinien przewodzić. 17 Do złącz P$2 i P$3 należy podpiąć obciążenie. Sugeruje się także wpięcie zewnętrznego filtra RC, który zmniejszyłby zakłócenia powodowane dużą częstotliwością przełączania triaka. 

Sygnał zero podpięty jest do pinu mikrokontrolera skonfigurowanego jako wejście zewnętrznego przerwania. W momencie, gdy przerwanie zostanie wyzwolone, zerowana jest zmienna bulbCounter, która przechowuje czas, jaki upłynął od momentu przejścia przebiegu sinusoidalnego napięcia przez zero. Timer mikrokontrolera skonfigurowany jest tak, by generować przerwanie co 1 mikrosekundę. W momencie wyzwolenia przerwania inkrementowana jest wartość zmiennej bulbCounter i porównywana z wartością zmiennej brightness, która przechowuje zadaną przez Użytkownika jasność żarówki (w skali od 0 do 100). Jeżeli wartości obu zmiennych są równe, optotriak załącza triaka, w przeciwnym wypadku triak nie przewodzi prądu.

W poprzednich postach pokazałem, jak można obsłużyć zewnętrzne przerwanie i zmienić stan wyjścia. Podpinając ten układ do Atmegi i biorąc poprawkę na prawidłową wartość rezystora R3 możemy pobawić się żarówkami w naszym domu.




czwartek, 17 marca 2016

Szczęśliwi czasu nie liczą - Timer/Counter

Szczęśliwi (programiści) czasu nie liczą

Genezy tego stwierdzenia możemy się doszukać w programowaniu mikrokontrolerów. Mówi ona o tym, że szczęśliwym jest programista, który nie musi się martwić o odmierzanie czasu na swoim mikroprocku w sposób programowy; szczęśliwym jest programista, który korzysta ze sprzętowych mechanizmów wbudowanych w mikroprocka.

Kwarc, czyli jakiego masz proca?!

Znajomość częstotliwości taktowania procesora to jedna z tych rzeczy, które każdy szanujący się komputerowy maniak powinien znać! Chyba. Ja taktowania procesora w moim PC nie pamiętam - wiem, że dzięki niemu mogę się spotkać z Wiedźminem i Visual Studio nie będzie przymulać. Przy wysokopoziomowych smutnych biznesowych projektach ta wiedza przydaje się bardzo rzadko (co innego gamedev...). Tak czy siak - programując mikrokontrolery musimy znać częstotliwość taktowania. Bez tego ani rusz! Dosłownie - przy pierwszym podłączeniu Atmegi przez programator powinniśmy "powiedzieć" jej, z jaką częstotliwością ma dla nas pracować. Częstotliwość - czyli tempo, w jakim będą wykonywane rozkazy programu.
Tempo Atmedze może nadawać jeden z dwóch oscylatorów - wewnętrzny - max. 8MHz (odsyłam do datasheetu po szczegóły - sekcja Calibrated Internal RC Oscillator) i zewnętrzny. Zewnętrzny oscylator to tak naprawdę uzupełnienie układu oscylatora "zalanego" w obudowie Atmegi. Najprościej rzecz ujmując do nóżek XTAL1 i XTAL2 musimy podpiąć rezonator - np. kwarcowy z wybraną przez nas częstotliwością. Jaką częstotliwość wybrać? Wiadomka! Jak największą...
Niekoniecznie. Prawidłową odpowiedzią jest: "to zależy, do czego chcesz wykorzystać mikrokontroler" - i jakiej dokładności się spodziewasz, korzystając z wybranego oscylatora.

Tik-tak

Nasz mikrokontroler wie już z jaką częstotliwością ma naduszać wykonanie programu. Ale czy tylko po to wznosiliśmy się na wyżyny konstrukcji układów cyfrowych podpinając kwarc? Oczywiście nie. Oscylator przyda się podczas korzystania z timerów. Prostokątny przebieg pochodzący z oscylatora poinformuje nasz licznik (timer), kiedy powinien zinkrementować przynależny mu rejestr. Na tapetę weźmy Timer0, więc inkrementowanym rejestrem będzie TCNT0. Timer ten jest 8-bitowy, więc w rejestrze pojawią się wartości od 0 do 255.

i++

Brawo my! Możemy skorzystać ze sprzętowego inkrementatora. Teraz tylko trzeba dopisać do tego jakiś powód. Weźmy kolejny rejestr przynależny naszemu licznikowi OCR0. Gdy wartość TCNT0 będzie równa OCR0, niech zmieni się stan pinu opisanego OC0 - czy to nie cudowne, że timer sam potrafi zmienić stan pinu?

FastPWM

Co nam da taka konfiguracja? Możliwość zmiany wypełnienia przebiegu na pinie OC0. Co niesie za sobą zmiana wypełnienia? Kiedy "załączamy" diodę podpiętą do pinu, przebieg jest wypełniony w 100% - co dla naszej LED oznacza np. 5V, gdy zasilamy takim napięciem mikrokontroler. Zmieniając wypełnienie, napięcie na układzie LED-rezystor ulega proporcjonalnemu pomniejszeniu. Proporcja wynosi OCR0/255. Więc najprościej rzecz ujmując - na nóżce OC0 pjawiać się będzie napięcie od 0 do 5V z możliwym krokiem co 1/255 * 5V.

Po czym poznać, że działa?

Podpinając LED pod nóżkę OC0 (PB0 dla Atmegi 162) i wgrywając poniższy kod zaobserwujemy stopniowe przygaszanie i rozjaśnianie diody.

I to wszystko po to, żeby znowu mrugać diodą?

Tak! I nie tylko diodą! Mruganie diody to jedna z możliwości prezentacji działania zmiany wypełnienia przebiegu. Zmieniając napięcie możemy kontrolować prędkość obrotową silniczka DC (autko może jechać wolniej albo szybciej, wiatraczek chłodzący procesor może zwiększać swoje obroty z jakiegoś powodu...) albo pójść krok dalej i wykorzystać nasz układ do sterowania oświetleniem - analogiczna zasada ma prawo działać dla prądu zmiennego - trzeba tylko cięższej artylerii elektronicznej, żeby to wszystko wysterować.

Kodzik!

#define F_CPU 8000000UL

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

void Wait()
{
   _delay_ms(4);
}

int main(void)
{
   uint8_t brightness = 0;
   uint8_t maxBrightness = 250;
   uint8_t minBrightness = 0;

   // Fast PWM mode
   TCCR0 |= (1 << WGM01) | (1 << WGM00);
 
   // OC0 enabled, clear on match
   TCCR0 |= (1 << COM01) | (1 << COM00);
   
   // timer0 clock source prescaler
   TCCR0 |= (1 << CS00);
   
   // pin OC0 as output
   DDRB |= (1 << PB0);
   
   while (1)
   {
       for (brightness = minBrightness; brightness < maxBrightness; brightness++)
       {
           OCR0 = brightness;
           Wait();
       }
      
       for (brightness = maxBrightness; brightness > minBrightness; brightness--)
       {
           OCR0 = brightness;
           Wait();
       }
   }
}



Ta wiedza pozwoli Ci rozwijać projekty:
Zegarek
PWM

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 MCUCRgdy 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!

Wejście - Hello from the other side

Czy jest na sali tłumacz?!

Nasze urządzenie powinno umieć się z nami komunikować. Komunikacja jest ważna. Jak mamy być Agile, skoro komunikacja szwankuje. Mruganie diodą to sposób, w jaki Atmega nas informuje o jakimś istotnym wydarzeniu z jej zapętlonego życia - w najprostszym przypadku o rozkazie zmiany stanu na określonym pinie. Bardzo niskopoziomowy sposób pracy (poziom zmiany napięć) jest "tłumaczony na nasze zmysły" przy pomocy świecącej diody.
 Co musimy zrobić, żeby mikrokontroler zdał sobie sprawę, że oczekujemy od niego podjęcia jakiejś akcji? Przetłumaczyć "nasze" na "jego". Weźmy dotyk. My czegoś dotykamy, procesor wie, że czegoś od niego chcemy.

Input port

W tym celu musimy wykonać pracę odwrotną do załączania diody. Krok pierwszy - konfiguracja pinu jako wejście. Wykorzystajmy ponownie "rejestr kierunkowy".

DDRB &= ~(1 << PB3);


Gdy pin jest skonfigurowany jako wejście, rejestr PORTx służy do załączania rezystora podciągającego do zasilania. Jakkolwiek zagmatwanie to nie brzmi - włączmy to, przyda się :)

PORTB |= (1<<PB3);


Teraz, żeby odczytać stan pinu musimy się dobrać do rejestru PINx. Rejestr PINx możemy sobie zapisać do zmiennej i szukać stanu interesującego nas bitu (0b00000100 odczytane z PINB powie nam, że PB3 znajduje się w stanie wysokim) albo skorzystać ze skróconego i czytelnego zapisu

PINB & (1 << PB3);


który możemy opakować w konstrukcję if, gdyż prędzej czy później użyjemy wiedzy o stanie pinu do podejmowania istotnych dla programu decyzji.

Button

Krok drugi - przetłumaczenie ruchu na zmianę napięcia. W tym celu posłuży nam przycisk, który podpięty do mikrokontrolera i do masy (GND, poziom 0), po naciśnięciu sprawi, że na nóżce mikrokontrolera zmieni się stan. Weźmy najprostszy - tact-switch (to ten typ, który naciskasz długopisem albo szpilką jak chcesz zrestartować Twój stary router).

Transoptor

O transoptorze było już ostatnio - dla przypomnienia - dioda i fototranzystor zalane w jednej obudowie, "załączenie" diody sprawia, że tranzystor zaczyna przewodzić. Izolacja galwaniczna. Ten wdzięczny element możemy wykorzystać też w tym przypadku - wystarczy go podpiąć "odwrotnie". Naciśnięcie przycisku sprawi, że dioda się zaświeci, a działający tranzystor wymusi zmianę napięcia na pinie kontrolera

Kodzik!

Wiedząc, że prawidłowo skonfigurowany pin wejściowy posiada tylko 2 stany (1 i 0) mamy pełne prawo do użycia else. Zatem niech naszą wolą będzie załączenie diody gdy przycisk jest naciśnięty.

#include <avr/io.h>

int main(void)
{
   // pb0 - output
   DDRB |= (1 << PB0);
 
   // pb3 - input
   DDRB &= ~(1 << PB3);
 
   // pull-up for pb3
   PORTB |= (1 << PB3);
    
   // turn off led
   PORTB |= (1 << PB0);
 
   while (1)
   {
      if ((PINB & (1 << PB3)) == 0)
      {
         PORTB &= ~(1 << PB0);
      }
      else
      {
         PORTB |= (1 << PB0);
      }
   }
}


Dlaczego to zadziała?

Gdy nie naciskamy przycisku, na naszym pinie występuje stan wysoki (dzięki jego ekscelencji rezystorowi pull-up - "podciągającemu" - wyrównuje on napięcie na nóżce do napięcia logicznej 1, jeżeli już jesteś jego psychofanem - nie lękaj się! pull-up powróci w kolejnych postach!). Naciskając przycisk zwieramy obwód do masy. Prąd jest leniwym stworzeniem i szuka sobie najłatwiejszej drogi "przepływu" - o jak najmniejszym oporze - więc popłynie przez nasz przycisk - omijając mikrokontroler - a ten z braku przepływu prądu zreflektuje się, że gdzieś tam na jego nóżce jest 0 i skasuje 1 z rejestru PINx odpowiedzialnego za trzymanie tego stanu.


Dlaczego to nie zadziała?

Tyle w teorii. Teraz praktyka. Gdy naciśniemy przycisk, nasza dioda może zacząć nerwowo mrugać. Ale spokojnie, to nie oznacza, że od razu musimy ją wieźć do specjalisty. Co zaszło? Wszelkie mechaniczne połączenia nie są idealne, nie stykają się ze sobą w czasie t0. Ba! Styki naciśniętego przycisku nie zawsze stykają się idealnie, co powoduje przeskoki ładunku elektrycznego, co z kolei zmiany napięcia zauważalne przez nasz mikrokontroler. Zatem wszystko działa w zgodzie z powszechnie znanymi prawami. No OK, ale my nie chcemy, żeby nam ta dioda mrugała. W tym celu należy wyeliminować wspomniane drgania. Jak? Sprzętowo albo programowo.

Drgania zestyków mogą trwać krótko względem czasu, który będziemy trzymać przycisk - dlatego między kolejnymi odczytami stanu pinu możemy chwilkę zaczekać. Czas oczekiwania dobieramy eksperymentalnie - dopasowujemy do najlepszych zasad UX dotyczących naciskania przycisków, jakie nam przyjdą do głowy i okazuje się, że _delay_ms(200); jest w sam raz!  Niekoniecznie... Polecam zabawę z inkrementacją zmiennej i dołożenie do ifa sprawdzenia, czy ta zmienna ma wartość 0.

Rozwiązanie sprzętowe jest bardziej wdzięczne - nie wymusza na procesorze przebywania większości swojego życia w trybie oczekiwania. Każdy by się zirytował - szkoda na to prądu.
Wepnijmy zatem równolegle do przycisku kondensator ceramiczny 100nF. Powinno być o wiele lepiej :)

No dobrze, a co ja z tego będę miał?

...dozgonną sąsiedzką satysfakcję. I możliwość podpięcia czegokolwiek, co zamknie albo otworzy obwód elektryczny. Najprostszy przykład - zamontujmy przycisk we framudze drzwi. W momencie, gdy drzwi są zamknięte, układ jest zwarty, wszystko działa jak należy, gdy ktoś otworzy drzwi, nasz procesor powinien rozpocząć procedurę informowania o włamaniu - zaświecić wszystkim co ma i zacząć krzyczeć na lewo i prawo, że ktoś go próbuje ukraść.


Ta wiedza pozwoli Ci rozwijać projekty:
Alarm
Klawiatura

środa, 2 marca 2016

Wyjście - Blink World!

Akcja i reakcja

Człowiek łatwo przyzwyczaja się do luksusu. Pisząc kodzik, którego zadaniem będzie wykonanie jakichś mniej lub bardziej skomplikowanych operacji, nie zawracamy sobie za bardzo głowy tym, w jaki sposób dostrzeżemy wynik tej operacji. Siedzimy przed monitorem, na którym wszystko zobaczymy. Build, Run i wszystko widać. Z mikrokontrolerami jest troszeczkę ciężej. Na początku nie mamy ani kawałka wyświetlacza albo połączenia poprzez port szeregowy. Jedyne, do czego mamy dostęp, do pamięć. Czas zrobić coś, żeby wyniki operacji wykonanych na pokładzie Atmegi były dla nas widoczne gołym okiem


Port

Patrząc na naszego procka, ciężko nie zauważyć "nóżek". Korzystając z noty katalogowej z łatwością odnajdziemy ich opisy. Część z nich posiada opis Pxn - gdzie x to litera od A do D (do F w przypadku Atmegi 128), n numer pinu od 0 do 7. Każdy port ma przypisany swój własny rejestr konfiguracyjny i rejestr odpowiedzialny za sterowanie napięciem na konkretnej nóżce.
Każdy pin z osobna możemy skonfigurować jako pin wyjściowy, wejściowy lub 3-stanowy (0, 1 i High-Z - stan wysokiej impedancji, nieustalony) . Skupmy się na tym pierwszym.

Konfiguracja portów

Żeby skonfigurować port jako wyjście, do rejestru odpowiadającemu naszemu portowi musimy wpisać 1 na pozycji odpowiadającej upatrzonej nóżce...
Aby pin 0 portu B był ustawiony jako wyjście:

DDRB |= (1 << PB0);


Korzystając z operatora przesunięcia bitowego i definicji pinu 0 dostępnej z poziomu biblioteki avr io ustawiamy wpisujemy 1 na pozycję odpowiadającą pinowi do rejestru kierunkowego (Data Direction Register)

Teraz możemy sterować stanem naszego pinu.

PORTB |= (1 << PB0);
PORTB &= ~(1 << PB0);


Wpisując 1 do rejestru PORTB ustawiamy stan wysoki na pinie, wpisując 0, ustawiamy stan niski. Czym są te stany? Niczym innym jak wartościami napięcia, jakie pojawią się na naszej nóżce. Stan wysoki będzie odpowiadał napięciu około napięcia zasilania, stan niski - ok 0.

int main()
{

   DDRB |= (1 << PB0);
   PORTB |= (1 << PB0);

   while (1)
   {

   }
}


Wgrywamy kawałek kodu do procka.

LED

Teraz multimetry w dłoń i sprawdzamy, czy na pinie 0 portu B mamy napięcie zbliżone do napięcia zasilania! Prawdopodobnie mamy. Ale czy za każdym razem będziemy ślęczeć z multimetrem? Nie. Z pomocą przyjdzie nam LED. Świecąca dioda, którą opatrzywszy w rezystor podepniemy do naszego mikrokontrolera.

Kierunek ma znaczenie

Jeżeli podepniemy diodę tak, że krótsza nóżka będzie bliżej mikrokontrolera - by ją zaświecić, trzeba będzie posłać na nią "0" - stan niski.
Jeżeli podepniemy diodę tak, że dłuższa nóżka będzie bliżej mikrokontrolera - by ją zaświecić, trzeba będzie posłać na nią "1" - stan wysoki.

Użyj rezystora - dioda posłuży dłużej

LED potrzebuje około 20mA, żeby ładnie świecić. Nasz Port może "wystawić" aż 100mA. Przy podpiętej 1 diodzie do 1 nóżki, może tam popłynąć prąd o właśnie takim natężeniu. Pięciokrotnie wyższy prąd nie działa dobrze na naszą diodę - zaczyna się po prostu palić. Jak temu zapobiec? Włączyć do walki ruch oporu!

Rezystancja

źródło: http://www.electronics.dit.ie/staff/tscarff/DT089_Physical_Computing_1/LEDS/Leds.htm 
Wspomagając się wykresami możemy wywnioskować, że zielona dioda zaświeci najładniej, gdy dotrze do niej prąd o natężeniu 20mA, a spadek napięcia wyniesie ok. 3.5V. Zasilając naszą Atmegę napięciem 5V, właśnie takie (mniej więcej) napięcie otrzymamy, wykonując instrukcję  PORTB |= (1 << PB0); Jaki rezystor należy zastosować?
Wróćmy na moment na lekcję fizyki. Prawo Ohma. Prosty wzór R = U/I [V/A]. Na naszym "świecącym" układzie wartość U to 5V. Oczekiwany prąd I - 20mA. Na wartość U złożą się napięcie na diodzie i napięcie na rezystorze - oczekiwane napięcie na diodzie znamy - wynosi 3.5V. 5 - 3.5 = 1.5. Podzielmy tę wartość przez 20, pamiętając o jednostkach! 1.5/0,020 = 75 Om.
I co teraz? Potrzebujemy rezystor o takim oporze. W każdym sklepie z rezystorami powinniśmy znaleźć interesujący nas element. A co jeśli okaże się, że nie posiadamy takiego? Weź wyższy - kolejny w szeregu - powinien zadziałać :)

Za te słowa będę się pewnie smażył w elektronicznym piekle.. ale po pewnym czasie przestaniesz zwracać uwagę na to, jaką diodę podpinasz i jaki rezystor do niej dobierasz. Chwycisz pierwszy lepszy rezystor, podepniesz diodę, sprawdzisz, czy świeci i zaczniesz robić ciekawsze rzeczy. Matematyka przyda się, gdy prototyp trzeba będzie przerobić w coś, co ma posłużyć dłużej bez większej ingerencji... Czy to nie brzmi znajomo?

Reasumując:
Q: Co się stanie, gdy rezystor o zbyt niskiej wartości?
A: Dioda zwyczajnie szybciej się zużyje (spali).
Q: Co się stanie, gdy dam rezystor o zbyt wysokiej wartości?
A: Dioda nie zaświeci.


Gdy 100mA to za mało

Jak już wymasterujemy świecenie diodą na wszystkie możliwe sposoby może pojawić się pytanie - i co dalej? Przecież nie podepnę mojego urządzenia do Internetu tylko po to, żeby świecić diodą?
Chcąc wysterować coś więcej niż proste światełko, przyda nam się wiedza o kolejnym elemencie elektronicznym - tranzystorze. Typów tranzystorów jest multum. Skupmy się na jednym NPN. Tranzystor posiada 3 nóżki - bazę, kolektor i emiter (na schemacie B, C i E). Wykorzystamy jego moce, by móc załączać/wyłączać wymagające więcej prądu urządzenia (np. buzzer, przekaźnik albo bagatela silnik DC!). Do bazy koniecznie podpinamy rezystor (patrz przykład z LED, sprawdź w nocie katalogowej, jaki powinien być prąd bazy i nie przekraczaj go), nóżkę emitera do masy (GND, "-" na płytce stykowej), do kolektora układ, który chcemy sterować (zacznij od podpięcia diody :)). Rezystor przy bazie? To rezystor do nóżki! Uruchamiamy układ  - "1" sprawi, że tranzystor "załączy" podpięty do kolektora układ, "0" wyłączy go.

Izolacja galwaniczna

Mikorkontroler to bardzo wrażliwe urządzenie i podpinanie do niego czegokolwiek mniej lub bardziej bezpośrednio może wpłynąć negatywnie na jego pracę - w najgorszym wypadku całkowicie ja zatrzymać... na amen. Na szczęście istnieją metody galwanicznej izolacji! Weźmy taki transoptor. Transoptor to układ składający się z diody i fototranzystora (?!). Jak to działa? Podajemy napięcie na diodę, ta świecąc na fototranzystor "załącza" go lub "wyłącza". Czy można prościej? Proszę bardzo. Weź do ręki pilot od telewizora. W jego górnej części znajdziesz diodę na podczerwień (odkrytą lub zasłoniętą przezroczystym plastikiem w kolorach zbliżonych do czerwonego). Poczuj jedność z pilotem - Ty jesteś mikrokontrolerem i naciskając Power na pilocie, kierujesz go w stronę telewizora i sprawiasz, że telewizor się załącza. Czy istnieje połączenie fizyczne między Tobą a telewizorem? Nie. Czy masz wpływ na działanie telewizora? Tak! Proste, czyż nie?
Podobnie działa przekaźnik - z tą różnicą, że tu wykorzystujemy zjawisko elektromagnetyczne. To tak jakbyś zamiast naciskania przycisku, dokładał jakąś metalową część do obwodu (włączał światło w pokoju za pomocą tego fancy klawisza na ścianie).


Dużo treści... a gdzie kodzik?!

Do pinów 0 i 1 portu B podpiąłem 2 diody.

#define F_CPU 8000000UL
#include <util/delay.h>
#include <avr/io.h>

int main()
{
   DDRB |= (1 << PB0) | (1 << PB1);

   PORTB |= (1 << PB0);
   PORTB &= ~(1 << PB1);

   while (1)
   {
         PORTB ^= (1 << PB0);
         PORTB ^= (1 << PB1);

         _delay_ms(500);
   }
}

Do czego można wykorzystać ten kawałek kodu?
A gdyby tak rozszyfrować protokół pilota od telewizora, podpiąć diodę IR i sterować telewizorem?

Może się przydać:
Atmega 162 - datasheet


Ta wiedza pozwoli Ci rozwijać projekty:
Sterowanie silnikiem krokowym
Zdalnie sterowany pojazd