M a r k   C r o s b i e

You are in: Serial I/O with a PIC  Home  Resume  Linux  LEGO  Electronics  Movies  Mac OS X 

Development LED flasher Square wave generator Timed delay library Serial I/O Stepper motor driver Motor Mind B LCD library
Contact Mark at mark@mastincrosbie.com

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
;    0    Input   Serial
;    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	equ	0	; Bit 0 on PORTA
LED_BIT		equ	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        equ 0x34
DELAY_50MS_LO        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.

	btfsc   PORTA, SERIAL_BIT  
		; Get PORTA with a 'POSITIVE' serial bit on it
	retlw   0           ; Do nothing if we got 5V, or logic '0'

	movf    MASK, W
	iorwf   INCHAR, f      ; SET the bit
	retlw   0

;                                                          *
; Specific Delay Functions.  These are specific delays     *
; which load specific delay counts.  These routines then   *
; call the generic DELAY subroutine.                       *
;                                                          *

	movwf   DELAYHI
	movwf   DELAYLO
	retlw   0

	; Baud rate is 9600, so set up accorsingly
	movwf   DELAYHI
	movwf   DELAYLO
	retlw   0

	movlw   DELAY_50MS_HI
	movwf   DELAYHI
	movlw   DELAY_50MS_LO
	movwf   DELAYLO
	retlw   0

	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
	decfsz  COUNTLO
	goto    DelayLoop
	decfsz  COUNTHI
	goto    DelaySetup2
	goto    DelayDone
	movf    DELAYLO, W
	movwf   COUNTLO
	goto    DelayLoop
	retlw   0

; ***** Main Program Entry !!!
	clrf    PORTA
	clrf    PORTB

	; Set up input and output bits
	bsf	STATUS, RP0
	bsf	TRISA, SERIAL_BIT	; PORTA.0 is input
	bcf	TRISB, LED_BIT		; PORTB.4 is output
	bcf	STATUS, RP0

	call    Delay50MS    ; Wait for tristate to settle...?...
	call    Delay

	;** Set up Baud Constants
	movlw   DELAY_FULL_9600_HI
	movlw   DELAY_FULL_9600_LO
	movlw   DELAY_HALF_9600_HI
	movlw   DELAY_HALF_9600_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!

	call    DelayHalfSerial   ; Delay half a bit
	call    Delay
	btfss   PORTA, SERIAL_BIT     ; Serial bit *should* be zero.
	goto    WaitChar    ; False alarm..

	; Delay one bit time
	call    DelayFullSerial
	call    Delay
	call    GetSerialBit  ; Get Bit into INCHAR

	bcf	STATUS, C	; Clear the carry bit
	rlf	MASK, F		; shift mask left one bit, thru carry
	btfss	STATUS, C	; is the carry set?
	goto BitLoop

	; 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


PIC Microcontroller Project Book
Lot's of great PIC project ideas!

Programming and Customizing the Pic Microcontroller

If you are learning to program microcontrollers then Myke's book is good start.
The Art of Electronics
A classic in the field. Teaches you the art and science of linear and digital electronic design. If you want to learn why your circuit is not working, read this book and you'll know why.
Mobile Robots: Inspiration to Implementation
A very readable introduction to the art of robotic design and implementation from the best practioners in the field: the MIT Artificial Intelligence Lab!
Microcontroller Cookbook
A set of cookbook style designs for the 8051 and PIC microcontrollers. A handy reference to have if you need a quick solution to a problem.

© 2002-2004 Mark Crosbie   shareright © 2002 Phlash