$NOMOD51 $INCLUDE(REG51FX.INC) ; register definitions compatible ; with standard Intel architecture FX cpu USING 0 ; use register block 0 SWTPORT EQU P2 ; switches connected to Port 2 LEDPORT EQU P3 ; LEDs connected to Port 3 ;*************************************************** ; INTERNAL DATA BIT DEFINITIONS, locations 20H-2FH ;--------------------------------------------------- BSEG AT 00 Tick: DBIT 1 ; 1 = 1.25 msec tick has come ;*************************************************** ; INTERNAL DATA BYTE DEFINITIONS ;--------------------------------------------------- ;internal data area variables. ; DSEG AT 30H ; word locations are HIGH:LOW SwitchNo: DS 1 ; current switch number to process (0->7) SwitchStates: DS 1 ; current state of Switch 0 DS 1 ; current state of Switch 1 DS 1 ; current state of Switch 2 DS 1 ; current state of Switch 3 DS 1 ; current state of Switch 4 DS 1 ; current state of Switch 5 DS 1 ; current state of Switch 6 DS 1 ; current state of Switch 7 SwitchCounters: DS 2 ; counter for Switch 0 DS 2 ; counter for Switch 1 DS 2 ; counter for Switch 2 DS 2 ; counter for Switch 3 DS 2 ; counter for Switch 4 DS 2 ; counter for Switch 5 DS 2 ; counter for Switch 6 DS 2 ; counter for Switch 7 STACK: DS 20 ; reserve 20 bytes of space RAM_END: DS 1 ; end of allocated RAM marker ;*************************************************** ; INTERRUPT VECTOR DEFINITIONS ;--------------------------------------------------- CSEG AT 0000H ; reset vector JMP PWR_UP ; power-on entry point CSEG AT 0003H ; intr 0 vector CLR EX0 ; external int 0 not used RETI CSEG AT 000BH ; timer 0 vector RETI ; not used CSEG AT 0013H ; intr 1 vector CLR EX1 ; external int 1 not used RETI CSEG AT 001BH ; timer 1 vector JMP T1_ISR ; timer 1 int isr CSEG AT 0023H ; UART vector CLR ES ; serial port int not used RETI ;*************************************************** ; BASE OF CODE AREA ;--------------------------------------------------- CSEG AT 040H DB 'Copyright (C) Carousel Design 2003',0 ;*************************************************** ; NAME: INIT_T1 ; Initialize a 1.25 mSec period interrupt for a ; state machine dispatcher on timer 1. This is same ; as a frequency of 800 Hz. ; Mode 1 is 16-bit ; TMOD is not bit addressable. ; ; Timer clocks are 11.0952 mHz / 12 = 924600 Hz ; Divisor for 800 Hz = 924600 / 800 = 1155.75 (approx 1156) ;--------------------------------------------------- T1_RELOAD EQU (65536 - 1156) INIT_T1: MOV TMOD, #10H ; timer 1 - mode 1 ; MOV TH1, #HIGH(T1_RELOAD) ;to setup 800 Hz rate MOV TL1, #LOW(T1_RELOAD) ;timer 1 for 1.25 ms CLR Tick ; clear the tick indicator flag SETB TR1 ; start the timer 1 SETB ET1 ; enable its interrupt RET ;*************************************************** ;NAME: T1_ISR ; Used to time an 800 Hz ticker bit ;--------------------------------------------------- T1_ISR: PUSH PSW ; save entry state SETB Tick ; show tick time ; CLR ET1 ; stop it for a moment MOV TH1, #HIGH(T1_RELOAD) ; to setup 800 Hz rate MOV TL1, #LOW(T1_RELOAD) ; timer 1 for 1.25 ms SETB ET1 ; restart the timer ; POP PSW ; restore state RETI ;*************************************************** ;NAME: SWT_MSK ; Used to convert a switch number to a port mask ; Entry A is the 0-7 switch mask ;--------------------------------------------------- SWT_MSK: INC A MOVC A, @A+PC RET ; DB 00000001B ; Bit 0 <- 0 DB 00000010B ; Bit 1 <- 1 DB 00000100B ; Bit 2 <- 2 DB 00001000B ; Bit 3 <- 3 DB 00010000B ; Bit 4 <- 4 DB 00100000B ; Bit 5 <- 5 DB 01000000B ; Bit 6 <- 6 DB 10000000B ; Bit 7 <- 7 ;*************************************************** ; MAIN PROGRAM INITIALIZATION ;--------------------------------------------------- PWR_UP: MOV SP, #STACK ; set stack pointer ; MOV SWTPORT, #0FFH ; set to make the switch ports inputs MOV LEDPORT, #0FFH ; fix so all LEDs are off CALL INIT_T1 ; initialize timer 1 interrupt CLR A ; initialize switch number variable MOV SwitchNo, A MOV R0, #SwitchStates ; clear all switch states to 0 MOV R2, #8 SwStInit: MOV @R0, #0 INC R0 DJNZ R2, SwStInit MOV R0, #SwitchCounters ; clear all switch counters MOV R2, #8 SwCtInit: MOV @R0, #0 INC R0 MOV @R0, #0 INC R0 DJNZ R2, SwCtInit ; SETB EA ; enable interrupts JMP MAIN_LOOP ; ; ; here is the main loop state table for the processing ; of the states for a specific switch. ; ; States are defined as: ; State 0: Waiting for a switch input to show it was ; detected going low. ; State 1: Waiting for second verification of a switch ; input in the low state as a debounce verification. ; State 2: Waiting for 5 second counter time to expire while LED ; is kept on for 5 seconds ; State 3: Waiting for switch input to show high again ; STATE_TABLE: DW MAIN_STATE_0 ; pointer to State 0 routine DW MAIN_STATE_1 ; pointer to State 1 routine DW MAIN_STATE_2 ; pointer to State 2 routine DW MAIN_STATE_3 ; pointer to State 3 routine STATE_CNT EQU ($ - STATE_TABLE)/2 ;number of states ; ; ;*************************************************** ; main loop process ; ; This processes the current state each switch in a round robin manner. ; Each state is dispatched whenever the timer 1 interrupt indicates that ; 1.25 milliseconds has gone by. Then the state routine for one switch is called ; followed by an increment of the switch number variable. The main loop ; then goes back to the top to wait for another 1.25 msec period to expire. ; Since we process each of 8 switches in turn the effective processing rate ; for each switch input is 1,25 msec * 8 = 10 mSec (or 100 Hz). ;--------------------------------------------------- ; MAIN_LOOP: JNB Tick, MAIN_LOOP ; wait till a tick has gone by ; CLR Tick ; clear bit once seen ; MOV A, SwitchNo ; fetch the state for the current ADD A, #SwitchStates ; switch MOV R0, A MOV A, @R0 CJNE A, #STATE_CNT,ML_A ; check for legal state number ML_A: JC ML_B ; state number OK CLR A ; reset to 0 if invalid MOV @R0, A ML_B: MOV DPTR, #STATE_TABLE ; point to state branch table CALL CALL_TABLE ; call to state routine ; MOV A, SwitchNo ; increment to next switch number INC A ANL A, #7 ; limit to 3 bits of switch number MOV SwitchNo, A ; JMP MAIN_LOOP ; go wait for next tick time ;*************************************************** ;NAME: MAIN_STATE_0 ; This is state processing routine to wait for a ; switch to be in the pressed state where the input ; for the switch comes from the SWTPORT. If the input ; is still high then just stay in State 0 otherwise ; transition to State 1. ; ; Variable SwitchNo has the current switch number. ;--------------------------------------------------- MAIN_STATE_0: MOV A, SwitchNo ; get the switch number CALL SWT_MSK ; get mask for this switch number ANL A, SWTPORT ; look at switch state JNZ MAIN_STATE_0X ; exit no change if switch still high ; ;switch is pressed so change to State 1. ; MOV A, SwitchNo ; set new state for the current ADD A, #SwitchStates ; switch MOV R0, A MOV @R0, #1 ; force next state number to State 1 ; MAIN_STATE_0X: RET ;*************************************************** ;NAME: MAIN_STATE_1 ; This is state processing routine to validate that ; switch is still in the pressed state where the input ; for the switch comes from the SWTPORT. If the input ; is still low then transfer to State 2 with LED on ; for 5 seconds. Otherwise if input is high we have ; bounce so return back to state 0. ; ; Variable SwitchNo has the current switch number. ;--------------------------------------------------- FIVE_SEC_CNT EQU 5 * 100 ; 5 seconds is 5 * 100 Hz state 2 rate (i,e, 10 msec) MAIN_STATE_1: MOV A, SwitchNo ; get the switch number CALL SWT_MSK ; get mask for this switch number ANL A, SWTPORT ; look at switch state JZ MAIN_STATE_1B ; input low so switch still pressed ; MAIN_STATE_1A: ; here if switch input bounced back high MOV A, SwitchNo ; set new state for the current ADD A, #SwitchStates ; switch MOV R0, A MOV @R0, #0 ; force next state number back to State 0 JMP MAIN_STATE_1X ; MAIN_STATE_1B: ;here if switch input valid low for 10 mSec MOV A, SwitchNo ; get bit to set on the LED for this switch CALL SWT_MSK ; get mask for this switch number XRL A, 0FFH ; invert mask ANL LEDPORT, A ; drive the LED bit low to turn on the LED ; MOV A, SwitchNo ; set the counter for this switch to 5 seconds CLR C RLC A ; make *2 word type index to counter table ADD A, #SwitchCounters MOV R0, A ; make pointer to counter word MOV @R0, #HIGH(FIVE_SEC_CNT) ;set counter for 5 seconds INC R0 MOV @R0, #LOW(FIVE_SEC_CNT) ; MOV A, SwitchNo ; set new state for the current ADD A, #SwitchStates ; switch MOV R0, A MOV @R0, #2 ; force next state number to State 2 ; MAIN_STATE_1X: RET ;*************************************************** ;NAME: MAIN_STATE_2 ; This is state processing routine to wait out the ; five second time that the LED is activated. The ; switch input status is just ignored. If the count ; is still active then the state stays as State 2. ; When the 5 seconds has expired then the LED for ; this switch is shut off and the state is changed ; to state 3. ; ; Variable SwitchNo has the current switch number. ;--------------------------------------------------- MAIN_STATE_2: MOV A, SwitchNo ; decrement the counter for this switch CLR C RLC A ; make *2 word type index to counter table ADD A, #SwitchCounters+1 ;offset to access the low byte first. MOV R0, A MOV A, @R0 ; fetch low byte and decrement it DEC A CJNE A, #0FFH, MAIN_STATE_2A ;low byte did not underflow DEC R0 DEC @R0 ; decrement the high byte INC R0 MAIN_STATE_2A: MOV @R0, A ; save the low byte ; DEC R0 ; check for count having gone to zero ORL A, @R0 JNZ MAIN_STATE_2X ; exit staying in state 2 if counter not expired ; MAIN_STATE_2B: ;here when the 5 second timer has expired MOV A, SwitchNo ; get bit to set on the LED for this switch CALL SWT_MSK ; get mask for this switch number ORL LEDPORT, A ; drive the LED bit high to turn off the LED ; MOV A, SwitchNo ; set new state for the current ADD A, #SwitchStates ; switch MOV R0, A MOV @R0, #3 ; force next state number to State 3 ; MAIN_STATE_2X: RET ;*************************************************** ;NAME: MAIN_STATE_2 ; This is state processing routine to wait for the ; switch input to go back high, It may have already ; gone high during the 5 second LED period in ; which case we catch it high here on the first check. ; If the switch input is still low then stay in ; State 3. If the switch has gone high then transfer ; back to State 0. ; ; Variable SwitchNo has the current switch number. ;--------------------------------------------------- MAIN_STATE_3: MOV A, SwitchNo ; get the switch number CALL SWT_MSK ; get mask for this switch number ANL A, SWTPORT ; look at switch state JZ MAIN_STATE_3X ; exit no change if switch still low ; ;switch is released so change to State 0. ; MOV A, SwitchNo ; set new state for the current ADD A, #SwitchStates ; switch MOV R0, A MOV @R0, #0 ; force next state number to State 0 ; MAIN_STATE_3X: RET ;*************************************************** ;NAME: CALL_TABLE ; Routine to call a routine through a table. ; Come here with A as the 0-n index into the ; table and DPTR pointing to the base of the ; table. The return at end of called routine ; takes execution back to where this routine ; was called from. This routine also uses R0. ;--------------------------------------------------- CALL_TABLE: CLR C ; RLC A ; multiply * 2 for word access MOV R0, A ; save a copy of index INC A ; increment index to the hig byte MOVC A, @A+DPTR ; low byte PUSH ACC ; onto stack MOV A, R0 MOVC A, @A+DPTR ; high byte PUSH ACC ; onto stack RET ; direct branch to the subroutine END