cancel
Showing results for 
Search instead for 
Did you mean: 

STM32U5 UART - loopback test

daniellages
Associate II

Hi! I have a STM32U585 board and I'm trying to make a UART loopback test. I tried two different modes:

  • Asynchronous (Full-Duplex) - wire between the TX and RX
  • Single-Wire (Half-Duplex) - wire looping to itself

Both worked in similar ways. I successfully was able to handle 1 byte, so this is the code that worked fine

/* USER CODE BEGIN PV */ uint8_t tx = 0; uint8_t rx = 0; /* USER CODE END PV */

and inside the main function I had

while(1) { printf("Sending byte: %d\r\n", tx); HAL_UART_Transmit(&huart3, &tx, 1, HAL_MAX_DELAY); HAL_UART_Receive(&huart3, &rx, 1, HAL_MAX_DELAY); printf("Received byte: %d\r\n", rx); tx++; HAL_Delay(1000); }

I could see in the terminal that it was successfully incrementing both tx and rx variables.

 

Now, it all went wrong when I tried to use an array instead of a single byte. This is what I tried:

/* USER CODE BEGIN PV */ uint8_t tx[4] = {0x55, 0x12, 0x00, 0xFF}; uint8_t rx[4] = {0}; /* USER CODE END PV */

and the main function had

while(1) { printf("Sending bytes:\r\n"); for(int i=0; i<4; i++) printf(" 0x%02X", tx[i]); printf("\r\n"); HAL_UART_Transmit(&huart3, tx, 4, HAL_MAX_DELAY); HAL_UART_Receive(&huart3, rx, 4, HAL_MAX_DELAY); printf("Received bytes:\r\n"); for(int i=0; i<4; i++) printf(" 0x%02X", rx[i]); printf("\r\n"); tx[2]++; HAL_Delay(2000); }

From this in the terminal shows that it got stuck at HAL_UART_Transmit(), this was the full output

    Sending bytes:  0x55 0x12 0x00 0xFF

 

By running it in debug mode I was able to realize that it got stuck on this while loop inside the stm32u5xx_hal_uart.c

HAL_StatusTypeDef UART_WaitOnFlagUntilTimeout(UART_HandleTypeDef *huart, uint32_t Flag, FlagStatus status, uint32_t Tickstart, uint32_t Timeout) { /* Wait until flag is set */ while ((__HAL_UART_GET_FLAG(huart, Flag) ? SET : RESET) == Status) { ...

Can anyone help me out? Is there any info about my test that you need me to provide?

Thanks in advance!

1 ACCEPTED SOLUTION

Accepted Solutions

There's no buffer depth, so don't use blocking functions.

At a polling/loop level you could check for TXE and RXNE flags in the status register and advance your transmit and receive buffer pointers.

Or look to use HAL_UART_ReceiveIT to capture reception as it occurs.

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

View solution in original post

7 REPLIES 7

There's no buffer depth, so don't use blocking functions.

At a polling/loop level you could check for TXE and RXNE flags in the status register and advance your transmit and receive buffer pointers.

Or look to use HAL_UART_ReceiveIT to capture reception as it occurs.

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

Don't use HAL_MAX_DELAY. That is ~49 days before it'll timeout. For 4 bytes, 100ms will suffice. That why it seems like you're stuck in the UART_WaitOnFlagUntilTimeout function

 

You should always read the return status when using HAL_UART_Transmit and HAL_UART_Receive.

You should get either HAL_ERROR, HAL_OK, HAL_BUSY or HAL_TIMEOUT returned. In your case it'll probably be HAL_TIMEOUT

 

HAL_StatusTypeDef hal_status; hal_status = HAL_UART_Transmit(&huart3, tx, 4, 100); // same for HAL_UART_Receive if(hal_status != HAL_OK) { if(hal_status == HAL_TIMEOUT) { // do something } else if(hal_status == HAL_BUSY) { // do something } else if(hal_status == HAL_ERROR) { // do something } return;// do not continue, until errors fixed } // continue

 

I was told that if a devices starts to smoke, put the smoke back in. I guess I never got all the smoke because the device never worked afterwards.
Don't worry, I won't byte.
TimerCallback tutorial! | UART and DMA Idle tutorial!

If you find my solution useful, please click the Accept as Solution so others see the solution.
TDK
Super User

You need to be actively receiving characters as they come in. At least, when you're receiving more than 1 of them. There's no way to do a loopback like this in blocking mode. You'll have to use HAL_UART_Receive_IT or HAL_UART_Receive_DMA.

> Single-Wire (Half-Duplex) - wire looping to itself

Loopback on half-duplex makes no sense.

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

That worked very well, for anyone wondering I activated the NVIC global interrupts in UART and this was the logic I implemented:

/* USER CODE BEGIN 0 */ uint8_t tx_buf[4] = {0x55, 0x12, 0x29, 0xFF}; uint8_t rx_buf[4] = {0}; volatile uint8_t rx_done = 0; void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if(huart == &huart3) { rx_done = 1; } } /* USER CODE END 0 */

and in main function

while(1) { rx_done = 0; printf("Sending: "); for(int i=0; i<4; i++) printf("0x%02X ", tx_buf[i]); printf("\r\n"); if(HAL_UART_Receive_IT(&huart3, rx_buf, 4) != HAL_OK) printf("RX start failed\r\n"); HAL_Delay(1); if(HAL_UART_Transmit_IT(&huart3, tx_buf, 4) != HAL_OK) printf("TX start failed\r\n"); while(!rx_done) {} printf("Received: "); for(int i=0; i<4; i++) printf("0x%02X ", rx_buf[i]); printf("\r\n"); tx_buf[2]++; HAL_Delay(3000); }

And this was the output:

    Starting...
    Sending: 0x55 0x12 0x29 0xFF  
    Received: 0x55 0x12 0x29 0xFF  
    Sending: 0x55 0x12 0x2A 0xFF  
    Received: 0x55 0x12 0x2A 0xFF  
    Sending: 0x55 0x12 0x2B 0xFF  
    Received: 0x55 0x12 0x2B 0xFF


@Karl Yamashita wrote:

Don't use HAL_MAX_DELAY. That is ~49 days before it'll timeout.


Actually, I think the HAL_UART_Receive takes HAL_MAX_DELAY to mean infinite timeout; ie, it will wait forever?

The name (or, at least, the usage) is misleading.

 

PS:

found it:

https://community.st.com/t5/stm32-mcus-products/ultrasonic-a02yyuw-not-working-in-stm32l443vct6/m-p/731949/highlight/true#M263525

A complex system that works is invariably found to have evolved from a simple system that worked.
A complex system designed from scratch never works and cannot be patched up to make it work.
TDK
Super User

I'm not sure the distinction between 49 days and forever matters a whole heck of a lot on an MCU, but the behavior is documented in various places.

TDK_0-1748306136162.png

Waiting forever is better than silently ignoring errors most of the time IMO, especially during the debugging phase. Maybe it changes when you move into production.

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

Yes, you are right. I just looked at the 32 bit value but didn't look to see what UART_WaitOnFlagUntilTimeout actually did.

I was told that if a devices starts to smoke, put the smoke back in. I guess I never got all the smoke because the device never worked afterwards.
Don't worry, I won't byte.
TimerCallback tutorial! | UART and DMA Idle tutorial!

If you find my solution useful, please click the Accept as Solution so others see the solution.