stm32f407关于外部中断各种函数(一)(HAL库)

以下内容全部来自正点原子,本人只是对主要知识点进行整理,方便以后查看。

一、串口配置

想要使用外部中断就需要对针脚进行配置,具体串口配置方法可以看stm32f407关于串口配置的各种函数(一)(基于HAL库)

比如对F端口10引脚设置中断:

 
void extix_init(void)
{

   GPIO_InitTypeDef gpio_init_struct;
    
   
    gpio_init_struct.Pin = GPIO_PIN_10;
    gpio_init_struct.Mode = GPIO_MODE_IT_FALLING;            /* 下降沿触发 */
    gpio_init_struct.Pull = GPIO_PULLUP;

    HAL_GPIO_Init(GPIOF, &gpio_init_struct);    /* 配置为下降沿触发中断 */

}

二、外部中断相关知识

中断就是在程序运行时突然收到中断请求,去运行中断函数,运行完中断函数后返回原来被中断的地方继续正常运行函数。

2.1可用外部中断口(EXTI)

EXTI 支持 23 个外部中断/事件请求,这些都是信息输入端, 具体如下:

EXTI 线 0~15:对应外部 IO 口的输入中断

EXTI 线 16:连接到 PVD 输出

EXTI 线 17:连接到 RTC 闹钟事件

EXTI 线 18:连接到 USB 唤醒事件

EXTI 线 19:连接到以太网唤醒事件

EXTI 线 20:连接到 USB OTG HS(在 FS 中配置)唤醒事件

EXTI 线 21:连接到 RTC 入侵和时间戳事件

EXTI 线 22:连接到 RTC 唤醒事件

用户经常可以用到的外部中断有16个,分别为EXTI0~EXTI15,正好对应引脚GPIO_PIN_0~GPIO_PIN_15。

我们知道,不同端口都有16个引脚,那么每个端口的不同引脚都是独立的外部中断吗?当然不是。GPIOA_0(表示A端口0引脚)的中断和GPIOB_0、GPIOC_0等等不同端口的引脚0用的都是同一个EXTI0中断,总的来说就是:如果我对GPIOA_0配置了中断,那么GPIOB_0就不能配置中断;对GPIOA_5配置了中断,那么GPIOF_5就不能配置中断。

STM32F407 的 IO 口外部中断函数只有 7 个,分别为:

void EXTI0_IRQHandler();
void EXTI1_IRQHandler();
void EXTI2_IRQHandler();
void EXTI3_IRQHandler();
void EXTI4_IRQHandler();
void EXTI9_5_IRQHandler();
void EXTI15_10_IRQHandler();

中断线0-4,每个中断线对应一个中断函数,中断线5-9共用中断函数EXTI9_5_IRQHandler, 中断线 10-15 共用中断函数 EXTI15_10_IRQHandler

2.2外部中断配置的相关函数

中断使能函数:配置中断首先需要将中断进行使能:HAL_NVIC_EnableIRQ(EXTIx_IRQn)

比如对中断0进行使能

HAL_NVIC_EnableIRQ(EXTI0_IRQn);

 中断失能函数:用于对已经使能的中断进行失能

HAL_NVIC_DisableIRQ(EXTI0_IRQn);

系统复位函数:用于对系统进行复位(没有输入函数)

HAL_NVIC_SystemReset();

中断优先级分组函数:void HAL_NVIC_SetPriorityGrouping(uint32_t PriorityGroup)

可以选择范围:NVIC_PRIORITYGROUP_0 到 NVIC_PRIORITYGROUP_4(共 5 组)。

不同分组的主优先级(也叫抢占优先级)和子优先级(也叫响应优先级)的等级数如下:

优先级分组

主优先级

子优先级

描述

NVIC_PriorityGroup_0

0

0-15

主-0bit,子-4bit

NVIC_PriorityGroup_1

0-1

0-7

主-1bit,子-3bit

NVIC_PriorityGroup_2

0-3

0-3

主-2bit,子-2bit

NVIC_PriorityGroup_3

0-7

0-1

主-3bit,子-1bit

NVIC_PriorityGroup_4

0-15

0

主-4bit,子-0bit

例如将优先级分组设为2级:

HAL_NVIC_SetPriorityGrouping(NVIC_PriorityGroup_2)

设定为分组2后,可以使用的主优先级就只有0~3,子优先级也只有0~3。

注:HAL的初始化函数 HAL_Init() 已经将优先级分组默认设为了第4组

 中断优先级函数:void HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t PreemptPriority, uint32_t SubPriority);

比如需要EXTI0的抢占优先级设为2,响应优先级设为1,则代码为:

HAL_NVIC_SetPriority(EXTI0_IRQn, 2, 1);   

注:在正点原子的实例中,并没有对中断优先级分组进行配置,也就是说它的分组是用的默认的4分组,4分组响应优先级应该只有0才对,但是在它对中断优先级进行配置时用的响应优先级为2,是它出错了,还是它在什么地方重新分组了但是我没有发现?

2.3、中断服务函数和HAL库的中断服务函数

中断服务函数就是产生中断后要执行的那个函数。

中断标志位清理函数:__HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin); 

系统产生中断后会将相关中断标志位设为1,系统会根据标志位运动中断服务函数,所以需要在运行中断服务函数时需要先将标志位清零,否则此中断会一直运行。

stm32有它自身的中断服务函数:EXTIx_IRQHandler();x为中断代号,比如EXTI0的中断服务函数就是:

void EXTI0_IRQHandler(void)
{
/*写上需要执行的代码*/
}

HAL为了方便,也对中断要进行的函数进行了封装,函数为:HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)

其定义为:

void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
{
  /* EXTI line interrupt detected */
  if(__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != RESET)
  {
    __HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);
    HAL_GPIO_EXTI_Callback(GPIO_Pin);
  }
}

可以发现,在HAL提供的函数中,先将中断标志位清零,然后执行HAL_GPIO_EXTI_Callback(GPIO_Pin);函数,用户可以在HAL_GPIO_EXTI_Callback(GPIO_Pin)函数中编写自己需要的代码。

比如我的中断是引脚0引起的,我的中断可以写成:

void EXTI0_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);
}


void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
/*写上需要执行的代码*/
}

产生中断后执行顺序为:

可以发现,其实在 EXTI0_IRQHandler(void)函数中就可以直接先清理标志位,然后再执行自己想要执行的代码。

这里的代码却是先转到函数HAL_GPIO_EXTI_IRQHandler(GPIO_PIN),HAL_GPIO_EXTI_IRQHandler(GPIO_PIN)函数里面清理标志位,再转到函数HAL_GPIO_EXTI_Callback(GPIO_PIN),HAL_GPIO_EXTI_Callback(GPIO_PIN)中才是自己想要执行的代码,这样确实是复杂化了,但是这里的中断只配置了一个,当需要配置很多中断,并且不同中断直接需要执行同一个函数的时候,这种嵌套结构反而会降低编程量。

三、实战篇

现在有两个按键,分别连接在GPIOA_0和GPIOA_1,还有两个led灯,分别连接在GPIOF_10和GPIOG_11,现在要求用中断写一个函数,当KEY1按下时LED1灯状态反转,当KEY2按下时LED2灯状态反转。

#include "stm32f4xx.h"
#include "core_cm4.h"
#include "stm32f4xx_hal.h"
#include "stdio.h"

/*  ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓初始化led↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓   */
void led_int(void)
{

    GPIO_InitTypeDef gpio_init_struct;                      /* 定义结构体 */
   
    gpio_init_struct.Pin = GPIO_PIN_10;                     /* LED1引脚 */
    gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;            /* 推挽输出 */
    gpio_init_struct.Pull = GPIO_PULLUP;                    /* 上拉 */
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;          /* 高速 */
 
    HAL_GPIO_Init(GPIOF, &gpio_init_struct);                /* 初始化LED1引脚 */


    gpio_init_struct.Pin = GPIO_PIN_11;                     /* LED2引脚 */

    HAL_GPIO_Init(GPIOF, &gpio_init_struct);                /* 初始化LED2引脚 */
}


/*  ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓对按键的中断进行初始化↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓   */
void extix_init(void)
{
    GPIO_InitTypeDef gpio_init_struct;
    
    gpio_init_struct.Pin = GPIO_PIN_0;
    gpio_init_struct.Mode = GPIO_MODE_IT_FALLING;            /* 下降沿触发 */
    gpio_init_struct.Pull = GPIO_PULLUP;
    HAL_GPIO_Init(GPIOA, &gpio_init_struct);    /* KEY0配置为下降沿触发中断 */

    gpio_init_struct.Pin = GPIO_PIN_1;
    gpio_init_struct.Mode = GPIO_MODE_IT_FALLING;            /* 下降沿触发 */
    gpio_init_struct.Pull = GPIO_PULLUP;
    HAL_GPIO_Init(GPIOA, &gpio_init_struct);    /* KEY1配置为下降沿触发中断 */
  

    HAL_NVIC_SetPriority(EXTI0_IRQn, 0, 2);               /* 抢占0,子优先级2 */
    HAL_NVIC_EnableIRQ(EXTI0_IRQn);                       /* 使能中断线0 */

    HAL_NVIC_SetPriority(EXTI1_IRQn, 1, 2);               /* 抢占1,子优先级2 */
    HAL_NVIC_EnableIRQ(EXTI1_IRQn);                       /* 使能中断线1 */
    
}

/*  ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓对中断0进行初始化↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓   */
void EXTI0_IRQHandler(void)
{
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);         /* 调用中断处理公用函数 清除KEY0所在中断线 的中断标志位 */
    __HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_0);         /* HAL库默认先清中断再处理回调,退出时再清一次中断,避免按键抖动误触发 */
}


/*  ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓对中断1进行初始化↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓   */
void EXTI1_IRQHandler(void)
{
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_1);         /* 调用中断处理公用函数 清除KEY0所在中断线 的中断标志位 */
    __HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_1);         /* HAL库默认先清中断再处理回调,退出时再清一次中断,避免按键抖动误触发 */
}

/*  ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓HAL的回调函数↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓   */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
 
    delay_ms(20);      /* 消抖 */
    switch(GPIO_Pin)
        {

            case GPIO_PIN_0:
                
                if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0)==0)     /* 判断按键是不是按下了 */
                    {
                        HAL_GPIO_TogglePin(GPIOF, GPIO_PIN_10);
                    }
                break;   

            case GPIO_PIN_1:
                
                if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1)==0)     /* 判断按键是不是按下了 */
                    {
                        HAL_GPIO_TogglePin(GPIOF, GPIO_PIN_11);
                    }
                break;   

            default : break;
        }
}

int main(void)
{

    HAL_Init();                                    /* 初始化HAL库 */ 
    __HAL_RCC_GPIOA_CLK_ENABLE();                  /* 使能A端口时钟 */
    __HAL_RCC_GPIOF_CLK_ENABLE();                  /* 使能F端口时钟 */
    led_init();                                /* 对LED串口进行初始化*/
    extix_init;                                /* 对KEY串口进行初始化*/

    HAL_GPIO_WritePin(GPIOF,GPIO_PIN_10,GPIO_PIN_RESET);/* 将LED初始化*/
    HAL_GPIO_WritePin(GPIOF,GPIO_PIN_11,GPIO_PIN_RESET);
 
    while(1)
    {
        delay_ms(1000);
    }
}

注:代码中用的delay函数是用的正点原子自带的,这里用来表示一下,具体delay函数在后面时钟才会学到。需要特地注意的是,一般用的delay函数都是通过时钟中断实现的,但是在按键中断中用时钟中断会造成两个中断冲突,所以一般来说,在一个中断中使用其他中断要非常注意两个中断的抢占优先级。

正点原子这里的delay函数并不是用中断实现的,而是通过监视时钟参数实现的,后面会学到。

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值