导言
上一章节《STM32F103_LL库+寄存器学习笔记06 - 梳理串口与串行发送“Hello,World"》梳理完USART的基本设置与发送字符串“Hello,World",接着梳理接收缓冲区非空中断。
实用的串口接收程序都会使用中断方式,不会使用轮询方式。最主要的原因当波特率较高时,轮询极其容易错失数据,而中断方式能确保及时获取数据。另外,轮询方式需要不断查询USART_SR的RXNE位(接收数据寄存器非空),即使没有数据到达。这会导致CPU资源浪费,尤其在低速通讯或数据不频繁到达时。中断方式只有在数据到达时(RXNE = 1),才需要CPU介入处理,其他时间CPU可以专注处理其他任务,显著提高系统效率。
接收中断也有区分:
- 接收缓冲区非空中断(本章节目的)
- IDLE接收空闲中断(需配合DMA一起使用,后续再梳理)
效果如下:
在电脑上用串口助手往STM32开发板发送字符串"Send\r\n",STM32开发板收到字符串"Send"后,马上回传给电脑的串口助手。
如上图所示,往开发板发送字符串"Send\r\n"
后,开发板会将内容原封不动回传。
项目地址:https://github.com/q164129345/MCU_Develop/tree/main/stm32f103_ll_library07_usart_interrupt_receive
一、CubeMX
如上所示,开启USART1全局中断。
确认是LL库代码后,生成代码。
1.1、usart.c
1.2、stm32f1xx_it.c
如上所示,多了全局中断函数USART1_IRQHandler()
。
这个函数命名在启动文件的中断向量表里。
如上所示,启动文件里的中断向量表跟《STM32F1参考手册》章节9.1.2的表一样,USART1全局中断上面是SPI2全局中断。
二、代码(LL库)
开始使用LL,实现接收缓存区非空中断程序。
2.1、main.c
2.2、stm32f1xx_it.c
2.3、编译、下载
效果如下:
三、寄存器的梳理
3.1、在USART_CR1打开接收缓存区非空中断
如上所示,寄存器USART_CR1的位5-RXNEIE置1时,开启接收区非空中断。
USATT1->CR1 |= (1UL << 5UL); // // 设置 CR1 寄存器的 RXNEIE 位(位 5)为 1,启用 RXNE 中断
3.2、在USART_SR判断接收缓冲区非空中断
如上所示,从寄存器USART_SR的位5-RXNE判断,是不是读数据寄存器非空,表示是否有收到数据。
if (USART1->SR & (1UL << 5UL)) {
// 读数据寄存器非空,USART->DR寄存器收到数据
}
四、代码(寄存器方式)
4.1、main.c
4.2、stm32f1xx_it.c
如上所示,从USART1->SR
判断接收缓冲区非空中断的同时也要判断程序有没有开启接收缓冲区非空中断(USART1->CR
的位5-RXNEIE),这样做的目的是程序更加健壮。
uint8_t received_data = (uint8_t)(USART1->DR & 0xFF)
的写法为什么不能用uint8_t received_data = USART1->DR
???
- received_data是8位变量 ,然而USART1->DR是32位变量。
- USART1->DR 是一个 32 位寄存器,直接赋值时编译器会发出警告(如果开启了严格的警告选项,例如 -Wconversion),提示从 32 位到 8 位的隐式转换可能丢失数据。
- 如果将来串口配置改为 9 位数据模式(LL_USART_DATAWIDTH_9B),直接赋值会丢失第 8 位的信息(因为 uint8_t 只能存储 8 位)。
- 避免编译器警告,代码更健壮。可移植性更好,即使将来数据位宽改变(例如 9 位),也能通过调整掩码(例如 0x1FF)轻松适配。例如:
uint16_t received_data = (uint16_t)(USART1->DR & 0x1FF);
4.3、编译、下载
如上所示,代码的效果跟LL库一样。
如上所示,通过debug模式打断点,观察寄存器USART1->SR的位RXNE确实被置1了。