' 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 ' Keywords : ASSEMBLY INTERRUPTS, LCDOUT ' 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_DREG PORTD Define LCD_DBIT 4 Define LCD_RSREG PORTE Define LCD_RSBIT 0 Define LCD_EREG PORTE 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 Asm myint ; 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 Else bcf STATUS,IRP ; If bank 0 or 1 clear IRP EndIF ; 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 finished 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 EndAsm 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 mainloop: 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 EndIF 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 Return ' 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 Else LCDOut $FE,$c0,"USART Overrun" ' Display usart error on line_2 EndIF 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 End