Вы находитесь на странице: 1из 30

# PWM On The ATmega328

INTRODUCTION:
This tutorial is a mirror of the ATmega8 tutorial. Because the registers for this feature are
completely different I have split it into 2 complete chapters.
We know that the AVR lets us turn outputs on and off, but, what if we want to control the
brightness of an LED or the speed of a motor? Well the easiest way to do this is to change the
voltage of the output but we can fake it. If we take a voltage source and connect it for a
second and then disconnect it for a second it will .... well be on for a second then off for a
second. BUT, if we manage to speed this up so that we connect/disconnect the power supply
over 1000 times a second we can fool the power supply into thinking that our signal is half the
value of the power supply. This is called Pulse Width Modulation or PWM for short.

THEORY OF OPERATION:

## Figure 1: ATmega168/328 PWM Pins

When you take a square wave, its on for a while and off for a while. If we divide the on by
the off and multiply by 100% we will get what is called a duty cycle.
Duty_Cycle = [ON_time / (ON_time + OFF_time) ] * 100

So if we are on for 1ms and then off for 1ms we will end up with a 50% duty cycle; If we
are on for 1ms and off for 3ms we end up with a 25% duty cycle.
Output_Voltage = Duty_Cycle * Input_Voltage

Now if we take our duty cycle and multiply it by our voltage we will get the output voltage.
So if we have a 5V power supply and we activate a PWM on a 25% duty cycle we will make an
analog device behave as if it was receiving a 1.25V signal. Cool eh!?
Much like the counter functions PWM can be simulated in software however, the hardware
version is preferred because it just sort of does its own thing and, with very little coding you
can get a constant square wave going.
Remember the Prescaler? well its back in the PWM. And much like in the counter, its roll is
to slow things down. This is good because it allows us to run the PWM at different frequencies.
This is important because some devices are sensitive to PWM speeds. A motor for example
will get hot if the PWM waveform is too fast, and will jitter if the PWM is too slow. Since I
different frequencies but 10kHz will get you into the ball park.
The ATmega168/328 has 6(!!!) PWM outputs, 2 are located on each timer/counter.
As always, the output pin has the same limitations as any output (see the Digital Output
Chapter for details).
The AVR's PWM timer is capable of running in 3 different modes: Fast PWM, Phase Corrected
PWM and Phase and Frequency Phase Corrected PWM.

## Fast PWM mode:

Fast PWM works the same way as the normal counter. The Control Logic receives the signal
and increments the TCNTn register. When a match is detected the OCFnx flag is set and signal
is send to the Waveform Generator. The Waveform Generator then changes the state of the
OCnx pin (the state is determined by the selected mode). When the TCNTn register passes the
TOP value (0xFF or OCRnA) it simply overflows (or overruns) back to 0, at the same time the
OCFnx flag is set. The OCFnx flag can be configured to trigger an interrupt. The OCFnx flag
can be cleared by software, but as always is cleared automatically when an interrupt request is
triggered.
Due to the high frequency of this mode is best used for DAC, fading LEDs, rectification and
Power regulation.
The Frequency of the fast PWM can be calculated by the following equation.
PWM_fequency = clock_speed / [Prescaller_value * (1 + TOP_Value) ]

## Phase Corrected PWM mode:

The phase corrected mode is a bit strange, it counts up until it hits the TOP value (FIXED,
OCRnA or ICRn) then starts to count down until it hits the BOTTOM (0). The Control
Logic receives the signal and increments the TCNTn register. When a match is detected the
OCFnx flag is set and signal is send to the Waveform Generator. The Waveform Generator then
changes the state of the OCnx pin (the state is determined by the selected mode). When the
TCNTn register hits the TOP value (FIXED, OCRnA or ICRn) the OCFnx flag is set. The OCFnx

flag can be configured to trigger an interrupt. The OCF1x flag can be cleared by software, but
as always is cleared automatically when an interrupt request is triggered.
This mode can be inverted or none-inverted. In none-inverting mode, the OCn pin is
LOW(GND) on the Compare Match between TCNTn and OCRnx while up-counting, and
HIGH(VCC) on the Compare Match while down-counting. In inverting mode, the OCn pin is
HIGH(VCC) on the Compare Match between TCNTn and OCRnx while up-counting, and
LOW(GND) on the Compare Match while down-counting.
This mode is recommended for motor control.
The frequency of the Phase Corrected PWM can be calculated by the following equation.
PWM_frequency = clock_speed / (2 * Prescaller_value * TOP_value )

## Phase and Frequency Corrected PWM mode:

Phase Corrected and Phase and Frequency Corrected PWM modes function the same way if
we are not planning on changing our TOP value once the PWM mode is started. The only
difference that I could see on the data sheet is that the Phase and Frequency Corrected mode
updates its TOP value when it hits Bottom while the Phase Corrected mode updates its TOP
value when it hits the TOP.
If anyone knows anything more (or if I'm wrong) about these 2 modes please let me know.
This mode is recommended for motor control.
The frequency of the Phase and Frequency Corrected PWM can be calculated by the following
equation.
PWM_frequency = clock_speed / (2 * Prescaller_value * TOP_value )

## Figure 2: ATmega328 Timer0 (8bit)

Timer/Counter0 has 2 outputs, OC0A and OC0B. Since both of these outputs run off the
same timer and waveform generators both OC0A and OC0B are synchronized, this make the
timer perfect for making tank robots (I love tank robots).
Timer/Counter0 does not have a 32 or 128 devision in its prescaler. Because of this it is
somewhat limited to the frequencies that it could produce. If you need a specific frequency on
your PWM use Timer/Counter2 which has all of its prescaler values.
Timer/Counter0 is capable of running on 4 modes the Fast PWM with a max TOP (0xFF), a
Fast PWM mode with a variable TOP (OCR0A), a Phase Corrected PWM mode with a max TOP
(0xFF) and a Phase Corrected PWM mode with a variable TOP (OCR0A). Each of these modes
can be inverted or none-inverted.

7 bit
6 bit
5 bit
4 bit
3 bit 2 bit
1 bit
0 bit
TCCR0A COM0A1 COM0A0 COM0B1 COM0B0 WGM01 WGM00
Timer/Counter Control Register 0 A

## COM0A1 COM0A0 DESCRIPTION

0
0
OC0A disabled
WGM02 = 0: Normal Port Operation, OC0A
0
1
Disconnected
WGM02 = 1: Toggle OC0A on Compare Match
1
0
None-inverted mode (HIGH at bottom, LOW on Match)
1
1
Inverted mode (LOW at bottom, HIGH on Match)
Applies only to PWM modes

## COM0B1 COM0B0 DESCRIPTION

0
0
OC0B disabled
0
1
Reserved
1
0
None-inverted mode (HIGH at bottom, LOW on Match)
1
1
Inverted mode (LOW at bottom, HIGH on Match)
Applies only to PWM modes

## 7 bit 6 bit 5 bit 4 bit 3 bit

2 bit 1 bit 0 bit
TCCR0B FOC0A FOC0B - WGM02 CS02 CS01 CS00
Timer/Counter Control Register 0 A

## CS02 CS01 CS00 DESCRIPTION

0
0
0
Timer/Counter2 Disabled
0
0
1
No Prescaling
0
1
0
Clock / 8
0
1
1
Clock / 64
1
0
0
Clock / 256
1
0
1
Clock / 1024
CS bits

## MODE WGM02 WGM01 WGM00 TOP

0
0
0
0
1
0
0
1
0xFF
2
0
1
0
OCRA
3
0
1
1
0xFF
4
1
0
0
5
1
0
1
OCR0A
6
1
1
0
7
1
1
1
OCR0A

DESCRIPTION
Normal
PWM Phase Corrected
CTC
Fast PWM
Reserved
PWM Phase Corrected
Reserved
Fast PWM

## 7 bit 6 bit 5 bit 4 bit 3 bit

2 bit
1 bit
0 bit
TIMSK0 OCIE0B OIE0A TOIE0

2 bit

1 bit

0 bit

TIFR0

## 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0 bit

TCNT0
Timer/Counter Register (stores the counter value)

## 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0 bit

OCR0A
Output Compare Register A (stores the compare value)

## 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0 bit

OCR0B
Output Compare Register B (stores the compare value)
ATmega168/328 Code:
// this code sets up counter0 for an 8kHz Fast PWM wave @ 16Mhz Clock
#include <avr/io.h>
int main(void)
{
DDRD |= (1 << DDD6);
// PD6 is now an output
OCR0A = 128;
// set PWM for 50% duty cycle
TCCR0A |= (1 << COM0A1);
// set none-inverting mode
TCCR0A |= (1 << WGM01) | (1 << WGM00);
// set fast PWM Mode
TCCR0B |= (1 << CS01);
// set prescaler to 8 and starts PWM

while (1);
{
// we have a working Fast PWM
}
}

## Figure 3: ATmega328 Timer1 (16bit)

Timer/Counter1 has 2 outputs, OC1A and OC1B. Since both of these outputs run off the
same timer and waveform generators both OC1A and OC1B are synchronized, this make the
timer perfect for making tank robots (I love tank robots).
Timer/Counter1 is capable of running in 3 modes the Fast PWM mode, the Phase Corrected
PWM mode and, Phase and Frequency Corrected mode. Each of these modes can be inverted
or none-inverted. Just like Timer/Counter0 Timer/Counter1 has several options for controlling
the TOP value of the PWM unlike Timer/Counter0 however, the TOP value can be fixed, stored
in the OCR1A register or the ICR1 register (see table below).

7 bit
6 bit
5 bit
4 bit
3 bit 2 bit
1 bit
0 bit
TCCR1A COM1A1 COM1A0 COM1B1 COM1B0 WGM11 WGM10
Timer/Counter Control Register 1 A

COM1A1 COM1A0
COM1B1 COM1B0 DESCRIPTION
0
0
Normal port operation, OC1A/OC1B disconnected.
Mode 9,11,14,15 only: Enable OCR1A only (OC1B
0
1
disconnected)

1
1

0
1

## None-inverted mode (HIGH at bottom, LOW on Match)

Inverted mode (LOW at bottom, HIGH on Match)

## 7 bit 6 bit 5 bit 4 bit

3 bit
2 bit 1 bit 0 bit
TCCR1B ICNC1 ICES1 - WGM13 WGM12 CS12 CS11 CS10
Timer/Counter Control Register 1 B

## CS12 CS11 CS10 DESCRIPTION

0
0
0
Timer/Counter2 Disabled
0
0
1
No Prescaling
0
1
0
Clock / 8
0
1
1
Clock / 64
1
0
0
Clock / 256
1
0
1
Clock / 1024
1
1
0
External clock source on T1 pin, Clock on Falling edge
1
1
1
External clock source on T1 pin, Clock on rising edge
CS bits

0
0
0
0
0
Normal
PWM, Phase
1
0
0
0
1
Corrected, 8bit
PWM, Phase
2
0
0
1
0
Corrected, 9bit
PWM, Phase
3
0
0
1
1
Corrected, 10bit
5
0
1
0
1
Fast PWM, 8bit
6
0
1
1
0
Fast PWM, 9bit
7
0
1
1
1
Fast PWM, 10bit
PWM, Phase and
8
1
0
0
0
Frequency
Corrected
PWM, Phase and
9
1
0
0
1
Frequency
Corrected
PWM, Phase
10
1
0
1
0
Correct
PWM, Phase
11
1
0
1
1
Correct
14
1
1
1
0
Fast PWM

TOP
0xFFFF
0x00FF
0x01FF
0x03FF
0x00FF
0x01FF
0x03FF
ICR1

OCR1A
ICR1
OCR1A
ICR1

15

Fast PWM

## Waveform Generator Mode bits (Abbreviated)

7 bit
6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0 bit
TCCR1C FOC1A FOC1B
Timer/Counter Control Register C

## 7 bit 6 bit 5 bit 4 bit 3 bit

2 bit
1 bit
0 bit
TIMSK1 ICIE1
OCIE1B OCIE1A TOIE1

## 7 bit 6 bit 5 bit 4 bit

3 bit
2 bit 1 bit 0 bit
TIFR OCF2 TOV2 ICF1 OCF1A OCF1B TOV1
- TOV0
Timer/Counter Interrupt Flag Register

## 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0 bit

TCNT1H
TCNT1L
Timer/Counter Register (stores the counter value, 16 bit)

## 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0 bit

OCR1AH
OCR1AL
Output Compare Register A (stores the compare value, 16 bit)

## 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0 bit

OCR1BH
OCR1BL
Output Compare Register B (stores the compare value, 16 bit)
ATmega168/328 Code:
// this code sets up counter1 A output at 25% and B output at 75%
// using ICR1 as top (16bit), Fast PWM.
#include <avr/io.h>
int main(void)
{
DDRB |= (1 << DDB1)|(1 << DDB2);
// PB1 and PB2 is now an output

OCR1A

OCR1 = 0xFFFF;
// set TOP to 16bit
OCR1A = 0x3FFF;
// set PWM for 25% duty cycle @ 16bit
OCR1B = 0xBFFF;
// set PWM for 75% duty cycle @ 16bit
TCCR1A |= (1 << COM1A1)|(1 << COM1B1);
// set none-inverting mode
TCCR1A |= (1 << WGM11);
TCCR1B |= (1 << WGM12)|(1 << WGM13);
// set Fast PWM mode using ICR1 as TOP
TCCR1B |= (1 << CS10);
// START the timer with no prescaler

while (1);
{
// we have a working Fast PWM
}
}

## Figure 2: ATmega328 Timer2 (8bit)

Timer/Counter0 is pretty cool because it has 2 outputs, OC2A and OC2B. Since both of
these outputs run off the same timer and waveform generators both OC2A and OC2B
are synchronized, this make the timer perfect for making tank robots (I love tank robots).
As stated under Timer/Counter0, Timer/Counter2 has all of its prescaler values, this makes
it capable of producing a lot more frequencies then its 8 bit brother.
Timer/Counter0 is capable of running on 4 modes the Fast PWM with a max TOP (0xFF), a
Fast PWM mode with a variable TOP (OCR2A), a Phase Corrected PWM mode with a max TOP
(0xFF) and a Phase Corrected PWM mode with a variable TOP (OCR2A). Each of these modes
can be inverted or none-inverted.

7 bit
6 bit
5 bit
4 bit
3 bit 2 bit
1 bit
0 bit
TCCR2A COM2A1 COM2A0 COM2B1 COM2B0 WGM21 WGM20
Timer/Counter Control Register 2 A

## COM2A1 COM2A0 DESCRIPTION

0
0
OC2A disabled
WGM22 = 0: Normal Port Operation, OC2A
0
1
Disconnected
WGM22 = 1: Toggle OC2A on Compare Match
1
0
None-inverted mode (HIGH at bottom, LOW on Match)
1
1
Inverted mode (LOW at bottom, HIGH on Match)
Applies only to PWM modes

## COM2B1 COM2B0 DESCRIPTION

0
0
OC2B disabled
0
1
Reserved
1
0
None-inverted mode (HIGH at bottom, LOW on Match)
1
1
Inverted mode (LOW at bottom, HIGH on Match)
Applies only to PWM modes

## 7 bit 6 bit 5 bit 4 bit 3 bit

2 bit 1 bit 0 bit
TCCR2B FOC2A FOC2B - WGM22 CS22 CS21 CS20
Timer/Counter Control Register 2 A

## CS22 CS21 CS20 DESCRIPTION

0
0
0
Timer/Counter2 Disabled
0
0
1
No Prescaling
0
1
0
Clock / 8
0
1
1
Clock / 32
1
0
0
Clock / 64
1
0
1
Clock / 128
1
1
0
Clock / 256
1
1
1
Clock / 1024
CS bits

## MODE WGM22 WGM21 WGM20 TOP

0
0
0
0
0xFF
1
0
0
1
0xFF
2
0
1
0
OCRA
3
0
1
1
0xFF
4
1
0
0
5
1
0
1
OCR0A
6
1
1
0
7
1
1
1
OCR0A

DESCRIPTION
Normal
PWM Phase Corrected
CTC
Fast PWM
Reserved
PWM Phase Corrected
Reserved
Fast PWM

## 7 bit 6 bit 5 bit 4 bit 3 bit

2 bit
1 bit
0 bit
TIMSK2 OCIE2B OIE2A TOIE2

2 bit

1 bit

0 bit

TIFR2

## 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0 bit

TCNT2
Timer/Counter Register (stores the counter value)

## 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0 bit

OCR2A
Output Compare Register A (stores the compare value)

## 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0 bit

OCR2B
Output Compare Register B (stores the compare value)

ATmega168/328 Code:
// this code sets up counter2 for an 8kHz Fast PWM wave @ 16Mhz Clock
#include <avr/io.h>
int main(void)
{
DDRD |= (1 << DDD6);
// PD6 is now an output
OCR2A = 128;
// set PWM for 50% duty cycle
TCCR2A |= (1 << COM2A1);
// set none-inverting mode
TCCR2A |= (1 << WGM21) | (1 << WGM20);
// set fast PWM Mode
TCCR2B |= (1 << CS21);
// set prescaler to 8 and starts PWM

while (1);
{
// we have a working Fast PWM
}
}Advanced Arduino: direct use of ATmega counter/timers

## What are the counter/timers

The Arduino platform consists of a set of software libraries that run on a group of
micro-controller chips. Currently the supported micro-controllers are the AVR
ATmega168, ATmega328, and the more featureful ATmega1280 and ATmega2560
used in the Arduino Mega. Also smaller chips like the ATtiny are useable if you are
prepared to do some more homework.
Currently the Arduino platform uses the ATmega series but the software is not
strongly tied to any one architecture (in principle).
These notes are, however, firmly tied to the ATmega architecture and the ATmega168
and ATmega328 chips of the standard Arduinos. The ATmega1280 and 2560 are very
similar however (but with more counter/timers).
Where to find things out
All things Arduino can be found at or from the Arduino wesbite. Google naturally is
very useful for locating more resources. The chip datasheets can be located in
numerous places but Atmel's website has them at their ATmega328p doc page.
Existing use/support of counter/timers in Arduino libraries
The millis() and micros() functions use timer0 to keep track of the time since
last reset.
The Tone library uses whichever timer(s) are needed for the pin(s) involved see the pin/timer map below.
analogWrite() function uses whichever timer is needed for the pin involved.
The Servo library uses timer1 to provide interrupt-driven servo-signal
generation.
The standard Arduino has 3 timers, timer0 is 8 bit and used for the millis() and
micros() functions. Timer1 is 16 bit and not used by default, timer2 is another 8 bit
timer like timer0 but not used by default.
timer
timer0
timer0
timer1
timer1
timer2
timer2
timer3
timer3
timer3
timer4
timer4
timer4

bits
8
8
16
16
8
8
16
16
16
16
16
16

channel
A
B
A
B
A
B
A
B
C
A
B
C

Arduino pin
6
5
9
10
11
3
-

Mega pin
13
4
11
12
10
9
5
2
3
6
7
8

timer
timer5
timer5
timer5

bits
16
16
16

channel
A
B
C

Arduino pin
-

Mega pin
44
45
46

At restart the Arduino bootloader sets up the timers ready for analogWrite() PWM
use. The timer/counters are set to phase-correct 8-bit PWM mode and with clock
prescaled to system clock divided by 64. However timer0 is set to fast PWM mode
for use with the millis()/micros() code.
For standard Arduinos the system clock is 16MHz so that the timers are clocking at
250kHz by default. Phase correct 8-bit PWM mode takes 510 clocks to cycle and fast
8-bit PWM mode takes 256. This means a frequency of 490Hz for pins 5 and 6 and
977Hz for pins 3, 9, 10, 11 when used with analogWrite(). It is possible to change the
clock prescaling to change these default PWM frequencies.
ATmega counter-timer basics
The counter/timer hardware modules are very flexible and consist of a set of registers
to control the behaviour as well as the counter/timer register itself. 16 bit timers have
mainly 16-bit registers (although the control registers are 2 separate 8-bit ones in all
timers).
In the following description I shall use timer1's register names, but the naming is
systematic and replacing the '1' by another number you get the corresponding
names.TCCR1A and TCCR1B are the two 8-bit control registers. These set up the
clock prescale ratio, set the mode for the counter/timer and set the output control for
the PWM pins. TCNT1 is the 16 bit (for timer1) counter value itself. At the assembly
language level this register has to be written or read as two 8-bit half-registers and in
a certain order. The GCC AVR compiler used by the Arduino software automatically
generates the two accesses as if this was a single 16 bit transfer. However if you have
an interrupt handler that also accesses 16 bit registers in the same timer you must
protect accesses by disabling interrupts across the access.
Each timer has 2 or 3 channels which control a dedicated pin if the relevant output
mode is set and that pin is enabled as an output. The OCR1A and OCR1B are the 16
bit Output Compare Registers that apply to the two channels (A and B) that timer1
has. The Mega has timers 3 4 and 5 that each have 3 channels, A, B and C. The same
considerations about 16 bit transfers as for the TCNT1 register of course.
The Input Capture Register ICR1 is a 16 bit register used to record the value
of TCNT1 when an external event happens - typically a change on the ICP1 pin
(Arduino pin 8). Only 16 bit timers have input capture.
The basic operation of the unit, common to most modes is for the counter
register TCNT1 to count up and/or down between values BOTTOM (which is zero)
and TOP. In some modes TOP is provided by a register such as OCR1A or ICR1, in
other modes it is fixed at 0xFF or 0x1FF or 0x3FF or 0xFFFF (MAX). Many modes
compare the OCR1A and OCR1B values to the TCNT1 at each clock and may

change the output pins for those channels. In phase-correct PWM mode the counter
reverses direction when getting to BOTTOM or TOP thus ramping both up and down.
In other modes the counter just counts up and resets to BOTTOM (usually zero) upon
reaching TOP.
There are interrupt handlers for several events associated with a timer, namely
overflow, compare matching on A or B, and input capture. The names for the
interrupt vectors
are TIMER1_CAPT_vect, TIMER1_COMPA_vect, TIMER1_COMPB_vect, TIME
R1_OVF_vect. There is no CAPT vector for timers 0 and 2 since they are 8-bit.
Control register set-up
The TCCR1A and TCCR1B registers can be programmed thus:
void timer1_setup (byte mode, int prescale, byte outmode_A, byte outmode_B, byte
capture_mode)
{
// enforce field widths for sanity
mode &= 15 ;
outmode_A &= 3 ;
outmode_B &= 3 ;
capture_mode &= 3 ;

## byte clock_mode = 0 ; // 0 means no clocking - the counter is frozen.

switch (prescale)
{
case 1: clock_mode = 1 ; break ;
case 8: clock_mode = 2 ; break ;
case 64: clock_mode = 3 ; break ;
case 256: clock_mode = 4 ; break ;
case 1024: clock_mode = 5 ; break ;
default:
if (prescale < 0)
clock_mode = 7 ; // external clock
}
TCCR1A = (outmode_A << 6) | (outmode_B << 4) | (mode & 3) ;
TCCR1B = (capture_mode << 6) | ((mode & 0xC) << 1) | clock_mode ;

Note that timer2 has different prescaler codes from all the other timers - consult the
datasheet.
The output modes are 2-bit values affecting the pin relevant to that channel, with
following meanings
code meaning
00
01
10
11

## The 16 modes are summarized in this table.

code
Mode
0
Normal
1
PWM, Phase Correct, 8-bit

## TOP Update of OCR1x TOV1 Flag Set

0xFFFF Immediate
MAX
0x00FF TOP
BOTTOM

code
Mode
TOP Update of OCR1x TOV1 Flag Set
2
PWM, Phase Correct, 9-bit
0x01FF TOP
BOTTOM
3
PWM, Phase Correct, 10-bit
0x03FF TOP
BOTTOM
4
CTC
OCR1A Immediate
MAX
5
Fast PWM, 8-bit
0x00FF BOTTOM
TOP
6
Fast PWM, 9-bit
0x01FF BOTTOM
TOP
7
Fast PWM, 10-bit
0x03FF BOTTOM
TOP
8
PWM, Phase and Frequency Correct ICR1
BOTTOM
BOTTOM
9
PWM, Phase and Frequency Correct OCR1A BOTTOM
BOTTOM
10 PWM, Phase Correct
ICR1
TOP
BOTTOM
11
PWM, Phase Correct
OCR1A TOP
BOTTOM
12 CTC
ICR1
Immediate
MAX
13 (Reserved)
14 Fast PWM
ICR1
BOTTOM
TOP
15 Fast PWM
OCR1A BOTTOM
TOP
Note that the TOV1 flag is set when the counter overflows, normally when it clocks round to zero or
when it reaches MAX or TOP depending on the mode. This, if enabled, triggers
the TIMER1_OVF_vect interrupt.

## Pulse Width Modulation - PWM

There seem to be a bewildering variety of PWM modes, but the basic varieties are
fast, phase-correct and frequency-and-phase-correct. Fast simply counts up
tillTOP and resets to zero (BOTTOM). The others count up to TOP and then count
down to BOTTOM and then count up again, etc. This keeps the waveform symmetric
as the duty-cycle varies, but has nearly twice the period of the equivalent fast more.
Frequency-correct really means frequency-settable in that the value of TOP is not
constrained to a power of two but is controlled by either ICR1 or OCR1A in
particular the value of TOP is carefully changed only at a safe point when TCNT1 ==
BOTTOM. Several of the modes set the counter to being effectively 8, 9 or 10 bits.
The default arrangement on the Arduino is 8-bit phase-correct (or on timer0, 8-bit
fast).
Counting
In normal and CTC modes the counter is simply counting and not controlling the
PWM output channels - in CTC mode TOP can be controlled. To truly count the
clock configuration should be set to the external clock options uses pin T1 which is
Arduino pin 5. In these modes the overflow interrupt only happens if the counter
reaches MAX (0xFFFF) - if this is handled then more than 16 bits of counting is
possible with the aid of software. You can measure frequency, say, by using timer1 to
count input pulses this way and sample the result in an overflow interrupt on a
different timer (which is used as a timer not a counter).

Timing
Any mode can be used for timing if the clock source is set appropriately (prescaled
system clock). Timing is simply counting system clocks. Accurate timing requires the
system clock is crystal-controlled. The new Arduino Uno doesn't have a crystal clock
and is not suitable for more than 0.2% accuracy. Most crystal-derived clocks give
better than 0.01% accuracy. For times to microsecond accuracy a prescale of 1 or 8 is
needed and an interrupt handler should be used since polling is likely too slow and
other interrupts generate jitter.

Blog Entry
Working with Atmel AVR Microcontroller Basic Pulse Width Modulation (PWM) Peripheral
April 30, 2011 by rwb, under Microcontroller.

Pulse Width Modulation (PWM) is a technique widely used in modern switching circuit to control the amount of
power given to the electrical device. This method simply switches ON and OFF the power supplied to the
electrical device rapidly. The average amount of energy received by the electrical device is corresponding to the
ON and OFF period (duty cycle); therefore by varying the ON period i.e. longer or shorter, we could easily
control the amount of energy received by the electrical device. The Light Emitting Diode (LED) will respond to
this pulse by dimming or brighten its light while the electrical motor will respond to this pulse by turning its
rotor slow or fast.

The above picture show a typical Pulse Width Modulation (PWM), the PWM duty cycle is the proportion of the
signal ON time to one period (T) of the signal time. The duty cycle will be higher when the ON time is longer
than the OFF time and vice versa. Duty cycle is expressed in percentage; therefore you could define the duty
cycle of PWM signal as follow:

The 100% PWM duty cycle means its fully ON and we could say that 100% of the power or energy is delivered
to the electrical device, while 15% duty cycle means only 15% of the power is being delivered to the electrical
device. This average in power could be presented as average in voltage as follow:
The PWM signal normally has a fixed frequency (period) with a duty cycle that could vary from 0% to 100%.
Now you understand that by just adjusting the PWM duty cycle we could easily control the LED brightness or
the electrical motor spinning speed.
Today most of modern microcontroller has a build in PWM peripheral inside; this make generating PWM signal is
become easy and straightforward, you could read more about non microcontroller PWM base generator on The
LM324 Quad Op-Amp Line Follower Robot with Pulse Width Modulation article on this blog. On this tutorial we are going to

use Atmel AVR ATMega168 microcontroller which support up to 6 PWM output simultaneously and at the end of
this tutorial we will take advantage of all the available PWM peripheral to make a nice RGB LED Light and Sound
Show.
The AVR Microcontroller PWM Peripheral
Most of the microcontroller PWM peripheral depends on the TIMER peripheral to provide the PWM signals
frequency. The PWM peripheral will use the TIMER counter register (TCNT) as a digital step-up /down and
continuously compare to the pre-determine duty cycle register (OCR output compare register) value.
When TCNT equal to OCR value the wave generator circuit will set (ON) or reset (OFF) the corresponding
microcontroller PWM I/O ports. The following picture show a simplified version of Atmel AVR ATMega168
microcontroller PWM peripheral (please refer to the Atmel ATMega48/88/168/328 datasheet for more
information):
Each of the AVR ATMega168 microcontrollers TIMER has two PWM channels named channel A and channel B,
where each channel has its own output compare register (OCR). From the diagram above you could see that
both channel share the same TIMER counter register (TCNT), this mean you could only have one PWM
frequency for each TIMER (TIMER0, TIMER1, and TIMER2) but you could have different duty cycle on each
channel (A and B). The AVR ATMega48/88/168/328 microcontroller provides three PWM modes which are Fast
PWM Mode, Phase Correct PWM Mode, and Phase and Frequency Correct Mode. The last mode is only available
on TIMER1 (16-bit timer). Ok before we continue lets list down the hardware and software needed for this
tutorial:
1. AVRJazz Mega168 or AVRJazz Ultimate 28P board (I used in this project), you could also use any AVR
ATMega168 board or bare ATMega168, whatever available to you (the electronics components listed here is just
for the final project)
3. Resistors: 10K (1), 15K (1), and 18 (1)
4. One 10K Trimpot
5. Non polar Capacitor 0.1uF (3) and 0.01uF (1)
6. Polar Capacitor 220uF/16V (1) and 10uF/16V (1)
7. One 5 mm RGB LED
8. National Semiconductor LM386 IC
9. One white ping-pong ball for defusing the RGB LED light
10. One Speaker
11. The latest Atmel AVRStudio (in this project I used v4.18) and WinAVR GNU-C Compiler (in this project I
used WinAVR 20100110)
12. AVR Microcontroller Programmer
13. Atmel ATMega48/88/168/328 and LM386 datasheet.
The AVR Fast PWM Mode
The AVR fast PWM mode could generate the most high frequency PWM waveform compared to the other two
PWM modes (i.e. Phase Correct or Phase and Frequency Correct mode). This PWM mode simply uses the TIMER
counter register (TCNTn, where n represent the TIMER 0, TIMER1, and TIMER2 respectively) incremental value
which is start from 0x00 (BOTTOM) to 0xFF (8-bit TOP) or 0xFFFF (16-bit TOP).
When the TIMER counter register reach the output compare register (OCRnA or OCRnB) value then the wave
generator circuit will CLEAR (logical low) the output compare bit channel (OCnA or OCnB). When the TIMER
counter register value reach the TOP value then it will SET (logical high) the output compare bit channel and
the whole process will repeat again from BOTTOM. This PWM generation process could be shown on this
following diagram:
As shown on the diagram above, the behavior of output compare bit channel (OCnA or OCnB) output could be
set to non-inverting (CLEAR and SET) or inverting (SET and CLEAR) mode by setting the compare match
channel bit (COMnA1, COMnA0, COMnB1, and COMnB0) on Timer/Counter register A (TCCRnA). The Fast PWM

mode could be set by setting the wave generation mode bit (WGM01 andWGM00) on Timer/Counter register A
(TCCRnA) and WGM02 bit on TCCRnB register. When the TIMER counter register (TCNTn) equal to Output
Compare Register (OCRnA or OCRnB) it will generate the Output Compare interrupt and when
the TCNTn register reach TOP it will generate the TIMER overflow interrupt (TOV).
As you see at the Atmel AVR microcontroller PWM peripheral diagram above when we update the Output
Compare Register (OCRnA and OCRnB) value, the value will be updated on the Output Compare Register Buffer
first and when the TIMER0 Counter Register (TCNT0) reach TOP then the OCRn register will be updated with
the OCRn buffer value and at the same time the Output Compare Bits (OCnA or OCnB) will be set.
On this following C code, we are going to use TIMER0 Fast PWM mode on both channel A and channel B.
//***************************************************************************
// File Name
: avrpwm01.c
// Version
: 1.0
// Description : AVR TIMER0 Fast PWM Mode
// Author
: RWB
// Target
: AVRJazz Ultimate 28PIN Board
// Compiler
: AVR-GCC 4.3.3; avr-libc 1.6.7 (WinAVR 20100110)
// IDE
: Atmel AVR Studio 4.18
// Programmer
: AVRJazz Mega168 STK500 v2.0 Bootloader
//
: AVR Visual Studio 4.18, STK500 programmer
// Last Updated : 21 March 2011
//***************************************************************************
#include <avr/io.h>
#include <util/delay.h>
int main(void)
{
unsigned char duty_cyc_a,duty_cyc_b;
// Initial PORT Used
DDRD = 0b11111111;
// Set PORTD: Output
PORTD = 0x00;
// Initial TIMER0 Fast PWM
// Fast PWM Frequency = fclk / (N * 256), Where N is the Prescaler
// f_PWM = 11059200 / (64 * 256) = 675 Hz
TCCR0A = 0b10100011; // Fast PWM 8 Bit, Clear OCA0/OCB0 on Compare Match, Set on TOP
TCCR0B = 0b00000011; // Used 64 Prescaler
TCNT0 = 0;
// Reset TCNT0
OCR0A = 0;
// Initial the Output Compare register A & B
OCR0B = 0;
duty_cyc_a=0; // Initial Duty Cycle for Channel A
duty_cyc_b=255;
// Initial Duty Cycle for Channel B
for(;;) {
// Loop Forever
while(duty_cyc_a < 255) {
OCR0A=duty_cyc_a++;
OCR0B=duty_cyc_b--;
_delay_ms(10);
}
while(duty_cyc_b < 255) {
OCR0A=duty_cyc_a--;
OCR0B=duty_cyc_b++;
_delay_ms(10);
}
}
return 0;
}
/* EOF: avrpwm01.c */

## // Standard Return Code

The TIMER0 Fast PWM mode is activated by setting the Wave Generation Mode bits WGM02=0,WGM01=1,
and WGM00 = 1 on TIMER0 Timer/Counter control Registers (TCCR0A and TCCR0B) as follow:
// Initial TIMER0 Fast PWM
// Fast PWM Frequency = fclk / (N * 256), Where N is the Prescaler
// f_PWM = 11059200 / (64 * 256) = 675 Hz
TCCR0A = 0b10100011; // Fast PWM 8 Bit, Clear OCA0/OCB0 on Compare Match, Set on TOP
TCCR0B = 0b00000011; // Used 64 Prescaler

Therefore by assigning the Clock Set Bit CS02 = 0, CS01 = 1, and CS00 = 1 respectively, we tell the TIMER0 to
use 64 as a prescaler, therefore you could calculate the PWM frequency if we use 11059200 Hz external crystal
as follow:
PWM Frequency = Freq Clock / (prescaler x 256) = 11059200 / (64 x 256) = 675 Hz
You could freely choose or experiment with any PWM frequency that work best with the electrical devices that
you want to control with the Fast PWM mode signal.
One of disadvantage using the Fast PWM mode to generate the PWM signal is the PWM phase is shifted when
we change the PWM duty cycle. This because when the TIMER0 Counter Register (TCNTn) reach TOP and start
from BOTTOM it will always SET (or CLEAR) the Output Compare Bits (OCnA or OCnB) despite the Output
Compare Register (OCRnA and OCRnB) value; therefore when we change the duty cycle in Fast PWM mode the
PWM signal phase is always shifted as illustrated on this following diagram:
This make the Fast PWM mode is not suitable when we want to use for controlling the motor speed precisely;
therefore on our next discussion we will correct this shifted phase effect by using the AVR microcontroller Phase
Correct PWM mode for generating the PWM signal.
Now as you understand of how to use the Fast PWM mode on TIMER0, you could easily adapt this principal to
TIMER1 (16-bit) and TIMER2 (8-bit). Please refer to the Atmel ATMega48/88/168/328 datasheet for complete
information.
The AVR Phase Correct PWM Mode
Differ from the Fast PWM Mode, the Phase Correct PWM mode is using dual slope TIMER counter. Basically the
TIMER counter register (TCNTn) will increase its value (up counter) from BOTTOM to TOP and then decrease its
value (down counter) from TOP to BOTTOM. When the TIMER counter register equal to the Output Compare
Register (OCRnA and OCRnB) then the wave generator bit will simply toggle the Output Compare channel
(OCnA and OCnB) as shown on this following diagram:
As shown on the diagram above you could see that the Phase Correct PWM mode will have half of the PWM
signal frequency compared to the fast PWM mode. Because of the dual slope technique used in the Phase
Correct PWM mode to generate the PWM signal, therefore the phase correct PWM mode is more precision and
suitable to be used as a motor controller, because as we change the PWM signal duty cycle the phase between
each duty cycles remain the same as illustrated on this following diagram:

On this following C code, we are going to use TIMER0 Phase Correct PWM mode on both channel A and channel
B.
//***************************************************************************
// File Name
: avrpwm02.c
// Version
: 1.0
// Description : AVR TIMER0 Phase Correct PWM Mode
// Author
: RWB
// Target
: AVRJazz Ultimate 28PIN Board
// Compiler
: AVR-GCC 4.3.3; avr-libc 1.6.7 (WinAVR 20100110)
// IDE
: Atmel AVR Studio 4.18
// Programmer
: AVRJazz Mega168 STK500 v2.0 Bootloader
//
: AVR Visual Studio 4.18, STK500 programmer
// Last Updated : 21 March 2011
//***************************************************************************
#include <avr/io.h>
#include <util/delay.h>
int main(void)
{
unsigned char duty_cyc_a,duty_cyc_b;
// Initial PORT Used
DDRD = 0b11111111;
// Set PORTD: Output
PORTD = 0x00;
// Initial TIMER0 Phase Correct PWM
// Fast PWM Frequency = fclk / (N * 510), Where N is the Prescaler
// f_PWM = 11059200 / (64 * 510) = 338.82 Hz
TCCR0A = 0b10100001; // Phase Correct PWM 8 Bit, Clear OCA0/OCB0 on Compare Match, Set on
TOP
TCCR0B = 0b00000011; // Used 64 Prescaler
TCNT0 = 0;
// Reset TCNT0

OCR0A = 0;
OCR0B = 0;
duty_cyc_a=0;
duty_cyc_b=255;

## // Initial the Output Compare register A & B

// Initial Duty Cycle for Channel A
// Initial Duty Cycle for Channel B

for(;;) {
// Loop Forever
while(duty_cyc_a < 255) {
OCR0A=duty_cyc_a++;
OCR0B=duty_cyc_b--;
_delay_ms(10);
}
while(duty_cyc_b < 255) {
OCR0A=duty_cyc_a--;
OCR0B=duty_cyc_b++;
_delay_ms(10);
}
}
return 0;
}
/* EOF: avrpwm02.c */

## // Standard Return Code

The TIMER0 Phase Correct PWM mode is activated by setting the Wave Generation Mode
bits WGM02=0,WGM01=0, and WGM00 = 1 on TIMER0 Timer/Counter control Registers (TCCR0A and TCCR0B)
as follow:
// Initial TIMER0 Phase Correct PWM
// Fast PWM Frequency = fclk / (N * 510), Where N is the Prescaler
// f_PWM = 11059200 / (64 * 510) = 338.82 Hz
TCCR0A = 0b10100001; // Phase Correct PWM 8 Bit, Clear OCA0/OCB0 on Compare Match, Set on TOP
TCCR0B = 0b00000011; // Used 64 Prescaler
Therefore by assigning the Clock Set Bit CS02 = 0, CS01 = 1, and CS00 = 1 respectively, we tell the TIMER0 to
use 64 as a prescaler, therefore the Phase Correct PWM frequency could be calculated as follow:
PWM Frequency = Freq Clock / (prescaler x 510) = 11059200 / (64 x 510) = 338.82 Hz
Again you could easily adapt this principal to TIMER1 (16-bit) and TIMER2 (8-bit) as well (please refer to Atmel
ATMega48/88/168/328 datasheet for complete information).
The AVR Phase and Frequency Correct PWM Mode
The Phase and Frequency Correct PWM Mode feature is only available on TIMER1 (16-bit). Basically the Phase
and Frequency Correct PWM mode use the same dual slope technique used in Phase Correct PWM mode to
generate the PWM signal. These two modes actually are identical if we never change the PWM signal frequency,
but if we need to change the PWM signal frequency on fly, then we need to use the AVR ATMega168
microcontroller Phase and Frequency Correct mode to generate the PWM signal.
Differ from the Phase Correct PWM Mode, in Phase and Frequency Correct PWM Mode the Output Compare
Register (OCRnA and OCRnB) is updated from the buffer when the Timer Counter Register (TCNTn) reaches
BOTTOM instead of TOP in Phase Correct PWM Mode. The frequency could be change by changing the TOP
value, here you could understand why we need to use the Phase and Frequency Correct PWM mode, because as
we change the frequency and at the same time the PWM peripheral update the Output Compare register
(OCRnA and OCRnB) then there will be a glitch in the PWM frequency signal.
In Phase and Frequency Correct PWM mode because the Output Compare Register is updated at the BOTTON
therefore the rising and falling length of the PWM signal is always equal this result in frequency being corrected
when we change the frequency on fly.
Typically in controlling the electrical device with PWM signal we seldom change the PWM frequency on fly,
therefore the common application for this mode is to generate the sound. On this following C code example I
used the Phase and Frequency Correct PWM to generated tone. Another example of using this PWM mode could
be read in AVR Twinkle-Twinkle Song Using PWM Project article on this blog.
//***************************************************************************
// File Name
: avrpwm03.c
// Version
: 1.0
// Description : AVR TIMER0 Phase and Frequency Correct PWM Mode

// Author
: RWB
// Target
: AVRJazz Ultimate 28PIN Board
// Compiler
: AVR-GCC 4.3.3; avr-libc 1.6.7 (WinAVR 20100110)
// IDE
: Atmel AVR Studio 4.18
// Programmer
: AVRJazz Mega168 STK500 v2.0 Bootloader
//
: AVR Visual Studio 4.18, STK500 programmer
// Last Updated : 21 March 2011
//***************************************************************************
#include <avr/io.h>
#include <util/delay.h>
// Notes Frequency from http://www.phy.mtu.edu/~suits/notefreqs.html
// The Original frequency value (decimal) is converted to the integer value
#define C4 262
#define Cc4 277
#define D4 294
#define Dc4 311
#define E4 330
#define F4 349
#define Fc4 370
#define G4 392
#define Gc4 415
#define A4 440
#define Ac4 466
#define B4 494
#define C5 523
#define Cc5 554
#define D5 587
#define Dc5 622
#define E5 659
#define F5 698
#define Fc5 740
#define G5 783
#define Gc5 831
#define A5 880
#define Ac5 932
#define B5 988
#define C6 1047
// LED Display variables
unsigned char ledstat,led_out;
// PlayNotes function
void PlayNotes(unsigned int note_frequency,unsigned int duration)
{
unsigned int top_value,duty_cycle;
// Calculate the Top Value
// TOP = Board Clock Frequency / (2 x N x Notes Frequency)
// Where N is Prescler: 8
topvalue=(F_CPU / (16 * note_frequency));
// Reset the TIMER1 16 bit Counter
TCNT1H = 0;
TCNT1L = 0;
// Set the TIMER1 Counter TOP value on ICR1H and ICR1L
ICR1H = (top_value >> 8 ) & 0x00FF;
ICR1L = top_value;
// Set the TIMER1 PWM Duty Cycle on OCR1AH and OCR1AL
// Always use half of the TOP value (PWM Ducty Cycle ~ 50%)
duty_cycle=top_value / 2;
OCR1AH=(duty_cycle >> 8 ) & 0x00FF;
OCR1AL=duty_cycle;
// Turn ON the TIMER1 Prescaler of 8
TCCR1B |= (1<<CS11);
// Notes Delay Duration
_delay_ms(duration);
// Turn OFF the TIMER1 Prescaler of 8
TCCR1B &= ~(1<<CS11);
// Delay Between Each Notes 1/5 duration
_delay_ms(duration * 1/5);
}
// Display LED function
void DisplayLED(void)

{
if (ledstat) {
PORTD=led_out;
led_out=led_out << 1;
if (led_out >= 0x80) ledstat=0;
} else {
PORTD=led_out;
led_out=led_out >> 1;
if (led_out <= 0x01) ledstat=1;
}

}
int main(void)
{
unsigned int notes[25]={C4,Cc4,D4,Dc4,E4,F4,Fc4,G4,Gc4,A4,Ac4,B4,
C5,Cc5,D5,Dc5,E5,F5,Fc5,G5,Gc5,A5,Ac5,B5,C6};
int icount;
unsigned char pstat;
unsigned int idelay;
// Initial PORT Used
DDRD = 0b11111111;
// Set PORTD as Output
PORTD = 0b00000000;
DDRB = 0b11111110;
// Set PB0 as Input and other as Output
PORTB = 0b00000000;
// Use Free running Mode
// Disable digital input on Channel 0
DIDR0 = 0b00000001;
// Initial TIMER1 Phase and Frequency Correct PWM
// Set the Timer/Counter Control Register
TCCR1A = 0b11000000; // Set OC1A when up counting, Clear when down counting
TCCR1B = 0b00010000; // Phase/Freq-correct PWM, top value = ICR1, Prescaler: Off
// Initial Variables
icount=0;
pstat=1;
led_out=1;
ledstat=1;
for(;;) {
// Loop Forever
// Reading User Trimpot on Analog Channel 0
// wait until convertion complete ADSC=0 -> Complete
DisplayLED();
if (pstat) {
PlayNotes(notes[icount++],idelay);
if (icount > 24) {
icount=24;
pstat=0;
}
} else {
PlayNotes(notes[icount--],idelay);
if (icount < 0) {
icount=0;
pstat=1;
}
}
}
return 0;
}
/* EOF: avrpwm03.c */

## // Standard Return Code

To generate a controllable tone, we need to produce the exact frequency on each notes, the notes frequency
could be found at this website address http://www.phy.mtu.edu/~suits/notefreqs.html. In order to generate
the C5 note we have to produce the PWM frequency of 523.23 Hz at 50% duty cycle. Therefore by setting the
Wave Generation Mode Bits WGM13=1, WGM12=0, WGM11=0, and WGM10=0respectively, we choose the

TIMER1 Phase and Frequency Correct PWM mode which has TOP value set on TIMER1 Input Capture
Registers ICR1H and ICR1L; the Pulse Width is set on TIMER1 Output Compare Registers OCR1A and OCR1B.
With the precaler being set to 8 and board frequency of 1109200 Hz, we could easily calculate the TOP value of
theC5 note as follow:
PWM Frequency = Freq Clock / (2 x N x TOP) = Freq Clock / (16 x TOP)
Or
TOP = Freq Clock / (16 x PWM Frequency) = 11059200 / (16 x 523) = 1322
Now by assigning the TOP value to the Input Capture Registers (ICR1H and ICR1L) and half of the TOP value to
the Output Compare Register (OCR1AH and OCR1AL) we could produce the C5 notes with 50% duty cycle as
shown on this following C code:
// Calculate the Top Value
// TOP = Board Clock Frequency / (2 x N x Notes Frequency)
// Where N is Prescler: 8
topvalue=(F_CPU / (16 * note_frequency));
// Set the TIMER1 Counter TOP value on ICR1H and ICR1L
ICR1H = (top_value >> 8 ) & 0x00FF;
ICR1L = top_value;
// Set the TIMER1 PWM Duty Cycle on OCR1AH and OCR1AL
// Always use half of the TOP value (PWM Ducty Cycle ~ 50%)
duty_cycle=top_value / 2;
OCR1AH=(duty_cycle >> 8 ) & 0x00FF;
OCR1AL=duty_cycle;
Because the ICR1H and ICR1L are the 8-bit registers, therefore we use the C language shift right operator to
assign the upper 8-bit top_value to ICR1H and the lower 8-bit TOP value to ICR1L. We use similar principal to
both OCR1H and OCR1L for the PWM duty cycle value (duty_cycle). The complete C code is implemented
in PlayNotes() function, which accept the frequency and duration parameters to produce the needed sound. The
AVR ATMega168 microcontroller ADC peripheral is used to control the playing notes delay by passing the
trimpot voltage reading connected to ADC channel 0 (PC0), we could control the notes duration as shown on
this following C code:
// Reading User Trimpot on Analog Channel 0
// wait until convertion complete ADSC=0 -> Complete
...
...
PlayNotes(notes[icount++],idelay);
Converter AVR C Programming articles on this blog
The RGB LED Light and Sound Show
On this last tutorial we will put all together the AVR ATMega168 basic PWM lessons that weve learned and
making some interesting RGB LED and Sound show as shown on this following schematic:
This RGB light and Sound show project used the well known LM386 linear amplifier IC from National
Semiconductor which recently has been acquired by Texas Instrument (April 2011) to produce a quite loud
sound from the TIMER1 Phase and Correct Frequency PWM mode through the speaker. The TIMER1 Output
Compare Channel A (PB1) PWM signal is being passed through the passive low pass filter (is also called an
integrator circuit for a non sinusoidal input signal such as square wave and triangle wave) in order to shape the
square wave forms to become the sinusoidal wave forms before being amplified by the LM386 IC.
To make the low pass filter (LPF) become a good integrator circuit we have to choose the LPF cutoff frequency
much less than the lowest frequency produced by the PWM signal but at the same time still produce an
adequate signal level to drive the LM386 amplifier input. The cutoff frequency of LPF could be calculated as this
following formula:
Frequency = 1 / (2 x pi x RC), where pi = 3.14159, R is resistance in Ohm, and C is capacitance in Farad

The lowest frequency produce by PWM signal is 262 Hz (C4 note), therefore by choosing R = 15K and C =
0.1uF, we could calculate the LPF cutoff frequency as follow:
Frequency = 1 / (2 x 3.14159 x 15000 x 0.0000001) = 106.10 Hz
This method is used to ensure that we could get a quite nice sound produced on the speaker instead of just
using raw square wave signal as shown on this following oscilloscope picture:
Each of the RGB LED cathodes is driven by TIMER0 Fast PWM channel A, channel B, and TIMER2 Phase Correct
PWM channel B respectively. The following is the complete C code for our RGB LED Light and Sound Show final
project:
//***************************************************************************
// File Name
: avrpwm04.c
// Version
: 1.0
// Description : AVR TIMER0 Phase and Frequency Correct PWM Mode
// Author
: RWB
// Target
: AVRJazz Ultimate 28PIN Board
// Compiler
: AVR-GCC 4.3.3; avr-libc 1.6.7 (WinAVR 20100110)
// IDE
: Atmel AVR Studio 4.18
// Programmer
: AVRJazz Mega168 STK500 v2.0 Bootloader
//
: AVR Visual Studio 4.18, STK500 programmer
// Last Updated : 22 March 2011
//***************************************************************************
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <stdlib.h>
// Notes Frequency from http://www.phy.mtu.edu/~suits/notefreqs.html
// The Original frequency value (decimal) is converted to the integer value
#define C4 262
#define Cc4 277
#define D4 294
#define Dc4 311
#define E4 330
#define F4 349
#define Fc4 370
#define G4 392
#define Gc4 415
#define A4 440
#define Ac4 466
#define B4 494
#define C5 523
#define Cc5 554
#define D5 587
#define Dc5 622
#define E5 659
#define F5 698
#define Fc5 740
#define G5 783
#define Gc5 831
#define A5 880
#define Ac5 932
#define B5 988
#define C6 1047
volatile unsigned char duty_cyc_a,duty_cyc_b, duty_cyc_c,led_a,led_b,led_c;
volatile unsigned int tempo;
// TIMER1 Overflow Interrupt
ISR(TIMER1_OVF_vect)
{
cli();
// Disable Interrupt
// Reading User Trimpot on Analog Channel 0
// wait until convertion complete ADSC=0 -> Complete
if (led_a) {
if (duty_cyc_a < 255) {
OCR0A=duty_cyc_a++;

} else {
led_a=0;
}
} else {
if (duty_cyc_a > 0) {
OCR0A=duty_cyc_a--;
} else {
led_a=1;
duty_cyc_a=TCNT1L;
}
}
if (led_b) {
if (duty_cyc_b < 255) {
OCR0B=duty_cyc_b++;
} else {
led_b=0;
}
} else {
if (duty_cyc_b > 0) {
OCR0B=duty_cyc_b--;
} else {
led_b=1;
duty_cyc_b=(unsigned char) rand() % 255; ;
}
}
if (led_c) {
if (duty_cyc_c < 255) {
OCR2B=duty_cyc_c++;
} else {
led_c=0;
}
} else {
if (duty_cyc_c > 0) {
OCR2B=duty_cyc_c--;
} else {
led_c=1;
duty_cyc_c=TCNT1H;
}
}
sei();

// Enable Interrupt
}
// PlayNotes function
void PlayNotes(unsigned int note_frequency,unsigned int duration)
{
unsigned int top_value,duty_cycle;
// Calculate the Top Value
// TOP = Board Clock Frequency / (2 x N x Notes Frequency)
// Where N is Prescler: 8
topvalue=(F_CPU / (16 * note_frequency));
// Reset the TIMER1 16 bit Counter
TCNT1H = 0;
TCNT1L = 0;
// Set the TIMER1 Counter TOP value on ICR1H and ICR1L
ICR1H = (top_value >> 8 ) & 0x00FF;
ICR1L = top_value;
// Set the TIMER1 PWM Duty Cycle on OCR1AH and OCR1AL
// Always use half of the TOP value (PWM Ducty Cycle ~ 50%)
duty_cycle=top_value / 2;
OCR1AH=(duty_cycle >> 8 ) & 0x00FF;
OCR1AL=duty_cycle;
// Turn ON the TIMER1 Prescaler of 8
TCCR1B |= (1<<CS11);
// Notes Delay Duration
_delay_ms(duration);
// Turn OFF the TIMER1 Prescaler of 8
TCCR1B &= ~(1<<CS11);
// Delay Between Each Notes
_delay_ms(duration * 1/5);

}
int main(void)
{
unsigned char song_index;
// Initial PORT Used
DDRD = 0b11111111;
// Set PORTD as Output
PORTD = 0b00000000;
DDRB = 0b11111110;
// Set PB0 as Input and other as Output
PORTB = 0b00000000;
// Use Free running Mode
// Disable digital input on Channel 0
DIDR0 = 0b00000001;
// Initial TIMER0 Fast PWM
// Fast PWM Frequency = fclk / (N * 256), Where N is the prescaler
// f_PWM = 11059200 / (64 * 256) = 675 Hz
TCCR0A = 0b10100011; // Fast PWM 8 Bit, Clear OCA0/OCB0 on Compare Match, Set on TOP
TCCR0B = 0b00000011; // Used 64 Prescaler
TCNT0 = 0;
// Reset TCNT0
OCR0A = 0;
// Initial the Output Compare register A & B
OCR0B = 0;
// Initial TIMER1 Phase and Frequency Correct PWM
// Set the Timer/Counter Control Register
TCCR1A = 0b11000000; // Set OC1A when up counting, Clear when down counting
TCCR1B = 0b00010000; // Phase/Freq-correct PWM, top value = ICR1, Prescaler: Off
TIMSK1 = (1<<TOIE1); // Enable Overflow Interrupt
// Initial TIMER2 Phase Correct PWM Mode
// Phase Correct PWM Frequency = fclk / (N * 512), Where N is the prescaler
// f_PWM = 11059200 / (64 * 512) = 337.5 Hz
TCCR2A = 0b00100001; // Fast PWM 8 Bit, Clear OC2B on Compare Match, Set on TOP
TCCR2B = 0b00000011; // Used 64 Prescaler
TCNT2 = 0;
// Reset TCNT2
OCR2B = 0;
// Initial the Output Compare register A & B
duty_cyc_a=(unsigned char) rand() % 255;
led_a=1;
duty_cyc_b=(unsigned char) rand() % 255;
led_b=1;
duty_cyc_c=(unsigned char) rand() % 255;
led_c=1;
sei();
// Enable Interrupt
song_index=0;
tempo=0;
for(;;) {
// Loop Forever
// Playing "What a Wonderfull World" Song Notes
PlayNotes(G4,300 + tempo); PlayNotes(A4,100 + tempo);
PlayNotes(C5,500 + tempo); PlayNotes(C5,450 + tempo);
PlayNotes(G5,1150 + tempo); PlayNotes(A5,350 + tempo);
PlayNotes(A5,350 + tempo); PlayNotes(A5,150 + tempo);
PlayNotes(G5,1150 + tempo); PlayNotes(F5,450 + tempo);
PlayNotes(F5,300 + tempo); PlayNotes(F5,250 + tempo);
PlayNotes(E5,1150 + tempo); PlayNotes(D5,600 + tempo);
PlayNotes(E5,175 + tempo); PlayNotes(D5,100 + tempo);
PlayNotes(C5,1050 + tempo);
PlayNotes(C5,550 + tempo); PlayNotes(C5,175 + tempo);
PlayNotes(C5,100 + tempo); PlayNotes(C5,100 + tempo);
PlayNotes(C5,150 + tempo); PlayNotes(C5,1300 + tempo);
PlayNotes(C5,600 + tempo); PlayNotes(B4,200 + tempo);
PlayNotes(C5,200); PlayNotes(D5,200 + tempo);
if (song_index >= 1) {
PlayNotes(C5,1600 + tempo);
if (song_index == 3) {
_delay_ms(100 + tempo);
PlayNotes(C5,550 + tempo); PlayNotes(C5,175 + tempo);
PlayNotes(C5,100 + tempo); PlayNotes(C5,100 + tempo);
PlayNotes(C5,150 + tempo); PlayNotes(C5,1300 + tempo);
PlayNotes(C5,800 + tempo); PlayNotes(B4,400 + tempo);
PlayNotes(C5,300 + tempo); PlayNotes(D5,300 + tempo);
PlayNotes(C5,2300 + tempo);
song_index = 0;
} else {
song_index=2;

}
} else {
PlayNotes(E5,1100 + tempo); PlayNotes(E5,800 + tempo);
PlayNotes(D5,1600 + tempo);
song_index=1;
}
if (song_index == 2) {
_delay_ms(100 + tempo);
PlayNotes(C5,450 + tempo); PlayNotes(D5,150 + tempo);
PlayNotes(D5,50 + tempo); PlayNotes(D5,50 + tempo);
PlayNotes(D5,1 + tempo); PlayNotes(D5,1000 + tempo);
PlayNotes(G4,450 + tempo); PlayNotes(E5,150 + tempo);
PlayNotes(E5,50 + tempo); PlayNotes(E5,50 + tempo);
PlayNotes(E5,1 + tempo); PlayNotes(E5,1000 + tempo);
PlayNotes(C5,350 + tempo); PlayNotes(D5,250 + tempo);
PlayNotes(D5,100 + tempo); PlayNotes(D5,75 + tempo);
PlayNotes(D5,350 + tempo); PlayNotes(C5,150 + tempo);
PlayNotes(D5,250 + tempo); PlayNotes(E5,1000 + tempo);
PlayNotes(E5,250 + tempo); PlayNotes(G5,175 + tempo);
PlayNotes(A5,450 + tempo); PlayNotes(A5,100 + tempo);
PlayNotes(E5,150 + tempo); PlayNotes(G5,1000 + tempo);
PlayNotes(A5,100 + tempo); PlayNotes(A5,50 + tempo);
PlayNotes(E5,150 + tempo); PlayNotes(G5,1000 + tempo);
PlayNotes(A5,100 + tempo); PlayNotes(A5,50 + tempo);
PlayNotes(E5,150 + tempo); PlayNotes(G5,1000 + tempo);
PlayNotes(F5,450 + tempo); PlayNotes(E5,650);
PlayNotes(D5,1300 + tempo);
song_index =3;
}

return 0;
}
/* EOF: avrpwm04.c */

## // Standard Return Code

From the C code above you could see that we use all the available AVR ATMega168 microcontroller PWM
sources to drive both the RGB LED and at the same time playing What a Wonderful World song. By using the
TIMER1 overflow interrupt (TOIE1=1 in TIMER1 interrupt mask register TIMSK1) we could display the RGB LED
and at the same time playing the song notes.
The RGB LED PWM duty cycle is achieved by assigning both random value and the 16-bit TIMER1 counter value
(TCNT1H and TCNT1L) to the Output Compare Register (OCRn) in ISR(TIMER1_OVF_vect)function routine. With
this method we could get the desired RGB LED light effect which is depend on the song notes. Of course you
could experiment with other register value as well (e.g . ICR1H and ICR1Lregisters).
Now its time to watch all the basic AVR PWM experiments weve done on this following video:

## The Final Thought

Knowing the basic working principal of the Atmel AVR microcontroller PWM peripheral is one of knowledge that
should be learned by anyone who want to involve in the embedded world professionally or just as a hobbyists. I
hope this basic AVR PWM tutorial will give you a solid AVR PWM knowledge to be used in your next embedded
project.
Bookmarks and Share

## Нижнее меню

### Получите наши бесплатные приложения

Авторское право © 2021 Scribd Inc.