665 lines
14 KiB
NASM
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
|
|
|