stm32之中断模式

  • 一、中断简介
  • 二、外部中断配置
    • (一)外部中断简介
    • (二)配置外部中断
      • 1.配置外部中断
      • 2.配置中断优先级
      • 3.生成并查看代码
    • (三)编写中断函数
    • (四) 实例演示
  • 三、使用串口中断发送“Hello windows!”
    • (一)配置工程
    • (二)编写用户函数
    • (三)实验效果
  • 四、使用串口接收数据并回显
  • 总结

一、中断简介

当CPU收到中断或者异常时,它会暂停执行当前的程序或任务,通过一定的机制跳转到负责处理这个信号的相关处理程序中,在完成对这个信号的处理后再跳转回刚才被打断的程序或任务中继续执行,其具体的处理过程如下。

二、外部中断配置

(一)外部中断简介

编号 1 是输入线, EXTI 控制器有 19 个中断/事件输入线,这些输入线可以通过寄存器设置为任意一个 GPIO,也可以是一些外设的事件,这部分内容我们将在后面专门讲解。输入线一般是存在电平变化的信号。

编号 2 是一个边沿检测电路,它会根据上升沿触发选择寄存器 (EXTI_RTSR) 和下降沿触发选择寄存器 (EXTI_FTSR)对应位的设置来控制信号触发。边沿检测电路以输入线作为信号输入端,如果检测到有边沿跳变就输出有效信号 1 给编号 3 电路,否则输出无效信号0。而 EXTI_RTSR 和EXTI_FTSR 两个寄存器可以控制器需要检测哪些类型的电平跳变过程,可以是只有上升沿触发、只有下降沿触发或者上升沿和下降沿都触发。

编号 3 电路实际就是一个或门电路,它一个输入来自编号 2 电路,另外一个输入来自软件中断事件寄存器 (EXTI_SWIER)。
EXTI_SWIER 允许我们通过程序控制就可以启动中断/事件线,这在某些地方非常有用。我们知道或门的作用就是有 1 就为 1,所以这两个输入随便一个有有效信号 1就可以输出 1 给编号 4 电路。

编号 4 电是一个与门电路,它一个输入是编号 3 电路,另外一个输入来自中断屏蔽寄存器(EXTI_IMR)。与门电路要求输入都为 1 才输出1,导致的结果是如果 EXTI_IMR 设置为 0 时,那不管编号 3 电路的输出信号是 1 还是 0,最终编号 4 电路输出的信号都为
0;如果 EXTI_IMR设置为 1 时,最终编号 4 电路输出的信号才由编号 3 电路的输出信号决定,这样我们可以简单的控制 EXTI_IMR 来实现是否产生中断的目的。编号 4 电路输出的信号会被保存到挂起寄存器(EXTI_PR) 内,如果确定编号 4 电路输出为 1 就会把 EXTI_PR 对应位置 1。

编号 5 是将 EXTI_PR 寄存器内容输出到 NVIC 内,从而实现系统中断事件控制。

EXTI(External interrupt/event controller)—外部中断/事件控制器,管理了控制器的 20 个中断/事件线。每个中断/事件线都对应有一个边沿检测器,可以实现输入信号的上升沿检测和下降沿的检测。 EXTI可以实现对每个中断/事件线进行单独配置,可以单独配置为中断或者事件,以及触发事件的属性。

中断优先级分组

中断允许嵌套,不同的中断有不同的优先级,高优先级的中断可以打断低优先级的中断。
中断分组分为抢占优先级和子优先级,在中断发生时,同抢断优先级的互不打断。同抢占优先级且同时发生的中断,才看子优先级。

(二)配置外部中断

我们本例是使用外部中断控制LED灯的变化,首先我们要开启外部时钟,初始化所要用到的LED端口,我在上一篇博客已经介绍过了,在这里就不赘述了。
具体可参考

STM32CubeMX环境搭建及使用

配置好所要用到的LED灯后,我们就开始开启外部中断了,因为STMF103c8t6最小系统板并没有按键,所以我们这里用杜邦线代替。使用杜邦线从高电平到低电平模拟开关按下,低电平到高电平模拟开关弹起。

1.配置外部中断

因为这里有三个LED灯,所以我初始化三个GPIO端口模拟按键。

  • 选择PA3,选择GPIO_EXTI3模式

继续选择PA6,PB13为GPIO_EXTI(这里按个人喜好选择端口)

  • 在Pinout&Configuration–System core–GPIO中选择PA3,配置为如下模式


然后经PA6,PB13也配置为下降沿触发,上拉输入

2.配置中断优先级

  • 在Pinout&Configuration–System core–NVIC中使能外部中断,并配置优先级

3.生成并查看代码

void MX_GPIO_Init(void)
{GPIO_InitTypeDef GPIO_InitStruct = {0};/* GPIO Ports Clock Enable */__HAL_RCC_GPIOC_CLK_ENABLE();__HAL_RCC_GPIOD_CLK_ENABLE();__HAL_RCC_GPIOA_CLK_ENABLE();__HAL_RCC_GPIOB_CLK_ENABLE();/*Configure GPIO pin Output Level */HAL_GPIO_WritePin(LED_Y_GPIO_Port, LED_Y_Pin, GPIO_PIN_RESET);/*Configure GPIO pin Output Level */HAL_GPIO_WritePin(LED_G_GPIO_Port, LED_G_Pin, GPIO_PIN_RESET);/*Configure GPIO pin Output Level */HAL_GPIO_WritePin(LED_R_GPIO_Port, LED_R_Pin, GPIO_PIN_RESET);/*Configure GPIO pin : PtPin */GPIO_InitStruct.Pin = LED_Y_Pin;GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;HAL_GPIO_Init(LED_Y_GPIO_Port, &GPIO_InitStruct);/*Configure GPIO pins : PAPin PAPin */GPIO_InitStruct.Pin = KEY1_Pin|KEY2_Pin;GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;GPIO_InitStruct.Pull = GPIO_PULLUP;HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);/*Configure GPIO pin : PtPin */GPIO_InitStruct.Pin = LED_G_Pin;GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;HAL_GPIO_Init(LED_G_GPIO_Port, &GPIO_InitStruct);/*Configure GPIO pin : PtPin */GPIO_InitStruct.Pin = KEY3_Pin;GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;GPIO_InitStruct.Pull = GPIO_PULLUP;HAL_GPIO_Init(KEY3_GPIO_Port, &GPIO_InitStruct);/*Configure GPIO pin : PtPin */GPIO_InitStruct.Pin = LED_R_Pin;GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;HAL_GPIO_Init(LED_R_GPIO_Port, &GPIO_InitStruct);/* EXTI interrupt init*/HAL_NVIC_SetPriority(EXTI3_IRQn, 0, 0);HAL_NVIC_EnableIRQ(EXTI3_IRQn);HAL_NVIC_SetPriority(EXTI9_5_IRQn, 1, 0);HAL_NVIC_EnableIRQ(EXTI9_5_IRQn);HAL_NVIC_SetPriority(EXTI15_10_IRQn, 2, 0);HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);}

(三)编写中断函数

我们在stm32f1xx_it.c文件中,可以找到配置好的中断处理函数
当中断来临时,进入相应的中断处理函数

可以看出,这几个外部中断都使用了同一个处理函数,HAL_GPIO_EXTI_IRQHandler,通过传入不同的参数,来区分是哪一条中断线触发的中断。不同的外部中断都调用了同一个HAL库的处理函数:HAL_GPIO_EXTI_IRQHandler。

我们打开HAL_GPIO_EXTI_IRQHandler函数看看

void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
{/* EXTI line interrupt detected */if (__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != 0x00u){__HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);HAL_GPIO_EXTI_Callback(GPIO_Pin);}
}

可以看到HAL_GPIO_EXTI_IRQHandler函数里面又调用了回调函数HAL_GPIO_EXTI_Callback(GPIO_Pin)

我们再打开HAL_GPIO_EXTI_Callback(GPIO_Pin)看看

/*** @brief  EXTI line detection callbacks.* @param  GPIO_Pin: Specifies the pins connected EXTI line* @retval None*/
__weak void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{/* Prevent unused argument(s) compilation warning */UNUSED(GPIO_Pin);/* NOTE: This function Should not be modified, when the callback is needed,the HAL_GPIO_EXTI_Callback could be implemented in the user file*/
}

关于这几个函数的逻辑关系如下

而在HAL_GPIO_EXTI_IRQHandler的处理函数中,又调用了一个名为HAL_GPIO_EXTI_Callback的回调函数。此回调函数是用户编写业务逻辑的函数。
 在计算机程序设计中,回调函数,或简称回调(Callback 即call then back
被主函数调用运算后会返回主函数),是指通过函数参数传递到其它代码的,某一块可执行代码的引用。这一设计允许了底层代码调用在高层定义的子程序。

我们注意到,在用户重定义的处理函数名称之前,有__Weak标记,这是为什么?在HAL库中采用了一种弱函数机制,在HAL_GPIO_EXTI_Callback函数前有个__weak标记,表明这是个弱函数。弱函数可以被用户定义的同名函数覆盖,也就是说,如果用户定义了一个函数名为HAL_GPIO_EXTI_Callback,系统就不再编译有weak标记的函数,所以,被weak标记的,都是备胎。

__weak在回调函数的时候经常用到。这样的好处是,系统默认定义了一个空的回调函数,保证编译器不会报错。同时,如果用户自己要定义用户回调函数,那么只需要重新定义即可,不需要考虑函数重复定义的问题

图片及参考来源(更多内容请访问)

HAL库教程4:外部中断

关于这部分不理解也没有关系,我们只需要知道我们要将中断处理函数放到一个名为void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)(用户重新定义一个)的函数中即可,这是我们唯一需要自己编写的服务函数

为了方便,我就在main文件中加入我写的中断服务函数


代码如下:

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{switch(GPIO_Pin){case GPIO_PIN_3: {HAL_GPIO_TogglePin(LED_R_GPIO_Port,LED_R_Pin);}break;case GPIO_PIN_6:{HAL_GPIO_TogglePin(LED_G_GPIO_Port,LED_G_Pin);}break;case GPIO_PIN_13:{HAL_GPIO_TogglePin(LED_Y_GPIO_Port,LED_Y_Pin);}break;default: break;}
}

此代码实现的是每一次按键被按下,LED电平翻转一次。

(四) 实例演示

本例由于使用c8t6系统板上没有多余的按键,所以我们使用杜邦线代替。我这里使用杜邦线将PA6引出,PA6每从高电平到低电平,LED灯翻转一下。

由于系统板可能有点接触不良,所以在实验过程中可能会出现灯莫名其妙的闪烁,这是正常情况。
由于其他“按键”情况都是一样的,所以我这里只展示了PA6,经测试,其他端口可以分别控制相应的LED灯。


三、使用串口中断发送“Hello windows!”

上例我们是在while循环里连续发送Hello windows!,但是这样程序执行效率低,本例我们使用串口的方式来发送数据,并且在while循环里做LED闪烁实验。

  • 串口中断请求

(一)配置工程

关于时钟和相关GPIO的配置我这里就跳过了,这里主要是配置USART1的中断。

  • 在Pinout&Configuration–Connectivity–Usart1下,选择模式为异步模式

  • 在该界面下选择NVIC Settings,勾选中断


在HAL库中,串口的接收中断、发送中断都需要开启这个中断。
然后经过基本的工程配置就可以生成代码了

具体可参考STM32CubeMX环境搭建及使用

(二)编写用户函数

我们使用一个数组来保存我们需要发送的数据

uint8_t temp[]="Hello windowns\n";

然后在main函数中调用HAL_UART_Transmit_IT(&huart1, temp, sizeof(temp))将数组中的内容发送出去,发送完成后,自动调用发送完成回调函数HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
我们在回调函数中发送再次发送相同的数据,在合适的条件下结束发送。

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{if(i<=100){HAL_UART_Transmit_IT(&huart1, temp, sizeof(temp));i++;   //每调用一次,i++,当调用超过100次时,不再发送数据}}


使能发送中断函数HAL_UART_Transmit_IT(&huart1, temp, sizeof(temp));

(三)实验效果

可以看到,上位机接收了1632个字节就不再接收了,同时LED也在闪烁。


此时LED灯闪烁

四、使用串口接收数据并回显

使用串口中断连续发送数据,程序一直频繁进入中断,效率不高,接下来我们使用串口中断接收来自上位机发送的数据,并将接收到的数据发送到上位机显示出来。
我们可以直接利用上面生成的工程,只需要修改一下代码即可。

  • 定义接收缓存区
 uint8_t receive[255];
  • 重新定义回调函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{if(huart->Instance == USART1){HAL_UART_Transmit(&huart1,receive,1,0xFFFF);HAL_UART_Receive_IT(&huart1,receive,1);      }}

  • 在主函数中开启接收中断

HAL_UART_Receive_IT(&huart1,receive,1);  //每接收一个字节就进入中断
  • 效果如下


此时LED灯仍在闪烁,如上例一样,这里就不演示了。

总结

如果使用cpu查询模式,程序会不断的执行查询,效率低;如果开启中断后,cpu就不用时时刻刻重复执行操作,而是可以腾出手去做其他的事;当中断来临(满足条件后),程序从当前执行的指令跳转到中断处,执行完毕后再继续做跳转前的工作,这极大的提升了效率。

关于在HAL库中大量使用回调函数(以函数名字作参数),一般是以-weak弱定义的,我们可根据实际需要重定义。使用回调函数可以让代码更加灵活、提高运行效率。

以上若有不当之处,敬请指教!!!

参考

HAL库教程4:外部中断

《STM32库开发指南–基于野火指南者开发板》

stm32之中断模式相关推荐

  1. 采用STM32外部中断模式控制LED灯亮灭

    文章目录 实验工具 一.STMCube配置项目 1.配置引脚 2.配置EXIT 3.配置SYS 4.配置GPIO 5.创建代码 二.使用KEil配置代码 1.打开生成的项目,找到stm32f1xx_i ...

  2. stm32外部中断模式控制灯亮灭

    文章目录 前言 一丶通过STMCube配置项目 1.引脚配置如图 2.配置EXIT 3.配置SYS 4.配置GPIO 5.代码创建出勾上这个 6.创建项目 二.通过KEil配置代码 1.打开生成的项目 ...

  3. 【STM32】5—UART串口(中断模式)

    目录 0 实验预期效果 1 相关原理图 2 软件配置 3 代码编写 3.1 函数认识 3.1.1 串口发送 3.1.2 串口接收 3.1.3 中断回调函数 3.2 代码编写 3.2.1 定义发送和接收 ...

  4. STM32低功耗停止模式 以及简述判断中断的两种方式

    #include "wkup.h" #include "led.h" #include "delay.h" // //本程序只供学习使用,未 ...

  5. 将STM32设置睡眠模式(使用中断唤醒)之小白学习笔记

    #include "bsp_exti.h" #include "Led_Key.h" #include "bsp_SysTick.h" in ...

  6. STM32输入捕获模式设置并用DMA接收数据

    参考: STM32的PWM输入模式设置并用DMA接收数据 Input capture mode The input stage samples the corresponding TIx input ...

  7. STM32外部中断与各通道对应关系

    EXTI0_IRQn的值,其实就是EXTI0中断向量在中断向量表中的位置(STM32技术参考手册中断向量表position栏中的数值) 一:外部中断使用配置过程: (1)配置时钟信号 (2)引脚属性 ...

  8. stm32外部中断_STM32学习笔记 | 电源管理及低功耗设计要点

    一款好的电子产品,都需要认真考虑电源管理的问题,电池供电的产品更应该注意低功耗的实现. STM32电源介绍 嵌入式开发直播课 - STM32 USART串口的应用 - 创客学院直播室​www.make ...

  9. (二)stm32之中断配置

    一.stm32的中断和异常 Cortex拥有强大的异常响应系统,它能够打断当前代码执行流程事件分为异常和中断,它们用一个表管理起来,编号为0~15为内核异常,16以上的为外部中断,这个表就是中断向量表 ...

最新文章

  1. ubuntu下安装 python 常用软件
  2. centos7中使用yum安装tomcat mysql 等
  3. 静态库符号文件冲突的解决办法,已实践OK, mark
  4. 默认子进程与父进程属于同一个进程组,所以注意对接受到的信号的处理方式
  5. php cms 的模板修改,phpcms v9后台登陆模板修改方法和程序版本更新提示修改方法...
  6. DataUml Design 教程6-DataUML Design 1.1版本号正式公布(支持PD数据模型)
  7. 关于App开发:模拟服务器数据接口 - MockApi
  8. 扫雷打开的初始区域递归_Python:游戏:写一个和 XP 上一模一样的“扫雷”!
  9. 用好CloudIDE提升Web全栈编码效率
  10. 092:QuerySet API详解-切片操作
  11. SAS学习笔记之《SAS编程与数据挖掘商业案例》(2)数据获取与数据集操作
  12. apqp过程流程图范本_过程流程图(APQP要求)
  13. php opendir(),php之opendir()函数的用法
  14. java group by_Java中如何实现对List内容分组(group by)?
  15. 蓝牙的原理,蓝牙耳机怎么连接手机
  16. windows 系统清理工具
  17. aip pytesseract识别图片中的文字
  18. 博客文章分类导引(持续更新)
  19. MVC、POJO、PO、DTO、TO、BO、VO、DAO、domian、delegate、sql
  20. 评估智能对话机器人的7大数据指标

热门文章

  1. 第五十四讲 设备树实现RGB驱动
  2. GiliSoft Image Watermark Master(图片去水印软件)官方中文版V8.0.0 | 图片去水印软件哪个好用?
  3. 一个女孩主动加你微信,不说话也不打招呼是因为啥呢?
  4. 20岁不考证,25岁不加班,30岁不熬夜,40岁你还有工作吗?
  5. (剖面图全网唯一教程)如何利用EDEM制作剖面图 (自己琢磨出)
  6. 旅游规划pta(无向图求顶点间最短路径)
  7. linux最新内核5.80版本移植详细过程
  8. 选购ERP软件时需避免的五大错误
  9. 构建关系抽取的动词源
  10. oppo手机如何找android,OPPO手机找回功能怎么用 OPPO R9丢失怎么找回