Witam!
Jest to część druga tego artykułu :
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
#include <avr/io.h> //#include <util/delay.h> int main(void) { DDRB |= (1<<PORTB0); // konfiguracja timera GTCCR |= (1<<TSM);// włączenie trybu ustawianie timera GTCCR |= (1<<PSR0);// reset ustawień i licznika TCCR0A |= (1<<COM0A0)|(1<<WGM01);// ustawienia trybu pracy TCCR0B |= (1<<CS00)|(1<<CS01);// ustawienia preskalera OCR0A = 10;//ustawienia rejestru wejściowego komparatora GTCCR &= ~(1<<TSM);// Wyjście z trybu ustawiania while(1) { } return 0; |
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 :
1 2 3 4 5 |
void gen_tone(uint8_t tone) { OCR0A=tone; TCCR0B |= (1<<CS02);// ustawienia preskalera } |
Funkcja wyciszająca głośnik :
1 2 3 4 5 6 7 |
void mute() { GTCCR |= (1<<TSM); TCCR0B &= ~(1<<CS02); TCNT0 = 0; GTCCR &= ~(1<<TSM); } |
Finalny program :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 |
#include <avr/io.h> #include <util/delay.h> void gen_tone(uint8_t tone) // funkcja generująca ton { OCR0A=tone;// ustawiamy wartość na rejestrze OCR0A TCCR0B |= (1<<CS02);// ustawienia preskalera } void mute() { GTCCR |= (1<<TSM);// włączenie trybu synchronizacji TCCR0B &= ~(1<<CS02);// zatrzymanie zegara timera TCNT0 = 0;// zerowanie licznika timera GTCCR &= ~(1<<TSM);// wyjście z trybu synchronizacji } int main(void) { DDRB |= (1<<PORTB0);// pin głośnika jako wyjście // konfiguracja timera GTCCR |= (1<<TSM);// włączenie trybu ustawianie timera GTCCR |= (1<<PSR0);// reset ustawień i licznika TCCR0A |= (1<<COM0A0)|(1<<WGM01);// ustawienia trybu pracy TCCR0B |= (1<<CS02);// ustawienia preskalera GTCCR &= ~(1<<TSM);// wyjście z trybu synchronizacji while(1) { gen_tone(10); _delay_ms(500); mute(); _delay_ms(50); gen_tone(10); _delay_ms(500); mute(); _delay_ms(50); gen_tone(10); _delay_ms(500); mute(); _delay_ms(50); gen_tone(12); _delay_ms(350); mute(); _delay_ms(50); gen_tone(8); _delay_ms(150); mute(); _delay_ms(50); gen_tone(10); _delay_ms(500); mute(); _delay_ms(50); gen_tone(12); _delay_ms(350); mute(); _delay_ms(50); gen_tone(8); _delay_ms(150); mute(); _delay_ms(50); gen_tone(10); _delay_ms(500); mute(); _delay_ms(500); // koniec pierwszej zwrotki gen_tone(7); _delay_ms(500); mute(); _delay_ms(50); gen_tone(7); _delay_ms(500); mute(); _delay_ms(50); gen_tone(7); _delay_ms(500); mute(); _delay_ms(50); gen_tone(6); _delay_ms(300); mute(); _delay_ms(50); gen_tone(8); _delay_ms(150); mute(); _delay_ms(50); gen_tone(10); _delay_ms(500); mute(); _delay_ms(50); gen_tone(12); _delay_ms(350); mute(); _delay_ms(50); gen_tone(8); _delay_ms(150); mute(); _delay_ms(50); gen_tone(10); _delay_ms(500); mute(); _delay_ms(500); //end mute(); _delay_ms(5000); } return 0; } |
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 :
1 2 3 4 5 6 7 8 9 10 11 12 13 |
void EEPROM_write(unsigned char ucAddress, unsigned char ucData) { while(EECR & (1<<EEPE)){}// poczekaj dopóki bit EEPE nie zostanie wyzerowany przez program (koniec zapisu) EEAR = ucAddress;// ustaw adres EEDR = ucData;// ustaw dane EECR = (0<<EEPM1)|(0<<EEPM0);// ustaw tryb zapisu EECR |= (1<<EEMPE);// bit EEMPE na 1 EECR |= (1<<EEPE);// Bit EEPE na 1, dane zostaną zapisane w ciągu 4 cykli zegara } |
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 :
1 2 3 4 5 6 7 8 9 |
unsigned char EEPROM_read(unsigned char ucAddress) { while(EECR & (1<<EEPE)){}// poczekaj aż poprzednia operacja zapisu nie zostanie zakończona EEAR = ucAddress;// ustaw adres EECR |= (1<<EERE);// ustaw bit odczytu return EEDR;// zwróć wynik } |
I to tyle z teorii pora na program :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
#include <avr/io.h> #include <util/delay.h> void EEPROM_write(unsigned char ucAddress, unsigned char ucData)// funkcja zapisująca dane { while(EECR & (1<<EEPE)){} EEAR = ucAddress; EEDR = ucData; EECR = (0<<EEPM1)|(0<<EEPM0); EECR |= (1<<EEMPE); EECR |= (1<<EEPE); } unsigned char EEPROM_read(unsigned char ucAddress)// funkcja odczytująca dane { while(EECR & (1<<EEPE)){} EEAR = ucAddress; EECR |= (1<<EERE); return EEDR; } void wait_until_relase()// funkcja czekająca dopuki przycisk nie zostanie puszczony { while(bit_is_clear(PINB,4)){} } void clear_led()// funkcja wyłączająca wszystkie LED { PORTB &= ~((1<<0)|(1<<1)|(1<<2)|(1<<3)); } int main(void) { // ustawienia pinów DDRB |= (1<<0)|(1<<1)|(1<<2)|(1<<3);// ustaw diody jako wyjścia PORTB |= (1<<4);// ustaw stan wysoki na pinie przycisku //początkowy stan LED unsigned char num=EEPROM_read(0);// odczytaj stan LED z EEPROM PORTB |= (1<<num);// ustaw ten stan jako początkowy while(1) { if(bit_is_clear(PINB,4))// obsługa przycisku { unsigned char num=EEPROM_read(0);// odczytaj aktualny stan z pamięci eeprom num++;// zwiększ stan o 1 if(num==4)// powrót do początku jeśli wyszliśmy poza zakres { num=0; } clear_led();// wyczyść stan LED PORTB |= (1<<num);// ustaw stan wysoki na wybranej diodzie _delay_ms(100);// zabezpieczenie przed drganiami styków wait_until_relase();// zaczekaj dopuki przycisk nie zostanie puszczony _delay_ms(100);// zabezpieczenie przed drganiami styków EEPROM_write(0,num);// zapisz stan diód do eeprom } } return 0; } |
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 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#include <avr/io.h> #include <util/delay.h> int main(void) { DDRB |= (1<<0); while(1) { PORTB |= (1<<0); _delay_ms(10000); PORTB &= ~(1<<0); _delay_ms(10000); } return 0; } |
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 :
1 |
PRR |= (1<<3)|(1<<2)|(1<<1)|(1<<0); |
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 :
1 |
__asm__ __volatile__ ( "sleep" "\n\t" :: ); |
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 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
#include <avr/io.h> #include <util/delay.h> #include <avr/interrupt.h> void enter_sleep() { MCUCR |= (1<<SM1);// ustawienie trybu uśpienia na Power Down WDTCR |= (1<<WDP3)|(1<<WDP0);// ustawienia preskalera watchdoga WDTCR |= (1<<WDIE);// ustawienie trybu pracy watchdoga SREG |=(1<<7);// uruchomienie przerwań MCUCR |= (1<<SE);// wejście w tryb uśpienia __asm__ __volatile__ ( "sleep" "\n\t" :: );// instrukcja sleep // mija 8 sekund zanim watchdog wybudzi mikrokontroler MCUCR &= ~(1<<SE);// wyłączenie uśpienia WDTCR &= ~(1<<WDIE);// wyłączenie watchdoga } ISR(WDT_vect) { // nic nie rób } int main(void) { DDRB |= (1<<0); PRR |= (1<<3)|(1<<2)|(1<<1)|(1<<0); while(1) { PORTB |= (1<<0); enter_sleep(); PORTB &= ~(1<<0); enter_sleep(); } return 0; } |
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 :)
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!
Dzięki (: