STM32F103五分钟入门系列外部中断大汇总
Posted 自信且爱笑‘
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了STM32F103五分钟入门系列外部中断大汇总相关的知识,希望对你有一定的参考价值。
学习板:STM32F103ZET6
强推系列:
STM32F103五分钟入门系列(一)跑马灯(库函数+寄存器)+加编程模板+GPIO总结
STM32F103五分钟入门系列(二)GPIO的七大寄存器+GPIOx_LCKR作用和配置
STM32F103五分钟入门系列(三)GPIO的常用库函数使用方法总结+一个网络上的误区
参考:
STM32F103五分钟入门系列(八)SysTick滴答定时器+SysTick中断实现跑马灯
STM32F103五分钟入门系列(十)NVIC中断优先级管理
本博会用到:
STM32F103五分钟入门系列(一)跑马灯(库函数+寄存器)+加编程模板+GPIO总结
STM32F103五分钟入门系列(四)蜂鸣器实验(库函数+寄存器)
STM32F103五分钟入门系列(五)按键实验(库函数+寄存器)
外部中断
- 前言
- 一、外部中断
- 二、外部中断的中断服务函数
- 三、举例
- (四)举例的工程文件链接
- (五)加一个重要说明(重要)
前言
上一博客总结了NVIC中断优先级管理,本博总结一下外部中断的一些内容。本博会用外部中断的方法来实现之前博客:STM32F103五分钟入门系列(四)蜂鸣器实验(库函数+寄存器)
中所举例子。
一、外部中断
(一)外部中断的数量及引脚映射关系
1、外部中断数量
在STM32F103中有19个外部中断:
线0~15:对应外部IO口的输入中断
线16:连接到PVD输出
线17:连接到RTC闹钟事件
线18:连接到USB唤醒事件
在底层中的定义:
2、外部中断与GPIO的映射关系GPIO_EXTILineConfig()函数
这个图详细的表述了外部中断与GPIO的映射关系:
外部中断EXTI0——>GPIOA.0、GPIOB.0、GPIOC.0…GPIOG.0
外部中断EXTI1——>GPIOA.1、GPIOB.1、GPIOC.1…GPIOG.1
.
.
.
外部中断EXTI15——>GPIOA.15、GPIOB.15、GPIOC.15…GPIOG.15
如EXTI0可以映射到PA0~PG0,那么EXTI0究竟映射到哪个引脚呢?当然是用到:GPIO_EXTILineConfig()
函数了。
在stm32f10x_gpio.h头文件中:
第一个参数:
第二个参数:
通过这个函数,将外部中断线与GPIO的引脚映射起来。
如对KEY_UP:
中断映射:
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0 )
从参数中也能看到PA0…那只能是外部中断线0与PA0映射。
(三)外部中断初始化函数 EXTI_Init()
该函数设置某个中断的触发方式及使能,在stm32f10x_exti.h头文件中。
打开函数体:
这个函数传递的参数是一个结构体,这种函数的配置应该见过N遍了。
第一个参数:MODE
表示模式是中断还是事件。
第二个参数:
表示触发方式是上升沿、下降沿、还是双边沿
第三个参数:
表示哪个引脚的中断,之前的GPIO_EXTILineConfig()已经将中断线与引脚对应起来了,所以这里只需选择是哪条中断线即可。
第四个参数:使能和失能
这类型函数设置方法之前有过基础,直接举例:
例:设置刚刚的KEY_UP:
①要使用中断,所以第一个参数MODE为EXTI_Mode_Interrupt
②按下KEY_UP后,PA0变为高电平,所以为上升沿触发,第二个参数为:EXTI_Trigger_Rising
③引脚连接在PA0,所以第三个参数为:EXTI_Line0
④使能中断,所以第四个参数为:ENABLE
直接附代码:
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Rising;
EXTI_InitStructure.EXTI_Line=EXTI_Line0;
EXTI_InitStructure.EXTI_LineCmd=ENABLE;
EXTI_Init(&EXTI_InitStructure);
(三)外部中断解除函数EXTI_DeInit()
与外部中断相关的寄存器在中文参考手册中,该函数将IMR、EMR、RTSR、FTSR寄存器都置0,对PR寄存器写1来清零。
注意该函数没有参数的,即调用该函数后,所有的外部中断都被屏蔽掉,所以该函数要慎用。
如果想清除某一个外部中断,而保留其他外部中断,则直接用寄存器的方法来设置。
例:清除前面例子中KEY_UP的外部中断。
①屏蔽线0上中断,所以IMR寄存器位0置0(事件模式时可不设置)
EXTI->IMR&=0xfffffffe;//屏蔽线0中断
②屏蔽线0上的事件请求,所以EMR寄存器位0置0(中断模式时可不设置)
EXTI->EMR&=0xfffffffe;//屏蔽线0事件
③禁止线0上升沿触发中断和事件,所以RTSR寄存器位0置0(下降沿触发中断时可不设置,双边沿触发需要设置)
EXTI->RTSR&=0xfffffffe;//禁止线0上升沿触发中断和事件
④禁止线0下降沿触发中断和事件,所以FTSR寄存器位0置0(上升沿沿触发中断时可不设置,双边沿触发需要设置)
EXTI->FTSR&=0xfffffffe;//禁止线0下降沿触发中断和事件
⑤挂起线0的中断,所以PR寄存器位0软件置1来清除改位。
EXTI->PR|=1;//挂起线0的中断和事件
(四)外部中断参数初始化函数EXTI_StructInit()
该函数将所有中断设置的参数初始化为:
①中断线:无
②模式:中断
③触发方式:下降沿
④失能
通过这种方法也能关闭全部外部中断:(慎用)
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_StructInit(& EXTI_InitStructure);
EXTI_Init(&EXTI_InitStructure);//失能全部外部中断
(五)软件中断函数EXTI_GenerateSWInterrupt()
该函数是可以通过软件来触发对应中断线产生中断。该函数对SWIER寄存器直接操作。
如果IMR和EMR寄存器允许中断和事件,即对应中断线上的中断或事件没有被屏蔽,不论设置的中断线是上升沿、下降沿还是双边沿触发,调用该函数后,都会产生一个中断。
(六)外部中断状态函数EXTI_GetFlagStatus()
如果发生中断,PR寄存器的对应位被硬件置1所以可以检测PR寄存器对应位是否为1来获得某外部中断线是否发生中断。
注意的是:
该函数返回的RESET为0,但是SET不是1,而是!0,所以最好不要这样写:
if(EXTI_GetFlagStatus(EXTI_Line0)==1)//发生中断
{
}
而应该:
if(EXTI_GetFlagStatus(EXTI_Line0))//发生中断
{
}
或者:
if(EXTI_GetFlagStatus(EXTI_Line0)==SET)//发生中断
{
}
(七)中断状态清除函数EXTI_ClearFlag()
该函数也是对PR寄存器的设置,将寄存器对应软件位置1后清除该位
(八)外部中断状态函数EXTI_GetITStatus()
该函数获取中断的状态,返回RESET和SET,与EXTI_GetFlagStatus()
函数一致,不过该函数还判断了IMR是否屏蔽了中断请求。所以EXTI_GetITStatus()
比EXTI_GetFlagStatus()
更严谨,以后习惯用这个函数就行。
(九)清除中断标志位函数EXTI_ClearITPendingBit()
该函数与EXTI_ClearFlag()
一模一样,所以俩个函数可以随便用。
二、外部中断的中断服务函数
(一)外部中断服务函数数量
STM32的外部中断服务函数只有6个:
线0:EXTI0_IRQHandler
线1:EXTI1_IRQHandler
线2:EXTI2_IRQHandler
线3:EXTI3_IRQHandler
线4:EXTI4_IRQHandler
线5~9:EXTI9_5_IRQHandler
线10~15:EXTI15_10_IRQHandler
中断服务函数定义在启动文件中:
线0~4各用一个中断服务函数,线5 ~9共用一个中断服务函数,线10 ~15共用一个中断服务函数。
(二)外部中断服务函数编写规则
在中断服务函数中写的代码主要是发生中断后要执行的代码,比如之前SysTick这一博客中,用它的中断实现跑马灯,则SysTick中断服务函数中就写LED灯的亮、灭。
在中断服务函数中,第一步要检测是否发生中断,如果发生中断,就执行中断后想要执行的代码,执行完中断服务函数后,需要清除中断标志,如果不清除中断标志,就一直显示处于中断中,一直会执行中断后的代码,导致程序跑飞。
代码格式:
void EXTIX_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line3)!=RESET)//判断某个线上的中断是否发生
{
//中断逻辑…
EXTI_ClearITPendingBit(EXTI_Line3); //清除 LINE 上的中断标志位
}
}
或者有时候把导致中断的举动写进中断服务函数中,如按下按键后,执行led亮、蜂鸣器响等。
void EXTI0_IRQHandler(void)
{
delay_ms(10);//消抖
if(WK_UP==1) //WK_UP按键
{
BEEP=!BEEP;
}
EXTI_ClearITPendingBit(EXTI_Line0); //清除LINE0上的中断标志位
}
当然,如上述代码,之前是检测到PA0有上升沿的,所以才会触发中断进入中断服务函数。然后再消抖后重新检查KEY_UP是否按下,确定按下后再执行蜂鸣器发声。不论消抖后检测到按下还是没按下,它都的的确确的发生了中断,即只要检测到上升沿就会发生中断,发生中断就会进入中断服务函数,PR寄存器对应位会被置1,所以中断服务函数后面也必须得对PR寄存器写1清零。
(三)外部中断代码的编写顺序
(1)初始化IO时钟
既然要用到线0~15的中断,所以GPIO的时钟必须先使能,不论是复用还是不复用。
(2)初始化IO为输入
既然是外部中断,那么可能是外部的一个信号触发中断,所以IO需设置为输入,至于是上拉、下拉还是浮空输入,需要看外围电路。
(3)开启复用时钟
GPIO正常情况下是只做输入输出的,要实现其他功能就需要GPIO的复用。IO口复用可以查看《STM32中文参考手册》第八章GPIO和AFIO
(4)设置IO口与中断的映射关系
即对GPIO_EXTILineConfig()函数的设置
(5)初始化线上中断,设置触发条件等
即EXTI_Init()函数的设置
(6)配置中断分组(NVIC),并使能中断
当然除了设置中断分组外,还需要设置外部中断的优先级,这些都在上一博客中总结了。
(7)编写中断服务函数
三、举例
(一)题
现用STM32F103五分钟入门系列(五)按键实验(库函数+寄存器)的例子:
①按下key_up后LED0、LED1交替闪烁,每0.5s闪烁一次,取消按下后,两个灯全灭。
②按下key0后,LED0常亮、蜂鸣器每隔0.5s间断发声。
③按下key2后,LED1常亮,蜂鸣器每隔0.5s间断发声。
④按下key1后,LED0、LED1同时亮,同时灭,且同时灭的时候蜂鸣器发声,同时亮的时候蜂鸣器不发声;间隔1s
因为有延时,会用到:STM32F103五分钟入门系列(九)延时函数(自己重写的底层)(上限:477218ms和477218588us)
(二)搞清楚IO和工作的高低电平
KEY_UP:接PA0、按下后IO为高电平,下拉输入(未按下时输入未知,拉到低电平)
KEY2:接PE2、按下后IO为低电平,上拉输入(未按下时输入未知,拉到高电平)
KEY1:接PE3、按下后IO为低电平,上拉输入(未按下时输入未知,拉到高电平)
KEY0:接PE4、按下后IO为低电平,上拉输入(未按下时输入未知,拉到高电平)
LED1:接PE5、输出低电平后灯亮、通用推挽输出
LED0:接PB5、输出低电平后灯亮、通用推挽输出
BEEP:接PB8、输出高电平发声、通用推挽输出
(三)代码编写
1、led.h和led.c
之前的几篇博客都讲解过,直接附代码:
led.h代码:
1 //led.h
2 #ifndef LED_H
3 #define LED_H
4 void LED_Init(void);
5
6 #endif
7
led.c代码:
1 //led.c
2 #include "sys.h"
3 #include "stm32f10x.h"
4 #include "led.h"
5 void LED_Init(void)
6 {
7 GPIO_InitTypeDef GPIO_InitStruct_B;
8 GPIO_InitTypeDef GPIO_InitStruct_E;
9 RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOE ,ENABLE);
10
11 GPIO_InitStruct_B.GPIO_Mode=GPIO_Mode_Out_PP;
12 GPIO_InitStruct_B.GPIO_Pin=GPIO_Pin_5;
13 GPIO_InitStruct_B.GPIO_Speed=GPIO_Speed_50MHz;
14 GPIO_Init(GPIOB,&GPIO_InitStruct_B);
15
16 GPIO_InitStruct_E.GPIO_Mode=GPIO_Mode_Out_PP;
17 GPIO_InitStruct_E.GPIO_Pin=GPIO_Pin_5;
18 GPIO_InitStruct_E.GPIO_Speed=GPIO_Speed_50MHz;
19 GPIO_Init(GPIOE,&GPIO_InitStruct_E);
20
21 GPIO_SetBits(GPIOB, GPIO_Pin_5);//PB5置高电平
22 //GPIO_WriteBit(GPIOB, GPIO_Pin_5,1);
23 //GPIO_Write(GPIOB,0x0020); //慎用
24 //PBout(5)=1;
25 GPIO_SetBits(GPIOE, GPIO_Pin_5);//PE5置高电平
26 //GPIO_WriteBit(GPIOE, GPIO_Pin_5,1);
27 //GPIO_Write(GPIOE,0x0020); //慎用
28 //PEout(5)=1;
29 }
2、beep.h和beep.c
之前的几篇博客都讲解过,直接附代码:
头文件:beep.h:
//beep.h
#ifndef BEEP_H
#define BEEP_H
void BEEP_Init();
#endif
beep.c代码:
1 //beep.c
2 #include "beep.h"
3 #include "stm32f10x.h"
4 #include "sys.h"
5
6 void BEEP_Init(void)
7 {
8 GPIO_InitTypeDef GPIO_InitStruct;
9 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);//使能GPIOB时钟
10
11 GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;
12 GPIO_InitStruct.GPIO_Pin=GPIO_Pin_8;
13 GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;//PB8 IO配置
14 GPIO_Init(GPIOB,&GPIO_InitStruct);
15
16 GPIO_ResetBits(GPIOB,GPIO_Pin_8); //初始状态置低电平,关闭蜂鸣器
17 //GPIO_WriteBit(GPIOB,GPIO_Pin_8, 0);//初始状态置低电平,关闭蜂鸣器
18 //GPIO_Write(GPIOB,0); //初始状态置低电平,关闭蜂鸣器
19 //PBout(8)=0; //初始状态置低电平,关闭蜂鸣器
20 }
3、key.h和key.c
之前的几篇博客都讲解过,直接附代码:
头文件key.h:
#ifndef KEY_H
#define KEY_H
void KEY_Init(void);
#endif
key.c文件:
//key.c
#include "stm32f10x.h"
#include "sys.h"
#include "key.h"
void KEY_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct_A;
GPIO_InitTypeDef GPIO_InitStruct_E;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOE , ENABLE);//使能GPIOA和GPIOE(PA0 PE2、3、4)
GPIO_InitStruct_A.GPIO_Mode=GPIO_Mode_IPD;
GPIO_InitStruct_A.GPIO_Pin=GPIO_Pin_0;
GPIO_InitStruct_A.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStruct_A);//PA0 key_up 下拉输入
GPIO_InitStruct_E.GPIO_Mode=GPIO_Mode_IPU;
GPIO_InitStruct_E.GPIO_Pin=GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4;
GPIO_InitStruct_E.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOE, &GPIO_InitStruct_E);//PE2、3、4 key0、key1、key2 上拉输入
}
4、delay.h和delay.c
之前博客重写过,直接附代码:
头文件:delay.h:
//delay.h
#ifndef _DELAY_
#define _DELAY_
#include "sys.h"
void delay_ms(u32 nms);
void delay_Init(void);
#endif
delay.c:
//delay.c
#include "delay.h"
void delay_Init(void)
{
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); //选择外部时钟源72MHZ/8=9MHZ
//SysTick->CTRL &=0xFFFFFFFB
SysTick->VAL=0; //清空计数器
}
void delay_ms(u32 n)
{
u32 a;//商
u32 b;//余数
u32 temp;
a=(9000*n/0xffffff);//2^24 -1=16777215=0xffffff
b=(9000*n)%0xffffff;
while(a)//商大于0时
{
SysTick->LOAD=(u32)0xffffff; //装载最大可装载值
SysTick->VAL=0; //清空计数器
SysTick->CTRL|=1; //使能计数器,开始计数
do
{
temp=SysTick->CTRL;
}while((temp&0x01)&&!(temp&(0x10000)));//while(计数器未使能&&(!CTRL计数器倒计数到0))
a--;
if(!a)//a为0,表示装载最大可装载值的次数用完
{
SysTick->CTRL&=0xfffffffe;//关闭计数器
}
}
SysTick->LOAD=b;//装载余数
SysTick->VAL =0x00; //清空计数器
SysTick->CTRL|=1; //重新使能计数器,开始计数
do
{
temp=SysTick->CTRL;
}while((temp&0x01)&&!(temp&(0x10000)));//while(计数器未使能&&(!CTRL计数器倒计数到0))
SysTick->CTRL&=0xfffffffe;//关闭计数器
SysTick->VAL =0X00; //清空计数器
}
5、添加.c和.h文件到工程
以LED为例:
(1)工程目录下新建文件夹LED
点击工程名——>【manage project…】
新建text文件,并保存
并保存为led.c
同理保存led.h
添加.c文件:
添加头文件.h
同理将其他文件添加进来:
6、exti.h
exti.h还是老规矩:
#ifndef _EXTI_
#define _EXTI_
void exti_Init(void);
#endif
7、exti.c
因为之前的key中都使能了GPIO时钟和设置了GPIO,所以现在直接开启复用时钟。
由于中断服务函数要在exti.c文件中写,所以把lexd.h、key.h、beep.h、delay.h包含进来
代码:
#include "stm32f10x.h"
#include "led.h"
#include "key.h"
#include "beep.h"
#include "delay.h"
#include "exti.h"
void exti_Init()
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
}
然后接下来就是中断线与IO的映射:
KEY_UP——>线0——>PA0
KEY2——>线2——>PE2
KEY1——>线3——>PE3
KEY0——>线4——>PE4
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);外部中断与PA0映射
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource2);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource3);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource4);
然后就是外部中断初始化函数:
#include "stm32f10x.h"
#include "led.h"
#include "key.h"
#include "beep.h"
#include "delay.h"
#include "exti.h"
void exti_init()
{
EXTI_InitTypeDef EXTI_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
//外部中断与PE2、PE3、PE4映射
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource2);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource3);
GPIO_EXTILineConfig(GPIO_Port以上是关于STM32F103五分钟入门系列外部中断大汇总的主要内容,如果未能解决你的问题,请参考以下文章
STM32F103五分钟入门系列SysTick滴答定时器+SysTick中断实现跑马灯
STM32F103(二十七)超长篇解读STM32访问外部flash
STM32F103(二十七)超长篇解读STM32访问外部flash