(本文是基于stm32f103c8t6加上LED组成的简易按键控制亮灯)
这里是部分可设置(也就是我们自己可以设置为我们想要的中断名称以及向量)中断向量。详情请参考STM32F103xx中文参考手册第九章《中断与事件》。
跟据中断的定义,我们可以通过中断使处理器转而去优先运行正常控制流之外的代码。
当中断信号达到时, CPU 必须停止它当前正在做的事情,并且切换到一个另一个活动。为了做到这就要在内核态堆钱保存程序计数器的当前值 (寄存器的内容) ,并把与中断类型相关的地址放进程序计数量。
举个简单的例子,最近恰逢电赛时期,在做东西的过程中我发现了许多问题,我本来是想做一个可视化的电压检测器,但是在写代码与实践的过程中我发现如果我把所有事情交给一块板子来处理,这块板子就会显得好忙好忙,于是它就开始“罢工”,显示屏上卡住了,我转到串口发送也会卡住,也就是所谓的程序跑飞了,我一开始一直不知道原因,后面才发现我用的ADC模式是扫描,这种方式对于板子的CPU占用真的太高了,也就是板子一直在做一个工作——AD转换,以至于顾不上显示屏上刷新了,后面我该用中断方式来传输AD转换数据就不会出现这种情况了,所以说中断对于我们来说真的用处很大。
那么有的小伙伴就要问了,如果很多个中断都被触发我们该执行哪一个程序呢,这就要看到我们的中断优先级了。
1、多个中断同时出现时,处理器先响应高优先级的中断
2、低优先级中断的ISR执行时,可以被高优先级中断再次打断
3、ISR比App Code拥有更高的执行优先级
详细请参考:STM32中断详解
用stm32F103核心板的GPIOA端一管脚接一个LED,GPIOB端口一引脚接一个开关(用杜邦线模拟代替)。采用中断模式编程,当开关接高电平时,LED亮灯;接低电平时,LED灭灯。
我们先来对题目进行分析,首先我们需要接一个LED灯,其次我们用一个开关来触发中断,使得我们按下开关LED亮,松开开关LED灭。
在这里我粗略地画一个示意图:
LED端:
这里表示我们PA5端口输出高电平就可以点亮LED灯。
开关端:
开关这里我们按下表示到达PB9的电平为高电平,根据我们题目要求,开关按下时使得LED点亮即我们将杜邦线接到高电平的时候触发中断,使得接在PA5管脚输出高电平点亮LED灯。
有到了我们最熟悉的环节了,前面扯够了废话现在我们该开始实际做了。
之前的博客中我也讲解了STM32CubeMX工程的建立以及一些注意事项,大家有不清楚的可以参考我之前的博客。有些过程我就直接跳过了,我就直接来到管脚设置步骤了。
在这里大家可以看到我之前画的示意图,我选择的是PA5接LED,PB9作为我们的外部中断触发,所以后面的管脚设置以及代码都是围绕这两个管脚来实现的,当然大家也可以设置其他的管脚。
相信接下来大家都知道下面的步骤了,我也不多啰嗦了。
管脚配置: PA5选择输出==“Output”,PB9选择中断触发“EXTI_9”==
接着我们要在左侧接着配置我们的GPIO,由于我们是高电平点亮,所以PA5直接用默认的设置就可以了。重点得看到我们PB9的设置:
在 GPIO mode 中我们可以选择触发模式
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上升沿和下降沿 这里根据我们的示意图选择为上升沿触发模式
GPIO Pull-up/Pull-down 我们之前讲过,是选择是否为上拉或下拉输出,这里我们选择否。
User Label 我们输入可以将这个触发设置为我们想要的名字,这里我起名为==“switch interrupt”==,当然大家也可以取其他名字,但是一定要记得我们现在起的名字,后面需要用到(这一步也不是必须的)。
现在我们的管脚基本上就配置好了,但是我们还需要使能我们的中断,也可以理解为开启中断,点击 NVIC,点击下面的使能 Enabled 开启中断。
看一下中断优先级:
中断优先级用于需要多个中断的工程先后顺序,我们这里只有一个中断,所以说不用关中断优先级。
最后我们设置一下时钟就OK啦,就可以导出我们的Keil工程了。
时钟树设置如下:
记得设置时钟树前要先开启时钟哦。
生成Keil功成之后,我们先仔细看看我们Keil工程各目录下的代码
因为我们用的是HAL库,所以我们点击Drivers目录下的stm32f1xx_hal_gpio.c文件,看到第546行。
void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
{
/* EXTI line interrupt detected */
if (__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != 0x00u)
{
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);
HAL_GPIO_EXTI_Callback(GPIO_Pin);
}
}
/**
* @brief EXTI line detection callbacks.
* @param GPIO_Pin: Specifies the pins connected EXTI line
* @retval None
*/
__weak void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
/* Prevent unused argument(s) compilation warning */
UNUSED(GPIO_Pin);
/* NOTE: This function Should not be modified, when the callback is needed,
the HAL_GPIO_EXTI_Callback could be implemented in the user file
*/
}
这些代码都是HAL库中的中断函数,其中 void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) 这个函数是遇到中断后跳转执行的函数,不知大家注意到前面的 __weak 没有,这表示这个函数是虚函数,需要我们来重新编写。
现在我们转到 main.c 文件中,找个位置将下面这段代码贴上去,我选择的是用户代码段4。
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if( GPIO_Pin == switch_interrupt_Pin)//判断外部中断源
{
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);//翻转PA5状态
}
}
大家注意我们判断外部中断的标志是 “GPIO_Pin == switch_interrupt_Pin” 这里的 ==“switch_interrupt_Pin”==使我们之前自定义的中断名称,大家根据自己定义的名称来改写这个判断标志,具体可以参考 main.h 文件中的宏定义如:
编译再烧录进入我们的板子中。
由于在寝室没拿到开关,所以将就一下杜邦线作为开关,杜邦线接3.3v灯亮,杜邦线接GND灯灭。效果大家一起看看吧:
STM32Cube提供了固件库,用户可直接调用固件库函数来开发,并且可以很好的实现STM32-MCU全系列的代码一致性。同时STM32CubeMX工具提供的可视化引脚、外设、时钟等配置功能,可以帮助快速完成工程的建立、初始化。大大降低了开发者的工作量。
STM32的NVIC和中断的总结
STM32 中断详解