; ***************************************************************
; * Copyright (C) 2005, Embed Inc (http://www.embedinc.com) *
; * *
; * Permission to copy this file is granted as long as this *
; * copyright notice is included in its entirety at the *
; * beginning of the file, whether the file is copied in whole *
; * or in part and regardless of whether other information is *
; * added to the copy. *
; * *
; * The contents of this file may be used in any way, *
; * commercial or otherwise. This file is provided "as is", *
; * and Embed Inc makes no claims of suitability for a *
; * particular purpose nor assumes any liability resulting from *
; * its use. *
; ***************************************************************
;
; Low level UART handler.
;
/include "qq2.ins.aspic"
extern intr_ret_uart ;jump here when done processing an interrupt
extern_flags ;declare global flag bits EXTERN
;
;***********************************************************************
;
; Configuration constants.
;
baud equ 115200 ;serial line baud rate
finsz equ 8 ;input FIFO size
fousz equ 8 ;output FIFO size
lbank equ 0 ;register bank for the local state of this module
;
; Derived constants.
;
uart_baud baud ;set asm constants for UART configuration
lbankadr equ bankadr(lbank) ;address within local state register bank
;
;***********************************************************************
;
; Global state. All this state is assumed to be in the GBANK register
; bank by other modules.
;
defram gbankadr
;
;***********************************************************************
;
; Local state.
;
defram lbankadr
fifo_define fifo_in, finsz ;define serial line input FIFO
fifo_define fifo_ou, fousz ;define serial line output FIFO
uart_itmp1 res 1 ;temp scratch for use by interrupt routine
uart_itmp2 res 1
.uart code
;
;***********************************************************************
;
; Subroutine UART_INIT
;
; Initialize the hardware and software state managed by this module.
;
glbsub uart_init, noregs
;
; Initialize the local state.
;
dbankif lbankadr
fifo_init fifo_in ;init input stream FIFO
fifo_init fifo_ou ;init output stream FIFO
;
; Set up the UART hardware.
;
uart_setup ;init to config from UART_BAUD, above
;
; Enable the UART interrupts. Interrupts are still globally disabled
; at this point. Only the individual peripheral interrupts are enabled
; here. The UART transmit interrupt is not enabled here because the
; output FIFO is definitely empty now. The interrupt will be enabled
; when a byte is stuffed into the output FIFO.
;
dbankif pie1
bsf pie1, rcie ;enable UART receive interrupts
;
; Initialize global state.
;
dbankif gbankadr
bsf flag_sout ;indicate UART_PUT is ready to accept byte immediately
leaverest
;
;***********************************************************************
;
; Routine UART_INTR_RECV
;
; This routine is jumped to from the interrupt handler during an interrupt
; when the UART has received a new byte. This routine must jump back to
; INTR_RET_UART when done handling the interrupt condition.
;
; Since this routine is running during an interrupt, it must not modify
; the general registers and other global state. Any call stack locations
; used here will not be available to the foreground code.
;
glbent uart_intr_recv ;UART receive interrupt handler
;
; Save the original RCSTA register value in UART_ITMP1, then save the
; data byte in UART_ITMP2. The UART incoming data register must be
; read to clear the interrupt condition, but the framing error bit
; is only valid before the data byte is read.
;
dbankif rcsta
movf rcsta, w ;save snapshot of receive status reg in UART_ITMP1
dbankif lbankadr
movwf uart_itmp1
dbankif rcreg
movf rcreg, w ;save data byte in UART_ITMP2, clear intr condition
dbankif lbankadr
movwf uart_itmp2
;
; Reset the receiver if an overrun occurred. This is the only way to
; clear an overrun condition.
;
dbankif rcsta
btfss rcsta, oerr ;input overrun condition ?
jump recv_derrov ;no overrun condition
bcf rcsta, cren ;disable then enable receiver to clear the error
bsf rcsta, cren ;re-enable the receiver
recv_derrov ;done dealing with overrun error condition
;
; Ignore the data byte if it was not properly followed by the stop bit.
; This is called a "framing error".
;
dbankif lbankadr
btfsc uart_itmp1, ferr ;no framing error with this data byte ?
jump intr_leave ;framing error, don't process this byte further
;
; Stuff the received byte into the FIFO if there is room for it.
;
dbankif lbankadr
ibankif lbankadr
fifo_skip_nfull fifo_in, finsz ;FIFO has room for another byte ?
jump intr_leave ;FIFO is full, ignore the new byte
fifo_put fifo_in, finsz, uart_itmp2 ;stuff the new data byte into the FIFO
dbankif gbankadr
bsf flag_sin ;indicate a serial line input byte is available
intr_leave unbank ;common code to return to interrupt handler
gjump intr_ret_uart ;done handling this interrupt
;
;***********************************************************************
;
; Subroutine UART_GET
;
; Return the next serial line input byte in REG0. If no input byte is
; available, this routine will wait until one is. This routine is
; guaranteed not to wait if FLAG_SIN is set before it is called.
;
glbsub uart_get, noregs
;
; Wait until an input byte is available.
;
dbankif gbankadr
get_wait
btfss flag_sin ;an input byte is available in the FIFO ?
jump get_wait ;no input byte available yet, check again
;
; The FIFO contains at least one input byte.
;
dbankif lbankadr
ibankif lbankadr
intr_off ;temp disable interrupts
fifo_get fifo_in, finsz, reg0 ;get the byte from the FIFO into REG0
fifo_skip_empty fifo_in ;no more input bytes available ?
jump get_nemt ;FIFO is not completely empty
dbankif gbankadr
bcf flag_sin ;indicate no input byte immediately available
get_nemt dbank? ;skip to here if FIFO is not completely empty
intr_on ;re-enable interrupts
leaverest
;
;***********************************************************************
;
; Subroutine UART_PUT
;
; Send the byte in REG0 over the serial line. The byte is actually queued
; for later transmission. If no room is available in the serial line
; output FIFO, then this routine waits until there is. It is guaranteed
; not to wait if FLAG_SOUT is set before it is called.
;
glbsub uart_put, noregs
;
; Wait until there is room in the output FIFO. This FIFO is emptied by
; the interrupt routine, which sets FLAG_SOUT when the FIFO is not full.
;
dbankif gbankadr
put_wait
btfss flag_sout ;output FIFO can accept another byte ?
jump put_wait ;FIFO is full, go back and check again
;
; The FIFO has room for at least one more byte.
;
dbankif lbankadr
ibankif lbankadr
intr_off ;temp disable interrupts
fifo_put fifo_ou, fousz, reg0 ;stuff the byte into the output FIFO
dbankif pie1
bsf pie1, txie ;make sure UART transmit interrupt is enabled
intr_on ;re-enable interrupts
;
; Clear FLAG_SOUT if the FIFO is full. FLAG_SOUT is currently set.
;
dbankif lbankadr
intr_off ;temp disable interrupts
fifo_skip_full fifo_ou, fousz ;FIFO is completely full ?
jump put_nfull ;FIFO still has room, done with FLAG_SOUT
dbankif gbankadr
bcf flag_sout ;indicate serial line output FIFO is full
put_nfull unbank ;skip to here if FIFO not completely full
intr_on ;re-enable interrupts
leaverest
;
;***********************************************************************
;
; Subroutine UART_INTR_XMIT
;
; This routine is jumped to from the interrupt handler during an interrupt
; when the UART is ready to accept a new byte. This routine must jump back
; to INTR_RET_UART when done handling the interrupt condition.
;
; Since this routine is running during an interrupt, it must not modify
; the general registers and other global state. Any call stack locations
; used here will not be available to the foreground code.
;
glbent uart_intr_xmit ;UART transmit interrupt handler
dbankif gbankadr
bsf flag_sout ;FIFO guaranteed not to be full after this interrupt
;
; Disable this interrupt if the serial line output FIFO is empty. The
; interrupt is always enabled when a byte is put into the FIFO.
;
dbankif lbankadr
fifo_skip_nempty fifo_ou ;a byte is available in the FIFO
jump xmit_off ;no byte available, disable this interrupt
;
; There is at least one byte in the FIFO. Send it.
;
dbankif lbankadr
ibankif lbankadr
fifo_get fifo_ou, fousz, uart_itmp1 ;get the data byte into UART_ITMP1
movf uart_itmp1, w ;get the data byte into W
dbankif txreg
movwf txreg ;write the data byte to the UART
;
; Disable this interrupt if the FIFO is now empty.
;
dbankif lbankadr
fifo_skip_empty fifo_ou ;nothing more left to send now ?
jump intr_leave ;still more to send, don't disable the interrupt
xmit_off dbankis lbankadr ;disable the UART transmit ready interrupt
dbankif pie1
bcf pie1, txie ;disable this interrupt
jump intr_leave ;done handling the interrupt
end