基于外部中中断机制,实现以下功能: 1.按键1,按下和释放后,点亮LED 2.按键2,按下和释放后,熄灭LED 3.按键3,按下和释放后,使得LED闪烁

题目:

参照外部中断的原理和代码示例,再结合之前已经实现的按键切换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 默认正常状态
void KEY_GPIO_Init(void);//初始化GPIO引脚
void KEY_AFIO_Map(void);//AFIO映射PA11到EXTI_Line11
void KEY_EXTI_Init(void);//初始化 EXTI 配置(EXTI_Line11)
void KEY_NVIC_Init(void);//初始化NVIC,开启EXTI9_5_IRQn外部中断
void EXTI9_5_IRQHandler(void);//中断服务程序
void EXTI0_IRQHandler(void);
int main(void) {
    KEY_GPIO_Init();
    KEY_AFIO_Map();
    KEY_EXTI_Init();
    KEY_NVIC_Init();
    GPIO_SetBits(GPIOA, GPIO_Pin_5);

    while (1) {
        if (LED_State == LED_BLINK) {
            GPIO_SetBits(GPIOA, GPIO_Pin_5);
            Delay_Ms(100);

            //当LED闪烁时如果不加入这行代码,
            //可能会引发按键操作点亮时,LED表现为熄灭状态
            //这是因为当GPIO_Pin_6引发中断时正好处在延迟时候,while循环没结束
            //当GPIO_Pin_6中断执行后,可能会执行GPIO_ResetBits(GPIOA,GPIO_Pin_5);
            if (LED_State == LED_NORMAL) {
                continue;
            }

            GPIO_ResetBits(GPIOA, GPIO_Pin_5);
            Delay_Ms(100);
        }

    }
}

void KEY_GPIO_Init(void) {
    //开启GPIO外设时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE);
    //初始化P0引脚
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    //上拉输入模式
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    //初始化P6和P8引脚
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_8;
    //下拉输入模式
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
    //推免输出模式
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
}

void KEY_AFIO_Map(void) {
    //开启AFIO外设时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
    // 映射 PA0 到 EXTI_Line0
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);
    // 映射 PB6 到 EXTI_Line6
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource6);
    // 映射 PB8 到 EXTI_Line8
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource8);

}

void KEY_EXTI_Init(void) {
    EXTI_InitTypeDef EXTI_InitStructure;
    EXTI_InitStructure.EXTI_Line = EXTI_Line0 ;
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //中断模式
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //采用下降沿触发
    EXTI_Init(&EXTI_InitStructure);

    EXTI_InitStructure.EXTI_Line = EXTI_Line6 | EXTI_Line8 ;
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //中断模式
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; //采用上升沿触发
    EXTI_Init(&EXTI_InitStructure);

}

void EXTI9_5_IRQHandler(void) {
    if (EXTI_GetITStatus(EXTI_Line6) == SET) {
        // 清除中断标志
        EXTI_ClearITPendingBit(EXTI_Line6);

        //检查引脚输入,如果是高电平说明按下还没有松开。
        while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_6) == Bit_SET);
LED_State = LED_NORMAL;
        //低电平点亮LED
        GPIO_ResetBits(GPIOA, GPIO_Pin_5);
        
    }

    if (EXTI_GetITStatus(EXTI_Line8) == SET) {
        // 清除中断标志
        EXTI_ClearITPendingBit(EXTI_Line8);

        //检查引脚输入,如果是高电平说明按下还没有松开。
        while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_8) == Bit_SET);
LED_State = LED_NORMAL;
        //高电平熄灭LED
        GPIO_SetBits(GPIOA, GPIO_Pin_5);
        
    }

}

void EXTI0_IRQHandler(void) {
    if (EXTI_GetITStatus(EXTI_Line0) == SET) {
        // 清除中断标志
        EXTI_ClearITPendingBit(EXTI_Line0);

		Delay_Ms(20);  // 简单消抖

        while (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == Bit_RESET);

        LED_State = LED_BLINK;
    }


}


//  初始化NVIC外设
void KEY_NVIC_Init(void) {
    //将优先级进行分组
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    //初始化NVIC外设
    NVIC_InitTypeDef NVIC_InitStruct;
    //设置中断号
    NVIC_InitStruct.NVIC_IRQChannel = EXTI9_5_IRQn;
    //开启中断
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    //初始化抢占优先级
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
    //初始化子优先级
    NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
    NVIC_Init(&NVIC_InitStruct);

    NVIC_InitStruct.NVIC_IRQChannel = EXTI0_IRQn;
    //开启中断
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    //初始化抢占优先级
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
    //初始化子优先级
    NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
    NVIC_Init(&NVIC_InitStruct);

}


#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
        }
    }
}

	

在这里插入图片描述
闪烁这里线没接对,竖着才行,竖着才是联通的.
在这里插入图片描述
在这里插入图片描述
修正图片
在这里插入图片描述


解决方案总结:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值