3

The scenario: I have a STM32 MCU, which uses an UART in DMA Mode with Idle Interrupt for RS485 data transfer. The baud rate of the UART is set in CubeMX, in this case to 115200. My Code works fine, when the Host uses the correct baud rate, it is also "long time" stable, no issues or worries.

BUT: when I set the wrong baud rate at the host, e.g. 56700 instead of 115200, the UART stops receiving data, even if I later set the baud rate at the host to the same baud rate the Microcontroller uses, it won't work. The only way to solve this issue so far is: reset the MCU and connect again with the correct baud rate.

To give you some (Pseudo-)Code:

uint8_t UART_Buf[128];
HAL_UART_Receive_DMA(&huart2, UART_Buf, 128);
__HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);

Or in Plain Words: there is a UART Buffer for DMA (UART_Buf[128]) and the UART is started with HAL_UART_Receive_DMA(...), DMA Rx is set to circular mode in CubeMX, also the Idle-Interrupt is activated, using the HAL Macro: __HAL_UART_ENABLE_IT(...); This code works fine so far.

Works fine means: when I transmit data from my PC to the Micro, the (one) Idle Interrupt is triggered (correctly) by the MCU. In the ISR I set a flag, to start the data parsing afterwards. I receive exactly the number of bytes I have sent, and all is fine.

BUT: when I make the wrong setting in my Terminal Program and instead of the (correct) baud rate of 115200, the baud rate select menu is set to e.g. 57600, the trouble begins:

The idle interrupt will still trigger after each transmission. But it triggers 2-4 times in a quick "burst" (depending on the baud rate) and the number of bytes received is 0. I'd expect at least some bs data, but there is exactly 0 data in the buffer - which I can check with the debugger. There is obviously received nothing. When I change the baud rate in my terminal program and restart it, there is still nothing received on the MCU.

I could live with 0 received bytes, if the baud rate of the host is incorrect, but it's pretty uncool that one incoming transmission of a host with the wrong baud rate disables the UART until a hardware reset is done.

My attempts to resolve this were so far: count the "Idle Interrupt Bursts" in combination with 0 received bytes to trigger a "self reset" routine, that stops the UART and restarts it, using the MX_USART2_UART_Init(); Routine. With zero effect. I can see the Idle Interrupt is still triggered correctly, but the buffer remains empty and no data is transferred into the buffer. The UART remains in a non-receiving state.

The Question Has anyone out there experienced similar issues, and if yes: how did you solve that?

Additional Info: this happens on a STM32F030 as well as on a STM32G03x

Jason Aller
  • 3,541
  • 28
  • 38
  • 38
Chris_B
  • 359
  • 5
  • 13

3 Answers3

3

When you send to the UART at the wrong baud rate it will appear to the receiver as framing errors and/or noise errors. It could also appear as random characters being received correctly, but this is less likely so don't be surprised to have nothing in your buffer.

When you are receiving with DMA, it is normal to turn the error interrupt on or else poll the error bits. When an error is detected you would then re-initialize everything and restart the DMA. This sounds like what you are trying to do by counting the idle interrupts, but you are just not checking the right bits.

If you don't want to do that, it is not impossible to imagine that you have nothing to do at the driver level and want to try to do the resynchronisation at a higher level (eg: start reading again and discard everything until a newline character) but you will have to bear in mind at least two things:

First, make sure you clear the DDRE bit in the USART_CR3 register. The name "DMA Disable on Reception Error" speaks for itself.

Second, the UART peripheral is able to self resynchronize, as long as you have an idle gap between bytes. If you switch the transmitter to the correct baud rate but keep blasting out data then the receiver may never correctly identify which bit is a start bit.

Tom V
  • 4,827
  • 2
  • 5
  • 22
  • Thanks for this answer! Stopping the transmission on the Host side for a second or two is no big deal, as I have a "query/response" scenario and can detect on the host, if there is no response at all. On the MCU I will follow the "clear the DDRE bit" advice! My gut feeling allready told me, that this issue should be DMA related - as the buffer remains empty. – Chris_B Feb 28 '22 at 05:14
1

After investigating this issue a little bit further, i found a solution.

Abstract: When a host connects to the MCU to an UART with an other baud rate than the UART is set to, it will go into an error state and stop DMA transmission to the RX Buffer. You can check if there is an error with the HAL_UART_GetError(...) function. If there is an error, stop the UART/DMA and restart it.

The Details: First of all, it was not the DDRE bit in the USART_CR2 register. This was set to 0 by CubeMX. But the hint of Tom V led me into the right direction.

I tried to recover the UART by playing around with the register bits. I read through the UART section of the reference manual multiple times and tried to figure out, which bits to set in which order, to resolve the error condition manually.

What I found out: When a transmission with the wrong baud rate is received by the UART the following changes in the UART Registers occur (on an STM32F030): Control register 1 (USART_CR1) - Bit 8 (PEIE) goes from 1 to 0. PEIE is the Parity Interrupt Enable Bit. Control register 2 (USART_CR2) - remains unchanged Control register 3 (USART_CR3) - changes from 0d16449 to 0d16384, which means Bit 0 (EIE - Error Interrupt enable) goes from 1 to 0 Bit 6 (DMAR - DMA enable receiver) goes from 1 to 0 Bit 14 (DEM - Driver enable mode) remains unchanged at 1

USART_CR3.DEM makes sense. I am using the RS485-Functionality of the F030, so the UART handles the Driver-Enable GPIO by itself. the transition from 1 to 0 at USART_CR3.EIE and USART_CR3.DMAR are most probably the reason why no more data are transfered to the DMA buffer.

Besides that, the error Flags in the Interrupt and status register (USART_ISR) for ORE and FE are set. ORE stands for Overrun Error and FE for Frame Error. Although these bit can be cleared by writing a 1 to the corresponding bit of the Interrupt flag clear register (USART_ICR), the ErrorCode in the hUART Struct remains at the intial error value.

At the end of my try&error process, I managed to have all registers at the same values they had during valid transmissions, but there were still no bytes received. Whatever i tried, id had no effect. The UART remained in a non receiving state. So i decided to use the "brute force" approach and use the HAL functions, which I know they work.

Finally the solution is pretty simple: if an Idle Interrupt is detected, but the number of received bytes is 0 => check the Error-Status of the UART with HAL_UART_GetError(...) If there is an error, stop the UART with HAL_UART_DMAStop(...) and restart it with HAL_UART_Receive_DMA(...)

The code:

if(RxLen) {
   // normal execution, number of received bytes > 0
   if(UA_RXCallback[i]) (*UA_RXCallback[i])(hUA);       // exec RX callback function
} else {
    if(HAL_UART_GetError(&huart2)) {
        HAL_UART_DMAStop(&huart2);                          // STOP Uart
        MX_USART2_UART_Init();                              // INIT Uart
        HAL_UART_Receive_DMA(&huart2, UA2_Buf, UA2_BufSz);  // START Uart DMA
        __HAL_UART_CLEAR_IDLEFLAG(&huart2);                 // Clear Idle IT-Flag
        __HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);        // Enable Idle Interrupt
   }
}
Chris_B
  • 359
  • 5
  • 13
  • So what you see is that "DMAR - DMA enable receiver" is set to 0 on error even through DDRE is 0. Doesn't that look like an MCU error? Did you check the errata sheet? – nielsen Feb 28 '22 at 18:24
  • nope I didn't, but maybe I should. Another annotiation: the code above obviously makes no sense. I C&P it and changed it in a way, that it becomes readable, but without the context. It's just for explanation, not the actual code I am using. Edit: I checked the Errata Sheet (https://www.st.com/content/ccc/resource/technical/document/errata_sheet/5a/99/09/42/3f/b1/44/6c/DM00091791.pdf/files/DM00091791.pdf/jcr:content/translations/en.DM00091791.pdf), but this behaviour is not described as an MCU error in there – Chris_B Mar 02 '22 at 06:44
  • @nielsen I'm having the same issue on STM32L4 chips. The uart peripheral seems identical. The HAL driver stops DMA in software when an error occurs during DMA RX mode. The HAL driver software clears DMAR, so it's not a hardware bug. – Louis Cloete Jan 18 '23 at 17:55
1

I had a similar issue. I'm using a DMA to receive data, and then periodically checking how many bytes were received. After a bit error, it would not recover. The solution for me was to first subscribe to ErrorCallback on the UART_HandleTypeDef.
In the error handler, I then call UART_Start_Receive_DMA(...) again. This seems to restart the UART and DMA without issue.

fizzybones
  • 21
  • 3