tags:
- “#中断”
- “#stm32”
- “#中断优先级”
- “#外部中断”
- “#NVIC”
- “#串口”
- “#舵机”
- “#按键”
date: 2026-08-04
中断的基础概念
1)单片机中断编程:
常规程序 – 突发事件 – 处理突发事件 – 继续常规程序
其中:突发事件称为中断源
处理突发事件的代码叫做中断响应函数;
2)突破了按部就班的做事儿方式,能够更快的响应突发事件
3)回顾之前的闪灯实验和串口接收实验[[LED闪灯实验]] [[简单数据接收实验]]
4)串口接收数据的时候,存在两级缓冲区,一级缓冲和二级缓冲,如果发送过来的数据,不及时处理就会丢失
5)思路:
main()
{
while(1)
{
//亮
//延迟
//灭
//延迟
}
}
void 中断响应函数
{
// 读取数据
//处理数据
}
- 闪灯程序正常进行,当接收到串口的数据时,调用中断响应函数对数据进行处理,处理过后继续闪灯程序。
- 当中断发生时,硬件会自动调用中断响应函数
中断优先级
- 中断优先级分组:
- 0~15,从高到低
- 抢占优先级,子优先级。![[5077253bbfe0ed20cc27d9cf3d991807.jpg]]
- 中断优先级就是中断的紧急程度,根据程序设计的需求,自己设置的
- 抢占优先级高的可以中断嵌套,响应优先级高的可以优先排队
- 中断排队:
- 规则:
- 优先级越高,排队越靠前
- 优先级相同时,按照中断号排队。
- 规则:
- 中断嵌套
- 在程序执行过程中,如果发生中断,单片机会调用对应的中断服务函数。若在该函数执行期间又产生了新的紧急中断,且该中断无法等待排队,系统便会打断当前中断服务函数的执行,转而处理新的中断。这种在一个中断内部嵌套另一个中断的现象,称为中断嵌套。
- 发生中断嵌套的条件:新中断的抢占优先级更高。
- 当中断发生的时候,如果来了另外的中断信号,当新来的中断的抢占优先级更高(数字更小)的时候,就发生中断嵌套,如果新来的中断抢占优先级更低,那就要排队
- 分组方式
- NVIC 通过 AIRCR 寄存器的 PRIGROUP 位,把 4 位优先级拆分为抢占优先级 + 子优先级
- 常用分组为 分组 2(2 位抢占 + 2 位子),如 CubeMX 默认配置
| 0 | 011 | 0 | 4 | 0 | 0~15 |
| 1 | 100 | 1 | 3 | 0~1 | 0~7 |
| 2 | 101 | 2 | 2 | 0~3 | 0~3 |
| 3 | 110 | 3 | 1 | 0~7 | 0~1 |
| 4 | 111 | 4 | 0 | 0~15 | 0 |
说明:
- 抢占优先级 = 数字越小优先级越高,可发生中断嵌套
- 子优先级 = 同抢占优先级下排队顺序,不能打断嵌套
- 分组 4 时没有子优先级,响应优先级全为 0,依靠中断号排队
EXTI外部中断
1)Extern Interrupt
- 监测指定GPIO口的电平信号,当其指定的GPIO口产生电平变化时,EXTI将立刻向NVIC发送中断申请,经过NVIC裁决后,即可中断CPU主程序,使CPU执行EXTI对应的中断程序
- 支持的触发方式:
- 上升沿
- 下降沿
- 双边沿
- 软件触发
- 支持所有IO口,但是相同的Pin不能同时触发中断
- 通道数:16个GPIO_Pin,外加PVD输出,RTC闹钟,USB唤醒,以太网唤醒
- 触发响应方式:中断响应/事件响应
- 事件响应:当外部中断检测到引脚电平变化的时候,触发事件,外部中断的信号就不会通向CPU,而是通向其他外设,用来触发其他外设的操作,比如触发ADC转换,触发DMA等。
2)AFIO复用IO口
- AFIO主要用于引脚复用功能的选择和重定义
- 在stm32中,AFIO主要完成两个任务:复用功能引脚重映射,中断引脚选择
NVIC模块
1)NVIC,是单片机内部的一个模块,全称是:Nested Vectored Interrupt Controller –嵌套向量中断控制器,负责管理单片机的中断。
2)![[00f9637b298ed228563fb94994b80f6e.jpg]]
-
中断使能,对应中断后面打上勾就相当于使能了这个中断。2)图中的四个方框表示中断优先级。蓝色线可以移动,控制抢占优先级和子优先级。
-
![[Pasted image 20260724152848.png]]
-
优先级分组
![[Pasted image 20260724153433.png]] -
仲裁电路用来管理中断嵌套和中断排队的逻辑。
-
中断响应函数存储在单片机的flash里面
串口中断实验
1)实验目的:
通过串口调试助手控制板载LED的闪烁速度,发送1是慢闪,2是正常,3是快闪
2)复习一下闪灯实验和UART串口实验:
[[LED闪灯实验]] [[简单数据接收实验]][[简单数据发送实验]]
1.板载LED是与PC13引脚连接,采用开漏接法。向pc13引脚写1是熄灭,0是点亮
2.UART分别有一个发送引脚TX,一个接收引脚RX,对于UART1,PA9引脚是TX引脚,PA10引脚是RX引脚,连接的时候,USBtoTTL模块的RX和PA9相连,TX和PA10相连,GND引脚接地。
3.在发送实验中,用到了HAL_UART_Transmit的编程接口。
3)第一步先实现LED的闪烁
1.通过闪灯实验我们知道,我们通过HAL_GPIO_WritePin,向PC13引脚写1或者0,同时通过HAL_Delay,延迟函数来控制LED的闪灯频率。
2,
![[Pasted image 20260725112319.png]]
一般把全局变量的声明写在Private variables下面,一对begin和end中间。
3.在Private variables位置声明一个变量:blinkinterval,我们可以通过改变这个值,来改变延迟的大小从而控制闪灯的频率。
/* USER CODE BEGIN PV */
static uint32_t blinkinterval = 1000;
/* USER CODE END PV */
int main(){
while (1)
{
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,
GPIO_PIN_RESET);
HAL_Delay(blinkinterval);
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,
GPIO_PIN_SET);
HAL_Delay(blinkinterval);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
}
首先我们点击魔术棒选项,选择c/c++,把代码优化关掉,等会要调试。
然后点击调试按钮。
在view的watch windows选项,选择watch1,在下方窗口输入我们想监控的变量,这里是blinkinterval,然后右键将十六进制关掉。
![[Pasted image 20260725114309.png]]
第一个就是十六进制的选项
![[Pasted image 20260725114327.png]]
通过改变value的值,可以看到LED闪烁的频率的变化。
4)设置UART。
- 选择异步模式,其他参数保持默认,比如波特率。
- 重点在于NVIC SETTING,是和过去的实验不同的地方。
- ![[Pasted image 20260725115300.png]]
- 串口1全局中断,打勾enable
5)中断的使用方法 – 新的编程接口。
HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart,
uint8_t *pData,
uint16_t Size)
作用:通过串口以中断方式接收指定数量的数据(非阻塞);
参数 huart:填写串口句柄的指针;
参数 pData:指向接收缓冲区的指针;
参数 Size :填写要接收数据的数量,以字节为单位;
返回值:返回操作结果(HAL_OK 表示启动成功,其他返回值表示失败)。
- 与 HAL_UART_Receive(阻塞式)不同,HAL_UART_Receive_IT 不会等待数据接收完成,而是注册中断后立即返回,CPU 可以继续执行其他代码。
- 当数据接收完成后,硬件触发中断,HAL 库会自动调用回调函数 HAL_UART_RxCpltCallback,需要在用户代码中实现该回调函数,在里面处理接收完成后的逻辑。
/* 在 while 循环前调用,启动中断接收 */
HAL_UART_Receive_IT(&huart1, &datarcvd, 1);
/* 在用户代码区重写回调函数 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
/* 判断是哪个串口触发的中断 */
if (huart == &huart1)
{
/* 在这里处理接收到的数据 */
if (datarcvd == 0)
{
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET);
}
else if (datarcvd == 1)
{
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);
}
/* 重新开启中断接收,准备接收下一个数据 */
HAL_UART_Receive_IT(&huart1, &datarcvd, 1);
}
}
-
关键点:中断接收只生效一次,每完成一次接收后,如果想继续接收后续数据,必须在回调函数中再次调用 HAL_UART_Receive_IT 重新开启中断。
-
右边文件夹处,有个,drivers/stm32,打开之后点击uart.c文件,在文件的2622行
-
![[Pasted image 20260725121145.png]]
-
ctrl+G可以检索到这个位置。
- weak弱方法可以覆盖,这是我们要写的回调函数的函数原型,我们写的函数名应和这个函数名完全一致。
- 回调函数程序。
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart == &huart1) // 固定的写法,判断传进来的句柄是不是串口1
{
if (datarcvd == '1')
{
blinkinterval = 1000;
}
else if (datarcvd == '2')
{
blinkinterval = 300;
}
else if (datarcvd == '3')
{
blinkinterval = 50;
}
HAL_UART_Receive_IT(&huart1, &datarcvd, 1);
}
}
总结:
1)中断,就是让cpu停止当前程序,先去处理中断响应函数,处理完响应函数之后,继续先前的程序。
2)HAL_UART_Receive_IT,这个编程接口的作用:通过串口接收一个数据的同时,触发中断,调用回调函数。中断接收触发之后,相当于派一个员工守在门口等着,当有数据来的时候,他就接收数据,触发中断,调用回调函数, 他在等待的时候,不影响cpu主程序的正常执行。
3)中断接收函数只生效一次,若想完成接收后继续接收,就需要在回调函数内,再调用一次中断接收函数,再派一个员工蹲守。
- 当GPIO引脚检测到电平变化时,会自动调用中断响应函数
- 沿的方向由引脚电平的跳变方向决定,与按下/抬起无关:
- 上升沿 Rising:电平从低→高(按键接GND+上拉时,松开=低→高=上升沿)
- 下降沿 Falling:电平从高→低(按键接GND+上拉时,按下=高→低=下降沿)
- 双边沿:上升沿和下降沿都会触发
- 本项目按键接 GND + 上拉,按下为低电平,故配置 Falling edge(下降沿)触发
网硕互联帮助中心


评论前必须登录!
注册