<think>
我们注意到用户在中断处理函数中调用了发送函数USART1SendString,这会导致在接收中断中执行发送操作。根据STM32 USART的工作原理,接收和发送使用同一个数据寄存器(DR)但有不同的状态标志和中断源。然而,在同一个USART上同时进行发送和接收操作是可行的,因为发送和接收有各自独立的寄存器(发送数据寄存器TDR和接收数据寄存器RDR)以及移位寄存器。但是,如果在中断服务程序中执行发送操作,可能会引入以下问题:
1. **中断服务程序执行时间过长**:发送字符串是一个相对耗时的操作(尤其是通过查询方式发送),会导致中断占用时间过长,可能错过后续接收到的数据。因为当新的数据到来时,如果前一个中断还没有处理完,新的接收中断得不到及时响应,就会导致数据丢失。
2. **中断优先级问题**:如果发送操作本身也使用中断(但用户提供的USART1SendString函数可能是查询方式),那么发送操作可能会抢占接收中断,导致接收中断被延迟。
3. **状态标志冲突**:在中断中发送数据会操作USART的状态寄存器(如检查TXE标志),但通常不会直接影响接收。但是,如果发送操作导致状态寄存器被清除,可能会影响到接收中断标志的清除。
4. **缓冲区溢出**:由于中断服务程序执行时间变长,在发送字符串期间,如果连续有多个字节到达,可能会因为处理不及时而溢出。
根据用户描述,注释掉发送函数后接收正常,不注释则只能接收前两个字符串(比如发送"Hello"只能收到"Hel"),这很可能是因为在中断中发送字符串占用了太多时间,导致后续数据接收中断被延迟甚至丢失。
### 原因分析
当第一个字节(如'H')到达时,进入中断,此时接收中断处理函数开始执行。在中断函数中,由于数据不是0x0d也不是0x0a,会进入`else`分支,执行`USART1SendString("temp3");`。这个发送函数会循环发送多个字符("temp3"包括5个字符),每个字符的发送都需要等待TXE标志(假设是查询方式发送),这样就会在中断中停留很长时间(比如5个字符×每个字符大约100us(在115200波特率下)≈500us)。
在115200波特率下,每个字节传输时间大约为:$T_{byte} = frac{10 bits}{115200 bps} approx 86.8 mu s$。因此,发送5个字节需要大约434us。在这434us内,如果又有新的数据到达(比如第二个字节'e'到达),由于中断尚未退出,新的接收中断无法被响应,此时第二个字节只能停留在接收数据寄存器中(如果只有一个字节的接收缓冲区,那么第三个字节来临时就会覆盖第二个字节?但实际上,STM32 USART有一个接收移位寄存器和一个接收数据寄存器,所以最多可以缓存2个字节:当接收移位寄存器接收完一个字节后会转移到接收数据寄存器,然后移位寄存器开始接收下一个字节。但是,如果接收数据寄存器中的字节没有被及时读取(因为中断还未返回),那么当第三个字节到来时,移位寄存器中的数据要转移到数据寄存器时,如果数据寄存器中还有数据(未被读取),就会发生溢出(ORE标志置位),导致数据丢失。
具体过程如下:
- 第一个字节'H':进入中断,开始执行中断服务程序(ISR),在ISR中发送"temp3",耗时较长。
- 在ISR执行期间,第二个字节'e'到达,被接收到移位寄存器,然后转移到接收数据寄存器(此时USART1->SR的RXNE标志置位,但因为ISR正在执行,所以不会触发新的中断)。
- 第三个字节'l'到达,移位寄存器开始接收。当接收完成时,移位寄存器中的字节要转移到接收数据寄存器,但此时接收数据寄存器中还有未读取的'e'(因为ISR还没执行完,还没来得及读'e'),因此发生溢出,第三个字节丢失,同时ORE标志置位。
- 当ISR执行完毕返回后,由于ORE标志置位,会再次进入中断(如果使能了错误中断),否则只能读取到一次数据(在中断中读取DR寄存器会同时清除RXNE和ORE标志,但只能读取到数据寄存器中的值,即'e',而第三个字节已经丢失)。
因此,用户只能接收到前两个字节('H'和'e'),第三个字节丢失。至于为什么用户收到的是"Hel"(三个字母)?实际上,用户发送的是"Hello",而用户只收到了前三个字节?这可能是另一种情况:在发送第一个字符串"temp3"期间,只丢失了部分字节。
### 解决方法
1. **避免在中断服务程序中执行耗时操作**:尤其是发送多个字节的操作。应该将发送操作移出中断,改为在中断中设置标志,在主循环中发送。
2. **使用DMA进行发送**:如果确实需要在中断中触发发送,可以使用DMA方式发送字符串,这样中断只需要启动DMA传输,然后立即返回,不会占用很长时间。
3. **使用环形缓冲区**:在接收中断中,仅将接收到的字节存入缓冲区,并设置一个标志,主循环中检查该标志并处理数据(包括发送响应)。
### 参考依据
根据STM32参考手册(以STM32F1系列为例):
- USART接收过程:数据从输入引脚经过起始位检测、采样,然后移入接收移位寄存器。当接收到一个完整字节后,该字节会被转移到接收数据寄存器(RDR),同时状态寄存器(SR)的RXNE标志置位,如果使能了RXNE中断,则触发中断。
- 当RXNE标志为1时,表示数据已经接收到并可被读取。如果在下一个数据接收完成(即移位寄存器接收完下一个字节)时,RXNE标志还没有被清除(即数据寄存器中的数据未被读取),则会发生溢出(ORE标志置位),新接收到的数据将丢失。
因此,在中断服务程序中,应尽快读取接收到的数据,避免长时间占用中断。
### 修改建议
修改中断服务程序,仅将接收到的字节存入缓冲区,并设置接收完成标志,在主循环中处理接收的数据和发送响应。
示例代码:
```c
// 定义接收缓冲区和标志
#define RX_BUF_SIZE 256
volatile uint8_t rx_buf[RX_BUF_SIZE];
volatile uint16_t rx_index = 0;
volatile bool recv_flag = false;
void USART1_IRQHandler(void)
} else {
// 缓冲区溢出处理
}
}
// 检查溢出错误
if (USART1->SR & USART_SR_ORE) {
// 清除溢出错误标志(通过先读SR再读DR)
uint8_t temp = USART1->DR; // 读取DR可以清除ORE
}
}
```