; @(#) master.asm 1.2@(#) ; Delta date: 04/10/2004 ; Author: Mark Mraz ; ; ; The program is the master program for my Halloween display. ; It consists of the following components: ; - A control panel consisting of 4 momentary push buttons, a toggle ; switch, and an LED. ; - An output pin that tells all the other props to go from sleep mode ; to standby mode. ; - The UART transmitter line, which goes to all the other props. ; The master uses this line to tell the other props what to do. ; - A line in from the laser board. The laser board uses this line ; to tell the master when the laser beam has been broken. ; - For testing purposes, there is also an LED board connected to ; the master. The LED board is a combination of a shift register, ; a transistor array, and 8 LEDs. This is used to give me an ; idea of what's going on inside the processor. This will most likely ; not be used in the actual setup. ; #define NDEBUG INCLUDE "maininc.inc" INCLUDE "commands.inc" ifndef DEBUG extern debounce_buttons EXTERN delay250ms, delay5us EXTERN clock_in_8_a, ci8a_data_pin_mask, ci8a_clock_pin_mask endif STANDBY_IND_PIN EQU 0 LED_PIN EQU 1 ; LED on the control panel CLOCK_PIN EQU 2 ; Clock pin for the LED board DATA_PIN EQU 3 ; Data pin for the LED board LASER_IND_PIN EQU 4 ; Signal from the laser board STANDBY_IND_PORT EQU PORTA LED_PORT EQU PORTA LASER_IND_PORT EQU PORTA ; ; Set the initial value of the outputs on PORTA, ; and set which pins are inputs on PORTA. ; PORTA_INIT EQU 0 PORTA_INPUT EQU (1 << LASER_IND_PIN) MASTER_SWITCH_PIN EQU 0 ; pin 2 used by the UART to transmit data BUTTON1_PIN EQU 4 BUTTON2_PIN EQU 5 BUTTON3_PIN EQU 6 BUTTON4_PIN EQU 7 BUTTONS_PORT EQU PORTB MASTER_SWITCH_PORT EQU PORTB BUTTONS_MASK EQU (1 << BUTTON1_PIN) | (1 << BUTTON2_PIN) | (1 << BUTTON3_PIN) | (1 << BUTTON4_PIN) ; ; Set the initial value of the outputs on PORTB, ; and set which pins are inputs on PORTB. ; PORTB_INIT EQU 0 PORTB_INPUT EQU BUTTONS_MASK | (1 << MASTER_SWITCH_PIN) ; ; Values for the variable ; SEQ_RUNNING_FLAG EQU 0 LASER_ON_FLAG EQU 1 ; ; Define some macros to make the code simpler. ; #define TURN_ON_LED bsf LED_PORT, LED_PIN #define TURN_OFF_LED bcf LED_PORT, LED_PIN #define TURN_ON_STANDBY bsf STANDBY_IND_PORT, STANDBY_IND_PIN #define TURN_OFF_STANDBY bcf STANDBY_IND_PORT, STANDBY_IND_PIN #define IF_LASER_TRIPPED btfsc LASER_IND_PORT, LASER_IND_PIN #define IF_LASER_NOT_TRIPPED btfss LASER_IND_PORT, LASER_IND_PIN #define IF_MASTER_ON btfss MASTER_SWITCH_PORT, MASTER_SWITCH_PIN #define IF_MASTER_OFF btfsc MASTER_SWITCH_PORT, MASTER_SWITCH_PIN #define IF_ZERO btfsc STATUS, Z #define IF_NOT_ZERO btfss STATUS, Z #define IF_SEQ_RUNNING btfsc flags, SEQ_RUNNING_FLAG #define IF_SEQ_NOT_RUNNING btfss flags, SEQ_RUNNING_FLAG #define IF_BUTTON1_NOT_PRESSED btfsc BUTTONS_PORT, BUTTON1_PIN #define IF_BUTTON1_PRESSED btfss BUTTONS_PORT, BUTTON1_PIN #define IF_BUTTON2_NOT_PRESSED btfsc BUTTONS_PORT, BUTTON2_PIN #define IF_BUTTON2_PRESSED btfss BUTTONS_PORT, BUTTON2_PIN #define IF_BUTTON3_NOT_PRESSED btfsc BUTTONS_PORT, BUTTON3_PIN #define IF_BUTTON3_PRESSED btfss BUTTONS_PORT, BUTTON3_PIN #define IF_BUTTON4_NOT_PRESSED btfsc BUTTONS_PORT, BUTTON4_PIN #define IF_BUTTON4_PRESSED btfss BUTTONS_PORT, BUTTON4_PIN DEBOUNCE_BUTTONS macro mask movlw mask ifndef DEBUG call debounce_buttons endif endm UDATA command RES 1 button_pressed RES 1 counter RES 1 tmp RES 1 flags RES 1 led_board_value RES 1 timer_counter RES 1 RESET_VECTOR CODE RESET_VECTOR_LOCATION goto start MAIN CODE start INIT bsf T1CON, T1CKPS1 ; Set timer1 prescaler to 1:8 bsf T1CON, T1CKPS0 ; " clrf flags clrf led_board_value call update_led_board bsf STATUS, RP0 ; OPTION_REG, SPBRG, and TXSTA are in upper memory bcf OPTION_REG, INTEDG ; Use falling edge for INT0 interrupt movlw 103 ; Setting baud rate; see manual movwf SPBRG ; to get the proper value bsf TXSTA, BRGH ; High speed bsf TXSTA, TXEN ; Enable transmit bcf STATUS, RP0 ; Back to lower memory bsf RCSTA, SPEN ; Enable serial port ; If the master switch is on at the start, ; set the standby pin IF_MASTER_OFF goto main_loop TURN_ON_STANDBY bsf flags, LASER_ON_FLAG main_loop IF_MASTER_OFF goto master_switch_off ; ; Check the laser, but only if the sequence isn't already running. ; btfsc flags, SEQ_RUNNING_FLAG goto test_buttons IF_LASER_TRIPPED goto laser_tripped test_buttons comf BUTTONS_PORT, w ; Read in the status of the panel buttons andlw BUTTONS_MASK ; Get rid of the bits we don't care about IF_ZERO ; If it is zero, no buttons are pressed goto main_loop ; so keep polling DEBOUNCE_BUTTONS BUTTONS_MASK IF_BUTTON1_PRESSED goto button1_pressed IF_BUTTON2_PRESSED goto button2_pressed IF_BUTTON3_PRESSED goto button3_pressed IF_BUTTON4_PRESSED goto button4_pressed goto main_loop master_switch_off IF_SEQ_NOT_RUNNING goto power_down movlw ALL_STOP_CMD movwf command call send_command ; Send the "all stop" command DELAY250MS 2 ; Give the other boards time to receive power_down bcf flags, SEQ_RUNNING_FLAG bcf flags, LASER_ON_FLAG TURN_OFF_STANDBY clrf led_board_value call update_led_board bcf INTCON, INTF bsf INTCON, INTE sleep nop ; Disable the interrupts bcf INTCON, INTE bcf INTCON, INTF DEBOUNCE_BUTTONS 1 << MASTER_SWITCH_PIN ; Double-check that the master is on IF_MASTER_OFF goto master_switch_off TURN_ON_STANDBY bsf flags, LASER_ON_FLAG goto main_loop ; Note: this code is also used by button1_pressed. laser_tripped bsf flags, SEQ_RUNNING_FLAG bcf flags, LASER_ON_FLAG movlw ALL_RUN_CMD movwf command call send_command goto main_loop button1_pressed DEBOUNCE_BUTTONS 1 << BUTTON1_PIN IF_BUTTON1_NOT_PRESSED ; Double-check button press goto main_loop ; It's not pressed anymore, so ignore it call wait_for_unpress DEBOUNCE_BUTTONS 1 << BUTTON1_PIN btfss flags, SEQ_RUNNING_FLAG goto laser_tripped bcf flags, SEQ_RUNNING_FLAG ; Stop sequence bsf flags, LASER_ON_FLAG movlw ALL_STOP_CMD movwf command call send_command goto main_loop ; Don't have a use for this button yet, so it doesn't do anything ; at the moment. button2_pressed DEBOUNCE_BUTTONS 1 << BUTTON2_PIN IF_BUTTON2_NOT_PRESSED ; Double-check button press goto main_loop ; It's not pressed anymore, so ignore it call wait_for_unpress goto main_loop button3_pressed DEBOUNCE_BUTTONS 1 << BUTTON3_PIN IF_BUTTON3_NOT_PRESSED ; Double-check button press goto main_loop ; It's not pressed anymore, so ignore it ; ; If button 3 is pressed while the sequence is running, ; send a reset and rerun command. ; If the sequence is not running, we toggle the state ; of the laser. ; btfsc flags, SEQ_RUNNING_FLAG goto reset_and_run movlw LASER_OFF_CMD btfss flags, LASER_ON_FLAG ; If the laser is off, movlw LASER_ON_CMD ; turn it on movwf command call send_command movlw 1 << LASER_ON_FLAG ; Toggle the laser on flag xorwf flags, f ; " call wait_for_unpress ; Wait for the user to release the button goto main_loop reset_and_run movlw ALL_RESET_RUN_CMD movwf command call send_command call wait_for_unpress goto main_loop button4_pressed DEBOUNCE_BUTTONS 1 << BUTTON4_PIN IF_BUTTON4_NOT_PRESSED ; Double-check button press goto main_loop ; It's not pressed anymore, so ignore it DELAY250MS 1 TURN_ON_LED call wait_for_unpress ; Get first part of destination call get_key IF_ZERO goto bad_button_command movwf command decf command, f bcf STATUS, C rlf command, f rlf command, f swapf command, f ; ; Get second part of destination ; call get_key IF_ZERO goto bad_button_command movwf tmp decf tmp, f swapf tmp, w iorwf command, f ; ; Get first part of command ; call get_key IF_ZERO goto bad_button_command movwf tmp decf tmp, f bcf STATUS, C rlf tmp, f rlf tmp, w iorwf command, f call get_key IF_ZERO goto bad_button_command addlw 255 ; Subtract 1 iorwf command, f TURN_OFF_LED ; Process the command here call send_command goto main_loop bad_button_command movlw 5 call flash_led goto main_loop ; ; Flashes the panel LED W times ; flash_led movwf counter flash_led_loop TURN_ON_LED DELAY250MS 1 TURN_OFF_LED DELAY250MS 1 decfsz counter, f goto flash_led_loop return ; ; This routine waits up to about 5 seconds for the user to press a button ; on the control panel. The 5 seconds is accomplished by using timer1. ; The prescaler is set to 1:8, and the entire 16 bits of the timer ; is used, which gives 65536 * 8 = 524,288ms. To bring it up to 5 seconds, ; it is wrapped in a loop that iterates 10 times. ; If the timeout occurs, the function returns 0 in W. Otherwise, it ; returns the number of the button pressed (1 through 4) in W. ; Timer1 is turned on at the beginning of this function, and is turned ; off when done. ; get_key movlw 11 ; We decrement at the top of the loop, movwf timer_counter ; so we need 11 instead of 10. get_key_outer_loop clrf TMR1L ; Clear timer1 clrf TMR1H ; " bcf PIR1, TMR1IF ; Clear timer1's interrupt flag bsf T1CON, TMR1ON ; Turn on timer1 decfsz timer_counter, f ; See if we've looped enough times goto get_key_loop ; We haven't bcf T1CON, TMR1ON ; We're done, so turn off timer1 retlw 0 ; we've timed out, so return 0 get_key_loop btfsc PIR1, TMR1IF ; If timer1's interrupt flag is set, goto get_key_outer_loop comf BUTTONS_PORT, w ; Read in the status of the panel buttons andlw BUTTONS_MASK ; Get rid of the bits we don't care about IF_ZERO ; If it is zero, no buttons are pressed goto get_key_loop ; so keep polling DEBOUNCE_BUTTONS BUTTONS_MASK clrw ; W will contain the button pressed IF_BUTTON1_PRESSED movlw 1 IF_BUTTON2_PRESSED movlw 2 IF_BUTTON3_PRESSED movlw 3 IF_BUTTON4_PRESSED movlw 4 movwf button_pressed movf button_pressed, w ; Make sure a button was pressed IF_ZERO ; If not, go back to polling. goto get_key_loop bcf T1CON, TMR1ON ; Turn off timer1 TURN_OFF_LED ; Tell the user the button was pressed DELAY250MS 1 TURN_ON_LED ; Fall through to wait_for_unpress ; ; Note: This is part of the current function here, and it is ; used as a function from several other sections of code. ; Be careful with any changes here. ; wait_for_unpress ; Wait for the user to release the button comf BUTTONS_PORT, w andlw BUTTONS_MASK IF_NOT_ZERO goto wait_for_unpress DEBOUNCE_BUTTONS BUTTONS_MASK comf BUTTONS_PORT, w andlw BUTTONS_MASK IF_NOT_ZERO goto wait_for_unpress movf button_pressed, w return ; ; Writes to the LED board using DATA_PIN and CLOCK_PIN. ; update_led_board ifndef DEBUG movlw 1 << DATA_PIN movwf ci8a_data_pin_mask movlw 1 << CLOCK_PIN movwf ci8a_clock_pin_mask movf led_board_value, w call clock_in_8_a bsf PORTA, CLOCK_PIN ; Clock one more time to update the latch bcf PORTA, CLOCK_PIN ; '' endif return ; ; Sends the value in using the UART ; send_command movf command, w ; Load the value to send into W ifndef DEBUG btfss PIR1, TXIF ; Wait for the transmitter to have room goto $ - 1 endif movwf TXREG ; Move the value to the transmit buffer movf command, w ; Send the command to the LED board movwf led_board_value ; for testing purposes. call update_led_board DELAY250MS 1 movf command, w ; Resend the command iorlw RETRANSMIT_FLAG ; Add the retransmission flag to the command ifndef DEBUG btfss PIR1, TXIF ; Wait for the transmitter to have room goto $ - 1 endif movwf TXREG ; Move the value to the transmit buffer return end