本文借鉴该博客:
点此跳转
NVIC,全程嵌套向量控制器
(来自正点原子官方ppt,其中的中断82个,但是只会用到一部分,比如外部中断,串口中断,pwm输出事件中断等)
单片机的单字(我只是记得,出处未找到),某种程度上可以理解为某个时刻单片机只会执行一行代码,C语言的代码是按照流程执行的,如果想在执行这个代码过程中因为特定的条件发生,比如接受到信号,比如计数达到重装载值等,执行的任务从当前任务跳出,去执行指定函数的功能,完成后再回来继续执行原来执行到一半的代码。(如有错误,欢迎指正,毕竟也算是个小白)
如果有多个可能要执行的中断任务,就要给他们根据需求排序,即规定好他们的优先级。在中断优先级设置中,有抢占优先级和响应优先级之分:
可以举个例子:我们在设置中断优先级分组时,使用库函数:
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)
{
assert_param(IS_NVIC_PRIORITY_GROUP(NVIC_PriorityGroup));
SCB->AIRCR = AIRCR_VECTKEY_MASK | NVIC_PriorityGroup;
}
------------------------------------------------------------------------
IS_NVIC_PRIORITY_GROUP(GROUP) (((GROUP) == NVIC_PriorityGroup_0) || \
((GROUP) == NVIC_PriorityGroup_1) || \
((GROUP) == NVIC_PriorityGroup_2) || \
((GROUP) == NVIC_PriorityGroup_3) || \
((GROUP) == NVIC_PriorityGroup_4))
我们使用参数NVIC_PriorityGroup_2,代表2位抢占优先级,2位响应优先级,抢占优先级可以取值0-3,响应优先级0-3。如果中断A抢占优先级0,响应优先级;中断B抢占优先级0,响应优先级1;中断C抢占优先级1,响应优先级0.
则优先级:A高于B,高于C
即优先级:先比较抢占优先级大小,再比较响应优先级大小;规定,越接近0,其优先级越大。
在设置优先级时候,需要做以下初始化:
NVIC_InitStructure.NVIC_IRQChannel = //多个中断通道,具体见stmf4xx.h
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=;
NVIC_InitStructure.NVIC_IRQChannelSubPriority =;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
在解决完初始化之后,就可以从startup_stm32f40_41xxx.s中找相关的中断函数,函数的功能有自己来写,这里只是一个例子:
每一个可以使用中断的通道都有固定的中断函数可以自定义。
—————————————————————————————
EXIT 外部中断
可以以选择类型(中断或事件)和相应的触发事件(上升沿触发、下降沿触发或边沿触发)
stm32的每个IO口都可以作为外部中断输入,stm32中断控制器支持22个外部中断/事件请求:
对应的IO口情况如下:
IO有16组,中断线有16个,但是中断服务函数并不是16个
现在来看下例程:
void EXTIX_Init(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
KEY_Init();
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);//EXIT的中断部分使用的时钟
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE, EXTI_PinSource2);
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE, EXTI_PinSource3);
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE, EXTI_PinSource4);
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource0);
EXTI_InitStructure.EXTI_Line = EXTI_Line0;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; //例程中对应WAKE_UP键
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
EXTI_InitStructure.EXTI_Line = EXTI_Line2 | EXTI_Line3 | EXTI_Line4;//对应KEY0,KEY1,KEY2
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x00;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = EXTI2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x03;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = EXTI3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = EXTI4_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
相应的中断函数
void EXTI0_IRQHandler(void)
{
delay_ms(10); //用于消抖
if(WK_UP==1)
{
BEEP=!BEEP;
}
EXTI_ClearITPendingBit(EXTI_Line0); //清除中断标志位
}
void EXTI2_IRQHandler(void)
{
delay_ms(10);
if(KEY2==0)
{
LED0=!LED0;
}
EXTI_ClearITPendingBit(EXTI_Line2);
}
void EXTI3_IRQHandler(void)
{
delay_ms(10);
if(KEY1==0)
{
LED1=!LED1;
}
EXTI_ClearITPendingBit(EXTI_Line3);
}
void EXTI4_IRQHandler(void)
{
delay_ms(10);
if(KEY0==0)
{
LED0=!LED0;
LED1=!LED1;
}
EXTI_ClearITPendingBit(EXTI_Line4);
}
外部中断和外部事件
中断在这些代码中,有一个地方需要注意,即
EXTI_InitStructure.EXTI_Line = EXTI_Line2 | EXTI_Line3 | EXTI_Line4;//对应KEY0,KEY1,KEY2
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
第二行代码,代表使用的是外部中断。实际上,除了外部中断,我们还可以使用外部事件,具体原理图如下:
图和解析来源在文章开头。
大概意思为外部事件需要借助DMA,A/D通道,这样可以在不借助CPU来输出一定信号;中断则需要CPU支持来完成