通过外部中断来检测四个按键按下的状态:
WK_UP 控制蜂鸣器响和停
KEY0 控制 LED_R 互斥点亮
KEY1 控制 LED_G 互斥点亮
KEY2 控制 LED_B 互斥点亮。
中断的基本概念:
中断请求(IRQ):
当发生某个特定事件(例如硬件信号、定时器溢出等)时,相关的外部或内部设备会向处理器发出中断请求。这种请求通知处理器有事情需要处理。
中断服务程序(ISR):
当处理器接收到中断请求后,它会暂停当前的执行,保存当前的状态,并跳转到预先定义的中断服务程序(ISR)。ISR 是一段特定的代码,用于处理中断事件。
中断优先级:
处理器可以为不同的中断分配不同的优先级,以决定在多个中断发生时,哪个中断首先得到处理。高优先级的中断会抢占低优先级的中断。
中断响应流程:
中断触发:外部事件或内部条件触发中断请求。
中断信号:处理器收到中断信号,暂停当前任务。
保存状态:处理器保存当前执行状态,以便中断处理完成后能够恢复。
执行 ISR:跳转到中断服务程序,处理具体的中断事件。
恢复状态:中断处理完成后,恢复之前保存的状态,继续执行被中断的任务。
中断向量表:
中断向量表是一个存储中断服务程序入口地址的表格。处理器通过查找中断向量表来找到并执行相应的 ISR。
中断的类型:
外部中断:
外部中断(External Interrupt)指的是由外部事件触发的中断信号,用于处理来自微控制器外部的信号或事件。这种中断通常由外部硬件信号(如按钮、传感器或其他设备)产生,并被用于通知微控制器有重要事件需要处理。
外部中断的配置通常涉及设置中断触发条件(例如边沿触发或电平触发)、中断优先级和使能中断请求。STM32 微控制器通过 EXTI(External Interrupt/Event Controller)模块来配置外部中断。
外部中断广泛用于处理事件驱动任务,如按钮按下检测、外部信号捕获、时钟脉冲计数等。
内部中断:
由内部外设或系统模块产生的中断,如定时器溢出、USART 接收数据、ADC 转换完成等。
系统异常:
处理器内的异常事件,如硬件故障、内存管理错误等。
在 STM32 微控制器中,中断的设置和使用通常包括以下步骤:
配置外设或 GPIO:设置需要触发中断的外设或引脚。
配置中断线:设置 EXTI 模块或其他中断控制器,指定中断源和触发条件。
使能中断:在 NVIC 中使能相应的中断线,设置中断优先级。
编写 ISR:实现中断服务程序来处理特定的中断事件。
例如,配置一个定时器中断:
cpp
// 定时器中断服务程序
void TIM1_UP_IRQHandler(void)
{
if (TIM1->SR & TIM_SR_UIF) // 检查更新中断标志
{
TIM1->SR &= ~TIM_SR_UIF; // 清除中断标志
// 执行中断处理代码
}
}
// 配置定时器并使能中断
void Timer_Init(void)
{
// 初始化定时器
// ...
// 启用定时器中断
HAL_NVIC_EnableIRQ(TIM1_UP_IRQn);
}
外部中断回调函数:
在 STM32 HAL 库中,HAL_GPIO_EXTI_Callback 是外部中断回调函数的标准命名。这个命名是由 HAL 库规定的,用于处理 GPIO 外部中断的回调。如果你想使用这个回调函数,你需要在你的代码中实现它,并在其中定义当外部中断触发时的具体行为。
cpp
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if (GPIO_Pin == GPIO_PIN_0)
{
// 处理引脚 0 的外部中断
}
else if (GPIO_Pin == GPIO_PIN_1)
{
// 处理引脚 1 的外部中断
}
// 其他引脚的处理
}
注意: 这个命名是为了确保与 HAL 库的兼容性和一致性。如果你在自定义项目中使用不同的命名,可能会影响到 HAL 库的功能调用。
HAL_GPIO_TogglePin 函数(高/低翻转):
HAL_GPIO_TogglePin 是 STM32 HAL 库中的一个函数,用于翻转(切换)指定 GPIO 引脚的状态。这意味着,如果引脚当前的电平是高(GPIO_PIN_SET),函数会将其切换为低(GPIO_PIN_RESET);如果电平是低,函数则会将其切换为高。
函数原型:
void HAL_GPIO_TogglePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
参数:
GPIOx :指定 GPIO 端口。例如:GPIOA, GPIOB, GPIOC 等。这个参数决定了要操作哪个 GPIO端口。
GPIO_Pin:指定要操作的 GPIO 引脚。可以是以下形式的掩码:GPIO_PIN_0, GPIO_PIN_1, GPIO_PIN_2, ..., GPIO_PIN_n(n 是引脚编号),或者多个引脚的位掩码组合,例 如GPIO_PIN_0 | GPIO_PIN_1。
作用是将指定的 GPIO 引脚的状态从高电平翻转为低电平,或从低电平翻转为高电平。这对于需要周期性切换引脚状态的应用非常有用,例如控制 LED 的闪烁。
实验开始:
- 具体的 pgio 引脚配置如下,蜂鸣器和 RGB 灯和以前一样配置,按键 KEY0(PD10) 、KEY1(PD9)、KEY2(PD8) 、WK_UP(PC13) 配置外部中断模式。配置串口 1 用于调试。
2、将按键 KEY0(PD10) 、KEY1(PD9)、KEY2(PD8) 配置为下降沿触发的外部中断模式,按键 WK_UP(PC13) 配置为上升沿触发的外部中断模式。这里举例为按键 WK_UP(PC13) 配置为上升沿触发的外部中断模式。
NVIC 使能中断线路组
(配置完生成代码)
EXTI (External Interrupt) line [9:5] 是 STM32 微控制器中的一个外部中断线路组,用于处理 GPIO 引脚 5 到 9 的外部中断请求。这一组中的每一条中断线都可以连接到对应的 GPIO 引脚,当引脚的电平发生变化时,触发相应的中断服务程序(ISR)。
3、在 HAL 库中,中断运行完后会先进入相对应的中断回调函数,处理完该函数后,才会退出中断。我们一般将中断需要处理代码会放在中断回调函数中去运行。
中断回调函数:void HAL_GPIO_EXTI_Callback*(uint16_t GPIO_Pin);
这里在 main.c 里可以定义 void HAL_GPIO_EXTI_Callback*(uint16_t GPIO_Pin); 函数编写逻辑代码,完成本实验。主函数可以空置,只需等待中断触发外部中断回调函数即可。
cpp
/* USER CODE BEGIN 4 */
// 外部中断回调函数
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
switch (GPIO_Pin)
{
case GPIO_PIN_8: // KEY2
HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_9);
printf("LED_B state transition\n");
break;
case GPIO_PIN_9: // KEY1
HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_8);
printf("LED_G state transition\n");
break;
case GPIO_PIN_10: // KEY0
HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_7);
printf("LED_R state transition\n");
break;
case GPIO_PIN_13: // WK_UP
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_2);
printf("beep state transition\n");
break;
default:
break;
}
}
/* USER CODE END 4 */
中断优先级的配置
在 STM32 中,中断优先级通常分为两个部分:
抢占优先级(Preemption Priority) : 决定了中断之间的优先处理顺序。抢占优先级高的中断可以打断抢占优先级低的中断。
响应优先级(Sub Priority): 决定了具有相同抢占优先级的中断的处理顺序。
通过以下函数可以设置中断优先级:
HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t PreemptPriority, uint32_t SubPriority);
示例:配置中断优先级
假设有三个中断源,分别是 EXTI0、EXTI1 和 EXTI2。我们希望 EXTI0 拥有最高的优先级,EXTI1 次之,EXTI2 优先级最低。可以通过以下代码设置:
// 设置 EXTI0 中断优先级为最高(抢占优先级 0,响应优先级 0)
HAL_NVIC_SetPriority(EXTI0_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(EXTI0_IRQn);
// 设置 EXTI1 中断优先级为次高(抢占优先级 1,响应优先级 0)
HAL_NVIC_SetPriority(EXTI1_IRQn, 1, 0);
HAL_NVIC_EnableIRQ(EXTI1_IRQn);
// 设置 EXTI2 中断优先级为最低(抢占优先级 2,响应优先级 0)
HAL_NVIC_SetPriority(EXTI2_IRQn, 2, 0);
HAL_NVIC_EnableIRQ(EXTI2_IRQn);