PathHome => AVR-Overview => Tutorial => Part 4

; Test 4: Learn more about timer in interrupt mode


; New things to learn here:

; - Timer in interrupt mode
; - Interrupts, Interrupt-Vector
; - BCD-Arithmetic

.NOLIST
.INCLUDE "8515def.inc"
.LIST

; define universal register


.DEF   mp = R16

; Counts the number of overflows of the hardware counter, called software counter

.DEF   z1 = R0

; Working register for the Interrupt-Service-Routine

; Note that any registers used during an interrupt, including the
; status-register with all the flags, must either be
; reserved for that purpose or they have to reset to their initial
; value at the end of the service routine! Otherwise nice and
; nearly unpredictable effects will occur.

.DEF   ri = R1

; Register zum Zählen der Sekunden, gepackte BCD-Ziffern


.DEF   sec = R2

; Reset-Vektor on address 0000


   RJMP   main

; This is the first time we really need this RJMP command, because here
; we have to put interrupt vectors to position 1, 2, 3 and so on.
; Interrupt-vector definitions not used here (all but the timer overflow vector)
; are dummied by the return-from-interrupt command RETI,
; RETI is a special return command for interrupt service routines as it
; preserves the interrupt-flags in the status-register. Be sure that the jump
; to the interrupt service routine tc0i is exactly at adress 0007, otherwise
; the interrupt fails. The following mechanism goes on: If the timer overflows
; (transition from 255 to 0) the program run is interrupted, the current adress
; in the program counter is pushed to the stack, the command at adress 0007
; is executed (usually a jump instruction). After finishing execution of the
; interrupt service routine the program counter value is restored from the
; stack and program execution maintains at that point.

The interrupt-vector commands, 1 Byte each:


   RETI ; Int0-Interrupt
   RETI ; Int1-Interrupt
   RETI ; TC1-Capture
   RETI ; TC1-Compare A
   RETI ; TC1-Compare B
   RETI ; TC1-Overflow
   RJMP   tc0i ; Timer/Counter 0 Overflow, my jump to the service routine
   RETI ; Serial Transfer complete
   RETI ; UART Rx complete
   RETI ; UART Data register empty
   RETI ; UART Tx complete
   RETI ; Analog Comparator

; Interrupt-Service-Routine for the counter

tc0i:   IN   ri,SREG ; save the content of the flag register
   INC   z1 ; increment the software counter
   OUT   SREG,ri ; restore the initial value of the flag register
   RETI ; Return from interrupt

; ; The main program starts here


main:   LDI   mp,LOW(RAMEND) ;Initiate Stackpointer
   OUT   SPL,mp ; for use with interrupts and subroutines
   LDI   mp,HIGH(RAMEND)
   OUT   SPH,mp

; Software-counter-register to zero

   LDI   mp,0 ; z1 cannot be accessed directly
   MOV   z1,mp ; copy 0 in mp to z1
   MOV   sec,mp ; and to the seconds counter

; Prescaler of the counter/timer = 256, that is 4 MHz/256 = 15625 Hz = $3D09

   LDI   mp,0x04 ;Initiate Timer/Counter 0 prescaler
   OUT   TCCR0,mp ; to Timer 0 Control register

; Port B is LED-port

   LDI   mp,0xFF ; all bits are output
   OUT   DDRB,mp ; to data direction register

; enable interrupts from timer 0

   LDI   mp,$02 ; Bit 1 set to 1
   OUT   TIMSK,mp ; in Timer Interupt Mask Register

; Enable all interrupts

   SEI ; enables interrupts by setting flag in status-register

; The 8-bit counter overflows from time to time and the interrupt service
; routine increments a counter in a register. The main program loop reads this
; counter register and waits until it reaches hex 3D. Then the timer is read until
; he reaches 09 (one second = dez 15625 = hex 3D09 timer pulses). The timer
; and the register are set to zero and one second is incremented. The seconds
; are handled as packed BCD-digits (one digit = four bits, 1 Byte represents
; two digits). The seconds are reset to zero if 60 is reached. The seconds
; are displayed on the LEDs.

loop:   LDI   mp,$3D ; compare value for register counter
loop1:   CP   z1,mp ; compare with the register
   BRLT   loop1 ; z1 < mp, wait
loop2:   IN   mp,TCNT0 ; read counter LSB
   CPI   mp,$09 ; compare with LSB
   BRLT   loop2 ; TCNT0 < 09, wait
   LDI   mp,0 ; zero to
   OUT   TCNT0,mp ; hardware-counter LSB
   MOV   z1,mp ; and to software-counter MSB
   RCALL   IncSec ; subroutine increment second counter
   RCALL   Display ; display second counter
   RJMP   loop ; the same again

; subroutine for incrementing the second counter

; in BCD-arithmetic! Lower Nibble = Bit 0..3, Upper Nibble = 4..7

IncSec:   SEC ; Set Carry-Flag for adding a one
   LDI   mp,6 ; Provoke overflow of lower Nibble
   ADC   sec,mp ; by adding 6 + 1 (Carry)
   BRHS   Chk60 ; if overflow then jump to 60-Check
   SUB   sec,mp ; subtract 6, no overflow occurred
Chk60:   LDI   mp,$60 ; compare with 60
   CP   sec,mp
   BRLT   SecRet 
; jump, if lower than 60
   LDI   mp,256-$60 ; load complement
   ADD   sec,mp ; and add to zero
SecRet:   RET ; return to the main program

; subroutine displaying the second counter on the LEDs

Display:
   MOV   mp,sec 
; copy seconds to mp
   COM   mp ; One-complement = XOR(FF) to invert
   OUT   PORTB,mp ; software-counter to LEDs
   RET ; Back to the main program


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