Abstract
The analogWrite() outputs PWM(Pulse Width Modulation) wave.
The ATmega328P has three timer/counter for PWM wave output. Each timer/counter has two comparators, six pins can output PWM wave. The Arduino Uno can also output PWM wave from six pins.
The two of three timers(timer/count 0 and timer/counter 2) are 8bit, and the rest is 16 bit.
The output frequency of the PWM wave is set in init(). The analogWrite() sets the duty cycle and begins to output the wave. The analogWrite() is implemented using timer related registers of ATmega328P.
Timer/Counter 0
The timer/counter 0 is an 8bit counter. It is controlled by registers named TCCR0A, TCCR0B, TCNT0, OCR0A, OCR0B, TIMSK0 and TIFR0.
The timer/counter 0 controls the PWM output of digital pins 6(related to OC0A) and 5(related to OC0B).
TCCR0A、TCCR0B
The TCCR0A(timer/counter 0 control register A) and TCCR0B(timer/counter 0 control register B) control the PWM output.
TCCR0A
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
Name | COM0A1 | COM0A0 | COM0B1 | COM0B0 | - | - | WGM01 | WGM00 |
TCCR0B
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
Name | FOC0A | FOC0B | - | - | WGM02 | CS02 | CS01 | CS00 |
The WGM02, WGM01 and WGM00 controls the wave form. The wave is determined by the combination of the three bits.
Mode | WGM02 | WGM01 | WGM00 | 意味 |
---|---|---|---|---|
0 | 0 | 0 | 0 | normal |
1 | 0 | 0 | 1 | PWM、Phase Correct |
2 | 0 | 1 | 0 | CTC |
3 | 0 | 1 | 1 | Fast PWM |
4 | 1 | 0 | 0 | reserved |
5 | 1 | 0 | 1 | PWM、Phase Correct |
6 | 1 | 1 | 0 | reserved |
7 | 1 | 1 | 1 | Fast PWM |
In case of the Arduino Uno, the init() function sets WMG01 and WGM00 to 1. The type of timer/counter 0 is mode 3, the Fast PWM. At the fast PWM of mode 3, the counter is incremented from 0 to 255, then it goes back to 0. At the fast PWM of mode 7 , the counter is incremented from 0 to the value stored in OCR0A, then it becomes 0.
The COM0A1 and COM0A0 control the PWM of digital pin 6.
COM0A1 | COM0A0 | Meanings |
---|---|---|
0 | 0 | Normal port operation, OC0A disconnected. |
0 | 1 | WGM02 = 0: Normal Port Operation, OC0A Disconnected. WGM02 = 1: Toggle OC0A on Compare Match. |
1 | 0 | Clear OC0A on Compare Match, set OC0A at BOTTOM,(non-inverting mode). |
1 | 1 | Set OC0A on Compare Match, clear OC0A at BOTTOM,(inverting mode). |
The COM0A0 is set to 0, and the analogWrite() sets the COM0A1 1.
The COM0B1 and COM0B0 control the PWM of digital pin5.
COM0B1 | COM0B0 | Meanings |
---|---|---|
0 | 0 | Normal port operation, OC0B disconnected. |
0 | 1 | Reserved. |
1 | 0 | Clear OC0B on Compare Match, set OC0B at BOTTOM,(non-inverting mode) |
1 | 1 | Set OC0B on Compare Match, clear OC0B at BOTTOM,(inverting mode). |
The COM0B0 is set to 0, and the analogWrite() sets the COM0B1 to 1.
The Arduino Uno dose not use FOC0A and FOC0B.
The CS02, CS01 and CS00 select clock.
CS02 | CS01 | CS00 | Meaningsg |
---|---|---|---|
0 | 0 | 0 | No clock source (Timer/Counter stopped) |
0 | 0 | 1 | Division ratio 1 |
0 | 1 | 0 | Division ratio 8 |
0 | 1 | 1 | Division ratio 64 |
1 | 0 | 0 | Division ratio 256 |
1 | 0 | 1 | Division ratio 1024 |
1 | 1 | 0 | External clock source on T0 pin. Clock on falling edge. |
1 | 1 | 1 | External clock source on T0 pin. Clock on rising edge. |
In case of the Arduino Uno, the init() sets the CS01 and CS00 to 1. So the division ratio is 64.
The frequency of the Fast PWM is calculated as follows.
$$f = clock frequency / (division ratio * 256)$$The clock frequency is 16MHz and the division ratio is 64, the frequency of wave output to the digital pin 5 and 6 is 16000000 / (64 * 256) = 976.5625 Hz.
TCNT0
The TCNT0 is an eight bit register and timer/counter. The value is incremented each 1 clock. If it gose to 255 then back to 0.
OCR0A、OCR0B
The OCR0A(for pin 6) and OCR0B(for pin 5)are 8bit compare registers. They hold duty ratio specified by analogWrite(). When using fast PWM, comparing TCNT0 with OCR0A or OCR0B, if they are the same value, the output becomes LOW. If TCNT0 becomes 0, the output becomes HIGH. The figure below depicts how fast PWM works.
TIMSK0
The TIMSK0 contols the interrupt mask.
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
Name | - | - | - | - | - | OCIE0B | OCIE0A | TOIE0 |
The OCIE0B and OCIE0A controls the interrupt mask of the timer/counter0. The analogWrite() dose not use them.
The TOIE0 is an overflow interrupt enable flag. The init() function sets the flag. It is not for analogWrite() but for millis() and micros().
TIFR0
The TIFR0 is a interurpt flag register of the timer/counter 0. The Arduino dose not set it.
Timer/Counter1
The timer/counter1 is a 16bit counter. It is controlled by registers named TCCR1A, TCCR1B, TCCR1C, TCNT1H/TCNT1L, OCR1AH/ORC1AL, OCR1BH/OCR1BL, ICR1H/ICR1L, TIMSK1 and TIFR1.
The timer/counter 1 controls the PWM output of digital pins 9(related to OC1A) and 10(related to OC1B).
TCCR1A、TCCR1B、TCCR1C
The TCCR1A(Timer/Counter1 Control Register A) , TCCR1B(Timer/Counter1 Control Register B) and TCCR1C(Timer/Counter1 Control Register C) control the PWM output.
TCCR1A
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
Name | COM1A1 | COM1A0 | COM1B1 | COM1B0 | - | - | WGM11 | WGM10 |
TCCR1B
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
Name | ICNC1 | ICES1 | - | WGM13 | WGM12 | CS12 | CS11 | CS10 |
TCCR1C
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
Name | FOC1A | FOC1B | - | - | - | - | - | - |
The WGM13, WGM12, WGM11 and WGM10 controls the wave form. The wave is determined by the combination of the four bits.
Mode | WGM13 | WGM12 | WGM11 | WGM10 | Meanings |
---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | Normal |
1 | 0 | 0 | 0 | 1 | PWM、Phase Correct, 8bit |
2 | 0 | 0 | 1 | 0 | PWM、Phase Correct, 9bit |
3 | 0 | 0 | 1 | 1 | PWM、Phase Correct, 10bit |
4 | 0 | 1 | 0 | 0 | CTC |
5 | 0 | 1 | 0 | 1 | Fast PWM, 8bit |
6 | 0 | 1 | 1 | 0 | Fast PWM, 9bit |
7 | 0 | 1 | 1 | 1 | Fast PWM, 10bit |
8 | 1 | 0 | 0 | 0 | PWM, Phase and Frequency Correct |
9 | 1 | 0 | 0 | 1 | PWM, Phase and Frequency Correct |
10 | 1 | 0 | 1 | 0 | PWM, Phase Correct |
11 | 1 | 0 | 1 | 1 | PWM, Phase Correct |
12 | 1 | 1 | 0 | 0 | CTC |
13 | 1 | 1 | 0 | 1 | Reserved |
14 | 1 | 1 | 1 | 0 | Fast PWM |
15 | 1 | 1 | 1 | 1 | Fast PWM |
In case of the Arduino Uno, the init() function sets WMG10 to 1. The type of timer/counter 1 is mode 1, the Phase Correct PWM. At the Phase Correct PWM of mode 1, the counter is incremented from 0 to 255, then the timer is decremented from 255 to 0.
The COM1A1 and COM1A0 control the PWM of the digital pin 9. The COM1B1 and COM1B0 control the PWM of the digital pin 10. The meanings of the combination of the registers are shown below.
COM1A1/COM1B1 | COM1A0/COM1B0 | Meanings |
---|---|---|
0 | 0 | Normal port operation, OC1A/OC1B disconnected. |
0 | 1 | Normal port operation, OC1A/OC1B disconnected. |
1 | 0 | Clear OC1A/OC1B on Compare Match when upcounting. Set OC1A/OC1B on Compare Match when downcounting. |
1 | 1 | Set OC1A/OC1B on Compare Match when upcounting. Clear OC1A/OC1B on Compare Match when downcounting. |
In case of Arduino Uno, COM1A0/COM1B0 are 0, the analogWrite() sets the COM1A1/COM1B1 to 1.
ICNC1 and ICES1 is not touched.
The CS12, CS11 and CS10 select the clock.
CS12 | CS11 | CS10 | Meanings |
---|---|---|---|
0 | 0 | 0 | No clock |
0 | 0 | 1 | Division ratio 1 |
0 | 1 | 0 | Division ratio 8 |
0 | 1 | 1 | Division ratio 64 |
1 | 0 | 0 | Division ratio 256 |
1 | 0 | 1 | Division ratio 1024 |
1 | 1 | 0 | External clock source on T0 pin. Clock on falling edge. |
1 | 1 | 1 | External clock source on T0 pin. Clock on rising edge. |
In case of the Arduino UNO, the init() function sets CS11 and CS10 to 1. So the division rate is 64.
The frequency of the Phase Correct PWM is calculated as follows.
$$ f = clock frequency / (division ratio * TOP * 2)$$The value of TOP is 255 when using phase correct PWM 8bit. As the clock frequency is 16 MHz and the division ratio is 64, the PWM frequency of the PWM output to digital pin 9 and pin 10 is, 16000000 / (64 * 255 * 2) = 490.1961 Hz.
The FOC1A and FOC1B is not used.
TCNT1H・TCNT1L
The TCNT1H and TCNT1L are 8bit counters and the combination of the two is used as a 16 bit counter. In case of the Arduino Uno, It is used as a 8bit counter because it increments from 0 to 255 then decrement from255 to 0.
OCR1AH・OCR1AL、OCR1BH・OCR1BL
The combination of OCR1A(for pin 9) and OCR1B(for pin 10) is a 16bit compare registers. They hold duty ratio specified by analogWrite(). When using Phase Correct PWM, comparing TCNT1H/TCNT1L with OCR1A or OCR1B, if they are the same value, the output becomes LOW. If TCNT1 becomes 0, the output becomes HIGH. The figure below depicts how Phase Correct PWM works.
ICR1H・ICR1L、TIMSK1、TIFR1
The analogWrite() dose not use ICR1H/ICR1L, TIMSK1, and TIFR1.
Timer/Counter 2
The timer/counter 2 is a 8bit counter. It is controlled by register named TCCR2A, TCCR2B, TCNT2, OCR2A, OCR2B, TIMSK2 , TIFR2 ASSR and GTCCR.
The timer/counter 2 controls the PWM output of digital pins 3(related to OC2B) and 11(related to OC2A).
TCCR2A、TCCR2B
The TCCR2A(timer/counter 2 control register A) and TCCR2B(timer/counter 2 control register B) control the PWM output.
TCCR2A
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
Name | COM2A1 | COM2A0 | COM2B1 | COM2B0 | - | - | WGM21 | WGM20 |
TCCR2B
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
Name | FOC2A | FOC2B | - | - | WGM22 | CS22 | CS21 | CS20 |
The WGM22, WGM21 and WGM20 controls the wave form. The wave is determined by the combination of the three bits.
Mode | WGM22 | WGM21 | WGM20 | Meanings |
---|---|---|---|---|
0 | 0 | 0 | 0 | Normal |
1 | 0 | 0 | 1 | PWM、Phase Correct |
2 | 0 | 1 | 0 | CTC |
3 | 0 | 1 | 1 | Fast PWM |
4 | 1 | 0 | 0 | Reserved |
5 | 1 | 0 | 1 | PWM、Phase Correct |
6 | 1 | 1 | 0 | Reserved |
7 | 1 | 1 | 1 | Fast PWM |
In case of the Arduino Uno, the init() function sets WMG20 to 1. The type of timer/counter 2 is mode 1, the Phase Correct PWM. At the Phase Correct PWM of mode 1, the counter is incremented from 0 to 255, then the timer is decremented from 255 to 0.
The COM2A1 and COM2A0 control the PWM of the digital pin 11. The COM2B1 and COM2B0 control the PWM of the digital pin 3. The meanings of the combination of the registers are shown below.
COM2A1 | COM2A0 | Meanings |
---|---|---|
0 | 0 | Normal port operation, OC2A disconnected. |
0 | 1 | Normal port operation, OC2A disconnected. |
1 | 0 | Clear OC2A on Compare Match when up-counting. Set OC2A on Compare Match when down-counting. |
1 | 1 | Set OC2A on Compare Match when up-counting. Clear OC2A on Compare Match when down-counting. |
COM2B1 | COM2B0 | Meanings |
---|---|---|
0 | 0 | Normal port operation, OC2B disconnected. |
0 | 1 | Reserved |
1 | 0 | Clear OC2B on Compare Match when up-counting. Set OC2B on Compare Match when down-counting. |
1 | 1 | Set OC2B on Compare Match when up-counting. Clear OC2B on Compare Match when down-counting. |
In case of Arduino Uno, the COM2A0 and COM2B0 are 0. The analogWrite() sets the COM2A1 and COM2B1to 1.
The analogWrite() dose not use FOC2A and FOC2B.
The CS22, C211 and CS20 select the clock.
CS22 | CS21 | CS20 | Meanings |
---|---|---|---|
0 | 0 | 0 | No clock |
0 | 0 | 1 | Division Ratio 1 |
0 | 1 | 0 | Division Ratio 8 |
0 | 1 | 1 | Division Ratio 32 |
1 | 0 | 0 | Division Ratio 64 |
1 | 0 | 1 | Division Ratio 128 |
1 | 1 | 0 | Division Ratio 256 |
1 | 1 | 1 | Division Ratio 1024 |
In case of the Arduino UNO, the init() function sets CS22 to 1. So the division rate is 64.
The frequency of the Phase Correct PWM is calculated as follows.
$$f = clock frequency / (division ratio \times 255 \times 2)$$As the clock frequency is 16 MHz and the division ratio is 64, the PWM frequency of the PWM output to digital pin 11 and pin 3 is, 16000000 / (64 * 255 * 2) = 490.1961 Hz.
TCNT2
The TCNT2 is an eight bit register and timer/counter. The value is incremented each 1 clock. If it goes to 255 then it decrements the counter to 0.
OCR2A、OCR2B
The OCR2A(for pin 11) and OCR2B(for pin 3)are 8bit compare registers. They hold duty ratio specified by analogWrite(). When using Phase Correct PWM, comparing TCNT2 with OCR1A or OCR1B, if they are the same value, the output becomes LOW. If TCNT2 becomes 0, the output becomes HIGH.
TIMSK2
The TIMSK2 controls the interrupt mask. The analogWrite() dose not set it but the tone() uses it.
TIFR2
The TIFR2 is an interrupt flag register. The analogWrite() dose not set it.
Source Code
The analogWrite() is defined in hardware/arduino/avr/cores/arduino/wiring_analog.c as below. Only the source code for Arduino UNO is quoted. The original source code supports many tips using #if’s.
|
|
The inputs are pin and val. The type of the inputs are uint8_t and int respectively. The function dose not return any value.
First set the pin to OUTPUT using pinMode().
|
|
Next if the val is equal to 0 or 255M, set the output to LOW and HIGH using digitalWrite().
|
|
If the val is neither 0 nor 255, the operation branches according to the result of the digitalPinToTimer() which returns the name of the timer.
|
|
If the result of the digitalPinToTimer() is TIMER0A, the function set the COM0A1 bit of the TCCR0A register to 1 using sbi(). The sbi() is a macro to set the bit(the second argument) of the address(the first argument) to 1. Then the function substitute OCR0A for val to set the duty ratio. After this the function set the TCCRnx and OCRnx according to the result of the digitalPinToTimer().
|
|
If the pin can not output the PWM, it outputs LOW if the val is less than 128 or HIGH.
|
|
Version
Arduino AVR Boards 1.8.6
Last Update
March 21, 2023