前言: 关于中断简单介绍
中断的流程:
中断流程图:
中断方式传送数据具有可以有效提高单片机工作效率, 适合于实时控制系统等优点, 相对于查询方式更为常用。
当CPU处理某件事情的时候, 外部发生的某一事件(如电平的改变、脉冲边沿跳变、定时器/计数器溢出等)请求CPU迅速处理, 于是CPU暂时中断当前的工作, 转去处理发生的事件。 处理完该事件后, 再回到原来中断处, 继续工作。 这样的过程称为中断 上图为中断流程图
这个中断的概念是不是有点晦涩难懂? 主要是这个是书上的内容, 所以不是很形象. 我再解释一遍:想象一个场景,
1、当有一天你正在和川建国同志吃饭(你和他吃饭这个事情就是主程序);
2、突然有个电话打给你, 说有个十五亿的小单子需要你去处理一下(这件事就是中断源),
3、 你停止吃饭, 川建国同志在这等你, 你去处理你的十五亿小合同(就是响应中断请求, 签合同过程就是中断服务程序),
4、处理完合同之后你又回到餐桌继续和他就餐(这就是返回主程序, 然后继续执行主程序).
这样解释是不是清晰一点.
关于STM32F407的中断介绍可以看一下原子的或者火哥的pdf, 如果实在看得下去, 看官方手册也行。 这里就不赘述, 通过Cube配置以及编程过程理解这个外部中断会好很多, 通过现象再回去看本质
同样的, 还使用前面两篇博客所用到的工程即可, 也可以自己新建一个, 当做对自己的测试
2-1. 使用核心板自带按键
操作简介:通过板子上的两个按钮控制LED灯的亮灭 WK_UP按键按下则进入中断, 并翻转LED0的状态, KEY0按下时翻转LED1的状态. 两者虽然功能一样, 但却有质的区别
这里要做的和按键那一篇一样, 只是把其中一个按键改为中断, 而不是作为GPIO_input 所以可以看完上一篇直接看这一篇继续。 点击下方蓝字可以看上一篇的博客
第一节补充: 按键操作(CubeMX加HAL库学STM32系列)
Step1 RCC&SYS配置这些都不用动, 时钟树的配置也不用动 (1) RCC&SYS以及时钟树配置不用改变 (2)更改一下PA0引脚配置: 把WKUP按键对应的PA0引脚模式由GPIO_input改为GPIO_EXIT0, 再把GPIO的配置更改一下即可 具体操作见下图 注 : 如果是用的原来的工程, 只改这个即可, 其他的LED引脚和按键引脚不用动, 如果是自己又新建了一个工程, 那其他引脚按照前面两篇的介绍配置, 然后这个PA0按照这一篇配置就好了, 问题不大 对应GPIO配置改为下图 (3)中断NVIC配置 我们设置了中断, 在NVIC里面要记得使能PA0引脚的中断 NVIC ( Nested Vectored Interrupt Controller ) : 中断向量控制器 在中断向量表里面使能EXIT line0中断 关于抢占优先级和子优先级: 当你使用多个中断的时候会用到这个。 就是为了防止多个中断冲突, 所以需要给他们每个中断排个号, 就不会乱了。 抢占优先级高的先执行, 若抢占优先级相同, 再看子优先级 (4)以上配置完之后就可以Generate CODE Step2 <程序编写> (1) 中断服务函数 stm32f4xx_it.c 这个文件里面看到我们要用的中断服务函数 我们要在中断里面做什么事情, 就要写在中断服务函数里面, 然后中断到来之后, 单片机就回去处理中断服务函数里面的工作 这个函数里面调用了 HAL_GPIO_EXTI_IRQHandler() 这个函数, 这个函数是处理GPIO外部中断的函数 可以看到里面的参数是GPIO_PIN_0, 因为我们用的是PA0即GPIOA的0引脚 Go to definition 一下, 可以看这个函数的定义 /** * @brief This function handles EXTI interrupt request. // 这个功能是处理外部中断请求 * @param GPIO_Pin Specifies the pins connected EXTI line // GPIO_Pin指定连接EXTI线的引脚 * @retval None // 无返回值 */ void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin) { /* EXTI line interrupt detected */ if(__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != RESET) { __HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin); // 清除这个引脚的中断标识位 HAL_GPIO_EXTI_Callback(GPIO_Pin); // 回调外部中断 } } 综上: 中断服务函数最终会执行中断回调函数 HAL_GPIO_EXTI_Callback() (2) 中断回调函数 中断回调函数如下图 这个函数是空的, 所以我们可以自己重构这个函数, 在它内部实现我们要做的功能 我们需要重构中断回调函数 在main.c里面写入我们的代码 : 提示 : 不要忘了把代码写在 /* USER CODE BEGIN / / USER CODE END */ 之间 /* USER CODE BEGIN 0 */ // 重构中断回调函数 void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { // 判断是否为WKUP引脚(即GPIO_PIN_0)进入中断 if (WKUP_Pin == GPIO_Pin) { HAL_GPIO_TogglePin(LED0_GPIO_Port, LED0_Pin); // 翻转LED0的的电平状态 /* 下面这一句话与上面一句是等价的, 因为LED0是我们给这个引脚起的别名, 在main.h文件里面有对应的宏定义 */ //HAL_GPIO_TogglePin(GPIOF, GPIO_PIN_10); } } /* USER CODE END 0 */ (3) 主函数 在主函数里面用KEY0做一个一样的功能, 作为对比 /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ // 在while(1)里面循环扫描, 判断读取的按键引脚状态 // 下面扫描KEY0按键的引脚信号 if (HAL_GPIO_ReadPin(KEY0_GPIO_Port, KEY0_Pin) == GPIO_PIN_RESET) { HAL_Delay(10); // 延时10ms, 做一个软件的消抖, 防止因抖动而检测到按键按下 if (HAL_GPIO_ReadPin(KEY0_GPIO_Port, KEY0_Pin) == GPIO_PIN_RESET) { // 做一个松手检测, 若KEY0一直是RESET(低电平),则一直在死循环 // 当KEY0位SET才会跳出,进而继续执行下面的对 LED1 的操作 while(HAL_GPIO_ReadPin(KEY0_GPIO_Port, KEY0_Pin) == GPIO_PIN_RESET); HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin); } } } /* USER CODE END 3 */ (4) 编译下载到单片机, 看看单片机什么反应 左边的红色按键是WKUP, 左边的蓝色LED是LED0 仔细看一下动图中的效果可以发现, 中断的WKUP按键的功能并不是很完美, 这是因为没有消抖导致的, 在中断里面加个软件消抖的程序就可以了。 此外, 虽然两种方式实现的功能是一样的, 但是他们的区别就在于, KEY0翻转LED状态实在while(1)循环里面做的, 这就相当于主函数里面一直循环扫描这个按键的状态, 比较耗费资源. 而WKUP按键按下翻转LED是在中断里面做的,不影响主函数里面做其他事情. 如果以后做的东西要求写很多代码, 最好多多利用中断,这样会更高效。只有当事情来了CPU再去处理,其他时间主函数里面正常做其他事情。 这样既能提高MCU效率, 也不会让自己的代码全部写成一坨在主函数里面
上一篇:第一节:用Cube学32之简单IO口操作(点灯及按键)
下一篇:STM32CubeMX串口配置及库printf打印
推荐阅读最新更新时间:2024-11-10 11:14