Path: Home => AVR-EN => Applications => RGB BCD watch m16 => Assembler source code
RGB display 15:39:13 AVR applications

RGB BCD watch with ATmega16
Software for the RGB BCD watch
Logo

Assembler source code for the RGB BCD watch

The original source code in assembler format is available here.

;
; ***********************************
; * RGB BCD watch with ATmega16     *
; * Version 1.0 as of January 2019  *
; * (C)2019 by avr-asm-tutorial.net *
; ***********************************
;
.nolist
.include "m16adef.inc" ; Define device ATmega16A
.list
;
; *************************************
;  D E B U G G I N G   S W I T C H E S
; *************************************
;
; Accelerate watch by a factor
.equ cAccel = 1 ; Acceleration of clock, 1..1000
;    (should be 1 in final version)
;
.equ Yes = 1 ; Yes for all switches
.equ No = 0 ; No for all switches
;
; Light all LEDs in all colors one by one in the rows:
;   1. blue - green - red for delay seconds each
;   2. ones, twos, fours, eights, tens, twenties, fourties
;   3. seconds - minutes - hours
.equ dbg_leds = No ; Should be no in final version
.equ dbg_leds_delay = 10 ; On-Time in 50 ms multiples
;
; Debug the ADC: Set hours to MSB result
.equ dbg_adc = No ; Should be no in final version
;
; Debug the keys:
;   Leds dark if no key pressed
;   Set hour tens red on key 1
;   Set hour twenties on key 2
;   (Disables all normal operation!)
.equ dbg_key = No
;
; Debug the color balance:
;   Lights all three LEDs around and around
;   (Disables all normal operation!)
.equ dbg_colbal = No
;
; Monitoring of registers during input phase
.equ dbg_moni_flags = No ; Monitor the flags
.equ dbg_moni_bounce = No ; Monitor the bounce counter
;
;
; **********************************
;        H A R D W A R E
; **********************************
;
; Device: ATmega16A, Package: 40-pin-PDIP
;           __________
;        1 /          |40
;   AH o--|PB0     PA0|--o POT
;   AM o--|PB1     PA1|--o NC
;   AS o--|PB2     PA2|--o NC
;   K1 o--|PB3     PA3|--o C0B
;   K2 o--|PB4     PA4|--o C0G
; MOSI o--|PB5     PA5|--o C0R
; MISO o--|PB6     PA6|--o C1B
;  SCK o--|PB7     PA7|--o C1G
;  RES o--|RESET  AREF|--o AREF
;  +5V o--|VCC     GND|--o 0V
;   0V o--|GND    AVCC|--o AVCC
; 4MHz o--|XTAL2   PC7|--o C4B
; XTAL o--|XTAL1   PC6|--o C3R
;  C4G o--|PD0     PC5|--o C3G
;  C4R o--|PD1     PC4|--o C3B
;  C5B o--|PD2     PC3|--o C2R
;  C5G o--|PD3     PC2|--o C2G
;  C5R o--|PD4     PC1|--o C2B
;  C6B o--|PD5     PC0|--o C1R
;  C6G o--|PD6     PD7|--o C6R
;       20|___________|21
;
;
; **********************************
;  P O R T S   A N D   P I N S
; **********************************
;
.equ pRgbC1O = PORTA ; RGB1 Cathode Output port
.equ pRgbC1D = DDRA ; RGB1 Cathode Direction port
.equ bRgbC1M = 0xF8 ; RGB1 Cathode Output Mask
.equ pRgbC2O = PORTC ; RGB2 Cathode Output port
.equ pRgbC2D = DDRC ; RGB2 Cathode Direction port
.equ pRgbC2M = 0xFF ; RGB2 Cathode Output Mask
.equ pRgbC3O = PORTD ; RGB3 Cathode Output port
.equ pRgbC3D = DDRD ; RGB3 Cathode Direction port
.equ pRgbC3M = 0xFF ; RGB3 Cathode Output Mask
.equ pRgbAO = PORTB ; RGB Anode Output port
.equ pRgbAD = DDRB ; RGB Anode Direction port
.equ pRgbAM = 0x07 ; RGB Anode Mask
.equ pKeyI = PINB ; Key input port
.equ bKey1 = PINB3 ; First key
.equ bKey2 = PINB4 ; Second key
;
; ******************************************
;   A D J U S T A B L E   C O N S T A N T S
; ******************************************
;
.equ clock=4000000 ; Define clock frequency
;
; Constants for color composition
.equ bBlue = 0 ; Blue led bit
.equ bGreen = 1 ; Green led bit
.equ bRed = 2 ; Red led bit
;
; Colors for the four quarters
.equ cColor1 = 255-(1<<bBlue) ; Color quarter 1
.equ cColor2 = 255-(1<<bGreen) ; Color quarter 2
.equ cColor3 = 255-(1<<bRed) ; Color quarter 3
.equ cColor4 = 255-((1<<bRed)|(1<<bBlue)) ; Color quarter 4
; Error checking of colors
.if ((cColor1&0x07)==0)||((cColor2&0x07)==0)||((cColor3&0x07)==0)||((cColor4&0x07)==0)
  .error "One of the colors has three color bits at zero!"
  .endif
;
; Start time of watch: time to be set at start-up
.equ starthour = 20 ; Start at 20 hours
.equ startminute = 0 ; Start at zero minute
.equ startsecond = 0 ; Start at zero seconds
;
; When key input active: blink hour/minute/second
.equ BlinkPeriod = 85 ; 85% on, 15% off
;
; Key input bouncing parameter
.equ BouncePeriod = 50 ; Debouncing period for keys in ms
;
; Input active time-out
.equ InputTimeOutMinutes = 5 ; Time in minutes to skip input period
.if InputTimeOutMinutes > 7
  .error "Input time-out minutes too large!"
  .endif
;
; ****************************************************
;  F I X E D  &  D E R I V I V E D  C O N S T A N T S
; ****************************************************
;
; Clock signals for second increase and MUX
;   Clock > Presc > CTC > SecDiv > Seconds
;   4 MHz    1     16000     250
.equ cTc1Presc = 1 ; TC1 Prescaler
.equ cTc1Clk = clock / cTc1Presc ; Frequency TC1
.equ cTc1CompA = 15999 ; Int for Mux
.equ cSecDiv = cTc1Clk/(cTc1CompA+1) ; TC0 Second divider
;
; Switch leds off after blink period
.equ cBlinkOff = ((100-BlinkPeriod)*cSecDiv+50) / 100 ; Second divider
;
; ADC constants
.equ cAdcCnt = 64 ; Sum up 64 ADC results
.equ cAdcPs = 32 ; ADC prescaler
.equ cAdcN = 13 ; Number of cycles
.equ cAdcTime = (cAdcCnt*cAdcPs*cAdcN*1000)/clock ; Time in ms
.equ cInpTO = 65536-(clock/cAdcCnt/cAdcPs/cAdcN)*60*InputTimeOutMinutes
.if cInpTO < 0
  .error "Input time-out too long!"
  .endif
.equ cBounce = (BouncePeriod+cAdcTime/2)/cAdcTime+1 ; Bounce counter
;
; **********************************
;       R E G I S T E R S
; **********************************
;
; used: R2:R1:R0 for binary conversion to color
.def rInpToL = R3 ; Time out input, LSB
.def rInpToH = R4 ; dto., MSB
.def rHrInp = R5 ; Input hour
  .equ sInput = 5
.def rMinInp = R6 ; Input minute
.def rSecInp = R7 ; Input second
.def rCmp = R8 ; Compare value for conversion
.def rConvCnt = R9 ; Counter for time conversion
.def rAdcRes = R10 ; MSB of ADC sum
.def rAdcL = R11 ; ADC sum, LSB
.def rAdcH = R12 ; dto., MSB
.def rAdcCnt = R13 ; ADC counter
.def rSecDiv = R14 ; Seconds divider
.def rSreg = R15 ; Save/Restore status port
.def rmp = R16 ; Define multipurpose register
.def rimp = R17 ; Multipurpose inside ints
;  Flags for flow control
.def rFlag = R18 ; Flag register
  ; Those four bits control key input
  ;   OSM_ = xxx0 = Normal operation, no time setting, no monitoring
  ;        = 0001 = Time setting hours, monitoring = Led one green
  ;        = 0011 = dto., minutes, monitoring = Led two blue
  ;        = 0111 = dto., seconds, monitoring = Led four green
  ;        = 1xx1 = dto., end time setting, monitoring = Leds eight/ten/twenty
  .equ bSetA = 0 ; Key flag, for hour setting
  .equ bSetM = 1 ; Flag for minute setting
  .equ bSetS = 2 ; Flag for second setting
  .equ bSetO = 3 ; Flag for end of time setting
  ; bBlink set when bSetA=1 and rSecDiv cycle reaches switching value
  .equ bBlink = 4 ; Switch active leds off
  ; bNoUpd set when bBlink is set, cleared at second start
  .equ bNoUpd = 5 ; Do not update during off-period
  ; Set by TC1A-ISR when a second is over
  .equ bSec = 6 ; Flag second over
  ; Set by ADC-Complete ISR when 64 measurements completed
  .equ bAdc = 7 ; ADC flag
.def rHr = R19 ; Time hours
  .equ sTime = 19 ; Pointer to time info for displaying time
.def rMin = R20 ; Time minutes
.def rSec = R21 ; Time seconds
.def rCol = R22 ; Color
.def rColCop = R23 ; Copy of rCol
.def rBcd = R24 ; BCD coded time
.def rBounce = R25 ; Debouncing counter for key inputs
; used: R27:R26 = X for conversion to cathode bits outside ints
; used: R29:R28 = Y for pointer for multiplexing cycle
; used: R31:R30 = Z for diverse purposes outside ints
;
; **********************************
;           S R A M
; **********************************
;
.dseg
.org SRAM_START
;
; MUX buffer
sRgb:
.byte 4 ; Four bytes: RGBC1, RGBC2, RGBC3,AGBA, Hours
.byte 4 ; Four bytes: RGBC1, RGBC2, RGBC3,AGBA, Minutes
.byte 4 ; Four bytes: RGBC1, RGBC2, RGBC3,AGBA, Seconds
sRgbEnd:
;
; **********************************
;           C O D E
; **********************************
;
.cseg
.org 000000
;
; *************************************
;  R E S E T  &  I N T - V E C T O R S
; *************************************
	rjmp Main ; Reset vector
	nop
	reti ; INT0, unused
	nop
	reti ; INT1, unused
	nop
	reti ; OC2, unused
	nop
	reti ; OVF2, unused
	nop
	reti ; ICP1, unused
	nop
	rjmp Oc1AIsr ; OC1A, MUX and second counting
	nop
	rjmp Oc1BIsr ; OC1B, anode driver off for dimming
	nop
	reti ; OVF1, unused
	nop
	reti ; OVF0, unused
	nop
	reti ; SPI, unused
	nop
	reti ; URXC, unused
	nop
	reti ; UDRE, unused
	nop
	reti ; UTXC, unused
	nop
	rjmp AdccIsr ; ADCC, ADC cycle
	nop
	reti ; ERDY, unused
	nop
	reti ; ACI, unused
	nop
	reti ; TWI, unused
	nop
	reti ; INT2, unused
	nop
	reti ; OC0, unused
	nop
	reti ; SPMR, unused
	nop
;
; *****************************************
;  I N T - S E R V I C E   R O U T I N E S
; *****************************************
;
; TC1 Compare Match A Interrupt Service Routine
Oc1AIsr:
  in rSreg,SREG ; Save SREG
  dec rSecDiv ; Decrease second divider
  brne Oc1AIsr1 ; Not zero, perform MUX
  ldi rimp,cSecDiv ; Restart seconds divider
  mov rSecDiv,rimp
  sbr rFlag,1<<bSec ; Set seconds flag
  cbr rFlag,1<<bNoUpd ; Updates on
Oc1AIsr1:
  ldi rimp,0x1F ; Clear anode drivers
  out pRgbAO,rimp
  ld rimp,Y+ ; Read first cathodes
  out pRgbC1O,rimp ; Write first RGB port
  ld rimp,Y+ ; Read second cathodes
  out pRgbC2O,rimp ; Write second RGB port
  ld rimp,Y+ ; Read third cathodes
  out pRgbC3O,rimp ; Write third RGB port
  ld rimp,Y+ ; Read anodes
  out pRgbAO,rimp ; Write anode
  cpi YL,sRgbEnd ; End of buffer?
  brne Oc1AIsr2 ; No
  ldi YH,High(sRgb) ; Restart Mux
  ldi YL,Low(sRgb)
Oc1AIsr2:
  sbrs rFlag,bSetA ; Key input active?
  rjmp Oc1AIsr3 ; No
  ldi rimp,cBlinkOff ; Seconds divider reached off?
  cp rimp,rSecDiv ; Blink cycle reached
  brne Oc1AIsr3 ; No, skip flag setting
  sbr rFlag,(1<<bBlink)|(1<<bNoUpd) ; Set blink flag to turn input off
Oc1AIsr3:
  out SREG,rSreg ; Restore SREG
  reti
;
; OC1B Interrupt Service Routine
;   switches anode driver off for dimming
Oc1BIsr:
  ldi rimp,0b00011111 ; Anode driver bits to one
  out pRgbAO,rimp ; in anode driver port
  reti
;
; ADC Conversion Complete Interrupt Service Routine
;   sums up 64 results, if complete: copy MSB, clear sum, set flag
;   restart ADC conversion
AdccIsr:
  in rSreg,SREG ; Save SREG
  in rimp,ADCL ; Read ADC result LSB
  add rAdcL,rimp ; add to sum LSB
  in rimp,ADCH ; Read MSB
  adc rAdcH,rimp ; add to MSB
  dec rAdcCnt ; Decrease counter
  brne AdccIsr1 ; Not zero, restart ADC
  mov rAdcRes,rAdcH ; Copy MSB result
  clr rAdcL ; Restart sum LSB
  clr rAdcH ; dto., MSB
  ldi rimp,cAdcCnt ; Restart counter
  mov rAdcCnt,rimp ; in counter register
  sbr rFlag,1<<bAdc ; Set ADC flag
AdccIsr1:
  ; Restart ADC
  ldi rimp,(1<<ADEN)|(1<<ADSC)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS0)
  out ADCSRA,rimp ; in ADC control port
  out SREG,rSreg ; Restore SREG
  reti
;
; **********************************
;  M A I N   P R O G R A M   I N I T
; **********************************
;
Main:
	ldi rmp,High(RAMEND) ; End of SRAM, MSB
	out SPH,rmp ; Init MSB stack pointer
	ldi rmp,Low(RAMEND) ; End of SRAM, LSB
	out SPL,rmp ; Init LSB stack pointer
  ; Init SRAM MUX buffer
  ldi XH,High(sRgb) ; Point to SRAM buffer, MSB
  ldi XL,Low(sRgb) ; dto., LSB
Main1:
  ldi rmp,0b11111000 ; First buffer byte
  st X+,rmp ; Cathodes 1 off
  ldi rmp,0xFF ; all others FF
  st X+,rmp ; Cathodes 2 off
  st X+,rmp ; Cathodes 3 off
  adiw XL,1 ; Jump over anode byte
  cpi XL,Low(sRgbEnd) ; End of buffer?
  brne Main1 ; No, go on
  ldi rmp,0b00011110 ; Anode driver hours
  sts sRgb+3,rmp ; Hour anode active
  ldi rmp,0b00011101 ; Anode driver miinutes
  sts sRgb+7,rmp ; Minute anode active
  ldi rmp,0b00011011 ; Anode driver seconds
  sts sRgb+11,rmp ; Second anode active
  ; Init the I/O ports
  ldi rmp,pRgbAM|(1<<bKey1)|(1<<bKey2) ; RGB Anode Mask plus key pull-ups
  out pRgbAO,rmp ; RGB Anode Output port
  ldi rmp,pRgbAM ; RGB Anode Mask
  out pRgbAD,rmp ; RGB Anode Direction port
  ldi rmp,bRgbC1M ; RGB1 Cathode Output Mask
  out pRgbC1O,rmp ; RGB1 Cathode Output port
  out pRgbC1D,rmp ; RGB1 Cathode Direction port
  ldi rmp,pRgbC2M ; RGB2 Cathode Output Mask
  out pRgbC2O,rmp ; RGB2 Cathode Output port
  out pRgbC2D,rmp ; RGB2 Cathode Direction port
  ldi rmp,pRgbC3M ; RGB3 Cathode Output Mask
  out pRgbC3O,rmp ; RGB3 Cathode Output port
  out pRgbC3D,rmp ; RGB3 Cathode Direction port
;
; *************************************
;  H A R D W A R E   D E B U G G I N G
; *************************************
;
; Routines for hardware debugging
;   Those do not perform normal counting but
;   end in indefinite loops!
.if dbg_key == Yes
  ; Debug the attached keys
  ;   Set hour tens red if key 1 pressed
  ;   Set hour twenties red if key 2 pressed
	  ldi rmp,0b00011110 ; Anode hours on
	  out pRgbAO,rmp 
	dbg_key_loop:
	  sbic pKeyI,bKey1 ; Skip if key 1 low
	  sbi pRgbC3O,PORTD1 ; Red LED off
	  sbis pKeyI,bKey1 ; Skip if key 1 high
	  cbi pRgbC3O,PORTD1 ; Red LED on
	  sbic pKeyI,bKey2 ; Skip if key 2 low
	  sbi pRgbC3O,PORTD4 ; Red LED off
	  sbis pKeyI,bKey2 ; Skip if key 2 high
	  cbi pRgbC3O,PORTD4 ; Red Led on
	  rjmp dbg_key_loop ; Repeat on and on again
  .endif
.if dbg_colbal == Yes
  ; Debug the color balance of all leds
  ;   Switch all leds to white one by one
  ;   and fastly change to the next led
  ;   Colors should be clean white, but
  ;   only 1/20th brightness
  ColBal_loop:
    ldi rMin,0xFF ; Start with upper leds off
    ldi rHr,0xFF
  ColBal_loop1:
    ldi rBcd,0b00011011 ; Start anodes with seconds
  ColBal_loop2:
    ldi rSec,0b11000000 ; Start with seconds ones
    ldi rmp,20 ; 20 leds to cycle
  ColBal_loop3:
    out pRgbC1O,rSec ; Set cathodes, seconds
    out pRgbC2O,rMin ; dto., minutes
    out pRgbC3O,rHr ; dto., hours
    out pRgbAO,rBcd ; Set anode driver
    ori rSec,0b00000111 ; Set all three lower bits
    lsl rSec ; Shift to next led, seconds
    rol rMin ; dto., minutes
    rol rHr ; dto., hours
    lsl rSec ; and to overnext led
    rol rMin
    rol rHr
    lsl rSec ; and to third led
    rol rMin
    rol rHr
    dec rmp
    brne ColBal_loop3 ; Continue led out and shift
    ori rBcd,0b00100000 ; Set next key to one
    lsr rBcd ; Next anode
    brcs ColBal_loop2 ; Not at the end
    rjmp ColBal_loop1 ; Restart all new
  .endif
.if dbg_leds == Yes
  ; Debug the leds: Set the leds
  ;   a) blue-green-red,
  ;   b) ones, twos, fours, eights, tens, twentys, fourtys
  ;   c) seconds, minutes, hours 
  Led_loop1:
    ldi rMin,0xFF ; Upper leds off
    ldi rHr,0xFF
    ldi rBcd,0b00011011 ; Start anodes seconds
  Led_loop2:
    ldi rSec,0b11110000 ; Start with seconds ones
    ldi rmp,21 ; 21 leds to cycle
    sbrs rBcd,0 ; Hour cycle?
    ldi rmp,18 ; 18 leds only
  Led_loop3:
    out pRgbC1O,rSec ; Set cathodes, second
    out pRgbC2O,rMin ; dto., minute
    out pRgbC3O,rHr ; dto., hour
    out pRgbAO,rBcd ; Set anode driver
    ldi rCol,dbg_leds_delay ; Multiple 50 ms delay
  Led_loop4:
    ldi ZH,High((5*clock)/400) ; Delay loop 50 ms, MSB
    ldi ZL,Low((5*clock)/400) ; dto., LSB
  Led_loop5:
    sbiw ZL,1 ; Count down, 2 clock cycles
    brne Led_loop5 ; 2 clock cycles for branching
    ; 4 clock cycles = 1 us @4 MHz
    ; 50.000 us per loop
    dec rCol ; Count delay down
    brne Led_loop4 ; Next delay cycle
    ori rSec,0b00000100 ; Set blue cathode led 0 off
    lsl rSec ; Next cathode, second
    rol rMin ; Next cathode, minute
    rol rHr ; Next cathode, hour
    dec rmp ; Downcount led
    brne Led_loop3 ; Next led output
    lsr rBcd ; Next anode
    brcs Led_loop2 ; Restart with first led
    rjmp Led_loop1 ; Restart at the beginning
  .endif
; *****************************************
;  C O N T I N U E   N O R M A L   I N I T
; *****************************************
  ; Init the start time of the watch
  ldi rHr,starthour
  ldi rMin,startminute
  ldi rSec,startsecond
  ; Init the ADC
  ldi rmp,1<<REFS0 ; VREF=AVCC, ADC channel 0
  out ADMUX,rmp ; to MUX port
  clr rAdcL ; Restart sum LSB
  clr rAdcH ; dto., MSB
  ldi rmp,cAdcCnt ; Restart counter
  mov rAdcCnt,rmp
  ; Start the ADC: Int Enable, clock prescaler = 32
  ldi rmp,(1<<ADEN)|(1<<ADSC)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS0)
  out ADCSRA,rmp ; in ADC control port
  ; Init TC1 as MUX/second timer/anode driver off
  ldi rmp,High(cTC1CompA/cAccel) ; CTC compare value, MSB
  out OCR1AH,rmp ; to compare port MSB
  ldi rmp,Low(cTC1CompA/cAccel) ; dto., LSB
  out OCR1AL,rmp ; dto., LSB
  clr rmp ; Switch LEDs to lowest brightness
  out OCR1BH,rmp ; MSB
  out OCR1BL,rmp ; LSB
  ldi rmp,0 ; WGM10 and WGM11 to 0
  out TCCR1A,rmp
  ldi rmp,(1<<WGM12)|(1<<CS10) ; CTC on compare A, Prescaler=1
  out TCCR1B,rmp ; Start TC1
  ldi rmp,(1<<OCIE1A)|(1<<OCIE1B) ; Enable compare match interrupts
  out TIMSK,rmp ; in timer interrupt mask
  ; Init flags
  clr rFlag ; All flags off
  ; Init sleep
  ldi rmp,1<<SE ; Enable sleep idle
  out MCUCR,rmp ; in master control port
	sei ; Enable interrupts
;
; **********************************
;    P R O G R A M   L O O P
; **********************************
;
Loop:
  sleep ; Go to sleep
  nop ; Dummy for wake-up
  sbrc rFlag,bSec ; Second flag set?
  rcall Second ; Perform second increase
  sbrc rFlag,bAdc ; ADC conversion sum complete?
  rcall AdcRdy ; Perform ADC service
  sbrc rFlag,bBlink ; Off-period reached?
  rcall Blink ; Switch leds off
  rjmp Loop
;
; *********************************************
;  T I M E   I N C R E A S E  &  D I S P L A Y
; *********************************************
;
Second:
  cbr rFlag,1<<bSec ; clear flag
  ; Increase time by one second
  inc rSec ; Increase seconds
  cpi rSec,60 ; End of minute?
  brcs DisplayTimeInp ; No, output time
  clr rSec ; Restart second
  inc rMin ; Next minute
  cpi rMin,60 ; End of hour?
  brcs DisplayTimeInp ; No, output time
  clr rMin ; Restart minute
  inc rHr ; Next hour
  cpi rHr,24 ; End of day?
  brcs DisplayTimeInp ; No, output time
  clr rHr ; Restart hour
DisplayTimeInp:
  ; Display current time or the input time?
  ldi ZH,High(sTime) ; Point to time hours
  ldi ZL,Low(sTime)
  ; Output input time instead?
  sbrs rFlag,bSetA ; Key input active?
  rjmp Convert ; No, display current time
DisplayInput:
  ldi ZH,High(sInput) ; Output input time, MSB
  ldi ZL,Low(sInput) ; dto., LSB
;
; Convert time/input to display
Convert:
  ldi XH,High(sRgb) ; Point X to SRAM buffer, MSB
  ldi XL,Low(sRgb) ; dto., LSB
  ldi rmp,3 ; Three bytes to convert
  mov rConvCnt,rmp ; Set convert counter
  ldi rmp,24/4 ; Comparer for hours
  mov rCmp,rmp ; To compare register
ReadDateTime:
  ld rmp,Z ; Read the next data byte time/input
  ldi rCol,cColor1 ; Set color for quarter 1
  cp rmp,rCmp ; Compare with first quarter value
  brcs ToBcd ; Smaller, convert to BCD
  ldi rCol,cColor2 ; Set color for quarter 2
  lsl rCmp ; Compare value * 2
  cp rmp,rCmp ; Compare with second quarter value
  brcs ToBcd ; Smaller, convert to BCD
  ldi rCol,cColor3 ; Set color for quarter 3
  mov R0,rCmp ; Copy compare value
  lsr R0 ; Divide by two
  add rCmp,R0 ; Add to compare value, 3/4
  cp rmp,rCmp ; Compare with third quarter value
  brcs ToBcd ; Smaller, convert to BCD
  ldi rCol,cColor4 ; Set color for quarter 4
ToBcd:
  ldi rmp,60/4 ; Set compare value for minutes/seconds to 15
  mov rCmp,rmp ; in compare register
  ; Read and convert value to BCD in rBcd
  ld rmp,Z+ ; Read time/input byte again, point to next
  ldi rBcd,-0x10 ; Tens to minus 1
ToBcd1:
  subi rBcd,-0x10 ; Add tens
  subi rmp,10 ; Subtract 10 from value
  brcc ToBcd1 ; No carry, continue adding tens and subtracting
  subi rmp,-10 ; Add ten to restore last subtraction
  or rBcd,rmp ; Add the ones to the tens
ToRgb:
  ldi rmp,7 ; Seven times three colors
  clr R0 ; Clear buffer, byte 1
  clr R1 ; dto., byte 2
  clr R2 ; dto., byte 3
ToRgb1:
  ldi rColCop,0x07 ; Color all ones = leds of
  lsr rBcd ; Shift next BCD bit to carry
  brcc ToRgb2 ; Bit is zero, led off
  mov rColCop,rCol ; Bit is one, led to color
ToRgb2:
  ; Shift the three color bits into the buffer
  lsr rColCop ; Shift first color bit to carry
  ror R2 ; and into buffer from high to low
  ror R1
  ror R0
  lsr rColCop ; Shift second color bit to carry
  ror R2 ; and into buffer from high to low
  ror R1
  ror R0
  lsr rColCop ; Shift third color bit to carry
  ror R2 ; and into buffer from high to low
  ror R1
  ror R0
  dec rmp ; Count led down
  brne ToRgb1 ; Next led
  st X+,R0 ; Store buffer in SRAM for mux display, byte 1
  st X+,R1 ; dto., byte 2
  st X+,R2 ; dto., byte 3
  adiw XL,1 ; jump over anode byte
  dec rConvCnt ; Decrease counter
  brne ReadDateTime ; Read and convert next byte
  ret ; Conversion done
;
; ****************************************
;  A D C   C Y C L E   C O M P L E T E D
; ****************************************
;
; Perform reaction on ADC sum complete
AdcRdy:
  cbr rFlag,1<<bAdc ; Clear the ADC flag
  .if dbg_adc == Yes
    ; Display ADC result as 0..23 on hour position
    ldi rmp,24 ; Convert to hour
	  rcall Multiply
	  mov rHr,ZH ; Write to hour
	  ldi ZH,High(sTime) ; Point Z to actual time, MSB
	  ldi ZL,Low(sTime) ; dto., LSB
	  rcall Convert ; Display the current time
	  .endif
  sbrc rFlag,bSetA ; No time setting active?
  rjmp AdcDebounce ; No, check debounce
  ; Input inactive, set dim value and check key 1 pressed
  ldi rmp,250 ; Multiply ADC MSB by 250
  rcall Multiply
  lsr ZH ; Divide by 2, MSB
  ror ZL ; dto., LSB
  lsr ZH ; Divide by 4, MSB
  ror ZL ; dto., LSB
  out OCR1BH,ZH ; Set compare B port in TC1, MSB
  out OCR1BL,ZL ; dto., LSB
  sbic pKeyI,bKey1 ; Key 1 pressed?
  rjmp AdcRdyRet ; No
  sbr rFlag,1<<bSetA ; Set Set flag
  cbr rFlag,(1<<bSetS)|(1<<bSetM)|(1<<bSetO) ; Clear the other flags
  mov rSecInp,rSec ; Copy seconds
  mov rMinInp,rMin ; Copy minutes
  rjmp AdcSetHour ; Set hour to ADC MSB result and debounce
AdcDebounce:
  tst rBounce ; Bouncing period ended?
  breq AdcOFlag ; Yes, check O flag set
  sbis pKeyI,bKey1 ; Key 1 active?
  rjmp AdcRestartBounce ; Yes, restart bounce counter
  sbis pKeyI,bKey2 ; Key 2 active?
  rjmp AdcRestartBounce ; Yes, restart bounce counter
  dec rBounce ; Decrease debounce counter
  rjmp AdcRdyRet ; Done
AdcOFlag:
  sbrs rFlag,bSetO ; O flag set?
  rjmp AdcKey1 ; No, check key 1 pressed
  cbr rFlag,1<<bSetA ; Clear time set flag
  rjmp DisplayTimeInp
AdcKey1:
  sbic pKeyI,bKey1 ; Key 1 pressed?
  rjmp AdcKey2 ; No, check key 2
  sbrs rFlag,bSetS ; S flag set?
  rjmp AdcKey1M ; No, check M flag
  cbr rFlag,1<<bSetS ; Clear S flag
  ldi rmp,60 ; Multiply by 60
  rcall Multiply
  mov rMinInp,ZH ; Set minutes
  mov rSecInp,ZH ; and set seconds
  rjmp AdcRestartBounce
AdcKey1M:
  sbrs rFlag,bSetM ; M flag set?
  rjmp AdcSetO ; No, set O flag, leave input mode
  cbr rFlag,1<<bSetM ; Clear M flag
  ldi rmp,60 ; Multiply by 60
  rcall Multiply
  mov rMinInp,ZH ; Set minutes input
  rjmp AdcSetHour ; and set hour
AdcKey2:
  sbic pKeyI,bKey2 ; Key 2 pressed?
  rjmp AdcTimeOut ; No, check input change
  sbrs rFlag,bSetS ; S flag set?
  rjmp AdcKey2M ; No, check M flag
  ldi rmp,60 ; Multiply by 60
  rcall Multiply
  ldi rmp,cSecDiv ; Restart second divider for a full second
  mov rSecDiv,rmp ; Seconds divider to restart = restart second
  cbr rFlag,1<<bSec ; Clear second flag if currently set
  mov rSec,ZH ; Set seconds
  mov rMin,rMinInp ; Copy input minutes to time minutes
  mov rHr,rHrInp ; Copy input hours to time hours
  rjmp AdcSetO ; Set O flag to leave input mode
AdcKey2M:
  sbrs rFlag,bSetM ; M flag set?
  rjmp AdcKey2H ; No, check hour flag
  sbr rFlag,1<<bSetS ; Set S flag
  ldi rmp,60 ; Multiply ADC MSB by 60
  rcall Multiply
  mov rMinInp,ZH ; Set minute
  mov rSecInp,ZH ; Set second
  rjmp AdcRestartBounce ; Restart bounce counter
AdcKey2H:
  sbr rFlag,1<<bSetM ; Set minute flag
  cbr rFlag,1<<bSetS ; Clear second flag
  ldi rmp,60 ; Multiply ADC MSB by 60
  rcall Multiply
  mov rMinInp,ZH ; Set minute
  rjmp AdcSetHour ; and hour
AdcTimeOut:
  inc rInpToL ; Check time-out, LSB
  brne AdcNoKey ; Not zero
  inc rInpToH ; dto., inc MSB
  brne AdcNoKey ; Not zero
  ; Time-out input period
  cbr rFlag,1<<bSetA ; Clear TSet flag
  rjmp DisplayTimeInp ; Display time
AdcNoKey:
  sbrc rFlag,bNoUpd ; No uddate flag set?
  rjmp AdcRdyRet ; Yes, do not update
  ldi XH,High(sInput) ; Point X to hour input, MSB
  ldi XL,Low(sInput) ; dto., LSB
  ldi rmp,24 ; Convert MSB ADC to hours
  rcall Multiply
  sbrs rFlag,bSetM ; M flag set?
  rjmp AdcNoKey1 ; No, check change
  adiw XL,1 ; Next position
  ldi rmp,60 ; Convert MSB ADC to minutes/seconds
  rcall Multiply
  sbrc rFlag,bSetS ; S flag clear?
  adiw XL,1 ; No, increase address
AdcNoKey1:
  ld rmp,X ; Read current value at position
  cp ZH,rmp ; Compare with new value
  breq AdcRdyRet ; Not changed, do nothing
  st X,ZH ; Store changed value
  rjmp DisplayInput
AdcSetHour:
  ldi rmp,24 ; Multiply MSB ADC by 24
  rcall Multiply
  mov rHrInp,ZH
  rjmp AdcRestartBounce
AdcSetO:
  sbr rFlag,1<<bSetO ; Set O flag
AdcRestartBounce:
  ldi rmp,High(cInpTO)
  mov rInpToH,rmp
  ldi rmp,Low(cInpTO)
  mov rInpToL,rmp
  ldi rBounce,cBounce
AdcRdyRet:
.if dbg_moni_flags == Yes
  mov rSecInp,rFlag ; Copy rFlag to seconds input
  rcall DisplayTimeInp ; and display
  .endif
.if dbg_moni_bounce == Yes
  mov rSecInp,rBounce ; Copy bounce to seconds input
  rcall DisplayTimeInp
  .endif
  ret
;
; Multiply MSB of last ADC result by rmp
;   MSB Result in ZH
;   rmp is multiplicator
Multiply:
  mov R0,rAdcRes ; Copy MSB ADC result to R0
  clr R1 ; Clear MSB multiplier
  clr ZL ; Clear multiplication result in Z, LSB
  clr ZH ; dto., MSB
Multiply1:
  tst rmp ; Ready multiplying?
  breq Multiply3 ; Yes
  lsr rmp ; Next bit
  brcc Multiply2 ; Not one, skip adding
  add ZL,R0 ; Add multiplicator, LSB
  adc ZH,R1 ; dto., MSB
 Multiply2:
  lsl R0 ; Multiplicator * 2, LSB
  rol R1 ; dto., MSB
  rjmp Multiply1 ; Continue multiplication
Multiply3:
  ret
;
; Blink active input leds off
Blink:
  cbr rFlag,1<<bBlink ; Clear flag
  sbrs rFlag,bSetS ; Seconds active?
  rjmp Blink1
  clr rSecInp ; Seconds to zero
  rjmp DisplayTimeInp ; and display
Blink1:
  sbrs rFlag,bSetM ; Minutes active?
  rjmp Blink2
  clr rMinInp ; Minutes to zero
  rjmp DisplayTimeInp
Blink2:
  clr rHrInp ; Hours to zero
  rjmp DisplayTimeInp
;
; End of source code
;



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

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