cancel
Showing results for 
Search instead for 
Did you mean: 

STM32 UART Transmit stops responding after some time

anumathews7
Associate II

Hi all,

I am using a STM32L476RG NUCLEO Board with FreeRTOS and STM32 iCUBE LoRaWAN stack. I have a UART interface connected to Raspberry Pi, which periodically sends and receives data to-from STM32. I am using a circular buffer with DMA and IDLE Line detection as amount of data I receive is not fixed.

(https://github.com/controllerstech/STM32/tree/master/UART%20CIRCULAR%20BUFFER)

However,  UART Transmit stops after sometime. I have followed the instructions  in

https://community.st.com/t5/stm32-mcus-products/stm32-uart-interrupt-stops-working-after-1-hour/td-p/99642

And have stopped and restarted my HAL_UART_Receive_IT().

However it seems not to work, Could you please give me some suggestions?

 

6 REPLIES 6
TDK
Guru

> I am using a circular buffer with DMA and IDLE Line detection

> And have stopped and restarted my HAL_UART_Receive_IT().

Are you using IT or DMA? Can't use both. Perhaps show your code or follow an existing example:

https://github.com/STMicroelectronics/STM32CubeL4/blob/4ca3922f320dbb2c4f3f9486f4a094f36125f31d/Projects/NUCLEO-L476RG/Examples/UART/UART_ReceptionToIdle_CircularDMA/readme.txt#L63

 

If you feel a post has answered your question, please click "Accept as Solution".

Transmit and Receive can operate in different modes. Check what the UART Peripheral reports as a problem. Check Status and Error flagging. Then check in DMA unit.

Check it's not stuck in interrupt context. Toggle a GPIO and scope, check if that saturates. Don't put a persistent peripheral view over registers or FIFOs that might have secondary effects.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
FBL
ST Employee

Hello @anumathews7 

Are you receiving data in circular mode ? 

Could you share the minimum code to reproduce the issue?

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.

Yes, I am receiving data in circular mode

void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size) {

if (huart->Instance == USART3)
{

//memcpy(MainBuf,RxBuf,Size);

oldPos = newPos; // Update the last position before copying new data

/* If the data in large and it is about to exceed the buffer size, we have to route it to the start of the buffer
* This is to maintain the circular buffer
* The old data in the main buffer will be overlapped
*/
if (oldPos+Size > MainBuf_SIZE) // If the current position + new data size is greater than the main buffer
{
uint16_t datatocopy = MainBuf_SIZE-oldPos; // find out how much space is left in the main buffer
memcpy ((uint8_t *)MainBuf+oldPos, RxBuf, datatocopy); // copy data in that remaining space

oldPos = 0; // point to the start of the buffer
memcpy ((uint8_t *)MainBuf, (uint8_t *)RxBuf+datatocopy, (Size-datatocopy)); // copy the remaining data
newPos = ((Size)-datatocopy); // update the position
}

/* if the current position + new data size is less than the main buffer
* we will simply copy the data into the buffer and update the position
*/
else
{
memcpy (MainBuf+oldPos, RxBuf, Size);
newPos = Size+oldPos;
}

}

print_mem_hex(RxBuf, Size);

//HAL_UARTEx_ReceiveToIdle_DMA(&huart3, RxBuf, RxBuf_SIZE);

Bob S
Principal

You originally said that TX stops, and then also said you stopped and restarted your HAL_UART_Receive_IT().  And you show you RX code.

So which is it?  Does your TX stop?  Or does your RX stop?  Or both?

Also realize that the RxEventCallback() is called from the UART interrupt handler.  Whatever print-mem_hex() does, it probably shouldn't be called from interrupts.

And as an aside and FWIW, I typically do not use/rely on "receive to idle" to frame incoming data.  Unless you can absolutely guarantee the sender will always send "packets" with no delay between bytes (I'm not betting MY paycheck on that).  I just run my RX in continuous DMA circular mode with a buffer at least 2x the size of the largest packet.  Then check for incoming data based off a timer or polling loop.  THAT always works, though software needs to determine start/end of data packets/frames.  Similar to https://github.com/MaJerle/stm32-usart-uart-dma-rx-tx .

Use code formatting as it makes it easier to read code

Clipboard01.jpg

 

 

You say "And have stopped and restarted my HAL_UART_Receive_IT()" but I don't see it in your code you've posted. So if HAL returns HAL_BUSY when you call HAL_UARTEx_ReceiveToIdle_DMA, then the interrupts is not enabled and you'll never get an interrupt on any new data received.

What you should do is set a error flag and check the flag outside of the interrupt. If set, then try to enable the HAL_UARTEx_ReceiveToIdle_DMA again. See code below

 

void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size) 
{
	if (huart->Instance == USART3) {

//memcpy(MainBuf,RxBuf,Size);

		oldPos = newPos; // Update the last position before copying new data

		/* If the data in large and it is about to exceed the buffer size, we have to route it to the start of the buffer
		 * This is to maintain the circular buffer
		 * The old data in the main buffer will be overlapped
		 */
		if (oldPos + Size > MainBuf_SIZE) // If the current position + new data size is greater than the main buffer
				{
			uint16_t datatocopy = MainBuf_SIZE - oldPos; // find out how much space is left in the main buffer
			memcpy((uint8_t*) MainBuf + oldPos, RxBuf, datatocopy); // copy data in that remaining space

			oldPos = 0; // point to the start of the buffer
			memcpy((uint8_t*) MainBuf, (uint8_t*) RxBuf + datatocopy,
					(Size - datatocopy)); // copy the remaining data
			newPos = ((Size) - datatocopy); // update the position
		}

		/* if the current position + new data size is less than the main buffer
		 * we will simply copy the data into the buffer and update the position
		 */
		else {
			memcpy(MainBuf + oldPos, RxBuf, Size);
			newPos = Size + oldPos;
		}

	}

	print_mem_hex(RxBuf, Size);

    UART_EnIdleDMA(huart);

}


int errorFlag;

void UART_EnIdleDMA(ART_HandleTypeDef *huart)
{
    if(HAL_UARTEx_ReceiveToIdle_DMA(huart, RxBuf, RxBuf_SIZE) != HAL_OK)
    {
        // set error flag. Check error flag in main loop and call HAL_UARTEx_ReceiveToIdle_DMA again later.
		errorFlag = 1;
    }
}

void UART_CheckIdleDMA(void)
{
	if(errorFlag)
	{
		errorFlag = 0;
		UART_EnIdleDMA(&huart2);
	}
}


int main(void)
{
	// other HAL code

	while(1)
	{

		UART_CheckIdleDMA();

	}

}

 

Also should avoid doing memcpy and print routines inside an interrupt as it will prevent other interrupts from firing. You'll want to re-enable UART interrupts and exit right away. That way you don't miss any new packets of data. 

 

See my github code https://github.com/karlyama**bleep**a/Nucleo-L432KC_UART_DMA_HAL_status 

It shows how to deal with HAL status. It also uses a ring buffer to save the received data to. Because I use pointers, there is no need to memcpy the data to a buffer. The DMA knows which index of the buffer to save each packet of data to. 

UART_Parse function shows how to then get each packet and parse it.

 

Tips and Tricks with TimerCallback https://www.youtube.com/@eebykarl
If you find my solution useful, please click the Accept as Solution so others see the solution.