cancel
Showing results for 
Search instead for 
Did you mean: 

How can I make a STM32H7A3 UART work in loopback mode?

magene
Senior II

I have some simple polling UART code that works fine when connected to a GPS receiver. I send a command and get a response back. Now I'm trying something new and it would be convenient to work with RX and TX looped back (connected together) so I receive whatever I send out. Seems like the same code that works talking to the GPS receiver should work in loopback mode but I haven't been able to make it so. The HAL_UART_Receive function does receive the first character that gets transmitted but then the HAL_UART_Receive function sits in the UART_WaitOnFlagUntilTimeout function waiting for more characters. It seems logical that using polling mode the receive function doesn't start receiving until the transmit function is completely finished sending out characters and then the character are gone, but then why would the receive function get even a single character? I've tried using interrupt mode with the receive function before the TX function with the same result but that might be because I'm not setting up the interrupt transfer correctly. Any ideas about how to do loop back correctly will be greatly appreciated.

Here's the main UART RX/TX function:

void getGPSTimeHack()
{
	gpsUSART2Handle.Instance        = USART2;
	gpsUSART2Handle.Init.BaudRate     = 9600;
	gpsUSART2Handle.Init.WordLength   = UART_WORDLENGTH_8B;
	gpsUSART2Handle.Init.StopBits     = UART_STOPBITS_1;
	gpsUSART2Handle.Init.Parity       = UART_PARITY_NONE;
	gpsUSART2Handle.Init.HwFlowCtl    = UART_HWCONTROL_NONE;
	gpsUSART2Handle.Init.Mode         = UART_MODE_TX_RX;
	gpsUSART2Handle.Init.OverSampling = UART_OVERSAMPLING_16;
	gpsUSART2Handle.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
 
	if (HAL_UART_DeInit(&gpsUSART2Handle) != HAL_OK)
	{
		Error_Handler();
	}  
	if (HAL_UART_Init(&gpsUSART2Handle) != HAL_OK)
	{
		Error_Handler();
	}
	
	getTimeCmdBytes.assign(getTimeCmdString.begin(), getTimeCmdString.end());
	cout << "Starting GPS UART Transmit" << endl;
	if(HAL_UART_Transmit(&gpsUSART2Handle, (uint8_t*)getTimeCmdBytes.data(), getTimeCmdBytes.size(), 1000) != HAL_OK)
	{
		Error_Handler();
	}
  
	cout << "Starting GPS UART Receive" << endl;
	if (HAL_UART_Receive(&gpsUSART2Handle, (uint8_t *)gpsRXBuffer, sizeof(gpsRXBuffer), 0x1FFFFFF) != HAL_OK)
	{
		Error_Handler();
	}
		
	cout << gpsRXBuffer << endl;
	for (int i = 0; i < sizeof(gpsRXBuffer); i++)
		missionStartTimeBytes[i] = gpsRXBuffer[i];
}

and here's my override of the HAL_UART_MspInit function:

void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{  
	GPIO_InitTypeDef  GPIO_InitStruct = { 0 };
	RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = { 0 };
  
	if (huart->Instance == USART1)
	{
		/*##-1- Enable peripherals and GPIO Clocks #################################*/
		/* Enable GPIO TX/RX clock */
		//	USARTx_TX_GPIO_CLK_ENABLE();
		//	USARTx_RX_GPIO_CLK_ENABLE();
			__HAL_RCC_GPIOB_CLK_ENABLE();
 
		/* Enable USARTx clock */
		//	USARTx_CLK_ENABLE(); 
			__HAL_RCC_USART1_CLK_ENABLE();
  
		/*##-2- Configure peripheral GPIO ##########################################*/  
		/* UART TX GPIO pin configuration  */
		GPIO_InitStruct.Pin       = GPIO_PIN_6;    	//USARTx_TX_PIN;
		GPIO_InitStruct.Mode      = GPIO_MODE_AF_PP;
		GPIO_InitStruct.Pull      = GPIO_PULLUP;
		GPIO_InitStruct.Speed     = GPIO_SPEED_FREQ_VERY_HIGH;
		GPIO_InitStruct.Alternate = GPIO_AF7_USART1;     			//USARTx_TX_AF;
 
		HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
 
		/* UART RX GPIO pin configuration  */
		GPIO_InitStruct.Pin = GPIO_PIN_7;    		//USARTx_RX_PIN;
		GPIO_InitStruct.Alternate = GPIO_AF7_USART1;    		//USARTx_RX_AF;
 
		HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
	}
	else if (huart->Instance == USART2)
	{
		/*##-1- Enable peripherals and GPIO Clocks #################################*/
		/* Enable GPIO TX/RX clock */
		//	USARTx_TX_GPIO_CLK_ENABLE();
		//	USARTx_RX_GPIO_CLK_ENABLE();
			__HAL_RCC_GPIOD_CLK_ENABLE();
 
		/* Enable USARTx clock */
		//	USARTx_CLK_ENABLE(); 
			__HAL_RCC_USART2_CLK_ENABLE();
  
		/*##-2- Configure peripheral GPIO ##########################################*/  
		/* UART TX GPIO pin configuration  */
		GPIO_InitStruct.Pin       = GPIO_PIN_5;   	//USARTx_TX_PIN;
		GPIO_InitStruct.Mode      = GPIO_MODE_AF_PP;
		GPIO_InitStruct.Pull      = GPIO_PULLUP;
		GPIO_InitStruct.Speed     = GPIO_SPEED_FREQ_VERY_HIGH;
		GPIO_InitStruct.Alternate = GPIO_AF7_USART2;    			//USARTx_TX_AF;
 
		HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
 
		/* UART RX GPIO pin configuration  */
		GPIO_InitStruct.Pin = GPIO_PIN_6;   		//USARTx_RX_PIN;
		GPIO_InitStruct.Alternate = GPIO_AF7_USART2;   		//USARTx_RX_AF;
 
		HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
	}
}

3 REPLIES 3
LCE
Principal

If the UART is used in polling mode and you first TX, and then RX it makes sense that the always "listening" RX gets the first byte into its RX buffer, but as that is not read by software then stops receiving.

You need to use either an interrupt or DMA solution.

The interrupt on the RX side could look like:

  • UART error check
  • copy received byte into buffer, count number
  • maybe check byte for message start / end flags if such are used or use a timeout (outside IRQ handler) to find if RX is complete

DMA: I never used that on the RX side, there was something with fixed length or so... but as you know what you will transmit, that might work for you.

magene
Senior II

@LCE Thanks for the suggestions. I was able to go back and make my interrupt driven UART code work in loop back mode and I'm good to go for now with loopback. Oddly enough, I was able to get a UART driver using DMA working with my GPS receiver a long time ago and never tried it in loop back mode. That code uses DMA for RX and TX and the character match interrupt so I don't have to worry about having to know how many characters I want tor receive.

Cheers - Gene

Pavel A.
Evangelist III

For "UART error check" - pending overrun and maybe other error flags prevent RX from being detected.

https://github.com/STMicroelectronics/stm32h7xx_hal_driver/issues/17