cancel
Showing results for 
Search instead for 
Did you mean: 

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

MES98
Associate III

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!

41 REPLIES 41

@MES98 wrote:
  • I am required to use interrupt-based reception

Why ?

This is exactly the kind of situation that DMA is for!

As @Ozone said previously, "High interrupt loads ... can easily consume most of the available core performance".

That's why we have  DMA - to offload that burden!

 

You haven't shown your code - how are you handling the interrupt?

It will probably need to be well-optimised - straight HAL probably won't hack it.

How to insert source code

 


@MES98 wrote:

Is there any recommended way to handle high baud rate UART reception .


DMA.

 


@MES98 wrote:

 

Any suggestions or best practices 


Use DMA.

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.

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.

Ozone
Principal III

> I am required to use interrupt-based reception

I don't know why you think that.

A bitrate of 10MBit/s only requires you to handle the input efficiently.
Although DMA can be problematic with burst transmissions, especially of unknown length.

However, interrupts become problematic when approaching a rate of 1 MHz, and you are beyond that.
Context switching latencies are unavoidable (12 cycle in and out, with FPU register even more).
And as mentioned, the typical HAL implementation with callbacks is inappropriate for this use case.
You would need to handle data in the interrupt handler directly, with the least possible amount of instructions.
High interrupt loads and lengthy handlers easily break an application, many posters here managed this at much lower bit rates.

> Are there specific FIFO, interrupt, or buffering strategies that could help in this scenario?

You would have to look up the reference manual of your MCU, but I don't know any STM32 MCU employing a UART FIFO. ST mostly bets on DMA here, which serves a similiar purpose.
The benefit is, the interrupt rate is a fraction ( byte rate / buffer size), but it would not necessarily be aligned with the timing of the transmissions. And error handling might get tricky.

Did you consider reducing the bitrate ?
I can see no justification from the application side for such a high low-latency throughput.

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.
MES98
Associate III

@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 */
}

 

MES98
Associate III

clk config.pnggpdma1.pnggpdma1-2.png

Ozone
Principal III

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


@MES98 wrote:
// Re-enable DMA to receive next package​

In circular mode this is not needed. DMA can run forever. Just make sure you process the data before it is overwritten. You can do this by using a sufficiently large buffer.
Here is a tutorial that might help you: https://controllerstech.com/stm32-uart-4-receive-data-using-dma/

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.
MES98
Associate III

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!


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