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.

TM4C123GH6PM: UART TX Interrupt Lost

Part Number: TM4C123GH6PM

Hello!


I have quite a big project that consists of many modules. One module is responsible for communication with the host system via UART. I disable FIFO and use one interrupt per sent byte, my problem is that when I use UARTTxIntModeSet(UART_BASE, UART_TXINT_MODE_FIFO) then at some random moment of time interrupt is not fired during the message transmission. It can happen after millions of successful transactions, absolutely randomly. On the other hand, when I use UARTTxIntModeSet(UART_BASE, UART_TXINT_MODE_EOT) everything works fine, but I'm interested in why with the MODE_FIFO such a strange thing occur, and also because I will use FIFO interrupts in my other project.

UART Initialization routine:

void UART_Host_Init(uint32_t clockRate)
{
    //
    // Enable the GPIO Peripheral used by the UART.
    //
    SysCtlPeripheralEnable(UART_HOST_PERIPH_GPIO);

    //
    // Enable UART Host clock
    //
    SysCtlPeripheralEnable(UART_HOST_CLOCK);

    //
    // Configure GPIO Pins for UART mode.
    //
    GPIOPinConfigure(UART_HOST_GPIO_RX);
    GPIOPinConfigure(UART_HOST_GPIO_TX);
    GPIOPinTypeUART(UART_HOST_GPIO_BASE_PORT, UART_HOST_GPIO_PIN_RX | UART_HOST_GPIO_PIN_TX);

    //
    // Use the internal 16MHz oscillator as the UART clock source.
    //
    UARTConfigSetExpClk(UART_HOST, clockRate, UART_HOST_SPEED, (UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_TWO | UART_CONFIG_PAR_NONE));
    UARTFIFODisable(UART_HOST);
    //
    // If the below line is un-commented, then everything works
    //
    // UARTTxIntModeSet(UART_HOST, UART_TXINT_MODE_EOT);


    //
    // Enabling RT - receive time-out, TX, and RX interrupts
    //
    IntEnable(UART_HOST_INT_INDEX);
    UARTIntEnable(UART_HOST, UART_INT_RT | UART_INT_TX | UART_INT_RX);
}

In the main loop:

    while(1)
    {
    	//
        // Some stuff happening
        //

    	if(msgSent == true)
    	{
    		msgSent = false;
    		if(UARTCharPutNonBlocking(UART_HOST, message[index++]) == false)
    		{
#ifdef DEBUG
				while(1) {};
#endif
    		}
    	}
    }

UART Interrupt:

UARTIntHandler(void)
{
	uint32_t ui32Status;

	ui32Status = UARTIntStatus(UART_HOST, true); 				// get interrupt status
	UARTIntClear(UART_HOST, ui32Status); 					// clear the asserted interrupts

	//
	// Receiving data
	//
	if((ui32Status & UART_INT_RT) || (ui32Status & UART_INT_RX))
	{
		g_ui32UARTIntReceive++;
		while(UARTCharsAvail(UART_HOST)) 						//loop while there are chars
			UARTCharGetNonBlocking(UART_HOST);					// get the received character
	}

	//
	// Sending data
	//
	if(ui32Status & UART_INT_TX)
	{
		g_ui32UARTIntTransmit++;
		if(index < LENGTH)
		{
			if(UARTCharPutNonBlocking(UART_HOST, message[index++]) == false)
			{
#ifdef DEBUG
				while(1) {};
#endif
			}
		}
		else
		{
			msgSent = true;
			index = 0;
		}
	}
}

  • Andrei,

    The idea behind UART_TXINT_MODE_FIFO is to raise an interrupt flag when, during a UART transmission, a certain number of free bytes on the FIFO becomes available again.

    Let's say you just placed 15 bytes into the FIFO. The UART hardware starts shifting out the bytes according to the baud rate configured. After 7 bytes are sent, the interrupt calls to let you know that "there are at least 8 bytes free on the FIFO, in case you need to send something else".
    The figure of 8 bytes on this example is a parameter configured via UARTFIFOLevelSet(); - you can use different levels.
    If your FIFO level was set to 50% (8 bytes), and you only write 6 bytes to FIFO, you will not get an interrupt ever (there is no crossing from 9 bytes pending down to 8). The transmission will still happen properly.

    The other option (EOT) flags the int whenever there are no more bytes in the FIFO to be sent (cross from 1 byte pending down to zero).

    Usually, there is no reason for you to monitor EOT or to worry about free FIFO after you placed all needed bytes in the FIFO: the UART hardware will always transfer those bytes for you, you can trust that!

    If you originally had more than FIFO-free bytes to transfer, I suggest you keep sending one byte at a time to FIFO until it is full (check inside the UARTCharPutNonBlocking function to see how it works). Then, configure the interrupt to let you know when there is free space again, and only then resume sending another set of bytes.

    Rgds

    Bruno
  • Dear Bruno Saraiva ,

    Thank you for your answer. My understanding of the FIFO and associated interrupts usage is exactly the same.

    I will formulate my question other way: if you disable FIFO (and it effectively becomes 1-byte-deep holding register) is there any difference in how TX interrupt will get fired depending on the EOT bit in UARTCTL register?

    As I see it, it should now work in the same fashion independent of EOT bit: whenever your one and only byte is fully shifted, you get your interrupt.

  • Andrei Gasilovs said:
    As I see it, it should now work in the same fashion independent of EOT bit: whenever your one and only byte is fully shifted, you get your interrupt.

    You might consult the datasheet/reference manual, but I would disagree. Don't know the TM4C UART in detail, but others. And there, the "TX empty" flag is set as soon as the TX register is copied to the shift register. That is, when the actual send just starts. "TX empty" is not "End of transmission" (EOT).

  • Yes, I have experienced such behavior too, for instance with C2000 MCUs.

    And that's my question for TM4C123. When TX interrupt is fired if FIFO is disabled and EOT is not set? I can not figure it out form the datasheet and TRM...

  • OK, I tested it with the timer. It works as f. m. described. When FIFO is disabled interrupt is fired when byte is transferred to the shift register, and EOT is fired when the whole byte is sent.

    But now back to my original question regarding why do I sometimes lose my TX interrupt in this configuration?

  • Andrei Gasilovs said:
    But now back to my original question regarding why do I sometimes lose my TX interrupt in this configuration?

    I have done such low-level stuff on other MCUs, but used only the TivaWare functions on TM4C.

    However, the usual reason to "lose" interrupts is either an overrun, or accidentally clearing of the flag. Since writing TX is synchronous, an overrun is not supposed to happen. Perhaps you are clearing the TXE flag in another interrupt routine together with other flags. This would go unnoticed until both interrupts happen concurrently. For less frequent writes, this would add a "random factor" to the issue, and make it asynchronous.

    I might look at the code and the flags if I find the time ...

  • Hi,

    One small "bemol": UARTFIFODisable() function should be used only after UARTEnable() since this silently enables the FIFO. Please look into the source code of this function.

  • f. m.


    I try to decouple my modules as much as possible. As far as I can tell, no other interrupts can affect this UART registers/flags.
    I did however noticed one strange thing: it seems to happen more often and much quicker if I decrease the MCU frequency.

    Petrei

    Yes, that's true. But I use UARTConfigSetExpClk function that calls UARTEnable internally and only after that I call UARTFIFODisable.

  • Andrei,
    It was nice reading your posts and the fact that you shared your conclusions as for the interrupt EOT/FIFO functioning, thanks!
    Some behaviors eventually fall into the "mystery pot" - particularly when they occur very randomly. But I should not even mention this, for you are clearly highly able to understand and diagnose all the events on your problem.
    I would them use my typical approach when I get tired of mysterious behaviors: eliminate the need to figure it out!
    Meaning, do you really need that interrupt? Can't you obtain all your needed results monitoring the FIFO levels (and better yet, making use of all the 16 bytes that the FIFO gives you)? If you REALLY need to be warned of EOT, it can in theory be calculated up front - you know you system clock, you know your baud rate, hence you can set a timer to alert you when this time passes - but still, maybe you can manage your transmission buffer without needing to know that the very last byte is already gone...
    Cheers
    Bruno
  • I'd like to turn your first question around: What makes you believe you are losing TX interrupts ?

    If you do not confuse the bytes you write to UART TX, there is nothing lost. The TX interrupt tells you when TX is ready again - it's still ready later on, unless you write to TX from elsewhere.

  • f. m.


    Well, that's a reasonable question. I think that I am loosing interrupts because otherwise the transmission should never stop. And it doesn't if I use EOT interrupt. This is the one and only change I make in the code: switch to either EOT or FIFO interrupt and program runs differently. With EOT everything runs nicely and with FIFO it stops sending data at random moments of time.

    Bruno Saraiva


    Yes, I totally agree with your thoughts. But you know that felling, when yet another thing falls into the "mystery pot", it increases the worlds entropy and brings anxiety.
    For the moment this case now falls under YAGNI principle and I will leave it. But I'll get back to it as soon as I'll have more spare time and will kill this little bug...

    Thanks to everyone for your help and thoughts!

  • Andrei Gasilovs said:
    switch to either EOT or FIFO interrupt and program runs differently. With EOT everything runs nicely and with FIFO it stops sending data at random moments of time.

    That's why I avoid FIFOs (or DMA) for UARTs. At least for reception, a certain fill level (number of bytes) is required to raise an interrupt. That uses to garble up variable-length transmission protocols, and often destroys the application timing.

    Not sure how the FIFO exactly works for sending, I would consult the UART section of the datasheet closely.