中断系统
中断向量:由于程序中的中断函数地址是由编译器分配的,不是固定的。但是中断跳转,由于硬件的限制,只能跳到固定的地址执行程序。为了让硬件跳到一个不固定的中断函数里,需要在内存里定义一个地址列表,这个列表的地址是固定的,中断函数发生后,就跳到固定的位置,在这个位置,由编译器,再加上一条跳转到中断函数的代码,这样中断跳转就可以跳转到任意位置了。终端地址的列表就叫中断向量表。相当于中断跳转的一个跳板。
NVIC结构(嵌套中断向量控制器)
统一分配中断优先级和管理中断,NVIC是一个内核外设,是CPU小助手。
·NVIC的中断优先级由优先级寄存器的4位(0~15)决定,这4位可以进行切分,分为高n位的抢占优先级和低4-n位的响应优先级
·抢占优先级高的可以中断嵌套,响应优先级高的可以优先排队,抢占优先级和响应优先级均相同的按中断号排队
分组方式 | 抢占优先级 | 响应优先级 |
分组0 | 0位,取值为0 | 4位,取值为0~15 |
分组1 | 1位,取值为0~1 | 3位,取值为0~7 |
分组2 | 2位,取值为0~3 | 2位,取值为0~3 |
分组3 | 3位,取值为0~7 | 1位,取值为0~1 |
分组4 | 4位,取值为0~15 | 0位,取值为0 |
EXTI(Extern Interrupt)外部中断
外部中断源EXTI的中断向量:EXTI0,EXTI1,EXTI2,EXTI3,EXTI4,EXTI9_5,EXTI9_5,EXTI15_10。
EXTI(Extern Interrupt)外部中断
EXTI可以监测指定GPIO口的电平信号,当其指定的GPIO口产生电平变化时,EXTI将立即向NVIC发出中断申请,经过NVIC裁决后即可中断CPU主程序,使CPU执行EXTI对应的中断程序
支持的触发方式:上升沿/下降沿/双边沿/软件触发
支持的GPIO口:所有GPIO口,但相同的Pin不能同时触发中断
通道数:16个GPIO_Pin,外加PVD输出、RTC闹钟、USB唤醒、以太网唤醒
触发响应方式:中断响应/事件响应(中断相应就是触发中断函数。事件相应就是中断信号不通向CPU,而是通向其他外设,出发其他外设的操作)
PA0,PB0PC0等相同Pin的引脚经过AFIO(数据选择器)后只有其中一个能连接到EXTI通道0上。
AFIO复用IO口
·AFIO主要用于引脚复用功能的选择和重定义
·在STM32中,AFIO主要完成两个任务:复用功能引脚重映射、中断引脚选择
EXTI框图
数据选择器,根据控制端而选择一个输入端输出
中断屏蔽寄存器起到开关作用,如果置1则输出根据请求挂起寄存器输出(中断标志位),如果置0,则屏蔽中断。
发射脉冲信号,从而触发其他外设。
外部中断配置流程
- 配置RCC,把涉及到的外设时钟都打开(GPIO,AFIO,EXTI,NVIC。其中EXTI,NVIC无需单独打开,NVIC是内核外设,内核外设也不需要开启时钟。RCC开启的都是内核之外的外设)
- 配置GPIO将端口设置为输入模式
- 配置AFIO,选择使用的这路GPIO,连接到EXTI
- 配置EXTI,选择边沿触发方式,选择出发响应方式
- 配置NVIC,给中断选择优先级
AFIO和GPIO在一个文件里面GPIO.H
void GPIO_AFIODeInit(void); //复位AFIO外设,清除AFIO外设配置
void GPIO_PinLockConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);//锁定GPIO配置,锁定参数的引脚,防止意外更改
void GPIO_EventOutputConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);//配置事件输出功能
void GPIO_EventOutputCmd(FunctionalState NewState);//配置事件输出功能
void GPIO_PinRemapConfig(uint32_t GPIO_Remap, FunctionalState NewState);//Pin重映射
void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);//配置AFIO数据选择器,选择中断引脚。
EXTI库函数
void EXTI_DeInit(void);//清除EXTI的配置状态,恢复成默认上电状态
void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);//根据结构体EXTI_InitStruct的参数配置EXTI
外设
EXTI_InitStruct的参数有:
EXTI_Line指定要配置的中断线(EXTI_Line0~EXTI_Line19)
EXTI_LineCmd 指定选择中断线的新状态( ENABLE和DISABLE)
EXTI_Mode指定外部中断线的模式(EXTI_Mode_Interrupt 和EXTI_Mode_Event)
EXTI_Trigger指定触发信号的有效边沿(EXTI_Trigger_Rising 上升沿,EXTI_Trigger_Falling下降沿, EXTI_Trigger_Rising_Falling 双边)
void EXTI_StructInit(EXTI_InitTypeDef* EXTI_InitStruct);//把参数传递的结构体变量赋一个默认值
void EXTI_GenerateSWInterrupt(uint32_t EXTI_Line);//软件触发外部中断
FlagStatus EXTI_GetFlagStatus(uint32_t EXTI_Line);//获取指定标志位是否被置1(在主程序中使用)
void EXTI_ClearFlag(uint32_t EXTI_Line);//对置1的标志位进行清除(在主程序中使用)
ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);//获取中断标志位是否被置1(在中断函数中使用)
void EXTI_ClearITPendingBit(uint32_t EXTI_Line);//清除中断挂起标志位(在中断函数中使用)
NVIC库函数在杂项里面misc.h
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);//用来中断分组(抢占和响应)
分组方式整个芯片只能用一种,整个工程定义一次就行
@arg NVIC_PriorityGroup_0: 0 bits for pre-emption priority
* 4 bits for subpriority
* @arg NVIC_PriorityGroup_1: 1 bits for pre-emption priority
* 3 bits for subpriority
* @arg NVIC_PriorityGroup_2: 2 bits for pre-emption priority
* 2 bits for subpriority
* @arg NVIC_PriorityGroup_3: 3 bits for pre-emption priority
* 1 bits for subpriority
* @arg NVIC_PriorityGroup_4: 4 bits for pre-emption priority
* 0 bits for subpriority
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);//根据参数结构体来初始化NVIC
NVIC_IRQChannel 指定中断通道来开启或关闭(用那个通道EXTI0_IRQn,EXTI1_IRQn,EXTI2_IRQn,EXTI3_IRQn,EXTI4_IRQn,EXTI9_5_IRQn,EXTI9_5_IRQn,EXTI15_10_IRQn)
NVIC_IRQChannelCmd 指定通道使能还是失能(ENABLE,DISABLE)
NVIC_IRQChannelPreemptionPriority 指定通道的抢占优先级
NVIC_IRQChannelSubPriority 指定通道的响应优先级
(根据NVIC_PriorityGroupConfig的分组对用来设置)
void NVIC_SetVectorTable(uint32_t NVIC_VectTab, uint32_t Offset);//设置中断向量表
void NVIC_SystemLPConfig(uint8_t LowPowerMode, FunctionalState NewState);//系统低功耗配置
/******************************************************************************
//1配置RCC,打开时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//打开GPIOB时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//打开AFIO时钟
//2配置GPIO端口
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
GPIO_InitStructure.GPIO_Speed =GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
//3配置AFIO,选择使用PB14作为中断输入
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource14);
//4配置EXTI,选择边沿触发方式
EXTI_InitTypeDef EXTI_Initstructure;
EXTI_Initstructure.EXTI_Line = EXTI_Line14;
EXTI_Initstructure.EXTI_LineCmd = ENABLE;
EXTI_Initstructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_Initstructure.EXTI_Trigge
r = EXTI_Trigger_Falling;
EXTI_Init(&EXTI_Initstructure);
//5 NVIC配置
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStructure);
******************************************************************************/
中断函数
在STM32中断函数的名字是固定的,函数名在启动文件startup_stm32f10x_md.s中查找。
以IRQHandler结尾的字符串就是中断函数的名字(例如BP14是EXTI15_10通道 函数名为EXTI15_10_IRQHandler)。中断函数都是无参数无返回值,函数名写错就无法进入中断
//中断函数
void EXTI15_10_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line14) == SET) //判断中断标志位,10-15通道都可以触发EXTI15_10_IRQHandler函数,判断该函数是否14通道触发
{
//中断函数内容
EXTI_ClearITPendingBit(EXTI_Line14);//执行完中断函数后,需要将中断标志位清零
}
}