【问题标题】:STM32 UART in DMA mode stops receiving after receiving from a host with wrong baud rateDMA模式下的STM32 UART从波特率错误的主机接收后停止接收
【发布时间】:2022-08-23 20:57:26
【问题描述】:

场景:我有一个 STM32 MCU,它在 DMA 模式下使用带有空闲中断的 UART 进行 RS485 数据传输。 UART 的波特率在 CubeMX 中设置,在本例中设置为 115200。我的代码工作正常,当主机使用正确的波特率时,它也“长时间”稳定,没有问题或担心。

但是:当我在主机上设置错误的波特率时,例如56700而不是115200,UART停止接收数据,即使我稍后将主机的波特率设置为微控制器使用的相同波特率,它也不起作用。到目前为止,解决此问题的唯一方法是:重置 MCU 并以正确的波特率重新连接。

给你一些(伪)代码:

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

或者简单地说:有一个用于 DMA 的 UART 缓冲区 (UART_Buf[128]) 并且 UART 以 HAL_UART_Receive_DMA(...) 启动,DMA Rx 在 CubeMX 中设置为循环模式,空闲中断也被激活,使用HAL 宏:__HAL_UART_ENABLE_IT(...);到目前为止,此代码运行良好。

工作正常意味着: 当我将数据从我的 PC 传输到 Micro 时,(一个)空闲中断由 MCU(正确)触发。在 ISR 中,我设置了一个标志,以便之后开始数据解析。我收到的正是我发送的字节数,一切都很好。

但是:当我在终端程序中进行错误设置而不是(正确)波特率 115200 时,波特率选择菜单设置为例如57600,麻烦开始了:

每次传输后仍会触发空闲中断。 但它会在快速“突发”中触发 2-4 次(取决于波特率),并且接收到的字节数为 0。我希望至少有一些BS数据,但缓冲区中正好有 0 个数据——我可以用调试器检查。显然没有收到任何东西。当我在终端程序中更改波特率并重新启动它时,MCU 上仍然没有收到任何内容。

如果主机的波特率不正确,我可以接受 0 个接收字节,但是在硬件重置完成之前,一个具有错误波特率的主机的传入传输会禁用 UART,这很不酷。

到目前为止,我尝试解决这个问题:使用 MX_USART2_UART_Init() 计算“空闲中断突发”和 0 个接收字节以触发“自复位”例程,该例程停止 UART 并重新启动它;常规。零效应。我可以看到空闲中断仍然被正确触发,但是缓冲区仍然是空的,并且没有数据传输到缓冲区中。 UART 保持在非接收状态。

问题有没有人遇到过类似的问题,如果是的话:你是如何解决的?

附加信息:这发生在 STM32F030 和 STM32G03x 上

  • 当您进行“自重置”时,您是否也重置了 DMA?

标签: stm32 usart baud-rate


【解决方案1】:

当您以错误的波特率发送到 UART 时,接收器将显示为帧错误和/或噪声错误。它也可能显示为正确接收的随机字符,但这种可能性较小,所以不要对缓冲区中没有任何内容感到惊讶。

当您使用 DMA 接收时,打开错误中断或轮询错误位是正常的。当检测到错误时,您将重新初始化所有内容并重新启动 DMA。这听起来像你试图通过计算空闲中断来做的事情,但你只是没有检查正确的位。

如果您不想这样做,则并非不可能想象您在驱动程序级别无事可做并想尝试在更高级别进行重新同步(例如:重新开始阅读并丢弃所有内容,直到换行字符),但你必须记住至少两件事:

首先,确保清除 USART_CR3 寄存器中的 DDRE 位。 “接收错误时禁用 DMA”这个名称不言自明。

其次,只要字节之间有空闲间隙,UART 外围设备就能够自我重新同步。如果您将发送器切换到正确的波特率但不断输出数据,那么接收器可能永远无法正确识别哪个位是起始位。

【讨论】:

  • 感谢您的回答!在主机端停止传输一两秒钟没什么大不了的,因为我有一个“查询/响应”场景,并且可以在主机上检测到,如果根本没有响应。在 MCU 上,我将遵循“清除 DDRE 位”的建议!我的直觉告诉我,这个问题应该与 DMA 相关——因为缓冲区仍然是空的。
【解决方案2】:

在进一步调查了这个问题之后,我找到了一个解决方案。

抽象的:当主机将 MCU 连接到具有不同于 UART 设置的波特率的 UART 时,它将进入错误状态并停止 DMA 传输到 RX 缓冲区。您可以检查 HAL_UART_GetError(...) 函数是否有错误。如果出现错误,请停止 UART/DMA 并重新启动它。

细节:首先,它不是 USART_CR2 寄存器中的 DDRE 位。 CubeMX 将其设置为 0。但汤姆五世的暗示将我引向了正确的方向。

我试图通过使用寄存器位来恢复 UART。我多次阅读了参考手册的 UART 部分,并试图找出以何种顺序设置哪些位,以手动解决错误情况。

我发现了什么: 当 UART 接收到具有错误波特率的传输时,UART 寄存器会发生以下变化(在 STM32F030 上): 控制寄存器 1 (USART_CR1) - 第 8 位 (PEIE) 从 1 变为 0。PEIE 是奇偶校验中断使能位。 控制寄存器 2 (USART_CR2) - 保持不变 控制寄存器 3 (USART_CR3) - 从 0d16449 变为 0d16384,这意味着 位 0(EIE - 错误中断启用)从 1 变为 0 第 6 位(DMAR - DMA 启用接收器)从 1 变为 0 位 14(DEM - 驱动器启用模式)保持不变为 1

USART_CR3.DEM 是有道理的。我正在使用 F030 的 RS485 功能,因此 UART 自己处理驱动程序启用 GPIO。 USART_CR3.EIE 和 USART_CR3.DMAR 从 1 到 0 的转换很可能是没有更多数据传输到 DMA 缓冲区的原因。

除此之外,中断和状态寄存器 (USART_ISR) 中的错误标志为 ORE 和 FE 被设置。 ORE 代表溢出错误,FE 代表帧错误。虽然这些位可以通过将 1 写入中断标志清除寄存器 (USART_ICR) 的相应位来清除,但 hUART 结构中的 ErrorCode 仍保持初始错误值。

在我的 try&error 过程结束时,我设法使所有寄存器的值与它们在有效传输期间的值相同,但仍然没有收到任何字节。无论我尝试什么, id 都没有效果。 UART 保持在非接收状态。所以我决定使用“蛮力”方法并使用我知道它们有效的 HAL 函数。

最后解决方案非常简单: 如果检测到空闲中断,但接收到的字节数为 0 => 使用 HAL_UART_GetError(...) 检查 UART 的错误状态 如果出现错误,请使用 HAL_UART_DMAStop(...) 停止 UART,并使用 HAL_UART_Receive_DMA(...) 重新启动它

编码:

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

【讨论】:

  • 因此,您看到的是即使 DDRE 为 0,“DMAR - DMA 启用接收器”也会在错误时设置为 0。这看起来不像 MCU 错误吗?你检查勘误表了吗?
  • 不,我没有,但也许我应该。另一个注释:上面的代码显然没有意义。我对它进行了 C&P 并以某种方式对其进行了更改,使其变得可读,但没有上下文。这只是为了解释,而不是我使用的实际代码。编辑:我检查了勘误表 (st.com/content/ccc/resource/technical/document/errata_sheet/5a/…),但此行为并未被描述为 MCU 错误
【解决方案3】:

我有一个类似的问题。我正在使用 DMA 接收数据,然后定期检查接收到的字节数。稍有错误后,它就无法恢复。我的解决方案是首先在UART_HandleTypeDef 上订阅ErrorCallback
在错误处理程序中,我再次调用UART_Start_Receive_DMA(...)。这似乎可以毫无问题地重新启动 UART 和 DMA。

【讨论】:

    猜你喜欢
    • 2019-02-21
    • 2017-09-04
    • 2021-09-06
    • 2015-09-18
    • 1970-01-01
    • 2019-10-26
    • 2019-12-27
    • 2019-12-08
    • 1970-01-01
    相关资源
    最近更新 更多