DSPRelated.com
Forums

UART with GPIO pins

Started by Lucy Jordan October 10, 2003
I am trying to implement a UART based on the TI app note:

Implementation of a Software UART on TMS320C54x Using General-Purpose I/O Pins

The receive part works great but I cannot figure out why the transmit does not
work. I am using Hyperterm to verify everything but it does not receive anything
after I call the transmit function. Any ideas?


************************************************************
*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




Lucy-

> I am trying to implement a UART based on the TI app note:
>
> Implementation of a Software UART on TMS320C54x Using General-Purpose I/O Pins
>
> The receive part works great but I cannot figure out why the
> transmit does not work. I am using Hyperterm to verify
> everything but it does not receive anything after I call the
> transmit function. Any ideas?

Is Hyperterminal set for Hardware Flow Control or Software Flow Control
(XON/XOFF)?
You might try Software to take your CTS, RTS, etc. lines out of the equation.
Sometimes when you get one direction working and not the other the issue is the
handshake lines.

-Jeff