Timer1 - 8, 9 a 10-bit PWM

Zápisník experimentátora

Hierarchy: Časovač (timer)

Funkcia analogWrite v Arduine podporuje iba 8-bitový mód. Na timeri1 si možete zapnúť aj 9 a 10-bitový mód, ktorý vám poskytne vyššie rozlíšenie pre PWM signál. V tomto článku si ukážeme, ako sa to dá urobiť.

Použité súčiastky

  • Arduino Pro Mini {linkArduino}
  • Breadboard {linkBreadBoard}
  • LED {linkLed}
  • Rezistor 1k {linkR}

Elektrický obvod je v tomto prípade jednoduchý. Potrebujete iba Arduino a 2 LED a 2 rezistory. Zapojte si ich na piny 9 a 11, aby sa nachádzali na timeri 1 a 2. Ja obvykle používam modré LED a rezistory 1k. To stačí na to, aby LED svietila, ale súčasne neoslepovala vaše oči.

Napísal som štyri príklady, ktoré objasnia nastavenie systémových registrov, ktoré ovládajú PWM. Všetky príklady obsahujú ladiace výpisy z registrov, ktoré vyzerajú presne tak, ako v datasheete mikrokontroléra ATmega328P. Podrobnému popisu ladenia registrov som sa venoval v samostatnom článku. Preto nebudem v tomto článku popisovať súbor dump.h, ktorý obsahuje zdrojový kód na vizualizáciu obsahu systémových registrov.

  1. Výpis systémových registrov na timeri 1 a 2 tak, ako ich nastavuje Arduino pri štarte programu. Timer1 má nastavený mód 1 (PWM, phase correct, 8-bit, prescaler 64). Timer2 má nastavený mód 1 (PWM, phase correct, prescaler 64).
  2. Timer2 fast PWM 8-bit. Nastavenie módu 3 a najmenšej hodnoty PWM.
  3. Timer1 fast PWM 8-bit. Nastavenie módu 5 a najmenšej hodnoty PWM.
  4. Timer1 fast PWM 8, 9 a 10-bitové a porovnanie s timer2. Na oboch timeroch sa nastavuje najmenšia hodnota, čo umožňuje porovnať jas LED. Pri vyššom rozlíšení je jasne vidno, že LED svieti menej. Nastavenie módu 5, 6 a 7.

Príklad 1 - Výpis systémových registrov

Prvý príklad slúži na to, aby sme zistili, ako Arduino nastaví registre po štarte. Čiže ešte pred funkciami setup a loop. Keď sa ponoríte do zdrojových kódov funkcie (1, 2), nájdete tam množstvo podmieneného kódu, ktorého zmysel nie je vždy úplne jasný. Je to tak preto, lebo vo funkcii sa zohľadňuje väčšie množstvo mikrokontrolérov, ktoré rovnakú funkciu nastavujú pomocou rôznych bitov v rôznych registroch. Nastavil som najmenšie hodnoty PWM na pinoch 9 a 11, aby sa dal provnať jas oboch LED. Rozdiel je voľným okom v podstate nepostrehnuteľný.

#include "dump.h"

DREG(TCCR2A, COM2A1, COM2A0, COM2B1, COM2B0, RESERVED, RESERVED, WGM21, WGM20)
DREG(TCCR2B, FOC2A, FOC2B, RESERVED, RESERVED, WGM22, CS22, CS21, CS20)
DREG(TCCR1A, COM1A1, COM1A0, COM1B1, COM1B0, RESERVED, RESERVED, WGM11, WGM10)
DREG(TCCR1B, FOC1A, FOC1B, RESERVED, RESERVED, WGM12, CS12, CS11, CS10)

void setup() {
  pinMode(11, OUTPUT); // TIMER2A
  pinMode(9, OUTPUT);  // TIMER1A
  analogWrite(11, 1);  // minimal PWM value
  analogWrite(9, 1);   // minimal PWM value

  Serial.begin(9600);
  Serial.println("PWM modes");

  // TIMER2
  Serial << _(TCCR2A);
  Serial << _(TCCR2B);
  DUMPVAL(OCR2A)
  DUMPVAL(OCR2B)
 
  // TIMER1
  Serial << _(TCCR1A);
  Serial << _(TCCR1B);
  DUMPVAL(OCR1A)
  DUMPVAL(OCR1B)
}

void loop() {
}

Výpis z programu. Porovnaním výpisu s popisom v datasheete a s popisom v zdrojovom kóde Arduina vidíme, že na oboch timeroch sa nastavuje PWM mód phase correct s prescalerom 64. Tento mód nastavujú kvôli lepšiemu ovládaniu motorov. Pre použitie s LED to zmysel nemá a preto si budeme v ďalších príkladoch prepínať mód na fast PWM.

PWM modes
TCCR2A=10000001: COM2A1 WGM20
TCCR2B=100: CS22
OCR2A=1
OCR2B=0
TCCR1A=10000001: COM1A1 WGM10
TCCR1B=11: CS11 CS10
OCR1A=1
OCR1B=0

Príklad 2 - Timer2 fast PWM

Prepnutie módu z phase correct PWM na fast PWM. Zmena je len drobná. Mení sa len jeden bit v registri. Timer2 podporuje iba 8-bitové módy, takže vizuálne sme nedosiahli žiadnu zmenu oproti predchádzajúcemu stavu. Ale aspoň sme si ukázali, že vieme pomocou nastavenia bitov ovládať systémové registre.

#include "dump.h"

DREG(TCCR2A, COM2A1, COM2A0, COM2B1, COM2B0, RESERVED, RESERVED, WGM21, WGM20)
DREG(TCCR2B, FOC2A, FOC2B, RESERVED, RESERVED, WGM22, CS22, CS21, CS20)
 
void setup() {
  pinMode(11, OUTPUT); // TIMER2A
  analogWrite(11, 1);  // minimal PWM value

  Serial.begin(9600);
  Serial.println("PWM modes");
 
  // TIMER2
  Serial << _(TCCR2A);
  Serial << _(TCCR2B);
  DUMPVAL(OCR2A)
  DUMPVAL(OCR2B)

  Serial.println("TIMER2 Mode 3: Fast PWM");
  TCCR2A = _BV(COM2A1) | _BV(WGM21) | _BV(WGM20);
  Serial << _(TCCR2A);
  Serial << _(TCCR2B);
  DUMPVAL(OCR2A)
  DUMPVAL(OCR2B)
}

void loop() {
}

Výpis z programu. Výpis si môžete porovnať s datasheetom a ukazuje nám, že sme správne prepli bit WGM21.

PWM modes
TCCR2A=10000001: COM2A1 WGM20
TCCR2B=100: CS22
OCR2A=1
OCR2B=0
TIMER2 Mode 3: Fast PWM
TCCR2A=10000011: COM2A1 WGM21 WGM20
TCCR2B=100: CS22
OCR2A=1
OCR2B=0

Príklad 3 - Timer1 fast PWM

Prepnutie módu na timeri1. Opäť len meníme phase correct PWM na fast PWM. V tomto prípade som nechal nastavené obe LED, aby sme mohli vizuálne porovnať, či svietia približne rovnako. V tomto príklade je vidno, že bity nastavenia módu sa nachádzajú v dvoch registroch. Musíme ich správne nastaviť, aby sme dosiahli správny výsledok.

A keď si teraz po sebe kontrolujem tento program, vidím, že je v ňom drobná chyba. Zabudol som do výpisu registra TCCR1B pridať bit WGM13. V tomto prípade to nevadí, pretože tento bit sa používa až pri vyšších módoch a tie som v príklade nepoužil.

#include "dump.h"

DREG(TCCR1A, COM1A1, COM1A0, COM1B1, COM1B0, RESERVED, RESERVED, WGM11, WGM10)
DREG(TCCR1B, FOC1A, FOC1B, RESERVED, RESERVED, WGM12, CS12, CS11, CS10)
 
void setup() {
  pinMode(9, OUTPUT);  // TIMER1A
  analogWrite(9, 1);   // minimal PWM value
  pinMode(11, OUTPUT); // TIMER2A
  analogWrite(11, 1);  // minimal PWM value

  Serial.begin(9600);
  Serial.println("PWM modes");
 
  // TIMER1
  Serial << _(TCCR1A);
  Serial << _(TCCR1B);
  DUMPVAL(OCR1A)
  DUMPVAL(OCR1B)

  Serial.println("TIMER1 Mode 5: Fast PWM 8-bit");
  TCCR1A = (1 << WGM10) | (1 << COM1A1);
  TCCR1B = (1 << WGM12) | (1 << CS11) | (1 << CS10);
  Serial << _(TCCR1A);
  Serial << _(TCCR1B);
  DUMPVAL(OCR1A)
  DUMPVAL(OCR1B)
}

void loop() {
}

Výpis z programu. A opäť kontrola podľa datasheetu, či sme všetko správne nastavili.

PWM modes
TCCR1A=10000001: COM1A1 WGM10
TCCR1B=11: CS11 CS10
OCR1A=1
OCR1B=0
TIMER1 Mode 5: Fast PWM 8-bit
TCCR1A=10000001: COM1A1 WGM10
TCCR1B=1011: WGM12 CS11 CS10
OCR1A=1
OCR1B=0

Príklad 4 - 8, 9 a 10-bitové PWM

Posledný príklad nám ukazuje, ako sa minimálna hodnota v registri OCR1A prejaví v prípade zmeny módu PWM. Čím vyššie bitové rozlíšenie, tým menej LED dióda svieti. V príklade sa každé dve sekundy zmení mód a výsledný jas LED si môžete porovnať so stále nezmenenou hodnotou na druhej LED.

#include "dump.h"

DREG(TCCR1A, COM1A1, COM1A0, COM1B1, COM1B0, RESERVED, RESERVED, WGM11, WGM10)
DREG(TCCR1B, FOC1A, FOC1B, RESERVED, RESERVED, WGM12, CS12, CS11, CS10)
 
void setup() {
  pinMode(9, OUTPUT);  // TIMER1A
  analogWrite(9, 1);   // minimal PWM value
  pinMode(11, OUTPUT); // TIMER2A
  analogWrite(11, 1);  // minimal PWM value

  Serial.begin(9600);
  Serial.println("PWM modes");  
}

void loop() {
  Serial.println("TIMER1 Mode 5: Fast PWM 8-bit");
  TCCR1A = (1 << WGM10) | (1 << COM1A1);
  TCCR1B = (1 << WGM12) | (1 << CS11) | (1 << CS10);
  Serial << _(TCCR1A);
  Serial << _(TCCR1B);
  delay(2000);
  Serial.println("TIMER1 Mode 6: Fast PWM 9-bit");
  TCCR1A = (1 << WGM11) | (1 << COM1A1);
  TCCR1B = (1 << WGM12) | (1 << CS11) | (1 << CS10);
  Serial << _(TCCR1A);
  Serial << _(TCCR1B);
  delay(2000);
  Serial.println("TIMER1 Mode 7: Fast PWM 10-bit");
  TCCR1A = (1 << WGM11) | (1 << WGM10) | (1 << COM1A1);
  TCCR1B = (1 << WGM12) | (1 << CS11) | (1 << CS10);
  Serial << _(TCCR1A);
  Serial << _(TCCR1B);
  delay(2000);
}

Výpis z programu. Každé dve sekundy sa zmení mód a na sériový port sa vypíše obsah registrov.

PWM modes
TIMER1 Mode 5: Fast PWM 8-bit
TCCR1A=10000001: COM1A1 WGM10
TCCR1B=1011: WGM12 CS11 CS10
TIMER1 Mode 6: Fast PWM 9-bit
TCCR1A=10000010: COM1A1 WGM11
TCCR1B=1011: WGM12 CS11 CS10
TIMER1 Mode 7: Fast PWM 10-bit
TCCR1A=10000011: COM1A1 WGM11 WGM10
TCCR1B=1011: WGM12 CS11 CS10

Zdrojový kód

Zdrojový kód sa nachádza na serveri GitHub.



Video



17.06.2020


Menu