目录

一、矩阵键盘的原理图、PCB图

二、矩阵键盘的初始化

三、扫描函数详解

一、矩阵键盘的原理图、PCB图

二、矩阵键盘的初始化

  • PF0到PF3固定为推挽输出,PF12到PF15固定为下拉输入。
  • 即,无键按下时,对应PF12到PF15为0,有键按下时,PF12到PF15中,对应的引脚为高。

/**
  ******************************************************************************
  * @file    main.c
  * @author  Sumjess
  * @version V1.0
  * @date    2019-05-xx
  * @brief   按键应用bsp(扫描模式)
  ******************************************************************************
  * @attention
  *
  * 实验平台   :STM32 F429 
  * CSDN Blog  :https://blog.csdn.net/qq_38351824
  * 微信公众号 :Tech云
  *
  ******************************************************************************
  */ 
 
/**
  * @brief  配置矩阵按键用到的I/O口
  * @param  无
  * @retval 无     
    * @explain PF0到PF3固定为推挽输出,PF12到PF15固定为下拉输入。即,无键按下时,对应PF12到PF15为0,有键按下时,PF12到PF15中,对应的引脚为高。
  */
void Key4X4_GPIO_Config(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    
    /*开启按键GPIO口的时钟*/
    RCC_AHB1PeriphClockCmd(KEY_OUT_GPIO_CLK|KEY_IN_GPIO_CLK,ENABLE);
    
/*---------定义PF0到PF3为推挽输出---------*/
    GPIO_InitStructure.GPIO_Pin = KEY_OUT_PIN;         /*选择按键的引脚*/  
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;      /*设置引脚为输出模式*/    
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;       /*设置引脚的输出类型为推挽输出*/
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;   /*设置引脚不上拉也不下拉*/      
    GPIO_Init(KEY_OUT_GPIO_PORT, &GPIO_InitStructure); /*使用上面的结构体初始化按键*/   
  
/*---------定义PF4到PF7为下拉输入---------*/   
    GPIO_InitStructure.GPIO_Pin = KEY_IN_PIN;           /*选择按键的引脚*/  
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;        /*设置引脚为输入模式*/    
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;      /*设置引脚下拉*/ 
    GPIO_Init(KEY_IN_GPIO_PORT, &GPIO_InitStructure);   /*使用上面的结构体初始化按键*/
}

三、扫描函数详解

解释说明在下面。

/**
  * @brief  矩阵键盘循环扫描
  * @param  无
  * @retval 返回键值     
    * @explain 
  */
//实现矩阵键盘。返回值为,各按键的键值,此键值由用户自己定义。.
int KEY_Scan(void) //实现矩阵键盘。返回值为,各按键的键值,此键值由用户自己定义。
{
    u8 KeyVal;     //keyVal为最后返回的键值。
    
    GPIO_Write(KEY_OUT_GPIO_PORT,((KEY_OUT_GPIO_PORT->ODR & 0xfff0) | 0x000f)); //先让PF0到PF3全部输出高。
    
    if((KEY_IN_GPIO_PORT->IDR & 0xf000)==0x0000)  //如果PF12到PF15全为0,则没有键按下。此时返回值为-1.
        return -1;
    else
    {    
        Delay_ms(5);    //延时消抖。
        if((KEY_IN_GPIO_PORT->IDR & 0xf000)==0x0000)//如果延时5ms后,PF12到PF15又全为0,刚才引脚的电位变化是抖动产生的.
        return -1;
    }
//注意:如果程序能运行到下面,证明已经保证有按键按下了
    
    GPIO_Write(KEY_OUT_GPIO_PORT,((KEY_OUT_GPIO_PORT->ODR & 0xfff0) | 0x0001));    //让PF3到PF0输出二进制的0001. ===> PF0为高电平,其余为低电平
        switch(KEY_IN_GPIO_PORT->IDR & 0xf000)                //对PF12到PF15的值进行判断,以输出不同的键值。
        {
            case 0x1000: KeyVal=15; break;
            case 0x2000: KeyVal=11;    break;
            case 0x4000: KeyVal=7;    break;
            case 0x8000: KeyVal=3;    break;
        }
       
    GPIO_Write(KEY_OUT_GPIO_PORT,((KEY_OUT_GPIO_PORT->ODR & 0xfff0) | 0x0002));    //让PF3到PF0输出二进制的0010. ===> PF1为高电平,其余为低电平
        switch(KEY_IN_GPIO_PORT->IDR & 0xf000)                //对PF12到PF15的值进行判断,以输出不同的键值。
        {
            case 0x1000: KeyVal=14;    break;
            case 0x2000: KeyVal=10;    break;
            case 0x4000: KeyVal=6;    break;
            case 0x8000: KeyVal=2;    break;
        }
 
    GPIO_Write(KEY_OUT_GPIO_PORT,((KEY_OUT_GPIO_PORT->ODR & 0xfff0) | 0x0004));    //让PF3到PF0输出二进制的0100. ===> PF2为高电平,其余为低电平
        switch(KEY_IN_GPIO_PORT->IDR & 0xf000)                //对PF12到PF15的值进行判断,以输出不同的键值。
        {
            case 0x1000: KeyVal=13;    break;
            case 0x2000: KeyVal=9;    break;
            case 0x4000: KeyVal=5;    break;
            case 0x8000: KeyVal=1;    break;
        }
 
     GPIO_Write(KEY_OUT_GPIO_PORT,((KEY_OUT_GPIO_PORT->ODR & 0xfff0) | 0x0008));    //让PF3到PF0输出二进制的1000. ===> PF3为高电平,其余为低电平
        switch(KEY_IN_GPIO_PORT->IDR & 0xf000)                //对PF12到PF15的值进行判断,以输出不同的键值。
        {
            case 0x1000: KeyVal=12;    break;
            case 0x2000: KeyVal=8;    break;
            case 0x4000: KeyVal=4;    break;
            case 0x8000: KeyVal=0;    break;
        }                                      
    return KeyVal;        
}

GPIO_Write(KEY_OUT_GPIO_PORT,((KEY_OUT_GPIO_PORT->ODR & 0xfff0) | 0x000f)); //先让PF0到PF3全部输出高。

只解释这一句,一通百通。

1、说明
     运算顺序:->  &  | 
     0xfff0 后面的括号可以不加,但是由于总显示警告,还是加上了。
     0xfff0 =  1111111111110000
     0x000f =  0000000000001111
     ODR:GPIO端口输出数据寄存器
     & :两个数都为1,结果才是1
     | :有一个1,结果就是1
    --------------------------------------------------
    2、作用
     GPIOF->ODR :  GPIOF端口输出数据寄存器
     &  0xfff0  :  保证除了第0位~第3位以外的其他位,不会因下一步操作而受改变    
     |   0x000f  :  使得第0位~第3位变为高电平
    --------------------------------------------------
    3、举例
     例如  引脚F的各个引脚状态依次为1000 1001 0101 1100 ,我现在想使第0位~第3位变成1111,同时让其他位不受影响。
             ① 1000100101011100 = 0x895c
             ② 0x895c & 0xfff0 | 0x000f  这就是答案
             ③ 验证:
              (1)  0x895c & 0xfff0  ===>  1000100101011100 & 1111111111110000 计算答案为 1000100101010000
              (2) 不难发现,将第0位~第3位,变为0了
              (3) 1000100101010000 | 0x000f  ===> 1000100101010000 |  0000000000001111  计算答案为  1000100101011111
             ④ 1000100101011111 与之前的 1000100101011100