Path: Home => AVR-EN => Applications => Stop watches with AVRs => Stopwatch ATtiny24 => Assembler Source Code
Stopwatch_tn24 AVR Applications

Stopwatch with ATtiny24
Assembler source code
Logo

Assembler source code for the ATtiny24 stopwatch

The source code in asm format can be downloaded from here and requires the modified include file Lcd4Busy_mod.inc that can be downloaded from here.

;
; ******************************************
;  Stopwatch with LCD and Textlines Vers.04
;  Date: 25.07.2018 / 20:22
;  (C)2018 by Jochen Girschik
; ******************************************
;  
     .nolist
     .include "tn24adef.inc"
     .list
;
;    1000 Hz clock (1ms) generation:
;    Internal RC Oscillator clock 8 MHz
;      divided with clock prescaler = 2 to
;      effective controller Clock 4 MHz
;    Timer/Counter1 as millisecond generator:
;      Timer/Counter prescaler = 8 yields 500.000 Hz
;      Clear timer on Compare at 500-1 (CTC)
;      Interrupt at Compare Match A
;
;******************
;*   Constants    *
;******************
;
        .equ Clock      =  4000000         ; clock frequency
        .equ cstopclock =  1               ; clock stopwatch in ms
        .equ cToggle    =  255/cstopclock  ; Number of CTC cycles,
                                           ; Toggle suppression
;
;****************
;*   REGISTER   *
;****************
;       Register R0 used by lcd include
;		Registers R1 - R7 free
;
        .def rmSec      =  R8   ; Milliseconds
        .def rToggle    =  R9   ; Toggle counter
        .def rKey       =  R10  ; Key value
        .def rcSec      =  R11  ; Centiseconds
        .def rSec       =  R12  ; Seconds
        .def rMin       =  R13  ; Minutes
        .def rHour      =  R14  ; Hours
        .def rSreg      =  R15  ; Save/Restore SREG
        .def rmp        =  R16  ; Multi purpose register
        .def rFlag      =  R17  ; Flag register
            .equ bKey   =  0    ; Key flag
            .equ bToggle=  1    ; Toggle time flag
            .equ b1ms   =  2    ; 1 ms over
            .equ bRun   =  3    ; Stopwatch runs
        .def rimp       =  R18  ; Multi purpose register in interrupts
        .def rmo        =  R19  ; Multi purpose register LCD
        .def rLine      =  R20  ; Line counter LCD
        .def rRead      =  R21  ; Read register LCD
;
;       Registers R22 - R25 free
;       Registers R27:R26 used as XH:XL (in LCD and in Shift)
;       Registers R29:R28 free
;       Registers R31:R30 used as ZH:ZL for multiple purposes
;
;
;*******************************
;*     Define data segment     *
;*******************************
;
     .DSEG
        .ORG SRAM_START
;
     Times:
        .byte 20
;
;
;
;*************************************
;*   Reset- and Interrupt vectors    *
;*************************************
     .CSEG
     .ORG 0
        rjmp START              ; Reset vector
        reti ; EXT_INT0
        reti ; PCI0
        reti ; PCI1
        reti ; WATCHDOG
        reti ; ICP1
        RJMP INT_COMPA          ; OC1A
        reti ; OC1B
        reti ; OVF1
        reti ; OC0A
        reti ; OC0B
        reti ; OVF0
        reti ; ACI
        reti ; ADCC
        reti ; ERDY
        reti ; USI_STR
        reti ; USI_OVF   
;
;*********************************    
;*   Interrupt Service Routine   *
;*********************************
;
     INT_COMPA:
        IN rSreg, SREG          ; Save Status
        SBRS rFlag, bToggle     ; bToggle set? if yes jump over next instruction
        RJMP KeyPress           ; No, jump to key press
        IN rimp, PINA           ; Read key status
        ANDI rimp, 0x0F         ; Clear upper bits
        CPI rimp, 0x0F          ; Any key pressed?
        BREQ NoKey              ; No key pressed, jump 
        LDI rimp, cToggle       ; Load toggle constant
        MOV rToggle, rimp       ; Write to rToggle
        RJMP MsFlag             ; Done, move to MsFlag
;
     NoKey:
        DEC rToggle              ; Count toggle counter down
        BRNE MsFlag              ; Not yet zero
        CBR rFlag,1<<bToggle     ; Clear bToggle flag (toggle time over)
        RJMP MsFlag              ; Done, move to MsFlag
;
     KeyPress:
        IN rimp, PINA           ; Read key state
        ANDI rimp, 0x0F         ; Clear upper bits
        CPI rimp, 0x0F          ; No key pressed?
        BREQ MsFlag             ; Yes, move to MsFlag
        MOV rKey, rimp          ; Copy current key status
        SBR rFlag,(1<<bKey)|(1<<bToggle)    ; Set flags
        LDI rimp, cToggle        ; Load toggle constant
        MOV rToggle, rimp        ; Copy to rToggle
;
     MsFlag:
        SBR rFlag, 1<< b1ms     ; Set b1ms flag
        OUT SREG, rSreg         ; Restore status
        RETI                    ; Return from interrupt
;
;**************************
;*   Main program init    *
;**************************
;
     START:
;
;    Increase clock frequency
;	
        LDI rmp,1<<CLKPCE       ; Controller clock setting with clock prescaler (CLKPR)
                                ; Enable with CLKPCE set (all other bits cleared)
        OUT CLKPR, rmp          ; Write to CLKPR schreiben
; 
        LDI rmp, 1<<CLKPS0      ; Set the CLKPS0 bit (Prescaler = 2)
                                ; (CLKPCE cleared)
        OUT CLKPR, rmp          ; Write to port CLKPR
;
     .ifdef SPH                 ; For ATtiny44/84
        LDI rmp, HIGH(RAMEND)   ; Init MSB stack pointer
        OUT SPH, rmp
     .endif
        LDI rmp, LOW(RAMEND)    ; Init LSB stack pointer
        OUT SPL, rmp
;
;
;    Init ports for LCD 
;
        LDI rmp,(1<<bLcdCRE)|(1<<bLcdCRRS)|(1<<bLcdCRRW)
;
        OUT pLcdCR,rmp          ; Direction of control ports
        CLR rmp                 ; Clear outputs
        OUT pLcdCO,rmp          ; to output port
        LDI rmp,mLcdDRW         ; Data port output mask
        OUT pLcdDR,rmp          ; Write to direction port
;
        RCALL LcdInit           ; Call subroutine LCDInit
        RCALL LcdCurs0          ; LcdCurs0 aufrufen, disable cursor
;
;	
;    Write Text lines to LCD
;	
;
        LDI ZH, HIGH(2*TextTable)   ; Lade text table
        LDI ZL, LOW(2*TextTable)    ; Load text table
        RCALL LcdText                 ; Call subroutine LcdText
;
;    Init time and output
;
        RCALL Empty             ; Call subroutine Unterprogramm SRAM empty		
        RCALL LcdOut_Hour       ; Call subroutine LcdOut_Hour to display
;
;    Init the keys          
;
        IN rmp, DDRA            ; Read direction port
        ANDI rmp, 0xF0          ; Clear lower nibble
        OUT DDRA, rmp           ; Write to direction register Port A
        IN rmp, PORTA           ; Read output port A to rmp
        ORI rmp, 0x0F           ; Set lower nibble (Pull-up resistors on)
        OUT PORTA, rmp          ; To output port A
;
;
;    Millisecond clock generation
;
        LDI rmp, High(500-1)    ; MSB CTC value
        OUT OCR1AH, rmp         ; MSB to output compare port 1A
        LDI rmp, Low(500-1)     ; LSB CTC value
        OUT OCR1AL, rmp         ; LSB to output compare port 1A		
;
;    CTC mode and prescaler to 8
;
        LDI rmp, (1<<WGM12)|(1<<CS11)  ; WGM12 = CTC mode, CS11=Prescaler = 8
        OUT TCCR1B, rmp         ; To Timer/Counter1 Control Register B
; 
        LDI rmp, 1<<OCIE1A      ; Interrupt on timer compare match
        OUT TIMSK1, rmp         ; To Timer/Counter 1 Interrupt Mask
;
        LDI rmp, 1<<SE          ; Set Sleep Enable
        OUT MCUCR, rmp          ; In MCU Control Register
;
        SEI                     ; Global Interrupt Enable in SREG
;
;**************************************
;*   Loop == Start the program loop   *
;**************************************
;
     Loop:
        SLEEP
	NOP
        SBRC rFlag, b1ms        ; b1ms flag set? If not skip next instruction
        RCALL FlagTime          ; Yes, handle flag
        SBRC rFlag, bKey        ; Flag bKey set? If not skip next instruction
        RCALL FlagKey           ; Yes, handle flag
        SBRC rFlag, b1ms        ; Flag b1ms now set? If not skip next instruction
        RCALL FlagTime          ; Yes, handle flag again
        RJMP Loop               ;
;
;************************
;*   FlagTime set       *
;************************
;
     FlagTime:
        CBR rFlag, 1<<b1ms      ; Clear fFlag
        SBRS rFlag, bRun        ; Is the sopwatch running? If yes, skip next
        RET                     ; Jump back
;
;    MilliSek
        INC rmSec               ; Increase milliseconds
        LDI rmp, 10             ; Load max value
        CP rmSec, rmp           ; Compare with 10
        BRNE LcdOut_Milli       ; If not equal jump to LCD display milliseconds
;
;    Centi_Sek
        CLR rmSec               ; Restart milliseconds
        INC rcSec               ; Increase centiseconds
        LDI rmp, 100            ; Load compare value (100)
        CP rcSec, rmp           ; Compare with 100
        BRNE LcdOut_Centi       ; If not equal jump to display centiseconds
;
;    Sekunden
        CLR rcSec               ; Clear centiseconds
        INC rSec                ; Increase seconds
        LDI rmp, 60             ; Load compare value (60)
        CP rSec, rmp            ; Compare with 60
        BRNE LcdOut_Sek         ; If not equal jump to display seconds
;
;    Minuten
        CLR rSec                ; Clear seconds
        INC rMin                ; Increase minutes
        CP rMin, rmp            ; Compare with 60
        BRNE LcdOut_Min         ; If not equal jump to display minutes
;
;    Stunden
        CLR rMin                ; Clear minutes
        INC rHour               ; Increase hours
        LDI rmp, 24             ; Load compare value (24)
        CP rHour, rmp           ; Compare with 24
        BRNE LcdOut_Hour        ; If not equal jump to display hours
;
        CLR rHour               ; Clear hours
;
;    The positions of times
;
;    Line 1 is    "Current=00:00:00.000"	
;    Position LCD "01234567890123456789"
;	
     LcdOut_Hour:               ; Display hours
        LDI ZH, 0               ; on line 1
        LDI ZL, 8               ; Position starts at column 8
        RCALL LcdPos            ; Call LcdPos(ition)	
        MOV rmp, rHour          ; Copy rHour to rmp
        RCALL Bin2Dec2          ; Subroutine binary to 
;                               ; 2 decimal digits conversion
        LDI rmp, ':'            ; Colon to rmp
        RCALL LcdChar           ; and display
;
     LcdOut_Min:                ; Display minutes			
        LDI ZH, 0               ; in line 1
        LDI ZL, 11              ; Position starts at column 12	
        RCALL LcdPos            ; Call subroutine LcdPos
        MOV rmp, rMin           ; Copy minutes to rmp
        RCALL Bin2Dec2          ; Subroutine binary to 
;                               ; 2 decimal digits conversion
        LDI rmp, ':'            ; Colon to rmp
        RCALL LcdChar           ; and display
;
     LcdOut_Sek:                ; Display seconds
        LDI ZH, 0               ; in line 1
        LDI ZL, 14              ; Position starts at column 15
        RCALL LcdPos            ; Call subroutine LcdPos
        MOV rmp, rSec           ; Copy seconds to rmp
        RCALL Bin2Dec2          ; Subroutine binary to 
;                               ; 2 decimal digits conversion
        LDI rmp, ','            ; Colon to rmp
        RCALL LcdChar           ; and display
;
     LcdOut_Centi:              ; Display centiseconds
        LDI ZH, 0               ; in line 1
        LDI ZL, 17              ; Position starts at column 18
        RCALL LcdPos            ; Call subroutine LcdPos
        MOV rmp, rcSec          ; Copy centiseconds to rmp
        RCALL Bin2Dec2          ; Call binary-to-2-digit-decimal
;
     LcdOut_Milli:              ; Display milliseconds
        LDI ZH, 0               ; in line 1
        LDI ZL, 19              ; Position starts at column 20
        RCALL LcdPos            ; Call subroutine LcdPos
        MOV rmp, rmSec          ; Copy milliseconds to rmp
        SUBI rmp, -'0'          ; Add ASCII-zero
        RCALL LcdChar           ; Display character
;	 
        RET                     ; Return from subroutine
;
;*************************
;*   FlagKey set         *
;*************************
;
     FlagKey:
        CBR rFlag, 1<<bKey      ; Clear flag
        LSR rKey                ; Bit 0 of key input to carry
        BRCS Flag1              ; Carry not clear, jump to Flag1
        ; The reset button has been pushed
        CBR rFlag, 1<<bRun      ; Clear bit bRun in rFlag
                                ; Time increase off
        CLR rHour               ; Clear rHour
        CLR rMin                ; Clear rMin
        CLR rSec                ; Clear rSec
        CLR rcSec               ; Clear rcSec
        CLR rmSec               ; Clear rmSec
        RJMP LcdOut_Hour        ; Display hours and all other time info
;
      Flag1:                  
        LSR rKey                ; Bit 1 of key input to Carry
        BRCS Flag2              ; Carry not zero, jump to Flag2
        ; Start button has been pushed
        SBR rFlag, 1<<bRun      ; Set bRun flag active
        RET                     ; Done, back to Loop
;
     Flag2:
        LSR rKey                ; Bit 2 of key input to Carry
        BRCS Flag3              ; Carry not zero, jump to Flag3
        ; Stop button has been pressed
        CBR rFlag, 1<<bRun      ; Clear bRun flag
        RET                     ; Done, back to Loop
;
     Flag3:
        LSR rKey                ; Bit 3 of key input to Carry
        BRCS Flag3Ret           ; Carry not zero, jump to RET
;
;    Store time at reserved space in SRAM
;
        STS Times, rHour        ; Store rHour to SRAM 0x0060
        STS Times+1, rMin       ; Store rMin to SRAM 0x0061
        STS Times+2, rSec       ; Store rSec to SRAM 0x0062
        STS Times+3, rcSec      ; Store rcSec to SRAM 0x0063
        STS Times+4, rmSec      ; Store rmSec to SRAM 0x0064
        RCALL Shift             ; Call subroutine Shift
        RJMP Display            ; Jump to display all times
;
     Flag3Ret:                  ; Return from subroutine
        RET                     ; Back to RCALL +1
;
;	
;**********************
;*   Subroutines      *
;**********************
;
;    Convert binary in register rmp 
;    to 2 decimal digits and display on LCD
;
     Bin2Dec2:
        MOV ZH, rmp             ; Copy rmp to ZH
        LDI rmp, '0'-1          ; Load ASCII-0 minus 1 (=0x29) to reg rmp
;
     Bin2Dec2A:
        INC rmp                 ; Increase ASCII-number
        SUBI ZH, 10             ; Subtract 10 from ZH
        BRCC Bin2Dec2A          ; Repeat as long as Carry is clear
;
        RCALL LcdChar           ; Display ASCII character in rmp (first decimal digit)
        LDI rmp, '0'+10         ; Load Ascii-0 plus 10 to register rmp
        ADD rmp, ZH             ; Add ZH to register content in rmp
        RJMP LcdChar            ; Display ASCII character in rmp (second decimal digit)
;
;
;    Text lines on LCD as table
;
     TextTable:	
        .db "Current=", 0x0D,0xFF       ; Line 1
        .db "Last   =", 0x0D,0xFF       ; Line 2
        .db "Prelast=", 0x0D,0xFF       ; Line 3
        .db "PrePre ="                  ; Line 4
        .db 0xFE, 0xFE                  ; End of text
;
;
;    Clear SRAM data in 0x0060 to 0x0074
;
     Empty:                          ; Subroutine Empty
        LDI ZH, HIGH(Times)          ; Load ZH with MSB address SRAM(0x0060)
        LDI ZL, LOW(Times)           ; Load ZL with LSB address SRAM(0x0060)
;
     Empty1:                         ;   
        CLR R16                      ; Clear register rmp 
        ST Z+, R16                   ; Store rmp (=0) in SRAM
                                     ; Address Z with Auto-Increment
        CPI ZL, LOW(Times+20)        ; Compare ZL with Times+20
                                     ; (0x0060 + 0x0014 = 0x0074)
        BRNE Empty1                  ; If not equal: repeat
        RET                          ; Back to calling address +1
;
;
;    Shift SRAM storage
;      Shifts all times in storage by 5 positions
;      Works backwards from higher to lower addresses
;
     Shift:                          ; Subroutine Shift
        LDI ZH, HIGH(Times+20)       ; Load MSB target address ZH with 0x0074
        LDI ZL, LOW(Times+20)        ; Load LSB target address ZL with 0x0074        		
        LDI XH, HIGH(Times+15)       ; Load MSB source address XH with 0x006F
        LDI XL, LOW(Times+15)        ; Load LSB source address XL with 0x006F
;
     Shift1:                         ;		
        LD R16, -X                   ; Auto-decrement and load rmp with source byte 
        ST -Z, R16                   ; Auto-decrement amd store rmp to target address
        CPI XL, LOW(Times)           ; Compare source XL with Times (0x0060)
        BRNE Shift1                  ; If not equal, Then repeat
        RET                          ; Back to caller address +1
;		
;
;    Display displays all three times on LCD
;
     Display:                        ; Subroutine Display
        LDI ZH, 1                    ; Load ZH with 1, start at line 2 of the LCD
        LDI ZL, 8                    ; Load ZL with 8, column position 9 of the LCD
        LDI XH, HIGH(Times+5)        ; Load XH with MSB SRAM address 0x0065
        LDI XL, LOW(Times+5)         ; Load XL with LSB
;
     Display1:                       ;    		  		
        RCALL LcdPos                 ; Call subroutine LcdPos for positioning
;
        PUSH ZH                      ; Push line to stack
        LD R16, X+                   ; Load SRAM byte from address and increment address
        RCALL Bin2Dec2               ; Convert and display two digit decimal
;
        LDI R16, ':'                 ; Load colon to rmp
        RCALL LcdChar                ; rmp to display
        LD R16, X+                   ; Load next byte from SRAM and increment address
        RCALL Bin2Dec2               ; Convert and display two digit decimal
;		        
        LDI R16, ':'                 ; Load colon to rmp
        RCALL LcdChar                ; rmp to display
        LD R16, X+                   ; Load next byte from SRAM and increment address
        RCALL Bin2Dec2               ; Convert and display two digit decimal
;
        LDI R16, '.'                 ; Load decimal dot to rmp
        RCALL LcdChar                ; rmp to display
        LD R16, X+                   ; Load next byte from SRAM and increment address
        RCALL Bin2Dec2               ; Convert and display two digit decimal
;
        LD rmp, X+                   ; Load next byte from SRAM and increment address
        SUBI rmp, -'0'               ; Convert to ASCII (add ASCII-0)
        RCALL LcdChar                ; rmp to display
;
        POP ZH                       ; Restore line from stack
        INC ZH                       ; Next line
;
        CPI ZH, 4                    ; Already at line 5?
        BRCS Display1                ; Repeat if not
;
        RET                          ; Back to caller address +1
;
;						        		   			 
;***************************************
;*   Include the 4 bit LCD routines    *
;***************************************
;
     .include "Lcd4Busy_mod.inc"
;	     		



Praise, error reports, scolding and spam please via the comment page to me.

To the top of that page

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