在蓝桥杯物联网平台里面,有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;