ME Labs, Inc.
  Developer Resources:

Programming Clues
    Sample Programs
Compiler Manual
    PICBASIC™ Compiler
    Serin2/Serout2 Modes
    ASCII Character Set
    Number Conversion
    Floating Point
    PBP Debug Monitor
    Articles and Tutorials

Hardware Clues
    Parts / Vendor List
    PICPROTO™ Boards
    LAB-X1 Docs
    LAB-X2 Docs
    LAB-X20 Docs
    LAB-X3 Docs
    LAB-X4 Docs
    LAB-XUSB Docs
    LAB-XT Docs

ME Labs, Inc. | 1-719-520-5323 | Example Program - SERBUFAX.pbp

PICBASIC PRO program to demonstrate an interrupt-driven input buffer for hardware USART receive using Assembly language interrupt. Pin definitions compatible with LAB-X1 and PIC16F877
' Name        : SERBUFAX.pbp
' Compiler    : PICBASIC PRO Compiler 2.6
' Assembler   : PM or MPASM
' Target PIC  : 40-pin 16F877, 877A or similar
' Hardware    : LAB-X1 Experimenter Board
' Oscillator  : 4MHz external crystal
' Description : PICBASIC PRO program to demonstrate an interrupt-driven
' input buffer for hardware USART receive using Assembly language interrupt. 
' Pin definitions compatible with LAB-X1 and PIC16F877

' Defines for LCD
Define LCD_DBIT  4
Define LCD_RSBIT 0
Define LCD_EBIT  1

' Define interrupt handler
Define INTHAND myint

' Configure internal registers
ADCON1 = 7     ' Set PortA and E to digital operation
RCSTA = $90    ' Enable USART receive
TXSTA = $24    ' Set USART parameters
SPBRG = 25     ' Set baud rate to 9600

LED         VAR PORTD.0  ' Alias LED to PORTD.0
CREN        VAR RCSTA.4  ' Alias CREN (Serial receive enable)

'Variables for saving state in interrupt handler
wsave       VAR BYTE $70   system  ' Saves W
ssave       VAR BYTE bank0 system  ' Saves STATUS
psave       VAR BYTE bank0 system  ' Saves PCLATH
fsave       VAR BYTE bank0 system  ' Saves FSR

buffer_size CON 32                 ' Sets size of ring buffer
buffer      VAR BYTE[buffer_size]  ' Array variable for holding received characters
index_in    VAR BYTE bank0         ' Pointer - next empty location in buffer
index_out   VAR BYTE bank0         ' Pointer - location of oldest character in buffer
errflag     VAR BYTE bank0         ' Error flag

bufchar     VAR BYTE  ' Stores the character retrieved from the buffer
col         VAR BYTE  ' Stores location on LCD for text wrapping
i           VAR BYTE  ' Loop counter

   GoTo start         ' Skip around interrupt handler

' Assembly language INTERRUPT handler
; Uncomment the following if the device has less than 2k of code space
   ;movwf  wsave         ; Save W
   ;swapf  STATUS,W      ; Swap STATUS to W (swap avoids changing STATUS)
   ;clrf   STATUS        ; Clear STATUS
   ;movwf  ssave         ; Save swapped STATUS
   ;movf   PCLATH,W      ; Move PCLATH to W
   ;movwf  psave         ; Save PCLATH

; Save the FSR value for later  
   movf    FSR,W        ; Move FSR to W
   movwf   fsave        ; Save FSR
; Check for hardware overrun error
   btfsc   RCSTA,OERR   ; Check for usart overrun
   GoTo    usart_err    ; jump to assembly error routine

; Find in which bank the compiler put buffer, and set IRP               
   IF (_buffer > 0FFh)  ; Find the bank where buffer is located
      bsf   STATUS,IRP  ; If bank 2 or 3 set IRP
      bcf   STATUS,IRP  ; If bank 0 or 1 clear IRP
; Test for buffer overrun               
   incf   _index_in,W   ; Increment index_in to W
   subwf  _index_out,W  ; Subtract indexes to test for buffer overrun
   btfsc  STATUS,Z      ; check for zero (index_in = index_out)
   GoTo   buffer_err    ; jump to error routine if zero

; Increment the index_in pointer and reset it if it's outside the ring buffer
   incf   _index_in,F   ; Increment index_in to index_in
   movf   _index_in,W   ; Move new index_in to W
   sublw  _buffer_size-1; Subtract index_in from buffer_size-1
   btfss  STATUS, C     ; If index_in => buffer_size
   clrf   _index_in     ; Clear index_in

; Set FSR with the location of the next empty location in buffer
   movlw   Low _buffer  ; Get the location of buffer[0]
   addwf   _index_in,W  ; Add index_in to point to next empty slot
   movwf   FSR          ; Store pointer in FSR

; Read and store the character from the USART           
   movf   RCREG,W       ; Read the received character
   movwf  INDF          ; Put the received character in FSR location
; Restore FSR, PCLATH, STATUS and W registers
   movf   fsave,W       ; retrieve FSR value
   movwf  FSR           ; Restore it to FSR
   movf   psave,W       ; Retrieve PCLATH value
   movwf  PCLATH        ; Restore it to PCLATH
   swapf  ssave,W       ; Retrieve the swapped STATUS value (swap to avoid changing STATUS)
   movwf  STATUS        ; Restore it to STATUS
   swapf  wsave,F       ; Swap the stored W value
   swapf  wsave,W       ; Restore it to W (swap to avoid changing STATUS)
   retfie               ; Return from the interrupt

; Error routines        
buffer_err              ; Jump here on buffer error
   bsf   _errflag,1     ; Set the buffer flag

usart_err               ; Jump here on USART error
   bsf   _errflag,0     ; Set the USART flag
   movf  RCREG, W       ; Trash the received character
   GoTo  finished       ; Restore state and return to program

start:  ' Initialize variables
   index_in = 0
   index_out = 0
   col = 1
   errflag = 0

   INTCON = %11000000   ' Enable interrupts
   PIE1.5 = 1           ' Enable interrupt on USART

   Low PORTE.2          ' LCD R/W line low (W)
   Pause 100            ' Wait for LCD to start
   LCDOut $fe,1         ' Clear LCD

' Main program starts here - blink an LED at 1Hz
   High LED             ' Turn on LED connected to PORTD.0
   Pause 500            ' Pause 500mS
   Low LED              ' Turn off LED connected to PORTD.0
   Pause 500            ' Pause 500mS

display:                 ' dump the buffer to the LCD
   IF errflag Then error ' Goto error routine if needed
   IF index_in = index_out Then mainloop ' loop if nothing in buffer
   GoSub getbuf         ' Get a character from buffer   
   LCDOut bufchar       ' Send the character to LCD
   col = col + 1        ' Increment LCD location
   IF col > 20 Then     ' Check for end of line
      col = 1           ' Reset LCD location
      LCDOut $fe,$c0,REP " "\20 ' Clear any error on line-2 of LCD
      LCDOut $FE,2      ' Tell LCD to return home

   GoTo display         ' Check for more characters in buffer

' Subroutines

' Get a character from the buffer
getbuf:                         ' Move the next character in buffer to bufchar
   intcon = 0                   ' Disable interrupts while reading buffer
   index_out = index_out + 1    ' Increment index_out pointer (0 to 63)
   IF index_out => buffer_size Then index_out = 0 ' Reset pointer if outside buffer
   bufchar = buffer[index_out]  ' Read buffer location(index_out)
   INTCON = %11000000           ' Enable interrupts

' Display an error
error:                          ' Display error message
   INTCON = 0                   ' Disable interrupts while in the error routine
   IF errflag.1 Then            ' Determine the error
      LCDOut $FE,$c0,"Buffer Overrun" ' Display buffer error on line-2
      LCDOut $FE,$c0,"USART Overrun"  ' Display usart error on line_2
   LCDOut $fe,2         ' Send the LCD cursor back to line-1 home
   For i = 2 TO col     ' Loop for each column beyond 1
     LCDOut $fe,$14     ' Put the cursor back where it was
   Next i
   errflag = 0          ' Reset the error flag
   CREN = 0             ' Disable continuous receive to clear hardware error
   CREN = 1             ' Enable continuous receive
   INTCON = %11000000   ' Enable interrupts
   GoTo display         ' Carry on


Download the program file.