PIC Specific RS232 routine

12F683 9600 baud

Mike McLaren - K8LH (Westland, MI) says:

I just finished cleaning up an assembly lanugauge source file for a little 12F683 demo' program that shows one way (of probably many) to do reliable 9600 baud bit-banged serial I/O on this chip... It uses TMR2 generated 104-usec interrupts, as well as interrupt-on-change to detect the start bit, using the built-in 12F683 8-MHz INTOSC... Please feel free to use it if you think it may be useful...

	list p=12F683, b=8, c= 102, n=71, t=on, st=off, f=inhx32
;******************************************************************
;*								  *
;*  Filename: 12F683 Serial.asm					  *
;*    Author: Mike McLaren, K8LH   (k8lh@arrl.net)		  *
;*      Date: 27-Nov-04  (rev 22-Dec-04)			  *
;*								  *
;*   Test 'bit-banged' 9600 baud serial I/O Routines		  *
;*   Uses 12F683 INTOSC at 8-MHz				  *
;*								  *
;*								  *
;*								  *
;*     MPLab: 7.00    (tabs=8)					  *
;*     MPAsm: 3.90						  *
;*								  *
;******************************************************************

	#include	<p12f683.inc>
	errorlevel	-302

	__config	_FCMEN_OFF & _IESO_OFF & _BOD_OFF & _CPD_OFF & _CP_OFF & _MCLRE_OFF & _PWRTE_ON & _WDT_OFF & _INTOSC
;
; file register variables
;
PROC232	equ	0x20		;RS-232 Process Latch
TXCNT	equ	0x21		;TX-232 bit count
TXVAR	equ	0x22		;TX-232 data byte
RXCNT	equ	0x23		;RX-232 bit count
RXVAR	equ	0x24		;RX-232 data byte

;
; PROC232 flag bits
;
RXFLAG	equ	0x00		;1=rx in progress
RXCHAR	equ	0x01		;1=rx char available
TXFLAG	equ	0x02		;1=tx in progress

; other constants

RXPIN	equ	0x03		;RS-232 RX (GP3)
TXPIN	equ	0x04		;RS-232 TX (GP4)
BAUDX	equ	.208		;208 for 9600 baud
;
; file locations used by ISR for saving and restoring the stack
;
W_ISR	equ	0x70		;ISR 'W'
S_ISR	equ	0x71		;ISR 'STATUS'
P_ISR	equ	0x72		;ISR 'PCLATH'
;
; other vars
;
PTRL	equ	0x78		;PRTSTR routine
PTRH	equ	0x79		;PRTSTR routine

; Hardware notes:
;
; <1> Using INTOSC 8-MHz, 500-nsec instruction cycle
; <2> Using GP3 for 9600 baud 'bit banged' serial input
; <3> Using GP4 for 9600 baud 'bit banged' serial output
; <4> RS-232 signals inverted with MAX232 or 2N7000 drivers
; <5> Using 2700 ohm pull-up resistor on GP3

;
;  This program simply prints a text string to Hyperterminal 
;  and echos typed characters back to Hyperterminal...
;
;  Setup Hyperterminal for 9600, 8, 1, none...  Use a MAX232 or
;  similar level shifting circuit (I use a pair of 2N7000s) for
;  connection between the 12F683 and PC...  
;

;******************************************************************
;*                                                             	  *
;*                                                                *
;*                                                                *
;*                                                                *
;*                                                                *
;******************************************************************

	org	0x0000

START	clrf	STATUS		;				  |B0
	movlw	b'00010000'	;tx pin in 'stop' condition	  |B0
	movwf	GPIO		;setup GPIO latch		  |B0
	goto	MAIN		;				  |B0

;******************************************************************
;*                                                             	  *
;*    Interrupt Service Routine	for 'bit-banged' Serial I/O	  *
;*                                                                *
;*    Interrupts are generated every 104-us by TMR2 for 9600	  *
;*    baud shift operations and by IOC (interrupt on change)	  *
;*    on the RX start bit leading edge on RX pin GP3...		  *
;*								  *
;*    On detecting the RX start bit leading edge, GP3 IOC is	  *
;*    turned off, RXFLAG is set to indicate rx-in-progress,	  *
;*    RXCNT bit count variable is set, and TMR2 is advanced	  *
;*    by 1/2 bit (52-usec)...  Note - advancing TMR2 could	  *
;*    potentially mess up a tx-in-progress, so, don't send &	  *
;*    receive at the same time...				  *
;*                                                                *
;******************************************************************

	org	0x0004
;
; save W and STATUS registers on entry
;
ISR	movwf	W_ISR		;save W-reg			  |B?
	swapf	STATUS,W	;doesn't change STATUS bits	  |B?
	movwf	S_ISR		;save STATUS reg		  |B?
	clrf	STATUS		;bank 0				  |B0
	movf	PCLATH,W	;get PCLATH			  |B0
	movwf	P_ISR		;save PCLATH			  |B0
;
; 27-Nov-04, test for GP3 (RXPIN) start bit leading edge
;
	btfss	INTCON,GPIF	;IOC "start bit" interrupt?	  |B0
	goto	ISR_TX		;no, branch			  |B0
	movf	GPIO,W		;yes, read GPIO			  |B0
	bcf	INTCON,GPIF	;clear IOC interrupt flag	  |B0
	bsf	PROC232,RXFLAG	;indicate RX in progress	  |B0
	movlw	d'10'		;10 bits (start + 8 data + stop)  |B0
	movwf	RXCNT		;initialize RX bit counter	  |B0
	movlw	BAUDX/2		;				  |B0
	movwf	TMR2		;inc TMR2 by 1/2 bit (52-usec)	  |B0
	goto	ISR_X		;turn off GP3 IOC & exit	  |B0

;
; 23-Nov-04, code to test bit-banged serial output...
; RS-232 code processed each 104-usec interrupt for 9,600 baud...
;
ISR_TX	btfss	PROC232,TXFLAG	;TX in progress?		  |B0
	goto	ISR_RX		;no, branch			  |B0
	movf	TXCNT,W		;get TX bit count		  |B0
	xorlw	b'00001011'	;start bit?			  |B0
	btfsc	STATUS,Z	;no, branch			  |B0
	goto	ISR_TX1		;yes, send start bit		  |B0
	rrf	TXVAR,f		;shift lsb into C		  |B0
	bsf	TXVAR,7		;set bit 7 for stop bits	  |B0
	btfsc	STATUS,C	;skip if C=0			  |B0
	bsf	GPIO,TXPIN	;set TX pin			  |B0
	btfss	STATUS,C	;skip if C=1			  |B0
ISR_TX1	bcf	GPIO,TXPIN	;clear TX pin			  |B0
	decf	TXCNT,f		;decrement bit counter		  |B0
	btfsc	STATUS,Z	;last bit?			  |B0
	bcf	PROC232,TXFLAG	;yes, indicate end of TX	  |B0
;
; 25-Nov-04, code to test bit-banged serial input...
;
ISR_RX	btfss	PROC232,RXFLAG	;RX in progress?		  |B0
	goto	ISR_XIT		;no, branch			  |B0
	bcf	STATUS,C	;assume bit=0			  |B0
	btfsc	GPIO,RXPIN	;is it a 0?			  |B0
	bsf	STATUS,C	;no, bit=1			  |B0
	rrf	RXVAR,f		;shift bit into RXVAR		  |B0
	decfsz	RXCNT,f		;all 10 bits?			  |B0
	goto	ISR_XIT		;no, branch			  |B0
	rlf	RXVAR,f		;yes, get rid of the stop bit	  |B0
	bcf	PROC232,RXFLAG	;clear RX-in-progress flag	  |B0
	bsf	PROC232,RXCHAR	;indicate character available	  |B0
ISR_X	bsf	STATUS,RP0	;select bank 1			  |B1
	movlw	b'00001000'	;mask for bit 3			  |B1
	xorwf	IOC,f		;toggle GP3 IOC			  |B1
	bcf	STATUS,RP0	;select bank 0			  |B0
;
ISR_XIT	bcf	PIR1,TMR2IF	;clear TMR2 irq flag		  |B0
	movf	P_ISR,W		;				  |B0
	movwf	PCLATH		;restore PCLATH			  |B0
	swapf	S_ISR,W		;				  |B0
	movwf	STATUS		;restore STATUS			  |B?
	swapf	W_ISR,f		;don't screw up STATUS		  |B?
	swapf	W_ISR,W		;restore W-reg			  |B?
	retfie			;return from interrupt		  |B?
;
;******************************************************************

















;******************************************************************
;
; Initialization
;
MAIN	movlw	b'00000111'	;				  |B0
	movwf	CMCON0		;digital I/O			  |B0
	bsf	STATUS,RP0	;bank 1				  |B1
	clrf	ANSEL		;digital I/O			  |B1
	movlw	b'01110001'	;				  |B1
	movwf	OSCCON		;8-mhz INTOSC system clock	  |B1
	movlw	b'00001000'	;GP3 input, all others output	  |B1
	movwf	TRISIO		;				  |B1
;
; 27-Nov-04, code for interrupt-on-change on GP3 (RXPIN) to detect
; RS-232 RX start bit
;
	movlw	b'00001000'	;				  |B1
	movwf	IOC		;set IOC for GP3 input		  |B1
;
; wait for INTOSC to become stable before doing anything else
;
STABLE	btfss	OSCCON,HTS	;oscillator stable?		  |B1
	goto	STABLE		;no, branch			  |B1
	bcf	STATUS,RP0	;bank 0				  |B0
;
;  clear RS-232 flags before turning on interrupts
;
	clrf	PROC232		;clear process latch variable	  |B0
;
; configure TIMER2 for 104-usec interrupts (8-MHz clock)...
;
	clrf	TMR2		;clear TMR2 register		  |B0
	bsf	STATUS,RP0	;select Bank 1			  |B1
	bsf	INTCON,PEIE	;enable peripheral irqs		  |B1
	clrf	PIE1		;mask all peripheral irqs	  |B1
	bsf	PIE1,TMR2IE	;except for TMR2 irqs		  |B1
	bcf	STATUS,RP0	;select Bank 0			  |B0
	clrf	PIR1		;clear peripheral irq flags	  |B0
	movlw	b'00000000'	;pre 1, post 1:1		  |B0
	movwf	T2CON		;for 500-nsec ticks (8-mhz clock) |B0
	bsf	STATUS,RP0	;select Bank 1			  |B1
	movlw	BAUDX-1		;number of 500-nsec ticks	  |B1
	movwf	PR2		;104-usec interrupts		  |B1
	bcf	STATUS,RP0	;select Bank 0			  |B0
	bsf	INTCON,GIE	;enable global irqs		  |B0
	bsf	INTCON,GPIE	;enable interrupt-on-change irqs  |B0
	bsf	T2CON,TMR2ON	;start TMR2			  |B0

;******************************************************************
;
; Print a string
;
	movlw	low GREET	;Greeting string address lo	  |B0
	movwf	PTRL		;				  |B0
	movlw	high GREET	;Greeting string address hi	  |B0
	movwf	PTRH		;				  |B0
	call	PRTSTR		;print greeting string		  |B0
;
; Echo characters coming from Hyperterminal...
;
TEST	call	RX232		;receive character		  |B0
	call	TX232		;echo character			  |B0
	goto	TEST		;				  |B0

;******************************************************************
;******************************************************************
;
; TX232 - enter with character to be sent in W
;
TX232	btfsc	PROC232,TXFLAG	;TX in progress?		  |B0
	goto	TX232		;yes, branch and wait		  |B0
	movwf	TXVAR		;stuff character		  |B0
	movlw	b'00001011'	;				  |B0
	movwf	TXCNT		;set TX bit count		  |B0
	bsf	PROC232,TXFLAG	;initiate TX			  |B0
	return			;				  |B0

;******************************************************************
;
; RX232 - exit with received character in W
;
RX232	btfss	PROC232,RXCHAR	;character available flag?	  |B0
	goto	RX232		;no, loop			  |B0
	movf	RXVAR,W		;yes, get character		  |B0
	bcf	PROC232,RXCHAR	;clear flag			  |B0
	return			;				  |B0

;******************************************************************
;
PRTSTR	call	GETSTR		;Get a string character		  |B0
	andlw	b'11111111'	;				  |B0
	btfsc	STATUS,Z	;Last character?		  |B0
	return			;Yes, return			  |B0
	call	TX232		;Output char			  |B0
	incfsz	PTRL,F		;Increment pointer		  |B0
	goto	PRTSTR		;				  |B0
	incf	PTRH,F		;				  |B0
	goto	PRTSTR		;				  |B0

GETSTR	movf	PTRH,W		;				  |B0
	movwf	PCLATH		;				  |B0
	movf	PTRL,W		;				  |B0
	movwf	PCL		;				  |B0

GREET	dt	0x1b, 0x5b, a'H'		;home cursor
	dt	0x1b, 0x5b, a'J'		;clear screen
	dt	"K8LH 12F683 Serial Test Code v1.0"
	dt	0x0d, 0x0a, 0x0a, 0x00

	end


Questions: