************************************************************
*This program is a s/w UART for the C54x using the BIO and *
*XF, I/O pins, a hardware interrupt, and a timer. *
*The device operates in full-duplex mode and supports *
*up to 16 data bits, 1 or 2 stop bits, and parity (even or *
*odd). *
* *
*Written by: Adrienne Prahler Jaffe *
*Modified: 3/10/99 *
*Three function calls are available in this program, *
*setup, transmit and transmit_delay. *
*Functions being made c-callable *
************************************************************
.global _timer_isr, _transmit, _transmit_delay, _setup, _hw_int0,
_tx_datapass
.global _rcv_datapass, _UART_reg, _parity_value, _one, _UART_count,
_tx_datalo
.global _rcv_datalo
.global _newData
****************** MAIN SHELL **********************************
.mmregs ; allow addressing of memory registers by name
.bss _temp,1 ;
.bss _newData,1;
.bss _UART_reg,1 ; location of status register flags
.bss _rcv_datalo,1 ; rcv location
.bss rcv_datahi,1 ; rcv location
.bss _tx_datalo, 1 ; tx_data location
.bss tx_datahi, 1 ; tx_data location
.bss _UART_count,1 ; counter
.bss _one, 1 ; constant location
.bss _tx_datapass, 1 ; where transmit data is put for transmission
.bss _rcv_datapass, 1 ; where receive data is stored
; (striped of stop, start, parity)
.bss _parity_value,1 ; parity value location
; if total #1's in dataword even then
; lsb = 0, odd lsb = 1
.bss parity_bit, 1 ; parity_bit value
****************Added for debugging*******************************
.bss _data_storage, 330, 1 ; where receive data is stored
******************************************************************
**********************Timer information**************************
* timer interrupt rate (baud rate)= 1/(clkout*(TDDR+1)*(PRD+1))*
* TDDR is timer divide down ratio, 4 bits *
* PRD is the timer period register, 16 bits *
* *
* Knowing the desired baud rate and clkout of device, the values*
* of TDDR and PRD can be determined. *
* In this example, the TDDR is set to 1, if that is changed, the*
* values of tcr_timer_go needs to be changed so the lower 4 bits*
* are the desired values. *
* *
* The program uses two timer rates, one for a half bit time and *
* one for a whole bit time. The value calculated for the PRD *
* in the above equation is for the whole bit time. *
*****************************************************************
whole_bit_time .set 1706d ; set whole bit time assuming 32.768MHZ (set by
jumpers)
; and 9600 baud
half_bit_time .set 853d ; set half bit time assuming 32.768MHz
; and 9600 baud
tcr_setup .set 0000000000010000b ; setup tcr values, stop timer
tcr_timer_go .set 0000000001100001b ; tcr register values for
; timer start
*************variables set for operation of UART*****************
* UART_reg: *
*___15-5__________4__________3_______2_______1_______0____ *
*|reserved| parity error | delay | uart | tx | rcv | *
*--------------------- *
*RCV *
* 0 = not receiving data *
* 1 = receiving data *
*TX *
* 0 = no data to transmit *
* 1 = data waiting to be transmitted *
*UART *
* 0 = not busy transmitting *
* 1 = actively transmitting *
*Delay *
* 0 = not delayed *
* 1 = delayed *
*Parity error *
* 0 = nothing wrong *
* 1 = error occurred *
*****************************************************************
data_bits .set 8 ; number of data bits being transferred
stop .set 1 ; number of stop bits, 1 or 2
start_bits .set 1 ; number of start bits
parity .set 0 ; if 0, no parity
; if 1, odd parity
; if 2, even parity
.if parity != 0 ; if parity set then reserve a bit spot
par_bits .set 1
parity_or .set 01b<<data_bits+start_bits
.else
par_bits .set 0
.endif
************Setting up stopbit formatting values******************
.if stop=2 ;if 2 stop bits then set up this value for formatting
stop_or .set 011b<<data_bits+par_bits+start_bits
; setting up for formatting data
.elseif stop=1 ; if 1 stop bit set up this value for formatting
stop_or .set 01b<<data_bits+par_bits+start_bits
; setting up for formatting data
.else
.emsg "ERROR, must specify the number of stop bits to be 1 or 2"
.endif
**********Parity Routines****************************************
.if parity > 2
.emsg "ERROR, the parity value must correspond to no parity, even parity
or
odd parity!"
.endif
.if parity !=0 ; if parity set
.if data_bits+par_bits+start_bits>
N_par .set 32
.elseif data_bits+par_bits+start_bits>=9
N_par .set 16
.elseif data_bits+par_bits+start_bits>=5
N_par .set 8
.elseif data_bits+par_bits+start_bits>=3
N_par .set 4
.elseif data_bits+par_bits+start_bits=2
N_par .set 2
.elseif data_bits+par_bits+start_bits=1
N_par .set 1
.endif
.endif
*******************Main Program***********************
.text
******dummy main program to call UART setup and a delayed transmit******
start:
nop
call _setup
nop
nop
call _transmit_delay
nop
loop: nop ; looping
nop
nop
b loop
nop
nop
************setup UART**************************
_setup:
rsbx cpl ; enable direct addressing mode
ld #_UART_reg, DP ; setup DP for direct addressing
RSBX INTM ; enable global interrupts
RSBX SXM ; turn off sign extension mode, the UART must
; operate with this setting!
st #1, @_one ; create a Smem location with value 1 stored
st #9h, *(IMR) ; enable interrupt (timer and hw_int)
st #0, @_UART_reg ; initialize data locations
st #0, @_tx_datalo
st #0, @tx_datahi
st #0, @_newData ; initialize data locations
**************Added for debugging**************
stm #100, bk ; load circular buffer size
ssbx xf ; set xf high
nop
nop
nop
nop
stm #_data_storage, ar3 ; setup auxiliary register for
; receive data
st #8bh, @_tx_datapass ; load data into tx_datapass
************************************************
ssbx cpl
rete ; return to main program
************Timer ISR for UART**************************
_timer_isr: pshm st0 ; context save
pshm AL
pshm AH
pshm AG
rsbx cpl ; set cpl=0 for assembly functions
ld #_UART_reg, DP ; setup DP for direct addressing
rcv: bitf *(_UART_reg), #1h ; test rcv flag
nop
nop
bc tx, ntc ; if rcv not set then branch to tx
cmpm @_UART_count, #0h ; is count = 0?
nop
nop
bc exit_now, nbio, tc ; if count = 0 and bio =1 then exit now!
; not a VALID start bit!
cc timerwhole, bio, tc ; setup timer for whole bit if count=0
; bio=0
bc tx, bio, tc ; after time set up branch to tx routine
ld @_UART_count, T ; load counter into T register
; used for offset since data received
; lsb first.
ld @_rcv_datalo, A ; load rcv_data
add @rcv_datahi, 16, A
xc 1, nbio ; if bio pin high then
add @_one, TS, A ; add one shifted by bit position(count)
stl A, @_rcv_datalo ; store rcv data in memory location
sth A, @rcv_datahi
tx: bitf @_UART_reg, #4h ; test UART flag
nop
nop
bc exit_norm, ntc ; if not transmitting then just exit
bitf @_tx_datalo, #1h ; what is lsb?
ld @_tx_datalo, A ; load tx_data
add @tx_datahi, 16, A
xc 1, tc ; if lsb=1 set xf=1
ssbx xf
xc 1, ntc
rsbx xf ; if lsb=0 set xf=0
stl A, -1, @_tx_datalo ; store low portion of tx out with shift
; right of one
sth A, -1, @tx_datahi ; store hi portion of tx out with shift
; right of one
*******************Exit Routines from timer isr******************
* Normal exit routine *
* Invalid start bit detected *
* End of rcv data routine *
* End of tx data routine *
*****************************************************************
*********Normal Exit routine*************************************
exit_norm: cmpm @_UART_count, #stop+start_bits+par_bits+data_bits-1
;test if final bit
nop
nop
cc exit_final, tc ; if final bit exit
Addm #1, @_UART_count ; update counter
restore: ssbx cpl ; reset cpl=1 for c program
popm AG ; restore accumulator A contents and status
popm AH
popm AL
popm ST0
timer_end: rete ; return to main program
nop
nop
*********Invalid start bit************************************
exit_now: bitf @_UART_reg, #4h ; What is UART bit set to?
stm #tcr_setup, TCR ; stop timer by writing a 1 to tss in tcr
xorm #1h, @_UART_reg ; reset RCV flag
xc 2, tc ; if this was a delayed tx and uart=1
xorm #0ah, @_UART_reg ; reset txdelay bit, and uart bit!
st #9h, *(IMR) ; enable int0 and timer interrupts (timer
; stopped)
b restore ; exit routine
nop
nop
*************Final Bit exit Routine
exit_final: bitf @_UART_reg, #1h ; check rcv bit
ld @_rcv_datalo, A
add @rcv_datahi, A
cc end_rcv_routine, tc
nop
nop
bitf @_UART_reg, #4h ; check uart bit
stm #tcr_setup, TCR ; stop timer by writing a 1 to tss in tcr
nop
xc 2, tc
xorm #6h, @_UART_reg ; reset TX and UART flags
ldm IFR, A ; load interrupts pending
nop
nop
and #1h, A ; get rid of int0 pending interrupts
stlm A, IFR ; clear all pending interrupts
st #9h, *(IMR) ; enable int0 (timer still active but stopped)
call _transmit_delay ; loop back with a transmit delay call
nop
nop
ret ; restore saved registers
nop
nop
******************end rcv data routine***********************
end_rcv_routine:
formatting xor #stop_or, A ; get rid of stop bits
******************Parity Bit Routine********************
** only compiled if parity set at compile time **
********************************************************
.if parity != 0
call parity_calc ; does this match with the
; type of parity expected?
bitf @_parity_value, #1h ; check parity value
ld @_rcv_datalo, A ; load rcv_data to edit out parity,
add @rcv_datahi, 16, A ; stop, start
stl A, -(start_bits+data_bits), @parity_bit
; store parity bit
;to lsb in parity_bit
.if parity = 1 ; if odd parity & paritycalc is 0
cc parity_error, ntc ; then call error
.elseif parity = 2 ; if even parity & parity bit is 1
cc parity_error, tc ; then call error
.endif
bitf @parity_bit, #1h ; check parity bit value
xor #stop_or, A ; get rid of stop bits
nop
xc 2, tc ; if parity bit=1 then get rid of it
xor #parity_or, A
nop
.endif
**********************************************************
stl A, -start_bits, @_rcv_datapass ; put unformatted data word
; in rcv location
********************Added for debugging******************************
stl A, -start_bits, @_tx_datapass ; load tx data with just
; received data
;stl A, -start_bits, *AR3+% ; error checking routine
; putting data into a
; memory location so it can
; be checked later
**********************************************************************
xorm #1h, @_UART_reg ; reset RCV flag
st #1, @_newData ; new data flag
ret
nop
nop
************************ Timer setups**********************
timerhalf: stm #tcr_setup, TCR ; stop timer by writing a 1 to tss in tcr
stm #half_bit_time, PRD ; load timer period
stm #tcr_timer_go,TCR ; start timer
ret
timerwhole:
stm #tcr_setup, TCR ; stop timer by writing a 1 to tss in tcr
stm #whole_bit_time, PRD ; load timer period
stm #tcr_timer_go,TCR ; start timer
ret
***************Parity calculation routine*****************
** only compiled if parity set at compile time **
**********************************************************
parity_calc: .if parity != 0
.if N_par = 32
xor A, 16, A
.endif
.if N_par >= 16
xor A, 8, A
.endif
.if N_par>= 8
xor A,4, A
.endif
.if N_par>=4
xor A, 2, A
.endif
.if N_par>=2
xor A, 1,A
stl A, -N_par+1, @_parity_value ; load parity status
; into lsb
.endif
ret
nop
nop
.endif
********************Parity error routine*************************
parity_error: xorm #10h, @_UART_reg ;set parity error flag
ret
*****************Transmit Routine********************************
* Data passed in mem location tx_datapass *
* The routine will check if the tx flag is set and wait until *
* the tx flag is reset so transmit data won't be overwritten *
* before it is sent. If it is a delayed transmit the routine *
* will just format the data and return to the main program until*
* the next receive interrupt occurs. If it is a transmit the *
* registers for transmit and the timer are setup and started. *
* *
* If a transmit is started, then the device can not receive. *
* Full-duplex operation occurs only when a transmit delayed is *
* called since there is only one timer. *
*****************************************************************
_transmit: pshm AL ; context save since Accumulator A used
pshm AH
pshm AG
pshm ST0
rsbx cpl ; enable direct addressing mode
ld #_UART_reg, DP ; set DP for addressing
busy: BITF *(_UART_reg), #4h ; What is UART_flag set to?
nop
nop
BC busy, tc ; if UART=1 then can not overwrite data
tx_set BITf @_UART_reg, #2h ; What is TX_flag set to?
nop
nop
BC tx_set, tc ; if tx = 1 then can not overwrite data
xorm #2h, @_UART_reg ; if tx = 0 set TX=1
format:
*******************Parity formatting of tx data***********
** only compiled if parity set at compile time **
**********************************************************
.if parity != 0
ld @_tx_datapass, A ; load temp data for parity check
call parity_calc ; calculate parity_value
.endif
**********************************************************
ld @_tx_datapass, start_bits, A ; load temp data (offset by
; start_bit)
nop
;stl A,-1, *AR3+% ; added for debugging
add #stop_or, A ; add stop bits
**********************Add parity bit**********************
** only compiled if parity set at compile time **
**********************************************************
.if parity != 0
bitf @_parity_value, #1h ; check parity value
nop
nop
.if parity = 1
xc 2, ntc
add #1h, start_bits+data_bits, A ; add a 1 to make
; odd parity
; add #parity_or, A
; add a 1 to make
; odd parity
.elseif parity =2
xc 2, tc
add #1h, start_bits+data_bits, A ; add a 1 to make
; even parity
; add #parity_or, A
; add a 1 to make
; even parity
.endif
.endif
*********************************************************
bitf @_UART_reg, #8h ; is this a delayed transmit?
sth A,@tx_datahi ; store out data for transmit
stl A,@_tx_datalo
;stl A, *AR3+% ; added for debugging
; return to user program if delayed
td_end: bc tx_end, tc ; transmit - wait for rcv interrupt
nop
nop
waiting: BITf @_UART_reg, #1h ; what is RCV flag?
nop
nop
BC waiting, tc ; if recieving have to wait until done
; can insert user program if desired
st #8h, *(IMR) ; disable hw_int0, timer enabled
call timerwhole ; setup timer for 1 bit and start
xorm #4h, @_UART_reg ; set UART=1, going to transmit
st #0, @_UART_count ; initialize UART_count
tx_end: ssbx cpl ; reset cpl=1
popm st0 ; restore contents and status
popm ag
popm ah
popm al
t_end ret
_transmit_delay: xorm #8h, *(_UART_reg) ; enable delay bit (this is a
; delayed tx)
B _transmit
nop
nop
*********************Receive routine*************************************
*This is the hw_int routine that will be executed when a start *
*bit is detected. The routine checks the status of transmit and starts *
*the timer for a half bit time so the program can be sure a true start *
*bit not a glitch was detected. It also resets the counter, and *
*receive data locations. *
* *
*If the device is finishing a transmit routine, the TX and UART flags *
*will be reset. If the transmit_delay flag is set, the routine will *
*setup for a transmit during the receive. *
*************************************************************************
_hw_int0: pshm st0 ; context save
rsbx cpl ; enable direct addressing mode
ld #_UART_reg, DP ; setup DP for addressing
bitf *(_UART_reg), #8h ; is txd set?
call timerhalf ; setup and start time for half bit
xc 2, tc ; if delayed transmit
xorm #0ch, @_UART_reg ; reset TXD bit and set UART bit
end: ST #0, @_UART_count ; initialize UART counter
st #8h, *(IMR) ; disable int0
st #0, @_rcv_datalo
st #0, @rcv_datahi
xorm #1h, @_UART_reg ; set RCV flag = 1
; return with timer enabled
ssbx cpl
popm st0 ; context restore
hw_end: rete
|