Path: Home => AVR-EN => Assembler introduction => Timers    (Diese Seite in Deutsch: Flag DE) Logo

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
  1. start (and stop) a timer in normal mode,
  2. Switching pins on/off with the timer and comparers,
  3. run timers on different frequencies,
  4. operate timers in CTC mode,
  5. use timers in PWM modes,
  6. handle and apply 16-bit timers,
  7. control timers with interrupts,
  8. count with counters.
All calculation sheets in LibreOffice Calc here, all drawings in LibreOffice Calc here.

1 Starting and stopping a timer in assembler

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

The prescaler in an 8-bit TC 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 timer TC0 has been started The timer TC0 has reached the top value 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
Overflow in timers at 1 MHz clock 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.

2 Switching pins on/off with the timer

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 

Setting the direction of the OC0 bits 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.

OC0A and OC0B by time What is now going on is:
  1. when the timer reaches 1, it toggles OC0A (blue) and makes the output high,

    OC0A switches high

  2. when the timer reaches 128, it toggles OC0B (red) and makes the output high,

    OC0B switches high

  3. when the timer reaches 1 again (after changing from 255 to 0), it again toggles OC0A,
  4. when the timer reaches 128 again, it again toggles OC0B,
  5. and again and again and again ...
Now, assume we attach a red/green duo-LED on the two pins, like here:

A duo-LED on the two pins Phases OC toggling 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?

3 Timers on different frequencies

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).

A crystal as clock 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.

Commercially available crystals 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?

8-bit- and 10-bit-TC frequencies 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.

4 Timers in CTC mode

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).

The CTC mode of the timer 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 

CTC code simulated The code simulates as desired: the two pins toggle as calculated with 4.002 Hz and a pulse width of 50%.

CTC mode calculator spreadsheet 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.

5 Timers in PWM modes

A PWM signal 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

PWM signal times 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:
  1. TOP = 255: the timer has 256 stages, both compare matches can be used to switch OCnA and OCnB, or
  2. 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/B1COMnA/B0BottomCompare Match A/BRemark
00OCnA/B disconnected-
10Set OCnA/BClear OCnA/BNon-inverting PWM mode
11Clear OCnA/BSet OCnA/BInverting PWM mode

5.2.2 The COM bits in phase-correct PWM modes

Phase correct mode Inverted phase correct mode In the phase-correct PWM mode
  1. the timer first counts up to the TOP value, then counts down to the bottom,
  2. 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),
  3. in inverted mode the OC pin is cleared when up-counting and set when down-counting (inverted mode, right),
  4. 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/B1COMnA/B0Up-countingDown-countingRemark
00OCnA/B disconnected-
10Clear OCnA/BSet OCnA/BInverting phase-correct PWM mode
11Set OCnA/BClear OCnA/BNon-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.

6 16-bit timers

16-Bit-Timer/Counter with prescaler 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:
  1. the 8-bit access requires a special handling to avoid glitching, and
  2. 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:
  1. 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.
  2. 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

Modes in 16-bit timer/counters 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:
  1. the WGM mode bits can select between 8-, 9- or 10-bit resolution, which sets the TOP value to 255, 511 or 1023,
  2. 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

Two timers with two duo-LEDs 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

Two timers with two duo-LEDs stacked 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.

7 Timer interrupts

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 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).

A timer for long times 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:

The 260ms single shot

;
; ***********************************
; * 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.

8 Counters

Counting pulses on T0 and T1 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 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