Vous êtes sur la page 1sur 9

Programming 16 bit timer on Atmega328 - Embedded projects from around the web

HOME

ARM CORTEX TUTORIALS

AVR TUTORIALS

MSP430 TUTORIALS

CONTACT FORM

FORUM

SEARCH

OK

CATEGORIES
Electro-Labs.com

Download SoloPCB Tools for FREE

68HC Projects
Arduino
ARM Cortex
ARM Cortex Tutorial

Programming 16 bit timer on Atmega328


BY ADMIN

DECEMBER 11, 2010

AVR TUTORIAL

ARM7 Projects
ARM9 Projects
AVR Projects
AVR Tutorial
BASIC Stamp
ChipKIT Projects

Atmega328 has one 16 bit timer which is more powerful comparing to 8 bit timers. 16 bit timer is called

CPLD Projects

Timer/Counter1. Counter1 has twice more bits than 8 bit Counter0, so you get more counts leading to longer

DSP Projects

duration and more precise timings.

dsPIC
FPGA Projects
Handy Circuits
Linux board projects

16 bit timer has pretty same functionality as Timer0 plus more specific ones. We wont be discussing Normal,

Misc

CTC, fast PWM and correct phase PWM modes as these are equivalent to Timer0. But lets focus on fresh things.
There we have a new module called Input Capture Unit along with Input Capture Flag (ICF1) interrupt and new
waveform generating mode called Phase and Frequency Correct PWM.

MSC-51 Projects
MSP430 Projects
MSP430 Tutorial

Dealing with 16 bit registers

Netduino

Working with 16 bit timer means dealing with 16 bit registers:

Other MCU Projects


PIC Projects

TCNT1: [TCNT1H and TCNT1L] Timer/Counter1;


OCR1A: [OCR1AH and OCR1AL] Output Compare Register 1 A;

PIC32
Uncategorized

OCR1B: [OCR1BH and OCR1BL] Output Compare Register 1 B;


ZiLOG
ICR1: [ICR1H and ICR1L] Input Capture Register 1
As AVR is 8 bit microcontroller it has an 8 bit bus structure. So whole 16 bit number cant be stored at once. When
programming in C you can usually write 16 bit number in simple manner like this:

MOST POPULAR

OCR1A=0x2564;
Using Direct Memory Access (DMA)

Compiler will sort this out automatically. But if you need to write separate bytes of 16 register then you need to
write high byte first and then low:

1
2

OCR1AH=0x25;

http://www.embedds.com/programming-16-bit-timer-on-atmega328/[06-06-2014 20:26:49]

in STM32 projects

Programming 16 bit timer on Atmega328 - Embedded projects from around the web

Programming AVR I2C interface

OCR1AL=0x64;

This is because 16 bit registers share one special TEMP register allowing to write 16 bit value at once, but using
two clock cycles. If you need to read 16 bit register first has to be low byte and then high.
Nucleo-F401RE Arduino shape with
Cortex-M4 power

Power of Input Capture unit


Input capture functionality is special feature of 16 timer. It gives a new way of using timer. It is using external
signal on ICP1 pin (or comparator output) to save current counter value to Input Capture Register ICR1 and

Using watchdog timer in your

generate interrupt (ICF1) of event.

projects

ARM Cortex Tutorials

V-USB based virtual COM port


In this very simplified diagram you can see how it works. What this is is good for? Actually this functionality is
used a lot when you need to measure signal frequency, duty cycle. Schema is simple save ICR1 at least two
register values when calling interrupts and then calculate what ever you want. Be sure to save Input Capture
Register Value before another event occurs as it will overwrite current value.

ARCHIVES
Select
Month
Select
Month

RECENT COMMENTS
jolin on Want more out of Raspberry Pi? try
Banana Pi
admin on What is Arduino Zero?
Danilo Recchia on What is Arduino Zero?
Michael Langley on Who May Seek For Spy
Earpiece?
Input capture may be triggered by rising or falling edge and even noise canceler may be used to make sure signal is

ebi on Cornell University AVR student projects

real. Ok, its time for example. Lets measure the duty cycle of signal fed in to ICP1 pin.

RECENT FORUM POSTS


admin On Topic: Multichannel ADC using DMA
on STM32- May 14, 2014
admin On Topic: Fixed social login issue.January 3, 2014
admin On Topic: Changed website theme.December 28, 2013

WE RECOMMEND
So we are going to capture three time-stamps needed to calculate duty cycle. One pair for measuring signal at level
1 and second pair of times-tamps to measure signal period. Seams easy. But there are some hidden stones. First
one what if signal frequency is to big. In this case we cannot do much in software level just external frequency
dividers would help. Second problem is when signal is too slow and one full timer count-up may not be enough.
Here we can find a solution by introducing software counter to keep timer overflow counts. So we are going to deal

http://www.embedds.com/programming-16-bit-timer-on-atmega328/[06-06-2014 20:26:49]

Programming 16 bit timer on Atmega328 - Embedded projects from around the web

with two interrupts that may overlap. Lets see how it goes:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74

#include <avr/io.h>
#include <avr/interrupt.h>
//Counts overflovs
volatile uint16_t T1Ovs1, T1Ovs2;
//Variables holding three timestamps
volatile uint16_t Capt1, Capt2, Capt3;
//capture Flag
volatile uint8_t Flag;
//Initialize timer
void InitTimer1(void)
{
//Set Initial Timer value
TCNT1=0;
//First capture on rising edge
TCCR1B|=(1<<ICES1);
//Enable input capture and overflow interrupts
TIMSK1|=(1<<ICIE1)|(1<<TOIE1);
}
void StartTimer1(void)
{
//Start timer without prescaller
TCCR1B|=(1<<CS10);
//Enable global interrutps
sei();
}

//capture ISR
ISR(TIMER1_CAPT_vect)
{
if (Flag==0)
{
//save captured timestamp
Capt1=ICR1;
//change capture on falling edge
TCCR1B&=~(1<<ICES1);
//reset overflows
T1Ovs2=0;
}
if (Flag==1)
{
Capt2=ICR1;
//change capture on rising edge
TCCR1B|=(1<<ICES1);
//save first overflow counter
T1Ovs1=T1Ovs2;
}
if (Flag==2)
{
Capt3=ICR1;
//stop input capture and overflow interrupts
TIMSK1&=~((1<<ICIE1)|(1<<TOIE1));
}
//increment Flag
Flag++;
}
//Overflow ISR
ISR(TIMER1_OVF_vect)
{
//increment overflow counter
T1Ovs2++;
}
int main(void)
{
//dutycycle result holder
volatile uint8_t DutyCycle;
InitTimer1();
StartTimer1();
while(1)
{
//calculate duty cycle if all timestamps captured
if (Flag==3)
{
DutyCycle=(uint8_t)((((uint32_t)(Capt2-Capt1)+((uint3
/((uint32_t)(Capt3-Capt1)+((uint32_t)T1Ovs2*0x100

http://www.embedds.com/programming-16-bit-timer-on-atmega328/[06-06-2014 20:26:49]

Programming 16 bit timer on Atmega328 - Embedded projects from around the web

75
76
77
78
79
80
81
82
83
84
85
86
87

//send Duty Cycle value to LCD or USART


//clear flag
Flag=0;
//clear overflow counters;
T1Ovs1=0;
T1Ovs2=0;
//clear interrupt flags to avoid any pending interrup
TIFR1=(1<<ICF1)|(1<<TOV1);
//enable input capture and overflow interrupts
TIMSK1|=(1<<ICIE1)|(1<<TOIE1);
}
}
}

You can see in example that measuring of DutyCycle practically doesnt depend on frequency well at some range.
If running an MCU at 16 MHz then measured waveform period can range from about 1s or less up to several
seconds considering that 16 bit interrupt counter can have max 65536 values. I dont say that this algorithm is
effective or good for any reasons. If you actually know the range of PWM frequency you can do way better
approach. This example is just to represent how input capture mode works.

Phase and frequency correct PWM


This may sound a little scary but this mode is really easy to understand. We talked a bit about Phase correct PWM
for Timer0. The main difference between phase correct and phase and frequency correct PWM is where the
compare values and top values are updated. In phase correct mode these values were updated when counter reaches
TOP value. In phase and frequency correct mode TOP values and compare values are updated when counter reaches
BOTTOM. This way wave form gets symmetrical in all periods. In this mode TOP value can be defined in two
ways by ICR1 register, which is free in this mode, or OCR1A. Usually it is better to use ICR1 register then you
have free OCR1A register to generate waveforms. But if you need to change PWM frequency pretty fast then use
OCR1A because it is double buffered. Lowest TOP value can be 3 while highest 0xFFFF. Lets make simple
example of phase and frequency correct mode. We will generate 500Hz PWM non inverted on OC1A and
inverted on OC1B pins.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

#include <avr/io.h>
void InitPort(void)
{
//Init PB1/OC1A and PB2/OC1B pins as output
DDRB|=(1<<PB1)|(1<<PB2);
}
void InitTimer1(void)
{
//Set Initial Timer value
TCNT1=0;
//set non inverted PWM on OC1A pin
//and inverted on OC1B
TCCR1A|=(1<<COM1A1)|(1<<COM1B1)|(1<<COM1B0);
//set top value to ICR1
ICR1=0x00FF;
//set corrcet phase and frequency PWM mode
TCCR1B|=(1<<WGM13);
//set compare values
OCR1A=0x0064;
OCR1B=0x0096;
}
void StartTimer1(void)
{
//Start timer with prescaller 64
TCCR1B|=(1<<CS11)|(1<<CS10);
}
int main(void)
{
InitPort();
InitTimer1();
StartTimer1();
while(1)
{
//do nothing
}
}

http://www.embedds.com/programming-16-bit-timer-on-atmega328/[06-06-2014 20:26:49]

Programming 16 bit timer on Atmega328 - Embedded projects from around the web

This is it with 16-bit timer. Its only a fraction of what timer can do. I bet you can run other modes by your own
now. So keep practicing. Codes to download are here[25KB].
Read

TAGGED 16 bit timer input capture, AVR 16 bit timer, AVR 16 timer interrupts, AVR correct phase correct frequency PWM, AVR
timer examples, AVR Tutorial.

Arduino 3-axis accelerometer logger

Say Merry Christmas with LED display

18 COMMENTS
DT
December 11, 2010 at 2:57 pm

You cant disable interrupts within an ISR. The final RETI instruction will set the global I bit.
Would you like me to act as a reviewer for your material? Id be happy to help. I truly appreciate your efforts
with the tutorials.

Pingback: Electronics-Lab.com Blog Blog Archive Programming 16 bit timer on Atmega328

ADMIN
December 11, 2010 at 6:59 pm

Yep somehow I missed that. Anyway I was thinking of doing DutyCycle calculations inside
ISR(TIMER1_CAPT_vect)interrupt so disabling and enabling wouldnt be an issue.
Reviewing would be great. Even a contributing one person job leads to more errors.

ADMIN
December 11, 2010 at 7:11 pm

Corrected the code. Instead disabling interrupts I stooped timer until Duty cycle is calculated. Then timer is
started again.

DT
December 11, 2010 at 8:16 pm

I dont think that will work. You are stopping the Timer1 clock but this does not stop CLKio, so the IC edge
detectors will still operate and generate interrupts. The conventional approach is to disable the IC interrupt

http://www.embedds.com/programming-16-bit-timer-on-atmega328/[06-06-2014 20:26:49]

Programming 16 bit timer on Atmega328 - Embedded projects from around the web

instead (ICIE1).

DT
December 11, 2010 at 9:08 pm

(my last comment disappeared?)


Disabling the Timer1 clock is not the same as disabling the interrupt. The Input Capture edge detectors operate
from CLKio, which keeps going. The conventional approach is to enable/disable the Input Capture Interrupt.

ADMIN
December 12, 2010 at 12:58 pm

(my last comment disappeared?) Could be captcha problem. Somehow it was disabled automatically as some
other comments.
Thank you for finding this error. It really keeps interrupting and capturing timestamps of stopped timer what
leads to wrong calculations. Just disabled TIMER1_CAPT interrupts to avoid this.

ADMIN
December 12, 2010 at 1:05 pm

Just curious. Are you finding these tutorials useful and clear enough? Feel free to add suggestions where to pay
more attention to: explanations, technical details, code examples.

DT
December 12, 2010 at 3:19 pm

Personally, I dont need your tutorials, but if someone else is going to the considerable effort to write them then
Im happy to lend my experience to the process. Im fortunate in having been into embedded software as a
career for many years, and have already fallen into all the usual pit-falls!
Incidentally, in your corrected code, you can remove the redundant Timer clock disables/enables (apart from the
initial one), but just before you re-enable the interrupt you should clear the ICF1 flag by writing to TIFR1. This
is because you will probably have an old interrupt pending that you want to ignore.

ADMIN
December 12, 2010 at 3:46 pm

These pit-falls is probably most important part when learning. Your experience is invaluable here. I hope you
will keep an eye in future.
Regarding corrections. I left Timer running but disabled the capture and overflow interrupts. After calculus
done cleared interrupt flags in case any presence of them and re-enabled interrupts. This should work. I
understand that program layout isnt the best but this intended to be only to show how input capture works.

DT
December 12, 2010 at 5:08 pm

You shouldnt use a read-modify-write for the timer interrupt flags register (for exactly the same reason as with
PIN registers). You may clear flags that you didnt intend to, and although it wont affect this example, its not
good to leave a trap set like this.
Sorry to be pedantic on things like this, but if people try to use this code for their own experiments, they will
have problems if they try to extend the functionality at all.

http://www.embedds.com/programming-16-bit-timer-on-atmega328/[06-06-2014 20:26:49]

Programming 16 bit timer on Atmega328 - Embedded projects from around the web

ADMIN
December 12, 2010 at 5:35 pm

Dont be sorry. This is very important to do it right at the very beginning. This is what a tutorial is for. Gladly
Im also pulling some bad habits out.
Fixed. Thanks.

DT
December 12, 2010 at 10:11 pm

The duty cycle calculation really doesnt need 64-bit integers. That one line causes the code to grow from ~500
bytes to ~5500 bytes!!! And I dont think it will work since the casts are performed too late, which will
therefore overflow the 16-bit multiply that will be implemented. Also, the overflows should be multiplied by
010000 instead of 0xFFFF.
Its still not pretty, but this works and saves about 4.5kB of program space:
DutyCycle = (uint8_t)((((uint32_t)(Capt2 Capt1) PLUS ((uint32_t)T1Ovs1 * 0x10000L)) * 100L) /
((uint32_t)(Capt3 Capt1) PLUS ((uint32_t)T1Ovs2 * 0x10000L)));
Ive used PLUS since they seem to get removed when posting.
Note: it is vital that the subtraction is performed in unsigned 16-bit math then cast to 32-bit this then gives
the right answer even when Capt1 is larger than Capt2.

ADMIN
December 12, 2010 at 11:46 pm

Im wandering why I casted to 64 bit values while keeping 32 bits in mind? Probably did this absently.
Tested your code with simulator -works OK. Thanks.

DT
December 13, 2010 at 12:59 am

The Read-Modify-Write has crept back in on TIFR1

ADMIN
December 13, 2010 at 1:09 am

Thanks. Copied wrong part

Pingback: AVR Timers for dummies | Scrapbook

ANAND
May 31, 2012 at 12:15 pm

am using this program to detect frequency and through GSM we are sending that frequency as message to
mobile..but this program is not working for us.pls help us.

LEAVE A REPLY
Connect with:

Poweredby OneAllSocialLogin

http://www.embedds.com/programming-16-bit-timer-on-atmega328/[06-06-2014 20:26:49]

Programming 16 bit timer on Atmega328 - Embedded projects from around the web

Your email address will not be published. Required fields are marked *

Name *

Email *

Website

Comment

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym
title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite="">
<strike> <strong>

Post Comment

Notify me of follow-up comments by email.


Notify me of new posts by email.

You may also like Introduction to MSP430 Interrupts


In this tutorial, we will see an effective way on coding interrupts. Our task for today will be to learn
interrupts for GPIO and ...

AVR timers do more than count time


Probably Timer/Counters are complex peripherals in microcontrollers, but as fact they are most
commonly used no matter what complexity program is. Designers of timers ...

Using watchdog timer in your projects


All AVR microcontrollers have internal watchdog timer that can be successfully used in your
projects. Atmega328 and other modern AVR microcontrollers have so called ...

http://www.embedds.com/programming-16-bit-timer-on-atmega328/[06-06-2014 20:26:49]

Programming 16 bit timer on Atmega328 - Embedded projects from around the web

Controlling servo motor with AVR


Servo motors are a type of electromechanical actuators that do not rotate continuously like DC/AC
motors. They used to position and hold some object. ...

LATEST AVR TUTORIAL

June 2014

CONNECT WITH:
M

Using Volatile keyword in embedded code


Poweredby OneAllSocialLogin

Interfacing GPS Module with AVR

LOGIN

LATEST ARM CORTEX TUTORIAL

Username

10

11

12

13

14

15

17

18

19

20

21

22

23

24

25

26

27

28

29

30
May

Use fixed integer types to enhance portability

USERS ONLINE

Password

17 Users Online

Remember Me
Login

Register
Lost Password
Connect with:

Poweredby OneAllSocialLogin

Copyright Embedded projects from around the web|Privacy Policy


POWERED BY PAABOLA & WORDPRESS.

http://www.embedds.com/programming-16-bit-timer-on-atmega328/[06-06-2014 20:26:49]

16

Multichannel ADC using DMA on STM32

Introducing to STM32 ADC programming. Part2

Use fixed integer types to enhance portability

Vous aimerez peut-être aussi