Path: Home => AVR-EN => Applications => Multitimer tn24 => Assembler code
Multitimer small AVR applications

A multitimer with ATtiny24
Assembler source code
Logo

Source code for the multitimer tn24

HTML formatted assembler code, the original source code in asm format can be downloaded here.

;
; ****************************************
; * Multitimer with ATtiny24 and 12 LEDs *
; * Version 1.0 August 2018              *
; * (C)2018 avr-asm-tutorial.net         *
; ****************************************
;
.nolist
.include "tn24def.inc"
.list
;
; **********************************
;        H A R D W A R E
; **********************************
;
; Device: ATtiny24, Package: 14-pin-PDIP_SOIC
;
;                ______
;             1 /      |14
;       +3V o--|VCC GND|--o GND
; Taste Dwn o--|PB0 PA0|--o Led An 0
;  Taste Go o--|PB1 PA1|--o Led An 1
;     RESET o--|RES PA2|--o Led An 2
;  Taste Up o--|PB2 PA3|--o Led An 3
;        NC o--|PA7 PA4|--o Led Cat 0
; Led Cat 2 o--|PA6 PA5|--o Led Cat 1
;              |_______|
;
; *************************************
;  H A R D W A R E   D E B U G G I N G
; *************************************
;
; All hardware debugging code starting
;   at 000000 and ending in an indefinite
;   loop 
;
; Debug the LEDs
;   LEDs are turned on from LED5 to LED420
;   First round: all in green
;   Second round: all in red
.equ Debug_Leds = 0 ; 1 = Debug, 0=Normal
;
.if Debug_Leds == 1
.equ cDelay=50000
  ldi R16,0 ; LEDs off
  clr R18
Debug_Led1:
  ldi ZH,High(2*LedTable)
  ldi ZL,Low(2*LedTable)
  mov R17,R16
  lsl R17
  add ZL,R17
  ldi R17,0
  adc ZH,R17
  lpm R17,Z+
  eor R17,R18
  out PORTA,R17
  lpm R17,Z
  out DDRA,R17
  ldi ZH,High(cDelay)
  ldi ZL,Low(cDelay)
Debug_Led2:
  sbiw ZL,1
  brne Debug_Led2
  inc R16
  cpi R16,13
  brcs Debug_Led1
  clr R16
  ldi R17,0x7F
  eor R18,R17
  rjmp Debug_Led1
  .endif
;
; Debug the switches
;   Checks the three switches
;     State of switch is displayed on
;       LED5 (Down), LED60 (Run/Stop) and LED240 (Up)
;     As long as pressed the 
.equ Debug_Switches = 0 ; 1=Debug, 0=Normal
;
.if Debug_Switches == 1
Debug_Sw:
  ldi R16,0x07
  out PORTB,R16
  in R17,PINB
  ldi R16,1
  sbrs R17,0
  rjmp Debug_Sw_Nmbr
  ldi R16,5
  sbrs R17,1
  rjmp Debug_Sw_Nmbr
  ldi R16,9
  sbrs R17,2
  rjmp Debug_Sw_Nmbr
  clr R16
Debug_Sw_Nmbr:
  ldi ZH,High(2*LedTable)
  ldi ZL,Low(2*LedTable)
  lsl R16
  add ZL,R16
  ldi R16,0
  adc ZH,R18
  lpm R16,Z+
  out PORTA,R16
  lpm R16,Z+
  out DDRA,R16
  clr R16
Debug_Sw_Nmbr1:
  dec R16
  brne Debug_Sw_Nmbr1
  rjmp Debug_Sw
  .endif
;
; **********************************
;   A D J U S T A B L E   C O N S T
; **********************************
;
.equ clock=1000000 ; Define clock frequency
;
; Led state at start-up
.equ cStart = 5 ; Can be between 1 and 12
;
; Debouncing counter, in 0.1 seconds
.equ cDebounce = 2 ; Number of periods
;
; Auto blank when inactive
;   in tenth of seconds
.equ cAutoOff = 100 ; Automatic off
;
; **********************************
;  F I X  &  D E R I V.  C O N S T
; **********************************
;
; TC1 produces 0.1 Hz signal
.equ cTc1Presc = 8 ; TC1 prescaler
.equ cTc1Div = clock / cTc1Presc / 10 ; TC1 CTC divider
.equ cTc1CmpA = cTc1Div-1 ;
;
; Key debouncing counter
.equ cTgl = cDebounce + 1
;
; **********************************
;       R E G I S T E R S
; **********************************
;
; Free: R0 to R9
.def rOff = R10 ; Switch LED off
.def rSelect = R11 ; Current led selected
.def rState = R12 ; Current led state
.def rPort = R13 ; Current Port
.def rDdr = R15 ; Current DDR
.def rmp = R16 ; Define multipurpose register
.def rFlag = R17 ; Flag register
  .equ bRun = 0 ; Down count running
  .equ bTimeOut = 1 ; End of LED display
  .equ bLedTest = 7 ; Led testing at startup
.def rCnt = R18 ; Count down 0.1 seconds
.def rTgl = R19 ; Debounce toggle register
; free: R20 to R23
.def rSecL = R24 ; Half seconds counter, LSB
.def rSecH = R25 ; dto., MSB
; used: R27:R26 = X for multiplication
; free: R29:R28 = Y
; used: R31:R30 = Z for diverse purposes
;
; **********************************
;         C O D E
; **********************************
;
.cseg
;
; **********************************
; R E S E T  &  I N T - V E C T O R S
; **********************************
  rjmp Main ; Reset vector
  reti ; EXT_INT0, unused
  reti ; PCI0, unused
  rjmp Pcint1Isr ; PCI1
  reti ; WATCHDOG, unused
  reti ; ICP1, unused
  rjmp Tc1CmpAIsr ; OC1A
  reti ; OC1B, unused
  reti ; OVF1, unused
  reti ; OC0A, unused
  reti ; OC0B, unused
  reti ; OVF0, unused
  reti ; ACI, unused
  reti ; ADCC, unused
  reti ; ERDY, unused
  reti ; USI_STR, unused
  reti ; USI_OVF, unused
;
; **********************************
;  I N T - S E R V I C E   R O U T .
; **********************************
;
; PCINT1 external int
;   executed on every ppin change of key inputs
;   identifies pressed key and starts respective
;     actions
Pcint1Isr:
  tst rTgl ; Check toggle counter
  brne PcInt1Isr9 ; Toggle period has not ended yet
  in rmp,PINB ; Read keys
  ori rmp,0b11111000 ; Set all unused pins
  cpi rmp,0xFF ; No key pressed
  breq Pcint1Isr9
  ldi ZL,cTgl
  mov rTgl,ZL
  sbrs rmp,1 ; Run/Stop key?
  rjmp KeyRun
  sbrc rFlag,bRun
  reti
  ldi rSecH,High(cAutoOff)
  ldi rSecL,Low(cAutoOff) 
  sbrs rmp,0 ; Down key?
  rjmp KeyDown
  sbrs rmp,2 ; Up key?
  rjmp KeyUp
PcInt1Isr9:
  reti
;
KeyDown:
  mov rmp,rSelect
  cpi rmp,1 ; Already at lowest end?
  breq KeyDown1 ; Yes, ignore pulse
  dec rSelect 
  mov rState,rSelect
  rcall SetLed
KeyDown1:
  reti
;
KeyUp:
  mov rmp,rSelect
  cpi rmp,12
  brcs KeyUp1
  ldi rmp,11
  mov rSelect,rmp
KeyUp1:
  inc rSelect
  mov rState,rSelect
  rcall SetLed
  reti
;
KeyRun:
  ldi rmp,1<<bRun ; Invert run flag
  eor rFlag,rmp
  sbrc rFlag,bRun ; Skip next if bRun is clear
  rjmp KeyStop
  ldi ZH,High(2*LedDur)
  ldi ZL,Low(2*LedDur)
  mov rmp,rSelect
  lsl rmp
  add ZL,rmp
  ldi rmp,0
  adc ZH,rmp
  lpm rSecL,Z+
  lpm rSecH,Z
  mov rState,rSelect
  ldi rmp,9
  mov rOff,rmp
  ldi rmp,10
  mov rCnt,rmp
  rcall SetLed
  reti
;
KeyStop:
  ldi rSecH,High(cAutoOff) ; Load automatic off counter
  ldi rSecL,Low(cAutoOff)
  mov rState,rSelect
  rcall SetLed
  reti

;
; TC1 Compare A int
;   executed any 0.1 seconds when counting
;   decreases count
;     when zero: switches off counting
;       and goes back to display selected time
;     when odd: switches current LED off
;     when not odd: switches current LED on
;     when smaller than lower limit:
;       decreases state and switches to lower
;         LED
Tc1CmpAIsr:
  sbrs rFlag,bLedTest ; Test LED?
  rjmp Tc1CmpAIsrRun
  inc rState ; Next Led
  ldi rmp,13 ; Last LED?
  cp rState,rmp
  brcs Tc1CmpAIsrLed ; No, display LED
  rjmp Tc1CmpAIsrStart
Tc1CmpAIsrRun:
  tst rTgl ; Check toggle register
  breq Tc1CmpAIsrRun1
  dec rTgl
Tc1CmpAIsrRun1:
  sbrs rFlag,bRun ; Counting active?
  rjmp Tc1CmpAIsrAuto ; No, to Auto off
  dec rCnt ; Count 0.1 s counter down
  breq Tc1CmpAIsrSec
  cp rCnt,rOff ; End of PWM cycle?
  brne Tc1CmpAIsrReti
  clr rmp ; Clear LED
  out DDRA,rmp
  reti
Tc1CmpAIsrSec:
  ldi rCnt,10 ; Restart 0.1 s counter
  sbiw rSecL,1 ; Count seconds down
  breq Tc1CmpAIsrStart ; End of count
  rcall Sec2Led
  rjmp Tc1CmpAIsrLed
Tc1CmpAIsrAuto:
  sbrc rFlag,bTimeOut ; Timed out?
  reti ; Yes
  sbiw rSecL,1 ; Auto off counter
  brne Tc1CmpAIsrBlink ; Not at zero
  clr rmp ; Switch LED off
  out DDRA,rmp
  sbr rFlag,1<<bTimeOut ; Set time out flag
  reti
Tc1CmpAIsrBlink:
  ldi rmp,0
  sbrs rSecL,0
  out DDRA,rmp
  sbrc rSecL,0
  out DDRA,rDdr
  reti
Tc1CmpAIsrStart:
  ; Switch to start
  cbr rFlag,(1<<bRun)|(1<<bLedtest) ; Switch run and bLedtest off
  mov rState,rSelect ; switch to selected state
  ldi rSecH,High(cAutoOff) ; Set auto off value
  ldi rSecL,Low(cAutoOff)
Tc1CmpAIsrLed:
  rcall SetLed
Tc1CmpAIsrReti:
  reti

; *******************************
;  I S R   S U B R O U T I N E S
; *******************************
;
;
; Calculate LED from second counter
;   Converts the seconds in rSecH:rSecL
;     to the LED to be driven in rState
;     and the pulse duration in rOff
Sec2Led:
  ; Get LED for seconds time
  ldi ZH,High(2*LedDur+2)
  ldi ZL,Low(2*LedDur+2)
  clr rState ; rState is LED # counter
Sec2Led1:
  inc rState ; Increase counter
  lpm XL,Z+ ; Read LSB from table to X
  lpm XH,Z+
  sec
  cpc rSecL,XL ; Compare LSB
  cpc rSecH,XH ; Compare MSB
  brcc Sec2Led1 ; Repeat
  ; Read lower limit to X
  sbiw ZL,4 ; Point to pre last entry in table
  lpm XL,Z+ ; Read LSB lower limit to X
  lpm XH,Z ; dto., MSB
  ; Difference time - lower count
  mov ZH,rSecH ; Copy time
  mov ZL,rSecL
  sub ZL,XL ; Subtract from time
  sbc ZH,XH
  mov XH,ZH ; Copy to X
  mov XL,ZL
  ; Get multiplicator for LED number
  ldi ZH,High(2*MultTab)
  ldi ZL,Low(2*MultTab)
  add ZL,rState
  ldi rmp,0
  adc ZH,rmp
  lpm rmp,Z
  ; Test multiplicator = 0
  tst rmp
  brne Sec2Led2 ; Not zero, multiply
  ; LED5 or LED10
  ldi ZH,High(2*TenTable)
  ldi ZL,Low(2*TenTable)
  add ZL,XL ; Add time LSB
  adc ZH,XH ; dto., MSB
  lpm rOff,Z ; Read PWM value from table
  ret
Sec2Led2:
  ; >LED10, multiplicator not zero, multiply
  clr ZL ; Z is result
  clr ZH
Sec2Led3:
  tst rmp ; Ready multiplying?
  breq Sec2Led5 ; Yes, end of multiplication
  lsr rmp ; Divide multiplicator by 2, lowest bit to carry
  brcc Sec2Led4 ; Carry clear, do not add to result
  add ZL,XL ; Add multiplicator
  adc ZH,XH
Sec2Led4:
  lsl XL ; Multiply by 2
  rol XH
  rjmp Sec2Led3 ; Go on multiplying
Sec2Led5:
  inc ZH ; Plus one
  mov rOff,ZH ; To off store
  ret
;
; Duration table
LedDur:
.dw 0,5,10,20,30
.dw 60,90,120,180
.dw 240,300,360,420
.dw 65535 ; End of table
;
; Multiplicator table
MultTab:
.db 0,0,0,230,230,77,77,77,38,38,38,38,38,1
;
; For the seconds from 10 to 1 it is easier to derive the
; tenth of seconds from a short table instead. The content
; of the table would be:
; Table with tenth of second duty cycle beween seconds
;   10 and 0
; Note: the higher the number the shorter the LED-on time
TenTable:
.db 0,2,4,6,8,9
;
; Switch to LED in rState
SetLed:
  mov rmp,rState
  lsl rmp ; Multiply by 2
  ldi ZH,High(2*LedTable) ; Point to led table
  ldi ZL,Low(2*LedTable)
  add ZL,rmp
  ldi rmp,0
  adc ZH,rmp
  lpm rPort,Z+
  lpm rDdr,Z
  ldi rmp,0x7F ; Invert to red?
  sbrc rFlag,bRun ; Skip next if not running
  eor rPort,rmp
  out PORTA,rPort
  out DDRA,rDdr
  cbr rFlag,1<<bTimeOut
  ret
;
; Led table of the ports
;   1. Byte: PORT, 2. Byte: DDR
LedTable:
.db 0b00000000,0b00000000 ; off
.db 0b00010000,0b00010001 ; LED 5 green
.db 0b00010000,0b00010010 ; LED 10 green
.db 0b00010000,0b00010100 ; LED 20 green
.db 0b00010000,0b00011000 ; LED 30 green
.db 0b00100000,0b00100001 ; LED 60 green
.db 0b00100000,0b00100010 ; LED 90 green
.db 0b00100000,0b00100100 ; LED 120 green
.db 0b00100000,0b00101000 ; LED 180 green
.db 0b01000000,0b01000001 ; LED 240 green
.db 0b01000000,0b01000010 ; LED 300 green
.db 0b01000000,0b01000100 ; LED 360 green
.db 0b01000000,0b01001000 ; LED 420 green
;
; **********************************
;  M A I N   P R O G R A M   I N I T
; **********************************
;
Main:
  ; Stack init
  .ifdef SPH
    ldi rmp,High(RAMEND) ; Set SPH
    out SPH,rmp
    .endif
  ldi rmp,Low(RAMEND)
  out SPL,rmp ; Init LSB stack pointer
  ; Clear LED
  clr rState
  rcall SetLed
  ; Set default start values
  ldi rmp,cStart
  mov rSelect,rmp ; Start with preselected value
  ldi rSecH,High(cAutoOff) ; Set auto off value
  ldi rSecL,Low(cAutoOff)
  ; Init PCINT for keys
  ldi rmp,(1<<PORTB0)|(1<<PORTB1)|(1<<PORTB2) ; Pull ups
  out PORTB,rmp ; on
  clr rmp ; Configure as inputs
  out DDRB,rmp
  ldi rmp,(1<<PCINT8)|(1<<PCINT9)|(1<<PCINT10) ; Mask key pins
  out PCMSK1,rmp
  ldi rmp,1<<PCIE1 ; Enable PCINT1
  out GIMSK,rmp
  ; Init TC1
  ldi rFlag,(1<<bLedTest)|(1<<bRun) ; Led test phase on
  ldi rmp,High(cTc1CmpA) ; Set compare value
  out OCR1AH,rmp ; MSB
  ldi rmp,Low(cTc1CmpA)
  out OCR1AL,rmp ; LSB
  clr rmp ; Mode port A
  out TCCR1A,rmp ; Control port TC1 A
  ldi rmp,(1<<WGM12)|(1<<CS11) ; CTC on compare A, presc=8
  out TCCR1B,rmp ; Control port TC1 B
  ldi rmp,1<<OCIE1A ; TC1 Compare A interrupt enable
  out TIMSK1,rmp ; in TC1 int mask
  ; Enable sleep
  ldi rmp,1<<SE ; Sleep mode idle
  out MCUCR,rmp
  ;
  ; Enable interrupts
  sei ; Enable interrupts
;
; **********************************
;    P R O G R A M   L O O P
; **********************************
;
Loop:
  sleep
  rjmp loop
;
; End of source code
;



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