;*************************************************************************** ; ; Hardware RS232 Test Routine for PIC 16F77 V1.05 ; =============================================== ; ; written by Peter Luethi, 21.03.1999, Dietikon, Switzerland ; http://www.electronic-engineering.ch ; last update: 16.01.2005 ; ; V1.05: Added transmission of status message ('@') every 2.75 s ; when PIC terminal is idle. Used therefore 24 bit counter. ; (31.12.2004) ; ; V1.04: Added LCDcflag (LCD command/data flag) to comply with ; latest LCD modules. Changed controller type from PIC16C74A ; to flash device, i.e. PIC16F77. ; (18.04.2004) ; ; V1.03: Moved LCD and RS232 output from interrupt service routine ; to normal operation sub-routine, activated through flag. ; (28.10.2003) ; ; V1.02: Fixed issue in RS232 hardware reception service routine. ; Reads now both data bytes out of double-buffered RX FIFO. ; Tries to eliminate some sporadic reception hangs under ; heavy full-duplex traffic scenarios. ; In case of FIFO overrun errors in the USART receive logic, ; the service routine performs a partial reset of the USART ; receive logic. No specific error handling for RS232 ; reception framing errors. ; (28.10.2003) ; ; V1.01: Updated to latest revision of m_lcdv08.asm ; (28.12.2000) ; ; V1.00: Initial release (24.04.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 16F77 (16C74A) ; Clock Frequency: 4.00 MHz (HS mode) ; Throughput: 1 MIPS ; RS232 Configuration: 9600 with BRGH = 1 ; 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: Preemptive, interrupt-based RS232 ; data acquisition, with LCD display ; output and RS232 echo during normal ; operation ; ; ABSTRACT: ; ========= ; Fully hardware controlled RS232 reception and transmission. ; Display of received ASCII characters sent from PC via RS232 and ; their corresponding decimal value on the dot matrix LCD. ; The microcontroller sends feedback of received characters back to ; the terminal window. When the PIC terminal is idle, it sends a ; status message '@' to the PC every 2.75 seconds. ; ; ; DESCRIPTION: ; ============ ; Developed and tested on Microchip PIC 16C74A. ; ; Although it's not recommended to use BRGH = 1 with the 16C74A, ; I've tested the device on 9600 baud and it runs without any ; issues. Referring to the specification, the error using this ; selection in conjunction with a 4 MHz crystal is 0.16%. This is ; sufficient for error-free communication within this application. ; ; This program handles all aspects of transmission and reception ; through interrupts. ; ; Program shows the implementation and function of the modules m_bank.asm, ; m_wait.asm, m_lcd.asm and m_lcdv08.asm on the PIC16F77 or PIC16C74A. ; ; ; IMPLEMENTED FEATURES: ; ===================== ; - Bi-directional communication between microcontroller application ; and remote RS232 client by hardware-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 16F77 #include "p16f77.inc" ;PROCESSOR 16C74A ;#include "p16c74a.inc" ; embed configuration data within .asm file __CONFIG _CP_OFF & _WDT_OFF & _PWRTE_ON & _HS_OSC & _BODEN_ON ;***** MEMORY STRUCTURE ***** ORG 0x00 ; processor reset vector goto MAIN ; main program ORG 0x04 ; interrupt vector goto ISR ; Interrupt Service Routine (ISR) ;***** PORT DECLARATION ***** LCDtris equ TRISB ; LCD on portB LCDport equ PORTB ;***** CONSTANT DECLARATION ***** CONSTANT BASE = 0x20 ; base address of user file registers CONSTANT LCDWAIT = 0x01 ; clk in [0..5] MHz CONSTANT LCDSPEED = 0x00 ; clk in [0..9] MHz CONSTANT LF = d'10' ; line feed CONSTANT CR = d'13' ; carriage return CONSTANT TAB = d'9' ; tabulator CONSTANT BS = d'8' ; backspace ;***** 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 ! FLAGreg equ BASE+d'7' #define LCDbusy FLAGreg,0x00 ; LCD busy flag declared within flag register #define BCflag FLAGreg,0x01 ; blank checker for preceding zeros #define RSflag FLAGreg,0x02 ; RS232 data reception flag #define RX2flag FLAGreg,0x03 ; two RS232 bytes received #define LCDcflag FLAGreg,0x04 ; LCD command/data flag LO equ BASE+d'8' ; binary to decimal output (8 bit) LO_TEMP equ BASE+d'9' RXreg equ BASE+d'10' ; first RS232 RX-Data register RXreg2 equ BASE+d'11' ; second RS232 RX-Data register RXtemp equ BASE+d'12' ; intermediate data register for LCD output W_TEMP equ BASE+d'13' ; context register (ISR) STATUS_TEMP equ BASE+d'14' ; context register (ISR) PCLATH_TEMP equ BASE+d'15' ; context register (ISR) FSR_TEMP equ BASE+d'16' ; context register (ISR) LOcnt equ BASE+d'17' ; low byte of 24 bit counter MEDcnt equ BASE+d'18' ; medium byte of 24 bit counter HIcnt equ BASE+d'19' ; 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 (busy flag) #include "..\..\m_lcdv08.asm" ; 8 bit to decimal conversion for LCD ;***** MACROS ***** RS232init macro BANK1 ; Asynchronous USART assignment: bsf TXSTA,BRGH ; BRGH = 1 movlw d'25' ; 9600 baud @ 4.00 MHz, BRGH = 1 movwf SPBRG BANK0 bsf RCSTA,SPEN ; enable serial port BANK1 bsf PIE1,RCIE ; enable serial reception interrupt bsf TXSTA,TXEN ; enable serial transmission BANK0 bsf INTCON,PEIE ; enable peripheral interrupts bsf RCSTA,CREN ; enable continuous reception endm SEND macro send_char movlw send_char call _RSxmit endm SENDw macro call _RSxmit endm ;***** SUBROUTINES ***** 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 _RSxmit BANK1 _RSbusy btfss TXSTA,TRMT ; check, if previous transmission goto _RSbusy ; has been terminated BANK0 movwf TXREG ; send next char RETURN RSservice ; *** RS232 echo & LCD display routine for received RS232 characters *** ; first byte in RXreg movfw RXreg ; retrieve data movwf RXtemp ; store data call _RSdisp ; call output subroutine btfss RX2flag ; check for second data byte received goto RSdispEND ; second byte in RXreg2, i.e. RX2flag = 1 movfw RXreg2 ; retrieve data movwf RXtemp ; store data call _RSdisp ; call output subroutine bcf RX2flag ; reset flag btfss RCSTA,OERR ; check for RX overrun (overrun error bit) goto RSdispEND ; buffer overrun, severe error, reset USART RX logic bcf RCSTA,CREN ; reset USART RX logic, clear RCSTA,OERR bcf RCSTA,FERR ; reset framing error bit bsf RCSTA,CREN ; re-enable continuous reception RSdispEND bcf RSflag ; reset RS232 data reception flag BANK1 bsf PIE1,RCIE ; re-enable USART reception interrupt BANK0 ; (interrupt flag already cleared in ISR) RETURN _RSdisp ; output subroutine for serial data received LCD_DDAdr 0x45 movfw RXtemp LCDw ; ascii character output LCD_DDAdr 0x4D movfw RXtemp movwf LO LCDval_08 ; numeric ascii value output ; send echo back to PC SEND TAB SEND 'r' SEND 'e' SEND 'c' SEND 'e' SEND 'i' SEND 'v' SEND 'e' SEND 'd' SEND ' ' movfw RXtemp ; retrieve value SENDw ; send across RS232 line 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 '7' SEND '7' SEND CR ; Carriage Return SEND LF ; Line Feed 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 PIR1,RCIF ; check for USART interrupt goto _ISR_RS232 ; if set, there was an USART interrupt goto ISRend ; unexpected IRQ, terminate execution of ISR ;****************************** ;*** RS232 DATA ACQUISITION *** ;****************************** _ISR_RS232 movfw RCREG ; get RS232 data (first RX FIFO entry) movwf RXreg ; store first data byte btfss PIR1,RCIF ; check flag for second RX FIFO entry goto _ISR_RS232_A ; no second byte received, branch movfw RCREG ; get RS232 data (second RX FIFO entry) movwf RXreg2 ; store second data byte bsf RX2flag ; set flag to indicate second byte received _ISR_RS232_A bsf RSflag ; enable RS232 data reception flag BANK1 ; (for display routine) bcf PIE1,RCIE ; disable USART reception interrupt BANK0 ; (will be re-enabled in normal subroutine) ;goto _ISR_RS232end ; exit ISR _ISR_RS232end ;bcf PIR1,RCIF ; cleared by hardware: USART RX 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 clrf INTCON ; reset interrupts (disable all) LCDinit ; LCD initialization RS232init ; RS232 initialization ;*** ENABLE INTERRUPTS *** movlw b'11111000' andwf INTCON,F ; clear all interrupt flags clrf PIR1 ; clear all interrupt flags clrf PIR2 ; clear all interrupt flags bsf INTCON,GIE ; enable global interrupt clrf FLAGreg ; initialize all flags 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 '7' LCDchar '7' ;*** 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, ; i.e. RS232 has been idle for approx. 2.75 s, ; therefore, transmit now status message... ; send status message 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 PIC16F77 connected and stand-by..." ; create table retlw CR WTableEND retlw LF IF (HIGH (WelcomeTable) != HIGH (WTableEND)) ERROR "WelcomeTable hits page boundary!" ENDIF END