【蓝桥杯嵌入式(G431-HAL库)】Led 与按键

【备赛蓝桥杯嵌入式(G431-HAL库)】Led 与按键

实训平台:STM32G431RBT6

辅助工具:STM32CubeMx

01 基本简介

Led和按键是GPIO口输入和输出的应用。

Led考察GPIO口的输出,按键则考察GPIO口电平的读取,即输入。

需要使用HAL库里的读写函数,如下:

//HAL_GPIO_WritePin(GPIO口,引脚,引脚状态)
//HAL_GPIO_ReadPin(GPIO口,引脚)
//引脚状态如下:GPIO_PIN_RESET【让引脚输出低电平】
// GPIO_PIN_SET【让引脚输出高电平】

02 LED

2.1 电路介绍

LED电路图

① 锁存器:简单理解成一个开关,需要开启才能进行之后的操作。更详细知识百度。
(2023年2月更正)理解成开关加存储器的结合体更为贴切,当锁存器使能时,可以用于数据交换;交换后的数据就被关起来了,暂时无法更改,直到下一次锁存器使能进行数据交换。

② 由图可知,LED拥有公共的VDD,而为了让二极管导通,PCx引脚需要输入低电平(即0)

③由锁存器的知识可知,PD2引脚需要是有效电平,才能让PCx引脚工作,如图可得,PD2需要输入高电平(即1)

2.2 代码参考

2.2.0 CubeMx配置
  • RCC时间配置

RCC

  • 时钟树配置

    频率是80MHz,系数为2 20 2,选择PLLCLK为时钟源即可,如图

时钟树

  • LED引脚配置

在这里插入图片描述

其余的默认即可

在这里插入图片描述

2.2.1 基本操作

(以GPIO_PIN_8,即LD1为例)

/* 点亮 */
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_8,GPIO_PIN_RESET);
/* 熄灭 */
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_8,GPIO_PIN_SET);
/* 翻转 */
HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_8);

也可以直接对其寄存器GPIOC->ODR进行操作,但可能用到位操作,理解和实际操作更容易出错

 /* GPIO_PIN_8 地址为0x0100 */
 /* 想点亮第几个灯就位移几位,i需要从0开始,上限为7 */
 GPIOC->ODR = ~(0x0100 << i);
 GPIOC->ODR = (0x0100 << i);
2.2.2 其他操作
  • 初始化

    //GPIOC->ODR = 0xff00; //直接的对寄存器操作
    HAL_GPIO_WritePin(GPIOC,GPIO_PIN_All,GPIO_SET);
    HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_SET);
    HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_RESET);
    
  • LED状态附加

    /* 假如需要让LD1每隔0.5s闪烁一次,LD2每隔1s闪烁一次 */
    void Led_Disp(uint8_t ucLed){
        GPIOC->ODR = ~(ucLed << 8);
      HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_SET);
    HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_RESET);
    }
    /* time 用于计算时间,单位是ms */
    void LedFunc(){
        if(time == 500) ucLed ^= (1<<0);
        if(time == 1000) ucLed ^= (1<<1);
        Led_Disp(ucLed);
    }
    /*
    格式: ucLed ^= (1 << n);
    n为LD的编号,从0开始
    */
    

03 按键

3.1电路介绍

在这里插入图片描述

如图所示,可以看到按键输入端需要输入低电平,才能让按键电路导通。

并且电路图里每个按键都配有10k的电阻,即上拉电阻

3.2 消抖

按键消抖_百度百科 (baidu.com)

为什么要消除抖动:

为了让按键能实现按下一次就一个按键响应。

3.3 代码参考

3.3.0 Cube配置
  • 引脚配置

在这里插入图片描述

在这里插入图片描述

3.3.1 普通版
/* 按键功能:按下B1,让LD7状态翻转 */
if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0) == 0){
			HAL_Delay(10);
			if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0) == 0){
				HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_14);
				HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
				HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
			}
		}

思路:延时段时间(尽可能小)后再判断按键是否按下

缺点:延时采用了阻塞模式,容易造成按键按下一次实际产生两次的效果或按下后较长时间才反应过来

3.3.2 状态机

一般和定时器结合。

详细了解:状态机_百度百科 (baidu.com)

//宏定义 读取各个按键键值
#define B1 	HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0)
#define B2 	HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1)
#define B3 	HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2)
#define B4 	HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)

//定义按键状态:0-未按下,1-已经按下,2-持续按下
#define KEY_STATE_0 0
#define KEY_STATE_1 1
#define KEY_STATE_2 2

//按键扫描函数
uint8_t key_scanf(void){
    uint8_t key_return = 0;
    static uint8_t key_state;
    
 //   if(B1 && B2 && B3 && B4) key_state = KEY_STATE_0;
    
    switch(key_state){
			case 0:
				if(B1 && B2 && B3 && B4){ //所有按键都未按下,保持状态0
					key_state = KEY_STATE_0;
				}
				else{ //有按键按下
					key_state = KEY_STATE_1;
				}
				break;
			case 1:
				if(B1 && B2 && B3 && B4){ //用于软件消抖
					key_state = KEY_STATE_0;
				}
				else{ //判断键值
					if(!B1) key_return = 1;
					else if(!B2) key_return = 2;
					else if(!B3) key_return = 3;
					else if(!B4) key_return = 4;
					key_state = KEY_STATE_2;
				}
				break;
			case 2:
				if(B1 && B2 && B3 && B4){
					key_state = KEY_STATE_0;
				}
				break;    
    }
		
		return key_return;
}

void key_switch(){
	
	uint8_t key_val;
    /* 对该值需要有限定,即只能取相应的键值编码
    	随意取值可能造成按键功能混乱
    	尽量要有初始值
    */
	
	key_val = key_scanf();//一般10ms读取一次
    /*
    设置flag,如下:
    if(key_flag){
    	key_flag = 0;
    	key_val = key_scanf();
    }
    */
	
	if(key_val == 1){
		HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_14);
		HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
		HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
	}
	else if(key_val == 2){
		HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_15);
		HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
		HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
	}
	else if(key_val == 3){
		HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_8);
		HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
		HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
	}
	else if(key_val == 4){
		HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_9);
		HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
		HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
	}
}
3.3.3 长短按

当一个按键有长按和短按两个状态时,按键的短按不会影响到长按。

//宏定义 读取各个按键键值
#define B1 	HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0)
#define B2 	HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1)
#define B3 	HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2)
#define B4 	HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)

//定义按键状态:0-未按下,1-已经按下,2-持续按下
#define KEY_STATE_0 0
#define KEY_STATE_1 1
#define KEY_STATE_2 2

uint8_t keyOld = 0,keyLong = 0;
uint16_t keyTime = 0;
extern unsigned int ulTick_ms;//用滴答计时器计时
extern uint8_t keyFlag;

//按键扫描函数
uint8_t key_scanf(void){
    uint8_t key_return = 0;
    static uint8_t key_state;
    
    switch(key_state){
			case 0:
				if(B1 && B2 && B3 && B4){ //所有按键都未按下,保持状态0
					key_state = KEY_STATE_0;
				}
				else{ //有按键按下
					key_state = KEY_STATE_1;
				}
				break;
			case 1:
				if(B1 && B2 && B3 && B4){ //用于软件消抖
					key_state = KEY_STATE_0;
				}
				else{ //判断键值
					if(!B1) keyOld = 1;//暂时不赋返回值
					else if(!B2) keyOld = 2;
					else if(!B3) keyOld = 3;
					else if(!B4) keyOld = 4;
					keyTime = ulTick_ms;
					key_state = KEY_STATE_2;
				}
				break;
			case 2:
				if(B1 && B2 && B3 && B4){
					key_state = KEY_STATE_0;
					if(ulTick_ms - keyTime > 800){
						keyLong = 1;
						ulTick_ms = 0;
						keyTime = 0;
					}
					key_return = keyOld;//长短按已经判断完毕,赋值
				}
				break;    
    }
		return key_return;
}

void key_switch(){
	
	uint8_t key_val;
	
	if(keyFlag){ keyFlag = 0;key_val = key_scanf();}
	
	if(keyLong == 1){
		if(key_val == 1){
			keyLong = 0;
			HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_10);
			HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
			HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
		}
	}
	else{
			//短按
		if(key_val == 1){
			HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_14);
			HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
			HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
		}
	}
}
  • 7
    点赞
  • 55
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值