使用STM32 CubeMx 实现键盘使用 并将按键数值显示到数码管
GPIO的介绍已经在 上一篇写过了
下面直接讲 键盘使用
应用演示链接
键盘原理
电路连接如图
- 反转法
先将 (将行设为output)行的GPIO设为高电平,(将列设为input)读取列GPIO的电位,如果某列存在低电平,说明该列有键值被按下
再将(将列设为output)列的GPIO设为低电平,(将行设为input)读取行GPIO的电位,如果某行存在低电平,说明该行有键值被按下,
通过两次的反转确定了行和列 就可以获取到按下的键值
- 扫描法
将列所在GPIO设为input
将行所在GPIO设为output
遍历所有行,先将一行设为高电平,其他行设为低电平,读取所有列的值。如果有列存在低电平,就可以根据所遍历到的该列和该行获取到按键所在的位置 。
按键消抖
为了确保对一次按键动作只确认一次按键有效,必须消除抖动期的影响
软件消抖:
利用软件延时实现消抖
在检测到有键按下时(对应的行线为低电平),延时5-15ms后,若该行线仍为低电平,则确认该行确实有键按下。否则可认为是扰动。(软件消抖)
硬件消抖:
采用相应硬件电路,消除信号的抖动。
硬件常见有2种:
采用专用的键盘/显示器接口芯片:芯片中有自动去抖动的硬件电路。
采用基本RS触发器消除抖动
有查询式和中断式的。查询式是一直运行或延时判断是否有键被按下,中断式是使用中断判断按键被按下。
配置cubemx
- 查看原理图
找出按键的行和列 对应的GPIO口
可以看到列对应的GPIO口是 B0 B1 B13 B12
行对应的GPIO口是A6 A7 C4 C5
而且是共阳极 低电平有效 (大多元器件都是共阳极,可能是因为在数电中 非对应的也就是0更好用)
2. 配置相应GPIO口
这里示例使用 扫描法 (好处理)
将行所在引脚设置为input 列所在引脚设为output
同时也需要将数码管的给配置了 (STM32 GPIO应用之led灯、数码管里面有)
- 保存导出
配置项目名称,IDE等 合成代码
编写代码
使用扫描法 查询式
这里的键盘处理子程序就是 显示到数码管上面去。
1.数码管部分代码
数码管原理等部分可以参考另外一篇文章
这里给出代码
struct GPIO_PACK{
GPIO_TypeDef * port;
uint16_t pin;
};
struct GPIO_PACK segs[8]= {
{GPIOA,GPIO_PIN_15},
{GPIOA,GPIO_PIN_11},
{GPIOC,GPIO_PIN_9},
{GPIOC,GPIO_PIN_7},
{GPIOC,GPIO_PIN_8},
{GPIOA,GPIO_PIN_12},
{GPIOA,GPIO_PIN_8},
{GPIOC,GPIO_PIN_6},
} ;
struct GPIO_PACK bits[4]={
{GPIOD,GPIO_PIN_2},
{GPIOB,GPIO_PIN_4},
{GPIOB,GPIO_PIN_6},
{GPIOB,GPIO_PIN_7},
};
uint8_t shuzu[]={
0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,
0xf8,0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e
};
void DisplayOnebit(uint8_t digtal,uint8_t bit){
uint8_t i;
for(i=0;i<8;i++){
HAL_GPIO_WritePin(segs[i].port,segs[i].pin,(GPIO_PinState)(shuzu[digtal]&(0x01<<i)));
}
for(i=0;i<4;i++){
HAL_GPIO_WritePin(bits[i].port,bits[i].pin,(GPIO_PinState)(i!=bit));
}
}
void DisplayDigtal(uint16_t digtal){
int a=0;
while(a<10)
{
a++;
DisplayOnebit(digtal%10,0);
HAL_Delay(5);
if (digtal>=10){
DisplayOnebit(digtal/10%10,1);
HAL_Delay(5);
if (digtal>=100){
DisplayOnebit(digtal/100%10,2);
HAL_Delay(5);
if (digtal>=1000){
DisplayOnebit(digtal/1000%10,3);
HAL_Delay(5);
}
}
}
}
}
代码讲解也参考那篇文章
2. 按键 扫描法代码
按键的结构数组
列的放到lie结构数组中 行的放到 hang中
struct GPIO_PACK lie[4]={
{GPIOB,GPIO_PIN_0},
{GPIOB,GPIO_PIN_1},
{GPIOB,GPIO_PIN_13},
{GPIOB,GPIO_PIN_12}
};
struct GPIO_PACK hang[4]={
{GPIOA,GPIO_PIN_6},
{GPIOA,GPIO_PIN_7},
{GPIOC,GPIO_PIN_4},
{GPIOC,GPIO_PIN_5}
};
扫描法获取键值
void Displaykeying()
{
uint8_t i,j,s,key=0;uint8_t k=1;
for(i=0;i<4;i++)
{ HAL_GPIO_WritePin(lie[i].port,lie[i].pin,GPIO_PIN_RESET);
for(s=0;s<4;s++){
if(i!=s)
HAL_GPIO_WritePin(lie[s].port,lie[s].pin,GPIO_PIN_SET);}
for(j=0;j<4;j++)
{ k=HAL_GPIO_ReadPin(hang[j].port,hang[j].pin);
if(k==0)
HAL_Delay(5);
if(k==0)
switch(j)
{
case 0:key=j+1+i;break;
case 1:key=j+4+i;break;
case 2:key=j+7+i;break;
case 3:key=j+10+i;break;
}
}
}
//if(key!=0)
DisplayDigtal(key);
}
首先 第一个for循环 遍历列 ,遍历的列置低电平,使用嵌套第一个for循环使不是遍历到的那列置高电平,使用嵌套第二个for循环读取每一行的电位。
如果读取到有电位为低电平,加上延时去抖,还是低电平则说明有键被按下,而该键的位置就是遍历的列和 此时的行。
根据每个键对应的值,使用switch 获取。
获取到的键值,使用DisDigtal函数(封装好的数码管显示数值),直接就能显示出当前按下的键值。
在main主函数 while循环中 添加扫描键盘的函数Displaykeying就可以了
Displaykeying();
使用反转法
- cubemx中配置 列和行所在位置 配置为output
- 将数码管部分代码也要粘贴过来
- 代码中 要将行设为输出 列设为输入 和行设为输入 列设为输出
代码
void Loutput()
{
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Pin = H1_Pin|H2_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
HAL_GPIO_WritePin(GPIOB, L1_Pin|L2_Pin|L4_Pin|L3_Pin, GPIO_PIN_RESET);
/*Configure GPIO pins : H3_Pin H4_Pin */
GPIO_InitStruct.Pin = H3_Pin|H4_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
GPIO_InitStruct.Pin = L1_Pin|L2_Pin|L4_Pin|L3_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
void Houtput(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Pin = H1_Pin|H2_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
HAL_GPIO_WritePin(GPIOA,H1_Pin|H2_Pin,GPIO_PIN_RESET);
/*Configure GPIO pins : H3_Pin H4_Pin */
GPIO_InitStruct.Pin = H3_Pin|H4_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
HAL_GPIO_WritePin(GPIOC,H3_Pin|H4_Pin,GPIO_PIN_RESET);
GPIO_InitStruct.Pin = L1_Pin|L2_Pin|L4_Pin|L3_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
根据反转法 读取键值
uint8_t keyread(void)
{
Loutput();HAL_Delay(5);
if(HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_5)==GPIO_PIN_RESET)line=4;
else if(HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_4)==GPIO_PIN_RESET)line=3;
else if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_7)==GPIO_PIN_RESET)line=2;
else if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_6)==GPIO_PIN_RESET)line=1;
else return 0;
Houtput();HAL_Delay(5);
if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0)==GPIO_PIN_RESET)row=1;
else if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1)==GPIO_PIN_RESET)row=2;
else if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_13)==GPIO_PIN_RESET)row=3;
else if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_12)==GPIO_PIN_RESET)row=4;
Loutput();HAL_Delay(5);
return 1;
}
该函数如果有按键被按下则line 和row 值会直接发生变化,并返回1 没有检测到按键返回0
其中 line 和row是变量 需要在前面声明
while循环中需要添加部分代码
i = keyread();
if(i)
{
key = (line -1)*4 +row;
DisplayDigtal(key);
}
调用函数获取返回值,返回1则有键被按下,判断出键值,数码管显示键值。
在main函数之前定义变量
uint8_t line=0,row=0,i=0,key=0;
完成代码链接(使用的扫描法) GitHub