Timer1 - 8, 9 a 10-bit PWM

Zápisník experimentátora

Hierarchy: Časovač (timer)

Funkce analogWrite v Arduine podporuje pouze 8bitový mód. Na timeru1 si můžete zapnout i 9 a 10-bitový mód, který vám poskytne vyšší rozlišení pro PWM signál. V tomto článku si ukážeme, jak se to dá udělat.

Použité součástky

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

Elektrický obvod je v tomto případě jednoduchý. Potřebujete pouze Arduino a 2 LED a 2 rezistory. Zapojte si je na piny 9 a 11, aby se nacházely na timeru 1 a 2. Já obvykle používám modré LED a rezistory 1k. To stačí na to, aby LED svítila, ale současně neoslepovala vaše oči.

Napsal jsem čtyři příklady, které objasní nastavení systémových registrů, které ovládají PWM. Všechny příklady obsahují ladící výpisy z registrů, které vypadají přesně tak, jako v datasheetu mikrokontroléru ATmega328P. Podrobnému popisu ladění registrů jsem se věnoval v samostatném článku. Proto nebudu v tomto článku popisovat soubor dump.h, který obsahuje zdrojový kód na vizualizaci obsahu systémových registrů.

  1. Výpis systémových registrů na timeru 1 a 2 tak, jak je nastavuje Arduino při startu 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. Nastavení módu 3 a nejmenší hodnoty PWM.
  3. Timer1 fast PWM 8-bit. Nastavení módu 5 a nejmenší hodnoty PWM.
  4. Timer1 fast PWM 8, 9 a 10-bitové a srovnání s timer2. Na obou timerech se nastavuje nejmenší hodnota, což umožňuje porovnat jas LED. Při vyšším rozlišení je jasně vidět, že LED svítí méně. Nastavení módu 5, 6 a 7.

Příklad 1 - Výpis systémových registrů

První příklad slouží k tomu, abychom zjistili, jak Arduino nastaví registry po startu. Čili ještě před funkcemi setup a loop. Když se ponoříte do zdrojových kódů funkce (1, 2), najdete tam množství podmíněného kódu, jehož smysl není vždy zcela jasný. Je to tak proto, že ve funkci se zohledňuje větší množství mikrokontrolérů, které stejnou funkci nastavují pomocí různých bitů v různých registrech. Nastavil jsem nejmenší hodnoty PWM na pinech 9 a 11, aby se dal provnať jas obou LED. Rozdíl je pouhým okem v podstatě nepostřehnutelný.

#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. Porovnáním výpisu s popisem v datasheetu a s popisem ve zdrojovém kódu Arduina vidíme, že na obou timerech se nastavuje PWM mód phase correct s prescalerem 64. Tento mód nastavují kvůli lepšímu ovládání motorů. Pro použití s LED to smysl nemá a proto si budeme v dalších příkladech přepínat 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

Příklad 2 - Timer2 fast PWM

Přepnutí módu z phase correct PWM na fast PWM. Změna je jen drobná. Mění se pouze jeden bit v registru. Timer2 podporuje pouze 8-bitové módy, takže vizuálně jsme nedosáhli žádnou změnu oproti předchozímu stavu. Ale aspoň jsme si ukázali, že umíme pomocí nastavení bitů ovládat systémové registry.

#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 porovnat s datasheetem a ukazuje nám, že jsme správně přepnuli 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

Příklad 3 - Timer1 fast PWM

Přepnutí módu na timeru1. Opět jen měníme phase correct PWM na fast PWM. V tomto případě jsem nechal nastavené obě LED, abychom mohli vizuálně porovnat, zda svítí přibližně stejně. V tomto příkladu je vidět, že bity nastavení módu se nacházejí ve dvou registrech. Musíme je správně nastavit, abychom dosáhli správný výsledek.

A když si teď po sobě kontroluji tento program, vidím, že je v něm drobná chyba. Zapomněl jsem do výpisu registru TCCR1B přidat bit WGM13. V tomto případě to nevadí, protože tento bit se používá až při vyšších módech a ty jsem v příkladu 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ět kontrola podle datasheetu, zda jsme všechno správně 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

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

Poslední příklad nám ukazuje, jak se minimální hodnota v registru OCR1A projeví v případě změny módu PWM. Čím vyšší bitové rozlišení, tím méně LED dioda svítí. V příkladu se každé dvě sekundy změní mód a výsledný jas LED si můžete porovnat se stále nezměněnou hodnotou na druhé 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é dvě sekundy se změní mód a na sériový port se vypíše obsah registrů.

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 se nachází na serveru GitHub.



Video



17.06.2020


Menu