![]() |
AVR Applications Stopwatch with ATtiny24 Assembler source code |
![]() |
;
; ******************************************
; 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"
;