Tuesday, July 10, 2018

Arduino Timer / PWM cheat sheet

Arduino Timer

Arduino UNO (ATmega328p) has three 8bit timers
[INDENT]Timer0 - used for millis() micros() delay()… and is on pin 5, 6
Timer1 - 16bit timer is on pin 9, 10
Timer2 - 8bit timer is on pin 3, 11
[/INDENT]

Arduino Mega has six 8bit timers
[INDENT]Timer0 - millis, micros… and is on pin 4, 13
Timer1 - is on pin 11, 12
Timer2 - is on pin 9, 10
Timer3 - is on pin 2, 3, 5
Timer4 - is on pin 6, 7, 8
Timer5 - is on pin 46, 45, 44
[/INDENT]

Note: Don’t mess with Timer0 since we need to use millis() or micros() for measuring time

Software timer interrupt service routine

Arduino UNO cannot do complex timer like a computer can. It heavily depends on frequency oscillator, ie 16MHz. Then we have divisor to scale down main clock and then 8bit counter to help with the PWM or Timer.

There are 3 vectors for each timer that we can use to set up 3 ISRs

ISR(TIMER2_COMPA_vect){}
ISR(TIMER2_COMPB_vect){}
ISR(TIMER2_OVF_vect){}

Setup Overflow vector ISR to use it like a timer in PC

void setup() {
  TIMSK2 = (TIMSK2 & 0b11111110) | 0b00000001; // set only TOIE bit for overflow vector

  // Fast PWM without reset TCNT2 : WGM02, WGM01, WGM00 = 0,1,1 
  // default is Phase-correct PWM with WGM: 0,0,1
  // TCNT2 will be reset, when it matchs OCR2A, if WGM02 is set to 1
  TCCR2A = (TCCR2A & 0b11111100) | 0b00000011; // set WGM00, WGM01 to 1: xxxxxx11
  TCCR2B = (TCCR2B & 0b11110111) | 0b00000000; // set WGM02 to 0: xxxx0xxx

  // note : prescale bits may be different between timers,
  // divisor = 64,  for Timer2 = xxxxx100, for Timer0 = xxxxx011
  TCCR2B = (TCCR2B & 0b11111000) | 0b00000100; // 3 bits CS2 = xxxxx100 which is divisor 64

  // ISR(TIMER2_OVF_vect) will run every 1ms

  // overflow time = (1/16M)*(2^8)*divisor = 256*64/16000000 = 0.001024s or aprox 1ms
  // overflow time = 2 * (1/16M)*(2^8)*divisor  for Phase-correct PWM
}

ISR(TIMER2_OVF_vect){
  //do something
}

void loop() {

}

Or we can use COMPARE VECTOR A and B which is compare to value of OCR2A , OCR2B

void setup() {
  TIMSK2 = (TIMSK2 & 0b11111000) | 0b00000110; // use TIMER2_COMPA_vect, TIMER2_COMPB_vect
  TCCR2B = (TCCR2B & 0b11111000) | 0b00000100; // prescale 64
  // each period is approx 2ms for default mode phase-correct PWM
  OCR2A = 192; // compare value A
  OCR2B = 168; // compare value B
}

void loop() {

}

//when TCNT2 == OCR2A
ISR(TIMER2_COMPA_vect){}

 //when TCNT2 == OCR2B
ISR(TIMER2_COMPB_vect){}

The only problem in phase-correct PWM (by default), the counter, e.g. TCNT2, goes from 0 to 255 and goes back from 255 to 0 then it overflows. So that each TIMER2_COMPA_vect and TIMER2_COMPB_vect happens twice when it is in Phase-correct PWM (default) but TIMER2_OVF_vect happens only once.

Anyway, we can set the value of counter TCNT2 manually to *mess* up time and thus changing timer or frequency, depends on what you want. But it is kinda messed up. You know, its hard to predict *butterfly*, if you’re watching too much movies about time travelling.

Read datasheet of atmega328 to know how to set bits for TCCR2A, TCCR2B or TIMSK2 and so forth.


Arduino PWM

Arduino UNO (ATmega328p) has six 8bit PWMs (on 3 timers: timer0 - timer2)

// For Arduino using ATmega8, 168 or 328* (m8, m168, m328, m238p)
// Phase-corrected PWM by default
// B00000xxx - 3 last bits (least significant bit or LSB) decide the divisor of the timer
// Frequency = 16Mhz / (256 * divisor)
// Example: divisor = 1, frequency = 16Mhz / (256) = 62.5Khz, 

//----- PWM frequency for D5 & D6 -----
//Timer0 divisor = 1, 8, 64, 256, 1024
//TCCR0B = TCCR0B & B11111000 | B00000001;    // 62.5KHz
//TCCR0B = TCCR0B & B11111000 | B00000010;    // 7.8KHz
  TCCR0B = TCCR0B & B11111000 | B00000011;    // 976Hz (default)
//TCCR0B = TCCR0B & B11111000 | B00000100;    // 244Hz
//TCCR0B = TCCR0B & B11111000 | B00000101;    // 61Hz
 
//----- PWM frequency for D9 & D10 -----
//Timer1 divisor = 2, 16, 128, 512, 2048
//TCCR1B = TCCR1B & B11111000 | B00000001;    // 31KHz
//TCCR1B = TCCR1B & B11111000 | B00000010;    // 3.9KHz
  TCCR1B = TCCR1B & B11111000 | B00000011;    // 490Hz (default)
//TCCR1B = TCCR1B & B11111000 | B00000100;    // 122.5Hz
//TCCR1B = TCCR1B & B11111000 | B00000101;    // 30.6Hz
 
//----- PWM frequency for D3 & D11 -----
//Timer2 divisor = 2, 16, 64, 128, 512, 2048
//TCCR2B = TCCR2B & B11111000 | B00000001;    // 31KHz
//TCCR2B = TCCR2B & B11111000 | B00000010;    // 3.9KHz
//TCCR2B = TCCR2B & B11111000 | B00000011;    // 980Hz
  TCCR2B = TCCR2B & B11111000 | B00000100;    // 490Hz (default)
//TCCR2B = TCCR2B & B11111000 | B00000101;    // 245Hz
//TCCR2B = TCCR2B & B11111000 | B00000110;    // 122.5Hz
//TCCR2B = TCCR2B & B11111000 | B00000111;    // 30.6Hz 

Arduino Mega (ATmega2560) has fifteen 8bit PWMs (on six timers: timer0 - timer5)

//For Arduino using ATmega2560* (m1280, m1281, m2560, m2561)
// Phase-corrected PWM by default
// B00000xxx - 3 LSBs decide the divisor of the timer
// Frequency = 16Mhz / (256 * divisor)
// Example: divisor = 1, frequency = 16Mhz / (256) = 62.5Khz

//----- PWM frequency for D4 & D13 ------------------------------ 
//Timer0 divisor = 1, 8, 64, 256, 1024
//TCCR0B = TCCR0B & B11111000 | B00000001;    // 62.5KHz
//TCCR0B = TCCR0B & B11111000 | B00000010;    // 7.8KHz
  TCCR0B = TCCR0B & B11111000 | B00000011;    // 976Hz (default)
//TCCR0B = TCCR0B & B11111000 | B00000100;    // 244Hz
//TCCR0B = TCCR0B & B11111000 | B00000101;    // 61Hz
 
 
//----- PWM frequency for D11 & D12 -----------------------------
//Timer1 divisor = 1, 8, 64, 256, 1024
//TCCR1B = TCCR1B & B11111000 | B00000001;    // 31KHz
//TCCR1B = TCCR1B & B11111000 | B00000010;    // 3.9KHz
  TCCR1B = TCCR1B & B11111000 | B00000011;    // 490Hz
//TCCR1B = TCCR1B & B11111000 | B00000100;    // 122.5Hz
//TCCR1B = TCCR1B & B11111000 | B00000101;    // 30.6Hz
 
//----- PWM frequency for D9 & D10 ------------------------------
//Timer2 divisor = 2, 16, 64, 128, 256, 512, 2048
//TCCR2B = TCCR2B & B11111000 | B00000001;    // 31KHz
//TCCR2B = TCCR2B & B11111000 | B00000010;    // 3.9KHz
//TCCR2B = TCCR2B & B11111000 | B00000011;    // 980Hz
  TCCR2B = TCCR2B & B11111000 | B00000100;    // 490Hz
//TCCR2B = TCCR2B & B11111000 | B00000101;    // 245Hz
//TCCR2B = TCCR2B & B11111000 | B00000110;    // 122.5Hz
//TCCR2B = TCCR2B & B11111000 | B00000111;    // 30.6Hz
 
//----- PWM frequency for D2, D3 & D5 ---------------------------
//Timer3 divisor = 2, 16, 128, 512, 2048
//TCCR3B = TCCR3B & B11111000 | B00000001;    // 31KHz
//TCCR3B = TCCR3B & B11111000 | B00000010;    // 3.9KHz
  TCCR3B = TCCR3B & B11111000 | B00000011;    // 490Hz
//TCCR3B = TCCR3B & B11111000 | B00000100;    // 122.5Hz
//TCCR3B = TCCR3B & B11111000 | B00000101;    // 30.6Hz
 
//----- PWM frequency for D6, D7 & D8 ---------------------------
//Timer4 divisor = 2, 16, 128, 512, 2048
//TCCR4B = TCCR4B & B11111000 | B00000001;    // 31KHz
//TCCR4B = TCCR4B & B11111000 | B00000010;    // 3.9KHz
  TCCR4B = TCCR4B & B11111000 | B00000011;    // 490Hz
//TCCR4B = TCCR4B & B11111000 | B00000100;    // 122.5Hz
//TCCR4B = TCCR4B & B11111000 | B00000101;    // 30.6Hz
 
//----- PWM frequency for D44, D45 & D46 ------------------------
//Timer5 divisor = 2, 16, 128, 512, 2048
//TCCR5B = TCCR5B & B11111000 | B00000001;    // 31KHz
//TCCR5B = TCCR5B & B11111000 | B00000010;    // 3.9KHz
  TCCR5B = TCCR5B & B11111000 | B00000011;    // 490Hz
//TCCR5B = TCCR5B & B11111000 | B00000100;    // 122.5Hz
//TCCR5B = TCCR5B & B11111000 | B00000101;    // 30.6Hz

Above are settings for Phase-corrected PWM (by arduino default).

analogWrite(PWMpin, value);

Default frequencies of analogWrite():
[INDENT]Arduino Pins 5 and 6: 976Hz
Arduino Pins 9, 10, 11, and 3: 490Hz
[/INDENT]

Too low for switch mode power supply (buck converter) and generate audible noise when use for motor or brushless fan (audible tone around 490Hz).

One trick is use the commands above (eg: TCCR1B = TCCR1B & B11111000 | B00000011;) after calling analogWrite() to change the default frequency.

You can double the frequency if we switch to fast PWM.

TCCR2A = (TCCR2A & 0b11111100) | 0b00000011; // set WGM00, WGM01 to 1: xxxxxx11 for fast PWM

Otherwise, a custom analog output function should be used instead.

The input value (stored in OCRnA or OCRnB for first and second PWM pin) is compared against the value in an 8bit counter (0-255 and wrap around in register TCNTn).

[INDENT]If value <= counter, output HIGH
If value > counter, output LOW
[/INDENT]

In Phase-correct PWM mode

+----+                          +----+
|    |                          |    |
|    |                          |    |
|    |                          |    |
+    +------------++------------+    +
0   value        255/255      value  0

OR 

-----+                          +-----
     |                          |    
     |                          |    
     |                          |    
     +--------------------------+    
0   value        255/255      value  0

In Fast PWM mode

+-------+            +-------+
|	|            |       |
|	|            |       |
|	|            |       |
+	+------------+       +------------+
0      value        255/0    value       255