6. Stm32f407 key中断方式示例
硬件平台: stm32f407ve
软件平台: win10 (OS Name: Microsoft Windows 10 Enterprise
OS Version: 10.0.18363 N/A Build 18363)
Keil5 5.26.2
HAL库版本: 2.14.0(目前下载的最新的)
时间: 2020-02-12
中断:这个概念一般有点难以理解,但是又是一个非常重要的功能,基本所有的单片机(或者说处理器)都有中断。可见中断的重要性。51单片机都有5个中断源,这是我接触到的最简单的中断系统了。
打个比方,还是手机的那个例子,上次我说的是查询方式,这次我们的手机有了铃声了。那你需要接听任何一个电话还需要一直盯着屏幕吗?那很明显,不需要了,只要把手机带在身边,你随时可以被铃声暂停手头的事情(可能你在吃饭,睡觉,打豆豆等待各种日常生活),来接听呼入的电话。暂停手头的事情,接听电话就对应着cpu的中断处理过程(实际过程比这个要复杂一些),暂停当前的程序,跳到中断处理程序处理事件,然后返回继续当前的工作(当然接完电话,你也会回来继续干手中的事情)。还有一个比较小的细节就是,当你接电话的时候,实际你会在大脑中记住你刚刚在干嘛,不然等一下你放下电话就不知道要继续干嘛了。这个细节对应cpu也是同样需要处理的,所以就会有中断跳转之前会有现场保护(主要是寄存器值得保存到栈里),一会中断处理完之后(将之前保存到栈里的值重新返回寄存器,这个部分比较底层,涉及到cpu在执行指令时会用到哪些寄存器及cpu怎么执行指令的理论部分,这个地方不了解的话,确实不太好理解)还能回来接着干活。
但是你要意识到一个问题,程序可以跳转,还能跳转回来,这个需要一些复杂的机制才能处理,绝对不是直接跳来跳去。
回到正题,我们处理这个按键中断,stm32f407支持的中断比较多,外部中断只是其中的中断一个小部分。外部中断是指外部引脚电平的变化引起的中断事件。Stm32f407ve有82个引脚,82个引脚都可以设置中断,但是最多只能设置16个中断。这个因为GPIOA1,GPIOB1....GPIOH1共用了一个外部中断,如果GPIOB1设置了中断,那么其他组的1脚就无法设置为中断方式了。
这个地方可以参考中文参考手册的部分,比较详细。
按键中断有利于改善cpu的效率,cpu不用一直查询按键的状态,等到有按键按下的时候,直接打断cpu的执行,做中断处理。所以cpu在其他时间就可以完成很多任务了。(所有中断有利于改善cpu对于输入事件的处理效率)。
中断优先级(stm32f4的中断一共有81个吧,这么多中断一起的话,不可能都是同一个优先级,某些中断肯定要有些优越感,优先级高,有些中断就会被冷落,不重要就优先级低。这里同样注意一个问题,不一定我们每个中断都要设置优先级,我们只需要设置我们需要使用的中断就可以了。),程序中我们暂不处理这个问题,大家有点印象,就是多个中断同时发生的时候,优先级高的,可以被优先处理,如果优先级相同,就选择最先监测到中断的那个开始执行中断处理。还有一个会涉及到抢占,比如一个优先级的中断正在处理,但是一个比这个优先级更高的中断发生了,那么系统会再次打断正在处理的中断程序,处理更高优先级的事件。当然如果时低优先级的中断发生了,那么是不会被打断的。按键这个地方我们的优先级一般会设置一样,另一个角度来讲,几个按键同时触发中断的可能性极低,其实也不需要把某个按键做更紧急的处理(至少在我的程序中不需要这么处理)。
好,说了这么多,还是写程序,改动会比较大。
1.Keil的组件要增加外部中断的部分。
- 增加以下代码
- 设置按键中断模式
- 设置按键中断处理函数
- 主函数中修改按键的初始化函数,修改主循环(这里什么都不做,空转)
#include <stm32f4xx.h>
void led_init(void)
{
GPIO_InitTypeDef GPIO_Init;
//1.时钟使能
__HAL_RCC_GPIOE_CLK_ENABLE();
//2.端口设置
GPIO_Init.Mode = GPIO_MODE_OUTPUT_OD; //输出开漏模式
GPIO_Init.Pin = GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15;
GPIO_Init.Pull = GPIO_PULLUP;
GPIO_Init.Speed = GPIO_SPEED_FREQ_LOW;
// GPIO_Init.Alternate = ; //GPIO的mode设置位复用功能的时候,才会配置复用功能
HAL_GPIO_Init(GPIOE, &GPIO_Init);
//3.点亮或熄灭灯
}
/*按键初始化*/
void key_init(void)
{
GPIO_InitTypeDef GPIO_Init;
//1.时钟使能,按键使用的时GPIOE
__HAL_RCC_GPIOE_CLK_ENABLE();
//2.端口设置
GPIO_Init.Mode = GPIO_MODE_INPUT; //输入模式
GPIO_Init.Pin = GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12; //设置的引脚
GPIO_Init.Pull = GPIO_NOPULL; //上拉电阻可以有也可以没有,电路本身有上拉
// GPIO_Init.Speed = GPIO_SPEED_FREQ_LOW; //这个是设置输出速度,输入模式可以不管
// GPIO_Init.Alternate = ; //GPIO的mode设置位复用功能的时候,才会配置复用功能
HAL_GPIO_Init(GPIOE, &GPIO_Init);
}
/*按键中断方式初始化*/
void key_int_init(void)
{
GPIO_InitTypeDef GPIO_Init;
//1.时钟使能,按键使用的时GPIOE
__HAL_RCC_GPIOE_CLK_ENABLE();
//2.端口设置
GPIO_Init.Mode = GPIO_MODE_IT_FALLING; //下降沿中断模式
GPIO_Init.Pin = GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12; //设置的引脚
GPIO_Init.Pull = GPIO_NOPULL; //上拉电阻可以有也可以没有,电路本身有上拉
HAL_GPIO_Init(GPIOE, &GPIO_Init);
NVIC_SetPriority(EXTI15_10_IRQn,0xA0); //设置简单的中断优先级
NVIC_EnableIRQ(EXTI15_10_IRQn); //外部中断,10-15共用一个外部中断
}
/*
按键测试程序,我的例子实现为,(这是最简单的方法)
第一个按键按下的时候,第一个灯点亮
第二个按键按下的时候,第二个灯点亮
第三个按键按下的时候,第三个灯点亮
*/
int main(void)
{
int i;
GPIO_PinState pins;
HAL_Init(); //必须在第一个语句执行
led_init();
key_int_init();
while(1)
{
//什么事都不做,空转
}
}
void EXTI15_10_IRQHandler(void)
{
uint32_t ret;
EXTI_HandleTypeDef hexti;
int i;
uint32_t arr[3] = {EXTI_LINE_10,EXTI_LINE_11,EXTI_LINE_12};
uint32_t ledarr[3] = {GPIO_PIN_13,GPIO_PIN_14,GPIO_PIN_15};
for(i=0;i<3;i++)
{
hexti.Line = arr[i]; //哪一个外部中断线触发中断
ret = HAL_EXTI_GetPending(&hexti, EXTI_TRIGGER_FALLING);
if(ret)
{
//中断处理比较简单,直接翻转led
HAL_GPIO_TogglePin(GPIOE, ledarr[i]);
//清中断标志
HAL_EXTI_ClearPending(&hexti, EXTI_TRIGGER_FALLING); //下降沿
}
}
}
void SysTick_Handler(void)
{
HAL_IncTick();
}
github的工程源码: