5 prostych projektów AVR w C część 2

5 prostych projektów AVR w C część 2

Witam!

Jest to część druga tego artykułu :

5 prostych projektów AVR w C część 1

W tej część znajdują się projekty :

  • Grające Attiny
  • Zapis stanu LED do EEPROM
  • Długowieczne miganie

3 Grające Attiny

Pomysł na ten projekt powstał gdy programowałem przerwania zegarowe w projekcie z mruganiem sterowanym przyciskiem. Chodzi o wykorzystanie opcji kontroli pinu sygnałem z komparatora do generowania różnych tonów. A następnie wykorzystaniu tych tonów do zagrania jakiejś prostej melodii.

Schemat :

Schemat ten jest kolejną modyfikacją schematu bazowego z początku artykułu.

Z tym, że tym razem do pinu PB0 podpięta jest baza tranzystora BD911 który bezpośrednio kontroluje prąd płynący przez głośnik SP1.

Kiedy na pinie PB0 panuje stan wysoki, prąd płynie przez dwa rezystory 220 omów ( razem 440 omów) do bazy tranzystora, powoduje to, że zaczyna również płynąć prąd z kolektora do emitera. Zamyka to obwód głośnika i powoduje wychylenie membrany. Kiedy natomiast na pinie PB0 panuje stan niski, nic takiego się nie dzieje i obwód głośnika pozostaje otwarty (membrana wraca do położenia początkowego). W ten sposób podając sygnał o pewnej częstotliwości na pin PB0 można uzyskać dźwięk o tej samej częstotliwości z głośnika.

Jedyne więc co musimy zrobić to wygenerować sygnał o odpowiedniej częstotliwości

Do wygenerowania tego sygnału postanowiłem wykorzystać timer 0 oraz komparator A. Jak już pisałem przy poprzednim projekcie, istnieje możliwość kontrolowania wyjścia cyfrowego za pomocą komparatora.

Jak widać sygnał z komparatora może zmieniać stan pinu na niski, wysoki lub przełączać stan pinu. Komparator A może kontrolować pin OC0A (PB0).

W przypadku tego projektu najlepsza będzie opcja 2 czyli zmiana stanu pinu. Od razu uzyskamy bowiem sygnał prostokątny o częstotliwości równej połowie częstotliwości sygnału z komparatora.

Prosty kod programu do wygenerowania sygnału:

Notatka : Zamiast numeru bitów w rejestrze można używać też nazw poszczególnych bitów, są one podane w dokumentacji. Znacznie ułatwia to pisanie programu. 

Program ten jest dość prosty.

Najpierw ustawiamy pin do którego podłączony jest głośnik jako wyjście.

Następnie włączamy tryb synchronizacji dla timera 0 oraz resetujemy jego preskaler  i licznik.

W następnym kroku ustawiamy jego tryb pracy na CTC ( licznik jest zerowany przez sygnał z komparatora) oraz ustawiamy, że sygnał z komparatora zmienia stan pinu OCR0A na przeciwny.

W kolejnym kroku ustawiamy preskaler (w tym wypadku na 64).

Na końcu ustawiamy stan rejestru OCR0A na 10 ( do tej wartości będzie porównywana wartość licznika timera).

Następnie wychodzimy z trybu ustawiania timera (w dokumentacji nazywa on się trybem synchronizacji) i wchodzimy w pętlę nieskończoną w której nic więcej nie robimy.

W efekcie na pinie OC0A (PB0) pojawi się sygnał prostokątny o częstotliwości 716 Hz a my usłyszymy ton płynący z głośnika.

Teraz przydałoby się móc regulować tą częstotliwość.

Można ją regulować zmieniając wartość rejestru wejściowego preskalera (OCR0A) oraz wartość preskalera.

Znając te dwie wartości oraz częstotliwość zegara mikrokontrolera można obliczyć częstotliwość wyjściową.

Najpierw należy jednak zrozumieć jak jest generowany sygnał wyjściowy:

Krok 1 to sygnał zegarowy mikrokontrolera. W tym wypadku ma on 1 MHz czyli 1000000 Hz.

Krok 2 to przeskalowanie tego sygnału za pomocą preskalera timera 0. W zależności od ustawień, częstotliwość może być ta sama lub zostać podzielona przez pewną liczbę. Nazwijmy ją K.

Przeskalowany sygnał jest zliczany przez licznik timera 0. Liczy on cykle tego sygnału. Długość jednego cyklu to okres sygnału.

Komparator porównuje liczbę zliczoną przez licznik timera 0 a następnie kiedy liczba zawarta w licznika zgadza się z liczbą zawartą w rejestrze OCR0A, zmienia stan pinu na przeciwny. W czasie zmiany zerowany jest również licznik timera.

Tak więc najpierw należy obliczyć częstotliwość i okres sygnału który zasila zegar mikrokontrolera.

To akurat jest proste:

Częstotliwość to F=Fz/K

Okres to 1/F czyli 1/(Fz/K)

F to częstotliwość sygnału zasilającego timer. Fz to częstotliwość zegara mikrokontrolera a K to liczba preskalera. Należy jednak zwrócić uwagę, że gdy nie ma preskalowania to liczba ta jest równa Fz.

Teraz pora na częstotliwość z jaką przełącza się komparator, przy OCR0A na 0, częstotliwość jest równa częstotliwości sygnału wejściowego timera czyli F.

Jeśli natomiast ta wartość jest różna od 0 to wtedy do okresu jest dodawany czas N*(1/F). Przy czym N jest równe liczbie która która jest ustawiona w rejestrze OCR0A.  F to częstotliwość sygnału zegarowego timera.

Czyli wzór na okres przełączania  komparatora to      Ok = 1/F + N*(1/F) = 1/F + N/F = (1+N)/F

Czyli częstotliwość przełączania komparatora to    Fk = 1/Ok =1/(1+N)/F

Jednak ponieważ na jeden okres fali wyjściowej przypadają 2 przełączenia komparatora więc wzór na częstotliwość wyjściową to :

(1/((1+N)/(Fz/K)))/2imo, że głośnik nie będzie generował dźwięk.

Przy czym Fz to częstotliwość zegara mikrokontrolera, N to wartość rejestru OCR0A a K to wartość preskalera.

Tak więc na przykład dla wartości N=10, K=8 I Fz =1000000 częstotliwość wyjściowa to :

(1/((1+10)/(1000000/8)))/2 ~ 5681,818181818 Hz

Realna częstotliwość to ok 5727 Hz.

Bazując na wzorze obliczyłem maksymalną i minimalną częstotliwość sygnału wyjściowego w zależności od ustawień preskalera.

Dla 8 : Min : 244 Hz Max : 62500 Hz

Dla 64 : Min : 31Hz Max : 7813 Hz

Dla 256 : Min : 8Hz Max : 1953 Hz

Dla 1024 : Min : 2Hz Max : 488 Hz

Najbardziej optymalną opcją wydaje się preskaler 256.

Pozostaje jeszcze kwestia generowania ciszy. Ten sposób generowania tonów działa niezależnie od programu mikrokontrolera więc nie jest to po prostu kwestia przerwania pętli.

Można to zrobić zatrzymując timer. W tym celu należy ustawić go w w trybie synchronizacji a następnie wyzerować wszystkie bity ustawiające preskaler (to zatrzymuje zegar).

W tym trybie możemy również bezpiecznie wyzerować licznik timera co zapobiegnie zmianie zaburzeniu pierwszego okresu fali po wznowieniu generowania tonu.

Pora na kod :

Funkcja generująca ton :

Funkcja wyciszająca głośnik :

Finalny program :

Niestety nie udało mi się zaprogramować całego marszu imperatora ):

Udało się jednak zagrać pierwsze dwie części.

Brzmi raczej słabo :p

4 Zapis stanu 4 LED do EEPROM

Kolejny projekt zrobiłem aby nauczyć się wykorzystywać pamięć EEPROM ( pamięć nieulotna).

Projekt zawiera 3 diody LED oraz przycisk który wybiera diodę LED która ma się świecić. Wartość ta jest następnie zapisywana do EEPROM przez co po wyłączeniu i włączeniu zasilania, początkowy stan LEDów będzie taki sam jak przed wyłączeniem.

Schemat tego projektu jest ten sam jak w przypadku projektu z miganiem sterowanym przyciskiem :

Na początku kilka informacji o EEPROM w ATtiny45:

  • Rozmiar : 256 bajtów
  • Żywotność 100 000 cyklów zapisu
  • Zapis pamięci jest wrażliwy na zbyt niskie napięcie. Zapis w takiej sytuacji może doprowadzić do zniszczenia danych które mają być w niej zapisane.

Tak więc może zacznę od początku :

EEPROM składa się z 256 bajtów ponumerowanych od 0 do 255.

Pierwszą rzeczą którą należy zrobić jest ustalić który bit chcemy odczytać lub zapisać, ten krok jest wspólny zarówno  dla odczytu jak i zapisu.

Adres jest przechowywany w dwóch rejestrach :

W przypadku ATtiny 25 adres jest liczbą7 bitową i jest przechowywany w bitach EEAR0 – EEAR6.

W przypadku ATtiny 45 adres jest liczbą 8 bitową i jest przechowywan w całym rejestrze EEARL

W przypadku ATtiny 65 adres jest liczbą 9 bitową i jest przechowywany w rejestrze EEARL ale ostatni bit (EEAR8) jest w rejestrze EEARH.

W pierwszym kroku należy wybrać adres do jakiego chcemy zapisać lub z jakiego chcemy odczytać dane.

Teraz wszystko zależy od tego czy chcemy zapisać czy odczytać dane

Zapis :

Po ustawieniu adresu należy umieścić zapisywany bajt w rejestrze danych :

Następne kroki dotyczą rejestru kontroli  :

Najpierw należy ustawić tryb pracy EEPROM za pomocą bitów EEPM1 i EEPM0.

Tryb pierwszy automatycznie wymaże daną komórkę pamięci a następnie zapisze do niej dane.

Tryb drugi, tylko wymaże daną komórkę pamięci. Jest to przydatne gdy chcemy tylko wymazać pewną część pamięci.

Trzeci tryb tylko zapisze dane do danej komórki pamięci. Warto jednak wiedzieć, że dana komórka musi być wymazana przed zapisem.

Tryby drugi i trzeci mogą być przydatne gdy chcemy oszczędzić czas na zapisywaniu danych, możemy wtedy wymazać cały całą sporą część pamięci gdy mikrokontroler ma taką możliwość czasową a potem zapisaniu danych do tej wymazanej przestrzeni. W takiej sytuacji czas zapisu będzie krótszy o czas potrzebny do wymazania komórki pamięci.

W następnym kroku musimy ustawić jedynkę na bicie EEMPE.  Jego zadaniem jest sprawienie aby ustawienie bitu EEPE da jakiś efekt.

Następnie w ciągu 4 cykli zegara musimy ustawić 1 na bicie EEPE. Jeśli nie zdążymy to wtedy zapisanie jedynki na tym bicie nie da żadnego efektu i zostanie on automatycznie wyczyszczony. Bit ten zostanie również automatycznie wyczyszczony po zakończeniu zapisu.

I to wszystko (:

Warto jeszcze wspomnieć o bicie EERIE który włącza generowanie przerwań o wektorze EE_RDY zawsze kiedy pamięć EEPROM jest  gotowa do zapisu.

Warto jednak pamiętać, że przerwanie jest generowane przez cały czas gdy pamięć jest gotowa do zapisu.

Funkcja zapisująca bajt danych :

Odczyt :

No ale na co nam zapis danych jeśli nie możemy ich odczytać.

Odczytane dane trafiają do rejestru danych EEDR.

Kiedy chcemy odczytać dane w pierwszym kroku należy podać ich adres ( tak samo jak w przypadku zapisu).

W drugim kroku należy ustawić na 1 bit EERE w rejestrze EECR.

I to wszystko, w ciągu 4 cykli zegara wartość danej komórki pamięci zostanie zapisana do EEDR.

Funkcja odczytująca dane :

I to tyle z teorii pora na program :

5 Długowieczne miganie (oszczędzanie energii)

Robiąc projekty zasilane z baterii prędzej czy później natrafimy na konieczność oszczędzania energii. Weźmy na przykład projekt z mruganiem diodą. Pomimo, że zużycie energii przez mikrokontroler jest niewielkie to jednak gdy chcemy uzyskać długi czas pracy na baterii to liczy się każda miliamperogodzina. W tym projekcie pokarzę jak dodać oszczędzanie energii do projektu migającej diody tak aby zredukować zużycie energii do minimum.

Schemat jest ten sam jak w przypadku projektu migającej diody.

Najpierw sprawdźmy co można zrobić żeby zredukować zużycie energii przez ten układ.

Jeśli chodzi o LED to niewiele można zrobić, można jedynie zwiększyć wartość rezystora co ograniczy płynący przez nią prąd.

Jednak to ograniczy również jej jasność.

Jeśli chodzi o mikrokontroler to możemy :

A) Włączyć tryb uśpienia.

B) Wyłączyć zbędne układy mikrokontrolera.

Dostępne tryby oszczędzania energii na mikrokontrolerze Attiny45 :

Tryb Idle w zasadzie tylko zatrzymuje działanie procesora. Wszystkie inne podzespoły działają.

Tryb ADC Noise Reduction to tryb przeznaczony głównie do zmniejszania szumów generowanych przez mikrokontroler podczas pomiarów napięcia za pomocą przetwornika analogowo cyfrowego.

Tryb Power down to najbardziej oszczędny tryb wyłącza on wszystko z wyjątkiem najbardziej niezbędnych układów.

Wspólną cechą tych trybów jest to, że zatrzymują one działanie procesora. Co oznacza, że program nie wykonuje się w trakcie ich trwania.

Dodatkowo z każdego trybu uśpienia, mikrokontroler  musi zostać wybudzony za pomocą przerwania.

Jeśli natomiast chodzi o komponenty które można wyłączyć to możliwości są takie :

  • Timer1
  • Timer0
  • USI – Uniwersalny szeregowy inteface
  • ADC – Przetwornik analogowo cyfrowy.

Pora na zmierzenie poboru prądu układu przed wdrożeniem oszczędzania energii.

Wszystkie pomiary były przeprowadzane przy napięciu zasilania 5V.

Kod przy którym były dokonywane pomiary :

Pobór prądu przy zgaszonej diodzie to 2.7 mA natomiast pobór prądu przy zapalonej diodzie to 14.6 mA.

To dość mało jednak w układach bateryjnych liczy się każdy miliamper.

Na początek spróbujmy wyłączyć zbędne układy.

Za to odpowiada rejestr PPR :

Bit PRTIM1 wyłącza timer 1.

Bit PRTIM0 wyłącza timer 0.

Bit PRUSI wyłącza USI.

Bit PRADC wyłącza ADC.

Ponieważ do prostego migania diodą nie potrzebujemy żadnego z tych układów to postanowiłem wyłączyć je wszystkie :

Pobór mocy spadł do 14.2 mA kiedy dioda jest zapalona oraz 2.3 mA kiedy dioda jest zgaszona.

Czyli zysk to około 0.4 mA.

Teraz pora jednak na wdrożenie trybów uśpienia.

Za tryby uśpienia odpowiada główny rejestr kontrolny mikrokontrolera, MCUCR :

W pierwszym kroku należy wybrać tryb uśpienia w jaki chcemy wejść.

Za wybór trybu uśpienia odpowiadając bity SM0 i SM1.

W tym wypadku chcę uzyskać jak najmniejsze zużycie energii więc postanowiłem użyć trybu Power-down.

W następnym, niezwykle istotnym, kroku należy skonfigurować przerwanie które ma wybudzić mikrontroler z trybu uśpienia.

Jeśli błędnie skonfigurujemy przerwanie to mikrokontroler nie wybudzi się z trybu uśpienia.

W tej tabeli znajdują się informacje na temat dostępnych przerwań które mogą wybudzić mikrokontroler z trybu uśpienia.

Ponieważ projekt ma mrugać diodą więc potrzebujemy przerwania zegarowego które wybudzi mikrokontroler żeby zapalić lub zgasić diodę.

Jednak ponieważ timer 1 i 0 nie funkcjonują w tym trybie uśpienia więc jedyną opcją pozostaje timer Watchdoga. Jego normalnym przeznaczeniem jest reset mikrokontrolera w przypadku jego zawieszenia. Jednak nic nie stoi na przeszkodzie aby wykorzystać go do wybudzenia mikrokontrlera.

Za kontrolę timera watchdoga odpowiada rejestr WDTCR :

Pierwszą rzeczą jaką musimy zrobić jest ustawienie preskalera watchdoga za pomocą bitów WDP :

W przypadku naszego projektu najlepszą opcją będzie opcja 8s.

8 sekundowe opóźnienie pomiędzy zapaleniem i zgaszeniem diody pozwoli na łatwiejsze zmierzenie zużycia energii.

Następnie musimy ustawić tryb pracy watchdoga :

Za konfigurację watchdoga odpowiada bit WDE – Który uruchamia watchdoga w trybie resetowania mikronotrolera.

Oraz bit WDIE który uruchamia watchdoga w trybie generowania przerwania ( o wektorze WDT).

Kiedy bit WDE jest ustawiony wtedy bit WDIE jest automatycznie zmieniany na zero po pierwszym wygenerowaniu przerwania.

Potrzebujemy jednak tylko przerwań więc należy ustawić jedynie WDIE.

Na końcu należy również ustawić globalną flagę przerwań aby włączyć przerwania.

I to wszystko jeśli chodzi o ustawianie przerwań.

Następnym krokiem jest uruchomienie trybu uśpienia, ustawiając bit SE w rejestrze MCUCR.

Niezwłocznie po tym kroku należy wykonać instrukcję  SLEEP.

Należy to zrobić za pomocą linijki :

Funkcja ta powie kompilatorowi, że w tym miejsu w kodzie assemblera należy umieścić instrukcję SLEEP.

I to wszystko.

W tym momencie procesor wejdzie w tryb uśpienia z którego może zostać wybudzony przez przerwanie.

Kod do migania diodą z uśpieniem :

Pobór mocy po wdrożeniu trybu uśpienia oraz wyłączeniu zbędnych układów :

  • 0.006 mA kiedy dioda jest zgaszona. (!!!)
  • 11.5 mA kiedy dioda jest zapalona.

Teraz to się nazywa oszczędzanie energii (:

Inne sposoby aby zmniejszyć zużycie energii przez mikrokontroler to :

  • Zmniejszenie częstotliwości zegara.
  • Zmniejszenie napięcia zasilającego.

 Kilka słów na koniec : 

W załączniku znajduje się  archiwum zawierające archiwum zawierające projekty codeblocks do wszystkich przedstawionych tu programów. Zawiera ono także skompilowane programy gotowe do wgrania.

W razie znalezienia jakiś błędów merytorycznych lub niedociągnięć proszę o napisanie o tym w komentarzach.

Proszę jednak o wyrozumiałość bo jestem w miarę nowy jeśli chodzi o programowanie mikrokontrolerów w C.

No i to tyle :)

Pliki załączone do artykułu:

Ocena: 5/5 (głosów: 7)

Podobne posty

2 komentarzy do “5 prostych projektów AVR w C część 2

  • Dobry Człowieku!
    Przeleciałem Twoje artykuły prędziutko, bo terminy szalone. Ale specjalnie się zalogowałem, żeby Ci podziękować za tak dobrą i potrzebną treść! Właśnie zacząłem grzebać dużo głębiej w RAMPSach i myślę, że czyste C mi się bardzo przyda. A o doświadczeniach transcendentnych nawet nie wspominam :)

    Niech Ci patron w zniżkach wynagrodzi!!!
    Pozdrawiam słonecznie!

    Odpowiedz

Odpowiedz

anuluj

Masz uwagi?