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:
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.
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.
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.
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
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.
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
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:
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.
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.