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.

MSP432: Baud-rate Limit for "Application/User UART COM port"

Hello,

I am experiencing problems with the data transfer between host PC and MSP432 launchpad (MSP-EXP432P401R).

On the launchpad I have configured UART port on "EUSCI_A0_MODULE"  module. The launchpad runs the program which receives a 16-bytes of data over the UART port from the host PC. The UART data reception is handled in an ISR function associated with  EUSCI_A_UART_RECEIVE_INTERRUPT.  The data is saved in a 16-byte long buffer. When the buffer is full,  the data is sent to the host over the UART. This cycle is repeated over and over again. The program on the host PC (running Ubuntu 14.10) sends 16-byte long data block over "Application/User UART COM port" of the launchpad, and then waits until the 16-byte block is received back from the launchpad.

Here is the program that I run on the launchpad, where UART baudrate is :

const eUSCI_UART_Config uartConfig =
{
        EUSCI_A_UART_CLOCKSOURCE_SMCLK,          		// SMCLK Clock Source
        12,                                      		// BRDIV = 12
        0,                                       		// UCxBRF = 0
        0,                                       		// UCxBRS = 0
        EUSCI_A_UART_NO_PARITY,                  		// No Parity
	EUSCI_A_UART_LSB_FIRST,                		        // LSB First
        EUSCI_A_UART_ONE_STOP_BIT,               		// One stop bit
        EUSCI_A_UART_MODE,                       		// UART mode
	EUSCI_A_UART_LOW_FREQUENCY_BAUDRATE_GENERATION          // No-Oversampling
};


#define BUFF_SIZE  16 
static volatile uint8_t buff[BUFF_SIZE];
static volatile uint32_t buff_indx_filling = 0;
static volatile uint32_t buff_indx_emptying = 0;
static volatile bool buff_is_empty = true; // true: empty, false: full
static volatile uint32_t timerPeriod;
void ConfigSTimer(void);


int main(void)
{
    /* Halting WDT  */
    MAP_WDT_A_holdTimer();

    /* Selecting P1.2 and P1.3 in UART mode and P1.0 as output (LED) */
    MAP_GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P1,
             GPIO_PIN2 | GPIO_PIN3, GPIO_PRIMARY_MODULE_FUNCTION);
    MAP_GPIO_setAsOutputPin(GPIO_PORT_P1, GPIO_PIN0);
    MAP_GPIO_setOutputLowOnPin(GPIO_PORT_P1, GPIO_PIN0);
    MAP_GPIO_setAsOutputPin(GPIO_PORT_P2, GPIO_PIN0);
    MAP_GPIO_setOutputLowOnPin(GPIO_PORT_P2, GPIO_PIN0);
    MAP_GPIO_setAsOutputPin(GPIO_PORT_P2, GPIO_PIN1);
    MAP_GPIO_setOutputLowOnPin(GPIO_PORT_P2, GPIO_PIN1);

    /* Setting DCO to 48MHz (upping Vcore) */
    MAP_PCM_setCoreVoltageLevel(PCM_VCORE1);
    CS_setDCOCenteredFrequency(CS_DCO_FREQUENCY_48);

    /* Configuring UART Module */
    MAP_UART_initModule(EUSCI_A0_MODULE, &uartConfig);

    /* Enable UART module */
    MAP_UART_enableModule(EUSCI_A0_MODULE);

    /* Enabling interrupts */
    MAP_UART_enableInterrupt(EUSCI_A0_MODULE, EUSCI_A_UART_RECEIVE_INTERRUPT);
    MAP_Interrupt_enableInterrupt(INT_EUSCIA0);
    
    /*configure timer*/
    ConfigSTimer();

    MAP_Interrupt_enableSleepOnIsrExit();
    MAP_PCM_gotoLPM0();
    __no_operation();
}

/* EUSCI A0 UART ISR - received data from PC host */
void euscia0_isr(void)
{
	uint32_t status = MAP_UART_getEnabledInterruptStatus(EUSCI_A0_MODULE);
	MAP_UART_clearInterruptFlag(EUSCI_A0_MODULE, status);

	if(status & EUSCI_A_UART_RECEIVE_INTERRUPT)
	{
		if (buff_is_empty == true)
		{
			buff[buff_indx_filling++] = MAP_UART_receiveData(EUSCI_A0_MODULE);
			if (buff_indx_filling == BUFF_SIZE)
			{
				buff_is_empty = false;
				buff_indx_filling = 0;

				MAP_GPIO_toggleOutputOnPin(GPIO_PORT_P1, GPIO_PIN0);

				MAP_Timer32_enableInterrupt(TIMER32_0_MODULE);
				MAP_Timer32_setCount(TIMER32_0_MODULE, timerPeriod);
				MAP_Timer32_startTimer(TIMER32_0_MODULE, false);
			}
		}
	}
}

/* TIMER32_0 ISR - echoes data back to PC host */ 
void timer0_isr(void)
{
	MAP_Timer32_clearInterruptFlag(TIMER32_0_MODULE);

	if (buff_is_empty == false)
	{
		MAP_GPIO_toggleOutputOnPin(GPIO_PORT_P2, GPIO_PIN0);
		while (buff_indx_emptying<BUFF_SIZE)
		{
			MAP_UART_transmitData(EUSCI_A0_MODULE, buff[buff_indx_emptying++]);
		}
		buff_is_empty = true;
		buff_indx_emptying = 0;

		MAP_Timer32_haltTimer(TIMER32_0_MODULE);
		MAP_Timer32_disableInterrupt(TIMER32_0_MODULE);
	}

}


void ConfigSTimer(void)
{
	MAP_Timer32_initModule(TIMER32_0_MODULE, TIMER32_PRESCALER_1, TIMER32_32BIT, TIMER32_PERIODIC_MODE);
	MAP_Interrupt_setPriority(INT_T32_INT1, zINT_PRIORITY_LEVEL_0);

        MAP_Interrupt_enableInterrupt(INT_T32_INT1);

	timerPeriod  = (uint32_t)(0.01*((double)MAP_CS_getMCLK()));
}

The data transfer works fine as long as the UART port baudrate is <=1500000. For each transmission cycle, the host successfully transmits 16-byte long data block, and receives back the same block. However, when I set the baudrate to 2000000 or 4000000, the uC seems to miss some of the bytes from the data block transmitted from the host.  In this case, the host does not receive any data from the uC, since the 'buff' does not fill up. When the baudrate is 4000000, I have to transmit 20-bytes from the host, then the uC receives only 16-bytes that fills the 'buff', and the uC echoes back the 'buff' to the host. So in this case, it appears that uC does not receive 4 bytes out of 16 bytes.  

While transmitting the 16-byte data block from the host, if I observer the signal on uC 'RXD' pin of the UART port as shown here

it appears all 16-bytes are passed from the launchpad emulator section to the uC UART, as shown in the following oscilloscope screen shot:

Can someone explain why the uC does not receive all 16-bytes. The missing of data bytes is observed at lower baudrates if I add additional  instructions within the ISR routine associated with the UART port and the echo-back function. Similarly, for a given baudrate, e.g., 4000000, more bytes are missed if more instructions are added in these functions.  

  • You could toggle an output pin like the LED1 at the begin of each RX interrupt and compare it's level to the incoming data package. Then you can see if toggling matches with a new data byte or if the data comes in faster than the MSP432 responds on it.

    Dennis

  • Hello Dennis,

    As you proposed I toggled the LED1 at the beginning of the UART RX interrupt, and compared LED signal with the UART RXD signal. To this end the ISR 'euscia0_isr' associated with the 'EUSCI_A_UART_RECEIVE_INTERRUPT'  is changed as follows (only addition is the toggling of the LED1): 

    void euscia0_isr(void)
    {
    	uint32_t status = MAP_UART_getEnabledInterruptStatus(EUSCI_A0_MODULE);
    	MAP_UART_clearInterruptFlag(EUSCI_A0_MODULE, status);
    
    	if(status & EUSCI_A_UART_RECEIVE_INTERRUPT)
    	{
    		if (buff_is_empty == true)
    		{
    			buff[buff_indx_filling++] = MAP_UART_receiveData(EUSCI_A0_MODULE);
    			if (buff_indx_filling == BUFF_SIZE)
    			{
    				buff_is_empty = false;
    				buff_indx_filling = 0;				
    
    				MAP_Timer32_enableInterrupt(TIMER32_0_MODULE);
    				MAP_Timer32_setCount(TIMER32_0_MODULE, timerPeriod);
    				MAP_Timer32_startTimer(TIMER32_0_MODULE, false);
    			}
    		}	
    MAP_GPIO_toggleOutputOnPin(GPIO_PORT_P1, GPIO_PIN0); } }

    Here are the ressults:

    1. From host I transmitted 16-byte data block. The RXD signal shows all 16 bytes transmitted to the uC, whereas the LED shows only 14 bytes --> 2 bytes missed.

    2. From host I transmitted 32-byte data block. The RXD signal shows all 32 bytes transmitted to the uC, whereas the LED shows only 27 bytes --> 5 bytes missed.

    Any idea how this data loss can be prevented.

  • Jatala,

    have a look at the UART's error flags/interrupts and use them. They might help you to figure out the problem you have.

    Dennis
  • What happens if you only use the RX interrupt, fetching the incoming bytes, disabling everything else in your program? Maybe another ISR takes too long? But iIf the posted code is your complete program, then there shouldn't be any interrupt in between. You should try disabling the LPM and just let the processor run in a while( 1 ); loop.

  • Dennis, given code is complete program. I have tried with LPM mode disabled , and still get the same results.
  • And the error flags from the UART?
  • Hello Dennis,

    To observe the receive error flags - UCFE (frame rror), UCPE (parity error), and UCOE (overrun error), I modified the ISR 'euscia0_isr' associated with the 'EUSCI_A_UART_RECEIVE_INTERRUPT'. There what I do is each timer a character is received correctly, I toggle LED1. Besides, I also check if any of the errors (UCFE, UCPE, UCOE) occurred, and then toggle LED2-GREEN. Here is the ISR code that will give you complete picture what I am doing.

    void euscia0_isr(void)
    {
    	uint32_t status = MAP_UART_getEnabledInterruptStatus(EUSCI_A0_MODULE);
    	MAP_UART_clearInterruptFlag(EUSCI_A0_MODULE, status);
    
    	if(status & EUSCI_A_UART_RECEIVE_INTERRUPT)
    	{
    		if (buff_is_empty == true)
    		{
    			buff[buff_indx_filling++] = MAP_UART_receiveData(EUSCI_A0_MODULE);
    			if (buff_indx_filling == BUFF_SIZE)
    			{
    				buff_is_empty = false;
    				buff_indx_filling = 0;
    
    				MAP_Timer32_enableInterrupt(TIMER32_0_MODULE);
    				MAP_Timer32_setCount(TIMER32_0_MODULE, timerPeriod);
    				MAP_Timer32_startTimer(TIMER32_0_MODULE, false);
    			}
    		}
    
    		MAP_GPIO_toggleOutputOnPin(GPIO_PORT_P1, GPIO_PIN0);
    	}
    
    	if (UCA0STATW & ( UCFE | UCOE |  UCPE))
    	{
    		MAP_GPIO_toggleOutputOnPin(GPIO_PORT_P2, GPIO_PIN2);
    	}
    }

    From the host PC I sent 23-byte long block, and here is what happens: 

    So out of 23 bytes, the uC received only 16 bytes. Apparently, the remaining 7 bytes are lost. When we look at the number of the errors that occurred during time,  they are are exactly 7. The given code I am running on uC is a kind of minimalistic code where I am only receiving and echoing back data over the UART. The number of lost bytes increases if I perform additional work in the program code, which essentially I have to do for my application.  

    Now the question is how to avoid these errors, or in how to recover the lost bytes. I thought the launchpad on-board USB emulator should have built in mechanism to recover or cope with this kind of situation.

  • What you really need to know is the ISR execution time relative to the time between RX character receptions. Try this:

    void euscia0_isr(void)
    {
    	MAP_GPIO_setOutputHighOnPin(GPIO_PORT_P1, GPIO_PIN0);
    	uint32_t status = MAP_UART_getEnabledInterruptStatus(EUSCI_A0_MODULE);
    	MAP_UART_clearInterruptFlag(EUSCI_A0_MODULE, status);
    
    	if(status & EUSCI_A_UART_RECEIVE_INTERRUPT)
    	{
    		if (buff_is_empty == true)
    		{
    			buff[buff_indx_filling++] = MAP_UART_receiveData(EUSCI_A0_MODULE);
    			if (buff_indx_filling == BUFF_SIZE)
    			{
    				buff_is_empty = false;
    				buff_indx_filling = 0;
    
    				MAP_Timer32_enableInterrupt(TIMER32_0_MODULE);
    				MAP_Timer32_setCount(TIMER32_0_MODULE, timerPeriod);
    				MAP_Timer32_startTimer(TIMER32_0_MODULE, false);
    			}
    		}
    
    	}
    
    	if (UCA0STATW & ( UCFE | UCOE |  UCPE))
    	{
    		MAP_GPIO_toggleOutputOnPin(GPIO_PORT_P2, GPIO_PIN2);
    	}
    	MAP_GPIO_setOutputLowOnPin(GPIO_PORT_P1, GPIO_PIN0);
    }

    This will set the LED on at the start of the ISR and turn it off at the completion of the ISR. If the ISR execution time spans into the next RX character time, then your ISR is taking too long and you won't be able to keep up.

    If you find that to be the case then you have 3 options (or combinations thereof)

    1. Decrease the amount of work you need to do while receiving characters.

    2. Decrease the rate at which RX characters arrive (ie. lower baud rate).

    3. Increase the CPU clock frequency so your code can execute faster.

    Hope that helps.

  • Jatala said:
    Now the question is how to avoid these errors, or in how to recover the lost bytes. I thought the launchpad on-board USB emulator should have built in mechanism to recover or cope with this kind of situation.

    The XDS110-ET supports hardware flow control, if desired. Hardware flow control (CTS and RTS handshaking) allows the target MSP432P401R and the emulator to tell each other to wait before sending more data.

    However, the eUSCI_A UART in the MSP432P401R:
    - Doesn't support the CTS and RTS handshake signals, meaning they would have to be controlled by software

    - Only has a single entry UCAxRXBUF

    [Compared to the UART module in Tiva C devices which has a 16-deep receive FIFO and can control the CTS and RTS handshake signals]

    Therefore, given your high baud there probably isn't enough time for the software to de-assert the flow control signals and prevent data loss.

    Maybe using DMA to transfer received bytes from the eUSCI_A UART UCAxRXBUF would help to avoid the data loss?

  • Hello Brian,

    Thanks for the helpful discussion.

    I measured execution time of the 'euscia0_isr'. The time varies from 4.156 us to 7.4 us. Most of the time it is greater than 6 us. Whereas the interval between successive bytes on the UART RXD pin is 5us.

    Now

    1.  Decreasing the workload is not an option for me. In fact I need to do much more that the current program is doing. I will have other time critical tasks that need to be performed. Sometime the processor will be busy for up-to 200 us doing something else. The data transfer from host-to-uc and the other way around need to happen opportunistically in an asynchronous way. For example if there is byte received into the UART RX buffer, the launchpad USB emulator should not send the next byte to the uC until the previous byte is not read.

    2. I can decrease the baud-rate, but not much. May be down to 2 M. But that is not going to solve my problem.

    3. Increasing clock freq is not an option. I am already operating the board at the maximum possible clock rate.

    Kind regards

    Jatala

  • Hello Chester,

    Many thanks for providing helpful hints that cleared up basic misplaced expectations.
    Previously I worked on Tiva C launchpad. There I did not face any problem like with the UART port of MSP432 launchpad. When I decided to move to this latter device, I did not thought that it will not have hardware based flow control. Even the software based flow control (CTS and RTS) does not seem to exist in driverlib.

    That means the only viable option left is to use DMA. I have to try this. I suspect even with this option it would be difficult to guarantee loss-free data reception. For example how to stop any data arrival in UCAxRXBUF when DMA channel is being reconfigured.

    Kind regards
    Jatala
  • Then wait for MSP432 with USB interface and use CDC or change your algorithm. I am using MSP430 running at 24 MHz, both uarts on 4 Mbps without losing any bytes and without handshake. ISR code is lite, minimal, other things are done outside the ISR, and Uart buffers are big enough.
  • Hello Soli,

    It appears main limitation is the UART buffer size. In MSP432, the UCAxRXBUF is only single byte long. What is the buffer size in your MSP430.

    BR
    Jatala
  • The same, one byte. In ISR you just store received byte to buffer, and other things can be done outside the ISR.
  •  

    zrno soli said:
    The same, one byte. In ISR you just store received byte to buffer, and other things can be done outside the ISR.


    I wish I could pull off similar feat. Besides the UART RX ISR, do you have to serve other ISRs.

    BR

  • Of course I can have other ISR, but in this case Uart will not work, so I chose to not have them, and process other things by loops.
  • Jatala said:
    That means the only viable option left is to use DMA. I have to try this. I suspect even with this option it would be difficult to guarantee loss-free data reception. For example how to stop any data arrival in UCAxRXBUF when DMA channel is being reconfigured.

    The DMA controller in the MSP432P401R supports ping-pong mode. From the MSP432P4xx Family Technical Reference Manual SLAU356A:
    9.2.3.4.4.1 Example Application use case for Ping-Pong Cycle

    Ping-pong mode is ideally preferred when data is generated at high speed (e.g. ADC with fast sampling rate) and you want DMA to copy the data while CPU is still processing the earlier block of data.

    In many applications, the ADC output data is processed by CPU in blocks. Now, let us consider thescenario when DMA has copied a block data into the memory and interrupted CPU to process the data. CPU started processing the data but meanwhile ADC is ready with another conversion and triggers the DMA. DMA cannot copy to the earlier destination address because CPU hasn’t yet finished processing the previous data. So, where does DMA put this new data? Ping-pong mode comes to rescue here as DMA starts copying the data to a new location as defined by the alternate data structure. So, while the CPU is busy processing the data copied using primary data structure, DMA starts filling a new block using alternate data structure. Next, when CPU processes the data from alternate data structure, DMA starts filling the memory based on primary data structure. This way, by using ping-pong mode, application can prevent loss of any data for high data rate requirements.



  • Hello Chester,

    Thanks for all these information. Now I believe my salvation lies in the DMA ping-pong mode. I am going to try this.

    BR

    Jatala

  • Maybe you using another UART mode, without USB can solve because USB protocol is too boring to do a simple and robust UART communication...
  • Hi Jalata,

    I'm not sure if you've worked around this problem, but for what it's worth, I've come across something similar and posted my results here

    e2e.ti.com/.../531576

    Cheers

    Julian

**Attention** This is a public forum