AVR Assembler Source Blog

Ремонт частотных преобразователей AVR Assembler Source Blog: Timer_interrupt_mode

Saturday, October 5, 2013

Timer_interrupt_mode

ремонт частотников спб
                              ; Test 4: Even more about the board: timer in interupt mode
                              ; New things to learn here:
                              ; - Timer in interrupt mode
                              ; - Interrupts, Interrupt-vektor
                              ; - BCD-arithmetic
                              ; Here is my substitute for the DEVICE-command
.NOLIST
.INCLUDE "8515def.inc"
.LIST
                              ; Universal register definition
.DEF mp=R16
                              ; Counter for timer timeouts, MSB timer driven by software
.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 for counting the seconds as packed BCD
.DEF sec=R2
                              ; Reset-vector to adress 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-vector!
       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 the use by interrupts and subroutines
       ldi mp,HIGH(RAMEND)
       out SPH,mp
                              ; Software-Counter-Register reset to zero
       ldi mp,0               ; z1 cannot be set to a constant value, so we set mp
       mov z1,mp              ; to zero and copy that to R0=z1
       mov sec,mp             ; and set the seconds to zero
                              ; 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 for timer 0
       oldi mp,$02            ; set Bit 1
       oout TIMSK,mp          ; in the Timer Interupt Mask Register
                              ; enable all interrupts generally
       sei                    ; enable all interrupts by setting the flag in the 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 LSB in the hardware counter
       cpi mp,$09             ; compare with the target value
       brlt loop2             ; TCNT0 < 09, wait
       ldi mp,0               ; set register zero and ...
       out TCNT0,mp           ; reset hardware-counter LSB
       mov z1,mp              ; and software-counter MSB
       rcall IncSec           ; call the subroutine to increment the seconds
       rcall Display          ; call subroutine to display the seconds
       rjmp loop              ; once again the same
                              ; subroutine increment second counter
                              ; in BCD-arithmetic! Lower nibble = Bit 0..3, upper nibble = 4..7
IncSec:
       sec                    ; Setze Carry-Flag for adding an additional one to the seconds
       ldi mp,6               ; povoke overflow of the lower nibble by adding 6
       adc sec,mp             ; add 6 + 1 (Carry)
       brhs Chk60             ; if overflow of the lower nibble occurred go to 60 check
       sub sec,mp             ; subtract the additional 6 as no overflow occurred
Chk60:
       ldi mp,$60             ; 60 seconds already reached?
       cp sec,mp
       brlt SecRet            ; jump if less than 60
       ldi mp,256-$60         ; Load mp to add sec to zero
       add sec,mp             ; Add mp to reset sec to zero
SecRet:
       ret                    ; return to the main program loop
                              ; subrountine for displaying the sonds on the LEDs
Display:
       mov mp,sec             ; copy seconds to mp
       com mp                 ; One-complement = XOR(FF) to invert the bits
       out PORTB,mp           ; send to the LED port
       ret                    ; return to main program loop

1 comment: