Skip to main content
Associate
April 8, 2026
Solved

STM32H563ZIT6 cannot receive RS485 at high baud rates (e.g. 12 Mbaud)

  • April 8, 2026
  • 16 replies
  • 1531 views

TX works but RX interrupt not triggered

 

Hello,

I am currently using a custom board based on STM32H563ZIT6. My goal is to establish communication over RS485 between the STM32 and a PC (using Hterm). The system is designed to receive a command and send a response.


Communication Details:

  • Interface: RS485 (half-duplex)
  • PC Tool: Hterm
  • Converter: USB COM 485 PLUS 4
  • Packet size: Very small (typically 4 bytes)
  • Transmission interval: ≥ 300 ms

Current Situation:

  • Communication works perfectly between 115200 and 2,000,000 baud
  • I am trying to increase the baud rate up to 12 Mbaud

Problem:

At 12 Mbaud:

  • STM32 → PC communication works correctly
    (I can see transmitted data on Hterm)
  • PC → STM32 communication does NOT work
    • STM32 does not receive any data
    • RX interrupt is not triggered at all
    • It behaves as if no data is arriving

Additional Notes:

  • I have checked the termination resistor, and it seems correct
  • The data sent from PC is valid (verified from Hterm)
  • UART baud rate is configured up to 12.5 Mbaud in CubeMX
  • The issue only appears at very high baud rates

What I Suspect:

  • Possible limitation of RS485 converter at high baud rates
  • UART sampling/timing issues at 12 Mbaud
  • Interrupt-based reception may not be sufficient at such speeds
  • Hardware limitations (signal integrity, cable, etc.)

Questions:

  1. Is it realistic to achieve 12 Mbaud over RS485 in this setup?
  2. Could this be a limitation of the USB-RS485 converter?
  3. Are there any specific STM32 UART settings required for such high speeds?
  4. Should DMA be used instead of interrupt for reliable reception?
  5. Are there known limitations for STM32H5 series UART at these speeds?

Any insights or similar experiences would be very helpful.

Thanks in advance!

Best answer by MES98

Thank you all for your support and valuable suggestions.

I would like to briefly summarize the issue and the solution, in case it helps others in the future.

Initially, when using interrupt-based reception, I was able to reach up to around 4 Mbaud reliably.
After switching to DMA, I was able to increase this up to 8 Mbaud.

However, I could never reach the 10 Mbaud value stated in the datasheet — at that speed, I was neither able to receive nor transmit data reliably.

While going through the datasheets more carefully, I noticed that the UART clock is 24 MHz, and in simple terms, the achievable baud rates follow a relation like 24 / n (Mbaud).

Based on this, I tested different baud rates again:

  • My device still cannot reliably communicate at 10 Mbaud
  • But interestingly, it works perfectly at 12 Mbaud

So it seems that certain baud rates are more “naturally aligned” with the clock configuration, while others are not achievable due to divider limitations.


Thanks again to everyone for the help.
Have a great day!

16 replies

Michal Dudka
Lead
April 10, 2026

10 Mbaud = 1 byte per microsecond. This data rate can be handled using interrupts (assuming the MCU is running at 250 MHz). However, the interrupt routine must be written efficiently. If you’re using HAL, you’ll almost certainly have to use LL or simply direct register access and write the routine yourself (which isn’t complicated). You’ll also have to ensure that interrupt routines with higher priority (Systick?!) don’t take so long that the FIFO fills up and an overrun occurs.

However, it makes more sense to use DMA. I can’t think of any reason not to use DMA in such a case.

Lead II
April 13, 2026

Use DMA with circular buffer so you don't miss any data. You can use character timeout interrupts, but at such high baudrates the data is most likely very bursty so you would get interrupts on partial packets.

"Kudo posts if you have the same problem and kudo replies if the solution works.Click ""Accept as Solution"" if a reply solved your problem. If no solution was posted please answer with your own."
MES98Author
Associate
April 14, 2026

@unsigned_char_array @Ozone @Michal Dudka @Andrew Neil 

Thank you for your suggestions.

I have now started using DMA for UART reception as recommended. However, I may be doing something wrong since I don’t have much experience with DMA yet.

I am sharing the relevant part of my code and my clock configuration for reference.

Current behavior:

  • With DMA, communication works only at 115200 baud
  • At 230400 baud, I already start getting overrun errors

Additionally, I noticed something unexpected:

  • If I remove the following line:

 

 
 
HAL_UART_IRQHandler(&huart8);
  • Then inside my UART_ProcessData function, hdmarx becomes 0, and it behaves as if no data is being received at all

This makes me think that I might be misconfiguring the interaction between DMA and interrupt handling, but I’m not sure what I am missing.

Any guidance on correct DMA + UART configuration or what I might be doing wrong would be greatly appreciated.

---main.c---
#define UART_RX_BUFFER_SIZE 2048
uint8_t uart_rx_buffer[UART_RX_BUFFER_SIZE];
volatile uint16_t dma_old_pos = 0;

int main(void)
{

 /* USER CODE BEGIN 1 */
 /* USER CODE END 1 */

 /* MCU Configuration--------------------------------------------------------*/

 /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
 HAL_Init();

 /* USER CODE BEGIN Init */
 /* USER CODE END Init */

 /* Configure the system clock */
 SystemClock_Config();

 /* Configure the peripherals common clocks */
 PeriphCommonClock_Config();

 /* USER CODE BEGIN SysInit */
 /* USER CODE END SysInit */

 /* Initialize all configured peripherals */
 MX_GPIO_Init();
 MX_GPDMA1_Init();
 MX_UART8_Init();
 /* USER CODE BEGIN 2 */
 ring_buffer_init(&ring_buffer_xxx_1, buf_arr_uart1, sizeof(buf_arr_uart1));

// HAL_UART_Receive_IT (&huart8, &rx_data_uart1, 1);

 init_xxx_ports(&huart8, &huart10, &huart12, &huart11, &huart2, &huart9, &huart7, &huart3, &huart5, &huart1);

 HAL_StatusTypeDef ret;
 ret = HAL_UART_Receive_DMA(&huart8, uart_rx_buffer, UART_RX_BUFFER_SIZE);
 if(ret != HAL_OK){
	 return;
 }
 /* USER CODE END 2 */

 /* Infinite loop */
 /* USER CODE BEGIN WHILE */
 while (1)
 {
	 parse_rx_data_rs422_others(&ring_buffer_xxx_1, 1);
 /* USER CODE END WHILE */

 /* USER CODE BEGIN 3 */
 }
 /* USER CODE END 3 */
}


void HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart){
	UART_ProcessData();
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){
 if (huart->Instance == USART1) {
 	UART_ProcessData();
 // Handle received data in pRxBuff
 // ...
 // Re-enable DMA to receive next package
 	HAL_UART_Receive_DMA(&huart8, uart_rx_buffer, UART_RX_BUFFER_SIZE);
 }
}

void UART_ProcessData(){
	uint16_t dma_pos = UART_RX_BUFFER_SIZE - __HAL_DMA_GET_COUNTER(huart8.hdmarx);

	if(dma_pos != dma_old_pos) {
		if(dma_pos > dma_old_pos){
			ProcessBytes(&uart_rx_buffer[dma_old_pos], dma_pos - dma_old_pos);
		}
		else{
			ProcessBytes(&uart_rx_buffer[dma_old_pos], UART_RX_BUFFER_SIZE - dma_old_pos);
			ProcessBytes(&uart_rx_buffer[0], dma_pos);
		}
		dma_old_pos = dma_pos;
	}
}
void ProcessBytes(uint8_t *data, uint16_t len){
	for(uint16_t i = 0; i<len; i++){
		ring_buffer_queue(&ring_buffer_xxx_1, data[i]);

	}
}

 

---stm32h5xx_it.c---


/**
 * @brief This function handles UART8 global interrupt.
 */
void UART8_IRQHandler(void)
{
 /* USER CODE BEGIN UART8_IRQn 0 */


 /* USER CODE END UART8_IRQn 0 */
 HAL_UART_IRQHandler(&huart8);
 /* USER CODE BEGIN UART8_IRQn 1 */

 if (__HAL_UART_GET_FLAG(&huart8, UART_FLAG_IDLE))
 {
		__HAL_UART_CLEAR_IDLEFLAG(&huart8);
		UART_ProcessData();
//		 uint8_t data = huart8.Instance->RDR;
//		 ring_buffer_queue(&ring_buffer_xxx_1, data);
 }
 if(__HAL_UART_GET_FLAG(&huart8,UART_FLAG_ORE)){
		__HAL_UART_CLEAR_OREFLAG(&huart8);
 }
 /* USER CODE END UART8_IRQn 1 */
}

 

Ozone
Principal
April 14, 2026

> Additionally, I noticed something unexpected:
> If I remove the following line:
    HAL_UART_IRQHandler(&huart8);

The idea of DMA (or a FIFO) ist to not have UART interrupts in the first place.
You should only need to process DMA interrupts (complete transfers of your configured size). And errors, of course.

MES98Author
Associate
April 14, 2026

clk config.pnggpdma1.pnggpdma1-2.png

MES98AuthorBest answer
Associate
April 15, 2026

Thank you all for your support and valuable suggestions.

I would like to briefly summarize the issue and the solution, in case it helps others in the future.

Initially, when using interrupt-based reception, I was able to reach up to around 4 Mbaud reliably.
After switching to DMA, I was able to increase this up to 8 Mbaud.

However, I could never reach the 10 Mbaud value stated in the datasheet — at that speed, I was neither able to receive nor transmit data reliably.

While going through the datasheets more carefully, I noticed that the UART clock is 24 MHz, and in simple terms, the achievable baud rates follow a relation like 24 / n (Mbaud).

Based on this, I tested different baud rates again:

  • My device still cannot reliably communicate at 10 Mbaud
  • But interestingly, it works perfectly at 12 Mbaud

So it seems that certain baud rates are more “naturally aligned” with the clock configuration, while others are not achievable due to divider limitations.


Thanks again to everyone for the help.
Have a great day!

Andrew Neil
Super User
April 15, 2026

@MES98 wrote:

So it seems that certain baud rates are more “naturally aligned” with the clock configuration, while others are not achievable due to divider limitations.


Yes, this is common.

This is why there are crystals manufactured with what seem like strange frequency values - like 11.059MHz.

That number is chosen precisely because it easily divides to give common serial baud rates.

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.
Lead II
April 15, 2026

And it's why fractional baudrate exists. If a desired baudrate is close to 2 possible baudrates the peripheral can periodically switch between those baudrates per bit so the sample point doesn't drift too much. It's the same principle we use for leap years. 

"Kudo posts if you have the same problem and kudo replies if the solution works.Click ""Accept as Solution"" if a reply solved your problem. If no solution was posted please answer with your own."