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