浏览量: 243 次浏览

STM32 GPIO应用之键盘使用

2019年4月18日 0 作者 Nie Hen

使用STM32 CubeMx 实现键盘使用 并将按键数值显示到数码管

GPIO的介绍已经在 上一篇写过了
下面直接讲 键盘使用
应用演示

键盘原理

电路连接如图

enter description here

  1. 反转法

先将 (将行设为output)行的GPIO设为高电平,(将列设为input)读取列GPIO的电位,如果某列存在低电平,说明该列有键值被按下
再将(将列设为output)列的GPIO设为低电平,(将行设为input)读取行GPIO的电位,如果某行存在低电平,说明该行有键值被按下,
通过两次的反转确定了行和列 就可以获取到按下的键值

  1. 扫描法
    将列所在GPIO设为input
    将行所在GPIO设为output

遍历所有行,先将一行设为高电平,其他行设为低电平,读取所有列的值。如果有列存在低电平,就可以根据所遍历到的该列和该行获取到按键所在的位置 。
按键消抖
为了确保对一次按键动作只确认一次按键有效,必须消除抖动期的影响
软件消抖:
利用软件延时实现消抖
在检测到有键按下时(对应的行线为低电平),延时5-15ms后,若该行线仍为低电平,则确认该行确实有键按下。否则可认为是扰动。(软件消抖)
硬件消抖:
采用相应硬件电路,消除信号的抖动。
硬件常见有2种:
采用专用的键盘/显示器接口芯片:芯片中有自动去抖动的硬件电路。
采用基本RS触发器消除抖动
有查询式和中断式的。查询式是一直运行或延时判断是否有键被按下,中断式是使用中断判断按键被按下。

配置cubemx

  1. 查看原理图
    找出按键的行和列 对应的GPIO口
    enter description here =700*600

可以看到列对应的GPIO口是 B0 B1 B13 B12
行对应的GPIO口是A6 A7 C4 C5
而且是共阳极 低电平有效 (大多元器件都是共阳极,可能是因为在数电中 非对应的也就是0更好用)
2. 配置相应GPIO口
这里示例使用 扫描法 (好处理)
将行所在引脚设置为input 列所在引脚设为output

enter description here =500*400

同时也需要将数码管的给配置了 (STM32 GPIO应用之led灯、数码管里面有)

  1. 保存导出
    配置项目名称,IDE等 合成代码

编写代码

使用扫描法 查询式
enter description here
这里的键盘处理子程序就是 显示到数码管上面去。
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();

使用反转法

  1. cubemx中配置 列和行所在位置 配置为output
  2. 将数码管部分代码也要粘贴过来
  3. 代码中 要将行设为输出 列设为输入 和行设为输入 列设为输出
    代码
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