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.

EK-TM4C123GXL: Couple UART Initialization Questions

Other Parts Discussed in Thread: EK-TM4C123GXL

I have a couple questions about UART. Previously I set this up and working with DRM, but now I am trying to replicate it with TivaWare.

1. For my interrupts, I need to set Tx to End of Transmit (EOT), and Rx to trigger when a single data byte is received in the Rx Fifo. I set the TxIntMode to EOT, but then I have to use the FifoLevelSet function to set the RX interrupt level to one byte. This requires me to write a trigger level to the TX Fifo even though I want this in EOT mode. Will this overwrite my Tx EOT mode set?

2. When I programmed UART in DRM, there was no function to call to link an ISR function to an interrupt. I went into the startup_rvmdk file and wrote in the ISR, linking the ISR function to the interrupt there. But now I see this UARTIntRegister function which links a function to my uart interrupt. Do I still need to edit the startup_rvmdk file?

Thanks! Here's my init code so far if you care to take a look.

void uart_init(){
	//initializes UART1 
	//sets up transmit and receive interrupts
	
	//0. Init Clock to UART and PortC
	SysCtlPeripheralEnable(SYSCTL_PERIPH_UART1);
	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOC);
	
	//Give it time for clocks to start
	SysCtlDelay(10);
	//Disable UART before programming
	UARTDisable(UART1_BASE);
	
	//1.  set baud rate, txe/rxe, stp2 (clear then and fen), and wlen (8 bit_
	UARTConfigSetExpClk(UART1_BASE, SysCtlClockGet(), 9600, (UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE | UART_CONFIG_PAR_NONE));
	UARTFIFOEnable(UART1_BASE);
	
	//1.5. Set up AFSEL To C4, C5
	GPIOPinConfigure(GPIO_PC4_U1RX);
	GPIOPinConfigure(GPIO_PC5_U1TX);
	
	//Set C4 as Input (RX) and C5 as Output (TX)
	GPIOPinTypeGPIOOutput(GPIO_PORTC_BASE, GPIO_PIN_5);
	GPIOPinTypeGPIOInput(GPIO_PORTC_BASE, GPIO_PIN_4);
	
	
	//Enable Global NVIC Interrupt
	IntEnable(INT_UART1);
	
	//Enable Local Interrupts
	UARTIntEnable(UART1_BASE, (UART_INT_TX | UART_INT_RX)); //enable Tx and Rx int
	UARTTxIntModeSet(UART1_BASE, UART_TXINT_MODE_EOT); //set Tx mode to EOT
	UARTFIFOLevelSet(UART1_BASE, UART_FIFO_TX1_8, UART_FIFO_RX1_8); //set Rx to trigger with one byte
	
	//Disable for now so I don't get stuck in the TX isr.
	UARTIntDisable(UART1_BASE, UART_INT_TX);
	
	//Link a function to the UART Interrupt
	UARTIntRegister(UART1_BASE, isr_uart1);
	
	//Enable UART1
	UARTEnable(UART1_BASE);
}

  • Hello Zero_PD,

    1. When the EOT bit is set and the UART is using the FIFO then the TXRIS interrupt will be asserted only when the last data bit has cleared the serializer. So the EOT behavior will continue to work as expected.
    2. No you do not need to. Doing so will only cause the interrupt vector to be remapped to SRAM instead of using the Flash Mapped Interrupt vector table.

    Regards
    Amit
  • Great, thanks Amit! I'm going to give this a shot tonight. If I run into any problems I can't solve, I'll post in this thread.
  • Hi Amit - another quick question for you.

    I've been looking through the TivaWare Peripheral Library document but I'm having some trouble figuring out how to poll what triggered my UART ISR without resorting to DRM. When using DRM I would pull the raw interrupt status register to find out whether a TX or an RX got me into the ISR. I think I need to use the UARTIntStatus() function for this, but the description is unclear. If I write uint32_t x = UARTIntStatus(UART1_BASE, true) does this return the interrupt mask of the raised flag? What if more than one flag is high - does it OR them together?

    Does it look like this short block of code would function, or am I using the UARTIntStatus function incorrectly?

    void isr_uart1(void){
    
    	uint32_t trigger;
    	trigger = UARTIntStatus(UART1_BASE, true);
    	
    	if (trigger == UART_INT_RX){ //Receive bye triggered isr
    		printf("Byte received");
    		UARTIntClear(UART1_BASE, UART_INT_RX);
    		}
    	if (trigger == UART_INT_TX){ //a finished transmited triggered isr
    		printf("Byte transmitted");
    		UARTIntClear(UART1_BASE, UART_INT_TX);
    		UARTIntDisable(UART1_BASE, UART_INT_TX);
    	}	
    }

  • Hello Zero_PD,

    It will return the MIS register value. Now in the if condition you would need to have the correct check as shown below

    if ((trigger & UART_INT_RX)== UART_INT_RX){ //Receive bye triggered isr
    if ((trigger & UART_INT_TX) == UART_INT_TX){ //a finished transmited triggered isr

    Regards
    Amit
  • Thanks again Amit, that helped a lot. Here's another question for you and anyone else who cares to take a look.

    Right now my goal is to send and receive a byte via UART. To that end, I've connected C4 to C5 (UART1 RX to UART1 TX). I want to get into the ISR, and get into the TX and RX part of the ISR. I've set Tx interrupts to EOT, and Rx interrupts to 1/8 FIFO Level.

    The problem is I'm not getting into the Rx portion of the ISR. Because I haven't gotten print statements to work (a separate issue with teraterm and/or my initialization - I'm working that issue in parallel), I'm using LEDs to make sure I'm getting into the transmit and receive isr. The LED I turn on in the Tx ISR is turning on, meaning I've finished shifting a byte out and my transmit fifo is now empty. But the receieve ISR LED isn't turning on, which would mean I've received a byte.

    I've spent some time troubleshooting, going through each line of my code and comparing that to my former DRM code. I figured the issue had to lie with my interrupt initialization or the interrupt settings. I set the level to the lowest (1/8), assuming this meant after I received one data byte, my receive isr would trigger. Finally, crawling through the EK-TM4C123gxl datasheet, I found this paragraph:

    The trigger points at which the FIFOs generate interrupts is controlled via the UART Interrupt FIFO
    Level Select (UARTIFLS) register (see page 922). Both FIFOs can be individually configured to
    trigger interrupts at different levels. Available configurations include ⅛, ¼, ½, ¾, and ⅞. For example,
    if the ¼ option is selected for the receive FIFO, the UART generates a receive interrupt after 4 data
    bytes are received.

    I now think the Tiva I'm using now may be a slightly different model than the one used in my class. The FIFO stack was 8 high in my class, but this text makes me believe the FiFo stack on he 123gxl is 16 high. This would explain why my receive isr is never triggering - it's waiting for the Fifo to be 2, not one data byte high, and I'm only sending one at a time.

    Does this seem correct - and if so, is there any way to make my receive ISR trigger after one data byte is received?

    Thanks! My init code and isr are below if one cares to look.

    //__________________________________________________________
    //
    //This module handles uart init
    //and any other uart necessary permutations
    //________________________________________________________
    
    #include <stdint.h>
    #include <stdbool.h>
    #include "hw_gpio.h"
    #include "hw_ints.h"
    #include "hw_nvic.h"
    #include "hw_sysctl.h"
    #include "hw_uart.h"
    #include "hw_types.h"
    #include "hw_memmap.h"
    #include "gpio.h"
    #include "pwm.h"
    #include "sysctl.h"
    #include "cycle.h"
    #include "uart.h"
    #include "pin_map.h"
    #include "interrupt.h"
    #include "termio.h"
    
    
    void isr_uart1(void);
    void uart_init(void);
    
    void uart_init(){
    	//initializes UART1 
    	//sets up transmit and receive interrupts
    	
    	//0. Init Clock to UART and PortC
    	SysCtlPeripheralEnable(SYSCTL_PERIPH_UART1);
    	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOC);
    	
    	//Give it time for clocks to start
    	SysCtlDelay(10);
    	//Disable UART before programming
    	UARTDisable(UART1_BASE);
    	
    	//1.  set baud rate, txe/rxe, stp2 (clear then and fen), and wlen (8 bit_
    	UARTConfigSetExpClk(UART1_BASE, SysCtlClockGet(), 9600, (UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE | UART_CONFIG_PAR_NONE));
    	UARTFIFOEnable(UART1_BASE);
    	
    	//1.5. Set up AFSEL To C4, C5
    	GPIOPinConfigure(GPIO_PC4_U1RX);
    	GPIOPinConfigure(GPIO_PC5_U1TX);
    	
    	//Set C4 as Input (RX) and C5 as Output (TX)
    	GPIOPinTypeGPIOOutput(GPIO_PORTC_BASE, GPIO_PIN_5);
    	GPIOPinTypeGPIOInput(GPIO_PORTC_BASE, GPIO_PIN_4);
    	
    	
    	//Enable Global NVIC Interrupt
    	IntEnable(INT_UART1);
    	
    	//Enable Local Interrupts
    	UARTIntEnable(UART1_BASE, (UART_INT_TX | UART_INT_RX)); //enable Tx and Rx int
    	UARTTxIntModeSet(UART1_BASE, UART_TXINT_MODE_EOT); //set Tx mode to EOT
    	UARTFIFOLevelSet(UART1_BASE, UART_FIFO_TX1_8, UART_FIFO_RX1_8); //set Rx to trigger with one byte
    	
    	//Disable for now so I don't get stuck in the TX isr.
    	UARTIntDisable(UART1_BASE, UART_INT_TX);
    	
    	//Link a function to the UART Interrupt
    	UARTIntRegister(UART1_BASE, isr_uart1);
    	
    	//Enable UART1
    	UARTEnable(UART1_BASE);
    }
    
    void isr_uart1(void){
    
    	uint32_t trigger;
    	uint32_t data;
    	trigger = UARTIntStatus(UART1_BASE, true);
    	
    	if ((trigger & UART_INT_RX)== UART_INT_RX){ 
    		//printf("Byte received");
    		GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_3, 0xFF); //turn this led on if in receive isr
    		data = UARTCharGet(UART1_BASE);
    		UARTIntClear(UART1_BASE, UART_INT_RX);
    		}
    	if ((trigger & UART_INT_TX) == UART_INT_TX){ 
    		//printf("Byte transmitted"); 
    		GPIOPinWrite(GPIO_PORTB_BASE, GPIO_PIN_2, 0xFF); //turn this led on if in transmit isr
    		UARTIntDisable(UART1_BASE, UART_INT_TX);
    		UARTIntClear(UART1_BASE, UART_INT_TX);
    
    	}	
    }
    

  • Hello Zero_PD

    TM4C always had the buffer of 16 bytes. Could it be the device used in your class was a LM3!!!

    Regards
    Amit
  • You should get a receive timeout interrupt. Glancing at the documentation I was surprised to find that it appears to be separate from the receive interrupt and could be disabled. You want to enable it and treat it like a receive interrupt.

    Robert
  • Hello Robert,

    I am interested in which TM4C12x device came with 8 FIFO locations...

    Regards
    Amit
  • That's a great idea, Robert. After looking through the datasheet that should be able to function as my one-data-byte interrupt. Thanks for the suggestion.
  • I'm tormented by poster's "modification" to standard UART set-up/config via his penchant:   (also by the relentless sci-fi blurbs)

        //Set C4 as Input (RX) and C5 as Output (TX)
        GPIOPinTypeGPIOOutput(GPIO_PORTC_BASE, GPIO_PIN_5);
        GPIOPinTypeGPIOInput(GPIO_PORTC_BASE, GPIO_PIN_4);
     
    He's suggested (even) to others that such is "good practice!"  
     
    If that's true - my small firm has NEVER done such - and multiple UARTs - across several generations of LM3S, LX4F (may both rest in peace) and TM4C123 all work to specification.    One suspects that the (proper) GPIOPinTypeUART() would manage such GPIO setting - and not require poster, "over-ride."  
    Might Amit and/or Robert care to comment?   "Couple questions" has dragged on - might (unwarranted) poster "invention" have contributed?
  • Hi CB1,


    At the moment, everything is working properly. So there are no more questions.

    However, you do raise one interesting thought about "specification". In the future, I'll dig more through example code to find how ti employees initialized their subsystems. As I've mentioned before, I am used to programming the tiva launchpad in DRM. There I set, the AFSEL, program the PCTL register, and then set the appropriate tx/rx pins to output/input using GPIO_O_DIR.

    	//6. Alternate Function Select
    		HWREG(GPIO_PORTC_BASE+GPIO_O_AFSEL) |= (
    		BIT4HI | BIT5HI); //select ports 0 and 1 to have alternate functions
    			//mux val. here's where *** gets a little weird
    		  HWREG(GPIO_PORTC_BASE+GPIO_O_PCTL) = 
        (HWREG(GPIO_PORTC_BASE+GPIO_O_PCTL) & 0xff00ffff) + (2<<(4*BitsPerNibble)) +
          (2<<(5*BitsPerNibble));
    			//this should do it
    
    	//7. DEN and DIR
    			HWREG(GPIO_PORTB_BASE+GPIO_O_DEN) |= (BIT4HI|BIT0HI|BIT1HI|BIT2HI|BIT3HI);
    			HWREG(GPIO_PORTB_BASE+GPIO_O_DIR) &= ~(BIT4HI|BIT0HI|BIT1HI|BIT2HI|BIT3HI);
    			HWREG(GPIO_PORTB_BASE+GPIO_O_PUR) |= (BIT4HI|BIT0HI|BIT1HI|BIT2HI|BIT3HI);

    This is what I was "translating" into TivaWare. I did not use GPIOPinTypeUart because I did not know of its existence. I called PinConfigure, and then GpioPinTypeX. My theory as to why it didn't work was that, while I thought GPioPinConfigure was taking care of writing to the AFSEL and PCTL register, it may have only been writing to the AFSEL register. So I opened up gpio.c and it turns out I had it flipped - PinConfigure writes the mux value to the PCTL register, but embedded within PinTypeUART is the write to the AFSEL register, as well as the DEN and DIR registers.

  • Well - must applaud that clarity of writing - and your diligence in searching, finding and presenting facts here. Good for you - appreciated.

    Perhaps you can "edit away" your past "over-ride" of "PinTypeUART" - so that others do not adopt that approach.

    Know that the past LMI team - not this vendor - created the majority of API code which (while repainted) still works wonders.

    Believe you'll: "Speed, Ease & Enhance" your MCU development efforts via continued use of this effective & expansive API.
  • Hello Zero_PD,

    The reason to have PCTL and AFSEL/DEN as separate was to prevent an over burden of an API to do multiple tasks. As an example while the PCTL is configured by GPIOPinConfigure, the other properties of the IO could be easily handled in the GPIOPinTypeXXXyyy where XXX is the peripheral function and yyy is additional information, e,g, GPIOPinTypeI2CSCL.

    Regards
    Amit