;;; SIMPLE232.ASM ;;; Raffi Krikorian ;;; December 8, 2002 ;;; ;;; This code is meant for the PIC16F876 microcontroller -- it ;;; interfaces with the MAX202 transceiver to convert the TTL levels ;;; to the RS-232 levels needed to talk to the serial port. ALl this ;;; code needs to do is to turn on the serial port and setup the ;;; buttons and the LEDs to do some status output. ;;; ;;; When it receives a byte from the serial port, it will toggle the ;;; LEDs. When the button is pressed, then it will send out the ;;; string "fab class" to the serial port. ;;; LIST p = p16f876 #include p16f876.inc __CONFIG _HS_OSC & _WDT_OFF & _PWRTE_ON & _BODEN_OFF & _CP_OFF & _DP_OFF ;;; ;;; CONSTANTS ;;; ;;; the pins on PORTC that the LEDs are on PIN_LED0 EQU 0 PIN_LED1 EQU 1 ;;; the pin on PORTB that the button is on PIN_BUTTON EQU 0 ;;; the memory location that we are going to use to keep track of ;;; button presses -- we are storing it in bit BUTTON_DOWN BUTTON_STATE EQU 0x20 BUTTON_DOWN EQU 0 ;;; ;;; MACROS ;;; ;;; a simple inline macro that will create a binary number with the ;;; specified bit set. for example, a BIT(1) becomes b'00000010' and ;;; BIT(8) | BIT(0) evals to b'10000001' #define BIT(i) (1<<(i)) ;;; define a macro that will move whatever is in memory location src ;;; to memory location dest. it will do it via the W register, so if ;;; there is something in W, it will be destroyed by this macro MVL MACRO src, dest MOVF src, W MOVWF dest ENDM ;;; define another macro that will take the parameter, load it into W ;;; and then call tx_send with that value. this will cause the ;;; parameter of this macro to be sent out the serial port. be ;;; careful if there is something in W as it will be clobbered. TXL MACRO l MOVLW l CALL tx_send ENDM ;;; ;;; PROGRAM CODE ;;; ORG 0x0000 ; when the PIC starts up, jump to the GOTO init ; init block ORG 0x0004 ; when an interrupt is called, then RETFIE ; simply return from it ;;; do the initialisation of all the ports, setup the buttons, LEDs ;;; and the serial port init CLRF STATUS ; clear the status register, and then CLRF PORTB ; clear all the ports that we need to CLRF PORTC ; use (PORTB and PORTC) BSF STATUS, RP0 ; switch to bank 1, and setup PORTB to MVL BIT(PIN_BUTTON), TRISB; have an input on RB0 where the BCF OPTION_REG, RBPU; button is. also turn on the weak ; pull-ups to detect the button press. MVL BIT(7), TRISC ; make RC7 an input for serial port ; and make the rest be outputs for LED ;; setup the USART. since we are using a 20MHz clock, we need ;; load SPBRG with d'32' to configure a 38.4kpbs output speed. ;; (SPBRG stands for serial port baud rate generator) MVL d'32', SPBRG ; set the baud rate to be 38.4kbps, MVL BIT(TXEN) | BIT(BRGH), TXSTA ; turn on transmitter ; into high speed mode and then switch BCF STATUS, RP0 ; back into bank 0 to turn the serial MVL BIT(SPEN) | BIT(CREN), RCSTA ; port on to receive ;; make sure to initialise the memory location CLRF BUTTON_STATE ;; now turn on both of the LEDs BSF PORTC, PIN_LED0 BSF PORTC, PIN_LED1 ;;; main loop that the program is going to sit in. at every iteration ;;; through this loop we are going to test to see if there are any ;;; bytes that we should read from the serial port, and then we are ;;; going to see if the state of the button changed. main BTFSC RCSTA, OERR ; if there is an overflow error, or an GOTO rx_reset ; framing error, then we have to reset BTFSC RCSTA, FERR ; the serial port GOTO rx_reset BTFSC PIR1, RCIF ; but if we do receive something valid GOTO rx_handler ; go jump to a handler for the byte _main_after_rx BTFSS PORTB, PIN_BUTTON ; if the button is pressed down, GOTO button_handler ; then go to a button handler. if it BCF BUTTON_STATE, BUTTON_DOWN ; up (PORTB, PIN_BUTTON ; will be high), then clear the flag ; in memory that says the button is ; down _main_after_button ;; and restart the fun GOTO main ;;; a function that is called whenever a proper non-error byte is ;;; received in the serial port. since we are not evaluating what ;;; byte is coming in, we are just going to flip the second LED, and ;;; the first LED is going dark when the ASCII code of the incoming ;;; byte is even, and bright when odd. rx_handler ;; the incoming byte is going to be in RCREG, and the ASCII ;; code is odd if the last bit is 1, and even if the last ;; but is 0. we have to test and set instead of simply moving ;; a value into PORTC because we don't want to clobber the ;; TX bin if something is being transmitted. BTFSC RCREG, 0 GOTO _rx_handler_odd _rx_handler_even BCF PORTC, PIN_LED0 GOTO _rx_handler_byte_done _rx_handler_odd BSF PORTC, PIN_LED0 _rx_handler_byte_done ;; now test the value of the LED1, and flip it BTFSC PORTC, PIN_LED1 GOTO _rx_handler_flip_off _rx_handler_flip_on BSF PORTC, PIN_LED1 GOTO _rx_handler_flip_done _rx_handler_flip_off BCF PORTC, PIN_LED1 ;; return to the main loop after the rx section GOTO _main_after_rx ;;; a function that just turns off the receive portion of the serial ;;; port and then turns it back on. this needs to be toggled whenever ;;; there is an error to reset everything rx_reset BCF RCSTA, CREN ; turn off the constant receive then BSF RCSTA, CREN ; turn it back on ;; return to the main loop after the rx section GOTO _main_after_rx ;;; a function that is called when the button is pressed down. this ;;; will make sure that we do something only once per button press. ;;; the reason we have to jump through these loops is because at the ;;; speed a person might push the button, we might go through the main ;;; loop more than one time. we have to guard ourselves against this. button_handler ;; we are only going to send text out the serial port if the ;; BUTTON_STATE variable is cleared. the first time that we ;; go through this block and after we send the serial port, ;; we are going to set the BUTTON_STATE to make sure that we ;; don't resend through this BTFSC BUTTON_STATE, BUTTON_DOWN ; deal with the button state GOTO _main_after_button ; to make sure that we don't BSF BUTTON_STATE, BUTTON_DOWN ; retrigger this code ;; ok -- the button has been pressed -- time to send the ;; string out the serial port. we have to send ASCII codes ;; out. TXL d'102' ; f TXL d'97' ; a TXL d'98' ; b TXL d'32' ; TXL d'99' ; c TXL d'108' ; l TXL d'97' ; a TXL d'115' ; s TXL d'115' ; s TXL d'13' ; send a carriage return and a line TXL d'10' ; feed to move to the next line GOTO _main_after_button ;;; a function that will send out whatever is in W out through the ;;; serial port tx_send BTFSS PIR1, TXIF ; make sure the transmit buffer is GOTO tx_send ; empty and transmitted before sending MOVWF TXREG ; now move whatever in W to TXREG ; to send out RETURN ;;; the end of the program END