基础概念
STM32F4 的每个 IO 都可以作为外部中断的中断输入口,这点也是 STM32F4 的强大之处。 STM32F407 的中断控制器支持 22 个外部
中断/事件请求。每个中断设有状态位,每个中断/事件都有独立的触发和屏蔽设置。 STM32F407的 22 个外部中断为:
EXTI 线 0~15:对应外部 IO 口的输入中断。
EXTI 线 16:连接到 PVD 输出。
EXTI 线 17:连接到 RTC 闹钟事件。
EXTI 线 18:连接到 USB OTG FS 唤醒事件。
EXTI 线 19:连接到以太网唤醒事件。
EXTI 线 20:连接到 USB OTG HS(在 FS 中配置)唤醒事件。
EXTI 线 21:连接到 RTC 入侵和时间戳事件。
EXTI 线 22:连接到 RTC 唤醒事件。
从上面可以看出, STM32F4 供 IO 口使用的中断线只有 16 个,但是 STM32F4 的 IO 口却
远远不止 16 个,那么 STM32F4 是怎么把 16 个中断线和 IO 口一一对应起来的呢?于是 STM32就这样设计, GPIO 的管教 GPIOx.0~GPIOx.15(x=A,B,C,D,E, F,G,H,I)分别对应中断线 0~15。这
样每个中断线对应了最多 9 个 IO 口,以线 0 为例:它对应了 GPIOA.0、 GPIOB.0、 GPIOC.0、GPIOD.0、 GPIOE.0、 GPIOF.0、 GPIOG.0,GPIOH.0,GPIOI.0。而中断线每次只能连接到 1 个 IO口上,这样就需要通过配置来决定对应的中断线配置到哪个 GPIO 上了。下面我们看看 GPIO
跟中断线的映射关系图:
基本步骤
1) 使能 IO 口时钟, 初始化 IO 口为输入。
2) 使能 SYSCFG 时钟, 设置 IO 口与中断线的映射关系。
3)初始化线上中断,设置触发条件等。
4)配置中断分组(NVIC),并使能中断。
5)编写中断服务函数。
1) 使能 IO 口时钟, 初始化 IO 口为输入。
首先,我们要使用 IO 口作为中断输入,所以我们要使能相应的 IO 口时钟,以及初始化相应的 IO 口为输入模式,我们使用按键为外部中断,此处应设置按键的IO口。
在exti.c文件下 void EXTIX_Init(void) 函数中输入
key_init();
2) 使能 SYSCFG 时钟, 设置 IO 口与中断线的映射关系。
使能外部中断所必须的时钟SYSCFG,接下来,我们配置 GPIO 与中断线的映射关系。 在库函数中,配置 GPIO 与中断线的映射
关系的函数 SYSCFG_EXTILineConfig ()来实现的:
void SYSCFG_EXTILineConfig(uint8_t EXTI_PortSourceGPIOx, uint8_t EXTI_PinSourcex);
该函数将 GPIO 端口与中断线映射起来。
此部分代码:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG,ENABLE);//使能SYSCFG时钟
SYSCFG_EXTILineConfig (EXTI_PortSourceGPIOE, EXTI_PinSource4);//GPIO端口指按键的IO口,第二个参数是中断线,因为这里的按键是GPIOE_Pin4,故这里中断线对应为4.
3)初始化线上中断,设置触发条件等。
中断线上中断的初始化是通过函数 EXTI_Init()实现的。这里我们本来是在 EXTI_Init()里实现初始化,但因为还要使能时钟还要设置中断优先级等,整个中断初始化放在 void EXTIX_Init(void) 函数里。
故这里只需要写入原本 EXTI_Init()里该有的内容即可
EXTI_InitTypeDef EXTI_InitStruct;
EXTI_InitStruct.EXTI_Line=EXTI_Line4;//选择中断线
EXTI_InitStruct.EXTI_LineCmd=ENABLE;//使能
EXTI_InitStruct.EXTI_Mode=EXTI_Mode_Interrupt;//选择中断模式
EXTI_InitStruct.EXTI_Trigger=EXTI_Trigger_Falling;//选择下降沿触发中断
EXTI_Init(&EXTI_InitStruct);
4)配置中断分组(NVIC),并使能中断
设置 NVIC 中断优先级:
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel=EXTI4_IRQn;//使能按键外部中断
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;//使能外部中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0x01;//抢占优先级1
NVIC_InitStructure.NVIC_IRQChannelSubPriority=0x02;//响应优先级2
NVIC_Init(&NVIC_InitStructure);//中断优先级分组初始化
这里的优先级应该是有多个中断的时候需要,只有一个中断还需要设置优先级吗?
5)编写中断服务函数。
void EXTI4_IRQHandler(void)
{
delay_ms(5);
if(key0==0)
{
LED1 =0;
for(int i=0;i<5;i++)
{
LED0 =1;
delay_ms(500);
LED0=0;
delay_ms(500);
}
}
EXTI_ClearITPendingBit(EXTI_Line4);//清除 LINE4 上的中断标志位
}
整个的效果是,刚开始led0亮,进入中断后led0闪烁五次,led1常亮。
exti.h文件
#ifndef EXIT_H
#define EXIT_H
#include "sys.h"
void EXTIX_Init(void);
#endif
led.c部分
#include "led.h"
void LED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_ResetBits(GPIOA, GPIO_Pin_7);
}
led.h部分
#ifndef __LED_H
#define __LED_H
#include "sys.h"
#define LED0 PAout(6) // DS0
#define LED1 PAout(7) // DS1
void LED_Init(void);
#endif
按键初始化的代码解释参考这里
key.c部分
#include "key.h"
#include "delay.h"
#include "led.h"
void key_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4| GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOE, &GPIO_InitStructure);
}
key.h部分
#ifndef KEY_H
#define KEY_H
#include "sys.h"
#define key0 PEin(4)
#define key1 PEin(3)
void key_init(void);
#endif
main.c 文件
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "key.h"
#include "exits.h"
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组 2
delay_init(168);
LED_Init();
key_init();
EXTIX_Init();
LED0=0;
LED1=1;
while(1)
{
}
}
不懂main.c中中断优先级分组是干什么的