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);
}
}
}