Skip to main content
DWalt.1
Associate II
November 17, 2020
Solved

Sending Serial Data from one UART to another. HAL_UART_Receive not reading new bytes.

  • November 17, 2020
  • 18 replies
  • 7660 views

My Board is the STM32-G431KB Nucleo-32. I have a Mavlink Serial Tx/Rx (From a PX4 Autopilot) connected to PA10/PA9.

I am attempting to receive serial data at 115200 8N1 on UART1 and then send it at the same rate on UART2. I am just doing a basic polling implementation of HAL_UART_Receive() grabbing a single byte at a time.

Unfortunately, HAL_UART_Receive only gets the first byte and transmits it. After the first loop, HAL_UART_Receive doesn't get any new data from the serial line. I can see there is good data coming in on UART1 with a logic analyzer. If I don't set to 0 in the while loop, it just transmits out the last byte received over and over.

int main(void)
{
 /* MCU Configuration--------------------------------------------------------*/
 
 /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
 HAL_Init();
 
 /* Configure the system clock */
 SystemClock_Config();
 
 /* Initialize all configured peripherals */
 MX_GPIO_Init();
 MX_USART2_UART_Init();
 MX_USART1_UART_Init();
 /* USER CODE BEGIN 2 */
 
 /* Infinite loop */
 while (1)
 {
	 uint8_t c = 0;
	 HAL_UART_Receive(&huart1, &c, 1, 1);
	 HAL_UART_Transmit(&huart2, &c, sizeof(c), 0xFFFF);
 }
}

Can anyone point out where I'm going wrong? I expected HAL_UART_Receive to work like Arduino's 'serial->read'. Is there another step to force HAL_UART to grab a new byte?

Edit:

Using @KnarfB​ 's example below and the "uart forwarding" works fine. Incoming bytes on UART1 are transmitted out UART2 - as I intended.

while (1)
 {
	 if( USART1->ISR & USART_ISR_RXNE ) {
		 char c = USART1->RDR & 0xFF;
	 while( (USART2->ISR & USART_ISR_TXE) == 0 );
	 USART2->TDR = c;
	 }
 
 /* USER CODE END WHILE */
 
 /* USER CODE BEGIN 3 */
	 //HAL_Delay(20);
 }

Can anyone explain to me what I'm doing wrong with HAL_UART_Receive?

Edit: I believe I solved this by setting the parameter 'Fifo Mode' to 'Enable'

This topic has been closed for replies.
Best answer by KnarfB

Glad you solved it. Two minor remarks.

The HAL loop worked for me on STM32-G431RB Nucleo-64 when not using timeouts, i.e.

 while (1)
 {
	 HAL_StatusTypeDef status = HAL_UART_Receive(&huart2, &c, 1, 0 );
	 if (status == HAL_OK) {
		 HAL_UART_Transmit(&huart2, &c, sizeof(c), 0 );
	 }
 }

Basically, if you start using timeouts, HAL uses SysTick which is comparatively slow (1ms) and not well suited for single char timeouts. And yes, with timeouts I saw overflows as @Community member​ suspected. The bad thing is that the HAL API encourages using timeouts.

FIFO mode is only avail in some MCU series and I haven't used it here, but I know it from the famous 16550 UART and clones. Proper error handling and recovery can be a serious issue for serial comm. This might get worse when FIFOs are used.

PS: I was surprised to see that the G4 Nucleo-32 board has the SWO pin available. This is a nice debugging aid for debugging by tracing, previously available only on larger boards.

18 replies

TDK
November 17, 2020

What chip? What is the return value on HAL_UART_Receive?

"If you feel a post has answered your question, please click ""Accept as Solution""."
DWalt.1
DWalt.1Author
Associate II
November 17, 2020

STM32G431KB

i’ll get the return value tomorrow.

Tesla DeLorean
Guru
November 17, 2020
uint8_t c = 0;
if (HAL_UART_Receive(&huart1, &c, 1, 1) == HAL_OK)
 HAL_UART_Transmit(&huart2, &c, sizeof(c), 0xFFFF);

Tips, Buy me a coffee, or three.. PayPal Venmo (See Profile) Up vote any posts that you find helpful, it shows what's working..
DWalt.1
DWalt.1Author
Associate II
November 17, 2020

When I run that code it only enters the if statement on the first byte. Nothing will get sent after the first byte.

KnarfB
Super User
November 17, 2020

The code you have shown usually works*. Match the UART parameters you are using on all ends? What's on the other side? There is often a line buffering for serial comm, so you may check that '\n' and '\r' are sent. You can debug-step into HAL code to find out what happened.

*) you may/might/eventually will miss chars because there is no underlying FIFO or buffer for the receive side. For advanced solutions check https://stm32f4-discovery.net/2017/07/stm32-tutorial-efficiently-receive-uart-data-using-dma/

DWalt.1
DWalt.1Author
Associate II
November 17, 2020

The receiving side of uart2 is just a picocom terminal. The sender for uart1 is a mavlink byte stream. If I grab 500 bytes with HAL_UART_Receive it has the same behavior. It only fills the buffer once (and the data looks correct) but doesn’t replace the bytes in the buffer on subsequent calls.

my plan is to eventually use DMA but right now I am just hoping for a quick and dirty method to grab the bytes as they come in. Thanks for the link.

KnarfB
Super User
November 17, 2020

Not sure if mavlink continues sending if there is no proper answer from the other side (i.e. duplex comm). If the UARTs are properly setup using HAL, a polling echo can be done easily at register level. Thereby omitting the HAL software layer. (Tested with an echo on the same UART here):

 /* Infinite loop */
 /* USER CODE BEGIN WHILE */
 while (1)
 {
 if( USART2->ISR & USART_ISR_RXNE ) {
 char c = USART2->RDR & 0xFF;
 while( (USART2->ISR & USART_ISR_TXE) == 0 );
 USART2->TDR = c;
 }
 /* USER CODE END WHILE */
 
 /* USER CODE BEGIN 3 */
 }
 /* USER CODE END 3 */

 edit: code changed

waclawek.jan
Super User
November 17, 2020

If Rx overflows, it stops receiving. HAL_UART_Transmit() waits until TC, so it wastes time, but even then it's still the Rx double-buffer which should save the day so I don't quite see why would it overflow if both run at the same data rate - but IMO it's worth checking.

Once you get this running, there's another more suble gotcha: with entirely continuous Rx stream, beware of slightly faster Rx than Tx, it eventually grinds up any buffering you may have built in.

JW

PS. If you observe UART registers in debugger, don't.

DWalt.1
DWalt.1Author
Associate II
November 17, 2020

Luckily my final implementation is not serial forwarding, but this was an initial step to ensure I was getting the correct serial data.

I may have solved this by setting the 'Fifo Mode' to 'Enable' which was not the default value. Does this sound correct?

waclawek.jan
Super User
November 17, 2020

Well, I suggested to check if there was overflow in UART (UART_ISR.ORE), but if yes, FIFO might have solved it.

The fundamental difference between KnarfB's code and Cube/HAL is, that, as I've said above, Cube's Tx routine waits until TC after the last character.

JW

DWalt.1
DWalt.1Author
Associate II
November 17, 2020

I may have solved this by setting Fifo Mode to "Enable". By default, this Parameter is disabled.

@KnarfB​ does this sound right?