STM32F4xx NVIC与EXTI中断(HAL库)

概述

 使用STM32F407EXTI中断功能需要使用HAL库中的stm32f4xx_hal_exti.hstm32f4xx_hal_exti.c.
STM32F4的每个GPIO都可设置为 EXTI中断 功能。STM32F416EXTI中断线EXTI0~EXTI15,所有GPIOxGPIO_PIN_0对应EXTI0,所有GPIOxGPIO_PIN_1对应EXTI1 … 所有GPIOxGPIO_PIN_15对应EXTI15.

NVIC配置

 关于NVIC中断管理的函数定义在stm32f4xx_hal_cortex.c中。

中断优先级分组

/* 设置全局中断优先级分组,此函数只需调用一次
   参数:
   NVIC_PRIORITYGROUP_0
   NVIC_PRIORITYGROUP_1
   NVIC_PRIORITYGROUP_2
   NVIC_PRIORITYGROUP_3
   NVIC_PRIORITYGROUP_4 */
void HAL_NVIC_SetPriorityGrouping(uint32_t PriorityGroup);
PriorityGroupPreemptPrioritySubPriority
NVIC_PRIORITYGROUP_0none0~15
NVIC_PRIORITYGROUP_10~10~7
NVIC_PRIORITYGROUP_20~30~3
NVIC_PRIORITYGROUP_30~70~1
NVIC_PRIORITYGROUP_40~15none

设置中断优先级

/* 设置指定中断事件的 抢占优先级 和 响应优先级
   参数:
   (IRQ_Type)IRQn - 中断名
   (uint32_t)PreemptPriority - 抢占优先级
   (uint32_t)SubPriority - 响应优先级 */
void HAL_NVIC_SetPriority(IRQn_Type IRQn,uint32_t PreemptPriority,uint32_t SubPriority);

使能中断

/* 开启指定中断 */
void HAL_NVIC_EnableIRQ(IRQn_Type IRQn); 
/* 关闭指定中断 */
void HAL_NVIC_DisableIRQ(IRQn_Type IRQn);

抢占优先级和响应优先级

STM32单片机的优先级有两个属性:抢占优先级 和 响应优先级(又称子优先级),优先级编号越小其优先级越高。
抢占优先级(PreemptPriority)高抢占优先级的中断程序可以打断低抢占优先级的中断程序。即如果低抢占优先级的中断程序正在运行,此时发生高抢占优先级的中断事件,高抢占优先级的中断程序可以打断正在运行的低抢占优先级程序。
响应优先级(SubPriority)当抢占优先级相同时,高响应优先级的中断程序 先于 低响应优先级的中断程序执行。响应优先级只能处理同时发生的中断事件,而不能打断正在处理的中断事件
如果两个中断事件的抢占优先级和响应优先级都相同,先发生的中断事件先被处理

总结:抢占优先级(PreemptPriority)用于管理中断事件的处理权,未开始执行的中断程序(高抢占优先级)可以抢占正在执行的中断程序(低抢占优先级)的执行权。响应优先级(SubPriority)用于管理同时发生的中断事件的处理顺序,未开始执行的中断程序不能打断正在执行的中断程序

GPIO配置

GPIO初始化

GPIO_InitTypeDef GPIO_Init;

/* GPIOx时钟初始化。注意,IO中断仅需初始化对应GPIO的时钟即可 */
__HAL_RCC_GPIOx_CLK_ENABLE();

GPIO_Init.Pin = GPIO_PIN_0;

/* GPIO_MODE_IT_RISING (外部事件,上升沿触发);
   GPIO_MODE_IT_FALLING (外部事件,下降沿触发);
   GPIO_MODE_IT_RISING_FALLING (外部事件,上升沿 或 下降沿触发). */
// 上升沿触发
GPIO_Init.Mode = GPIO_MODE_IT_RISING;

// 上升沿触发,可选择下拉或者无拉
GPIO_Init.Pull = GPIO_PULLDOWN;
GPIO_Init.Speed = GPIO_SPEED_HIGH;
HAL_GPIO_Init(GPIOC,&GPIO_Init);

EXTI中断事件名

typedef enum {
    ...
	EXTI0_IRQn = 6,          //IO中断事件0, 对应 GPIOx 的 GPIO_PIN_0
	EXTI1_IRQn = 7,          //IO中断事件1, 对应 GPIOx 的 GPIO_PIN_1
	EXTI2_IRQn = 8,          //IO中断事件2, 对应 GPIOx 的 GPIO_PIN_2 
	EXTI3_IRQn = 9,          //IO中断事件3, 对应 GPIOx 的 GPIO_PIN_3
	EXTI4_IRQn = 10,         //IO中断事件4, 对应 GPIOx 的 GPIO_PIN_4
	EXTI9_5_IRQn = 23,       //IO中断事件5~9, 对应 GPIOx 的 GPIO_PIN_5 ~ GPIO_PIN_9 
	EXTI15_10_IRQn = 40,     //IO中断事件10~15, 对应 GPIOx 的 GPIO_PIN_10 ~ GPIO_PIN_15
    ...
} IRQn_Type

示例:

HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_1);
HAL_NVIC_SetPriority(EXTI0_IRQn,0,0);
HAL_NVIC_EnableIRQ(EXTI0_IRQn);

判断触发中断的GPIO_Pin

 对于GPIO_PIN_5 ~ GPIO_PIN_9GPIO_PIN_10 ~ GPIO_PIN_15,中断服务函数仅有EXTI9_5_IRQHandler()EXTI15_10_IRQHandler(),发生中断时,不能直接得到触发中断的GPIO,需要借助具体函数判断:

/* 判断指定的GPIO_Pin是否触发了EXTI中断。
   参数:GPIO_Pin - 指定的GPIO_Pin
   返回:
   (ITStatus)SET - 指定的GPIO_Pin触发了EXTI中断
   (ITStatus)RESET - 指定的GPIO_Pin未触发EXTI中断
*/
ITStatus __HAL_GPIO_EXTI_GET_IT(uint16_t GPIO_Pin);

示例:

void EXTI9_5_IRQHandler(void) {
    uint16_t GPIO_Pin;
	
	// Get the GPIO_Pin which requsted EXTI handle
	if(__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_5) != RESET) {
		GPIO_Pin = GPIO_PIN_5;
	} else if(__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_6) != RESET) {
		GPIO_Pin = GPIO_PIN_6;
	} else if(__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_7) != RESET) {
		GPIO_Pin = GPIO_PIN_7;
	} else if(__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_8) != RESET) {
		GPIO_Pin = GPIO_PIN_8;
	} else if(__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_9) != RESET) {
		GPIO_Pin = GPIO_PIN_9;
	}
	
	HAL_GPIO_EXTI_IRQHandler(GPIO_Pin);
}

void EXTI15_10_IRQHandler(void) {
	
	uint16_t GPIO_Pin;
	
	// Get the GPIO_Pin which requsted EXTI handle
	if(__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_10) != RESET) {
		GPIO_Pin = GPIO_PIN_10;
	} else if(__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_11) != RESET) {
		GPIO_Pin = GPIO_PIN_11;
	} else if(__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_12) != RESET) {
		GPIO_Pin = GPIO_PIN_12;
	} else if(__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_13) != RESET) {
		GPIO_Pin = GPIO_PIN_13;
	} else if(__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_14) != RESET) {
		GPIO_Pin = GPIO_PIN_14;
	} else if(__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_15) != RESET) {
		GPIO_Pin = GPIO_PIN_15;
	}
	
	HAL_GPIO_EXTI_IRQHandler(GPIO_Pin);
}

中断服务函数

/* EXTI0_IRQn */
void EXTI0_IRQHandler(void) {}
/* EXTI1_IRQn */
void EXTI1_IRQHandler(void) {}
/* EXTI2_IRQn */
void EXTI2_IRQHandler(void) {}
/* EXTI3_IRQn */
void EXTI3_IRQHandler(void) {}
/* EXTI4_IRQn */
void EXTI4_IRQHandler(void) {}
/* EXTI5_IRQn~EXTI9_IRQn */
void EXTI9_5_IRQHandler(void) {}
/* EXTI10_IRQn~EXTI15_IRQn */
void EXTI15_10_IRQHandler(void) {}

中断回调函数

概述

 使用中断服务函数处理中断事件时,需要在中断服务函数中手动清除中断标志。HAL库提供了更为简洁的中断处理方式:通用入口函数+中断回调函数

通用入口函数

通用入口函数HAL_GPIO_EXTI_IRQHandler()定义在stm32f4xx_hal_gpio.c中。

/* 通用入口函数的组成:
   1.清除中断标志:__HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin); 
   2.中断处理逻辑:HAL_GPIO_EXTI_Callback(GPIO_Pin); */
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 EXTI0_IRQHandler(void) {
    // 调用通用入口函数
    HAL_GPIO_EXTI_IRQHandler(GOIO_PIN_0);
}
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
    // 中断处理逻辑
}

中断回调函数

中断回调函数HAL_GPIO_EXTI_Callback()定义在stm32f4xx_hal_gpio.c中。中断回调函数实现中断处理逻辑,被通用入口函数调用。只需正确声明定义中断回调函数,而无需显式调用。

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
    // 中断处理逻辑
}

总结

 使用HAL库实现EXTI中断的函数调用流程为:

/* 中断向量函数,在中断向量函数中调用
   __HAL_GPIO_EXTI_GET_IT(GPIO_Pin)判断指定的GPIO_Pin是否触发中断 */
1. void EXTIx_IRQHandler(void); 
/* 通用入口函数,清除中断标志,被 HAL_GPIO_EXTI_Callback() 调用 */
2. void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin);
/* 中断回调函数,用户定义中断处理逻辑 */
3. void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin);  

示例

main.c

#include "stm32f4xx.h"

/* define */
#define LED_Port GPIOC
#define LED_Pin GPIO_PIN_0
#define LED_ON HAL_GPIO_WritePin(LED_Port,LED_Pin,GPIO_PIN_RESET)
#define LED_OFF HAL_GPIO_WritePin(LED_Port,LED_Pin,GPIO_PIN_SET)
#define LED_Toggle HAL_GPIO_TogglePin(LED_Port,LED_Pin)

#define Key_IT EXTI1_IRQn
#define Key_Port GPIOC
#define Key_Pin GPIO_PIN_1

#define Key_LED_CLK_ENABLE __HAL_RCC_GPIOC_CLK_ENABLE()
#define Key_LED_IT_ENABLE HAL_NVIC_EnableIRQ(Key_IT)

/* declare */
void Key_LED_Init(void);
HAL_StatusTypeDef SysClk_Init(uint32_t m,uint32_t n,uint32_t p,uint32_t q);

int main(void) {
    HAL_Init();
    SysClk_Init(8,336,2,7);
    /* set system NVIC group */
    HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_1);
    Key_LED_Init();
    while(1);
}

void Key_LED_Init(void) {
    GPIO_InitTypeDef GPIO_Init;
    
    /* #define Key_LED_CLK_ENABLE __HAL_RCC_GPIOC_CLK_ENABLE() */
    Key_LED_CLK_ENABLE;
    
    /* #define LED_Pin GPIO_PIN_0 */
    GPIO_Init.Pin = LED_Pin;
    GPIO_Init.Mode = GPIO_MODE_OUTPUT_OD;
    GPIO_Init.Pull = GPIO_NOPULL;
    GPIO_Init.Speed = GPIO_SPEED_LOW;
    /* #define LED_Port GPIOC */
    HAL_GPIO_Init(LED_Port,&GPIO_Init);
    
    // LED turn off when system reset
    /* #define LED_ON HAL_GPIO_WritePin(LED_Port,LED_Pin,GPIO_PIN_RESET)
       #define LED_OFF HAL_GPIO_WritePin(LED_Port,LED_Pin,GPIO_PIN_SET) */
    LED_OFF;
    
    GPIO_Init.Pin = Key_Pin;
    GPIO_Init.Mode = GPIO_MODE_IT_FALLING;
    GPIO_Init.Pull = GPIO_PULLUP;
    GPIO_Init.Speed = GPIO_SPEED_HIGH;
    HAL_GPIO_Init(Key_Port,&GPIO_Init);
    
    /* #define Key_IT EXTI1_IRQn */
    HAL_NVIC_SetPriority(Key_IT,0,0);
    
    /* #define Key_LED_IT_ENABLE HAL_NVIC_EnableIRQ(Key_IT) */
    Key_LED_IT_ENABLE;    
}

/* SYSCLK = PLLCLK = HSE*n/(m*p)
AHBCLK = HCLK = SYSCLK/1
APB1CLK = PCLK1 = HCLK/4
APB2CLK = PCLK2 = HCLK/2
return :
HAL_OK - Config succeed.
HAL_ERROR - Config failed. */
/* If HSE = 8MHz,call SysClk_Init(8,336,2,7) will set to :
SYSCLK = PLLCLK = 8*336/(8*2) MHz = 168MHz
AHBCLK = HCLK = SYSCLK/1 = 168/1 MHz = 168MHz
APB1CLK = PCLK1 = HCLK/4 = 168/4 MHz = 42MHz
APB2CLK = PCLK2 = HCLK/2 = 168/2 MHz = 84MHz */
HAL_StatusTypeDef SysClk_Init(uint32_t m,uint32_t n,uint32_t p,uint32_t q) {
    HAL_StatusTypeDef status = HAL_OK;
    RCC_OscInitTypeDef RCC_OscInitStructure;
    RCC_ClkInitTypeDef RCC_ClkInitStructure;
    /* Enable PWR clock. */
    __HAL_RCC_PWR_CLK_ENABLE();
    /* Config voltage scaler to PWR_REGULATOR_VOLTAGE_SCALE1.
    This configuration is for balancing performance and power consumpion. */
    __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
    /* Clock source choose HSE clock */
    RCC_OscInitStructure.OscillatorType = RCC_OSCILLATORTYPE_HSE;
    /* Open HSE clock */
    RCC_OscInitStructure.HSEState = RCC_HSE_ON;
    /* Open PLL clock */
    RCC_OscInitStructure.PLL.PLLState = RCC_PLL_ON;
    /* PLL clock source choose HSE clock */
    RCC_OscInitStructure.PLL.PLLSource = RCC_PLLSOURCE_HSE;
    /* Range from 2 to 63 */
    RCC_OscInitStructure.PLL.PLLM = m;
    /* Range from 64 to 432 */
    RCC_OscInitStructure.PLL.PLLN = n;
    /* Choose one from 2,4,6,8 */
    RCC_OscInitStructure.PLL.PLLP = p;
    /* Range from 2 to 15 */
    RCC_OscInitStructure.PLL.PLLQ = q;
    status = HAL_RCC_OscConfig(&RCC_OscInitStructure);
    if(status != HAL_OK) {return HAL_ERROR;}
    /* Choose PLL clock as system clock and config HCLK,PCLK1,PCLK2 */
    RCC_ClkInitStructure.ClockType = (RCC_CLOCKTYPE_SYSCLK
                                     |RCC_CLOCKTYPE_HCLK
                                     |RCC_CLOCKTYPE_PCLK1
                                     |RCC_CLOCKTYPE_PCLK2);
    /* HCLK = SYSCLK/1 */
    RCC_ClkInitStructure.AHBCLKDivider = RCC_SYSCLK_DIV1;
    /* PCLK1 = HCLK/4 */
    RCC_ClkInitStructure.APB1CLKDivider = RCC_HCLK_DIV4;
    /* PCLK2 = HCLK/2 */
    RCC_ClkInitStructure.APB2CLKDivider = RCC_HCLK_DIV2;
    status = HAL_RCC_ClockConfig(&RCC_ClkInitStructure,FLASH_LATENCY_5);
    if(status != HAL_OK) {return HAL_ERROR;}
    if(HAL_GetREVID() == 0x1001) { __HAL_FLASH_PREFETCH_BUFFER_ENABLE();}
    return HAL_OK;
}

/* 中断向量函数 */
void EXTI1_IRQHandler(void) {
    /* 调用通用入口函数 */
    HAL_GPIO_EXTI_IRQHandler(Key_Pin);
}

/* 中断回调函数 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
    // 按下按键,LED明暗切换
    if(GPIO_Pin == Key_Pin) {
        /* #define LED_Toggle HAL_GPIO_TogglePin(LED_Port,LED_Pin) */
        LED_Toggle;
    }
}
  • 4
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
针对于stm32f4xx芯片开发使用的hal版本库 /** ****************************************************************************** * @file stm32f4xx_hal.h * @author MCD Application Team * @version V1.4.2 * @date 10-November-2015 * @brief This file contains all the functions prototypes for the HAL * module driver. ****************************************************************************** * @attention * * © COPYRIGHT(c) 2015 STMicroelectronics * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3. Neither the name of STMicroelectronics nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ****************************************************************************** */

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值