本文为野火教程学习笔记。
EXTI原理解释
EXTI 简介
EXTI (External interrupt/event controller) ——外部中断/事件控制器,其用于管理外设中GPIO和一些其他外设的中断。可以实现单独对某一线配置中断触发方式等操作。
EXTI原理框图解释
边沿检测器检测外部的信号,上升沿和下降沿触发选择寄存器具体的配置会决定边沿检测电路检测何种边沿。
检测到对应边沿后,检测电路输出1,软件中断事件寄存器的配置决定是否产生中断或事件,相当于小开关。
若配置响应这个边沿,则小开关输出1。下面由请求挂起寄存器和事件屏蔽寄存器确定产生中断还是事件。这两个相当于选择开关。
如果为中断,最后来到中断屏蔽寄存器,这相当于大开关。
从原理图中我们可以看出,从输入线输入开始到NVIC向内核发出中断,中间要经过边沿检测电路,小开关,请求寄存器,大开关等寄存器。各个寄存器的对应名称已经标注在图片上。
输入线
得知 EXTI 中断的过程后,探讨输入线部分内容。
EXTI可以管理20根外部输入源,EXTI和对应外部输入源匹配后的整体叫做输入线。
外部的输入源与中断线EXTI的对应关系如下图
EXTI的前16根线的输入源只能是GPIO端口,单独一根线允许连接GPIO的固定一种端口;如:EXTI0 只能连接 0 端口,如:PA0,PB0… 不能连接别的端口,如不可连接PA1,PB1…
输入源+中断线=输入线
小结:外部中断理论部分
要使用 EXTI 外部中断。对于中断过程中,要配置框图中的各种寄存器以达到目的。对于输入线,要配置输入源和中断线以组成输入线。
编程实例
实例简介
上面介绍了EXTI的原理,我们现在编写一个按键控制LED灯的程序,其中让EXTI来检测按键按下的动作,然后产生中断,在中断函数中反转LED灯。
实验的电路图
从图中我们可以看出,按键默认是低电平。按下后为高电平。我们希望按键按下时有动作,所以我们要设置PA0上升沿触发,然后在中断函数中控制PB5反转。
开始编程(一)
首先我们在User文件夹下新建文件夹以存放C和H文件,并添加到USER下Group中,把H文件填入IncludePath.
初始化EXTI和GPIO
初始化EXTI
根据经验,固件库中一定有初始化的函数。打开stm32f10x_exti.h翻到最下面我们就可以看见这个头文件所有函数,果然有
void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);
一看就知道这是初始化函数。打开后发现只需要一个结构体的传入,F12这个结构体我们看到:
我们可以看见这个结构体提供了 :
EXTI_Line : 用来选定PXm 中的m
EXTI_Mode : 用来选定工作方式->中断/事件
EXTI_Tigger : 触发方式
EXTI_LineCmd : 开启开关。
这些成员变量的取值一定被枚举或宏定义好了。向上向下翻找就可以找到。
在这里你一定发现,通过初始化 EXTI ,可以确定中断线,但无法确定输入源。即只能指定 PXm 中的 m,无法确定其中的X。想要配置输入源 需要从GPIO库中的函数进行配置。
初始化GPIO
老习惯,去找找固件库有没有这样的函数。打开stm32f10x_gpio.h翻到对最下面,我们可以看到这条函数
void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
十分的神似,F12进去看看函数说明;
通过函数的描述我们可以看见这就是用来配置EXTI中断的,并且同时指定了输入源和中断线。
GPIO_PortSource指定输入源,GPIO_PinSource指定中断线。
值得注意的是,这个函数已经在操作AFIO这个外设了,因此,开时钟时必须开启AFIO的时钟。
对于这个例子,我们无需初始化GPIO,因为 PA0 的输入信号会直接通过 EXTI0 内部电路完成中断。但对于一种情况,GPIO的初始化是必要的,在思考中会 体现这一点。
最后提醒一下,不要忘记打开时钟。
void EXTI_KEY_Config(void)
{
EXTI_InitTypeDef EXTI_InitStruct;
//这里还缺少一个配置中断优先级的函数,下面再说
//EXTI_NVIC_Config();
//初始化EXTI
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); //千万别忘了打开时钟
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource0); //选择输入线
//配置EXTI初始化函数的结构体
EXTI_InitStruct.EXTI_Line = EXTI_Line0;
EXTI_InitStruct.EXTI_LineCmd = ENABLE;
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising;//上升沿触发
EXTI_Init(&EXTI_InitStruct);
}
小结:外部中断寄存器配置部分
通过上一个理论小结,我们需要配置框图中的寄存器保证中断可被内核接收,配置输入线保证外部中断可以传入。
这里程序部分,初始化 EXTI 配置了框图中的寄存器,配置了中断线。初始化 GPIO和 AFIO配置了中断线和输入源,即完成了输入线的配置。
开始编程(二)
由上节STM32中断应用总结的 中断编程顺序 可知,我们上面的工作不过是完成了第一步,配置外设中断。
配置中断优先级分组
初始化NVIC时要配置好优先级的分组方式,这就要用到
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);
这个函数,其中的参数也可在上下的枚举中找到。我们设置优先级分组为1。
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
配置 NVIC 寄存器
配置这个寄存器需要完成对NVIC_InitTypeDef 这个结构体的配置。
其中结构体的成员变量NVIC_IRQChannel 的参数可以在IRQn_Type中找到,F12进入
可以看见,IRQn_Type 事实上是一个枚举,而且其中使用宏定扩展对应了不同型号的F10x系列的芯片,值得注意的,EXTIm 中 m=0,1,2,3,4是所有芯片都有定义的,而且每个都有独立定义。但5-9,10-15就不是了,在下面大容量的宏定义中我们可以看见他们是被集体定义在一起的。在使用时要注意。
我们编写一个 EXTI_NVIC_Config() 用来对NVIC进行初始化。设置主优先级为1,子优先级为1。
static void EXTI_NVIC_Config(void)//static 说明此函数只可被这个.c文件的函数调用
{
NVIC_InitTypeDef NVIC_InitStruct;
//配置优先级分组方式
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
//配置初始化函数
NVIC_InitStruct.NVIC_IRQChannel = EXTI0_IRQn;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStruct);
}
至此所有的初始化工作已经完成,当被设置的端口有变化被检测到时,就会进入中断函数。
编写中断服务函数
所有的中断函数都必须被编写在 stm32f10x_it.c文件中以便于管理。并且中断函数的名字必须和启动文件。打开启动文件startup_stm32f10x_hd.s,我们找到名称。
找到这个函数名后,我们希望进入中断后再判断一下EXTI中断挂起寄存器PR是否真的置位了,可以用这个函数,在exti中可以找到
ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);
他会返回ITStatus类型,即Set或Reset。
在完成操作后,我们必须把挂起寄存器里的置位清零,这样程序才能去运行下面的工作,而不是重复跳入中断函数。用于操作的函数在exti中也有。这一句不可缺少。
void EXTI_ClearITPendingBit(uint32_t EXTI_Line);
这两个对EXTI_PR寄存器操作的函数,所需的参数都是EXTI_Line。用用于指定函数操作的线。可以说EXTI_Line 和GPIO_PIN十分相似。
我们在stm32f10x_it.c中添加这个函数
/*这部分宏定义实现了LED反转的功能应当被定义在bsp_led.h中合适
这里为了便于观察附于此*/
#define LED_G_GPIO_PIN GPIO_Pin_0
#define LED_G_GPIO_RORT GPIOB
#define LED_G_TEOOGLE {LED_G_GPIO_RORT->ODR ^= LED_G_GPIO_PIN;}
void EXTI0_IRQHandler(void)
{
//先判断是否真的置位了
if(EXTI_GetITStatus(EXTI_Line0) !=RESET)
{
LED_G_TEOOGLE; //LED 反转
EXTI_ClearITPendingBit(EXTI_Line0); //清除中断标志位
}
}
附上之前已经用很多次的LED配置代码
void LED_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
RCC_APB2PeriphClockCmd(LED_G_GPIO_CLK, ENABLE);
GPIO_InitStruct.GPIO_Pin = LED_G_GPIO_PIN;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(LED_G_GPIO_RORT,&GPIO_InitStruct);
}
至此所以的准备就已经完成了,只需要在main中简单的调用就可以了。
int main(void)
{
LED_GPIO_Config();
EXTI_KEY_Config();
while(1){}
}
思考
本次的实例中,输入线由 EXTI0 作为中断线,PA0作为输入源;一个中断线连接了一个输入源。
但如果一个中断线连接了多个输入源,如PA0,PB0,PC0… 和 EXTI0 连接。这时 PA0,PB0,PC0… 中任何一个产生中断信号后,都是通过 EXTI0 这条中断线进入中断的。即这些线的中断信号最后都会由同一个中断服务函数处理。如何判断是那条线产生的中断呢?
这里需要在中断服务函数中编写程序手动判断。主要思想是利用外部电平变化时,GPIO的 IDR 寄存器会记录电平。通过判断 IDR 的变化来判断是哪一个中断源。这时,在程序中就需要初始化输入的GPIO为浮空输入模式。(虽然浮空GPIO默认下就是浮空输入模式,为了保险起见,还是要初始化)
上一篇:STM32-不同芯片的移植
下一篇:STM32 PWM波比较输出
推荐阅读最新更新时间:2024-11-13 06:49
推荐帖子
- 8051 和 zigbee的問題
- 第一次接觸zigbee想利用zigbee透過8051作收發data之用但是只知道zigbee的rxtx接去8051的rxtx而且zigbee到8051的program也沒有有人能提供一下嗎寫作的方法?8051和zigbee的問題
- getover RF/无线
- 分享一个开机运行10秒进入LPM4的代码
- 测试开机运行10秒后,进入LPM4深度睡眠完全掉电。由P1.3外部中断唤醒后进入正常工作,10秒后再度进入掉电,如此循环,验证正确。externunsignedcharcount;/*========main========*/intmain(void){Grace_init();//ActivateGrace-generatedconfigurationfor(;;){
- Aguilera 微控制器 MCU
- 【micropython教程】+3 RTC的datetime数组参数读取
- 一,前言继续爱折腾,今天折腾RTC,焊接上电池以后,就开工作咯。。二,RTC函数在help()函数帮助下可以轻松找到RTC函数,本次实验使用到的几个函数见图所示。。今天在玩RTC的时候发现RTC.datetime()函数中有年,月,周,日,小时,分钟,秒还有毫秒等参数,如果能把这些参数拿出来用那不是很方面吗?比如定时比如在判断时间或者数字的时候进行跳转,说干就干,但是第一步就是要提取RTC.datetime
- 飞扬自我 MicroPython开源版块
- 最简单的AVR定时器的问题
- 由于本人是初学者,如果问题太没有含精量大家别见外啊我编译的60秒定时器不知道为什么就是不能再protues上实现。以下是源代码和protues仿真图,希望大家帮帮忙啊#includeiom16v.h#includemacros.h#defineuintunsignedint#defineucharunsignedcharuchartable={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};uin
- Learner_new Microchip MCU
- 谁做过0-30V的直流电压表
- 谁做过0-30V的直流电压表 ,有相关资料吗,希望发一下,加我QQ:175283992谁做过0-30V的直流电压表 我也在搞一个电压表好头疼啊,我发了一些资料上来你看看能有帮助吗?https://bbs.eeworld.com.cn/thread-331637-1-1.html我还没弄完,可以一起研究下。
- book11 单片机
- 【2024 DigiKey创意大赛】esp32s3 klipper面板提交
- 一、作品简介米娜大家好啊,这一次eeworld的活动咱也是白嫖上了(雾),这次咱申请的是esp32s3lcdevboard和一个stm32f401rct6的开发板,stm32f401rct6各位都很熟悉了就不再多说了,esp32s3lcd这块板子倒是挺好的,这屏幕是真的大啊(舔嘴),这块屏幕尺寸正好可以当86盒子,太适合做点面板了,但是因为作者家里只能家居设备实在是太少了玩homeassitant感觉也没什么太大的意思,正好有台旧的kp3s打印机刚被作者刷了个klipp
- 翻车鱼日历 DigiKey得捷技术专区
设计资源 培训 开发板 精华推荐
- 评估子板 - 32位恩智浦MPC5643L
- LM2931AD50R 具有抑制功能的超低压降稳压器的典型应用电路
- LTM8052EY ±5A、2.5V、2 象限稳压器的典型应用
- LTC4162EUFD-FST 1-9 节、3.2A 降压型开关电池充电器的典型应用,具有 PowerPath
- DC2046A-F,用于具有 DC/DC 和理想二极管桥的 LT4276C/LT4321 PoE(13W、5V/2.3A)PD 的演示板
- Nordic开源二代功耗测量套件Power Profiler Kit II
- LT3088HST 基准缓冲器的典型应用
- 直流和步进电机驱动IC —— TC78H653FTG
- IMU Unit ICM42688P+DPS310/SPL06
- NCP301HSN09T1 0.9V 窗口电压检测器的典型应用