Serial I/O with a PIC I was working on a project to control a stepper motor from a PC serial port. I needed to receive characters from the serial port, and change the motor actions based on the character received. The sample code shown here demonstrates how to receive characters from a serial line. The source code is shown below. Alternatively, you can click here to save the source onto your system, or download the hex code to download directly into the PIC. It is based on source from Tom Coonan that I simplified by restricting it to 9600 baud, 8-N-1 settings. If the digit 1 is received then it turns on an LED connected to pin RB4, if a digit 0 is received, it turns off the LED. The LED is driven via a 1K resistor. There is a 10K resistor between pin 3 of the serial plug and pin RA0 on the PIC to reduce the current into the pin. Connect a serial line to your computer, and wire the TX line of the serial cable to pin RA0, via a resistor. Start up a terminal program on the PC and set the terminal settings to 9600 baud 8-N-1. On Windows you will have to choose a COM port that corresponds to your computer's serial device. On Linux, I ran minicom configured to use port /dev/ttyS0, which corresponds to COM1 in Windows. ;
; led-ctrl.asm ; ; Accept serial data at 9600 8N1 and interpret it ; If a '1' is received, turn on an LED connected to RB4 ; If a '0' is received, turn off the LED connected to RB4 ; ; Serial TX line is connected via a 10K resistor to RA0 ; ; PORT MAP ; ; PORTA ; 0 Input Serial ; ; PORTB ; 4 Output LED ; ; Based on code by Tom Coonan ; ; Copyright 1998-2003 Mark Crosbie 9/29/98 ; include p16c84.inc
radix dec processor 16c84 SERIAL_BIT LED_BIT
equ
0 equ
; Bit 0 on PORTA 4 ; Bit 4 on PORTB
;**** Application's Memory Variables INCHAR equ 0xc ; Input character COUNTHI equ 0x10 ; Counter for Delay routine (MSB) COUNTLO equ 0x11 ; Counter for Delay routine (LSB) DELAYHI equ 0x12 ; Argument for DELAY subroutine (MSB) DELAYLO equ 0x13 ; Argument for DELAY subroutine (LSB) MASK equ 0x14 ; Mask register for each serial bit input TEMP equ 0x15 ; ; After the baud rates are established through the jumpers, the right ; delay constants are written into here. These are the constants ; used in all the run-time delays for serial. ; DELAY_HALF_SERIAL_HI equ 20 ; DELAY_HALF_SERIAL_LO equ 21 ; DELAY_FULL_SERIAL_HI equ 22 ; DELAY_FULL_SERIAL_LO equ 23 ; ;*** End MEMORY Registers ********************************** ; DELAY Constants. ; ; Delays were set emperically - using a Logic Analyzer and ; looking at peek signals for bit timing. ASSUMES the ; current design's 4MHz crystal, of course. DELAY_FULL_9600_HI equ 1 DELAY_FULL_9600_LO equ 25 DELAY_HALF_9600_HI equ 1 DELAY_HALF_9600_LO equ 12 DELAY_50MS_HI DELAY_50MS_LO
equ 0x34 equ 0x95
org 0 ; Reset Vector goto Start ; Main entry point of program ;**************************************** ; *
; Start Up Initializtion Stuff * ; * ;**************************************** ; Remember, INCHAR started out as all zeros.. ; Therefore, we only need to SET bits (when PORTA:0 = 0V) ; Also, remember, serial is inverted: ; 0V on PORTA:0 indicates a logic '1' ; 5V on PORTA:0 indicates a logic '0' ; ; The MASK variable is maintained by caller. ; ; GetSerialBit nop nop btfsc PORTA, SERIAL_BIT ; Get PORTA with a 'POSITIVE' serial bit on it retlw 0 ; Do nothing if we got 5V, or logic '0' GetSerialBitIsClr movf MASK, W iorwf INCHAR, f retlw 0
; SET the bit
;*********************************************************** ; * ; Specific Delay Functions. These are specific delays * ; which load specific delay counts. These routines then * ; call the generic DELAY subroutine. * ; * ;*********************************************************** DelayHalfSerial movf DELAY_HALF_SERIAL_HI, W movwf DELAYHI movf DELAY_HALF_SERIAL_LO, W movwf DELAYLO retlw 0 DelayFullSerial ; Baud rate is 9600, so set up accorsingly movf DELAY_FULL_SERIAL_HI, W movwf DELAYHI movf DELAY_FULL_SERIAL_LO, W movwf DELAYLO
retlw 0 Delay50MS movlw DELAY_50MS_HI movwf DELAYHI movlw DELAY_50MS_LO movwf DELAYLO retlw 0 InitDelay200MS call Delay50MS call Delay call Delay50MS call Delay call Delay50MS call Delay call Delay50MS call Delay retlw 0 ; General DELAY routine ; Inputs: ; DELAYHI - Initial Countdown value MSB ; DELAYLO - Initial Countdown value LSB ; Uses: ; COUNTHI - Counter value MSB ; COUNTLO - Counter value LSB Delay movf DELAYHI, W movwf COUNTHI movf DELAYLO, W movwf COUNTLO DelayLoop decfsz COUNTLO goto DelayLoop decfsz COUNTHI goto DelaySetup2 goto DelayDone DelaySetup2 movf DELAYLO, W movwf COUNTLO goto DelayLoop DelayDone retlw 0 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ***** Main Program Entry !!!
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Start clrf PORTA clrf PORTB ; Set up input and output bits bsf STATUS, RP0 bsf TRISA, SERIAL_BIT bcf TRISB, LED_BIT bcf STATUS, RP0 call call
Delay50MS Delay
; PORTA.0 is input ; PORTB.4 is output
; Wait for tristate to settle...?...
;** Set up Baud Constants movlw DELAY_FULL_9600_HI movwf DELAY_FULL_SERIAL_HI movlw DELAY_FULL_9600_LO movwf DELAY_FULL_SERIAL_LO movlw DELAY_HALF_9600_HI movwf DELAY_HALF_SERIAL_HI movlw DELAY_HALF_9600_LO movwf DELAY_HALF_SERIAL_LO call
InitDelay200MS
;******* START Main Loop Here ************** ; Loop and read characters WaitChar
; Wait for start bit clrf INCHAR ; clear the input character movlw B'00000001' ; start out at bit 0 movwf MASK btfss PORTA, SERIAL_BIT ; If we get a '1' or a START bit, skip! goto WaitChar ; Nope.. no start bit. Keep looking ; Got an edge! nop nop call call btfss goto
DelayHalfSerial ; Delay half a bit Delay PORTA, SERIAL_BIT ; Serial bit *should* be zero. WaitChar ; False alarm..
BitLoop ; Delay one bit time call DelayFullSerial call Delay call GetSerialBit ; Get Bit into INCHAR bcf STATUS, C rlf MASK, F btfss STATUS, C goto BitLoop
; Clear the carry bit ; shift mask left one bit, thru carry ; is the carry set?
; Delay one bit time for the STOP bit call DelayFullSerial call Delay ; See if the character is 0, if so, turn off the LED movf INCHAR, w sublw '0' btfsc STATUS, Z bcf PORTB, LED_BIT ; turn off the LED btfss STATUS, Z bsf PORTB, LED_BIT ; turn on the LED ; A small pause between each sample call Delay50MS call Delay goto WaitChar end