使用HAL_UART_Transmit发送数据量比较大的时候同时触发串口中断接收会导致串口中断无法接收

具体现象:

最近的一个项目,在使用串口HAL_UART_Transmit发送数据的同时接收控制指令,偶尔会无法进入串口中断,导致无法通讯的问题

在查找问题的时候发现HAL_UART_Transmit发送数据量比较大,大概一次2-3K字节,在发送数据的时候如果APP发送串口控制指令下来,那么理论上会进入HAL_UART_RxCpltCallback串口中断处理,并通过以下HAL_UART_Receive_IT串口中断获取数据并执行指令任务            

但实际运行的时候,刚开始能正常收发,直到APP连续发送好几条控制指令下来以后,串口突然无法进入中断处理函数,串口重新初始化、使用LOCK解锁、网上各种方法都试过不行。

原因:HAL_UART_Transmit 是阻塞式函数,在发送期间会通过 __HAL_UART_DISABLE_IT(huart, UART_IT_RXNE) 关闭接收中断,进入循环等待,直到发送缓冲区为空(TC 标志置位),在发送完成后才重新启用接收中断(__HAL_UART_ENABLE_IT(huart, UART_IT_RXNE))。

所以在在长时间发送大量数据期间(刚好我这边波特率必须要57600来传输2-3K不定长度字节数据),串口无法响应接收中断,导致会偶尔触发无法进入中断

解决办法:

优化HAL_UART_Transmit 发送流程,在HAL_UART_Transmit 发送完成后添加以下代码重启中断

__HAL_UART_ENABLE_IT(huart, UART_IT_RXNE);

 完整HAL_UART_Transmit 如下:

void Safe_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
{
    // 保存当前接收中断状态
    uint32_t isRxEnabled = __HAL_UART_GET_IT_SOURCE(huart, UART_IT_RXNE);
    
    // 执行阻塞式发送
    HAL_UART_Transmit(huart, pData, Size, Timeout);
    
    // 恢复接收中断(如果之前是启用的)
    if (isRxEnabled) {
        __HAL_UART_ENABLE_IT(huart, UART_IT_RXNE);
    }
}

<think>嗯,用户遇到了HAL_UART_Transmit_DMA在不加延时的情况下无法连续发送数据的问题,导致数据丢失。首先,我需要回忆一下STM32的DMA和UART相关知识,以及HAL的相关函数机制。根据用户提供的引用资料,有几个可能的原因和解决方案。 首先,引用[4]提到,DMA传输数据到串口的速度太快,导致下一条发送时串口仍处于BUSY状态。这是因为HAL中的状态管理机制会在发送时检查UART的状态是否为就绪。如果前一次发送未完成,再次调用发送函数会失败。这时候可能需要等待上一次发送完成后再发送,或者使用回调函数来触发下一次发送。 接着,引用[5]指出数据可能存储在栈上,函数返回后栈空间被回收,导致数据错误。这种情况下,用户需要确保发送的数据缓冲区在传输期间有效,比如使用全局变或静态变,而不是局部变。 另外,引用[1]提到DMA中断未处理的问题。HAL的DMA函数会开启多个中断,比如传输完成中断(TC),如果用户没有正确处理这些中断,可能导致状态未被正确更新,从而影响后续发送。需要检查是否在中断处理函数中正确清除标志位,或者是否启用了相应的中断回调函数。 然后,引用[3]说明HAL_UART_Transmit_DMA在配置DMA时会设置内存到外设的地址,并开启中断。可能用户没有正确配置DMA通道,或者在发送前没有重新初始化DMA参数,导致后续发送失败。需要确保每次发送前重新配置DMA,或者检查DMA通道是否被其他操作占用。 综合这些可能的原因,解决方案可能包括:使用阻塞方式等待发送完成、在发送前检查UART状态、使用静态或全局缓冲区、处理DMA传输完成中断以重置状态标志,以及正确管理DMA通道的配置和使能。此外,引用[2]提到同时使用接收和发送DMA时需要注意配置细节,但用户当前的问题主要集中在发送端,可能需要单独处理发送相关的问题。 需要验证用户是否在每次发送前等待UART就绪状态,或者是否在传输完成回调中处理了状态更新。例如,在发送完成后,HAL会调用HAL_UART_TxCpltCallback,用户可以在该回调中启动下一次发送,从而避免状态冲突。同时,检查数据缓冲区的生命周期是否足够长,避免发送过程中数据被覆盖或释放。 可能还需要考虑DMA的传输模式,比如是否使用了循环模式,但这可能不适用于单次传输的情况。另外,检查DMA和UART中断优先级,确保没有其他中断干扰传输过程。最后,确保在初始化时正确配置了DMA和UART的相关参数,比如波特率、数据位、止位等,这些配置错误也可能导致发送失败。 总结起来,可能的解决步骤是:1)确保缓冲区有效;2)处理传输完成中断以更新状态;3)在发送前检查UART是否就绪;4)使用回调函数进行连续发送;5)必要时添加适当的延时或等待机制。需要根据具体情况逐一排查这些可能性,找到导致数据丢失的具体原因,并应用对应的解决方案。</think>### HAL_UART_Transmit_DMA 连续发送数据丢失的原因及解决方案 #### **原因分析** 1. **串口状态未就绪** HAL在调用 `HAL_UART_Transmit_DMA()` 时,会检查串口状态是否为 `HAL_UART_STATE_READY`。若前一次DMA传输未完成(状态仍为 `HAL_UART_STATE_BUSY_TX`),后续发送请求会被直接忽略,导致数据丢失[^4]。 2. **DMA中断未正确处理** `HAL_UART_Transmit_DMA()` 会触发DMA传输完成中断(`DMA_IT_TC`),但若未在中断回调函数中更新状态标志,串口状态无法恢复为 `READY`,从而阻塞后续发送[^1][^3]。 3. **缓冲区生命周期问题** 若发送数据存储在局部变(栈空间)中,函数返回后缓冲区可能被释放或覆盖,导致DMA传输数据错误[^5]。 4. **DMA配置未重置** DMA传输完成后,需重新配置源地址和数据长度。若未重置,可能导致后续发送数据长度错误或地址无效[^3]。 --- #### **解决方案** ##### **1. 确保串口状态就绪** - **方法1:阻塞等待** 在每次调用 `HAL_UART_Transmit_DMA()` 前,检查串口状态: ```c while (huart->gState != HAL_UART_STATE_READY) { // 等待或执行其他任务 } HAL_UART_Transmit_DMA(&huart, buffer, length); ``` 注意:此方法可能影响实时性,需权衡使用。 - **方法2:中断回调触发** 在DMA传输完成中断回调函数 `HAL_UART_TxCpltCallback()` 中,设置状态标志或触发下一次发送: ```c void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) { if (huart->Instance == USART1) { // 标记状态为READY或启动下一次发送 is_tx_complete = true; } } ``` ##### **2. 使用全局/静态缓冲区** 确保发送数据缓冲区在DMA传输期间有效: ```c static uint8_t tx_buffer[256]; // 使用静态或全局变 void send_data() { memcpy(tx_buffer, "Hello", 5); HAL_UART_Transmit_DMA(&huart, tx_buffer, 5); } ``` ##### **3. 重新配置DMA参数** 在每次传输前,手动重置DMA配置(需根据HAL版本调整): ```c void restart_dma(UART_HandleTypeDef *huart, uint8_t *data, uint16_t len) { HAL_UART_DMAStop(huart); // 止DMA huart->hdmatx->Instance->CNDTR = len; // 重置数据长度 huart->hdmatx->Instance->CMAR = (uint32_t)data; // 重置源地址 HAL_UART_Transmit_DMA(huart, data, len); // 重新启动 } ``` ##### **4. 优化中断处理** 在DMA中断中清除标志并更新状态: ```c void DMAx_StreamX_IRQHandler(void) { HAL_DMA_IRQHandler(huart->hdmatx); // 手动清除TC标志(某些情况下HAL可能未完全处理) __HAL_DMA_CLEAR_FLAG(huart->hdmatx, DMA_FLAG_TCx); } ``` ##### **5. 添加延时或信号** 对于高频率发送,可结合RTOS信号或简单延时: ```c HAL_UART_Transmit_DMA(&huart, data1, len1); osDelay(1); // 微小延时(根据波特率调整) HAL_UART_Transmit_DMA(&huart, data2, len2); ``` --- #### **总结流程图** ```plaintext 1. 检查UART状态是否为READY → 是 → 启动DMA发送 2. 若状态为BUSY → 等待中断回调或超时 3. DMA传输完成 → 触发中断 → 更新状态为READY 4. 启动下一次发送 ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值