C.1 C.2 Counter/Timers Overview ATmega328P has two 8-bit and one 16-bit counter/timers. Unit C – Timers and Counters • Can count at some rate up to a value, generate an interrupt and start over counting from 0. • Useful for performing operations at specific time intervals. • Can be used for other tasks such as pulse-width modulation or counting external events. C.3 C.4 Counter/Timers Overview Use of Timers T T T • Use 1: Generate an interrupt But we already have delay() functions … why do we at a regular interval need timers? Interrupts generated at a fixed time interval – Great for fixed time periods • Delay functions tie up the processor while executing. • Use 2: Use HW timer to • Better to let a timer measure the delay, and generate an interrupt when complete. measure time duration • We can do other useful work while we are waiting for – Great for measuring unknown time to elapse! timer intervals while(1) uC senses event uC senses event { & starts timer & stops timer lcd_moveto(0,0); lcd_stringout("hello"); _delay_ms(500); } // is "hello" printed // every 500 ms?
C.5 C.6 General Overview of Timer HW Duration Timer • Timer can be configured to count at a certain rate (Δt) Timer HW Module Modulus A (OCRxA) • Start/stop the timer when 0000 0010 0000 0000 Start Over @ 0? the microcontroller senses T = n • Δ t the start/stop of an event Interrupt 0000 0001 1001 1100 = if equal n • The count value, n, can 16-bit Counter (TCNTx) System Clock Timer Count Interrupt Increments every determine the duration of (16MHz Arduino) = (TCNTx) ÷ ÷ ÷ ÷ if equal prescaled "clock" the event: T = n•Δt Prescalar 0000 1010 0110 1100 (1,8,256,1024) Modulus B (OCRxB) We'll just use the modulus A 3 register so you can ignore B for our 2 1 class Δ t C.7 C.8 Periodic Interrupt Timer Periodic Interrupt Timer Steps • Timer can be configured to count at a certain rate (Δt) and an To use the counter to generate interrupts at a fixed upper bound (aka modulus count / OCRxA register) interval: • Start the timer and whenever its value reaches the upper • Decide how long an interval is required between bound an interrupt will be generated interrupts (1 sec, 50 ms, etc.) for your application – Can be configured to immediately restart the count at 0 and repeat • Determine a counter frequency (time period), and a Interrupt Interrupt Interrupt counter modulus (max period)that will make the Timer Count counter take that long to count from 0 to the modulus (TCNTx) value. T = OCRxA • Δ t OCRxA • Configure registers. • Write an ISR. 3 2 • Start the timer. 1 Δ t
C.9 C.10 Counter/Timer Registers Counter/Timer Registers • Bad News: Lots of register bits to deal with • Good News: Can ignore most for simple timing C.11 C.12 Computing the Desired Cycle Delay Calculating the Prescalar • Primary step : calculate how many processor clock cycles are • The counter prescalar divides the processor clock down to a required for your desired delay lower frequency so the counter is counting slower. – Desired clock cycles (aka "modulus") = clock frequency × desired delay time • Can divide the processor clock by four different powers of – Arduino UNO clock is fixed at 16 MHz two: 8, 64, 256, or 1024. • Example: 0.25 second delay with a 16 MHz clock • Try prescalar options until the cycle count fits in 16-bits – Desired clock cycles = 16,000,000 c/s × 0.25s = 4,000,000 cycles – 4,000,000 / 8 = 500,000 ← too big – The Arduino timer starts at 0, so we will set the max count to 3,999,999 – 4,000,000 / 64 = 62,500 ← OK • 4,000,000-1 = 3,999,999 4,000,000 / 256 = 15,625 ← OK – • Problem: The desired value you calculate must fit in at most a 16- – 4,000,000 / 1024 = 3906.25 ← OK, but not an integer bit register (i.e. max 65,535) • In this example, either of the last three could work but since – If the number is bigger than 65,535 then a prescalar must be used to reduce we can only store integers in our timer count registers the last the clock frequency to the counter from 16MHz to something slower one would not yield exactly 0.25s (more like 0.249984s)
C.13 C.14 Counter/Timer Initialization 1 Counter/Timer Initialization 2 • Set the mode for “Clear Timer on Compare” (CTC) • Select the prescalar value with bits: – WGM13 = 0, WGM12 = 1 CS12, CS11, CS10 in TCCR1B reg. // Set to CTC mode – This tells the hardware to start over at 0 once the TCCR1B |= (1 << WGM12); 000 = stop ⟸ Timer starts when counter is reaches your desired value – // Enable Timer Interrupt // Set to CTC mode • Enable “Output Compare A Match Interrupt” prescaler set to non-zero TIMSK1 |= (1 << OCIE1A); TCCR1B |= (1 << WGM12); – OCIE1A = 1 – 001 = clock/1 // Load the MAX count // Enable Timer Interrupt Interrupt Interrup // Assuming prescalar=256 • Load the 16-bit counter modulus into OCR1A TIMSK1 |= (1 << OCIE1A); // counting to 15625 = – 010 = clock/8 Timer Count // 0.25s w/ 16 MHz clock (TCNTx) – This is the value the counter will count up to and // Load the MAX count OCR1A = 15625; – 011 = clock/64 T = OCRxA • Δ t // Assuming prescalar=256 then generate an interrupt. OCRxA // counting to 15625 = // Set prescalar = 256 – 100 = clock/256 // 0.25s w/ 16 MHz clock – The counter then clears to zero and starts counting // and start counter OCR1A = 15625; 3 TCCR1B |= (1 << CS12); – 101 = clock/1024 up again. 2 1 Δ t // Enable interrupts • Enable global interrupts – In C, the register can be accessed as… sei(); • A 16-bit value "OCR1A" • Or as two eight bit values "OCR1AH" and OCR1AL”. C.15 C.16 Counter/Timer Initialization 3 8-bit Counter/Timers • Make sure you have an appropriate #include <avr/io.h> #include <avr/interrupt.h> • The other two counters are similar but only 8-bits. ISR function defined volatile unsigned cnt = 0; – Using name ISR(TIMER1_COMPA_vect) void init_timer1(unsigned short m) • Same principle: find the count modulus that fits in an { TCCR1B |= (1 << WGM12); 8-bit value. TIMSK1 |= (1 << OCIE1A); OCR1A = m; TCCR1B |= (1 << CS12); } int main() { init_timer1(15625); sei() while(){ // do something w/ cnt } return 0; } ISR(TIMER1_COMPA_vect){ // increments every 0.25s cnt++; }
C.17 C.18 8-bit Timers (Timer 0 & Timer 2) ISR Names • Timer0 (Timer2) of the Arduino only have COM0 COM0 COM0 COM0 - - WGM WGM A1 A0 B1 B0 01 00 • In CTC mode, an "Output Compare A Match an 8-bit timer and max count value (thus we can only count up to 255) TCCR0A Reg. (TCCR2A) Interrupt" will vector to an ISR with these names: Timer/Counter0 Control Register • Set WGM01 (WGM21) bit to CTC • Enable interrupt via OCIE0A (OCIE2A) bit FOC FOC - - WGM CS02 CS01 CS00 in TIMSK0 (TIMSK2) register 0A 0B 02 – ISR(TIMER0_COMPA_vect) { } /* 8-bit Timer 0 */ • Load the OCR0A (OCR2A) Register TCCR0B Reg. (TCCR2B) Timer/Counter0 Control Register • Start timer when desired by setting appropriate prescalar – ISR(TIMER1_COMPA_vect) { } /* 16-bit Timer 1 */ Bit 7 OCIE0 OCIE0 TOIE0 B A CS0[2:0] Prescalar WGM?1, Meaning WGM?0 TIMSK0 Reg (TIMSK2) 000 Timer off 00 Normal (Counter) Timer0 Interrupt Mask Register – ISR(TIMER2_COMPA_vect) { } /* 8-bit Timer 2 */ 010 Clk / 8 01 Phase Correct PWM Bit 7 Bit 0 011 Clk / 64 10 CTC (Timer) 100 Clk / 256 OCR0A Reg (OCR2A) 11 Fast PWM (Top=255, Max/Modulo count value for TMR0 (2) Thresh=OCRx) 101 Clk / 1024 8-bit Timers can only count up to 255. Be sure to select a prescalar such that your OCR value will fit in 8-bits.
Recommend
More recommend