云计算百科
云计算领域专业知识百科平台

stm32 -- 中断


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 默认配置
分组编号PRIGROUP 值抢占优先级位数子优先级位数抢占优先级范围子优先级范围
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(下降沿)触发
赞(0)
未经允许不得转载:网硕互联帮助中心 » stm32 -- 中断
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!