题目:
参照外部中断的原理和代码示例,再结合之前已经实现的按键切换LED状态的实验,用外部中断改进其实现。
请自行参考文档《中断》当中,有关按键切换LED状态的内容, 自行连接电路图,基于外部中断机制,实现以下功能:
1.按键1,按下和释放后,点亮LED
2.按键2,按下和释放后,熄灭LED
3.按键3,按下和释放后,使得LED闪烁
具体按键的电路接法,可以直接参考文档的内容去实现。
关键点
分析:
按键外部中断实验
代码
#include "stm32f10x.h"
#include "Delay.h"
#define LED_NORMAL 0 // LED 正常状态(熄灭或者点亮)
#define LED_BLINK 1 // LED 闪烁状态
uint8_t LED_State = LED_NORMAL; // LED 默认正常状态
/**
* @brief 按键初始化,配置 PB6, PB8 和 PA0 为外部中断
* @param 无
* @retval 无
* 按键1控制LED点亮,它的两脚接入电源正极和PB6引脚,
* 按键2控制LED熄灭,它的两脚接入电源正极和PB8引脚,
* 按键3控制LED闪烁,它的两脚接入电源正极和PA0引脚.
*/
//按键引脚的的初始化一级配件相关的外部中断
void KEY_Init(void) {
// 开启 GPIOB 和 AFIO 时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
// 配置 PB6 和 PB8 为下拉输入模式(因为 按键 接正极,按下时会变为高电平)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_8;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; // 下拉输入模式
GPIO_Init(GPIOB, &GPIO_InitStructure);
// 配置 PA0 为上拉输入模式(因为 按键 接负极,按下时会变为低电平)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入模式
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 使用AFIO外设将三个按键引脚映射到对应达到EXTI线
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource6); // PB6
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource8); // PB8
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0); // PA0
EXTI_InitTypeDef EXTI_InitStructure;
// 配置 EXTI6 和 EXTI8(PB6 和 PB8)为上升沿触发
EXTI_InitStructure.EXTI_Line = EXTI_Line6 | EXTI_Line8;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; // 上升沿触发
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
// 配置 EXTI0(PA0)为下降沿触发
EXTI_InitStructure.EXTI_Line = EXTI_Line0; // 选择 PA0 对应的 EXTI0
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; // 下降沿触发
EXTI_Init(&EXTI_InitStructure);
// NVIC 配置初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStructure;
// 配置 NVIC,处理 EXTI6, EXTI8 和 EXTI0 的中断
// 优先级都设置为相同的优先级
NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn; // 处理 EXTI6 和 EXTI8 的中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; // 处理 EXTI0 的中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
/**
* @brief LED(PA3)初始化
* @param 无
* @retval 无
*/
void LED_Init(void) {
// 开启 GPIOA 时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// 配置 PA3 为开漏输出模式
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 设定初始状态为 LED 关闭(PA3 高阻态)
GPIO_SetBits(GPIOA, GPIO_Pin_3);
}
/**
* @brief 外部中断服务函数(ISR),处理 PB6 和 PB8 按键中断
* @param 无
* @retval 无
*/
void EXTI9_5_IRQHandler(void) {
// 检查是否是 PB6 触发的中断
if (EXTI_GetITStatus(EXTI_Line6) == SET) {
// 清除 EXTI6 中断标志
EXTI_ClearITPendingBit(EXTI_Line6);
// 按键持续按下,等待弹起
while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_6) == Bit_SET);
// 点亮 LED,设置LED为正常模式
LED_State = LED_NORMAL;
GPIO_ResetBits(GPIOA, GPIO_Pin_3); // 低电平,点亮 LED
}
// 检查是否是 PB8 触发的中断
if (EXTI_GetITStatus(EXTI_Line8) == SET) {
// 清除 EXTI8 中断标志
EXTI_ClearITPendingBit(EXTI_Line8);
// 按键持续按下,等待弹起
while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_8) == Bit_SET);
// 点亮 LED,设置LED为正常模式
LED_State = LED_NORMAL;
GPIO_SetBits(GPIOA, GPIO_Pin_3); // 高电平,熄灭 LED
}
}
/**
* @brief 外部中断服务函数(ISR),处理 PA0 按键中断,LED闪烁
* @param 无
* @retval 无
*/
void EXTI0_IRQHandler(void) {
if (EXTI_GetITStatus(EXTI_Line0) == SET) {
// 清除 EXTI0 中断标志
EXTI_ClearITPendingBit(EXTI_Line0);
while (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == Bit_RESET);
LED_State = LED_BLINK;
}
}
int main(void) {
LED_Init(); // 初始化 LED
KEY_Init(); // 初始化 PB6, PB8 和 PA0 按键
while (1) {
if (LED_State == LED_BLINK) {
// LED 进入闪烁状态
GPIO_ResetBits(GPIOA, GPIO_Pin_3); // 点亮 LED
Delay_Ms(100); // 延时 100ms
/*
Delay_Ms函数是使用中断来实现的
若LED在闪烁状态下,按压点亮LED按键
恰好进入了上一个Delay_Ms函数
等它中断延时结束就会执行LED熄灭操作
这样点亮LED按键的功能就会失效,所以这里要加一个判断
然后跳过后面的熄灯逻辑
*/
if (LED_State == LED_NORMAL) {
continue;
}
GPIO_SetBits(GPIOA, GPIO_Pin_3); // 熄灭 LED
Delay_Ms(100); // 延时 100ms
}
}
}
闪烁
解决方案总结:
: