面向应用学习stm32(4)-EXTI外部中断
前导:本文的目的与,意在于面向应用的学习单片机,故不会涉及太多的原理知识,例如寄存器之类的。
主要目的在于面向应用的学习单片机,学会单片机的基础用法,开发板采取野火的指南者f103。
作者大二小白,写的不好的地方轻点喷,欢迎评论区交流
全部工程代码开源在Gitee仓库
文章目录
- 1 中断简介
- 1.1 为什么需要中断?
- 1.2 中断
- 1.3 EXTI外部中断事件
- 1.4 NVIC中断组
- 1.5 代码编写步骤
- 2 中断例子代码
- 2.1 按键中断1
- 2.1.1需求
- 2.1.2EXTI配置
- 2.1.3NVIC配置
- 2.1.4逻辑代码
- 2.2 按键中断2
- 2.2.1 需求
- 2.2.2EXTI配置
- 2.2.3NVIC配置
- 2.2.4逻辑代码
1 中断简介
1.1 为什么需要中断?
想象一个生活场景,在生活中,我是一个处理能力的主体,做饭,烧水,买菜,打游戏等等的,在没有任何提醒的下,我是只能做一件事情的。
例如我在打游戏的时候,我只能打游戏,做饭的时候,只能做饭。
显然这样的效率是十分低下的,但是在真实的生活中,是充斥着各自提示消息的,例如水烧开的时候,发出响声,我听到水烧开的声音,放下手头的工作,去把烧水壶拿开,然后接着回头干我自己的事情。
假设我不去听水烧开的声音,我只是看着水在那边开始冒热气,直到烧开,那这几分钟内我什么事情都干不了。
同样,洗衣机洗衣服也是同理,我不会看着衣服洗完,我只会等待洗衣机洗好后发出滴滴声提醒我。
生活的存在着各种信息,提醒我们暂时放下手头的工作,转而完成另外一件事情,再回到手头工作
而在计算机中,这种机制叫做中断处理机制
1.2 中断
中断机制生活例子 | 对应代码 |
---|---|
开始洗衣服时,我会注意洗衣机洗完的响声 | 写代码绑定一个中断事件,进行监听 |
当洗衣机的响声和烧水壶的响声同时响起时,我会先去拿开烧水壶 | 配置中断优先组,当两个中断同时发生的时候,优先处理优先级高的事情 |
听到响声 | 配置中断触发条件 |
去拿走烧水壶 | 将操作写进中断服务函数 |
处理完事件后,回到手头上正在做的事情 | 清除标志位 |
中断整体工作流程如下
注意,跳转到中断服务程序的时候,我需要问你一个问题,怎么跳转?程序怎么知道,我要跳转到哪里。
这里要提及一个概念,函数在程序中,是有自己的地址的。程序是依靠着地址,跳转到函数,执行函数里的操作的。
中断以及对应函数是由stm32提供的,自然,官方也就给我们准备好了各种中断函数。
在**startup.s里有一系列的中断函数定义,也叫做异常向量组。**当某个中断发生的时候,就会跳转到对应的中断函数里,执行我们要做的操作。执行完再返回主函数。
这意味着,中断函数的名字我们是不要去改变的,我们要去这个文件里复制对应的中断服务函数,然后在里面写代码。
1.3 EXTI外部中断事件
External interrupt / event controller 外部中断/事件控制器
管理了一组中断/事件线。每个中断/事件线都对应有一个边沿检测器,可以实现输入信号的上升沿检测和下降沿的检测。
平时我们会使用 EXTI0至 EXTI15对应的就是GPIO的0-15号引脚的中断。
1.4 NVIC中断组
Nested vectoredinterrupt controller,即嵌套向量中断控制器。
嵌套的理解就是,多个中断接连发生,嵌套在一起的时候,该以什么顺序处理这些中断。
他具有五个分组,两个属性
分组部分:
记住整个程序只能配置一个分组,相当于是一个规则
这里的主和子,指的就是后面的抢占和优先的取值。
属性部分:
一个为抢占属性,另一个为响应属性,其属性编号越小,表明它的优先级别越高。
- 抢占,是指打断其他中断的属性,即因为具有这个属性会出现嵌套中断(在执行中断服务函数A 的过程中被中断B 打断,执行完中断服务函数B 再继续执行中断服务函数A),抢占属性由
NVIC_IRQChannelPreemptionPriority
的参数配置。 - 响应,则应用在抢占属性相同的情况下,当两个中断向量的抢占优先级相同时,如果两个中断同时到达, 则先处理响应优先级高的中断, 响应属性由
NVIC_IRQChannelSubPriority
参数配置。
若内核正在执行C 的中断服务函数,则它能被抢占优先级更高的中断A 打断。
由于B 和C 的抢占优先级相同,所以C 不能被B 打断。
但如果B 和C 中断是同时到达的,程序就会首先执行响应优先级别更高的B 中断
(高优先级的抢占优先级是可以打断正在进行的低抢占优先级中断的,而抢占优先级相同的中断,高优先级的响应优先级不可以打断低响应优先级的中断)。
优先级取值范围如下
1.5 代码编写步骤
说到编写步骤,不如先来看看,中断的整体执行流程(忽略我不好看的字)
根据这张图,我们可以写出,代码大致的配置流程如下
- 开启复用时钟(别问为什么哈哈)
- 配置指定端口的中断线
- 配置中断触发模式,中断触发的条件
- 使能中断线,并初始化EXTI
- 配置优先组,绑定中断源(也就是所要触发的中断线)
- 配置抢占优先级,响应优先级
- 使能并初始化
2 中断例子代码
2.1 按键中断1
2.1.1需求
- 我需要在按键按下,并且没有松手的时候,程序能够正常运行,松手的时候能够正常记录按键按下次数。
显然,之前的程序是有问题的。
当我们在按键扫描的函数里,增加判断松手的时候,也就是
#define KEY1 GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)
if(KEY1)
{Delay(2500000);while(KEY1);key = '1';
}
会导致一个bug,也就是,当我们按下,但不松手的时候,主函数就什么都执行不了因为他疯狂的扫描按键1的电平,而我们一直按着,就一直卡在while(1)里导致死循环。
就像是我最开始举的例子,我只能在水旁边看着它烧开。
所以我们现在就需要检查“听水烧开的声音” 这个信号。
也就是检查按键按下然后松手时触发的“信号”,配置中断了。
2.1.2EXTI配置
按照我们前面所说的流程进行配置。开启复用时钟后。
去stm32f10x_exti.h里找到对应的初始化结构体。
可以发现里面就有中断线,中断模式,中断触发器(触发条件),中断线使能四个参数,这些参数对应的定义在头文件里也有
得到这些信息后,我们开始配置,可以复制一份之前LCD的工程,在那上面进行改进。(在KEY_Init里配置)
//声明EXTI初始化结构体
EXTI_InitTypeDef EXTI_InitStructure;
//开启复用时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
//配置PA0的中断线
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource0);
//选择0号中断线,因为是PA0
EXTI_InitStructure.EXTI_Line=EXTI_Line0;
//配置模式为中断触发模式,一般只用这个。
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
//配置为下降沿触发,按键按下时是高电平,也就是上升沿,一松手就是下降沿了
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
//使能中断线
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
//初始化中断
EXTI_Init(&EXTI_InitStructure);
到这,EXTI的配置部分就已经完成了。
2.1.3NVIC配置
NVIC的相关内容存在misc.h里,我们还是照例跳进去看。发现一个这样的结构体
可以看到四个参数
- 1:中断源的通道,也就是我们的中断线0
- 2:抢占优先级
- 3:响应优先级
- 4:使能
代码编写,就像1.5里讲的一样,从组开始配置,我们查看misc.h的最下方,第一个函数。翻译过来就是
NVIC优先组配置。
并且在上方会发现它的取值有以下四个
代码编写如下(也可以在KEY_Init里配置)
//声明NVIC初始化结构体
NVIC_InitTypeDef NVIC_InitStructure;
//配置中断组1,我们就一个按键中断,这个组怎么配置其实都可以了
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
//配置中断源
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;//这个参数去stm32f10x.h里找
//配置抢占优先级
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
//配置响应优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
//使能
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
//初始化NVIC
NVIC_Init(&NVIC_InitStructure);
到这。NVIC的配置部分也完成了。然后,可以删掉我们的KEY_Scan函数了,因为用的不是扫描法
2.1.4逻辑代码
所有的配置部分写完之后,我们就准备开始编写业务逻辑代码。首先要转换一下思路之前我们是利用GPIO_ReadInputDataBit不断的扫描读取按键上的电平。
就像是我们看着水,水烧开了再进行操作。
现在因为我们刚刚配置了 下降沿的中断触发,也就是,每当我们按键按下时,松开时,产生的那个下降沿,就会被程序监听到。然后产生一个中断信号,然后跳转到中断函数中进行逻辑处理。
因为按键按下是高电平,产生上升沿,松手后变回低电平,下降沿产生。中断捕获到。跳转到中断函数进行处理。
问题来了,中断服务函数从何而来?在我1.2的中断讲解里有提到。可以回去观察。会发现里面有一个
EXTI0_IRQHandler。这就是EXTI0的中断服务函数的函数名了。
KEY也是连接在PA0上的,自然配置的也就是EXTI0的中断,那么复制这行函数,写到stm32f10x_it.c里面。
it是interrupt中断的意,约定俗成的我们中断服务函数都会写到stm32f10x_it.c里,当然要写其他地方也行。就像这样,这里面要放的就是我们产生中断后要进行的处理了。
接下来,我们可以写我们的逻辑了,思考一下,在中断函数里需要什么?
既然已经跳转到中断函数里了,那么证明这个中断被触发了,也就是,按键产生下降沿了,意味着我们已经松手了,松手后需要的就是。 按键次数++
我们现在这里 extern 一个主函数的按键次数变量。
c语言中,其他的c文件要使用不属于自己的.c文件时,需要使用extern,具体规则可以查一下csdn。
extern int key1_count;
void EXTI0_IRQHandler(void)
{Delay(2500000);//同样也需要消抖,否则还是有可能多次触发key1_count++;
}
可,这样可以了吗?
答案是否定的,计算机不像人那么聪明,听到水烧开,就去拿开水壶,拿开水壶后就回去接着干自己的事情。
计算机会进入这个中断,是因为产生中断信号后,某个标志位变为1了,标志为1的时候就会跳转到中断函数中。这段代码里,我们并没有把标志位清0,也就是说,在我们一次松手后,这个中断还是会不断的触发。因为进入这个中断的标志位一直为1。
解决方案也很简单,清除标志位就好,stm32官方肯定也考虑到了。在stm32f10x_exti.h里有这样一行函数
void EXTI_ClearITPendingBit(uint32_t EXTI_Line);
很明显,参数为中断线,效果为清除中断线的标志,那么最终代码改造如下
extern int key1_count;
void EXTI0_IRQHandler(void)
{Delay(2500000);//同样也需要消抖,否则还是有可能多次触发key1_count++;EXTI_ClearITPendingBit(EXTI_Line0); //产生中断后清除标志位
}
此时,我们加入流水灯和液晶屏进行测试。显示方面就不说了,上一章提到过了。
#include "stm32f10x.h"
#include "./lcd/bsp_ili9341_lcd.h"
#include "led.h"
#include "key.h"
#include <stdio.h>char disp[20];
int key1_count = 0;void LCD_Show();int main (void)
{KEY_Init();//LED初始化LED_Init();//按键初始化ILI9341_Init();//液晶初始化 LCD_SetColors(BLACK,WHITE);//设置白底黑字LCD_SetFont(&Font8x16);//设置字体大小ILI9341_Clear(0,0,LCD_X_LENGTH,LCD_Y_LENGTH);//清屏ILI9341_GramScan ( 6 );//设置显示模式LED_Color(LED_OFF);//关灯while (1){LED_Flashing(LED_RED);LCD_Show();}
}void LCD_Show()
{sprintf(disp,"KEY1_Count:%3d",key1_count);ILI9341_DispStringLine_EN(LINE(4),disp);
}
这时我们就会发现了,当按键按下,并且不松手的时候,流水灯不会卡死。而松手的时候,次数将会被记录并显示。
2.2 按键中断2
2.2.1 需求
配置KEY2的中断触发。
首先我们会想到,KEY2连接的是PC13,就会去寻找Exti_Line13,幸运的,我们找到了。
2.2.2EXTI配置
GPIO_EXTILineConfig(GPIO_PortSourceGPIOC,GPIO_PinSource13);EXTI_InitStructure.EXTI_Line=EXTI_Line13;EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;EXTI_InitStructure.EXTI_LineCmd = ENABLE;EXTI_Init(&EXTI_InitStructure);
2.2.3NVIC配置
和之前的一样,我们去寻找EXTI13_IRQn中断源。运气不好的是,我们发现里面并没有EXTI13_IRQn!!!,可以理解为因为芯片并没有办法为我们提供那么多的线路去连接。
但是我们在stm32f10x.h里会找到这个家伙,EXTI15_10IRQn,好家伙,10-15的中断源被定义在这一个里面了
那么我们如下改造
NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure);
2.2.4逻辑代码
又是和上次一样的坑,并没有13号中断线的中断服务函数。取而代之的是EXTI15_10_IRQHandler
所以这次我们在stm32f10x_it.c里就用这个函数写,写出如下代码
extern int key2_count;
void EXTI15_10_IRQHandler(void)
{Delay(2500000);key2_count++;EXTI_ClearITPendingBit(EXTI_Line13);
}
接下来,在LCD测试的地方增加入key2按键次数的显示就可以了
void LCD_Show()
{sprintf(disp,"KEY1_Count:%3d",key1_count);ILI9341_DispStringLine_EN(LINE(2),disp);sprintf(disp,"KEY2_Count:%3d",key2_count); //增加按键2ILI9341_DispStringLine_EN(LINE(4),disp);
}
面向应用学习stm32(4)-EXTI外部中断相关推荐
- STM32(五)——EXTI外部中断
文章笔记源于--江科大自化协的视频 一. 中断系统 中断 : 在主程序运行过程中,出现特定的中断触发条件,使得CPU暂停当前正在运行的程序,而去处理中断程序,完成后,又返回原来被暂停的位置继续工作 中 ...
- STM32笔记——EXTI外部中断
目录 一.概述 二.主要外设介绍 2.1 AFIO复用IO口 2.2 EXTI简介 EXTI框图: 2.3 NVIC中断优先级分组 三.EXTI外部中断整体结构 四.实验程序 4.1 对射式红外传感器 ...
- STM32学习笔记(三)丨中断系统丨EXTI外部中断(对射式红外传感器计次、旋转编码器计次)
本篇文章包含的内容 一.中断系统 1.1 中断的定义 1.2 中断优先级 1.3 中断的嵌套 1.4 STM32中的中断系统 1.4.1 STM32的中断资源 1.4.2 嵌套中断向量控制器 NVIC ...
- STM32中断—EXTI外部中断
5. 中断 本文来自于<STM32--江科大>的笔记整理. 中断系统 中断:在主程序运行过程中,出现了特定的中断触发条件(中断源),使得CPU暂停当前正在运行的程序,转而去处理中断程序,处 ...
- STM32 EXTI外部中断/事件
一.EXTI外部中断/事件功能框图 1.中断线路:①-②-③-④-⑤ ①输入线:有19个中断/事件输入线(联网型有20个),通过寄存器将这些输入线设置为任意一个GPIO. ②边沿检测电路:选择上升沿触 ...
- 【MM32F5270开发板试用】GPIO输入+EXTI外部中断例程demo试用
本篇文章来自极术社区与灵动组织的MM32F5270开发板评测活动,更多开发板试用活动请关注极术社区网站.作者:Zeee 前言: 首先,感谢灵动微电子与极术社区给予宝贵的试用机会.借助本次对Plus-F ...
- 28.EXTI外部中断原理与配置
EXTI外部中断原理与配置 参考资料 开发板配套资料 <STM32Fx开发指南-HAL库版本>-第x章 外部中断实验 STM32Fxxx官方资料: <STM32Fxx中文参考手册&g ...
- 02、【江科大自化协stm32F103c8t6】笔记之【入门32单片机及EXTI外部中断初始化参数配置】
----------接着上一篇笔记写 四.返回值函数 五.EXTI外部中断 1.配置RCC 2.配置GPIO 3.配置AFIO 4.配置EXTI 5.配置NVIC 中断函数 四.返回值函数 uint8 ...
- rt-thread 学习(二)外部中断 添加头文件 延时函数
rt-thread 学习(二)外部中断 添加头文件 延时函数 设置按键外部中断 开启硬件定时器 初始化配置部分 延时函数 添加头文件 设置按键外部中断 #include <rtthread.h& ...
最新文章
- 超参数搜索——网格搜索和随机搜索
- Glide-源码分析(一)
- android学习日记13--数据存储之ContentProvide
- 联发科技嵌入式_MTK深圳公司嵌入式软件工程师笔试题(含部分答案)
- 计算机网络第七版谢西仁答案
- 电力行业适合学习的开源软件
- filebeat-logstash-es综合运用
- 智能和弦生成工具-Plugin Boutique Scaler 2 v2.3.1 WiN-MAC
- 2021年茶艺师(初级)考试资料及茶艺师(初级)模拟试题
- docker中的localhost
- python去除字符串中的单词_从字符串中删除特定单词的最有效方法
- 为什么需要软件开发报告
- mysql分区表去重复_MySQL分区表管理
- 编写优质嵌入式C程序(转)
- 百度云其实真的很不错
- 写一个关机代码让别人的电脑根据你的指令关机,c语言写小程序恶搞别人
- C#基于Twain协议调用扫描仪,设置多图像输出模式(Multi image output)
- Linux 4.15 rc7,深度操作系统 15.4 RC更新详情
- Flink的State详解(1)
- Cubox是什么应用?如何将Cubox同步至Notion、语雀、在线文档中
热门文章
- 梦幻西游维护测试服务器仅开放转入,梦幻西游1月28日维护内容一览
- datepicker 季度
- 一款相见恨晚SCI论文写作神器-phrasebank-润色英文表达
- 用Adobe AIR支持多样设备多倍屏幕尺寸
- 2021年塔式起重机司机最新解析及塔式起重机司机新版试题
- php电影播放系统在线视频点播系统 php毕业设计题目课题选题 php毕业设计项目作品源码(4)系统后台功能和界面
- lib和dill区别
- Z变换(Z-transform)
- C++:atan2(y, x)函数用法详解
- 重庆成都汽车车身贴膜隐形车衣为什么要找膜手世家