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

Brak komentarzy:

Prześlij komentarz