外部中断基本知识介绍
EXTI–外部中断/事件控制器
STM32的每个IO都可以作为外部中断的中断输入口。
STM32的中断控制器支持19个外部中断/事件请求:
**线0~15:**对应外部IO口的输入中断。
**线16:**连接到PVD(可编程电压监测器:它的作用是监视供电电压,在供电电压下降到给定的阈值以下时,产生一个中断,通知软件做紧急处理)输出。
**线17:**连接到RTC闹钟事件。
**线18:**连接到USB唤醒事件。
注:互联网型STM32有20个中断/事件请求,多了一个线19:连接到以太网事件,星光嵌入式的枭龙,猛龙开发板的STM32为非互联网型。
每个外部中断线可以独立的配置出发方式(上升沿,下降沿或者双边沿触发)
外部中断通用I/O映像
外部中断服务函数
IO口外部中断在中断向量表中只分配了7个中断向量,只能使用7个中断服务函数
中断服务函数如下,名称要与启动代码中的保持一致
EXTI0_IRQHandler ------>GPIOx.0
EXTI1_IRQHandler ------>GPIOx.1
EXTI2_IRQHandler ------>GPIOx.2
EXTI3_IRQHandler ------>GPIOx.3
EXTI4_IRQHandler ------>GPIOx.4
EXTI9_5_IRQHandler ------>GPIOx.5~GPIOx.9
EXTI10_15_IRQHandler ------>GPIOx.10~GPIOx.15
注:中断服务函数没有入口参数,没有返回值。
寄存器 :有关中断函数
初始化函数
void MY_NVIC_Init(u8 NVIC_PreemptionPriority,u8 NVIC_SubPriority,u8 NVIC_Channel,u8 NVIC_Group)
//中断初始化(抢占优先级,响应优先级,中断编号,中断分组)
{
u32 temp;
MY_NVIC_PriorityGroupConfig(NVIC_Group);
//设置分组
temp=NVIC_PreemptionPriority<<(4-NVIC_Group);
temp|=NVIC_SubPriority&(0x0f>>NVIC_Group);
temp&=0xf;
//取低四位
NVIC->ISER[NVIC_Channel/32]|=(1<<NVIC_Channel%32);
//使能中断
NVIC->IP[NVIC_Channel]|=temp<<4;
//设置响应优先级和中断优先级
}
设置NVIC分组
void MY_NVIC_PriorityGroupConfig(u8 NVIC_Group)
{
u32 temp,temp1;
temp1=(~NVIC_Group)&0x07;
//取后三位
temp1<<=8;
temp=SCB->AIRCR;
//读取先前的设置
temp&=0X0000F8FF;
//清空先前的分组
temp|=0X05FA0000;
//写入钥匙
temp|=temp1;
SCB->AIRCR=temp;
//设置分组
}
外部中断配置函数
void Ex_NVIC_Config(u8 GPIOx,u8 BITx,u8 TRIM)
//外部中断配置(GPIOx,需要使能的位,触发模式)
{
u8 EXTADDR;
u8 EXTOFFSET;
EXTADDR=BITx/4;
//得到中断寄存器组的编号
EXTOFFSET=(BITx%4)*4;
RCC->APB2ENR|=0x01;
//使能IO复用时钟
AFIO->EXTICR[EXTADDR]&=~(0x000F<<EXTOFFSET);
//清除原来的设置
AFIO->EXTICR[EXTADDR]|=GPIOx<<EXTOFFSET;
//EXTI.BITx映射到GPIOx.BITx
//自动设置
EXTI->IMR|=1<<BITx;
//开启line BITx上的中断
if(TRIM&0x01)EXTI->FTSR|=1<<BITx;
//line BITx上事件下降沿触发
if(TRIM&0x02)EXTI->RTSR|=1<<BITx;
//line BITx上事件上升降沿触发
}
寄存器代码
test.c
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "beep.h"
#include "exti.h"
int main(void)
{
Stm32_Clock_Init(9);
//系统时钟设置
uart_init(72,115200);
//串口初始化为115200
delay_init(72);
//延时初始化
LED_Init();
//初始化与LED连接的硬件接口
BEEP_Init();
//初始化蜂鸣器
EXTIX_Init();
//初始化外部中断输入
while(1)
{
printf("OK\r\n");
delay_ms(500);
}
}
exti.h
#ifndef __EXTI_H
#define __EXTI_H
void EXTIX_Init(void);
#endif
exti.c
#include "exti.h"
#include "delay.h"
#include "led.h"
#include "key.h"
#include "beep.h"
void EXTI0_IRQHandler(void)
//外部中断0服务函数
//我记得函数我写成了
//void EXTI0_IRQnHandler(void)
//没有报错,但是进入了死循环
{
delay_ms(10);
//消抖
if(KEY1 == 0)
{
BEEP = !BEEP;
}
EXTI->PR = 1<<0;
//清除LINE0的中断标志位
}
void EXTI3_IRQHandler(void)
{
delay_ms(10);
if(KEY2 == 0)
{
LED1 = !LED1;
}
EXTI->PR = 1<<3;
}
void EXTI2_IRQHandler(void)
{
delay_ms(10);
if(KEY3 == 0)
{
LED2 = !LED2;
}
EXTI->PR = 1<<2;
}
void EXTI1_IRQHandler(void)
{
delay_ms(10);
if(KEY4 == 0)
{
LED3 = !LED3;
}
EXTI->PR = 1<<1;
}
void EXTIX_Init(void)
//外部中断初始化程序
//初始化PA0/PE0/PE3/PE4为中断输入
{
KEY_Init();
Ex_NVIC_Config(GPIO_A,0,FTIR);
//PA0 下降沿触发
Ex_NVIC_Config(GPIO_C,3,FTIR);
//PC3 下降沿触发
Ex_NVIC_Config(GPIO_C,2,FTIR);
//PC2 下降沿触发
Ex_NVIC_Config(GPIO_C,1,FTIR);
//PC1 下降沿触发
MY_NVIC_Init(2,3,EXTI0_IRQn,2);
//抢占2,子优先级3 因为是PA0 所以中断
//源为EXTI0_IRQn,组2,下面同理
MY_NVIC_Init(2,2,EXTI3_IRQn,2);
MY_NVIC_Init(2,1,EXTI2_IRQn,2);
MY_NVIC_Init(2,0,EXTI1_IRQn,2);
}
在这里插入代码片
外部中断HAL库
外部中断操作HAL库函数分布文件:
头文件:stm32f1xx_hal_gpio.h
源文件:stm32f1xx_hal_gpio.c
头文件:stm32f1xx_hal_cortex.h
源文件:stm32f1xx_hal_cortex.c
重要函数
初始化函数
void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init)
设置中断优先级分组
void HAL_NVIC_SetPriorityGrouping(uint32_t PriorityGroup)
设置中断优先级
void HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t PreemptPriority, uint32_t SubPriority)
HAL_NVIC_SetPriority函数:设置中断优先级,函数有三个形参,作用分别是选择中断源,抢占优先级,响应优先级
使能中断
void HAL_NVIC_EnableIRQ(IRQn_Type IRQn)
HAL_NVIC_EnableIRQ函数:使能中断,函数有一个形参,选定中断源。
HAL外部中断API,中断中调用
void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
外部中断用户回调
__weak void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
HAL库代码
main.c
#include "MyIncludes.h"
void GPIO_EXTI_ISR(uint16_t GPIO_Pin)
{
if(GPIO_Pin == GPIO_PIN_0)
//如果指向GPIO_PIN_0,也是就key1按下
{
LED_Open(LED1);
LED_Open(LED2);
}
if(GPIO_Pin == GPIO_PIN_2)
//key3按下
{
LED_Close(LED1);
LED_Close(LED2);
}
}
int main()
{
System_Init();
//系统初始化
LED_Init();
//LED初始化
SysTick_Init(NULL);
//Systic_INit滴答定时器关闭
Exti_Init(EXTI_LINE0,GPIO_EXTI_ISR);
Exti_Init(EXTI_LINE2,GPIO_EXTI_ISR);
//中断初始化,外部中断 0 和 2,使用了函数指针
//指向了GPIO_EXTI_ISR()函数
while(1)
{
}
}
Exti.h
#ifndef __EXTI_H_
#define __EXTI_H_
#include "stm32f1xx.h"
#include "stm32_types.h"
#include "stm32f1xx_hal.h"
enum
{
EXTI_LINE0 = 0,
EXTI_LINE1,
EXTI_LINE2,
EXTI_LINE3,
EXTI_LINE4,
EXTI_LINE5,
EXTI_LINE6,
EXTI_LINE7,
EXTI_LINE8,
EXTI_LINE9,
EXTI_LINE10,
EXTI_LINE11,
EXTI_LINE12,
EXTI_LINE13,
EXTI_LINE14,
EXTI_LINE15,
};
//枚举0-15外部中断
typedef struct
{
void (*exti_isr)(uint16_t GPIO_Pin);
}_EXTI_ISR;
//函数指针先指向ISR,然后指向main.c中的
//void GPIO_EXTI_ISR(uint16_t GPIO_Pin);
void Exti_Init(uint8_t exti_line,void(*ISR)(uint16_t GPIO_Pin));
//外部中断初始化函数声明
#endif
Exti.c
#include "Exti.h"
_EXTI_ISR Exti_ISR;
//结构体声明变量
void Exti_Init(uint8_t exti_line,void (*ISR)(uint16_t GPIO_Pin))
{
GPIO_InitTypeDef GPIO_InitStruct;
Exti_ISR.exti_isr = ISR;
//函数指针指向ISR,也就是最终指向
//void GPIO_EXTI_ISR(uint16_t GPIO_Pin);
switch(exti_line)
{
case EXTI_LINE0:
__GPIOA_CLK_ENABLE();
//使能GPIOA时钟 因为按键key1 是PA0
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
//下降沿触发
GPIO_InitStruct.Pull = GPIO_NOPULL;
//外部电路无上拉或下拉激活
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
//高速
HAL_GPIO_Init(GPIOA,&GPIO_InitStruct);
//初始化GPIO
HAL_NVIC_SetPriority(EXTI0_IRQn,0x0f,0);
//设置中断优先级
//三个参数
//选定中断源 EXTI0_IRQn
//设置抢占优先级 0x0f 也就是 1111 4位
//设置响应优先级 0
//判断中断的优先级,先看抢占优先级,抢占优先级高的中断
//优先级别高。抢占优先级相同的情况下,先执行响应优先高的
//的。抢占优先级和响应优先级相同的情况下,根据中断向量
//表确定。
HAL_NVIC_EnableIRQ(EXTI0_IRQn);
//使能中断
//选定中断源
break;
case EXTI_LINE1:
__GPIOC_CLK_ENABLE();
GPIO_InitStruct.Pin = GPIO_PIN_1;
GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOC,&GPIO_InitStruct);
HAL_NVIC_SetPriority(EXTI1_IRQn,0x0f,0);
HAL_NVIC_EnableIRQ(EXTI1_IRQn);
break;
case EXTI_LINE2:
__GPIOC_CLK_ENABLE();
GPIO_InitStruct.Pin = GPIO_PIN_2;
GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOC,&GPIO_InitStruct);
HAL_NVIC_SetPriority(EXTI2_IRQn,0x0f,0);
HAL_NVIC_EnableIRQ(EXTI2_IRQn);
break;
case EXTI_LINE3:
__GPIOC_CLK_ENABLE();
GPIO_InitStruct.Pin = GPIO_PIN_3;
GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOC,&GPIO_InitStruct);
HAL_NVIC_SetPriority(EXTI3_IRQn,0x0f,0);
HAL_NVIC_EnableIRQ(EXTI3_IRQn);
break;
}
}
void EXTI0_IRQHandler(void)
//外部中断服务函数,用来响应外部中断触发
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);
//作用:1:清楚中断标记位
// 2:调用回调函数
}
void EXTI1_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_1);
}
void EXTI2_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_2);
}
void EXTI3_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_3);
}
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if(Exti_ISR.exti_isr != NULL)
Exti_ISR.exti_isr(GPIO_Pin);
//外部中断用户回调
//可以理解中断函数具体要响应的动作
}