参考资料
开发板配套资料
《STM32Fx开发指南-HAL库版本》-第x章 外部中断实验
STM32Fxxx官方资料:
《STM32Fxx中文参考手册》- 中断和事件
笔记基于正点原子官方视频
视频连接https://www.bilibili.com/video/BV1Wx411d7wT?p=71&spm_id_from=333.1007.top_right_bar_window_history.content.click
如有侵权,联系删除
① STM32的每个
IO都可以作为外部中断输入。
② 每个外部中断线
可以独立的配置触发方式(上升沿
,下降沿
或者双边沿
触发),触发/屏蔽,专用的状态位。
③ STM32供IO使用的中断线只有16个,但是STM32F系列的IO口多达上百个,STM32F103ZGT6(112个IO口),那么中断线怎么跟IO口对应呢?
在位置:H:\2技术书籍\STM32F429开发指南中书《STM32F4xx中文参考手册》8.2.4小节及以后几小节有相关寄存器的详细介绍。
其中:
SYSCFG 外部中断配置寄存器 1 的低16位配置的是 EXTI0 至 EXTI3
SYSCFG 外部中断配置寄存器 2 的低16位配置的是 EXTI4 至 EXTI7
SYSCFG 外部中断配置寄存器 3 的低16位配置的是 EXTI8 至 EXTI11
SYSCFG 外部中断配置寄存器 4 的低16位配置的是 EXTI12 至 EXTI15
例如:
在 EXTI0 对应的四个位填入 0000,则配置的是 PA[0] 引脚
在 EXTI2 对应的四个位填入 0000,则配置的是 PA[2] 引脚
在 EXTI6 对应的四个位填入 0011,则配置的是 PD[6] 引脚
在 EXTI12 对应的四个位填入 0110,则配置的是 PG[12] 引脚
在 EXTI15 对应的四个位填入 0101,则配置的是 PF[15] 引脚
从表中可以看出,外部中断线5~9分配一个中断向量,共用一个服务函数外部中断线10~15分配一个中断向量,共用一个中断服务函数。
EXTI0_IRQHandler
EXTI1_IRQHandler
EXTI2_IRQHandler
EXTI3_IRQHandler
EXTI4_IRQHandler
EXTI9_5_IRQHandler
EXTI15_10_IRQHandler
映射配置
和触发方式
都是在GPIO初始化函数中完成: GPIO_InitTypeDef GPIO_Initure;
GPIO_Initure.Pin=GPIO_PIN_0; //PA0
GPIO_Initure.Mode=GPIO_MODE_IT_RISING; //上升沿触发
GPIO_Initure.Pull=GPIO_PULLDOWN;
HAL_GPIO_Init(GPIOA,&GPIO_Initure);
在上面代码配置GPIO_Initure.Mode时,里面模式的子代码里就有关于触发方式的代码。
在位置:工程模板 - HARDWARE - exit.c 文件中有关于触发方式的代码,如下图:
//中断服务函数
void EXTI0_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0); //调用中断处理公用函数
}
void EXTI2_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_2); //调用中断处理公用函数
}
void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
{
if(__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != RESET)
{
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);
HAL_GPIO_EXTI_Callback(GPIO_Pin);
}
}
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
switch(GPIO_Pin)
{
case GPIO_PIN_0:
//控制逻辑
break;
case GPIO_PIN_2:
//控制逻辑
break;
}
}
① 使能IO口时钟。
② 初始化IO口,设置触发方式:HAL_GPIO_Init();
③ 设置中断优先级,并使能中断通道。
④ 编写中断服务函数:函数中调用外部中断通用处理函数HAL_GPIO_EXTI_IRQHandler。
⑥ 编写外部中断回调函数:HAL_GPIO_EXTI_Callback;
KEY0->PH3 上拉输入,下降沿触发
KEY1->PH2 上拉输入,下降沿触发
KEY2->PC13 上拉输入,下降沿触发
WK_UP->PA0 下拉输入,上升沿触发
实现的功能:
按键KEY0按下: 同时控制LED0和LED1翻转。
按键KEY1按下: LED1状态翻转。
按键KEY2按下: LED0翻转。
按键WK_UP按下:控制LED0和LED1互斥点亮。
这里使用之前的按键输入实验作为模板进行外部中断实验
在HARDWARE文件夹下面创建exti.c和exti.h,并添加到工程中
因为使用到了PH3, PH2 , PC13,PA0四个引脚,所以要分别对这四个引脚编写中断服务函数
中断服务列表
EXTI0_IRQHandler
EXTI1_IRQHandler
EXTI2_IRQHandler
EXTI3_IRQHandler
EXTI4_IRQHandler
EXTI9_5_IRQHandler
EXTI15_10_IRQHandler
可见用到了EXTI0_IRQHandler、EXTI2_IRQHandler、EXTI3_IRQHandler、EXTI15_10_IRQHandler 四个句柄函数,
同样在位置:工程文件 - CORE - starup_stm32f429xx.s中也可以看到配置好的句柄函数
在位置:工程文件 - HALLIB - stm32f4xx_hal.c - stm32f4xx_hal_gpio.h 文件中
至此exti.c文件框架搭建完毕
#include “exti.h”
void EXTI_Init(void)
{
}
void EXTI0_IRQHandler(void)
{
}
void EXTI2_IRQHandler(void)
{
}
void EXTI3_IRQHandler(void)
{
}
void EXTI15_10_IRQHandler(void)
{
}
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
}
按键模式配置
KEY0->PH3 上拉输入,下降沿触发
KEY1->PH2 上拉输入,下降沿触发
KEY2->PC13 上拉输入,下降沿触发
WK_UP->PA0 下拉输入,上升沿触发
因为用到了PA、PC、PH三个IO口,所以要对这三个口进行初始化,然后对IO口模式进行配置,具体使能方式前文按键配置有详细说明。
代码如下
GPIO_InitTypeDef GPIO_Initure;
__HAL_RCC_GPIOA_CLK_ENABLE(); //开启GPIOA时钟
__HAL_RCC_GPIOC_CLK_ENABLE(); //开启GPIOC时钟
__HAL_RCC_GPIOH_CLK_ENABLE(); //开启GPIOH时钟
GPIO_Initure.Pin=GPIO_PIN_0; //PA0
GPIO_Initure.Mode=GPIO_MODE_IT_RISING; //上升沿触发
GPIO_Initure.Pull=GPIO_PULLDOWN; //下拉
GPIO_Initure.Speed=GPIO_SPEED_HIGH; //高速
HAL_GPIO_Init(GPIOA,&GPIO_Initure);
GPIO_Initure.Pin=GPIO_PIN_13; //PC13
GPIO_Initure.Mode=GPIO_MODE_IT_FALLING; //下降沿触发
GPIO_Initure.Pull=GPIO_PULLUP; //上拉
GPIO_Initure.Speed=GPIO_SPEED_HIGH; //高速
HAL_GPIO_Init(GPIOC,&GPIO_Initure);
GPIO_Initure.Pin=GPIO_PIN_2|GPIO_PIN_3; //PH2,3
GPIO_Initure.Mode=GPIO_MODE_IT_FALLING; //下降沿触发
GPIO_Initure.Pull=GPIO_PULLUP; //上拉
GPIO_Initure.Speed=GPIO_SPEED_HIGH; //高速
HAL_GPIO_Init(GPIOH,&GPIO_Initure);
其中共调用了4个中断,所以要配置4条中断线
在位置:工程文件 - HALLIB - stm32f4xx_hal.c - stm32f429xx.h 文件中有中断线函数的头文件名
配置在 HAL_NVIC_SetPriority(EXTI0_IRQn
,2,0); 和 HAL_NVIC_EnableIRQ(EXTI0_IRQn
); 中
//中断线0-PA0
HAL_NVIC_SetPriority(EXTI0_IRQn,2,0); //抢占优先级为2,子优先级为0
HAL_NVIC_EnableIRQ(EXTI0_IRQn); //使能中断线0
//中断线2-PH2
HAL_NVIC_SetPriority(EXTI2_IRQn,2,1); //抢占优先级为2,子优先级为1
HAL_NVIC_EnableIRQ(EXTI2_IRQn); //使能中断线2
//中断线3-PH3
HAL_NVIC_SetPriority(EXTI3_IRQn,2,2); //抢占优先级为2,子优先级为2
HAL_NVIC_EnableIRQ(EXTI3_IRQn); //使能中断线2
//中断线13-PC13
HAL_NVIC_SetPriority(EXTI15_10_IRQn,2,3); //抢占优先级为2,子优先级为3
HAL_NVIC_EnableIRQ(EXTI15_10_IRQn); //使能中断线13
至此,外部中断初始化函数编写完毕,代码如下
void EXTI_Init(void) //外部中断初始化
{
GPIO_InitTypeDef GPIO_Initure;
__HAL_RCC_GPIOA_CLK_ENABLE(); //开启GPIOA时钟
__HAL_RCC_GPIOC_CLK_ENABLE(); //开启GPIOC时钟
__HAL_RCC_GPIOH_CLK_ENABLE(); //开启GPIOH时钟
GPIO_Initure.Pin=GPIO_PIN_0; //PA0
GPIO_Initure.Mode=GPIO_MODE_IT_RISING; //上升沿触发
GPIO_Initure.Pull=GPIO_PULLDOWN; //下拉
GPIO_Initure.Speed=GPIO_SPEED_HIGH; //高速
HAL_GPIO_Init(GPIOA,&GPIO_Initure);
GPIO_Initure.Pin=GPIO_PIN_13; //PC13
GPIO_Initure.Mode=GPIO_MODE_IT_FALLING; //下降沿触发
GPIO_Initure.Pull=GPIO_PULLUP; //上拉
GPIO_Initure.Speed=GPIO_SPEED_HIGH; //高速
HAL_GPIO_Init(GPIOC,&GPIO_Initure);
GPIO_Initure.Pin=GPIO_PIN_2|GPIO_PIN_3; //PH2,3
GPIO_Initure.Mode=GPIO_MODE_IT_FALLING; //下降沿触发
GPIO_Initure.Pull=GPIO_PULLUP; //上拉
GPIO_Initure.Speed=GPIO_SPEED_HIGH; //高速
HAL_GPIO_Init(GPIOH,&GPIO_Initure);
//中断线0-PA0
HAL_NVIC_SetPriority(EXTI0_IRQn,2,0); //抢占优先级为2,子优先级为0
HAL_NVIC_EnableIRQ(EXTI0_IRQn); //使能中断线0
//中断线2-PH2
HAL_NVIC_SetPriority(EXTI2_IRQn,2,1); //抢占优先级为2,子优先级为1
HAL_NVIC_EnableIRQ(EXTI2_IRQn); //使能中断线2
//中断线3-PH3
HAL_NVIC_SetPriority(EXTI3_IRQn,2,2); //抢占优先级为2,子优先级为2
HAL_NVIC_EnableIRQ(EXTI3_IRQn); //使能中断线2
//中断线13-PC13
HAL_NVIC_SetPriority(EXTI15_10_IRQn,2,3); //抢占优先级为2,子优先级为3
HAL_NVIC_EnableIRQ(EXTI15_10_IRQn); //使能中断线13
}
在位置:工程文件 - HALLIB - stm32f4xx_hal.c - stm32f4xx_hal_gpio.h 文件中也有其定义句柄函数名
把该句柄函数放在中断服务器函数中,代码如下
void EXTI0_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0); //调用中断处理公用函数
}
void EXTI2_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_2); //调用中断处理公用函数
}
void EXTI3_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_3); //调用中断处理公用函数
}
void EXTI15_10_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_13); //调用中断处理公用函数
}
因为在HAL库中,有关于位带操作的定义,所以可以直接使用LED0、LED1、KEY0、KEY1、KEY2、WK_UP变量名来编写函数
编写好按键控制逻辑,代码如下
#include "delay.h"
#include "led.h"
#include "key.h"
//中断服务程序中需要做的事情
//在HAL库中所有的外部中断服务函数都会调用此函数
//GPIO_Pin:中断引脚号
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
delay_ms(100); //消抖
switch(GPIO_Pin)
{
case GPIO_PIN_0:
if(WK_UP==1)
{
LED1=!LED1;//控制LED0,LED1互斥点亮
LED0=!LED1;
}
break;
case GPIO_PIN_2:
if(KEY1==0) //LED1翻转
{
LED1=!LED1;
}
break;
case GPIO_PIN_3:
if(KEY0==0) //同时控制LED0,LED1翻转
{
LED0=!LED0;
LED1=!LED1;
}
break;
case GPIO_PIN_13:
if(KEY2==0)
{
LED0=!LED0;//控制LED0翻转
}
break;
}
}
至此,exti.c文件编写完毕,代码如下:
#include "exti.h"
#include "delay.h"
#include "led.h"
#include "key.h"
void EXTI_Init(void) //外部中断初始化
{
GPIO_InitTypeDef GPIO_Initure;
__HAL_RCC_GPIOA_CLK_ENABLE(); //开启GPIOA时钟
__HAL_RCC_GPIOC_CLK_ENABLE(); //开启GPIOC时钟
__HAL_RCC_GPIOH_CLK_ENABLE(); //开启GPIOH时钟
GPIO_Initure.Pin=GPIO_PIN_0; //PA0
GPIO_Initure.Mode=GPIO_MODE_IT_RISING; //上升沿触发
GPIO_Initure.Pull=GPIO_PULLDOWN; //下拉
GPIO_Initure.Speed=GPIO_SPEED_HIGH; //高速
HAL_GPIO_Init(GPIOA,&GPIO_Initure);
GPIO_Initure.Pin=GPIO_PIN_13; //PC13
GPIO_Initure.Mode=GPIO_MODE_IT_FALLING; //下降沿触发
GPIO_Initure.Pull=GPIO_PULLUP; //上拉
GPIO_Initure.Speed=GPIO_SPEED_HIGH; //高速
HAL_GPIO_Init(GPIOC,&GPIO_Initure);
GPIO_Initure.Pin=GPIO_PIN_2|GPIO_PIN_3; //PH2,3
GPIO_Initure.Mode=GPIO_MODE_IT_FALLING; //下降沿触发
GPIO_Initure.Pull=GPIO_PULLUP; //上拉
GPIO_Initure.Speed=GPIO_SPEED_HIGH; //高速
HAL_GPIO_Init(GPIOH,&GPIO_Initure);
//中断线0-PA0
HAL_NVIC_SetPriority(EXTI0_IRQn,2,0); //抢占优先级为2,子优先级为0
HAL_NVIC_EnableIRQ(EXTI0_IRQn); //使能中断线0
//中断线2-PH2
HAL_NVIC_SetPriority(EXTI2_IRQn,2,1); //抢占优先级为2,子优先级为1
HAL_NVIC_EnableIRQ(EXTI2_IRQn); //使能中断线2
//中断线3-PH3
HAL_NVIC_SetPriority(EXTI3_IRQn,2,2); //抢占优先级为2,子优先级为2
HAL_NVIC_EnableIRQ(EXTI3_IRQn); //使能中断线2
//中断线13-PC13
HAL_NVIC_SetPriority(EXTI15_10_IRQn,2,3); //抢占优先级为2,子优先级为3
HAL_NVIC_EnableIRQ(EXTI15_10_IRQn); //使能中断线13
}
void EXTI0_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0); //调用中断处理公用函数
}
void EXTI2_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_2); //调用中断处理公用函数
}
void EXTI3_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_3); //调用中断处理公用函数
}
void EXTI15_10_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_13); //调用中断处理公用函数
}
//中断服务程序中需要做的事情
//在HAL库中所有的外部中断服务函数都会调用此函数
//GPIO_Pin:中断引脚号
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
delay_ms(100); //消抖
switch(GPIO_Pin)
{
case GPIO_PIN_0:
if(WK_UP==1)
{
LED1=!LED1;//控制LED0,LED1互斥点亮
LED0=!LED1;
}
break;
case GPIO_PIN_2:
if(KEY1==0) //LED1翻转
{
LED1=!LED1;
}
break;
case GPIO_PIN_3:
if(KEY0==0) //同时控制LED0,LED1翻转
{
LED0=!LED0;
LED1=!LED1;
}
break;
case GPIO_PIN_13:
if(KEY2==0)
{
LED0=!LED0;//控制LED0翻转
}
break;
}
}
至此,main.c函数编写完毕,代码如下
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "exti.h"
int main(void)
{
HAL_Init(); //初始化HAL库
Stm32_Clock_Init(360,25,2,8); //设置时钟,180Mhz
delay_init(180); //初始化延时函数
uart_init(115200); //初始化USART
LED_Init(); //初始化LED
EXTI_Init(); //外部中断初始化
while(1)
{
printf("OK\r\n"); //打印OK提示程序运行
delay_ms(1000); //每隔1s打印一次
}
}
现象这里不再赘述
至此,IO口外部中断实验结束