; electronic dice ; Copyright 2008 Stefan Schuermans ; Copyleft: GNU public license V2 - http://www.gnu.org/copyleft/gpl.html ; a BlinkenArea project - http://www.blinkenarea.org/ ; fuse bits: low=0x64 high=0xD9:m upper=0xFF ; clock frequency: 8 MHz (internal RC oscillator) ; PA0..1: unused ; PA2: reset ; PB0..2: unused ; PB3: input from start button (low active) ; PB4: input from turn off button (low active) ; PB5: programming input ; PB6: programming output ; PB7: programming clock ; PD0..6: output to LED1..7 .INCLUDE "tn2313def.inc" ; IO pins .equ PIN_BUTTONS = PINB ; button inputs .equ BIT_BTN_START = 3 ; start button .equ BIT_BTN_OFF = 4 ; turn off button .equ PORT_LEDS = PORTD ; LED outputs ; general purpose registers .def TMP = r16 ; register for temporary storage .def CNT = r17 ; multi-purpose counter .def PATTERN = r18 ; number of pattern .def STATE = r0 ; register for state of button .DSEG .ORG 0x060 ; current pattern being shown ; 0: LEDs off ; 1..6: number of pattern CUR_PATTERN: .BYTE 1 ; time to show the current pattern ; 0: infinite ; 1..255: duration in ticks DURATION: .BYTE 1 ; remaining time to show current pattern ; 0: pattern is stable ; 1..255: remaining time in ticks REMAINING: .BYTE 1 ; random counter ; 1..6: start with this pattern is start is pressed now RANDOM: .BYTE 1 ; saved input states .equ IN_STATE_CNT = 5 IN_STATES: .BYTE IN_STATE_CNT ; debounced input state IN_DEBOUNCED: .BYTE 1 .CSEG .ORG 0x000 rjmp ENTRY ; RESET reti ; INT0 reti ; INT1 reti ; TIMER1_CAPT reti ; TIMER1_COMPA reti ; TIMER1_OVF rjmp TICK ; TIMER0_OVF reti ; USART0_RX reti ; USART0_UDRE reti ; USART0_TX reti ; ANALOG_COMP reti ; PC_INT reti ; TIMER1_COMPB reti ; TIMER0_COMPA reti ; TIMER0_COMPB reti ; USI_START reti ; USI_OVERFLOW reti ; EE_READY reti ; WDT ; code entry point ENTRY: ; initialize stack pointer ldi TMP,RAMEND out SPL,TMP ; set clock prescaler to 1:1 ldi TMP,1< LEDs: 3 cpi PATTERN,1 brne OUT_PATTERN_NOT_1 ldi TMP,1<<2 rjmp OUT_PATTERN_OUT OUT_PATTERN_NOT_1: ; pattern 2 ---> LEDs: 4, 7 cpi PATTERN,2 brne OUT_PATTERN_NOT_2 ldi TMP,1<<3|1<<6 rjmp OUT_PATTERN_OUT OUT_PATTERN_NOT_2: ; pattern 3 ---> LEDs: 1, 3, 6 cpi PATTERN,3 brne OUT_PATTERN_NOT_3 ldi TMP,1<<0|1<<2|1<<5 rjmp OUT_PATTERN_OUT OUT_PATTERN_NOT_3: ; pattern 4 ---> LEDs: 1, 4, 6, 7 cpi PATTERN,4 brne OUT_PATTERN_NOT_4 ldi TMP,1<<0|1<<3|1<<5|1<<6 rjmp OUT_PATTERN_OUT OUT_PATTERN_NOT_4: ; pattern 5 ---> LEDs: 1, 3, 4, 6, 7 cpi PATTERN,5 brne OUT_PATTERN_NOT_5 ldi TMP,1<<0|1<<2|1<<3|1<<5|1<<6 rjmp OUT_PATTERN_OUT OUT_PATTERN_NOT_5: ; pattern 6 ---> LEDs: 1, 2, 4, 5, 6, 7 cpi PATTERN,6 brne OUT_PATTERN_NOT_6 ldi TMP,1<<0|1<<1|1<<3|1<<4|1<<5|1<<6 rjmp OUT_PATTERN_OUT OUT_PATTERN_NOT_6: ; no or unknown pattern ---> LEDs off ldi TMP,0 ; output pattern OUT_PATTERN_OUT: out PORT_LEDS,TMP ; done ret ; random number generator ; changes TMP GEN_RANDOM: ; just switch to next random number (1 -> 2, 2 -> 3, ..., 5 -> 6, 6 -> 1) lds TMP,RANDOM inc TMP cpi TMP,7 brlo GEN_RANDOM_NO_OVERFLOW ldi TMP,1 GEN_RANDOM_NO_OVERFLOW: sts RANDOM,TMP ; done ret ; roll the dice ; changes: TMP ROLL_DICE: ; remaining time indicates stable pattern -> do nothing lds TMP,REMAINING cpi TMP,0 breq ROLL_DICE_DONE ; decrement remaining time dec TMP sts REMAINING,TMP ; if remaining time not yet zero -> done for now cpi TMP,0 brne ROLL_DICE_DONE ; switch to next pattern lds PATTERN,CUR_PATTERN ; get number of next pattern and save it inc PATTERN cpi PATTERN,7 brlo ROLL_DICE_NO_OVERFLOW ldi PATTERN,1 ROLL_DICE_NO_OVERFLOW: sts CUR_PATTERN,PATTERN rcall OUT_PATTERN ; output new patten ; increment duration of pattern lds TMP,DURATION inc TMP sts DURATION,TMP ; set remaining time to full duration sts REMAINING,TMP ; if duration is not yet above 40 ticks -> done cpi TMP,40+1 brlo ROLL_DICE_DONE ; rolling the dice finished ldi TMP,0 ; set duration to infinite sts DURATION,TMP ldi TMP,0 ; set pattern to stable sts REMAINING,TMP ; done ROLL_DICE_DONE: ret ; start button pressed ; changes: TMP PRESSED_START: ; save registers push PATTERN ; select initial pattern lds TMP,RANDOM ; use pattern number from random number generator sts CUR_PATTERN,TMP ; start dice ldi TMP,1 ; duration: 1 tick sts DURATION,TMP sts REMAINING,TMP ; remaning time: full duration ; show initial pattern lds PATTERN,CUR_PATTERN rcall OUT_PATTERN ; restore registers pop PATTERN ; done ret ; off button pressed ; changes: TMP PRESSED_OFF: ; save registers push PATTERN ; turn off LEDs ldi PATTERN,0 rcall OUT_PATTERN ; halt dice ldi TMP,0 ; no current pattern sts CUR_PATTERN,TMP ldi TMP,0 ; infinite duration sts DURATION,TMP ldi TMP,0 ; pattern is stable sts REMAINING,TMP ; restore registers pop PATTERN ; done ret ; get input states, debounce them and react to presses ; changes: TMP GET_INPUTS: ; save registers push CNT push STATE push XH push XL ; get current input state in STATE,PIN_BUTTONS com STATE ; invert value read (button inputs are active low) ; shift all input states one place further ldi CNT,IN_STATE_CNT ldi XH,high(IN_STATES) ldi XL,low(IN_STATES) GET_INPUT_SHIFT: ld TMP,X st X+,STATE mov STATE,TMP dec CNT brne GET_INPUT_SHIFT ; detect released buttons ; button is released if it was pressed and has been read released for IN_STATE_CNT times lds STATE,IN_DEBOUNCED ; begin with debounced state (was pressed) ldi CNT,IN_STATE_CNT ; AND with all complemented states (read released) ldi XH,high(IN_STATES) ldi XL,low(IN_STATES) GET_INPUT_RELEASE: ld TMP,X+ com TMP and STATE,TMP dec CNT brne GET_INPUT_RELEASE ; state contains now a 1 for every button released ; mark released buttons as released com STATE ; clear all debounced buttons states of buttons that were released lds TMP,IN_DEBOUNCED and TMP,STATE sts IN_DEBOUNCED,TMP ; detect pressed buttons ; button is pressed if it was released and has been read pressed for IN_STATE_CNT times lds STATE,IN_DEBOUNCED ; begin with complemented debounced state (was released) com STATE ldi CNT,IN_STATE_CNT ; AND with all states (read pressed) ldi XH,high(IN_STATES) ldi XL,low(IN_STATES) GET_INPUT_PRESS: ld TMP,X+ and STATE,TMP dec CNT brne GET_INPUT_PRESS ; state contains now a 1 for every button pressed ; mark pressed buttons as released lds TMP,IN_DEBOUNCED ; set all debounced buttons states of buttons that were pressed or TMP,STATE sts IN_DEBOUNCED,TMP ; react to pressed buttons sbrc STATE,BIT_BTN_START ; start button pressed? rcall PRESSED_START sbrc STATE,BIT_BTN_OFF ; stop button pressed? rcall PRESSED_OFF ; restore registers pop XL pop XH pop STATE pop CNT ; done ret ; tick - interrupt called every 8ms ; changes: nothing TICK: ; save TMP and SREG push TMP ; put TMP onto stack in TMP,SREG ; put SREG onto stack push TMP ; random number generator rcall GEN_RANDOM ; roll the dice rcall ROLL_DICE ; process inputs rcall GET_INPUTS ; restore SREG and TMP pop TMP ; restore SREG from stack out SREG,TMP pop TMP ; restore TMP from stack ; end of interrupt reti ; main program - initialization MAIN: ; off and stable ldi TMP,0 ; no current pattern sts CUR_PATTERN,TMP ldi TMP,0 ; infinite duration sts DURATION,TMP ldi TMP,0 ; pattern is stable sts REMAINING,TMP ldi TMP,1 ; initialize random number generator sts RANDOM,TMP ; turn off pattern ldi PATTERN,0 rcall OUT_PATTERN ; initialize input states ldi TMP,0 ; no button pressed ever ldi CNT,IN_STATE_CNT ldi XH,high(IN_STATES) ldi XL,low(IN_STATES) MAIN_INIT_IN_STATES: st X+,TMP dec CNT brne MAIN_INIT_IN_STATES ldi TMP,0 ; debounced state: no button pressed sts IN_DEBOUNCED,TMP ; turn on interrupts sei ; main program - main loop MAIN_LOOP: ; reset watchdog timer wdr ; bottom of main loop rjmp MAIN_LOOP