Home ==> Mikrobeginner ==> 6. LED with interrupt
ATtiny13

Lecture 6: A LED blinks with the timer interrupt


With this lecture we enter into the large and powerful world of interrupt programming and say good bye to simple linear program execution. As you will see, interrupts enable a whole world of complex operations while linear programming allows to solve only simple projects and requirements. With interrupts the controller will execute several tasks (seemingly) in parallel. If you are familiar with that you will never return to the linear method. But be aware that this requires lots of new techniques that are far outside the linear program execution world (who said that assembler programming will be easy learning as it offers so many new opportunties?).

6.0 Overview

  1. Introduction to interrupt programming
  2. Hardware, components, mounting
  3. Timer with Overflow interrupt
  4. Timer with CTC interrupt

6.1 Introduction to interrupt programming

6.1.1 Interrupts

The main advantage of interrupt programming is that several processes can run in parallel and that all arising conditions that require handling can be handled in due time and correct. This is the end of any delay loops with exact execution times.

Interrupts are automated breaks that occur whenever certain conditions come true. They interrupt normal program execution, perform so-called interrupt service routines and return back to exactly the location where normal execution was interrupted. For example, if the timer overflows (from 0xFF to 0x00), a certain bit in the timer's interrupt mask port is tested. If enabled, the normal program execution is suspended and the controller
  1. sets a certain bit in a port to signal that this interrupt occurred,
  2. saves the current execution address (PC counter) in a certain storage place,
  3. clears the Interrupt Enable bit I in its status register to prevent further interrupts from execution,
  4. jumps to a certain fixed location in the flash and continues execution there, and
  5. clears the set bit in the port to signal that this interrupt is actually processed (it could well be that an interrupt cannot be processed immediately, so the bit continues to signal that interrupt execution is still to be performed).
The further program execution is a matter of the self-designed interrupt service routine (ISR), e.g. what has to be done if the timer overflows. After having done what has to be done in case of a timer overflow
  1. the Interrupt-Enable bit I in the status register has to be set again,
  2. the saved program execution address is read from the storage, written back to the PC counter and program execution continues there (where execution was interrupted).
This execution flow has several serious consequences: I-Bit This is the location of the I bit in the status register. It is by default cleared. So to execute interrupts requires to set this bit. Don't forget this. This is done with the SEI instruction. If you need to (temporarily or permanently) disable interrupt execution use the CLI instruction to clear this bit.

This bit is automatically cleared at the start of the ISR, it has to be set at the end of the ISR (see below for a respective instruction).

Home Top Introduction Hardware Overflow-Int CTC-Int


6.1.2 Stack storage

To enable the execution of interrupts the controller needs a storage where the address of the PC can be temporarily stored after having been interrupted. For this the AVRs use a so-called stack in the static memory. The ATtiny13 has 64 bytes of SRAM on board, by far enough to store those two bytes. Those are not at fixed locations but are dynamically allocated on the stack. The stack is an SRAM area that starts at the last SRAM location (in def.inc defined as RAMEND) and grows downwards, to lower addresses. Its address is in port SPL (stack pointer Low). As the ATtiny13 has less than 256 bytes SRAM, the port SPH (stack pointer High) is not necessary in our case.

The throw of the PC's address at an interrupt is decreasing SPL by two bytes. After re-storing the PC to its original value before interrupting, SPL is having its previous value again.

The stack can further be used to call subroutines and, after execution, to return to the calling location. The respective instruction is RCALL: it pushes the PC (low and high byte) to the stack, jumps to the given address (resp. a relative displacement) and at the instruction RET back to the calling address.

The same is the case at an interrupt. At returning back the I flag in the status register has to be set to enable pending and further interrupts. The return from the ISR with the instruction RETI additionally sets the I flag.

6.1.3 Interrupt vectors

To which location in the flash storage does an interrupt jump? To the first locations in the flash storage called RESET and INTERRUPT vectors. In an ATtiny13 those are:
#AddressNameDescription
00000RESETJump address after reset, by applying the operating voltage, at brown-out detection and at a watchdog reset
10001INT0Level change on the INT0 input pin
20002PCINT0Level change on an input pin
30003TIM0_OVFTimer 0 overflow
40004EE_RDYFinished a write cycle to the EEPROM
50005ANA_COMPPolarity change on the analog comparer
60006TIM0_COMPATimer 0 Compare A
70007TIM0_COMPBTimer 0 Compare B
80008WDTWatchdog event
90009ADCAD conversion complete


In later parts of the course we will use all these vectors, excluding the watchdog.

As the vectors are consequtive and have only one word (in larger ATtiny and ATmega two words) a vector has to be either a return from the interrupt or a jump instruction. The first 10 instruction words in an ATtiny13 assembler program therefore look like this:

; Reset- and vector table
;
.CSEG ; Assemble to the flash storage (Code SEGment)
.ORG 0 ; Address to zero (Reset- and interrupt vektors start at zero)
	rjmp Start ; Reset Vektor, jump to init
	reti ; INT0-Int, inactive
	reti ; PCINT-Int, inactive
	reti ; TIM0_OVF, inactive
	reti ; EE_RDY-Int, inactive
	reti ; ANA_COMP-Int, inactive
	reti ; TIM0_COMPA-Int, inactive
	reti ; TIM0_COMPB-Int, inactive
	reti ; WDT-Int, inactive
	reti ; ADC-Int, inactive
;
; Program init at Reset
;
Start:
	; [Here the program starts]

The RETI instruction on all inactive vectors ensures that in case of an unplanned activation of an interrupt the return back is executed in a systematic manner.

More chaotic software designers are incautious and set the vector address with an .ORG directive. They forget that a flash memory location cannot be empty but still contains 0xFFFF, an NOP instruction. If they accidentially enable a timer 0 compare match interrupt funny things will happen. In the most probable case a different ISR is executed or the I flag is never set and further interrupts are simply blocked or the init routine restarts all indefintely. Good luck with debugging and identifying the error.

6.1.4 The timer overflow interrupt

Overflow-Int-Enable To use this interrupt the Overflow-Interrupt-Enable-Flag TOIE0 in the Timer Interrupt Mask Register TIMSK0 has to be set, e.g. with

	ldi R16,1<<TOIE0
	out TIMSK0 R16

Of course, And, of course, an ISR has to be written so that the jump in the vector table finds a location. Such a typical ISR could be:

ovflw_isr:
	in R15,SREG ; save status register in a register
	dec R17 ; count down a register
	brne ovflw_isr1 ; jump if not zero
	sbr R18,0b00000001 ; set bit 0 in register R18
	ldi R17,10 ; restart count down from 10
ovflw_isr1:
	out SREG,R15 ; restore status register
	reti ; return from interrupt and set I-flag

Typical is the saving of the status. Because the DEC and SBR instructions within the ISR change flags, it is absolutely crucial to do that.

Further, the ISR sets a flag in a register to signal that the timer reached overflow for ten times. This flag can be used outside the ISR to perform further operations, e.g. to write a chacter to an LCD or do other time-consuming operations. The ISR works fine as there is enough time: twenty timer overflows until the flag is overwritten should provide enough time to react.

6.1.5 The compare match interrupt

If the compare match interrupt bits OCIE0A and/or OCIE0B are set every match leads to a jump to the respective vector. If the timer is in CTC mode, the compare match A interrupt is initiated on every reset of the timer. The same applies to the PWM mode.

6.1.6 Interrupts and sleep modes

In the sleep mode "idle", that we used already, all interrupts wake up the controller. Execution continues with the ISR of the respective interrupt source, then continues with the instruction following the sleep instruction. In this phase it makes sense to check if any of the ISR routines has set flags that require extended reaction.

Home Top Introduction Hardware Overflow-Int CTC-Int


6.2 Hardware, components, mounting

Two LEDs For the interrupt experiments the same hardware as already used comes into play. The only change is that an additional red LED and a resistor is attached to OC0B (on pin 6). These components are already described in lecture 2.

6.3 Timer with overflow interrupt

6.3.1 Task description

The following task has to be performed:

6.3.2 Steps towards solution

It is clear from the task description that it requires a lot more than simple blinking mechanics. The parallel change in intensity with the second LED also requires some new methods. The solution is an interrupt-controlled counting method.

Such a program looks basically very different than a linear program. As most of the controller software is interrupt-driven a closer look into such structure is recommendable.

We use the timer overflow interrupt. The 1.2 Mcs/s controller clock divided by 256 pulses that are necessary to overflow the timer leads to 4,687.5 interrupts per second, if the prescaler would be at 1. If we divide this by 2 (for 0.5 s ON cycle and 0.5 s OFF cycle), we are at 2,344 ints per second (rounded up). As this is larger than 256 we need a 16 bit wide counter.

Because the masking of the fifth LED pulse requires some more complex processes we place this outside the interrupt service routine. This would not be necessary in any case because the next overflow int is 256 clock cycles later and we have only one single ISR to process, but we do that to learn the principle.

6.3.3 Program

This here is one of the possible solutions, here is the source code. The rigid listing of used registers, ports and port bits, the distinct naming of all constants and the complete interrupt vector list is not only for educational purposes but is a recommendable principle in assembler programming as it eases debugging and finding errors. This and the extended comments in the source code allows to understand the code even weeks after.

;
; ***************************************
; * Timer with Overflow interrupt       *
; * (C)2017 by www.avr-asm-tutorial.net *
; ***************************************
;
.NOLIST
.INCLUDE "tn13def.inc"
.LIST
;
; ---------- Registers ------------------
; free: R0 .. R14
.def rSreg = R15 ; SREG interim storage
.def rmp = R16 ; multi purpose register
.def rimp = R17 ; multi purpose inside interrupts
.def rFlag = R18 ; Flag register
	.equ bPol = 0 ; Flag polarity change
.def rCnt= R19 ; Blink counter
.def rPwm = R20 ; PWM counter
; free: R20 .. R23
.def rCntL = R24 ; 16 bit counter, LSB
.def rCntH = R25 ; dto., MSB
;free: R26 .. R31
;
; ---------- Ports, port bits -----------
.equ pLedOut = PORTB ; LED output port
.equ bLedOut = PORTB0 ; LED ON/OFF output pin
.equ pLedDdr = DDRB ; LED direction port
.equ bLedDdr = DDB0 ; LED direction pin
.equ pLedIn  = PINB ; LED input port
.equ bLedIn  = PINB0
;
.equ bPwmOut = PORTB1 ; LED PWM output pin
.equ bPwmDdr = DDB1 ; Direction pin PWM
;
; ---------- Timing ---------------------
.equ cClock  = 1200000 ; Controller clock
.equ cPolChange = 2 ; Polarity changes per cycle Led On/Off
.equ cPresc  = 1 ; Prescaler timer
.equ cCount  = cClock / 256 / cPresc / cPolChange + 1
.equ cBlink  = 5 ; Blink counter
;
; ---------- Reset- and Interrupt-vectors -------
.CSEG ; Program code segment
.ORG 0 ; Reset- and vector address
	rjmp Start ; Reset vektor, jump to init
	reti ; INT0-Int, inactive
	reti ; PCINT-Int, inactive
	rjmp Tc0IsrO ; TIM0_OVF, active
	reti ; EE_RDY-Int, inactive
	reti ; ANA_COMP-Int, inactive
	reti ; TIM0_COMPA-Int, inactive
	reti ; TIM0_COMPB-Int, inactive
	reti ; WDT-Int, inactive
	reti ; ADC-Int, inactive
;
; ---------- Interrupt-Service-Routines ----------
Tc0IsrO: ; Timer 0 Overflow ISR
	in rSreg,SREG ; save status register
	sbiw rCntL,1 ; decrease counter by one
	brne Tc0IsrO1 ; jump if not zero
	sbr rFlag,1<<bPol ; set flag polarity change
	ldi rCntH,HIGH(cCount) ; restart counter
	ldi rCntL,LOW(cCount)
Tc0IsrO1:
	mov rimp,rCntL ; copy Low byte counter
	andi rimp,0b00011111 ; isolate the lower five bits
	brne Tc0IsrO2 ; jump if not zero
	dec rPwm ; decrease PWM value
	out OCR0B,rPwm ; to compare match port B
Tc0IsrO2:
	out SREG,rSreg ; restore status register
	reti ; End of ISR, set I bit
;
; ---------- Initiieren und Programmschleife -----
Start:
	; Init stack
	ldi rmp,LOW(RAMEND) ; stack pointer to end of SRAM
	out SPL,rmp ; set stack pointer
	; Activate LED output pins
	ldi rmp,(1<<bLedDdr)|(1<<bPwmDdr) ; LED output pins on, drivers on
	out pLedDdr,rmp ; to direction port
	; Init counter
	ldi rCnt,cBlink ; init Blink counter
	ldi rCntH,HIGH(cCount) ; 16 bit counter to start value
	ldi rCntL,LOW(cCount)
	; Init compare register for PWM
	ldi rmp,0xFF ; Compare match A to 255
	out OCR0A,rmp ; to compare match port A
	clr rPwm ; Start value for PWM
	out OCR0B,rPwm ; to compare match port B
	; Start Timer/Counter 0 as timer with overflow int
	ldi rmp,(1<<COM0B1)|(1<<WGM01)|(1<<WGM00) ; Fast PWM, COM0B
	out TCCR0A,rmp ; to timer control port A
	ldi rmp,1<<CS00 ; prescaler 1, start timer
	out TCCR0B,rmp ; to timer control port B
	ldi rmp,1<<TOIE0 ; overflow interrupt
	out TIMSK0,rmp ; to timer interrupt mask
	; Sleep mode
	ldi rmp,1<<SE ; sleep enable, mode idle
	out MCUCR,rmp ; to universal control port
	; Enable interrupts
	sei ; set I flag in status register
; Program loop
Loop:
	sleep ; send to sleep
	nop ; delay one cycle after wakeup
	sbrc rFlag,bPol ; jump over next instruction if flag not set
	rcall Polarity ; process flag polarity change
	rjmp Loop ; send to sleep again
;
; --------- Change polarity of the LED ----------
Polarity:
	cbr rFlag,1<<bPol ; clear flag
	sbis pLedOut,bLedOut ; jump over next instruction if LED is off
	rjmp Polarity3 ;  Led is on, switch LED off
	; Led is Off
	cpi rCnt,0 ; is blink counter zero?
	breq Polarity1 ; yes, restart counter
	; Led off, counter not zero
	dec rCnt ; decrease blink counter
	brne Polarity2 ; switch Led on
	ret ; no polarity change, return
Polarity1: ; Restart counter
	ldi rCnt,cBlink ; restart counter
	ret ; no polarity change, return
Polarity2: ; switch Led on
	cbi pLedOut,bLedOut ; Led on
	ret ; return, LED on
Polarity3: ; switch Led off
	sbi pLedOut,bLedOut ; Led off
	ret ; return, Led off
;
; End of source code
;

New are the following instructions:

6.3.4 The result

Led combination These are the LEDs in different modes: the one to the right with abrupt on and off cycles, the one to the left with softly changing intensity. Current requirements are minimized by the sleep mode: while the controller is majourly sleeping and is only woken up when needed.

6.3.5 Program structuring

The visible structure of the source code is:
  1. Register definitions, port definitions and constants at the beginning,
  2. Reset- and interrupt vectors,
  3. Interrupt service routines,
  4. Main program inits with hardware configuration and start values,
  5. Loop processing, and
  6. routines outside interrupt service routines.
This structure makes sense in all interrupt controlled programs, in order to keep the overview. This also helps with potential debugging activities if the program does not do what it should.

6.3.6 Simulation of the processes

Simulation done with avr_sim is done with the following framework conditions: This is the situation after the init phase:

TC0 OVF interrupt after init The timer TC0 is in Fast PWM mode, its TOP and its compare match A value is set to 255, the PWM compare match value B is zero, it prescales clock by 1 and its overflow interrupt enable bit is set.

Port B after init In port B the two direction bits for pins PB0 and PB1 are set, while the portbits are zero, so both LEDs are switched on.

Simulation after init Init required 18.33 µs, set the stackpointer to the end of SRAM in the device, set the 16-bit counter in R25:R24 to 2,344 (=$0928), set the LED down counter in R19 to 5 and enabled the interrupt bit in SREG.

First Overflow, interrupt request The first overflow occured, the timer restarts and has its overflow interrupt flag set. With the next instruction, the controller executes the interrupt and jumps to the interrupt service routine.

Simulation first overflow Port after first overflow The overflow occurs after 208.3 µs, together with the int enable, the I flag enable and the sleep enable instructions 213 µs.

On overflow the portbit PB1 has been set and the PWM cycle starts.

Compare match B With the next instruction executed, the compare match B is executed, because TCNT was at 0 and Compare Match B was also at zero in the last clock period. The LED was off for one clock cycle and is switched on again. Remember: such an output cannot be completely on, it is off for at least one timer cycle.

TC0 interrupt execution With the next instruction the requested interrupt is executed. The controller dropped the current execution address to the stack and has jumped to the vector address.

Simulation Int + 1 On interrupt execution the stackpointer has decreased by two and the I flag in the status register is cleared.

Counter decrease Within the ISR the 16-bit counter is decreased. As it is not zero yet (see Z flag in SREG), the polarity change bit is not set during this turn.

TC0 interrupt after RETI Simulation after RETI Following the RETI instruction the TC0 interrupt is terminated, leaving the timer overflow interrupt enabled, the stackpointer returns to the top of the SRAM and the I flag is set. As the timer interrupt woke up the controller, the code following the sleep instruction will be executed after the interrupt service routine has finished. This code is executed 2,343 times with the polarity change flag cleared. The 2,344th time the flag is set and the LED output pin PB0 changes its polarity.

TC0 compare B set A special condition occurs if the compare match B value is changed within the execution of the interrupt service routine. As the compare match value in Fast PWM mode is only updated when the TOP of the timer is reached, the new value (255) is written to the compare match B portregister at the end of the current PWM cycle. The yellow background signals that there is a difference between the displayed and the effectice value. This mechanism ensures that no repeated compare match occurs and that the same compare match value is applied over the whole cycle.

End of first cycle This is the time when the first counting cycle is over: The counting time for the blinking LED on PB0 is fairly exact. It would be more exact if we would clear the stopwatch and repeat the cycle all over again, breaking execution always on the same code location.

End of the second cycle This is the end of the second cycle. Approximately 1 second has elapsed until the LED on PB0 will be switched on again.

6.3.7 Advantages and disadvantages

The advantage of this solution is high flexibility: it can simply be modified to fit to other needs. The frequncy of blinking or changes of the LED port pins can simply be modified by changing a few constants on top.

The disadvantage is that blinking is not exact. This is because dividing by 256 does not lead to an integer value. This disadvantage is addressed by the following opportunity.

Home Top Introduction Hardware Overflow-Int CTC-Int


6.4 Timer with CTC interrupt

In order to achieve a more exact timing the following is considered.

6.4.1 CTC selection

Different divider factors can be achieved in the CTC mode of the timer. Other division factors than 256 are possible. To achieve more exact timings a combination of the clock frequency, the prescaler, the CTC compare match and the software counter has to be selected in a more intelligent way. The following table provides for all clock frequencies of the ATtiny13 and for 8 and 16 bit counters the potential CTC dividers that result in an integer value of 1 cs/s.

Colors:16 bit counter 8 bit counter CTC=256@16 bit counter CTC=256@8 bit counter

From that the following consequences result (CTC values larger than 100 only).
Clock (cs/s)Prescaler Integer CTC divider
9,600,0001256 (37500) 250 (38400)240 (40000) 200 (48000)192 (50000) 160 (60000)150 (64000)
8250 (4800)240 (5000) 200 (6000)192 (6250) 160 (7500)150 (8000) 128 (9375)
64250 (600)240 (625) 200 (750)150 (1000)
256250 (150)150 (250)
1024
4,800,0001256 (18750) 250 (19200)240 (20000) 200 (24000)192 (25000) 160 (30000)150 (32000) 128 (37500)
8250 (2400)240 (2500) 200 (3000)192 (3125) 160 (3750)150 (4000)
64250 (300)200 (375) 150 (500)
256250 (75)150 (125)
1024
2,400,0001256 (9375) 250 (9600)240 (10000) 200 (12000)192 (12500) 160 (15000)150 (16000) 128 (18750)
8250 (1200)250 (1200) 240 (1250)200 (1500) 160 (1875) 
64 250 (150)150 (250)  
256 
1024 
1,200,0001250 (4800) 240 (5000)200 (6000) 192 (6250)160 (7500) 150 (8000)128 (9375)
8250 (600)240 (625) 200 (750)150 (1000)
64250 (75)150 (125)
256
1024
128,0001256 (500) 250 (512)200 (640) 160 (800)128 (1000)
8250 (64)200 (80) 160 (100)128 (125)
64250 (8)200 (10)
256250 (2)
1024


For 1.2 Mcs/s a CTC divider of 250 (125 for 2 cs/s) and a prescaler value of 64 is appropriate and convenient. Because the second LED has to be controlled by a PWM the timer has to be operated in PWM mode.

6.4.2 Program

Now an 8 bit register is sufficient as CTC counter, here is the source code..

;
; ***************************************
; * Timer with COMP-A-Interrupt         *
; * (C)2017 by www.avr-asm-tutorial.net *
; ***************************************
;
.NOLIST
.INCLUDE "tn13def.inc"
.LIST
;
; ---------- Registers ------------------
; free: R0 .. R14
.def rSreg = R15 ; SREG interim storage
.def rmp = R16 ; multi purpose register
.def rimp = R17 ; multi purpose inside ISRs
.def rFlag = R18 ; Flag register
	.equ bPol = 0 ; Flag polarity change
.def rCnt= R19 ; Blink counter
.def rCtcInt = R20 ; CTC interrupt counter
.def rPwm = R21 ; PWM counter
; free: R22 .. R31
;
; ---------- Ports, Port bits -----------
.equ pLedOut = PORTB ; LED output port
.equ bLedOut = PORTB0 ; LED on/off pin
.equ pLedDdr = DDRB ; LED direction port
.equ bLedDdr = DDB0 ; LED direction pin
.equ pLedIn  = PINB ; LED input port
.equ bLedIn  = PINB0 ; LED input pin
;
.equ bPwmOut = PORTB1 ; second LED Pwm pin
.equ bPwmDdr = DDB1 ; second LED direction pin
;
; ---------- Timing ---------------------
.equ cClock  = 1200000 ; controller clock
.equ cPolChange = 2 ; frequency Led on/off
.equ cPresc  = 64 ; prescaler timer
.equ cCtcInt = 125 - 1 ; CTC int counter
.equ cCtcCnt = cClock / cPresc / cPolChange / (cCtcInt +1)
.equ cBlink  = 5 ; fifth pulse off
;
; ---------- Reset- and Interrupt-vectors -------
.CSEG ; Program code
.ORG 0 ; Reset- and vector table address
	rjmp Start ; Reset vector, jump to init
	reti ; INT0-Int, inactive
	reti ; PCINT-Int, inactive
	reti ; TIM0_OVF, inactive
	reti ; EE_RDY-Int, inactive
	reti ; ANA_COMP-Int, inactive
	rjmp Tc0IsrA ; TIM0_COMPA-Int, active
	reti ; TIM0_COMPB-Int, inactive
	reti ; WDT-Int, inactive
	reti ; ADC-Int, inactive
;
; ---------- Interrupt Service Routines ----------
Tc0IsrA: ; Timer 0 Compare A Interrupt
	in rSreg,SREG ; save status register
	dec rPwm ; decrease PWM counter
	brne Tc0IsrA1 ; jump if not zero
	ldi rPwm,cCtcInt ; restart PWM counter
Tc0IsrA1:
	out OCR0B,rPwm ; update PWM compare
	dec rCtcInt ; decrease ctc counter
	brne Tc0IsrA2 ; jump if not zero
	sbr rFlag,1<<bPol ; set flag polarity change
	ldi rCtcInt,cCtcCnt ; restart ctc counter
Tc0IsrA2:
	out SREG,rSreg ; restore status register
	reti ; End of ISR, set I bit
;
; ---------- Init and program loop -----
Start:
	; Init stack
	ldi rmp,LOW(RAMEND) ; stack pointer to SRAM end
	out SPL,rmp ; set stack pointer
	; Activate LED output pins
	ldi rmp,(1<<bPwmDdr)|(1<<bLedDdr) ; LED output pins, driver on
	out pLedDdr,rmp ; to direction port
	; Init counters
	ldi rCnt,cBlink ; init blink counter
	ldi rCtcInt,cCtcCnt ; init ctc counter
	ldi rPwm,cCtcInt ; init PWM start value
	; Compare match A value for CTC
	ldi rmp,cCtcInt ; CTC divider 125
	out OCR0A,rmp ; to compare match port A
	; Timer 0 as in PWM mode, start with overflow interrupt
	ldi rmp,(1<<COM0B1)|(1<<WGM01)|(1<<WGM00) ; PWM on port OC0B, Fast PWM
	out TCCR0A,rmp ; to timer control port A
	ldi rmp,(1<<WGM02)|(1<<CS01)|(1<<CS00) ; Fast PWM, prescaler 64, start
	out TCCR0B,rmp ; to timer control port B
	ldi rmp,1<<OCIE0A ; Compare A interrupt
	out TIMSK0,rmp ; to timer interrupt mask
	; Sleep mode
	ldi rmp,1<<SE ; sleep enable, mode idle
	out MCUCR,rmp ; to universal control register
	; Enable interrupts
	sei ; set interrupt flag in status register
; Program loop
Loop:
	sleep ; put to sleep
	nop ; delay after wakeup
	sbrc rFlag,bPol ; jump over next instruction if flag clear
	rcall Polarity ; process flag
	rjmp Loop ; go to sleep again
;
; --------- Polarity change of the LED ----------
Polarity:
	cbr rFlag,1<<bPol ; clear flag
	sbis pLedOut,bLedOut ; jump over next instruction if LED is off
	rjmp Polarity3 ;  Led is on, jump to LED = on
	; Led is off
	cpi rCnt,0 ; is the blink counter zero?
	breq Polarity1 ; yes, restart counter
	; Led off, counter not zero
	dec rCnt ; decrease blink counter
	brne Polarity2 ; switch Led on
	ret ; return, no polarity change
Polarity1: ; Restart counter
	ldi rCnt,cBlink ; restart counter
	ret ; return, no polarity change
Polarity2: ; switch Led on
	cbi pLedOut,bLedOut ; Led on
	ret
Polarity3: ; switch Led off
	sbi pLedOut,bLedOut ; Led off
	ret
;
; End of source code
;

Of course, the change in the LED timing is invisible, the difference to the previous solution is minimal. But we know for sure that this is exact because we programmed that.

6.4.3 Simulation

TC0 CTC mode after init Port B after init Those are the configurations after initiation. The timer is in Fast PWM mode, its TOP value equals the Compare Match A value of 125 (dividing the clock signal by 125), the interrupt on compare match A is enabled and the prescaler is set to divide the controller clock by 64. The port drives the two output pins according to the PORTB values, that will be controlled via software (PB0) resp. via timer PWM compare match B (set on PWM cycle start and cleared on compare match B).

TC0 first ctc int Port B first ctc int The timer has reached the CTC value 124 and, with the next counting pulse, has cleared TCNT (CTC on compare Match A) and is executing the Compare Match A interrupt service routine. Compare Match B value has been set to 123, just one timer pulse below the CTC value. The port pin PB0 has been cleared.

Simulation first CTC interrupt Since entering the first sleep instruction 6.67 ms have elapsed, which is rather exact 125 * 64 / 1,200,000 seconds.

Simulation first cycle This is the end of the first cycle when the polarity flag is set to blink the LED after half a second has been reached.

Exact timing with a timer in CTC mode can be achieved at whatever time, no matter at which frequency the controller works and what the controller has to do in between. Here we combined the second blinking with a PWM controlled LED intensity. Many other things might be tight to this timer signals of one single 8-bit timer. Design your own to fit it to your specific needs!

Home Top Introduction Hardware Overflow-Int CTC-Int


©2017 by http://www.avr-asm-tutorial.net