需要添加的固件库文件有 misc.c、stm32f4xx_exti.c、stm32f4xx_gpio.c、stm32f4xx_rcc.c、stm32f4xx_syscfg.c 五个。
1) 使能 IO 口时钟,初始化 IO 口为输入
首先,我们要使用 IO 口作为中断输入,所以我们要使能相应的 IO 口时钟,以及初始化相应的 IO 口为输入模式,具体的使用方法在按键模块完成。
//按键IO口初始化函数
void KEY_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE); //使能GPIOF时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9; //KEY0 KEY1 KEY2 KEY3对应引脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; //普通输入模式
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //100M
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_Init(GPIOF, &GPIO_InitStructure); //初始化GPIOF6,7,8,9
}
2) 开启 SYSCFG 时钟,设置 IO 口与中断线的映射关系。
接下来,要配置 GPIO 与中断线的映射关系,那么我们首先需要打开SYSCFG 时钟。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);//使能 SYSCFG 时钟
这里一定要注意,只要我们使用到外部中断,就必须打开 SYSCFG 时钟。接下来,配置 GPIO 与中断线的映射关系。在库函数中,配置 GPIO 与中断线的映射关系的函数 SYSCFG_EXTILineConfig ()来实现的:
void SYSCFG_EXTILineConfig(uint8_t EXTI_PortSourceGPIOx, uint8_t EXTI_PinSourcex);
该函数将 GPIO 端口与中断线映射起来,使用如下:
//建立io口与中断线的映射关系
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOF, EXTI_PinSource6); //PF6连接到中断线6
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOF, EXTI_PinSource7); //PF6连接到中断线7
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOF, EXTI_PinSource8); //PF6连接到中断线8
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOF, EXTI_PinSource9); //PF6连接到中断线9
设置好中断 线映射之后,那么到底来自这个 IO 口的中断是通过什么方式触发的呢?接下来就要设置该中断线上中断的初始化参数了。
3) 初始化线上中断,设置触发条件等。
中断线上中断的初始化是通过函数 EXTI_Init()实现的。EXTI_Init()函数的定义是:
void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);
具体使用如下:
// 初始化线上中断,设置触发条件等
EXTI_InitStructure.EXTI_Line=EXTI_Line6 | EXTI_Line7 | EXTI_Line8 | EXTI_Line9; //中断线6-9
EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling; //下降沿触发
EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt; //中断事件
EXTI_InitStructure.EXTI_LineCmd=ENABLE; //使能中断线
EXTI_Init(&EXTI_InitStructure); //初始化中断线
4) 配置中断分组(NVIC),并使能中断。
我们设置好中断线和 GPIO 映射关系,然后又设置好了中断的触发模式等初始化参数。既然是外部中断,涉及到中断我们当然还要设置 NVIC 中断优先级。
//配置中断分组(NVIC),并使能中断
NVIC_InitStructure.NVIC_IRQChannel=EXTI9_5_IRQn; //使能外部按键中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority= 0x02; //抢占优先级2
NVIC_InitStructure.NVIC_IRQChannelSubPriority=0x02; //响应优先级2
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE; //使能中断通道
NVIC_Init(&NVIC_InitStructure); //中断优先级分组初始化
5) 编写中断服务函数。
我们配置完中断优先级之后,接着我们要做的就是编写中断服务函数。中断服务函数的名字是在 MDK 中事先有定义的。这里需要说明一下,STM32F4 的 IO 口外部中断函数只有 7 个,分别为:
EXPORT EXTI0_IRQHandler
EXPORT EXTI1_IRQHandler
EXPORT EXTI2_IRQHandler
EXPORT EXTI3_IRQHandler
EXPORT EXTI4_IRQHandler
EXPORT EXTI9_5_IRQHandler
EXPORT EXTI15_10_IRQHandler
中断线 0-4 每个中断线对应一个中断函数,中断线 5-9 共用中断函数 EXTI9_5_IRQHandler,中断线 10-15 共用中断函数 EXTI15_10_IRQHandler。
//外部中断服务函数
void EXTI9_5_IRQHandler(void)
{
delay_ms(10); //消抖
if(KEY0==0)
LED0=!LED0; //LED反转
else if(KEY1==0)
LED1=!LED1;
else if(KEY2==0)
LED2=!LED2;
EXTI_ClearITPendingBit(EXTI_Line6); //清除 LINE6 上的中断标志位
EXTI_ClearITPendingBit(EXTI_Line7); //清除 LINE7 上的中断标志位
EXTI_ClearITPendingBit(EXTI_Line8); //清除 LINE8 上的中断标志位
EXTI_ClearITPendingBit(EXTI_Line9); //清除 LINE9 上的中断标志位
}
6) 软件设计
exti.c
#include "key.h"
#include "common.h"
#include "led.h"
#include "exti.h"
//外部中断服务函数
void EXTI9_5_IRQHandler(void)
{
delay_ms(10); //消抖
if(KEY0==0)
LED0=!LED0; //LED反转
else if(KEY1==0)
LED1=!LED1;
else if(KEY2==0)
LED2=!LED2;
EXTI_ClearITPendingBit(EXTI_Line6); //清除 LINE6 上的中断标志位
EXTI_ClearITPendingBit(EXTI_Line7); //清除 LINE7 上的中断标志位
EXTI_ClearITPendingBit(EXTI_Line8); //清除 LINE8 上的中断标志位
EXTI_ClearITPendingBit(EXTI_Line9); //清除 LINE9 上的中断标志位
}
//初始化PF6-PF9为中断输入
void EXTIX_Init(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
KEY_Init(); //按键初始化
LED_Init(); //LED初始化
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);//使能 SYSCFG 时钟
//建立io口与中断线的映射关系
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOF, EXTI_PinSource6); //PF6连接到中断线6
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOF, EXTI_PinSource7); //PF6连接到中断线7
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOF, EXTI_PinSource8); //PF6连接到中断线8
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOF, EXTI_PinSource9); //PF6连接到中断线9
// 初始化线上中断,设置触发条件等
EXTI_InitStructure.EXTI_Line=EXTI_Line6 | EXTI_Line7 | EXTI_Line8 | EXTI_Line9; //中断线6-9
EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling; //下降沿触发
EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt; //中断事件
EXTI_InitStructure.EXTI_LineCmd=ENABLE; //使能中断线
EXTI_Init(&EXTI_InitStructure); //初始化中断线
//配置中断分组(NVIC),并使能中断
NVIC_InitStructure.NVIC_IRQChannel=EXTI9_5_IRQn; //使能外部按键中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority= 0x02; //抢占优先级2
NVIC_InitStructure.NVIC_IRQChannelSubPriority=0x02; //响应优先级2
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE; //使能中断通道
NVIC_Init(&NVIC_InitStructure); //中断优先级分组初始化
}
exti.h
#ifndef __EXTI_H
#define __EXTI_H
void EXTI9_5_IRQHandler(void); //中断服务函数
void EXTIX_Init(void); //外部中断初始化函数
#endif
main.c
#include "common.h"
#include "exti.h"
#include "led.h"
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置系统中断优先级分组2
delay_init(); //初始化延时函数
EXTIX_Init();//初始化外部中断函数
LED0=0; //先点亮红灯
while(1)
{
delay_ms(1000);
}
}