Path:
Home =>
AVR-EN =>
Assembler introduction => Timers
(Diese Seite in Deutsch:
)
Beginner's introduction to AVR assembler language
Timers in assembler language
One the most used internal hardware are timers. Here you'll learn how to
- start (and stop) a timer in normal mode,
- Switching pins on/off with the timer and comparers,
- run timers on different frequencies,
- operate timers in CTC mode,
- use timers in PWM modes,
- handle and apply 16-bit timers,
- control timers with interrupts,
- count with counters.
All calculation sheets in LibreOffice Calc here,
all drawings in LibreOffice Calc here.
The following source code starts the 8-bit timer/counter 0:
ldi R16,1<<CS00 ; Set bit CS00 to 1 (a 1 shifted left zero times)
out TCCR0B,R16 ; Write to port register TCCR0B
That is what happens here: the Timer/Counter 0's prescaler multiplexer
is set to 0b001, which means that the clock signal (by default: 1 MHz)
is selected as output of the multiplexer and now clocks the 8-bit-TC0.
This counts in its TCNT0 port register, and goes up by 1 in every clock
cycle.
These two lines are already it. From now on the timer runs. If you want
to stop it: clear the CS00 bit (and all other CS bits) in the timer control
register B, the multiplexer then is fixed on the no-input-level and the
timer halts immediately.
The left picture shows the Timer TC0 in the simulator
avr_sim.
What if the timer reaches its 8-bit-end at 255 or 0b11111111 or 0xFF
(on the right)? He overflows and simply starts from scratch, which is
zero. And counts up again. And so on, and so on ...
To read its current state: simply read the TCNT0 port register to a
register:
in R16,TCCNT0 ; Read 8-bit counter
But: halt. It does not make any sense to read a timer/counter, near-to
never. So better forget this idea of reading TCNT for a while until
you learn more about timers, their properties and their use.
How fast does the counter count and how fast does he reach its overflow
condition? That depends from the controller's clock frequency:
Table 1.1, available as spreadsheet
"normal" in the LibreOffice Calc file
timers.ods
As the table for a clock of 1 MHz shows, the timer overflows with a
frequency of 3,906.25 Hz or after 0.256 milli-seconds.
But: such a timer has not only one CS-bit for switching it on and off,
but three CS-bits. Those select between divided clock rates of 1, 8,
64, 256 or 1,024. To select the 1,024 just formulate like this:
ldi R16,(1<<CS02)|(1<<CS00) ; Set bits CS02 and CS00 to 1, | is a binary OR
out TCCR0B,R16 ; Write to timer/counter control port register TCCR0B
Now the timer overflows with 3.81 Hz or every 262.1 ms.
That would be a low-enough frequency that could already be seen on a
LED. Which will be the main task of the next chapter.
Each timer has two pins associated that can be switched on, off or
can be toggled. Those pins are named OCnA and OCnB for the timer
n. In an ATtiny24 in PDIP those are located at PB2 (OC0A) and PA7
(OC0B) for TC0.
The bits that determine the switch mode are located in port register
TCCRnA. For TC0 those are the bits COM0A1 and COM0A0 for OC0A and
COM0B1 and COM0B0 for OCR0B. Are both bits zero, the respective pin
is disconnected from the timer. If only COMnx0 is set, the port
bit is toggled, if only COMnx1 is set the port bit is cleared, and
if both bits are set the port bit is set one.
But when should all this happen? This is the task for the two compare
match port registers A and B. Whenever the timer reaches those values,
the next timer pulse following the match will perform the switching.
If we set the Compare A value to zero and the Compare B value to 127
we'll get a nice flip-flop when toggling:
sbi DDRB,DDB2 ; Set PB2 as output pin
sbi DDRA,DDA7 ; Set PA7 as output pin
ldi R16,0 ; Compare A to zero
out OCR0A,R16 ; To compare A port register
ldi R16,127 ; Compare B to 127
out OCR0B,R16 ; To compare B port register
ldi R16,(1<<COM0A0)|(1<<COM0B0) ; Toggle A and B
out TCCR0A,R16 ; To timer/counter control register A
ldi R16,(1<<CS02)|(1<<CS00) ; Prescaler = 1,024
out TCCR0B,R16 ; To timer/counter control register B
Loop: ; The endless loop
rjmp Loop
The first two instructions make the two OC pins output, so you can
measure and see what the timer does with those.
The next four instructions set the compare registers to two diffent
values, so that the two pins are switched on and off (to toggle)
at different timer stages.
The following two instructions set both OC pins to the toggle mode on
compare match by setting the COM0X0 bit to one. Please note that those
four bits behave different in PWM modes (see below).
The last two instructions start the timer with a prescaler of 1,024.
What is now going on is:
- when the timer reaches 1, it toggles OC0A (blue) and makes the
output high,
- when the timer reaches 128, it toggles OC0B (red) and makes the
output high,
- when the timer reaches 1 again (after changing from 255 to 0),
it again toggles OC0A,
- when the timer reaches 128 again, it again toggles OC0B,
- and again and again and again ...
Now, assume we attach a red/green duo-LED on the two pins, like here:
In phase 1, with OC0A high and OC0B low, the red LED is on. In phase
2, with both outputs high, the LED is off. In phase 3, with OC0A low
and OC0B high, the green LED is on. Followed by a pause in phase 4,
where both outputs are low.
Be aware that the timer now acts in this way automatically, no further
actions and instructions are necessary. The timer does not need any
further care, it runs solely alone and we can send the controller to
sleep.
A nice red/green blinker with pauses in between.
Some questions:
What would be the blinking if we set OCR0B to 31? or to 1? Please use
the spreadsheet "redgreen" in the LibreOffice Calc file
timers.ods to find that out, just change
the Compare B value accordingly. And: if you do not like the pauses
in between: what would be the solution for a pause-less red/green
blinker?
If you look back on Table 1.1, you'll see
that the frequencies under different prescaler settings are not
simple integers, because dividing by powers of two is not leaving a
rest of zero, if our frequency is at the default of 1 MHz. Even
if we increase the frequency to 8 MHz, which can be done with
clearing the CLKDIV8 fuse of the ATtiny24, does not help a lot:
only the prescaler of 1 leads to an integer, all others have
something behind the decimal dot.
You can change that by driving the clock of the ATtiny24 by a frequency
that is a power of two, e.g. by 2.048 MHz. If you input this in
the spreadsheet "normal" in the LibreOffice Calc file
timers.ods, you'll see that at least up to
64 the resulting frequency is an integer (125 Hz).
This is how an ATtiny24 can be driven with a crystal that has a
frequency that is a power of two: the two pins PB0 and PB1 change their
properties if it's fuses for clocking are changed. Do not forget to
clear the CLKDIV8 fuse, also.
This excerpt from the spreadsheet "crystals" of the LibreOffice
Calc file timers.ods shows all commercially
available crystals, their frequency, their casings, and the frequency
that they clock a timer with, if the prescaler is set to 8, 64, 256
or 1,024.
Lots of crystals deliver nice frequencies with all zeros behind the
decimal dot (green backgound). But which deliver frequencies after a
further division by 256 in the timer?
Here are the frequencies after a further 8-bit division in the
timer. Only the crystals with 2.097152, 3.93216, 4.194304 and 6.5536
MHz deliver integer frequencies over the whole prescaler range.
Timers can not only count to 255 and re-start again, they can also re-start
on smaller counter states. To do that they use the compare match value in
OCRnA: if the counter reaches the compare value, the next following timer
signal re-starts the timer at zero. This is called the CTC mode (Clear Timer
on Compare).
To bring the 8-bit-timer/counter into this mode we need to write the so-called
Wave-Generation-Mode bits (WGM) to 0b010 and the compare A in OCR0A to the
desired divider value minus one. For historic reasons, the WGM bits (of which
up to four bits can be available, depending from the AVR device and the timer
length) are in parts located in TCCR0A (WGM01 and WGM00) and in TCCR0B (WGM02).
As we only need to set WGM01, we have to write this to TCCR0A, where already
the COM bits are.
We use the calculator in the spreadsheet "ctc" in the LibreOffice
Calc file timers.ods to calculate our frequencies.
For 1 MHz and a prescaler value of 1,024 we input a divider value of
122. This value, minus one, goes to our compare match A port register. The
resulting frequency will be 8.004 Hz. As the toggle needs two events
for a full swing the frequency on the output pin is half that, 4.002 Hz.
The full code:
sbi DDRB,DDB2 ; Set PB2 as output pin
sbi DDRA,DDA7 ; Set PA7 as output pin
sbi PORTA,PORTA7 ; Set PA7 high on start-up
ldi R16,121 ; Compare A to zero
out OCR0A,R16 ; To compare A port register
out OCR0B,R16 ; To compare B port register
ldi R16,(1<<COM0A0)|(1<<COM0B0)|(1<<WGM01) ; Toggle A and B in CTC mode
out TCCR0A,R16 ; To timer/counter control register A
ldi R16,(1<<CS02)|(1<<CS00) ; Prescaler = 1,024
out TCCR0B,R16 ; To timer/counter control register B
Loop: ; Endless looping
rjmp Loop
The code simulates as desired: the two pins toggle as calculated
with 4.002 Hz and a pulse width of 50%.
To play around with divider values, compare A and crystal
frequencies you can also use the sheet "crystals" in
the LibreOffice Calc file timers.ods,
from column N to R. You can use that sheet to
select your optimal crystal frequency in CTC mode.
The difference between normal and CTC mode can best be seen for
the 2.048 and 4.096 MHz crystal: those deliver integer
frequencies with all prescaler dividers if the divider is set
to 250, with Compare A at 249, instead of the 256 in normal
mode, this slight difference of 6 really makes a difference.
And again: nothing needs to be done by the controller following
the init of the timer, all is automatic.
PWM means "Pulse width mode": a signal goes high on
timer start, called "bottom", and is switched off when
a compare match is exceeded. The opposite is also possible: on
bottom the output gets low, on compare match high, which is
called an "Inverted PWM" signal.
For what can those PWM signals be useful? If we connect a LED
with a resistor to that output signal, the LED goes on and off.
If the signal duration is short enough, we don't see the switching.
Our slow eye recognizes a dimmed LED: the shorter the ON time, the
lower the intensity.
Or if we add a driver transistor to the output and we switch a
motor on and off: the shorter the driver transistor is active, the
less power drives the motor.
5.1 Times in PWM modes
These are the time relations of a PWM signal.
The PWM cycle time is tPWM = 256 * NPresc /
clock for an eight bit PWM. With a prescaler value of 1 and a clock
of 1 MHz this is 0.256 ms (or a PWM frequency of
3.9 kHz, while our eye can only recognize about 30 Hz,
but our ears can hear the motor humming at this frequency).
If the compare match value would be 127, the switching would be
exactly at 50% of the whole time (at (127 + 1) / 256 = 50%). The
shortest time would be a compare match value of zero, which would
yield (0 + 1) / 256 = 0.39%.
Please note that the TCNT values are not linear, as shown in the
diagram, but they have 256 stages. I was too lazy to paint those
discrete 256 single stages.
5.2 The COM bits
Besides the already described PWM mode there are different modes
possible. Those are described here.
5.2.1 The COM bits in Fast PWM mode
Fast PWM mode is the mode which was described above: the timer
counts up and switches on compare match plus one. Two sub-modes are
possible and can be used:
- TOP = 255: the timer has 256 stages, both compare matches
can be used to switch OCnA and OCnB, or
- TOP = OCRnA: the timer has (OCRnA + 1) stages, only compare
match B can be used to switch OCnB.
In the second case, the PWM frequency is increased: the lower OCRnA,
the higher the frequency. So setting OCRnA to 31 yields a 5-bit-PWM
with a TOP value of 31 instead of 256 and an 8 times higher frequency.
No audible motor humming any more, but only OCRnB values between
zero and 31 make any sense.
The COM bits in Fast PWM mode have the following meaning:
COMnA/B1 | COMnA/B0 | Bottom | Compare Match A/B | Remark |
0 | 0 | OCnA/B disconnected | - |
1 | 0 | Set OCnA/B | Clear OCnA/B | Non-inverting PWM mode |
1 | 1 | Clear OCnA/B | Set OCnA/B | Inverting PWM mode |
5.2.2 The COM bits in phase-correct PWM modes
In the phase-correct PWM mode
- the timer first counts up to the TOP value, then counts
down to the bottom,
- when reaching the compare A or B (plus one) when up-counting
the OC pin is set, when down-counting the OC pin is cleared,
(normal mode, left),
- in inverted mode the OC pin is cleared when up-counting and
set when down-counting (inverted mode, right),
- changes of the compare match values come only into effect
when the bottom count is reached.
That mode makes sense, if the compare match values often change.
It ensures that all pulses have the correct duration (which would
be the case if compare values change, e. g. when the new
value is below the current count: the compare match would not be
reached in this cycle).
The up- and down-counting reduces the PWM frequency by the factor
of two, it therfore should have been called "slow PWM mode".
The COM bits in Phase-correct PWM mode have the following meanings:
COMnA/B1 | COMnA/B0 | Up-counting | Down-counting | Remark |
0 | 0 | OCnA/B disconnected | - |
1 | 0 | Clear OCnA/B | Set OCnA/B | Inverting phase-correct PWM mode |
1 | 1 | Set OCnA/B | Clear OCnA/B | Non-inverting PWM mode |
5.2.3 The COM bits in phase- and frequency-correct PWM modes
The frequency-correct PWM mode additionally allows to change the
TOP value of the counter (by changing OCRnA or ICRn or by changing
the mode bits in TCCRnA between 8-, 9- or 10-bit PWM), but this
change only comes into effect when the timer reaches the bottom.
This mode ensures that only complete PWM cycles are performed
and that changes of the TOP value cannot lead to false pulse
durations (e. g. when the new TOP value is below the
current compare match value).
This is also a "slow motion" mode.
16-bit timers/counters are pretty much the same like their
8-bit companions. The prescaler is the same as for 8-bit.
But they have 16 bits counting capability, they can count
from zero up to 216 - 1 = 65,535.
As AVRs are 8-bit-controllers, the 16 bits are subdivided
in two portions for accessing. Accesses on their port registers
(TCNT and all compare registers), are subdivided into
a low (bit 0 to 7) and a high part (bit 8 to 15). Those are
added as L and H to their names. So TCNT1 has a TCNT1L and
a TCNT1H port register.
Two special features are different for 16-bit-TCs:
- the 8-bit access requires a special handling to avoid
glitching, and
- as 16-bit-PWMs do not make much sense, they have an
extra port register pair that can be used as CTC compare
register.
See the next chapters on those issues.
6.1 Accessing 16-bit port register pairs
8-bit controllers cannot access 16-bit port registers as a
whole, they need two instructions for access: the MSB and the
LSB have to be accessed separately. As the timer performs
counting in between those two instructions, the
following situations can occur:
- In between the first and the second read of the TCNT a
count appears that changes the MSB. The LSB and the MSB
read belong to different counter values.
- While writing the compare match value's MSB and LSB, a
compare match with the first written byte already appears,
it isn't sure that the other byte to be written has the
same value as before. Anyway, a false compare match can
occur.
For both cases, read and write, a special handling has been
implemented. When reading the LSB of the register pair, also
the current MSB is copied to an interim storage place. When
reading the MSB next, not the real MSB is read but the content
of the interim storage. This ensures that LSB and MSB read
belong to the same timer/counter stage.
When writing the MSB of a register pair, the write operation
does not access the MSB but is diverted to an interim storage
place. When writing the LSB next, both the LSB and the stored
MSB are written to the register pair at once. That ensures
that the LSB and the MSB are both written simultanously.
Not respecting this procedure (read: LSB first, write MSB
first) can lead to funny effects. Seconds later you'll read
the old MSB instead of the current, or the write operation
to the MSB and LSB is not reaching the TCNT1 at all if
you'll only write the MSB and forget the LSB write.
Note that in PWM timer modes the written compare match values
are not applied at once. They are written as a whole to an
interim storage, from which the compare match value is only
copied when the timer reaches bottom (TCNTn=0). So, if you
simulate a PWM, you better not be surprised if you don't see
an already written compare match value immediately. It will
come later on, in the next PWM cycle.
6.2 The input capture register in 16-bit TCs
Timers that have four mode control bits instead of only one,
two or three (usually 16-bit timers have that) have additional
methods to control the PWM's resolution:
- the WGM mode bits can select between 8-, 9- or 10-bit
resolution, which sets the TOP value to 255, 511 or 1023,
- they have an Input Capture Register (ICR), which is a
third compare register and can be set as CTC value source
and allows PWM resolutions to be set to anything between
1 and 65,536.
Note that in all those cases, both OCnA and OCnB pins can be
used to generate PWM pulses.
With that you are enough flexible to control your PWM's
resolution and to tailor this to your specific needs.
6.3 An example application
This here is an example with two active TCs as a pair: TC0
as 8-bit and TC1 as 16-bit TC. Both blink independently with
their own blink frequency.
This here is the source code:
; ********************************************
; * Two timers in normal mode in an ATtiny24 *
; * Version 1 as of August 2022 *
; * (C)2022 by Gerhard Schmidt *
; ********************************************
;
.nolist
.include "tn24adef.inc" ; Define device ATtiny24A
.list
;
; Frequencies, exportet from LibreOffice sheet
.equ clock = 1000000 ; Clock frequency in Hz
; TC0
.equ cF8 = 1000 ; Frequency in Hz
.equ cPresc8 = 8 ; Prescaler value
.equ cCs8 = 1<<CS01 ; Prescaler bits
.equ cDiv8 = 63 ; Divider value
.equ cCmp8 = 62 ; Compare match value CTC
; TC1
.equ cF16 = 200000 ; Frequency in mHz
.equ cPresc16 = 1 ; Prescaler value
.equ cCs16 = 1<<CS10 ; Prescaler bits
.equ cDiv16 = 2500 ; Divider value
.equ cCmp16 = 2499 ; Compare match value CTC
;
.cseg
.org 000000
; Init TC0
sbi DDRB,DDB2 ; Set PB2 as output pin
sbi DDRA,DDA7 ; Set PA7 as output pin
sbi PORTA,PORTA7 ; Set PA7 high
ldi R16,cCmp8 ; Compare A
out OCR0A,R16 ; To compare A port register
out OCR0B,R16 ; To compare B port register
ldi R16,(1<<COM0A0)|(1<<COM0B0)|(1<<WGM01) ; Toggle A and B, CTC mode
out TCCR0A,R16 ; To timer/counter control register A
ldi R16,cCs8 ; Prescaler CS bits
out TCCR0B,R16 ; To timer/counter control register B
; Init TC1
sbi DDRA,DDA6 ; Set PA6 as output pin
sbi DDRA,DDA5 ; Set PA5 as output pin
sbi PORTA,PORTA6 ; Set PA6 to high
ldi R16,High(cCmp16) ; Compare, MSB first
out OCR1AH,R16 ; To compare A
ldi R16,Low(cCmp16) ; dto., LSB
out OCR1AL,R16 ; To compare A
ldi R16,High(cCmp16) ; Compare, MSB first
out OCR1BH,R16 ; To compare B
ldi R16,Low(cCmp16) ; dto., LSB
out OCR1BL,R16 ; To compare B
ldi R16,(1<<COM1A0)|(1<<COM1B0)
out TCCR1A,R16
ldi R16,(1<<WGM12)|cCs16
out TCCR1B,R16
Loop:
rjmp loop
;
; End of source code
Please note that the line
ldi R16,(1<<WGM12)|cCs16
combines WGM12 with the CS bit combination, which has been imported
from the spreadsheet "ctc" in the LibreOffice Calc file
timers.ods.
If you want to avoid the spreadsheet import: Calculating the prescaler
and the compare match value from the frequency in assembly language is
not a simple task, because you have to test all five prescaler values
in a stacked .if-.then-.else-.endif clause like here, for the 16-bit
timer TC1:
.equ clock = 1000000 ; The clock frequency in Hz
.equ cFreq = 1000 ; The desired frequency in Hz
.equ cTcTop1 = 65536 ; 256 for 8-bit, 65536 for 16 bit
.equ cFreq2 = 2 * cFreq
.if clock / cFreq2 <= cTcTop1
.equ cPresc = 1
.equ cCs = 1<<CS10
.else
.if clock / cFreq2 / 8 <= cTcTop1
.equ cPresc = 8
.equ cCs = 1<<CS11
.else
.if clock / cFreq2 / 64 <= cTcTop1
.equ cPresc = 64
.equ cCs = (1<<CS11)|(1<<CS10)
.else
.if clock / cFreq2 / 256 <= cTcTop1
.equ cPresc = 256
.equ cCs = 1<<CS12
.else
.if clock / cFreq2 / 1024 <= cTcTop1
.equ cPresc = 1024
.equ cCs = (1<<CS12)|(1<<CS10)
.else
.error "Prescaler value exceeds 1,024!"
.endif
.endif
.endif
.endif
.endif
; Calculate compare value with rounding
.equ cDiv = ((clock + cFreq2 / 2) / cFreq2 + cPresc / 2) / cPresc
.equ cCmp = cDiv - 1
Quite heavy code, but works fine.
When you set their interrupt enable bits, any overflow, compare
match or, in 16-bit-timers, ICR match in the timer interrupt
mask register (TIMSK(n)) can generate an interrupt. This allows
to run the timer without having to poll its count stage.
Like any other interrupt,
- the timer sets its respective TIF bit, and
- if the general interrupt bit I is set and no interrupt
request with a higher priority is pending,
- the current PC address is thrown onto the stack, and
- the general interrupt flag I is cleared, and
- the controller jumps to the interrupt's vector address,
and
- clears the TIF bit.
- on RETI the previous address on the stack is written to
to the PC and execution is continued there.
The interrupt vectors of an ATtiny24 looks like that:
rjmp Main ; Reset vector
reti ; EXT_INT0
reti ; PCI0
reti ; PCI1
reti ; WATCHDOG
reti ; ICP1: Timer 1 compare match ICR
reti ; OC1A: Timer 1 compare match A
reti ; OC1B: Timer 1 compare match B
reti ; OVF1: Timer 1 overflow
reti ; OC0A: Timer 0 compare match A
reti ; OC0B: Timer 0 compare match B
reti ; OVF0: Timer 0 overflow
reti ; ACI
reti ; ADCC
reti ; ERDY
reti ; USI_STR
reti ; USI_OVF
More than half of all interrupts in an ATtiny24 have timers
as sources.
16-bit Timer 1 has a higher priority than 8-bit timer 0.
The ATtiny24 has two timer interrupt mask registers: TIMSK0
for timer 0 and TIMSK1 for timer 1. The interrupt enable
bits for timer 0 are OCIE0A, OCIE0B and TOIE0, for timer 1
the same with 1 instead of 0 and additionally ICIE1 for the
Capture interrupt.
Within timer-interrupt-service-routines anything can be
done: setting flags, counting software counters down,
switching port-pins on and off, etc. The
interrupt-service-routine can do anything beyond the simple
OC pin switching. It can even switch the timer off, by
clearing the CS bits of the timer (software-single-shot).
The following source code demonstrates a very, very
long-term timer: on pushing a switch, a pulse output goes
high and stays so for between 0.26 seconds and
4.77 hours. It uses the 8-bit timer 0 and a
16-bit counter in R25:R24 that, in an overflow interrupt,
is counted down from a constant cDelay. The constant
can be calculated in the spreadsheet "longtime"
of the LibreOffice Calc file
timers.ods. If you need a
15 minute long signal for your stairway light: just
set cDelay to 3,433. The source code can be
downloaded from here:
;
; ***********************************
; * Single one shot pulse on INT0 *
; * ATtiny24A, version 1.0 *
; * (C)2022 by Gerhard Schmidt *
; ***********************************
;
.nolist
.include "tn24adef.inc" ; Define device ATtiny24A
.list
;
; **********************************
; D E L A Y C O N S T A N T
; **********************************
;
.equ cDelay = 1 ; Can be between 0 and 65535
;
; **********************************
; R E G I S T E R S
; **********************************
;
; free: R0 to R15
; used: R16 as multi purpose register
; free: R17 to R23
.def rCntL = R24
.def rCntH = R25
; free: R26 to R31
;
.cseg
.org 000000
;
; **********************************
; R E S E T & I N T - V E C T O R S
; **********************************
rjmp Main ; Reset vector
rjmp ExtInt0Isr ; EXT_INT0
reti ; PCI0
reti ; PCI1
reti ; WATCHDOG
reti ; ICP1
reti ; OC1A
reti ; OC1B
reti ; OVF1
reti ; OC0A
reti ; OC0B
rjmp Ovf0Isr ; OVF0
reti ; ACI
reti ; ADCC
reti ; ERDY
reti ; USI_STR
reti ; USI_OVF
;
; **********************************
; I N T - S E R V I C E R O U T .
; **********************************
;
ExtInt0Isr:
sbi PORTB,PORTB0 ; Set the output pin
ldi rCntH,High(cDelay)
ldi rCntL,Low(cDelay)
ldi R16,(1<<CS02)|(1<<CS00) ; Prescaler = 1024
out TCCR0B,R16
ldi R16,1<<TOIE0
out TIMSK0,R16
reti
;
Ovf0Isr:
sbiw rCntL,1
brne Ovf0IsrReti
cbi PORTB,PORTB0
clr R16
out TCCR0B,R16
Ovf0IsrReti:
reti
;
; **********************************
; M A I N P R O G R A M I N I T
; **********************************
;
Main:
.ifdef SPH
ldi R16,High(RAMEND)
out SPH,R16
.endif
ldi R16,Low(RAMEND)
out SPL,R16 ; Init LSB stack pointer
; Init PB0 as signal output pin
sbi DDRB,DDB0 ; Pin PB0 as output pin
; Init the INT0 pin
sbi PORTB,PB2 ; Pull-upp resistor on
ldi R16,1<<ISC01 ; Interrupt on falling edge
out MCUCR,R16
ldi R16,1<<INT0
out GIMSK,R16
sei ; Enable interrupts
;
; **********************************
; P R O G R A M L O O P
; **********************************
;
Loop:
rjmp loop
;
; End of source code
;
; (Add Copyright information here, e.g.
; .db "(C)2022 by Gerhard Schmidt " ; Source code readable
; .db "C(2)20 2ybG reahdrS hcimtd " ; Machine code format
;
If you need even longer times: the 16-bit TC1 extends that
to longer than a month. Just exchange a few lines in the code
and use interrupt vector OVF1. The shortest time then
are 67 seconds for cDelay = 1, the longest are
50.9 days for cDelay = 0.
Some timers/counters can also work as counters, as the name
already says. Usually only TC0 and TC1 can do that, TC2
can't.
Timers/counters, which are used as counters, have an input
pin called "Tn". If you
- set both CSn2=1 and CSn1=1, and
- set both pull-up-resistors active, and
- if you toggle this input pin, then
- the timer advances when you press the button
if CSn0=0, or
- the timer advances when you release the button
if CSn0=1.
In all cases, the duration of the pulse has to last at least
for four clock cycles long. That is the reason why a controller
with 1 MHz clock cannot count more than 250 kHz,
while one on 20 MHz can count up to 5 MHz.
But: do not expect that this works correct! All switches in
the real world are toggling when closed. And the four clock
cycles are not long enough to avoid 10-milliseconds of
switch toggling. So better use an RC on those inputs or
program a toggling prevention phase for 10 or 20 milli-seconds.
The second would be a nice task for a one-shot in a second
timer.
Overflow detection, comparers, the COM-pins and, in 16-bit
counters, the input capture, work in that mode, too, so you can
detect any of those events and you catch them with an interrupt.
9 Conclusion
Timers/counters in AVRs are versatile pieces of hardware. You
can use those for any timing purpose, very short as well as
very long times can be handled. Use timers/counters in all
your AVR assembler programs whenever timing issues play a role.
Be it repeatedly or as a single event. I promise: you'll never
use counting loops again after being familiar with the basics of
timers/counters in AVRs.
To the page top
©2022 by http://www.avr-asm-tutorial.net