This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

SW-EK-TM4C1294XL: UART3 errors when receiving and transmitting using interupts.

Part Number: SW-EK-TM4C1294XL
Other Parts Discussed in Thread: TM4C1294NCPDT

This is the information I have from our engineer.

guessing a collision with UART3_DR_R.

You'll see by the attached .c file I've done a (sloppy) bypass of the UART3_DR_R in the code. See the static volatile unsigned char "check"; at line 65 of the file.

The data echo is very close using the bypass "check" $GPGLL, becomes RRu111 for some reason at the beginning of the data burst).

the balance of the data is good.

/*
* Read serial input and echo it back in hex.
*
* This version uses UART3 on PA4/PA5.
*
* Pin I/O Function
*
* PA4 I serial-line input (TTL level)
* PA5 O serial-line output (TTL level)
* PF0 O LED D4
* PF4 O LED D3
* PN0 O LED D2
* PN1 O LED D1
*
* UART RX TX
* 0 PA0 PA1
* 1 PB0/PQ4 PB1
* 2 PA6/PD4 PA7/PD5
* 3 PA4/PJ0 PA5/PJ1
* 4 PK0/PA2 PK1/PA3
* 5 PC6 PC7
* 6 PP0 PP1
* 7 PC4 PC5
*/

#include "bug-workaround.h"

#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>

#include "hw_memmap.h"
#include <tm4c1294ncpdt.h>

#include "driverlib/sysctl.h"

#define BAUDRATE 4800
#define SERTXBSIZE 65536
#define IRQ_UART3 ( INT_UART3 - 16)

/*
* Initialized data does not seem to work(!!); when I initialized an
* array, it did not contain what I initialized it to even right at
* entry to main(). Apparently initialized data simply isn't
* initialized. Makes me wonder what other aspects of C they botched!
*
* But that's why we don't initialize anything here. Instead, see
* init_data().
*
* Comments of the form `"string" -jjrh' are from jjrh's code,
* sometimes with additional commentary added.
*
* Note the volatile qualifiers on the things which are accessed by
* both the main line and the interrupt handler.
*/

static unsigned int cfreq;
static volatile unsigned char txbuf[SERTXBSIZE];
static volatile int txbh;
static volatile int txbt;
static volatile int txbn;
static volatile int outbv;
static volatile int txon;

static volatile unsigned char check;

/*
* See above about initialized data. This compensates for the botchery
* outlined there.
*
* This initializes just the things that shouldn't be touched on
* reinit (cf reinit_data()).
*/
static void init_data(void)
{
}

/*
* This is just like init_data(), except it's the stuff that _should_
* be done on reinit.
*/
static void reinit_data(void)
{
}

/*
* Yell for human help. The idea is that a human can notice the light
* pattern and attach with a debugger to look at blinky_reason.
*/
static volatile const char *blinky_reason __attribute__((__used__));
void blinky(const char *reason)
{
unsigned char b;
int i;

NVIC_DIS0_R = 0xff;
blinky_reason = reason;
b = 0;
while (1)
{ GPIO_PORTK_DATA_BITS_R[2] = b;
for (i=10000000;i>0;i--) ;
b ^= 2;
}
}

static void setup(void)
{
unsigned int brgdiv;

// If you change 120000000 here, check the MIIADDR setting!
cfreq = SysCtlClockFreqSet(SYSCTL_XTAL_25MHZ|SYSCTL_OSC_MAIN|SYSCTL_USE_PLL|SYSCTL_CFG_VCO_480,120000000);
/*
* Spin up ports A, F, and N (5.5, page 382), others down.
* The uses for the ports:
*
* A: A4 is serial in, A5 is serial out (both TTL-level)
* F: F0 drives D4, F4 drives D3
* N: N0 drives D2, N1 drives D1
*
* QPN MLKJ HGFE DCBA
* 001 0000 0010 0001
*/
SYSCTL_RCGCGPIO_R = 0x1021;
// Enable UART 3. Be careful; UART 0 may already be enabled.
SYSCTL_RCGCUART_R |= 1 << 3;
/*
* "There must be a delay of 3 system clocks after [a port's] clock is
* enabled before any [of its] registers are accessed." --10.5
*
* These three register reads are just a simple way to burn at least
* three clock cycles. (I'm assuming a register read takes at least
* one clock.)
*/
(void)SYSCTL_RCGCGPIO_R;
(void)SYSCTL_RCGCGPIO_R;
(void)SYSCTL_RCGCGPIO_R;
/*
* Set directions. The only pins we want as outputs are F0, F4, N0,
* and N1.
*/
GPIO_PORTF_AHB_DIR_R = 0x11;
GPIO_PORTN_DIR_R = 0x03;
/*
* Ports F and N should be digital I/O, not alternative function; port
* A pins 4 and 5 should be alternative function.
*/
GPIO_PORTA_AHB_AFSEL_R = 0x30;
GPIO_PORTF_AHB_AFSEL_R = 0;
GPIO_PORTN_AFSEL_R = 0;
/*
* UART 3 is peripheral function 1 for A4/A5.
*
* +-------------------------- peripheral function for port pin 5
* | +------------------ port pin 5
* | | +----------- peripheral function for port pin 4
* | | | +--- port pin 4
* | +-|------|-----+-|--- bits in PCTL for each port bit
* v v v v v v
* (1 << (4*5)) | (1 << (4*4));
*
* All others are implicitly zero, which is the "GPIO, not alternative
* function" setting.
*/
GPIO_PORTA_AHB_PCTL_R = (1 << (4*5)) | (1 << (4*4));
/*
* Drive strength on everything should be 8mA. For this, we use
* EDM=11, DR8R=1, DR4R=1, DR2R=0. I'm not sure why DR4R is 1, but
* that's the setting used by TI's code. We set DR4R before DR8R
* because that's how TI's code does it. My reading of the hardware
* doc is that setting DR8R clears DR2R and DR4R for those pins.
*
* But see PDF pages 752 and 753.
*/
GPIO_PORTF_AHB_PC_R = (3 << (2*4)) | (3 << (2*0));
GPIO_PORTF_AHB_DR2R_R = 0;
GPIO_PORTF_AHB_DR4R_R = 0x11;
GPIO_PORTF_AHB_DR8R_R = 0x11;
GPIO_PORTN_PC_R = (3 << (2*4)) | (3 << (2*0));
GPIO_PORTN_DR2R_R = 0;
GPIO_PORTN_DR4R_R = 0x11;
GPIO_PORTN_DR8R_R = 0x11;
/*
* Drive strength on A4/A5 should be 8mA. We actually need this for
* only the output pin; these settings are ignored for the input pin.
*/
GPIO_PORTA_AHB_PC_R = (3 << (2*5)) | (3 << (2*4));
GPIO_PORTA_AHB_DR2R_R = 0;
GPIO_PORTA_AHB_DR4R_R = 0x30;
GPIO_PORTA_AHB_DR8R_R = 0x30;
/*
* Turn on digital enable for the pins we use.
*
* We use A4 A5 F0 F4 N0 N1
*/
GPIO_PORTA_AHB_DEN_R = 0x30;
GPIO_PORTF_AHB_DEN_R = 0x11;
GPIO_PORTN_DEN_R = 0x03;
/*
* Enable interrupts only later; we have state to set up first.
*/
// Set up UART3.
while (UART3_FR_R & UART_FR_BUSY) ;
UART3_LCRH_R &= ~UART_LCRH_FEN;
UART3_CTL_R = 0;
/*
* Conceptually,
* BRD = sysclk / (clkdiv * baudrate)
* where sysclk is what we call cfreq, clkdiv is 16 or 8 according as
* whether HSE is clear or set in _CTL_R, and baudrate is (duh!) the
* desired baud rate. This BRD value is not necessarily an integer.
* The integer part is stored in IBRD, with the high 6 bits of the
* fractional part stored in FBRD. IBRD and FBRD values take effect
* only after a write to LCRH.
*
* Our clkdiv is 16. Conceptually, we compute BRD*128, add 1 (for
* rounding), and divide by 2. Since clkdiv is 16 and 128/16 is 8,
* this turns into....
*/
brgdiv = (((cfreq * 8) / BAUDRATE) + 1) / 2;
UART3_IBRD_R = brgdiv / 64;
UART3_FBRD_R = brgdiv % 64;
UART3_LCRH_R = UART_LCRH_WLEN_8;
// UART3_CC_R default value should be good
UART3_CTL_R = UART_CTL_RXE | UART_CTL_TXE | UART_CTL_UARTEN;
txon = 0;
}

/*
* We want to copy volatile data into a volatile buffer. This would be
* just plain bcopy/memcpy, except that that doesn't take
* volatile-qualified pointers and doesn't promise volatile-style
* semantics. So....
*/
static void copyvtov(const volatile void *from, volatile void *to, int nb)
{
const volatile unsigned char *fp;
volatile unsigned char *tp;

fp = from;
tp = to;
for (;nb>0;nb--) *tp++ = *fp++;
}
/* ======================================================================================================================================= */
/*
* Send a byte to the UART's transmitter. This is out in its own
* function because it's needed two places: in the transmit interrupt
* handler and when sending a byte when the transmitter is idle.
*
* According to the hardware doc - see page 1171's description of when
* the transmit interrupt is triggered, and note we're running with
* FIFOs off - an interrupt is requested when the holding register
* _is_ empty. This appears to be a lie; the behaviour I see is
* consistent with the theory that it interrupts when the holding
* register _becomes_ empty. Thus, if we just turn on interrupts
* without pushing a byte out, we'll never get a transmit interrupt.
* We could work around this in the interrupt handler by ignoring the
* UART_RIS_TXRIS bit and always checking the transmitter, but even
* then output gets delayed until the ISR is next entered for some
* other reason, typically the next received character.
*
* So, we push a byte out when the transmitter was idle. Detecting
* this condition is what txon exists for.
*/
static int xmit_send( void)
{
if ( txbn > 0)
{
UART3_DR_R = txbuf[ txbt];
UART3_DR_R = check;
txbt ++;

if ( txbt >= SERTXBSIZE)
txbt -= SERTXBSIZE;
txbn --;

return( 1);
}

return( 0);
}
/* ======================================================================================================================================= */
/*
* Queue a string of bytes for serial output. This just copies them
* into the transmit output buffer and, if the transmitter was idle
* (see the comment on xmit_send), pushes the first byte to the
* hardware.
*/
static void queue_serial_tx( volatile const char *s, int len)
{
if ( len < 0)
blinky( "serial transmit negative length");

if ( txbn + len >= SERTXBSIZE)
blinky( "serial transmit overflow");

if ( txbh + len <= SERTXBSIZE)
{
copyvtov( s, &txbuf[ txbh], len);
}
else
{
copyvtov( s, &txbuf[ txbh], SERTXBSIZE - txbh);
copyvtov( s + ( SERTXBSIZE - txbh), &txbuf[ 0], len - ( SERTXBSIZE - txbh));
}

txbh += len;

if ( txbh >= SERTXBSIZE)
txbh -= SERTXBSIZE;

txbn += len;
// See the comment on xmit_send().
if ( !txon)
{
txon = 1;
UART3_IM_R |= UART_IM_TXIM;
xmit_send();
}
}
/* ======================================================================================================================================= */
/*
* Queue a received byte (plus status) as three-digit hex with a space
* before it. This provides echo that is visibly different from the
* input, but derived therefrom in a way that's easy to check.
*/
static void queue_serial_tx_hex(unsigned int v)
{
// unsigned char buf[16];
// unsigned char *bp;
unsigned char c;

// bp = &buf[16];
// *--bp = "0123456789abcdef"[v&15];
// *--bp = "0123456789abcdef"[(v>>4)&15];
// *--bp = "0123456789abcdef"[(v>>8)&15];
// *--bp = ' ';
c = v;
c = check;
// check = 0;

// printf( "%c", &c);
//c = 0x42;
//if (!( c & 0xf00))
queue_serial_tx(( void *)&c, 1);

// queue_serial_tx((void *)bp,&buf[16]-bp);
}
/* ======================================================================================================================================= */
/*
* Push a 4-bit status value to the on-board LEDs. The low four bits
* of b are used to drive D1-D4.
*/
static void show_bits( unsigned char b)
{
GPIO_PORTF_AHB_DATA_BITS_R[ 0x01] = b >> 3;
GPIO_PORTF_AHB_DATA_BITS_R[ 0x10] = b << 2;
GPIO_PORTN_DATA_BITS_R[ 0x01] = b >> 1;
GPIO_PORTN_DATA_BITS_R[ 0x02] = b << 1;
}
/* ======================================================================================================================================= */
/*
* Set the priority of an interrupt. See the NVIC's PRIx register
* descriptions on pages 159 and following.
*/
static void set_int_pri( int irq, int pri)
{
volatile uint32_t *rp;
int s;


rp = &( &NVIC_PRI0_R)[ irq >> 2];
s = 5 + (( irq & 3) << 3);
rp[ 0] = ( rp[ 0] & ~( 7 << s)) | (( pri & 7) << s);
}
/* ======================================================================================================================================= */
/*
* Tell the NVIC to listen to a specific interrupt.
*/
static void enable_int( int irq)
{
( &NVIC_EN0_R)[ irq >> 5] = 1 << ( irq & 31);
}
/* ======================================================================================================================================= */
/*
* Enable interrupts from the serial port we're using. This includes
* configuring the NVIC.
*/
static void enable_serial_interrupts( void)
{
// Configure interrupt priorities.
NVIC_APINT_R = NVIC_APINT_VECTKEY | NVIC_APINT_PRIGROUP_7_1;
set_int_pri( IRQ_UART3, 0);
// Tell the NVIC to listen to the interrupts.
enable_int( IRQ_UART3);
// Tell the UART it can interrupt.
UART3_IM_R = UART_IM_TXIM | UART_IM_RXIM;
}
/* ======================================================================================================================================= */
/*
* UART interrupt handler.
*
* Use is not in this file; see g_pfnVectors[] in startup_gcc.c.
*
* In principle, we need to be careful here to make sure this code and
* queue_serial_tx don't collide badly, which would mean using
* volatile carefully and doing things in the correct order. In this
* particular case, though, we can ignore the issue because
* queue_serial_tx is never called except (indirectly) from here.
*/
void isr_uart3( void)
{
unsigned int ris;
unsigned int v;


ris = UART3_RIS_R;

if ( ris & UART_RIS_RXRIS)
{
if (! ( UART3_FR_R & UART_FR_RXFE))
{
//UART3_DR_R = 0x42;
v = UART3_DR_R;
check = v;
queue_serial_tx_hex( v);
}
}

if ( ris & UART_RIS_TXRIS)
{
if ( !( UART3_FR_R & UART_FR_TXFF))
{
if ( !xmit_send())
{
txon = 0;
UART3_IM_R &= ~UART_IM_TXIM;
}
}
}
}
/* ======================================================================================================================================= */
/*
* main(). Just initialize, enable interrupts, and then loop, counting
* up on D1-D4, incrementing every million times around the loop, to
* provide an easy human-visible indication of liveness. (If we hang
* or livelock, the lights should freeze.)
*/
int main(void);
int main(void)
{
unsigned int c;


init_data();

// setup();
// enable_serial_interrupts();

while ( 1)
{
reinit_data();
setup();
outbv = 8;
enable_serial_interrupts();
c = 0;

while ( 1)
{
if ( c < 1)
{
c = 1000000;
outbv ++;
}
else
{
c --;
}

show_bits( outbv);
}
}
}

  • Yes, you are correct, the TXRIS (UART transmit raw interrupt status bit), when in non-FIFO mode is set when the UARTDR register becomes empty and is ready to receive a new character. I will make a note to clarify the documentation, but there is no near-term update planned of the datasheet.

    You would save yourself a lot of effort if you were to use the TivaWare library functions provided. Also, see item 4 of the "Read Before Posting" thread concerning the use of direct register modification. https://e2e.ti.com/support/microcontrollers/other/f/908/t/695568

  • Bob,

    There was no suggested solution to fix the issue as we are directly accessing the registers.

    We are studying the TivaWare library functions and examples.

    Hoping to approach it with Tiva functions to prevent collisions.

    Regards