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
相关产品推荐
相关产品推荐

