stm32 蓝桥杯 物联网 独立键盘的使用

embedded/2025/3/14 15:08:50/

蓝桥杯物联网平台里面,有5个外接设备,其中有一个就是6个独立按键。首先,我们先看一下按键有关的电路图。

电路图与cubemx设定

由图可见,独立键盘组由两行三列构成,我们通过行列来锁定要访问的独立按键在哪。ROW1挂载在CN1的2上,对应PA12,其他的几条线也是如此。

先通过stm32 cubemx来初始化io口,因为按下去对应的是down,因此我们默认输入口为pull-up。在这里我们选择扫描两次,每次扫描三个按键。因此我们设定两个ROW为输出口,COL1~COL3为输入口。 

 task.h设定

首先,因为我们要使用原先按键的三段式防抖结构,因此我们需要定义好各个按键的的属性。因为该开发板是模块化设计,除了6个独立按键外还可能会使用其他的模块(温度等),因此我们需要进行一次区分,如果是使用独立按键,我们就define Using_BTN,如果不是用这个,就注释掉即可。屏幕的状态也需进行改变,增加了一个State_Key用于显示我们按下独立按键后的变化。

#define KB1 0x11
#define KB2 0x12
#define KB3 0x13
#define KB4 0x14
#define KB5 0x15
#define KB6 0x16
#define Using_BTN
void Oled_Proc();
void Key_Proc();
extern uint16_t Time_Num; 
typedef enum      //oled屏幕的状态(状态机
{State_Main = 0,State_Time_Num,State_Key            //新了一个key的状态
}interface;

task.c设定 

初始化函数的编写

前文说过,我们用Using_BTN来判断是否有用到独立按键,如果用到独立按键,那就初始化独立按键相关的GPIO属性,如果没有,那就不初始化。这些初始化的代码直接从GPIO初始化代码中剪贴即可。

void BSP_Init()
{OLED_Init();#ifdef Using_BTN				//若有使用独立键盘,则初始化独立键盘相关gpioGPIO_InitTypeDef GPIO_InitStruct = {0};/* GPIO Ports Clock Enable */__HAL_RCC_GPIOB_CLK_ENABLE();__HAL_RCC_GPIOA_CLK_ENABLE();/*Configure GPIO pin : COL1_Pin */HAL_GPIO_WritePin(GPIOA, ROW2_Pin|ROW1_Pin, GPIO_PIN_RESET);GPIO_InitStruct.Pin = COL1_Pin;GPIO_InitStruct.Mode = GPIO_MODE_INPUT;GPIO_InitStruct.Pull = GPIO_PULLUP;HAL_GPIO_Init(COL1_GPIO_Port, &GPIO_InitStruct);/*Configure GPIO pins : COL3_Pin COL2_Pin */GPIO_InitStruct.Pin = COL3_Pin|COL2_Pin;GPIO_InitStruct.Mode = GPIO_MODE_INPUT;GPIO_InitStruct.Pull = GPIO_PULLUP;HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);/*Configure GPIO pins : ROW2_Pin ROW1_Pin */GPIO_InitStruct.Pin = ROW2_Pin|ROW1_Pin;GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);#endif }

 按键处理函数的改动

按键处理函数的改动主要在于对独立按键的判断,并且因为题目需求额外定义了一个Key_Num用于储存要在第三显示屏上显示的数据。

重头戏也就是按键扫描功能。当确定有用了独立按键组,则开始独立按键的扫描。因为我设定的是每周期扫描两组,每组扫描三个,因此用到了两个if逻辑。先判断ROW1,当到这行以后再去判断对应的每一列。ROW2也是如此。当某个按键被按下(某个地方的gpio为RESET)时,就把Key_Value值更改为之前对应的KB系列定义即可。

接着就是经典三段防抖,然后是判断哪个按键被按下了,显示数值修改为想要值即可。

uint16_t Key_Num;void Key_Proc()
{if(task_time.Key_Time < Key_Min_Time)	return;						//判断是否到key_proc的刷新时间task_time.Key_Time = 0;uint16_t Key_Value;if(HAL_GPIO_ReadPin(ASW1_GPIO_Port,ASW1_Pin) == GPIO_PIN_RESET)      //判断按下的是不是asw1,如果是,就赋值,如果不是甚至没按,就为0{Key_Value = Asw_1;}else   {Key_Value = Asw_None;}#ifdef Using_BTN			//如果使用了独立键盘,就开启独立键盘扫描功能if(Key_Value == Asw_None){HAL_GPIO_WritePin(ROW1_GPIO_Port,ROW2_Pin,GPIO_PIN_SET);HAL_GPIO_WritePin(ROW2_GPIO_Port,ROW1_Pin,GPIO_PIN_RESET);if(HAL_GPIO_ReadPin(COL1_GPIO_Port,COL1_Pin) == GPIO_PIN_RESET) Key_Value = KB1;else if(HAL_GPIO_ReadPin(COL2_GPIO_Port,COL2_Pin) == GPIO_PIN_RESET) Key_Value = KB2;else if(HAL_GPIO_ReadPin(COL3_GPIO_Port,COL3_Pin) == GPIO_PIN_RESET) Key_Value = KB3;		else Key_Value = Asw_None;}if(Key_Value == Asw_None){HAL_GPIO_WritePin(ROW2_GPIO_Port,ROW1_Pin,GPIO_PIN_SET);HAL_GPIO_WritePin(ROW1_GPIO_Port,ROW2_Pin,GPIO_PIN_RESET);if(HAL_GPIO_ReadPin(COL1_GPIO_Port,COL1_Pin) == GPIO_PIN_RESET) Key_Value = KB4;else if(HAL_GPIO_ReadPin(COL2_GPIO_Port,COL2_Pin) == GPIO_PIN_RESET) Key_Value = KB5;else if(HAL_GPIO_ReadPin(COL3_GPIO_Port,COL3_Pin) == GPIO_PIN_RESET) Key_Value = KB6;		else Key_Value = Asw_None;}#endifKey.Key_Up = Key_Value &(Key_Value ^ Key.Key_Old);					//三段经典消除按键抖动Key.Key_Down = ~Key_Value &(Key_Value ^ Key.Key_Old);	Key.Key_Old = Key_Value;if(Key.Key_Down == Asw_1)																		//确定按下了,就开始切换工作{if(state == State_Main) state = State_Time_Num;else if(state == State_Time_Num)state = State_Key;else state = State_Main;Key.Key_Down = Asw_None;																		//复位key_down}if(Key.Key_Down == KB1)//请务必用if,别一串的else if{Key_Num = 1;}if(Key.Key_Down == KB2){Key_Num = 2;}if(Key.Key_Down == KB3){Key_Num = 3;}if(Key.Key_Down == KB4){Key_Num = 4;}if(Key.Key_Down == KB5){Key_Num = 5;}if(Key.Key_Down == KB6){Key_Num = 6;}}

OLED显示函数 

 OLED就没啥好说了,就在switch里多加个键盘的case就行。

case State_Key:if(state!=old_state){old_state = state;OLED_Clear();}sprintf((char*)Oled_buf_line1,"Key:%03d",Key_Num);break;


http://www.ppmy.cn/embedded/172516.html

相关文章

垃圾收集算法与收集器

在 JVM 中&#xff0c;垃圾收集&#xff08;Garbage Collection, GC&#xff09;算法的核心目标是自动回收无用对象的内存&#xff0c;同时尽量减少对应用性能的影响。以下是 JVM 中主要垃圾收集算法的原理、流程及实际应用场景的详细介绍&#xff1a; 一、标记-清除算法&#…

深入理解pytest框架中的conftest.py:使用与作用原理

pytest是Python中最流行的测试框架之一&#xff0c;以其简洁、灵活和强大的功能而闻名。在pytest中&#xff0c;conftest.py文件是一个特殊的文件&#xff0c;用于共享测试配置、夹具&#xff08;fixtures&#xff09;和插件。理解conftest.py的使用和作用原理&#xff0c;可以…

leetcode283.移动零

题目&#xff1a; 给定一个数组 nums&#xff0c;编写一个函数将所有 0 移动到数组的末尾&#xff0c;同时保持非零元素的相对顺序。 请注意 &#xff0c;必须在不复制数组的情况下原地对数组进行操作。 示例 1: 输入: nums [0,1,0,3,12] 输出:[1,3,12,0,0] 示例 2: 输入: nums…

碰一碰发视频源码搭建,碰一碰发视频私有化部署,碰一碰发视频OEM贴牌

引言 随着移动互联网的快速发展&#xff0c;短视频应用成为了用户日常娱乐和信息获取的重要方式。碰一碰发视频功能作为一种新颖的交互方式&#xff0c;能够通过设备之间的简单触碰实现视频的快速分享。本文将详细介绍如何搭建碰一碰发视频的源码&#xff0c;并进行私有化部署…

解决Docker Desktop中ext4.vhdx文件过大的问题

ext4.vhdx是Docker Desktop在Windows系统上使用WSL2&#xff08;Windows Subsystem for Linux 2&#xff09;时&#xff0c;用于存储Linux文件系统的虚拟硬盘文件。 基本概念 VHDX格式&#xff1a;VHDX是微软推出的一种虚拟硬盘格式&#xff0c;具有更大的存储容量、更好的性能…

SQL Server查询优化

最常用&#xff0c;最有效的数据库优化方式 查询语句层面 避免全表扫描 使用索引&#xff1a;确保查询条件中的字段有索引。例如&#xff0c;查询语句 SELECT * FROM users WHERE age > 20&#xff0c;若 age 字段有索引&#xff0c;数据库会利用索引快速定位符合条件的记…

基于Java 童装在线销售系统(源码+lw+部署文档+讲解),源码可白嫖!

摘要&#xff1a; 当今社会进入了科技进步、经济社会快速发展的新时代。国际信息和学术交流也不断加强&#xff0c;计算机技术对经济社会发展和人民生活改善的影响也日益突出&#xff0c;人类的生存和思考方式也产生了变化。传统购物管理采取了人工的管理方法&#xff0c;但这…

芯片研发不需要PPT

在芯片研发的实验室里&#xff0c;工程师的屏幕上跳动着波形图&#xff0c;仿真软件吞吐着海量数据&#xff0c;验证工程师盯着亚稳态问题看到眼睛干涩&#xff0c;而某个角落的会议室里&#xff0c;一群人正对着80页的PPT争论字体格式——这荒诞的场景&#xff0c;像极了芯片设…