前言
首先了解一下中断是什么。中断是计算机系统中的一种机制,用于处理和响应来自外部设备或内部事件的请求。当一个设备或事件发送一个中断请求时,CPU会暂时停止当前的任务,转而处理中断请求(记住,是暂停,不是同时),一旦中断处理程序执行完毕,CPU会回到原来的任务继续执行。。中断可以是硬件中断,例如设备的输入/输出请求,或者是软件中断,如操作系统的系统调用。
NVIC
NVIC (Nested Vectored Interrupt Controller) 中断控制器,NVIC由三个元素组成,中断使能、中断优先级、中断嵌套。
中断使能,相当于就是是否使用中断,
中断优先级,分为抢占优先级和响应优先级,用来管理中断进行顺序的(例如加减乘除,乘除的优先级更高,需要先计算)。可自由分配,如f103,优先级由4位管理,任意分配,数字越小,优先级越高。
中断嵌套,即当一个中断正在处理时,如果有更高优先级的中断请求发生,NVIC 可以暂时挂起当前中断的处理,转而处理更高优先级的中断。
NVIC 还支持中断嵌套,指在处理一个中断的过程中,如果有更高优先级的中断请求发生,系统可以中断当前正在执行的中断处理过程,转而处理更高优先级的中断。一旦更高优先级的中断处理完毕,中断控制器会恢复之前保存的上下文,继续处理原来的中断。
电路
我们继续按这个电路配置:
配置工程
打开之前的工程Test_F103,在这个工程的基础上继续添加外设。
配置GPIO
Pinout&Configuration -> System Core -> GPIO -> PA4 -> GPIO_EXTI4;
配置如下:
GPIO mode -> External Interrupt Mode with Falling...
GPIO Pull-up/Pull-down -> Pull-up
User Label -> KEY_2
各模式介绍:
External Interrupt Mode with Rising edge trigger detection:外部中断上升沿触发检测
External Interrupt Mode with Falling edge trigger detection:外部中断下降沿触发检测
External Interrupt Mode with Rising/Falling edge trigger detection:外部中断上升/下降沿触发检测
External Event Mode with Rising edge trigger detection:外部事件上升沿触发检测
External Event Mode with Falling edge trigger detection:外部事件下降沿触发检测
External Event Mode with Rising/Falling edge trigger detection:外部事件上升/下降沿触发检测
配置NVIC
System Core -> NVIC -> Prioty Group -> 3bit,1bit -> EXTI line4 interrupt -> Enabled -> Preemption Priority (7) -> Sub Priority(1)
按以上配置,3位抢占优先级,1位响应优先级,使能line4中断,抢占优先级设置为6,响应优先级设置为1
这时候你会发现,左边状态栏NVIC出现了一个红叉,错误警告,然后找到Time base: System tick timer这里抢占优先级15字体是红色的,配置的抢占优先级是3位,3位最大111,为7。将15改为7,红叉就消失了;
Time base: System tick timer设置为7,1;系统计时器,这个不需要很高的优先级,设置为最低就行。还有一点就是这个中断优先级如果与外部中断一样或者更低,如果在外部中断中使用延时,延时会被卡死,所以不推荐在中断中使用延时,如果非要用,然后又不想把时基优先级调高怎么办。
最后会在附录奉上。
配置好之后,输出程序。
程序讲解
先打开启动文件,找到线4中断处理函数EXTI4_IRQHandler,右键跟踪过去,Go To Definition...
在处理函数中,只调用了一个中断服务函数HAL_GPIO_EXTI_IRQHandler(),再次右键跟踪,看看中断服务干了些啥,
服务函数中,先是判断是否接收到指定的外部中断,如果接收到,则清除中断标志(HAL库的服务函数都会帮忙清除中断标志,所以在使用HAL库的时候,不用管标志位这一点)。并进入中断回调函数HAL_GPIO_EXTI_Callback(GPIO_Pin);再次跟踪回调函数;
回调函数前面有一个__weak,__weak是一个弱化符号,代表这个中断回调函数可以重新定义一个新的函数,并且只执行新定义的函数,不会报错。
所以说我们在使用HAL库的时候,只需要重写回调函数就行,其他的都可以不用管。在开发时,也不必每次都这么去找回调函数,因为生成程序的时候勾选了这个,为每一对外设生成一组单独的.c和.h文件,
所以只需要打开左边工具栏Functions,外部中断属于GPIO输入,找到....gpio.c,就可以找到对应的回调函数;如果是串口就找到...uart.c,DMA就找到...DMA.c,中断就找到...exti.c,其他的也一样
现在我们来编写中断服务函数,我们使用按键控制的,找到key.c,编写程序:
这里的上升沿检测是按键触发的,所以需要进行消抖,如果是芯片或者一些外设给的一个脉冲信号,不能消抖,一消就检测不到信号了。
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if(GPIO_Pin == GPIO_PIN_4)
{
delay_ms(10);
if(!HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_4))
{
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
}
}
}
结尾再附上一段延时代码。
附:
如下是内部晶振,主频64Mhz延时1us的程序:
void delay_us(uint16_t us)
{
for(;us>0;us--)
for(int i=0;i<9;i++);
}
51单片机的话可以根据时钟周期和频率计算,但是32单片机的话由于程序优化等各种原因,计算不准确,但是可以通过仿真测试出来。
老版的keil可以在这设置单片机频率,新版的这里变灰了
新版的可以在这里修改时钟频率,魔术棒 -> Debug -> Settings -> Trace -> Core Clock -> 64(单片机主频率) -> 确定
然后烧录程序,点击Debug图标进行仿真,打断点检测延时时间,根据具体情况调整。