1、基本概念
(1)中断:即CPU在正常执行程序的过程中,遇到外部(例如常见的按键中断)/内部(例如定时器中断)的紧急事件需要处理,暂时中断(中止)当前程序的执行,而转去为事件服务,待服务完毕,再返回到暂停处(断点)继续执行原来的程序。
(2)中断优先级:事件具有不同的轻重、缓急程度。系统工作时,我们总希望最紧急的事件优先被处理,以保证系统的实时性。这就引出了中断的优先级、中断嵌套问题。与中断控制器相连的每条线叫做中断线,要使用中断线,就得进行中断线的申请,就是IRQ,把申请一条中断线称为申请一个IRQ或者是申请一个中断号。IRQ和向量之间的映射可以通过中断控制器进行修改。
中断优先级的一个意义是出现多个中断同时触发,但是不能同时处理,所以先后顺序之分,要根据实际上的运行环境优先处理重要的中断。STM32 对中断优先级进行分组,共 5 组,组 0~4,这些分组是用于指定当前M4支持多少级抢占优先级和多少个响应优先级。同时,对每个中断设置一个抢占优先级和一个响应优先级。函数原型如下:
/**
* @brief Configures the priority grouping: pre-emption priority and subpriority.
* @param NVIC_PriorityGroup: specifies the priority grouping bits length.
* This parameter can be one of the following values:
* @arg NVIC_PriorityGroup_0: 0 bits for pre-emption priority //不支持抢占优先级
* 1 bits for subpriority //支持16级响应优先级(0 ~ 15)
* @arg NVIC_PriorityGroup_1: 1 bits for pre-emption priority //支持2级抢占优先级(0 ~ 1)
* 3 bits for subpriority //支持8级响应优先级(0 ~ 7)
* @arg NVIC_PriorityGroup_2: 2 bits for pre-emption priority //支持4级抢占优先级(数值0~3)
* 2 bits for subpriority //支持4级响应优先级(数值0~3)
* @arg NVIC_PriorityGroup_3: 3 bits for pre-emption priority //支持8级抢占优先级
* 1 bits for subpriority //支持2级响应优先级
* @arg NVIC_PriorityGroup_4: 4 bits for pre-emption priority //支持16级抢占优先级(数值0~15)
* 0 bits for subpriority //不支持响应优先级
* @note When the NVIC_PriorityGroup_0 is selected, IRQ pre-emption is no more possible.
* The pending IRQ priority will be managed only by the subpriority.
* @retval None
*/
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)
(3)抢占优先级与响应优先级
A.高抢占优先级是可以打断正在进行的低抢占优先级的中断。抢占优先级若相同,则不会出现抢占的过程
B.抢占优先级相同的中断,高响应优先级不可以打断低响应优先级的中断。
C.抢占优先级相同的中断,当两个中断同时发生的情况下,哪个响应优先级高,哪个先执行(响应)。
D.抢占优先级相同且响应优先级相同的中断,假如同时发生,会按照硬件内部固定的优先级执行
2、代码思路
#include "stm32f4xx.h"
static GPIO_InitTypeDef GPIO_InitStructure;
static EXTI_InitTypeDef EXTI_InitStructure;
static NVIC_InitTypeDef NVIC_InitStructure;
void delay(void)
{
uint32_t i=0xA00000;
while(i--);
}
void exit_init()
{
//打开端口A的硬件时钟,就是为端口A供电
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
//打开系统配置硬件时钟,就是为系统配置硬件时钟供电
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
//配置PA0引脚为输入模式
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_Init(GPIOA, &GPIO_InitStructure);
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource0);//将中断0连接PA0
//配置外部中断0
EXTI_InitStructure.EXTI_Line = EXTI_Line0; //配置外部中断0
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //中断模式
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //下降沿触发
EXTI_InitStructure.EXTI_LineCmd = ENABLE;//允许外部中断0工作
EXTI_Init(&EXTI_InitStructure);
//配置NVIC
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; //中断号
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x0F; //抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x0F; //响应优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //打开外部中断0通道
NVIC_Init(&NVIC_InitStructure);
}
#define PFout(x) *(volatile uint32_t *)(0x42000000+(GPIOF_BASE+0x14-0x40000000)*32+(x)*4)
#define PAin(x) *(volatile uint32_t *)(0x42000000+(GPIOA_BASE+0x10-0x40000000)*32+(x)*4)
int main()
{
//打开端口F的硬件时钟,就是为端口F供电
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);
//配置引脚
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_Speed=GPIO_High_Speed;
GPIO_InitStructure.GPIO_OType=GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_NOPULL;
GPIO_Init(GPIOF,&GPIO_InitStructure);//为何传地址
exit_init();
PFout(9)=1;
while(1)
{
}
}
void EXTI0_IRQHandler(void)
{
//检测标志位
if(EXTI_GetITStatus(EXTI_Line0) != RESET)
{
PFout(9)=0;
delay();
PFout(9)=1;
delay();
//清空标志位
EXTI_ClearITPendingBit(EXTI_Line0);
}
}
3、总结
配置中断的思路大致为:
端口A硬件时钟使能
SYSCFG硬件时钟使能
配置引脚的工作模式
将引脚连接到外部中断
中断触发方式:下降沿触发、上升沿触发
允许外部中断引脚申请中断请求
优先级的配置
中断服务函数
4.思考题
配置两个中断,设置不同的抢占优先级和响应优先级,观察优先级不同引起的现象的区别。