串口引起中断举例
硬件:STM32F103,对接JDY‑31蓝牙模块
引脚:PA9(TX),PA10(RX);波特率可设置;全局变量RxData保存收到的1字节数据
一、代码整体功能
初始化串口1,开启接收中断。蓝牙发来1个字节,不用主循环不停查询,单片机会自动进中断把数据读到RxData,while(1)主循环直接读取RxData使用。
二、USART1_Init()初始化函数逐块理解
1. 开启时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
类比:给GPIOA引脚、串口1模块“通电”,不通电外设不工作。
2. 配置TX PA9
GPIO_Initstructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Initstructure.GPIO_Pin = GPIO_Pin_9;
GPIO_Initstructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_Initstructure);
PA9作为串口发送脚,复用推挽输出,不归普通GPIO管,交给串口外设控制输出电平。
3. 配置RX PA10
GPIO_Initstructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
GPIO_Initstructure.GPIO_Pin = GPIO_Pin_10;
GPIO_Init(GPIOA, &GPIO_Initstructure);
PA10接收外部发来的数据,浮空输入,读取引脚上的电平信号。
4. 串口参数配置
USART1_InitStructure.USART_BaudRate = bound;
USART1_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART1_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //既接收也发送
USART1_InitStructure.USART_Parity = USART_Parity_No;
USART1_InitStructure.USART_StopBits = USART_StopBits_1;
USART1_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_Init(USART1, &USART1_InitStructure);
USART_Cmd(USART1, ENABLE); //使能串口1,串口开始工作
设置:波特率、无硬件流控、收发模式、无校验、1停止位、8位数据,最后使能串口硬件。
5. NVIC中断控制器配置
NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
NVIC_Init(&NVIC_InitStruct);
// NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
USART1_IRQn:串口1对应的中断通道NVIC_IRQChannelCmd = ENABLE:打开串口1总中断开关- 抢占优先级1,子优先级0:多个中断同时到来,决定谁优先执行
⚠️重点:
NVIC_PriorityGroupConfig()不能写在此函数内部,要放到main函数最开头,否则优先级设置无效。
6. 外设中断开关 + 清标志
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
USART_ClearFlag(USART1, USART_FLAG_TC);
USART_IT_RXNE:接收事件中断开关。收到字节产生RXNE标志后,才会向NVIC发起中断请求。
两层开关:NVIC总开关 + USART_ITConfig外设中断开关,两个都打开,中断才会触发。
USART_FLAG_TC:发送完成标志。这行只是初始化清除发送的残留标志,和接收中断无关,可以删掉。
三、中断服务函数
void USART1_IRQHandler(void)
{
if (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == SET)
{
USART_ClearFlag(USART1, USART_FLAG_RXNE);
RxData = USART_ReceiveData(USART1);
// USART_SendData(USART1, RxData);
// while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET) ;
}
}
函数名字固定,不能修改,启动文件靠这个名字找到中断入口。
完整事件举例(蓝牙发送字符 '1' 给单片机)
- JDY‑31蓝牙模块,通过PA10引脚,把字符
'1'逐位发给STM32。 - 串口硬件收完整一个字节,硬件自动置位
RXNE = SET(接收寄存器非空)。 - 因为打开了
USART_IT_RXNE,串口向NVIC发出中断申请。 - NVIC判断允许响应中断,CPU暂停
while(1)里面正在跑的代码,保存现场,跳转到USART1_IRQHandler。 if(USART_GetFlagStatus(USART1,USART_FLAG_RXNE)==SET)判断确认:是收到数据这件事触发的中断。RxData = USART_ReceiveData(USART1);读取寄存器拿到'1'存入全局变量。
读取
USART_ReceiveData()硬件会自动清除RXNE标志;代码又手动调用ClearFlag,属于冗余,不报错。
- 中断函数运行结束,CPU恢复现场,回到
while(1)继续运行;主循环读取RxData就拿到蓝牙的数据。
注释掉的回显代码:打开后收到什么就原路发回去;中断里面不建议写while阻塞等待,会拖慢系统。
四、3个关键标志通俗区分
- RXNE:接收寄存器非空。收到1字节硬件置1;读接收寄存器硬件自动清零,用于接收中断。
- TXE:发送寄存器空,可以发送下一个字节。
- TC:整帧数据全部从TX引脚物理发送完毕,需要手动软件清零;本代码没有使用TC中断。
五、高频踩坑点
- 时钟忘记开启,串口完全无反应。
- 中断两个开关缺一不可:NVIC使能、
USART_ITConfig(USART_IT_RXNE,ENABLE)。只置RXNE标志不会进中断。 - 中断服务函数名字不能写错。
- 中断内部不要写
Delay()、复杂运算;只做读取赋值,业务逻辑放主循环while(1)。 - 中断优先级分组函数必须放在main开头。
六、极简流程口诀
外部发来字节 → 硬件置RXNE标志 → 外设发起中断请求 → NVIC裁决放行 → 跳中断服务函数读数据 → 返回主循环。
明白,笔记文字保留,完整代码放在笔记的最末尾,笔记内部不粘贴大段代码,只用代码片段讲逻辑,重新整理:
USART1串口接收中断笔记|通俗版
硬件:STM32F103,对接JDY‑31蓝牙模块
引脚:PA9(TX),PA10(RX)
全局变量RxData:用来保存中断收到的1字节
一、整体功能
初始化串口1,开启接收中断。
蓝牙发来字节,不需要while(1)不停查询,硬件自动触发中断,把数据存入RxData,主循环直接读取RxData使用。
二、USART1_Init 初始化讲解
- 开启时钟
给GPIOA、USART1打开时钟,外设不上电无法工作。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
-
配置TX引脚 PA9
模式:复用推挽输出,引脚交给串口外设控制,不作为普通GPIO。 -
配置RX引脚 PA10
模式:浮空输入,接收外部蓝牙发来的电平信号。 -
串口参数配置
波特率、8位数据、1停止位、无校验、收发模式,最后USART_Cmd(USART1,ENABLE)使能串口。 -
NVIC配置(内核中断控制器)
USART1_IRQn:串口1中断通道NVIC_IRQChannelCmd = ENABLE:NVIC总中断开关- 设置抢占优先级、子优先级
⚠️坑:
NVIC_PriorityGroupConfig()不能写在这里,要放在main()最开头,否则优先级无效。
- 外设中断开关
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
打开RXNE接收中断开关。
中断两层开关:NVIC总开关 + 这行外设中断开关,两个都开才会进中断。
USART_ClearFlag(USART1, USART_FLAG_TC);
清除发送完成TC标志,和接收无关,可以删掉。
三、中断服务函数
函数名USART1_IRQHandler固定,不能改名,启动文件靠这个名字找到中断入口。
真实过程举例:蓝牙发字符 '1'
- JDY‑31通过PA10把
'1'发给单片机。 - 串口硬件收完整1字节,硬件自动置
RXNE = SET。 - 因为开启
USART_IT_RXNE,串口向NVIC发起中断请求。 - NVIC允许响应中断,CPU暂停
while(1),保存现场,跳转到中断服务函数。 - 判断
RXNE==SET,确认是收到数据触发中断。 RxData = USART_ReceiveData(USART1);读取数据。
读
USART_ReceiveData()硬件自动清除RXNE;代码里手动ClearFlag属于冗余,不报错。
- 中断执行完毕,CPU恢复现场,回到主循环,主循环读
RxData拿到数据。
注释掉的回显代码:收到数据立刻原路发回;中断里面不要写while阻塞,影响实时性。
四、三个标志对比
- RXNE:接收寄存器非空。收到字节硬件置1;读数据硬件自动清零。用于接收中断。
- TXE:发送寄存器空,可以发送下一个字节。
- TC:一帧数据全部物理从引脚发完;需要软件手动清零;本代码没用到TC中断。
五、踩坑总结
- 忘记开时钟,串口完全不工作。
- 中断两个开关必须全部打开,只出现RXNE标志不会自动进中断。
- 中断函数名字不能写错。
- 中断里面禁止Delay、复杂运算;只做读取赋值,业务逻辑放while(1)。
- 中断优先级分组写在main最开头。
六、记忆口诀
外部发来字节 → 硬件置RXNE标志 → 外设发起中断请求 → NVIC裁决放行 → 跳中断服务函数读数据 → 返回主循环
————————
✅完整代码(放在这里,笔记全部讲完之后)
#include "USART1.h"
#include "sys.h"
u8 RxData=0x00;
/*
*** USART1 配置串口1与JDY-31通信 ***
*** 波特率:115200,8位数据位,1位停止位,无奇偶校验位 ***
*** 接口:PA9->TX PA10->RX ***
*** 作者:陈加油嗯 ***
***关注微信公众号学习更多单片机知识:微信搜索“陈加油嗯” ***
*/
void USART1_Init(uint32_t bound)
{
GPIO_InitTypeDef GPIO_Initstructure;
NVIC_InitTypeDef NVIC_InitStruct;
USART_InitTypeDef USART1_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
// USART1_TX PA9
GPIO_Initstructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Initstructure.GPIO_Pin = GPIO_Pin_9;
GPIO_Initstructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_Initstructure);
// USART1_RX PA10
GPIO_Initstructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
GPIO_Initstructure.GPIO_Pin = GPIO_Pin_10;
GPIO_Initstructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_Initstructure);
USART1_InitStructure.USART_BaudRate = bound;
USART1_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //无硬件数据流控制
USART1_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收模式
USART1_InitStructure.USART_Parity = USART_Parity_No; //无奇偶校验位
USART1_InitStructure.USART_StopBits = USART_StopBits_1; //一个停止位
USART1_InitStructure.USART_WordLength = USART_WordLength_8b; //字长为8位数据格式
USART_Init(USART1, &USART1_InitStructure);
USART_Cmd(USART1, ENABLE); //使能USART1
NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
NVIC_Init(&NVIC_InitStruct);
// NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
USART_ClearFlag(USART1, USART_FLAG_TC);
}
void USART1_IRQHandler(void)
{
if (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == SET)
{
USART_ClearFlag(USART1, USART_FLAG_RXNE);
RxData = USART_ReceiveData(USART1); //接收到的数据存到 RxData
// USART_SendData(USART1, RxData);
// while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET) //等待数据发完
// ;
}
}
更多推荐

所有评论(0)