ir_herz/v2.2/firmware/akl-mini.asm
Stephan Messlinger 4e119bde29 bla
2023-12-11 22:19:12 +01:00

665 lines
14 KiB
NASM

;AntiKippenLights-Mini (AKL-Mini) - 18 animated LEDs on a microcontroller
;ATtiny24 @ 1 MHz (internal R/C oscillator), BOD disabled to save power
;Fuses: lfuse=0x62, hfuse=0xDF, efuse=0xFF (factory defaults)
;
;Based on "KippenLights special edition with PIC16C54" version 0.2, 2005-01-01
;by Stefan Schuermans <1stein@schuermans.info>
;GNU public license - http://www.gnu.org/copyleft/gpl.html
;
;To disable auto-power-off function, bridge PA0 to ground (J1). This
;configuration is read at power-on (by applying power or pressing the button).
;
;To enable always-on mode, bridge PA0 to PA2 (J2). This configuration is only
;read once after applying power.
;
;PORTS
; PA0 = No off-timer detect (connect to GND) - disables auto-power-off,
; Always on detect (connect to PA2) - disables auto-power-off & button
; PA1 = push-button input (internal pull-up)
; PA2-7 = LED anodes 1~6
; PB0-2 = LED group cathodes A~C (A: LEDs 1~6, B: LEDs 7~12, C: LEDs 13~18)
; PB3 = [Reset]
;
;LED ARRANGEMENT
; 15
; 12 08 16
; 13 11 09 07 11 12 13 14 17
; 14 10 06 18
; 15 05 10 01
; 16 04 02
; 17 03 09 08 07 06 03
; 18 02 04
; 01 05
;
; (for the heart arrangement, directions of clockwise
; and counter-clockwise animations are reversed)
;
;VERSIONS
; 2017-11-09 - Version 1.0.0 - Arne Rossius
; * complete re-write
; 2019-02-27 - Version 2.0.0 - Arne Rossius
; * changed from ATtiny2313 to ATtiny24
; * corrected conflicting use of 'value' register
;button hold-down delay before power-on (1 ms units)
.equ POWER_ON_DELAY = 50 ;50 ms
;auto-power-off timeout (~8.4 kHz units)
.equ AUTO_POWER_OFF = 8400 * 60 * 60 * 2 ;approx. 2 hours
;.equ AUTO_POWER_OFF = 8400 * 5 ;approx. 5 seconds
;===============================================================================
.include "../include/tn24def.inc"
.undef XL
.undef XH
.undef YH
.undef ZL
.undef ZH
.def temp = R16
.def count = R17
.def repeat = R18
.def value = R19
.def leds = R20
.def pwm = R21
.def button_cnt = R22
.def timeout1 = R23
.def timeout2 = R24
.def timeout3 = R25
.def timeout4 = R26
.def flags = R27
.equ fAlwaysOn = 0
.equ fNoAutoOff = 1
;===============================================================================
.dseg
RAM_Frame: .byte 18 ;one frame of LED data (brightness values 0~63)
;===============================================================================
.cseg
.org 0x000
rjmp reset
.org PCI0addr
;pin change interrupt: button pressed
reti
;===============================================================================
reset:
;init ports
ldi temp, 0xFC ;PORTA
out DDRA, temp
ldi temp, 0x03
out PORTA, temp
ldi temp, 0x07 ;PORTB
out DDRB, temp
ldi temp, 0x0F
out PORTB, temp
;disable analog comparator (to save power)
ldi temp, 0x80
out ACSR, temp
;init stackpointer
ldi temp, RAMEND
out SPL, temp
;configure pin change interrupt on PA1 (push-button inut)
ldi temp, 0x02 ;detect pin change on PA1 only
out PCMSK0, temp
ldi temp, 1<<PCIE0 ;enable pin change interrupt
out GIMSK, temp
;init registers
clr flags
ldi timeout1, BYTE1(AUTO_POWER_OFF)
ldi timeout2, BYTE2(AUTO_POWER_OFF)
ldi timeout3, BYTE3(AUTO_POWER_OFF)
ldi timeout4, BYTE4(AUTO_POWER_OFF)
;check if always-on mode selected
sbic PINA, 0
rjmp always_on_end
sbi PORTA, 2
rjmp PC+1
sbic PINA, 0
ori flags, 1<<fAlwaysOn | 1<<fNoAutoOff
always_on_end:
cbi PORTA, 2
;sleep
sbrs flags, fAlwaysOn
rcall power_off
;-------------------------------------------------------------------------------
main_loop:
rcall animation_run_cw
rcall animation_flicker
rcall animation_worm_ccw
rcall animation_blink
rcall animation_run_ccw
rcall animation_wobble
rcall animation_worm_cw
rcall animation_glow
rjmp main_loop
;===============================================================================
animation_blink:
;flash all LEDs on and off 3 times
ldi repeat, 3
animation_blink_loop:
ldi value, 63 ;all LEDs on
rcall set_leds
ldi count, 50
rcall show_frame
ldi value, 0 ;all LEDs off
rcall set_leds
ldi count, 50
rcall show_frame
dec repeat
brne animation_blink_loop
ret
;--------------------
animation_glow:
;slowly fade all LEDs on and off 3 times
ldi repeat, 3
ldi value, 0
animation_glow_on:
rcall set_leds
ldi count, 2
rcall show_frame
inc value
cpi value, 63
brne animation_glow_on
animation_glow_off:
rcall set_leds
ldi count, 2
rcall show_frame
dec value
nop
brne animation_glow_off
dec repeat
brne animation_glow_on
ret
;--------------------
animation_flicker:
;alternate between odd and even LEDs 10 times
ldi repeat, 10
animation_flicker_loop:
ldi value, 0 ;even LEDs off (odd LEDs on)
rcall set_leds_even
ldi count, 15
rcall show_frame
ldi value, 63 ;even LEDs on (odd LEDs off)
rcall set_leds_even
ldi count, 15
rcall show_frame
dec repeat
brne animation_flicker_loop
ret
;--------------------
animation_wobble:
;slowly fade between odd and even LEDs 5 times
ldi repeat, 5
ldi value, 0
animation_wobble_on:
rcall set_leds_even
ldi count, 2
rcall show_frame
inc value
cpi value, 63
brne animation_wobble_on
animation_wobble_off:
rcall set_leds_even
ldi count, 2
rcall show_frame
dec value
nop
brne animation_wobble_off
dec repeat
brne animation_wobble_on
ret
;--------------------
animation_run_cw:
;light chaser with 2 dots, clockwise, 2.5 rotations
ldi repeat, 5
animation_run_cw_start:
ldi YL, RAM_Frame
animation_run_cw_loop:
ldi value, 0
rcall set_leds
ldi value, 63
std Y+9, value
st Y+, value
ldi count, 20
rcall show_frame
cpi YL, RAM_Frame+9
brne animation_run_cw_loop
dec repeat
brne animation_run_cw_start
ret
;--------------------
animation_run_ccw:
;light chaser with 2 dots, counter-clockwise, 2.5 rotations
ldi repeat, 5
animation_run_ccw_start:
ldi YL, RAM_Frame+9
animation_run_ccw_loop:
ldi value, 0
rcall set_leds
ldi value, 63
st -Y, value
std Y+9, value
ldi count, 20
rcall show_frame
cpi YL, RAM_Frame
brne animation_run_ccw_loop
dec repeat
brne animation_run_ccw_start
ret
;--------------------
animation_worm_cw:
;worm with fade-out tail, clockwise, 3 rotations
ldi repeat, 5
ldi value, 0
rcall set_leds
animation_worm_cw_start:
ldi YL, RAM_Frame
animation_worm_cw_loop:
rcall fade_leds
ldi value, 63
st Y+, value
ldi count, 20
rcall show_frame
cpi YL, RAM_Frame+18
brne animation_worm_cw_loop
dec repeat
brne animation_worm_cw_start
ret
;--------------------
animation_worm_ccw:
;worm with fade-out tail, counter-clockwise, 3 rotations
ldi repeat, 5
ldi value, 0
rcall set_leds
animation_worm_ccw_start:
ldi YL, RAM_Frame+18
animation_worm_ccw_loop:
rcall fade_leds
ldi value, 63
st -Y, value
ldi count, 20
rcall show_frame
cpi YL, RAM_Frame
brne animation_worm_ccw_loop
dec repeat
brne animation_worm_ccw_start
ret
;===============================================================================
set_leds:
;set all LEDs to the same brightness value
sts RAM_Frame+0, value
sts RAM_Frame+1, value
sts RAM_Frame+2, value
sts RAM_Frame+3, value
sts RAM_Frame+4, value
sts RAM_Frame+5, value
sts RAM_Frame+6, value
sts RAM_Frame+7, value
sts RAM_Frame+8, value
sts RAM_Frame+9, value
sts RAM_Frame+10, value
sts RAM_Frame+11, value
sts RAM_Frame+12, value
sts RAM_Frame+13, value
sts RAM_Frame+14, value
sts RAM_Frame+15, value
sts RAM_Frame+16, value
sts RAM_Frame+17, value
ret
;--------------------
set_leds_even:
;set even LEDs to given brightness value, odd LEDs to the inverse
mov temp, value
com temp
andi temp, 63
sts RAM_Frame+0, value
sts RAM_Frame+1, temp
sts RAM_Frame+2, value
sts RAM_Frame+3, temp
sts RAM_Frame+4, value
sts RAM_Frame+5, temp
sts RAM_Frame+6, value
sts RAM_Frame+7, temp
sts RAM_Frame+8, value
sts RAM_Frame+9, temp
sts RAM_Frame+10, value
sts RAM_Frame+11, temp
sts RAM_Frame+12, value
sts RAM_Frame+13, temp
sts RAM_Frame+14, value
sts RAM_Frame+15, temp
sts RAM_Frame+16, value
sts RAM_Frame+17, temp
ret
;--------------------
fade_leds:
;fade each LED to half its brightness value
lds value, RAM_Frame+0
lsr value
sts RAM_Frame+0, value
lds value, RAM_Frame+1
lsr value
sts RAM_Frame+1, value
lds value, RAM_Frame+2
lsr value
sts RAM_Frame+2, value
lds value, RAM_Frame+3
lsr value
sts RAM_Frame+3, value
lds value, RAM_Frame+4
lsr value
sts RAM_Frame+4, value
lds value, RAM_Frame+5
lsr value
sts RAM_Frame+5, value
lds value, RAM_Frame+6
lsr value
sts RAM_Frame+6, value
lds value, RAM_Frame+7
lsr value
sts RAM_Frame+7, value
lds value, RAM_Frame+8
lsr value
sts RAM_Frame+8, value
lds value, RAM_Frame+9
lsr value
sts RAM_Frame+9, value
lds value, RAM_Frame+10
lsr value
sts RAM_Frame+10, value
lds value, RAM_Frame+11
lsr value
sts RAM_Frame+11, value
lds value, RAM_Frame+12
lsr value
sts RAM_Frame+12, value
lds value, RAM_Frame+13
lsr value
sts RAM_Frame+13, value
lds value, RAM_Frame+14
lsr value
sts RAM_Frame+14, value
lds value, RAM_Frame+15
lsr value
sts RAM_Frame+15, value
lds value, RAM_Frame+16
lsr value
sts RAM_Frame+16, value
lds value, RAM_Frame+17
lsr value
sts RAM_Frame+17, value
ret
;--------------------
show_frame:
;show current frame 'count' times
rcall frame
dec count
brne show_frame
;--------------------
frame:
;multiplex current frame once (with PWM)
;63 * 119 = 7497 total cycles (excluding return instruction) => ~7.5 ms
;running from 62 to 0: LED on if pwm < value, 100% on for value = 63
ldi pwm, 62
pwm_loop:
;total loop cycles: 25+3+25+5+25+5+25+2+4 = 119 cycles
;(1 MHz / 119) = ~8.4 kHz loop frequency
;calculate data for LED group A (25 cycles)
ldi leds, 0xC0 ;pull-ups enabled for PA1 and PA0
lds temp, RAM_Frame+0
cp pwm, temp ;carry set (LED on) if pwm < temp
ror leds
lds temp, RAM_Frame+1
cp pwm, temp
ror leds
lds temp, RAM_Frame+2
cp pwm, temp
ror leds
lds temp, RAM_Frame+3
cp pwm, temp
ror leds
lds temp, RAM_Frame+4
cp pwm, temp
ror leds
lds temp, RAM_Frame+5
cp pwm, temp
ror leds
;output (3 cycles)
out PORTA, leds ;output LED data for group A
cbi PORTB, 0 ;enable LED group A
;calculate data for LED group B (25 cycles)
ldi leds, 0xC0
lds temp, RAM_Frame+6
cp pwm, temp
ror leds
lds temp, RAM_Frame+7
cp pwm, temp
ror leds
lds temp, RAM_Frame+8
cp pwm, temp
ror leds
lds temp, RAM_Frame+9
cp pwm, temp
ror leds
lds temp, RAM_Frame+10
cp pwm, temp
ror leds
lds temp, RAM_Frame+11
cp pwm, temp
ror leds
;output (5 cycles)
sbi PORTB, 0 ;disable LED group A
out PORTA, leds ;output LED data for group B
cbi PORTB, 1 ;enable LED group B
;calculate data for LED group C (25 cycles)
ldi leds, 0xC0
lds temp, RAM_Frame+12
cp pwm, temp
ror leds
lds temp, RAM_Frame+13
cp pwm, temp
ror leds
lds temp, RAM_Frame+14
cp pwm, temp
ror leds
lds temp, RAM_Frame+15
cp pwm, temp
ror leds
lds temp, RAM_Frame+16
cp pwm, temp
ror leds
lds temp, RAM_Frame+17
cp pwm, temp
ror leds
;output (5 cycles)
sbi PORTB, 1 ;disable LED group B
out PORTA, leds ;output LED data for group C
cbi PORTB, 2 ;enable LED group C
;poll push-button (7 cycles)
sbic PINA, 1
rjmp button_released
;button pressed
sbrs button_cnt, 7 ;prevent overflow
inc button_cnt
cpi button_cnt, 127 ;pressed for (127 / 8.4 kHz) = ~15 ms
brne button_end
sbrs flags, fAlwaysOn
rcall power_off
rjmp button_end
button_released:
clr button_cnt
rjmp PC+1 ;2 cycles
nop ;1 cycle
button_end:
;auto-power-off timeout counter (8 cycles)
clc ;clear carry
sbrs flags, fNoAutoOff
subi timeout1, 1
sbci timeout2, 0
sbci timeout3, 0
sbci timeout4, 0
brne PC+2
rcall power_off
;extra delay (10 cycles) - button (7) + timeout (8) + 10 = 25 cycles
rcall return ;7 cycles
rjmp PC+1 ;2 cycles
nop ;1 cycle
;output (2 cycles)
sbi PORTB, 2 ;disable LED group C
;end of PWM loop (4 cycles when looping, 3 when exiting loop)
subi pwm, 1
brcs PC+2
rjmp pwm_loop
return:
ret
;--------------------
power_off:
;button pressed or auto-power-off timeout elapsed: turn off
;disable LED group C
sbi PORTB, 2
;set all LED cathodes low (turn on LEDs installed in reverse)
ldi temp, 0x03
out PORTA, temp
;wait until button released
power_off_wait:
sbis PINA, 1
rjmp power_off_wait
;pull PA0 low (avoid pull-up current)
cbi PORTA, 0
sbi DDRA, 0
;enable interrupts (use pin change interrupt for wake-up)
ldi temp, 1<<PCIF0 ;clear pending pin change interrupts
out GIFR, temp
sei
power_off_sleep:
;sleep
ldi temp, 1<<SM1 | 1<<SE ;power-down mode, sleep enabled
out MCUCR, temp
sleep
ldi temp, 1<<SM1 ;disable sleeping
out MCUCR, temp
;check if button pressed for a certain time (filter ESD and spikes)
clr button_cnt
power_on_loop:
;delay ~1 ms (999 cycles)
ldi temp, 0 ;256 * 3 = 768 cycles delay
dec temp
brne PC-1
ldi temp, 77 ;77 * 3 = 231 cycles delay
dec temp
brne PC-1
;poll button
sbic PINA, 1
rjmp power_off_sleep
;button pressed
inc button_cnt
cpi button_cnt, POWER_ON_DELAY
brlo power_on_loop
;power on
ldi button_cnt, 255 ;avoid duplicate button press detection
;restore PA0 as input with pull-up
cbi DDRA, 0
sbi PORTA, 0
;disable interrupts
cli
;reset power-off timeout
ldi timeout1, BYTE1(AUTO_POWER_OFF)
ldi timeout2, BYTE2(AUTO_POWER_OFF)
ldi timeout3, BYTE3(AUTO_POWER_OFF)
ldi timeout4, BYTE4(AUTO_POWER_OFF)
;read PA0 (auto-power-off disable)
andi flags, ~(1<<fNoAutoOff)
sbis PINA, 0
ori flags, 1<<fNoAutoOff
ret