Program 16 bit AVR timer with WinAVR
AVR 16 bit timer is more advanced timer than 8 bit timer. It has more features and this allows more accurate program execution timing. 16 bit timer is used when precise signal generation or signal timing measurement needed. As 8 bit timer counter can calculate up to 225 counts the 16 bit timer counter maximum value may reach 65535. In AVR microcontrollers 16 bit timer is Timer1. It contains a 16 bit input capture register (ICR1) and two 16 bit output compare registers (OCR1A and OCR1B). Of course the timer counters register (TCNT1) which is 16 bit long. When programming is ASM language there is special procedure for accessing it (refer to datasheet). While in C language it is done automatically we won’t get to deep into this. Timer1 is controlled by two timer counter control registers (TCCR1A/B). Signals are visible at timer interrupt flag register (TIFR) and interrupts can be individually masked in timer interrupt mask register (TIMSK).
Few words about prescaler. As timer counter is a binary counter it has a prescaler like other timers in AVR microcontrollers. Prescaler may be selected in TCCR1B with bits CS12, CS11 and CS10 like in 8 bit timers.
Lets us go through timer1 modes to clear things out.
Input capture mode
This mode is usually used for measuring of time interval between two events like measuring pulse width. This is done by capturing time event at the beginning and at the beginning of the event. They are subtracted from each other to find how long event lasted. For this task input capture register is used ICR1. Signal is captured using input capture pin (ICP). In a TCCR1B bit ICES1. Input capture mode has also noise canceling feature. Bu setting bit ICNC1 in TCCR1B register will activate this. Noise canceller helps to avoid spikes in the input. It simply waits for low or high signal for four clock cycles.
When input capture has occurred the interrupt is generated, so the interrupt service routine has to determine that was first or second capture and store ICR1 data to some memory because after second capture data will be overwritten.
Let’s say wee want to measure positive signal width using Atmega8 microcontroller.
#include <avr\io.h>
#include <avr\interrupt.h>
#include <avr\iom8.h>
#define ICP PINB0
//define ovrflow counter
uint16_t ov_counter;
//define times for start and end of signal
uint16_t rising, falling;
//define overall counts
uint32_t counts;
//overflow counter interrupts service routine
ISR(TIMER1_OVF_vect){
ov_counter++;
}
//Timer1 capture interrupt service subroutine
ISR(TIMER1_CAPT_vect){
/*This subroutine checks was it start of pulse (rising edge)
or was it end (fallingedge)and performs required operations*/
if (ICP) //if high level
{
//save start time
rising=ICR1;
//set to trigger on falling edge
TCCR1B=TCCR1B&0xBF;
//reset overflow counter
ov_counter=0;
}
else
{
//save falling time
falling=ICR1;
//rising edge triggers next
TCCR1B=TCCR1B|0x40;
counts=(uint32_t)falling-(uint32_t)rising+(uint32_t)ov_counter;
/*you can convert coutns to seconds and send to LCD*/
}
}
int main(void) {
//enable overflow and input capture interrupts
TIMSK=0x24;
/*Noise canceller, without prescaler, rising edge*/
TCCR1B=0xC1;
sei();
for (;;) {
/* loop forever timer does the job*/
}
}
Timer output compare mode
Output compare mode is like anti-input capture mode. This mode is able to generate varying frequency and symmetry signals. This may be used to generate music sounds and so on.
Output compare mode is controlled by TCCR1A register. It can control two pins of microcontroller OC1A and OC1B. They may be left unaffected, toggled, set or cleared. Regarding to bits settings in TCCR1A register. Output compare mode on compare match can generate an interrupt. Interrupt service routine may be used to calculate the OCR1A value.
There are several of output compare modes (selecting bits WGM13:0 in TCCR1A register):
- Normal mode (WGM13:0=0). Is used to generate interrupts as some given time also as overflow interrupts.
- Clear timer on compare match (WGM13:0=4 or 12). The OCR1A or ICR1 Register are used to control counter resolution. In CTC mode the counter is cleared to zero when the counter value (TCNT1) matches either the OCR1A (WGM13:0 = 4) or the ICR1 (WGM13:0 = 12). The OCR1A or ICR1 define the top value for the counter, hence also its resolution. This mode allows greater control of the Compare Match output frequency.
#include <avr\io.h>
#include <avr\interrupt.h>
#include <avr\iom8.h>
#define OCP PINB1
/*This example demonstrates how 1kHz signal
can be generated using timer and compare match interrupt
if F_CPU=1MHz, then 1 clk is 1us. For 1kHz signal
there is 1000clk=1000us. For toggling ponts = 500clk
*/
//Output compare ISR
ISR(TIMER1_COMPA_vect){
OCR1A+=500;
}
int main(void) {
//Set PORTB1 pin as output
DDRB=(1<<OCP);
//Output compare toggles OC1A pin
TCCR1A=0x40;
//start timer without prescaler
TCCR1B=0x01;
//enable output compare interrupt for OCR1A
TIMSK=0x10;
sei();
for (;;) {
/* loop forever timer does the job*/
}
}
results:
- Fast PWM mode (WGM13:0=5, 6, 7, 14 or 15). This mode is used to provide high frequency PWM. It’s a single slope generation. The PWM resolution for fast PWM can be fixed to 8-, 9-, or 10-bit, or defined by either ICR1 or OCR1A. The minimum resolution allowed is 2-bit (ICR1 or OCR1A set to 0x0003), and the maximum resolution is 16-bit (ICR1 or OCR1A set to MAX):
Phase correct PWM mode (WGM13:0=1, 2, 3, 10 or 11). It is dual slope mode. Resolution can be selected between 8, 9, 10 bits or defined by ICR1 or OCR1A. The output is phase correct, but unsymmetrical:
- Phase and frequency correct PWM mode (WGM13:0=8 or 9). This PWM mode generates phase correct PWM and correct frequency because of symmetrical output:
Example bellow shows how correct phase and frequency PWM duty cycle can be controlled using two buttons connected to PIND0 and PIND1:
#include <avr\io.h>
#include <avr\iom8.h>
int main(void) {
//Port D pins as input
DDRD=0x00;
//Enable internal pull ups
PORTD=0xFF;
//Set PORTB1 pin as output
DDRB=0xFF;
//
OCR1A=76;
//Output compare OC1A 8 bit non inverted PWM
TCCR1A=0x91;
//start timer without prescaler
TCCR1B=0x01;
for (;;) {
if(bit_is_clear(PIND, 0)) {
//increase duty cycle
OCR1A+=10;
loop_until_bit_is_set(PIND, 0);
}
if(bit_is_clear(PIND, 1)) {
//decease duty cycle
OCR1A-=10;
loop_until_bit_is_set(PIND, 1);
}
}
}
Download example projects ready to run with VMLAB simulation tools from here
Comments
Input Capture Mode
Hi, for input capture:
- The code shows the interrupt being called for both rising and falling, even though it is configured for Rising Edge..
- Why use uint32_t for a 16bit timer value?
Actually edge is changed
Actually edge is changed insie interrupt. Once ISR is called on rising edge, next will be on falling edge"
//set to trigger on falling edge
TCCR1B=TCCR1B&0xBF;
uint32_t is used in case pulse is longer than timer overflows. so to be sure there are 32 bit value used. Anyway dont mind
counts=(uint32_t)falling-(uint32_t)rising+(uint32_t)ov_counter;
the counts value is calculated wrongly. should be like:
counts=((uint32_t)falling +(uint32_t)(ov_counter*0xFFFF))-(uint32_t)rising;
ICR
how can you convert this counts to seconds
Just use the clocking rate of
Just use the clocking rate of your timer and divide counts by counts per seconds.
In the event that you aer using a timer with a nice frequency like 1 Mhz you get your count in us. else you will just have to divide. unit analysis => counts divided by (counts / s ) = s
ICP
There is a bug in example of ICP mode. Configuring uP for ICP mode disables the port PIN function (PINB0 always FALSE).
Add new comment