STM32U5 UART - loopback test
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2025-05-22 10:18 AM
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!
Solved! Go to Solution.
- Labels:
-
ST boards
-
STM32U5 series
Accepted Solutions
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2025-05-22 11:17 AM
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.
Up vote any posts that you find helpful, it shows what's working..
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2025-05-22 11:17 AM
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.
Up vote any posts that you find helpful, it shows what's working..
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2025-05-22 11:46 AM
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
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2025-05-22 2:58 PM
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2025-05-23 3:01 AM
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
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2025-05-23 3:07 AM - edited ‎2025-05-23 3:10 AM
@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:
A complex system designed from scratch never works and cannot be patched up to make it work.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2025-05-26 5:36 PM
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.
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2025-05-26 10:22 PM
Yes, you are right. I just looked at the 32 bit value but didn't look to see what UART_WaitOnFlagUntilTimeout actually did.
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.
