学习板:STM32F103ZET6
强推系列: STM32F103五分钟入门系列(一)跑马灯(库函数+寄存器)+加编程模板+GPIO总结
STM32F103五分钟入门系列(二)GPIO的七大寄存器+GPIOx_LCKR作用和配置
STM32F103五分钟入门系列(三)GPIO的常用库函数使用方法总结+一个网络上的误区
参考:
51单片机(四)定时器中断(+数码管—24小时制钟表)
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上的事件请求,所以EMR寄存器位0置0(中断模式时可不设置)
EXTI->EMR&=0xfffffffe;
③禁止线0上升沿触发中断和事件,所以RTSR寄存器位0置0(下降沿触发中断时可不设置,双边沿触发需要设置)
EXTI->RTSR&=0xfffffffe;
④禁止线0下降沿触发中断和事件,所以FTSR寄存器位0置0(上升沿沿触发中断时可不设置,双边沿触发需要设置)
EXTI->FTSR&=0xfffffffe;
⑤挂起线0的中断,所以PR寄存器位0软件置1来清除改位。
EXTI->PR|=1;
(四)外部中断参数初始化函数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);
}
}
或者有时候把导致中断的举动写进中断服务函数中,如按下按键后,执行led亮、蜂鸣器响等。
void EXTI0_IRQHandler(void)
{
delay_ms(10);
if(WK_UP==1)
{
BEEP=!BEEP;
}
EXTI_ClearITPendingBit(EXTI_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
2 #ifndef LED_H
3 #define LED_H
4 void LED_Init(void);
5
6 #endif
7
led.c代码:
1
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);
22
23
24
25 GPIO_SetBits(GPIOE, GPIO_Pin_5);
26
27
28
29 }
2、beep.h和beep.c
之前的几篇博客都讲解过,直接附代码:
头文件:beep.h:
#ifndef BEEP_H
#define BEEP_H
void BEEP_Init();
#endif
beep.c代码:
1
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);
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;
14 GPIO_Init(GPIOB,&GPIO_InitStruct);
15
16 GPIO_ResetBits(GPIOB,GPIO_Pin_8);
17
18
19
20 }
3、key.h和key.c
之前的几篇博客都讲解过,直接附代码:
头文件key.h:
#ifndef KEY_H
#define KEY_H
void KEY_Init(void);
#endif
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);
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);
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);
}
4、delay.h和delay.c
之前博客重写过,直接附代码:
头文件:delay.h:
#ifndef _DELAY_
#define _DELAY_
#include "sys.h"
void delay_ms(u32 nms);
void delay_Init(void);
#endif
delay.c:
#include "delay.h"
void delay_Init(void)
{
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);
SysTick->VAL=0;
}
void delay_ms(u32 n)
{
u32 a;
u32 b;
u32 temp;
a=(9000*n/0xffffff);
b=(9000*n)%0xffffff;
while(a)
{
SysTick->LOAD=(u32)0xffffff;
SysTick->VAL=0;
SysTick->CTRL|=1;
do
{
temp=SysTick->CTRL;
}while((temp&0x01)&&!(temp&(0x10000)));
a--;
if(!a)
{
SysTick->CTRL&=0xfffffffe;
}
}
SysTick->LOAD=b;
SysTick->VAL =0x00;
SysTick->CTRL|=1;
do
{
temp=SysTick->CTRL;
}while((temp&0x01)&&!(temp&(0x10000)));
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);
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);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource2);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource3);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource4);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);
EXTI_InitStructure.EXTI_Line=EXTI_Line0;
EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Rising;
EXTI_InitStructure.EXTI_LineCmd=ENABLE;
EXTI_Init(&EXTI_InitStructure);
EXTI_InitStructure.EXTI_Line=EXTI_Line2;
EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd=ENABLE;
EXTI_Init(&EXTI_InitStructure);
EXTI_InitStructure.EXTI_Line=EXTI_Line3;
EXTI_Init(&EXTI_InitStructure);
EXTI_InitStructure.EXTI_Line=EXTI_Line4;
EXTI_Init(&EXTI_InitStructure);
}
然后就是中断优先级分组、各中断优先级、中断使能。
中断优先级分组在main.c中写,因为用到四个中断,且不想被打断,所以可以用分组2,抢占优先级一样,响应优先级0~3.
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
然后就是中断优先级设置,在exti.c中
#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;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource2);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource3);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource4);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);
EXTI_InitStructure.EXTI_Line=EXTI_Line0;
EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Rising;
EXTI_InitStructure.EXTI_LineCmd=ENABLE;
EXTI_Init(&EXTI_InitStructure);
EXTI_InitStructure.EXTI_Line=EXTI_Line2;
EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd=ENABLE;
EXTI_Init(&EXTI_InitStructure);
EXTI_InitStructure.EXTI_Line=EXTI_Line3;
EXTI_Init(&EXTI_InitStructure);
EXTI_InitStructure.EXTI_Line=EXTI_Line4;
EXTI_Init(&EXTI_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel=EXTI0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel=EXTI2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel=EXTI3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=2;
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel=EXTI4_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=3;
NVIC_Init(&NVIC_InitStructure);
}
接下来写中断服务函数:
PA0、KEY_UP: 注意当松开按键后,再清除中断,否则里面函数只执行一次!
void EXTI0_IRQHandler()
{
delay_ms(10);
while(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0))
{
PBout(5)=~PBout(5);
delay_ms(500);
PBout(5)=~PBout(5);
PEout(5)=~PEout(5);
delay_ms(500);
PEout(5)=~PEout(5);
}
PBout(5)=1;
PEout(5)=1;
EXTI_ClearITPendingBit(EXTI_Line0);
}
PE2、KEY2:
void EXTI2_IRQHandler()
{
delay_ms(10);
while(!(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_2)))
{
PBout(5)=0;
GPIO_SetBits(GPIOB, GPIO_Pin_8);
delay_ms(500);
GPIO_ResetBits(GPIOB, GPIO_Pin_8);
delay_ms(500);
}
PBout(5)=1;
GPIO_ResetBits(GPIOB, GPIO_Pin_8);
EXTI_ClearITPendingBit(EXTI_Line2);
}
PE3、KEY1:
void EXTI3_IRQHandler()
{
delay_ms(10);
while(!(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3)))
{
PEout(5)=0;
GPIO_SetBits(GPIOB, GPIO_Pin_8);
delay_ms(500);
GPIO_ResetBits(GPIOB, GPIO_Pin_8);
delay_ms(500);
}
PEout(5)=1;
GPIO_ResetBits(GPIOB, GPIO_Pin_8);
EXTI_ClearITPendingBit(EXTI_Line3);
}
PE4、KEY0:
void EXTI4_IRQHandler()
{
delay_ms(10);
while(!(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4)))
{
PBout(5)=~PBout(5);
PEout(5)=~PEout(5);
GPIO_ResetBits(GPIOB, GPIO_Pin_8);
delay_ms(500);
PBout(5)=~PBout(5);
PEout(5)=~PEout(5);
GPIO_SetBits(GPIOB, GPIO_Pin_8);
delay_ms(500);
}
PBout(5)=1;
PEout(5)=1;
GPIO_ResetBits(GPIOB, GPIO_Pin_8);
EXTI_ClearITPendingBit(EXTI_Line4);
}
8、main.c
#include "stm32f10x.h"
#include "exti.h"
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
exti_Init();
while(1)
{
}
}
9、exti…c完整程序
#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;
NVIC_InitTypeDef NVIC_InitStructure;
LED_Init();
KEY_Init();
BEEP_Init();
delay_Init();
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource2);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource3);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource4);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);
EXTI_InitStructure.EXTI_Line=EXTI_Line0;
EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Rising;
EXTI_InitStructure.EXTI_LineCmd=ENABLE;
EXTI_Init(&EXTI_InitStructure);
EXTI_InitStructure.EXTI_Line=EXTI_Line2;
EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd=ENABLE;
EXTI_Init(&EXTI_InitStructure);
EXTI_InitStructure.EXTI_Line=EXTI_Line3;
EXTI_Init(&EXTI_InitStructure);
EXTI_InitStructure.EXTI_Line=EXTI_Line4;
EXTI_Init(&EXTI_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel=EXTI0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel=EXTI2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel=EXTI3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=2;
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel=EXTI4_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=3;
NVIC_Init(&NVIC_InitStructure);
}
void EXTI0_IRQHandler()
{
delay_ms(10);
while(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0))
{
PBout(5)=~PBout(5);
delay_ms(500);
PBout(5)=~PBout(5);
PEout(5)=~PEout(5);
delay_ms(500);
PEout(5)=~PEout(5);
}
PBout(5)=1;
PEout(5)=1;
EXTI_ClearITPendingBit(EXTI_Line0);
}
void EXTI2_IRQHandler()
{
delay_ms(10);
while(!(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_2)))
{
PBout(5)=0;
GPIO_SetBits(GPIOB, GPIO_Pin_8);
delay_ms(500);
GPIO_ResetBits(GPIOB, GPIO_Pin_8);
delay_ms(500);
}
PBout(5)=1;
GPIO_ResetBits(GPIOB, GPIO_Pin_8);
EXTI_ClearITPendingBit(EXTI_Line2);
}
void EXTI3_IRQHandler()
{
delay_ms(10);
while(!(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3)))
{
PEout(5)=0;
GPIO_SetBits(GPIOB, GPIO_Pin_8);
delay_ms(500);
GPIO_ResetBits(GPIOB, GPIO_Pin_8);
delay_ms(500);
}
PEout(5)=1;
GPIO_ResetBits(GPIOB, GPIO_Pin_8);
EXTI_ClearITPendingBit(EXTI_Line3);
}
void EXTI4_IRQHandler()
{
delay_ms(10);
while(!(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4)))
{
PBout(5)=~PBout(5);
PEout(5)=~PEout(5);
GPIO_ResetBits(GPIOB, GPIO_Pin_8);
delay_ms(500);
PBout(5)=~PBout(5);
PEout(5)=~PEout(5);
GPIO_SetBits(GPIOB, GPIO_Pin_8);
delay_ms(500);
}
PBout(5)=1;
PEout(5)=1;
GPIO_ResetBits(GPIOB, GPIO_Pin_8);
EXTI_ClearITPendingBit(EXTI_Line4);
}
(四)举例的工程文件链接
(三)中例子的工程链接
(五)加一个重要说明(重要)
外部中断不一定是外部信号触发的中断,中断线对应IO口的模式也不一定非要是输入模式。虽然外部中断映射到了GPIO,但是不影响GPIO的输入输出,即只要设置好中断线的上升沿触发、下降沿触发,无论对应IO是输入模式还是输出模式,都可以触发中断
例如:
LED0——>PB5——>通用推挽输出——>中断线5
BEEP——>PB8
我们对LED0实现闪烁,然后设置外部中断线5与PB5映射,且下降沿触发中断,在中断服务函数中让蜂鸣器取反。
就前面的例子修改代码:
exti.c:
#include "stm32f10x.h"
#include "sys.h"
#include "beep.h"
#include "exti.h"
void exti_Init()
{
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
BEEP_Init();
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource5);
EXTI_InitStructure.EXTI_Line=EXTI_Line5;
EXTI_InitStructure.EXTI_LineCmd=ENABLE;
EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;
EXTI_Init(&EXTI_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel=EXTI9_5_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=3;
NVIC_Init(&NVIC_InitStructure);
}
void EXTI9_5_IRQHandler()
{
PBout(8)=~PBout(8);
EXTI_ClearITPendingBit(EXTI_Line5);
}
main.c:
#include "stm32f10x.h"
#include "exti.h"
#include "delay.h"
#include "led.h"
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
exti_Init();
delay_Init();
LED_Init();
while(1)
{
PBout(5)=~PBout(5);
delay_ms(1000);
}
}
测试后发现,LED0每亮一次,蜂鸣器就取反一次(发声或停止发声)。而此时LED0是推挽输出,由软件置0和1的,这样同样可以发生外部中断!
|