You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

STM32 USART同时收发随机丢字节问题及真全双工实现咨询

STM32 USART全双工通信问题解答

核心结论:STM32 USART完全支持真正的全双工通信

所谓“收发共用同一个DR寄存器”只是地址映射层面的共用,硬件上TX和RX的移位寄存器、数据缓存是完全独立的:读DR时访问的是接收数据寄存器,写DR时访问的是发送数据寄存器,两者互不干扰。客服提到的“问答模式”只是一种半双工的应用场景,并非USART的硬件限制,USART本身就是为全双工设计的通信接口。

你的代码中导致丢字节的关键问题

1. DMA发送与中断发送冲突

你在main中启用了USART_DMAReq_Tx,但实际采用的是TXE中断方式发送数据。DMA和中断同时操作USART的DR寄存器会导致控制权争夺,必然引发数据丢失或错乱。两者必须二选一,不能同时启用。

2. 发送缓冲区索引的竞态风险

USART1SendChar在主程序上下文操作USART1TxIdx0,而TXE中断在中断上下文操作USART1TxIdx1,如果主程序写入缓冲区时恰好触发TXE中断,会导致索引计算错误,进而丢失数据。

3. 接收缓冲区无溢出保护

当连续接收数据超过256字节且未遇到换行符(0x0A)时,USART1RxIdx会溢出,覆盖旧数据,直接导致丢字节。

4. 中断处理逻辑的潜在问题

在RXNE中断中直接调用USART1SendChar,虽然不会并行执行,但如果发送缓冲区已满,新写入的数据会覆盖未发送的内容,导致发送数据错乱。

修复方案

1. 关闭DMA发送模式

注释掉main中的DMA启用代码:

// USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);

2. 保护发送缓冲区的索引操作

在USART1SendChar中操作缓冲区索引时,临时关闭USART中断,避免竞态:

static void USART1SendChar(char *buf, unsigned short cnt)
{
    if(USART1Act==0) return;
    
    // 关闭USART中断,保护索引操作
    uint32_t usart_cr1 = USART1->CR1;
    USART1->CR1 &= ~(USART_CR1_TXEIE | USART_CR1_RXNEIE);
    
    for (unsigned short idx1 = 0; idx1 < cnt; ++idx1)
    {
        // 检查发送缓冲区是否已满
        unsigned short next_idx = USART1TxIdx0 + 1;
        if (next_idx == sizeUSART1TX) next_idx = 0;
        if (next_idx == USART1TxIdx1) {
            // 缓冲区已满,停止写入
            break;
        }
        USART1TxBuf[USART1TxIdx0] = buf[idx1];
        USART1TxIdx0 = next_idx;
    }
    
    // 恢复原中断配置
    USART1->CR1 = usart_cr1;
    // 确保TXE中断开启(如果缓冲区有数据)
    if (USART1TxIdx0 != USART1TxIdx1) {
        USART1->CR1 |= USART_CR1_TXEIE;
    }
}

3. 添加接收缓冲区溢出处理

在RXNE中断中先检查缓冲区是否已满:

void USART1_IRQHandler(void)
{
    if (USART1->SR & USART_SR_RXNE)  //RXNE, reset by reading DR
    {
        if(USART1RxIdx >= sizeUSART1RX) {
            // 缓冲区溢出,丢弃当前数据并清除标志
            (void)USART1->DR;
            return;
        }
        USART1RxBuf[USART1RxIdx++] = USART1->DR;

        if (USART1RxBuf[USART1RxIdx-1] == '\n')   //command end
        {
            // ... 执行指令逻辑

            USART1SendChar(USART1RxBuf, USART1RxIdx);   //confirm the command
            memset(USART1RxBuf, 0, sizeUSART1RX);
            USART1RxIdx = 0;
        }
    }
    if ((USART1->SR & USART_SR_TXE) && (USART1->CR1 & USART_CR1_TXEIE))
    {
        USART1->DR = USART1TxBuf[USART1TxIdx1++];
        if (USART1TxIdx1 == sizeUSART1TX)
            USART1TxIdx1 = 0;
        if (USART1TxIdx1 == USART1TxIdx0)
            USART1->CR1 &= ~USART_CR1_TXEIE;
    }
}

4. 优化中断优先级(可选)

如果CAN中断优先级过高,会延迟USART中断的响应时间,导致丢字节。可以调整USART1的中断优先级,确保在高负载下能及时处理收发请求:

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; // 提高抢占优先级,根据实际系统调整
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;

内容的提问来源于stack exchange,提问作者reko

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 23:12:34