STM32的每个IO都可以作为外部中断输入。
STM32的中断控制器支持19个外部中断/事件请求:
线0~15:对应外部IO口的输入中断。
线16:连接到PVD输出。
线17:连接到RTC闹钟事件。
线18:连接到USB唤醒事件。
每个外部中断线可以独立的配置触发方式(上升沿,下降沿或者双边沿触发),触发/屏蔽,专用的状态位。
从上面可以看出,STM32供IO使用的中断线只有16个,但是STM32F10x系列的IO口多达上百个,STM32F103ZET6(112),
STM32F103RCT6(51),那么中断线怎么跟io口对应呢?
通过该图可知,每一个中断线都对应了7组中的对应编号的IO口(这边以ZET6:112个IO=16个 * 7组)。
有如下关系:
GPIOx.0映射到EXTI0
GPIOx.1映射到EXTI1
…
GPIOx.15映射到EXTI15
注意:每个时间只能有一个I/O可映射到对应的中断线。
是不是16个中断线就可以分配16个中断服务函数呢?
IO口外部中断在中断向量表中只分配了7个中断向量,也就是只能使用7个中断服务函数(下表少写了EXTI0)
从表中可以看出,外部中断线5~9分配一个中断向量,共用一个服务函数外部中断线10 ~15分配一个中断向量,共用一个中断服务函数。
EXTI0_IRQHandler
EXTI1_IRQHandler
EXTI2_IRQHandler
EXTI3_IRQHandler
EXTI4_IRQHandler
EXTI9_5_IRQHandler
EXTI15_10_IRQHandler
①映射
②中断使能、触发模式等使能方式。
③编写中断服务函数
一般配置步骤:
①初始化IO口为输入。
GPIO_Init();
② 开启IO口复用时钟。 RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
③设置IO口与中断线的映射关系。
void GPIO_EXTILineConfig();
④初始化线上中断,设置触发条件等。
EXTI_Init();
⑤配置中断分组(NVIC),并使能中断。
NVIC_Init();
⑥编写中断服务函数。
EXTIx_IRQHandler();
⑦清除中断标志位
EXTI_ClearITPendingBit();
①void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
//设置IO口与中断线的映射关系
exp: GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource2);
②void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);
//初始化中断线:触发方式等
③ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);
//判断中断线中断状态,是否发生
④void EXTI_ClearITPendingBit(uint32_t EXTI_Line);
//清除中断线上的中断标志位
新建模版,在HARDWARE文件夹添加exit文件。然后就是填入工程,添加头文件路径等等操作。
#include "stm32f10x.h" void EXTIX_Init(void){
GPIO_InitTypeDef GPIO_InitStruce; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_GPIOE , ENABLE); GPIO_InitStruce.GPIO_Mode=GPIO_Mode_IPU;//上拉输入 GPIO_InitStruce.GPIO_Pin=GPIO_Pin_4|GPIO_Pin_3; GPIO_InitStruce.GPIO_Speed=GPIO_Speed_50MHz; GPIO_Init(GPIOE, &GPIO_InitStruce); GPIO_InitStruce.GPIO_Mode=GPIO_Mode_IPD;//下拉输入 GPIO_InitStruce.GPIO_Pin=GPIO_Pin_0; GPIO_InitStruce.GPIO_Speed=GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStruce); }
增加:RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO , ENABLE);
该函数存在于:stm32f10x_gpio.h中
增加:GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource4);
该函数存在于:stm32f10x_exti.h中
增加:
EXTI_InitTypeDef EXTI_InitStruce; GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource4); EXTI_InitStruce.EXTI_Line=EXTI_Line4; //确定中断线 EXTI_InitStruce.EXTI_LineCmd=ENABLE;//使能 EXTI_InitStruce.EXTI_Mode=EXTI_Mode_Interrupt;//模式:中断 EXTI_InitStruce.EXTI_Trigger=EXTI_Trigger_Falling;//触发方式(根据按键电路图,选择下降沿触发) EXTI_Init(&EXTI_InitStruce);
该函数存在于:misc.h中
通道的参数是在stm32f10x,h中找到IRQn结尾的。
增加:
EXTI_Init(&EXTI_InitStruce); NVIC_InitTypeDef NVIC_InitStruce; NVIC_InitStruce.NVIC_IRQChannel=EXTI4_IRQn ;//设置通道 NVIC_InitStruce.NVIC_IRQChannelCmd = ENABLE;//使能 NVIC_InitStruce.NVIC_IRQChannelPreemptionPriority=2;//设置优先级 NVIC_InitStruce.NVIC_IRQChannelSubPriority=2;//设置子优先级 NVIC_Init(&NVIC_InitStruce);
这边要注意,在这同时要在main.c中先写下该函数NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置NVIC中断分组,2位抢占优先级,2位响应优先级
,设置优先级后,这边填写的抢占优先级才有效。
该函数在启动文件CORE文件夹下的startup_stm32f10x_hd.s中:EXTI4_IRQHandler
故可在exti,c文件中编写一下程序:
void EXTI4_IRQHandler(void){
delay_ms(10);//防抖延时 if(KEY0 == 0){
LED0 = !LED0; LED1 = !LED1; } EXTI_ClearITPendingBit(EXTI_Line4);//手动清除标志位,exti.h中 }
exti.c文件:
#include "stm32f10x.h" #include "delay.h" #include "exti.h" void EXTIX_Init(void){
GPIO_InitTypeDef GPIO_InitStruce; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_GPIOE , ENABLE);//IO口时钟 GPIO_InitStruce.GPIO_Mode=GPIO_Mode_IPU;//上拉输入 GPIO_InitStruce.GPIO_Pin=GPIO_Pin_4|GPIO_Pin_3;//KEY0|KEY1 GPIO_InitStruce.GPIO_Speed=GPIO_Speed_50MHz; GPIO_Init(GPIOE, &GPIO_InitStruce); GPIO_InitStruce.GPIO_Mode=GPIO_Mode_IPD;//下拉输入 GPIO_InitStruce.GPIO_Pin=GPIO_Pin_0;;//KEY UP GPIO_InitStruce.GPIO_Speed=GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStruce); RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO , ENABLE);//IO口复用时钟 EXTI_InitTypeDef EXTI_InitStruce; GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource4); EXTI_InitStruce.EXTI_Line=EXTI_Line4; //确定中断线 EXTI_InitStruce.EXTI_LineCmd=ENABLE;//使能 EXTI_InitStruce.EXTI_Mode=EXTI_Mode_Interrupt;//模式:中断 EXTI_InitStruce.EXTI_Trigger=EXTI_Trigger_Falling;//触发方式(根据按键电路图,选择下降沿触发) EXTI_Init(&EXTI_InitStruce); NVIC_InitTypeDef NVIC_InitStruce; NVIC_InitStruce.NVIC_IRQChannel=EXTI4_IRQn;//设置通道 NVIC_InitStruce.NVIC_IRQChannelCmd = ENABLE;//使能 NVIC_InitStruce.NVIC_IRQChannelPreemptionPriority=2;//设置优先级 NVIC_InitStruce.NVIC_IRQChannelSubPriority=2;//设置子优先级 NVIC_Init(&NVIC_InitStruce); GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOE, ENABLE); GPIO_InitStructure.GPIO_Mode= GPIO_Mode_Out_PP; //模式选择推挽输出 GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5; //引脚选择5 GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; //速度旋转50MHz GPIO_Init(GPIOB, &GPIO_InitStructure); //第一个参数GPIOB //第二个参数是一个结构体变量,要用&去取,且该结构体中有三个变量 //要根据assert_param中去确定要填写什么样的格式数值。 GPIO_SetBits(GPIOB, GPIO_Pin_5); GPIO_InitStructure.GPIO_Mode= GPIO_Mode_Out_PP; //模式选择推挽输出 GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5; //引脚选择5 GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; //速度旋转50MHz GPIO_Init(GPIOE, &GPIO_InitStructure); //第一个参数GPIOE //第二个参数是一个结构体变量,要用&去取,且该结构体中有三个变量 //要根据assert_param中去确定要填写什么样的格式数值。 GPIO_SetBits(GPIOE, GPIO_Pin_5); } void EXTI4_IRQHandler(void){
delay_ms(10);//防抖延时 if(KEY0 == 0){
LED0 = !LED0; LED1 = !LED1; } EXTI_ClearITPendingBit(EXTI_Line4);//手动清除标志位,exti.h中 }
exti.h
#ifndef __EXIT_H #define __EXIT_H #include "sys.h" void EXTIX_Init(void); //外部中断初始化 #define KEY0 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4) #define KEY1 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3) #define WK_UP GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0) #define LED0 PBout(5) // DS0 #define LED1 PEout(5) // DS1 #endif
main.c
#include "stm32f10x.h" //stm32必要的头文件 #include "delay.h" #include "exti.h" int main(void){
delay_init(); NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置NVIC中断分组,2位抢占优先级,2位响应优先级 EXTIX_Init(); LED0 = 0; GPIO_SetBits(GPIOB, GPIO_Pin_5); GPIO_SetBits(GPIOE, GPIO_Pin_5); while(1){
delay_ms(1000); } }
按下按键KEY0实现两个LED的点亮与熄灭。