按键中断
从主函数开始
#include "main.h"
#include "driver_key.h"
#include "driver_led.h"
int main(void)
{
// 初始化HAL库函数必须要调用此函数
HAL_Init();
/*
* 系统时钟即AHB/APB时钟配置
* 使用外部高速时钟HSE(8MHz)配置系统时钟,经过PLL放大9倍,得到72MHz
*/
SystemClock_Config();
// 初始化按键
KeyInit();
//初始化LED
LedGpioInit();
while(1)
{
}
}
/*
* 函数名:void Error_Handler(void)
* 输入参数:无
* 输出参数:无
* 返回值:无
* 函数作用:程序错误处理函数,此处暂时设为死循环,不做任何动作
*/
void Error_Handler(void)
{
while(1)
{
}
}
Step1:初始化库函数及HAL_Init():
Step2:系统时钟配置SystemClock_Config();
Step3:初始化按键KeyInit(); 这是和上一个使用按键轮询方式区别最大的函数
- 进入KeyInit();
/*
* 函数名:void KeyInit(void)
* 输入参数:无
* 输出参数:无
* 返回值:无
* 函数作用:初始化按键的引脚,配置为下降沿触发外部中断
*/
void KeyInit(void)
{
// 定义GPIO的结构体变量
GPIO_InitTypeDef GPIO_InitStruct = {0};
// 使能按键的GPIO对应的时钟
KEY_UP_GPIO_CLK_EN();
KEY_DOWN_GPIO_CLK_EN();
KEY_LEFT_GPIO_CLK_EN();
KEY_RIGHT_GPIO_CLK_EN();
GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING; // 设置为下降沿触发外部中断
GPIO_InitStruct.Pull = GPIO_PULLUP; // 默认上拉
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; // 引脚反转速度设置为快
// 初始化'Up'键引脚配置
GPIO_InitStruct.Pin = KEY_UP_GPIO_PIN; // 选择按键的引脚
HAL_GPIO_Init(KEY_UP_GPIO_PORT, &GPIO_InitStruct);
// 初始化'Down'键引脚配置
GPIO_InitStruct.Pin = KEY_DOWN_GPIO_PIN; // 选择按键的引脚
HAL_GPIO_Init(KEY_DOWN_GPIO_PORT, &GPIO_InitStruct);
// 初始化Left'键引脚配置
GPIO_InitStruct.Pin = KEY_LEFT_GPIO_PIN; // 选择按键的引脚
HAL_GPIO_Init(KEY_LEFT_GPIO_PORT, &GPIO_InitStruct);
// 初始化'Right'键引脚配置
GPIO_InitStruct.Pin = KEY_RIGHT_GPIO_PIN; // 选择按键的引脚
HAL_GPIO_Init(KEY_RIGHT_GPIO_PORT, &GPIO_InitStruct);
/* EXTI interrupt init*/
HAL_NVIC_SetPriority(EXTI0_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(EXTI0_IRQn);
HAL_NVIC_SetPriority(EXTI3_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(EXTI3_IRQn);
HAL_NVIC_SetPriority(EXTI15_10_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);
}
第一步使能时钟:__HAL_RCC_GPIOx_CLK_ENABLE()
为了方便使用,使用宏定义:
#define KEY_UP_GPIO_CLK_EN() __HAL_RCC_GPIOA_CLK_ENABLE()
#define KEY_DOWN_GPIO_CLK_EN() __HAL_RCC_GPIOG_CLK_ENABLE()
#define KEY_LEFT_GPIO_CLK_EN() __HAL_RCC_GPIOC_CLK_ENABLE()
#define KEY_RIGHT_GPIO_CLK_EN() __HAL_RCC_GPIOE_CLK_ENABLE()
第二步配置结构体:Pin引脚、Mode模式、Pull上拉或者推挽
GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING; // 设置为下降沿触发外部中断
GPIO_InitStruct.Pull = GPIO_PULLUP; // 默认上拉
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
第三步初始化GPIOHAL_GPIO_Init(GPIO_TypeDef *GPIOx,GPIO_InitTypeDef *GPIO_Init)
初始化每个按键对应的GPIO,“HAL_GPIO_Init()”里会判断该引脚是否为EXTI模式,如果是则调用“__HAL_RCC_AFIO_CLK_ENABLE()”使能AFIO时钟;
// 初始化'Up'键引脚配置
GPIO_InitStruct.Pin = KEY_UP_GPIO_PIN; // 选择按键的引脚
HAL_GPIO_Init(KEY_UP_GPIO_PORT, &GPIO_InitStruct);
// 初始化'Down'键引脚配置
GPIO_InitStruct.Pin = KEY_DOWN_GPIO_PIN; // 选择按键的引脚
HAL_GPIO_Init(KEY_DOWN_GPIO_PORT, &GPIO_InitStruct);
// 初始化Left'键引脚配置
GPIO_InitStruct.Pin = KEY_LEFT_GPIO_PIN; // 选择按键的引脚
HAL_GPIO_Init(KEY_LEFT_GPIO_PORT, &GPIO_InitStruct);
// 初始化'Right'键引脚配置
GPIO_InitStruct.Pin = KEY_RIGHT_GPIO_PIN; // 选择按键的引脚
HAL_GPIO_Init(KEY_RIGHT_GPIO_PORT, &GPIO_InitStruct);
第四步设置EXTI中断线的优先级和使能
/* EXTI interrupt init*/
HAL_NVIC_SetPriority(EXTI0_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(EXTI0_IRQn);
HAL_NVIC_SetPriority(EXTI3_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(EXTI3_IRQn);
HAL_NVIC_SetPriority(EXTI15_10_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);
}
Step4:中断处理函数
当中断发生后,则自动跳到中断向量表(startup.stm32f103xe.s)中所对应的中断函数所在位置,执行中断函数的内容
__Vectors DCD __initial_sp ; Top of Stack
DCD Reset_Handler ; Reset Handler
DCD NMI_Handler ; NMI Handler
DCD HardFault_Handler ; Hard Fault Handler
DCD MemManage_Handler ; MPU Fault Handler
DCD BusFault_Handler ; Bus Fault Handler
DCD UsageFault_Handler ; Usage Fault Handler
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD SVC_Handler ; SVCall Handler
DCD DebugMon_Handler ; Debug Monitor Handler
DCD 0 ; Reserved
DCD PendSV_Handler ; PendSV Handler
DCD SysTick_Handler ; SysTick Handler
; External Interrupts
DCD WWDG_IRQHandler ; Window Watchdog
DCD PVD_IRQHandler ; PVD through EXTI Line detect
DCD TAMPER_IRQHandler ; Tamper
DCD RTC_IRQHandler ; RTC
DCD FLASH_IRQHandler ; Flash
DCD RCC_IRQHandler ; RCC
DCD EXTI0_IRQHandler ; EXTI Line 0
DCD EXTI1_IRQHandler ; EXTI Line 1
DCD EXTI2_IRQHandler ; EXTI Line 2
DCD EXTI3_IRQHandler ; EXTI Line 3
DCD EXTI4_IRQHandler ; EXTI Line 4
DCD DMA1_Channel1_IRQHandler ; DMA1 Channel 1
DCD DMA1_Channel2_IRQHandler ; DMA1 Channel 2
DCD DMA1_Channel3_IRQHandler ; DMA1 Channel 3
DCD DMA1_Channel4_IRQHandler ; DMA1 Channel 4
DCD DMA1_Channel5_IRQHandler ; DMA1 Channel 5
DCD DMA1_Channel6_IRQHandler ; DMA1 Channel 6
DCD DMA1_Channel7_IRQHandler ; DMA1 Channel 7
DCD ADC1_2_IRQHandler ; ADC1 & ADC2
DCD USB_HP_CAN1_TX_IRQHandler ; USB High Priority or CAN1 TX
DCD USB_LP_CAN1_RX0_IRQHandler ; USB Low Priority or CAN1 RX0
DCD CAN1_RX1_IRQHandler ; CAN1 RX1
DCD CAN1_SCE_IRQHandler ; CAN1 SCE
DCD EXTI9_5_IRQHandler ; EXTI Line 9..5
DCD TIM1_BRK_IRQHandler ; TIM1 Break
DCD TIM1_UP_IRQHandler ; TIM1 Update
DCD TIM1_TRG_COM_IRQHandler ; TIM1 Trigger and Commutation
DCD TIM1_CC_IRQHandler ; TIM1 Capture Compare
DCD TIM2_IRQHandler ; TIM2
DCD TIM3_IRQHandler ; TIM3
DCD TIM4_IRQHandler ; TIM4
DCD I2C1_EV_IRQHandler ; I2C1 Event
DCD I2C1_ER_IRQHandler ; I2C1 Error
DCD I2C2_EV_IRQHandler ; I2C2 Event
DCD I2C2_ER_IRQHandler ; I2C2 Error
DCD SPI1_IRQHandler ; SPI1
DCD SPI2_IRQHandler ; SPI2
DCD USART1_IRQHandler ; USART1
DCD USART2_IRQHandler ; USART2
DCD USART3_IRQHandler ; USART3
DCD EXTI15_10_IRQHandler ; EXTI Line 15..10
DCD RTC_Alarm_IRQHandler ; RTC Alarm through EXTI Line
DCD USBWakeUp_IRQHandler ; USB Wakeup from suspend
DCD TIM8_BRK_IRQHandler ; TIM8 Break
DCD TIM8_UP_IRQHandler ; TIM8 Update
DCD TIM8_TRG_COM_IRQHandler ; TIM8 Trigger and Commutation
DCD TIM8_CC_IRQHandler ; TIM8 Capture Compare
DCD ADC3_IRQHandler ; ADC3
DCD FSMC_IRQHandler ; FSMC
DCD SDIO_IRQHandler ; SDIO
DCD TIM5_IRQHandler ; TIM5
DCD SPI3_IRQHandler ; SPI3
DCD UART4_IRQHandler ; UART4
DCD UART5_IRQHandler ; UART5
DCD TIM6_IRQHandler ; TIM6
DCD TIM7_IRQHandler ; TIM7
DCD DMA2_Channel1_IRQHandler ; DMA2 Channel1
DCD DMA2_Channel2_IRQHandler ; DMA2 Channel2
DCD DMA2_Channel3_IRQHandler ; DMA2 Channel3
DCD DMA2_Channel4_5_IRQHandler ; DMA2 Channel4 & Channel5
__Vectors_End
因此我们需要编写中断函数的内容。
/*
* 函数名:void EXTI0_IRQHandler(void)
* 输入参数:无
* 输出参数:无
* 返回值:无
* 函数作用:外部中断0的中断处理函数
*/
void EXTI0_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(KEY_UP_GPIO_PIN);
}
/*
* 函数名:void EXTI3_IRQHandler(void)
* 输入参数:无
* 输出参数:无
* 返回值:无
* 函数作用:外部中断3的中断处理函数
*/
void EXTI3_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(KEY_RIGHT_GPIO_PIN);
}
/*
* 函数名:void EXTI15_10_IRQHandler(void)
* 输入参数:无
* 输出参数:无
* 返回值:无
* 函数作用:外部中断10-15的中断处理函数
*/
void EXTI15_10_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(KEY_DOWN_GPIO_PIN);
HAL_GPIO_EXTI_IRQHandler(KEY_LEFT_GPIO_PIN);
}
每个中断处理函数里,都调用的“HAL_GPIO_EXTI_IRQHandler()”准备后续处理,传入参数为外部中断的引脚号
进入到HAL_GPIO_EXTI_IRQHandler()
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);//回调函数
}
}
进入“HAL_GPIO_EXTI_Callback()
”回调函数
static volatile bool up_flag = false; // 定义一个全局静态标志,用以判断按键按下的次数,上下左右键类似
static volatile bool down_flag = false;
static volatile bool left_flag = false;
static volatile bool right_flag = false;
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
switch(GPIO_Pin) // 判断是哪个按键
{
case KEY_UP_GPIO_PIN: // 如果是上键
{
up_flag = !up_flag; // 按下一次标志翻转一次
RLED(OFF);
GLED(up_flag?OFF:ON); // 根据标志控制绿灯的亮灭
BLED(OFF);
break;
}
case KEY_DOWN_GPIO_PIN: // 如果是下键
{
down_flag = !down_flag; // 按下一次标志翻转一次
RLED(down_flag?OFF:ON); // 三色灯全灭或全亮
GLED(down_flag?OFF:ON);
BLED(down_flag?OFF:ON);
break;
}
case KEY_LEFT_GPIO_PIN: // 如果是左键
{
left_flag = !left_flag; // 按下一次标志翻转一次
RLED(left_flag?OFF:ON); // 根据标志控制红灯的亮灭
GLED(OFF);
BLED(OFF);
break;
}
case KEY_RIGHT_GPIO_PIN: // 如果是右键
{
right_flag = !right_flag; // 按下一次标志翻转一次
RLED(OFF); // 根据标志控制蓝灯的亮灭
GLED(OFF);
BLED(right_flag?OFF:ON);
break;
}
default:break;
}
}
主函数只需初始化LED和按键,无需任何操作。一旦按键按下产生中断,将自动跳转到对应中断向量位置,调用该位置的中断处理函数。