;***************************************************************************
;                                                                     
;	RS232 Software Interface for PIC 16XXX
;	======================================
;
;	written by Peter Luethi, 31.01.1999, Dietikon, Switzerland
;	http://www.electronic-engineering.ch
;	last update: 2010/05/04
;
;	V1.03:	Added constants for selected delay values, clean-up and
;			minor improvements (2010/05/04)
;	V1.02:	Changed error handling to dedicated ISR termination label 
;			_ISR_RS232error required at the end of the ISR (11.04.2004)
;	V1.01:	Clean-up and improvements (30.12.2000)
;	V1.00:	Initial release (31.01.1999)
;
;	This code and accompanying files may be distributed freely and
;	modified, provided this header with my name and this notice remain
;	intact. Ownership rights remain with me.
;	You may not sell this software without my approval.
;
;	This software comes with no guarantee or warranty except for my
;	good intentions. By using this code you agree to indemnify me from
;	any liability that might arise from its use.
;
;	
;	SPECIFICATIONS
;	==============
;	Processor:			Microchip PIC 16F84
;	Clock:				4.00 MHz XT
;	Throughput:			1 MIPS
;	Serial Rate:		9600 baud, 8 bit, no parity, 1 stopbit
;	Required Hardware:	RS232 level shifter (MAX232)
;	
;
;	DESCRIPTION
;	===========
;	Developed and tested on PIC 16F84, executeable on all interrupt
;	featured PICs.
;	Program handles all aspects of
;		Transmission (Register TXD) and
;		Reception (Register RXD) through interrupt.
;
;	104 us delay for each bit @ 9600 baud
;
;	Call of implemented procedures with:
;		"RS232init"		initialization
;		"SEND	'c'"	sends character 'c'
;		"SENDw"			sends content of working register
;		"RECEIVE"		macro in ISR: receive from RS232, store in RXD
;
;
;	DECLARATIONS needed in MAIN PROGRAM
;	===================================
;	ORG     0x00			; processor reset vector
;  	goto    MAIN			; main program
;
;	ORG     0x04			; interrupt vector
;	goto	ISR				; Interrupt Service Routine (ISR)
;
;	CONSTANT BASE = 0x0C	; base address of user file registers
;	TXD	equ	BASE+?			; TX-Data register
;	RXD	equ	BASE+?			; RX-Data register
;
;	#define	TXport	PORTA,0x00	; RS232 output port, could be
;	#define	TXtris	TRISA,0x00	; any active push/pull port
;	; RS232 input port is RB0, because of its own interrupt flag
;
;	Example code snippet of main program:
;	-------------------------------------
;	FLAGreg	equ	BASE+d'7'
;	#define	RSflag	FLAGreg,0x00	; RS232 data reception flag
;
;	RSservice			; RS232 service sub-routine
;		movfw	RXD		; get received RS232 data
;		<do whatever you want...>
;		bcf	RSflag		; reset RS232 data reception flag
;		bsf	INTCON,INTE	; re-enable RB0/INT interrupt
;		RETURN
;		
;	MAIN	RS232init		; RS232 Initialization
;		clrf	FLAGreg		; initialize all flags
;
;	LOOP	btfsc	RSflag		; check RS232 data reception flag
;		call	RSservice	; if set, call RS232 echo & LCD display routine
;		goto	LOOP
;
;		END
;
;	Example code snippet of ISR (to be implemented in main program):
;	----------------------------------------------------------------
;	;***** INTERRUPT SERVICE ROUTINE *****
;	ISR	<... context save ...>
;
;		;*** determine origin of interrupt ***
;		btfsc	INTCON,INTF	; check for RB0/INT interrupt
;		goto	_ISR_RS232	; if set, there was a keypad stroke
;	
;		<... check other sources, if any ...>
;
;		; catch-all
;		goto	ISRend		; unexpected IRQ, terminate execution of ISR
;	
;		;*** RS232 DATA ACQUISITION ***
;	_ISR_RS232
;		; first, disable interrupt source
;		bcf	INTCON,INTE	; disable RB0/INT interrupt
;		; second, acquire RS232 data
;		RECEIVE			; macro of RS232 software reception
;		bsf	RSflag		; enable RS232 data reception flag
;		goto	_ISR_RS232end	; terminate RS232 ISR properly
;
;		<... other ISR sources' handling section ...>
;
;		;*** ISR Termination ***
;		; NOTE: Below, I only clear the interrupt flags! This does not
;		; necessarily mean, that the interrupts are already re-enabled.
;		; Basically, interrupt re-enabling is carried out at the end of
;		; the corresponding service routine in normal operation mode.
;		; The flag responsible for the current ISR call has to be cleared
;		; to prevent recursive ISR calls. Other interrupt flags, activated
;		; during execution of this ISR, will immediately be served upon
;		; termination of the current ISR run.
;	_ISR_RS232error
;		bsf	INTCON,INTE	; after error, re-enable IRQ already here
;	_ISR_RS232end
;		bcf	INTCON,INTF	; clear RB0/INT interrupt flag
;		goto	ISRend		; terminate execution of ISR
;
;		<... other ISR sources' termination ...>
;
;	ISRend	<... context restore ...> ; general ISR context restore
;		RETFIE			; enable INTCON,GIE
;
;
;	LIMITATIONS
;	===========
;	- No parameterization for stop or parity bits
;	- Blocking busy wait for RS232 data acquisition within ISR, leaves
;	  room for improvements...
;	- No re-entrant code, i.e., cannnot be called concurrently from within
;	  multiple threads.
;
;
;	REQUIRED MEMORY
;	===============
;	2 registers: @ BASE+0 - BASE+1
;
;***************************************************************************

;***** INCLUDE FILES *****

	IFNDEF	M_BANK_ID
		ERROR "Missing include file: m_bank.asm"
	ENDIF

;***** HARDWARE DECLARATION *****

	IFNDEF	TXport
		ERROR "Define TXport in MAIN PROGRAM."
	ENDIF
	IFNDEF	TXtris
		ERROR "Define TXtris in MAIN PROGRAM."
	ENDIF
	
	MESSG "Default RS232 RXport is PORTB,0x00."

	#define	RXport	PORTB,0x00	; needs to be an interrupt supervised port
	#define	RXtris	TRISB,0x00	; if modified, set adequate flags in INTCON register
	
;***** CONSTANT DECLARATION *****

	CONSTANT NUM_BITS = d'8'		; number of data bits to transmit/receive
	CONSTANT WAIT_GENERAL = 0x1D	; delay for transmission and reception
	CONSTANT WAIT_RX_SB = 0x08		; delay for reception of start bit

	CONSTANT LF =	d'10'		; line feed
	CONSTANT CR =	d'13'		; carriage return
	CONSTANT TAB =	d'9'		; tabulator
	CONSTANT BS =	d'8'		; backspace

;***** REGISTER DECLARATION *****

	IFNDEF	BASE
	  ERROR "Declare BASE (Base address of user file registers) in MAIN PROGRAM"
	ENDIF

	TEMP1	set	BASE+d'0'	; universal temporary register
	TEMP2	set	BASE+d'1'

	IFNDEF	TXD
	  ERROR "Declare TXD register in MAIN PROGRAM"
	ENDIF
	IFNDEF	RXD
	  ERROR "Declare RXD register in MAIN PROGRAM"
	ENDIF	

;***** MACROS *****

RS232init macro
	BANK1
	bcf	TXtris		; set output
	bsf	RXtris		; set input with weak pull-up
	bcf	OPTION_REG,INTEDG ; RS232 interrupt on falling edge
	BANK0
	bsf	TXport		; set default state: logical 1
	bcf	INTCON,INTF	; ensure interrupt flag is cleared
	bsf	INTCON,INTE	; enable RB0/INT interrupt
	bsf	INTCON,GIE	; enable global interrupt
	endm

SEND	macro	S_string	; "SEND 'X'" sends character to RS232
	movlw	S_string
	call	SENDsub
	endm

SENDw	macro
	call	SENDsub
	endm

RECEIVE	macro
	call	SB_Wait		; first wait subroutine
	btfsc	RXport
	goto	_RSerror	; no valid start bit
	movlw	NUM_BITS	; ### number of data bits to receive, usually 8
	movwf	TEMP1
_RECa call	T_Wait		; inter-baud wait subroutine
	btfsc	RXport
	bsf		RXD,0x07
	btfss	RXport
	bcf		RXD,0x07
	decfsz	TEMP1,w		; skip if TEMP1 == 1
	rrf		RXD,f		; do this only NUM_BITS-1 times
	decfsz	TEMP1,f
	goto	_RECa
	call	T_Wait		; inter-baud wait subroutine
	btfss	RXport		; check if stop bit is valid
	goto	_RSerror	; no valid stop bit
	endm

;***** SUBROUTINES *****

SENDsub	movwf	TXD		; store in data register
	bcf		TXport		; start bit
	movlw	NUM_BITS	; ### number of data bits to send, usually 8
	movwf	TEMP1
	call	T_Wait
_SENDa	btfsc	TXD,0x00	; send LSB first
	bsf		TXport
	btfss	TXD,0x00
	bcf		TXport
	rrf		TXD,f
	call	T_Wait
	decfsz	TEMP1,f
	goto	_SENDa
	bsf		TXport		; stop bit
	call	T_Wait
	call	T_Wait		; allow some time for re-synchronization
	RETURN

T_Wait movlw WAIT_GENERAL	; ### for transmission & reception
	movwf	TEMP2		; total delay until next
	goto	X_Wait		; bit: 104 us @ 9600 baud

;*** When entering this subroutine, ISR context restore has already consumed some cycles ***
SB_Wait movlw WAIT_RX_SB	; ### for reception of start bit
	movwf	TEMP2		; total delay: 52 us @ 9600 baud
	;goto	X_Wait		; (=> sampling in the center of each bit)
	
X_Wait	decfsz	TEMP2,f		; wait loop
	goto	X_Wait
	RETURN

_RSerror clrf	RXD		; invalid data
	goto _ISR_RS232error	; goto RS232 error handling in ISR

