;***************************************************************************
;
;	RS232 Communication Test for PIC 16XXX V1.03
;	============================================
;
;	written by Peter Luethi, 26.03.1999, Dietikon, Switzerland
;	http://www.electronic-engineering.ch
;	last update: 31.12.2004
;
;	V1.03:	Added transmission of status message ('@') every 2.75 s
;		when PIC terminal is idle. Used therefore 24 bit counter.
;		(31.12.2004)
;
;	V1.02:	Fixed copy/paste issue of ISR context store/restore
;		(nobody is perfect): Erroneously erased INTCON,INTF
;		clearing, resulting in endless ISR calling...
;		Re-structured entire ISR and RS232 echo sub-routines
;		(11.04.2004)
;
;	V1.01:	ISR context restore improvements
;		(30.12.2000)
;
;	V1.00:	Initial release (26.03.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 Frequency:		4.00 MHz XT
;	Throughput:			1 MIPS
;	RS232 Baud Rate:		9600 baud, 8 bit, no parity, 1 stopbit
;	Required Hardware:		MAX 232, dot matrix LCD display
;	Code Size of entire Program:	approx. 566 instruction words
;	Acquisition Methodology:	Interrupt-based RS232 data acquisition,
;					with LCD display output and RS232 echo
;					during normal operation
;
;
;	DESCRIPTION:
;	============
;	Developed and tested on PIC 16F84, executeable on all interrupt
;	featured PICs.
;	This program handles all aspects of
;		Transmission (register TXD) and
;		Reception (register RXD) through interrupts (PortB0 IRQ).
;	Display of received ASCII characters sent from RS232 host and
;	their decimal representation on the dot matrix LCD display.
;	The microcontroller sends feedback of received RS232 characters back
;	to the terminal window. When the PIC terminal is idle, it sends a
;	status message '@' to the PC every 2.75 seconds.
;
;	Shows the implementation and function of the modules m_wait.asm,
;	m_lcd.asm, m_lcdv08.asm, and m_rs096.asm on the PIC16F84.
;
;
;	IMPLEMENTED FEATURES:
;	=====================
;	- Bi-directional communication between microcontroller application
;	  and remote RS232 client by software-based RS232 transmission.
;	- Display of received character on dot matrix LCD.
;
;***************************************************************************

;***** COMPILATION MESSAGES & WARNINGS *****

	ERRORLEVEL -207 	; found label after column 1
	ERRORLEVEL -302 	; register in operand not in bank 0

;***** PROCESSOR DECLARATION & CONFIGURATION *****

	PROCESSOR 16F84
	#include "p16f84.inc"
	
	; embed Configuration Data within .asm File
	__CONFIG   _CP_OFF & _WDT_OFF & _PWRTE_ON & _XT_OSC

;***** MEMORY STRUCTURE *****

	ORG     0x00			; processor reset vector
  	goto    MAIN			; main program

	ORG     0x04			; interrupt vector
	goto	ISR			; Interrupt Service Routine (ISR)

;***** PARAMETERIZATION *****

	CONSTANT LCDWAIT =	0x02	; LCD wait for initialization
	CONSTANT LCDSPEED =	0x01	; configure according to PIC clock
	
;***** PORT DECLARATION *****

	#define	TXport	PORTA,0x00	; RS232 output port, could be
	#define	TXtris	TRISA,0x00	; any active push/pull port

	LCDtris	equ	TRISB
	LCDport	equ	PORTB

;***** CONSTANT DECLARATION *****

	CONSTANT BASE = 0x0C		; base address of user file registers

;***** REGISTER DECLARATION *****

	TEMP1	set	BASE+d'0'	; Universal temporary register
	TEMP2	set	BASE+d'1'	; ATTENTION !!!
	TEMP3	set	BASE+d'2'	; They are used by various modules.
	TEMP4	set	BASE+d'3'	; If you use them, make sure not to use
	TEMP5	set	BASE+d'4'	; them concurrently! No use in ISR!

	LO	equ	BASE+d'5'
	LO_TEMP	equ	BASE+d'6'

	FLAGreg	equ	BASE+d'7'
	#define	RSflag	FLAGreg,0x00	; RS232 data reception flag
	#define LCDbusy FLAGreg,0x01	; LCD busy flag
	#define	LCDcflag FLAGreg,0x02	; LCD command/data flag
	#define	BCflag	FLAGreg,0x03	; blank checker for preceeding zeros

	TXD	equ	BASE+d'8'	; TX-Data register
	RXD	equ	BASE+d'9'	; RX-Data register

	W_TEMP	equ	BASE+d'10'	; context register (ISR)
	STATUS_TEMP equ	BASE+d'11'	; context register (ISR)
	PCLATH_TEMP equ	BASE+d'12'	; context register (ISR)
	FSR_TEMP equ	BASE+d'13'	; context register (ISR)

	LOcnt	equ	BASE+d'14'	; low byte of 24 bit counter
	MEDcnt	equ	BASE+d'15'	; medium byte of 24 bit counter
	HIcnt	equ	BASE+d'16'	; high byte of 24 bit counter

;***** INCLUDE FILES *****

	#include "..\..\m_bank.asm"
	#include "..\..\m_wait.asm"
	#include "..\..\m_lcd.asm"	; standard version (fixed delay)
	;#include "..\..\m_lcd_bf.asm"	; fast, bi-directional version
	#include "..\..\m_lcdv08.asm"
	#include "..\..\m_rs096.asm"	; standard RS232 baud rate

;***** MACROS *****

;***** SUB-ROUTINES *****

COUNTERinit ; *** Initialize 24 bit counter for status message wait interval ***
	clrf	LOcnt		; init counter
	clrf	MEDcnt		; init counter
	movlw	d'6'
	movwf	HIcnt		; init counter
	RETURN

RSservice ; *** RS232 echo & LCD display routine for received RS232 characters ***
	LCD_DDAdr 0x45
	movfw	RXD		; get received RS232 data
	LCDw			; send to LCD display
	LCD_DDAdr 0x4D
	movfw	RXD
	movwf	LO
	LCDval_08		; display decimal value
	
	SEND	TAB
	SEND	'r'
	SEND	'e'
	SEND	'c'
	SEND	'e'
	SEND	'i'
	SEND	'v'
	SEND	'e'
	SEND	'd'
	SEND	' '

	movfw	RXD		; get received RS232 data
	SENDw			; transmit across RS232

	SEND	' '
	SEND	'o'
	SEND	'n'
	SEND	' '
	SEND	'M'
	SEND	'i'
	SEND	'c'
	SEND	'r'
	SEND	'o'
	SEND	'c'
	SEND	'h'
	SEND	'i'
	SEND	'p'
	SEND	' '
	SEND	'P'
	SEND	'I'
	SEND	'C'
	SEND	'1'
	SEND	'6'
	SEND	'F'
	SEND	'8'
	SEND	'4'
	SEND	CR		; Carriage Return
	SEND	LF		; Line Feed

	; end of RS232 service (echo & display)
	bcf	RSflag		; reset RS232 data reception flag
	bsf	INTCON,INTE	; re-enable RB0/INT interrupt
	RETURN


;***** INTERRUPT SERVICE ROUTINE *****

ISR	;************************
	;*** ISR CONTEXT SAVE ***
	;************************

	bcf	INTCON,GIE	; disable all interrupts
	btfsc	INTCON,GIE	; assure interrupts are disabled
	goto	ISR
	movwf	W_TEMP		; context save: W
	swapf	STATUS,W	; context save: STATUS
	movwf	STATUS_TEMP	; context save
	clrf	STATUS		; bank 0, regardless of current bank
	movfw	PCLATH		; context save: PCLATH
	movwf	PCLATH_TEMP	; context save
	clrf	PCLATH		; page zero, regardless of current page
	bcf	STATUS,IRP	; return to bank 0
	movfw	FSR		; context save: FSR
	movwf	FSR_TEMP	; context save
	;*** context save done ***

	;**************************
	;*** ISR MAIN EXECUTION ***
	;**************************
	
	;*** determine origin of interrupt ***
	btfsc	INTCON,INTF	; check for RB0/INT interrupt
	goto	_ISR_RS232	; if set, there was a keypad stroke

	; 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

	;***********************************
	;*** CLEARING OF INTERRUPT FLAGS ***
	;***********************************
	; 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

	;*****************************************
	;*** ISR TERMINATION (CONTEXT RESTORE) ***
	;*****************************************

ISRend	movfw	FSR_TEMP	; context restore
	movwf	FSR		; context restore
	movfw	PCLATH_TEMP	; context restore
	movwf	PCLATH		; context restore
	swapf	STATUS_TEMP,W	; context restore
	movwf	STATUS		; context restore
	swapf	W_TEMP,F	; context restore
	swapf	W_TEMP,W	; context restore
	RETFIE			; enable global interrupt (INTCON,GIE)

;***** END OF INTERRUPT SERVICE ROUTINE *****


;************** MAIN **************

MAIN	LCDinit			; LCD Initialization
	RS232init		; RS232 Initialization
	clrf	FLAGreg		; initialize all flags

	;*** START-UP MESSAGE of LCD ***
	LCDchar	'R'
	LCDchar	'S'
	LCDchar	'2'
	LCDchar	'3'
	LCDchar	'2'
	LCDchar	' '
	LCDchar	'C'
	LCDchar	'o'
	LCDchar	'm'
	LCDchar	'm'
	LCDchar	'u'
	LCDchar	'n'
	LCDchar	'i'
	LCDchar	'c'
	LCDchar	'a'
	LCDchar	'-'

	LCDline	2
	
	LCDchar	't'
	LCDchar	'i'
	LCDchar	'o'
	LCDchar	'n'
	LCDchar	' '
	LCDchar	'o'
	LCDchar	'n'
	LCDchar	' '
	LCDchar	'P'
	LCDchar	'I'
	LCDchar	'C'
	LCDchar	'1'
	LCDchar	'6'
	LCDchar	'F'
	LCDchar	'8'
	LCDchar	'4'

	;*** START-UP MESSAGE to RS232 ***
	; this is done by reading a look-up table
	; define amount of table items for start-up message
	#define	tab_size4 d'48'
	movlw	tab_size4	; store amount of table items in counter
	movwf	TEMP5
	; transmit message
_ILOOP1	movlw	HIGH WelcomeTable ; get correct page for PCLATH
	movwf	PCLATH		; prepare right page bits for table read
	movfw	TEMP5		; get actual count-down value
	sublw	tab_size4	; table offset: w = tab_size4 - TEMP6
	call	WelcomeTable	; call lookup table
	SENDw			; RS232 output
	decfsz	TEMP5,f		; decrement counter
	goto	_ILOOP1

	WAITX	0x1A,  b'00000111'	; wait some time

	; a little bit animation...
	SEND	'a'
	SEND	'n'
	SEND	'i'
	SEND	'm'
	SEND	'a'
	SEND	't'
	SEND	'i'
	SEND	'n'
	SEND	'g'
	SEND	' '
	SEND	'L'
	SEND	'C'
	SEND	'D'
	SEND	'.'
	SEND	'.'
	SEND	'.'
	SEND	CR		; Carriage Return
	SEND	LF		; Line Feed

	movlw	d'16'
	movwf	TEMP5
_SHL1	LCDcmd	LCDSL		; shift left LCD display content
	WAIT	0xC0
	decfsz	TEMP5,f
	goto	_SHL1

	; finally, reset/clear LCD
	LCDcmd	LCDCLR
	
	LCDchar	'R'
	LCDchar	'S'
	LCDchar	'2'
	LCDchar	'3'
	LCDchar	'2'
	LCDchar	' '
	LCDchar	'R'
	LCDchar	'e'
	LCDchar	'c'
	LCDchar	'e'
	LCDchar	'p'
	LCDchar	't'
	LCDchar	'i'
	LCDchar	'o'
	LCDchar	'n'
	LCDchar	':'

	LCDline	2
	
	LCDchar	'C'
	LCDchar	'h'
	LCDchar	'a'
	LCDchar	'r'
	LCD_DDAdr 0x47
	LCDchar	'V'
	LCDchar	'a'
	LCDchar	'l'
	LCDchar	'u'
	LCDchar	'e'

	SEND	'r'
	SEND	'e'
	SEND	'a'
	SEND	'd'
	SEND	'y'
	SEND	'.'
	SEND	'.'
	SEND	'.'
	SEND	CR		; Carriage Return
	SEND	LF		; Line Feed

	call	COUNTERinit	; initialize 24 bit counter

	;******************************
_MLOOP	btfsc	RSflag		; check RS232 data reception flag
	call	COUNTERinit	; reset 24 bit counter
	btfsc	RSflag		; check RS232 data reception flag
	call	RSservice	; if set, call RS232 echo & LCD display routine

	; status message wait counter
	decfsz	LOcnt,f		; decrement low byte of 24 bit counter
	goto	_MLOOP		; loop
	decfsz	MEDcnt,f	; decrement medium byte of 24 bit counter
	goto	_MLOOP		; loop
	decfsz	HIcnt,f		; decrement high byte of 24 bit counter
	goto	_MLOOP		; loop

	; now, all bytes of 24 bit counter are zero, transmit status message...

	; send status message every 2.75 s
	SEND	'@'
	SEND	CR
	SEND	LF
	call	COUNTERinit	; initialize 24 bit counter
	goto	_MLOOP		; loop forever
	;******************************
	
	;ORG	0x230		; if necessary, move look-up tables

WelcomeTable
	addwf	PCL,F		; add offset to table base pointer
	retlw	CR
	retlw	LF
	DT	"Microchip PIC16F84 connected and stand-by..." ; create table
	retlw	CR
WTableEND retlw	LF
	IF (HIGH (WelcomeTable) != HIGH (WTableEND))
		ERROR "WelcomeTable hits page boundary!"
	ENDIF

	END

