cancel
Showing results for 
Search instead for 
Did you mean: 

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

DWalt.1
Associate II

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'

1 ACCEPTED SOLUTION

Accepted Solutions

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.

View solution in original post

18 REPLIES 18
TDK
Guru

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".
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
Up vote any posts that you find helpful, it shows what's working..

STM32G431KB

i’ll get the return value tomorrow.

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

KnarfB
Principal III

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/

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.

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

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.

Mavlink does not need an answer - it streams heartbeats and other data without any checks for responses. I can see the data on the Rx pin with a logic analyzer.