rendered paste body; ATtiny15L LED blinker
; Row Control: PB5, PB4 (cathode)
; Column Ctrl: PB3, PB0, PB1 (anode)
; Button Ctrl: PB2 (INT0)
;
; eric@elegantinvention.com
; lain<3jack
.include "tn15def.inc"
; friggin include lacks PORTB5 b/c
; external #RESET has to be disabled..
.equ PORTB5 = 5
.equ PB5 = 5
; define our control lines
.equ ROW1 = PORTB5
.equ ROW2 = PORTB4
.equ COL1 = PORTB3
.equ COL2 = PORTB0
.equ COL3 = PORTB1
.equ BTN_PIN = PORTB2
; define our LED bit positions
.equ LEDRT = 0 ; right top row1,col1
.equ LEDRM = 1 ; right middle row1,col2
.equ LEDRB = 2 ; right bottom row1,col3
.equ LEDLB = 3 ; left bottom row2,col3
.equ LEDLM = 4 ; left middle row2,col2
.equ LEDLT = 5 ; left top row2,col1
.equ LEDRST = 6 ; LED RESET
.equ LEDOVF = 7 ; LED OVERFLOW
; define the bit positions for up/down/updwn modes list
.equ LIST1 = 0 ; use item 1 (top 2)
.equ LIST2 = 1 ; use item 2 (middle 2)
.equ LIST3 = 2 ; use item 3 (bottom 2)
.equ LISTOVF = 3 ; LIST overflow
.equ LTDIR = 5 ; LIST direction for _m_updwn
.equ LTMISC = 6 ; unused MISC bit
.equ LISTUNF = 7 ; LIST "underflow"
; modes
.equ m_cw = 0x00
.equ m_ball = 0x01
.equ m_bhalf = 0x02
.equ m_down = 0x03
.equ m_up = 0x04
.equ m_updwn = 0x05
.equ m_sleep = 0x06
; "variables"
.def count1 = R16 ; used for the delay loop
.def count2 = R17
.def loops = R18 ; number of loops to perform
.def mode = R19 ; holds the current mode
.def temp = R20 ; temporary stuff
.def tempi = R21 ; don't want the interrupt vector screwing with temp
.def leds = R22 ; led status register
.def item1 = R23 ; list of states for up/down/updwn modes
.def item2 = R24
.def item3 = R25
.def listp = R26 ; current list position/direction and over/under-flow flags
.cseg
.org 0
rjmp main ; reset condition
.org INT0addr
rjmp button ; button pressed (int0 vector 0x001)
.org INT_VECTORS_SIZE ; start after interrupt vectors
delay:
ldi count1, 0xff
_delay_outer:
ldi count2, 0xff ; reload count2 for next loop
_delay_inner:
dec count2 ; count2--
cpi count2, 0x00 ; if count2 != 0, keep looping
brne _delay_inner
dec count1 ; count1--
cpi count1, 0x00 ; if count1 != 0, keep looping
brne _delay_outer
dec loops ; loops--
cpi loops, 0x00 ; if loops != 0, keep looping
brne delay
ret
button:
ldi loops, 0x03 ; wait 1.6MHz/(65025*3) = ~122ms (debounce)
rcall delay
sbr leds, (1<<LEDRST) ; enable the LEDRST condition
inc mode ; next mode..
ldi tempi, (m_sleep + 1)
cpse mode, temp ; return if mode != (m_sleep + 1)
reti
clr mode ; mode overflow, reset mode and return (exit sleep)
reti
main:
ldi item1, (1<<LEDRT)+(1<<LEDLT) ; the list for up/down/updwn modes
ldi item2, (1<<LEDRM)+(1<<LEDLM)
ldi item3, (1<<LEDRB)+(1<<LEDLB)
ldi mode, m_sleep ; set default mode
ser temp ; temp=0xff (all pins output)
cbr temp, (1<<BTN_PIN) ; button pin is input w/ pull-up
out DDRB, temp
in temp, MCUCR ; get current MCUCR status
sbr temp, (1<<SM1)+(1<<SE) ; enable sleep & set mode = power down mode
cbr temp, (1<<SM0)
cbr temp, (1<<ISC01)+(1<<ISC00) ; low level on BTN_PIN triggers int0
out MCUCR, temp ; update MCUCR
in temp, GIMSK ; grab GIMSK and enable external int0
sbr temp, (1<<INT0)
out GIMSK, temp
ldi temp, (1<<BTN_PIN) ; set BTNPIN=1 for PORTB for pull-up!!
out PORTB, temp
sei ; global interrupt enable
sleep ; sleep and wait for button press
idle:
ldi loops, 0x02 ; wait ~80ms between updating leds..
rcall delay
rcall update_leds ; actually update the led i/o pins (delayed)
cpi mode, m_cw ; if mode == m_cw
breq _m_cw
cpi mode, m_ball ; if mode == m_ball
breq _m_ball
cpi mode, m_bhalf ; if mode == m_bhalf
breq _m_bhalf
cpi mode, m_down ; if mode == m_down
breq _m_down_pre
cpi mode, m_up ; if mode == m_up
breq _m_up_pre
cpi mode, m_updwn ; if mode == m_updwn
breq _m_updwn_pre
ldi temp, (1<<BTN_PIN) ; make sure BTNPIN=1 for pull-up before sleep!
out PORTB, temp ; this will also disable the LEDs
sleep ; else: sleep
rjmp idle
update_leds: ; update the led i/o pins based on leds register
clr temp ; clear out temp
sbr temp, (1<<BTN_PIN) ; this keeps it pull-up!! IMPORTANT!
sbrc leds, LEDRT ; activate the proper ROWs/COLs for
sbr temp, (1<<ROW1)+(1<<COL1) ; the LEDs that are on
sbrc leds, LEDRM
sbr temp, (1<<ROW1)+(1<<COL2)
sbrc leds, LEDRB
sbr temp, (1<<ROW1)+(1<<COL3)
sbrc leds, LEDLT
sbr temp, (1<<ROW2)+(1<<COL3)
sbrc leds, LEDLM
sbr temp, (1<<ROW2)+(1<<COL2)
sbrc leds, LEDLB
sbr temp, (1<<ROW2)+(1<<COL1)
out PORTB, temp ; output temp on PORTB
ret
; specific led controlling calls,
_m_cw: ; rotate the leds clockwise
sbrs leds, LEDRST ; don't shift if LEDRST
lsl leds ; left shift = cw rotate
; LEDRST will be set if shifted too far
sbrc leds, LEDRST ; if LEDRST set, load the default
ldi leds, (1<<LEDRT)
cbr leds, (1<<LEDRST) ; clear the reset condition
rjmp idle
_m_down_pre:
rjmp _m_down
_m_up_pre:
rjmp _m_up
_m_updwn_pre:
rjmp _m_updwn
_m_ball: ; blink all
sbrc leds, LEDRST ; if LEDRST set, load the default
ldi leds, 0b00111111
cbr leds, (1<<LEDRST)
com leds ; take the 1's complement (invert the bits)
cbr leds, (1<<LEDRST)+(1<<LEDOVF) ; clear LEDRST and LEDOVF flags
rjmp idle
_m_bhalf: ; alternate halves
sbrc leds, LEDRST ; if LEDRST set, load the default
ldi leds, (1<<LEDRT)+(1<<LEDRM)+(1<<LEDRB)
cbr leds, (1<<LEDRST)
com leds ; 1's complement (invert the bits)
cbr leds, (1<<LEDRST)+(1<<LEDOVF) ; clear LEDRST and LEDOVF flags
rjmp idle
_m_down: ; light from top to bottom, 2 at a time
lsl listp ; shift listp left
mov temp, leds ; store leds in temp for LEDRST check later
sbrc listp, LISTOVF ; make sure we don't have an overflow
sbr listp, (1<<LIST1) ; reset to LIST1
cbr listp, (1<<LISTOVF) ; clear overflow even if there wasn't one
sbrc temp, LEDRST ; if LEDRST set,
ldi listp, (1<<LIST1) ; then set listp to LIST1
sbrc listp, LIST1 ; mov the proper item into the leds register
mov leds, item1
sbrc listp, LIST2
mov leds, item2
sbrc listp, LIST3
mov leds, item3
rjmp idle
_m_up: ; light from bottom to top, 2 at a time
ror listp ; right shift w/ carry the list
mov temp, leds ; store leds in temp for LEDRST check later
brcc _m_up_listunf1 ; if we have an underflow
sbr listp, (1<<LIST3) ; reset listp to LIST3 (default)
_m_up_listunf1:
cbr listp, (1<<LISTUNF) ; clear the underflow even if there wasn't one
sbrc temp, LEDRST ; if LEDRST set, load the default
ldi listp, (1<<LIST3) ; which is LIST3
sbrc listp, LIST1 ; mov the proper item into the leds register
mov leds, item1
sbrc listp, LIST2
mov leds, item2
sbrc listp, LIST3
mov leds, item3
rjmp idle
_m_updwn: ; light top->bottom->top->bottom->....
mov temp, listp ; listp -> temp
cbr listp, (1<<LTDIR) ; clear list direction in listp
sbrc temp, LTDIR ; if LTDIR=1, right shift w/ carry
ror listp
sbrs temp, LTDIR ; if LTDIR=0, left shift
lsl listp
sbrc listp, LISTOVF ; make sure we don't have an overflow
sbr listp, (1<<LIST3) ; reset to LIST3 if we do
sbrc listp, LISTOVF
sbr listp, (1<<LTDIR) ; change direction, LTDIR=1 (up)
cbr listp, (1<<LISTOVF) ; clear overflow even if there wasn't one
brcc _m_updwn_listunf1 ; if we have an underflow,
sbr listp, (1<<LIST1) ; reset listp to LIST1 (default)
cbr listp, (1<<LTDIR) ; change direction, LTDIR=0 (down)
_m_updwn_listunf1:
cbr listp, (1<<LISTUNF) ; clear the underflow even if there wasn't one
sbrc leds, LEDRST ; if LEDRST set, load the default
ldi listp, (1<<LIST1)
sbrc listp, LIST1 ; mov the proper item into the leds register
mov leds, item1
sbrc listp, LIST2
mov leds, item2
sbrc listp, LIST3
mov leds, item3
rjmp idle
; eof