xld0932 发表于 2021-4-19 23:00

【HC32F460开发板测评】05.矩阵按键的识别与处理

<p>HC32F460芯片搭载一个键盘控制模块(KEYSCAN),支持矩阵键盘的行和列扫描;列是由独立的输出KEYOUTm(m=0~7)驱动,而行KEYINn(n=0~15)则作为EIRQn(n=0~15)输入被检测;KEYSCAN模块通过行扫描查询的方法实现了按键识别的功能。具体有详细描述可以参考《HC32F460系列用户手册Rev1.21》的第12章节。</p>

<p>&nbsp;</p>

<p>本篇主要是基于KEYSCAN功能模块和基于GPIO输入输出功能这两种方式来实现对矩阵按键的扫描和处理;将扫描到的矩阵按键值通过串口打印在电脑终端上。</p>

<p>&nbsp;</p>

<p>硬件原理图:</p>

<p></p>

<p>矩阵按键头文件:</p>

<p>在头文件中,我们定义了KEY_SCAN_MODE这样一个宏,当值为1时,程序中使用KEYSCAN模块来实现矩阵按键的检测;当值为0时,程序中使用GPIO的输入输出来实现矩阵按键的检测;</p>

<pre>
<code class="language-cpp">/*******************************************************************************
* @file    MATRIX_KEYBOARD.h
* @authorxld0932
* @version V1.00
* @date 31-Mar-2021
* @brief ......
*******************************************************************************/


/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __MATRIX_KEYBOARD_H__
#define __MATRIX_KEYBOARD_H__


#ifdef __cplusplus
extern "C" {
#endif


#undefEXTERN


#ifdef__MATRIX_KEYBOARD_C__
#define EXTERN
#else
#define EXTERN extern
#endif


/* Includes ------------------------------------------------------------------*/
#include "config.h"


/* Exported constants --------------------------------------------------------*/
#define KEY_SCAN_MODE   0


/* Exported constants --------------------------------------------------------*/
#if KEY_SCAN_MODE

#define KEYOUT_PORT   (PortA)
#define KEYOUT0_PIN   (Pin04)
#define KEYOUT1_PIN   (Pin05)
#define KEYOUT2_PIN   (Pin06)

#define KEYIN_PORT      (PortD)
#define KEYIN0_PIN      (Pin12)
#define KEYIN1_PIN      (Pin13)
#define KEYIN2_PIN      (Pin14)

#else

#define KEYIN0_PORT   (PortD)
#define KEYIN0_PIN      (Pin12)

#define KEYIN1_PORT   (PortD)
#define KEYIN1_PIN      (Pin13)

#define KEYIN2_PORT   (PortD)
#define KEYIN2_PIN      (Pin14)

#define KEYOUT0_PORT    (PortA)
#define KEYOUT0_PIN   (Pin04)

#define KEYOUT1_PORT    (PortA)
#define KEYOUT1_PIN   (Pin05)

#define KEYOUT2_PORT    (PortA)
#define KEYOUT2_PIN   (Pin06)

#endif


/* Exported types ------------------------------------------------------------*/
/* Exported macro ------------------------------------------------------------*/


/* Exported functions --------------------------------------------------------*/
#if KEY_SCAN_MODE
EXTERN void MATRIX_KEYBOARD_Init(void);
#else
EXTERN void MATRIX_KEYBOARD_Scan(void);
#endif


#ifdef __cplusplus
}
#endif


#endif


/******************* (C) COPYRIGHT 2021 *************************END OF FILE***/

</code></pre>

<p>&nbsp;</p>

<p>矩阵按键源文件:</p>

<pre>
<code class="language-cpp">/*******************************************************************************
* @file    MATRIX_KEYBOARD.c
* @authorxld0932
* @version V1.00
* @date    31-Mar-2021
* @brief   ......
*******************************************************************************/


/* Define to prevent recursive inclusion -------------------------------------*/
#define __MATRIX_KEYBOARD_C__


/* Includes ------------------------------------------------------------------*/
#include "MATRIX_KEYBOARD.h"


/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/


/* Exported variables --------------------------------------------------------*/
/* Exported function prototypes ----------------------------------------------*/


#if KEY_SCAN_MODE


volatile uint8_t Row = 0xFF, Column = 0xFF;


/*******************************************************************************
* @brief      
* @param      
* @retval      
* @attention *******************************************************************************/
void MATRIX_KEYBOARD_ROW0_Callback(void)
{
    if(EXINT_IrqFlgGet(ExtiCh12) == Set)
    {
      uint8_t Index = KEYSCAN_GetColIdx();

      if((Row != 0) || (Column != Index))
      {
            Row    = 0;
            Column = Index;
            printf("\r\nMatrix Keyboard Row : 0, Column : %d", Index);
      }

      /* clear int request flag */
      EXINT_IrqFlgClr(ExtiCh12);
    }
}


/*******************************************************************************
* @brief      
* @param      
* @retval      
* @attention   
*******************************************************************************/
void MATRIX_KEYBOARD_ROW1_Callback(void)
{
    if(EXINT_IrqFlgGet(ExtiCh13) == Set)
    {
      uint8_t Index = KEYSCAN_GetColIdx();

      if((Row != 1) || (Column != Index))
      {
            Row    = 1;
            Column = Index;
            printf("\r\nMatrix Keyboard Row : 1, Column : %d", Index);
      }

      /* clear int request flag */
      EXINT_IrqFlgClr(ExtiCh13);
    }
}


/*******************************************************************************
* @brief      
* @param      
* @retval      
* @attention   
*******************************************************************************/
void MATRIX_KEYBOARD_ROW2_Callback(void)
{
    if(EXINT_IrqFlgGet(ExtiCh14) == Set)
    {
      uint8_t Index = KEYSCAN_GetColIdx();

      if((Row != 2) || (Column != Index))
      {
            Row    = 2;
            Column = Index;
            printf("\r\nMatrix Keyboard Row : 2, Column : %d", Index);
      }

      /* clear int request flag */
      EXINT_IrqFlgClr(ExtiCh14);
    }
}


/*******************************************************************************
* @brief      
* @param      
* @retval      
* @attention   
*******************************************************************************/
void MATRIX_KEYBOARD_InitROW0(void)
{
    stc_exint_config_t stcExtiConfig;
    stc_irq_regi_conf_t stcIrqRegiConf;
    stc_port_init_t stcPortInit;

    /* configuration structure initialization */
    MEM_ZERO_STRUCT(stcExtiConfig);
    MEM_ZERO_STRUCT(stcIrqRegiConf);
    MEM_ZERO_STRUCT(stcPortInit);

    /**************************************************************************/
    /* External Int Ch.12 for keyin0                                          */
    /**************************************************************************/
    stcExtiConfig.enExitCh = ExtiCh12;

    /* Filter setting */
    stcExtiConfig.enFilterEn = Enable;
    stcExtiConfig.enFltClk = Pclk3Div8;
    /* falling edge for keyscan function */
    stcExtiConfig.enExtiLvl = ExIntFallingEdge;
    EXINT_Init(&amp;stcExtiConfig);

    /* Set PD12 as External Int Ch.12 input */
    MEM_ZERO_STRUCT(stcPortInit);
    stcPortInit.enExInt = Enable;
    stcPortInit.enPullUp = Enable;
    PORT_Init(KEYIN_PORT, KEYIN0_PIN, &amp;stcPortInit);

    /* Select External Int Ch.12 */
    stcIrqRegiConf.enIntSrc = INT_PORT_EIRQ12;

    /* Register External Int to Vect.No.002 */
    stcIrqRegiConf.enIRQn = Int002_IRQn;

    /* Callback function */
    stcIrqRegiConf.pfnCallback = &amp;MATRIX_KEYBOARD_ROW0_Callback;

    /* Registration IRQ */
    enIrqRegistration(&amp;stcIrqRegiConf);

    /* Clear pending */
    NVIC_ClearPendingIRQ(stcIrqRegiConf.enIRQn);

    /* Set priority */
    NVIC_SetPriority(stcIrqRegiConf.enIRQn, DDL_IRQ_PRIORITY_DEFAULT);

    /* Enable NVIC */
    NVIC_EnableIRQ(stcIrqRegiConf.enIRQn);
}


/*******************************************************************************
* @brief      
* @param      
* @retval      
* @attention   
*******************************************************************************/
void MATRIX_KEYBOARD_InitROW1(void)
{
    stc_exint_config_t stcExtiConfig;
    stc_irq_regi_conf_t stcIrqRegiConf;
    stc_port_init_t stcPortInit;

    /* configuration structure initialization */
    MEM_ZERO_STRUCT(stcExtiConfig);
    MEM_ZERO_STRUCT(stcIrqRegiConf);
    MEM_ZERO_STRUCT(stcPortInit);

    /**************************************************************************/
    /* External Int Ch.13 for keyin1                                          */
    /**************************************************************************/
    stcExtiConfig.enExitCh = ExtiCh13;

    /* Filter setting */
    stcExtiConfig.enFilterEn = Enable;
    stcExtiConfig.enFltClk = Pclk3Div8;
    /* falling edge for keyscan function */
    stcExtiConfig.enExtiLvl = ExIntFallingEdge;
    EXINT_Init(&amp;stcExtiConfig);

    /* Set PD13 as External Int Ch.13 input */
    MEM_ZERO_STRUCT(stcPortInit);
    stcPortInit.enExInt = Enable;
    stcPortInit.enPullUp = Enable;
    PORT_Init(KEYIN_PORT, KEYIN1_PIN, &amp;stcPortInit);

    /* Select External Int Ch.13 */
    stcIrqRegiConf.enIntSrc = INT_PORT_EIRQ13;

    /* Register External Int to Vect.No.003 */
    stcIrqRegiConf.enIRQn = Int003_IRQn;

    /* Callback function */
    stcIrqRegiConf.pfnCallback = &amp;MATRIX_KEYBOARD_ROW1_Callback;

    /* Registration IRQ */
    enIrqRegistration(&amp;stcIrqRegiConf);

    /* Clear Pending */
    NVIC_ClearPendingIRQ(stcIrqRegiConf.enIRQn);

    /* Set priority */
    NVIC_SetPriority(stcIrqRegiConf.enIRQn, DDL_IRQ_PRIORITY_DEFAULT);

    /* Enable NVIC */
    NVIC_EnableIRQ(stcIrqRegiConf.enIRQn);
}


/*******************************************************************************
* @brief      
* @param      
* @retval      
* @attention   
*******************************************************************************/
void MATRIX_KEYBOARD_InitROW2(void)
{
    stc_exint_config_t stcExtiConfig;
    stc_irq_regi_conf_t stcIrqRegiConf;
    stc_port_init_t stcPortInit;

    /* configuration structure initialization */
    MEM_ZERO_STRUCT(stcExtiConfig);
    MEM_ZERO_STRUCT(stcIrqRegiConf);
    MEM_ZERO_STRUCT(stcPortInit);

    /**************************************************************************/
    /* External Int Ch.14 for keyin2                                          */
    /**************************************************************************/
    stcExtiConfig.enExitCh = ExtiCh14;

    /* Filter setting */
    stcExtiConfig.enFilterEn = Enable;
    stcExtiConfig.enFltClk = Pclk3Div8;
    /* falling edge for keyscan function */
    stcExtiConfig.enExtiLvl = ExIntFallingEdge;
    EXINT_Init(&amp;stcExtiConfig);

    /* Set PD14 as External Int Ch.14 input */
    MEM_ZERO_STRUCT(stcPortInit);
    stcPortInit.enExInt = Enable;
    stcPortInit.enPullUp = Enable;
    PORT_Init(KEYIN_PORT, KEYIN2_PIN, &amp;stcPortInit);

    /* Select External Int Ch.14 */
    stcIrqRegiConf.enIntSrc = INT_PORT_EIRQ14;

    /* Register External Int to Vect.No.004 */
    stcIrqRegiConf.enIRQn = Int004_IRQn;

    /* Callback function */
    stcIrqRegiConf.pfnCallback = &amp;MATRIX_KEYBOARD_ROW2_Callback;

    /* Registration IRQ */
    enIrqRegistration(&amp;stcIrqRegiConf);

    /* Clear Pending */
    NVIC_ClearPendingIRQ(stcIrqRegiConf.enIRQn);

    /* Set priority */
    NVIC_SetPriority(stcIrqRegiConf.enIRQn, DDL_IRQ_PRIORITY_DEFAULT);

    /* Enable NVIC */
    NVIC_EnableIRQ(stcIrqRegiConf.enIRQn);
}


/*******************************************************************************
* @brief      
* @param      
* @retval      
* @attention   
*******************************************************************************/
void MATRIX_KEYBOARD_InitCOLn(void)
{
    stc_port_init_t stcPortInit;
    stc_keyscan_config_t stcKeyscanConfig;

    /* configuration structure initialization */
    MEM_ZERO_STRUCT(stcPortInit);

    /* enable internal pull-up */
    //stcPortInit.enPullUp = Enable;
    PORT_Init(KEYOUT_PORT, (KEYOUT0_PIN | KEYOUT1_PIN | KEYOUT2_PIN), &amp;stcPortInit);

    /* Set corresponding pins to KEYSCAN function */
    PORT_SetFunc(KEYOUT_PORT, (KEYOUT0_PIN | KEYOUT1_PIN | KEYOUT2_PIN), Func_Key, Disable);

    /* enable KEYSCAN module source clock */
    PWC_Fcg0PeriphClockCmd(PWC_FCG0_PERIPH_KEY, Enable);

    /* output Hiz state 512 clocks*/
    stcKeyscanConfig.enHizCycle = Hiz512;

    /* output Low state 512 clocks */
    stcKeyscanConfig.enLowCycle = Low512;

    /* use HCLK as scan clock */
    stcKeyscanConfig.enKeyscanClk = KeyscanHclk;

    /* KEYOUT 0~3 are selected as column */
    stcKeyscanConfig.enKeyoutSel = Keyout0To2;

    /* KEYIN 12~14 are selected as row */
    stcKeyscanConfig.u16KeyinSel = (Keyin12 | Keyin13 | Keyin14);

    KEYSCAN_Init(&amp;stcKeyscanConfig);
}


/*******************************************************************************
* @brief      
* @param      
* @retval      
* @attention   
*******************************************************************************/
void MATRIX_KEYBOARD_Init(void)
{
    /* Keyboard row    0~2 init */
    MATRIX_KEYBOARD_InitROW0();
    MATRIX_KEYBOARD_InitROW1();
    MATRIX_KEYBOARD_InitROW2();

    /* Keyboard column 0~2 init */
    MATRIX_KEYBOARD_InitCOLn();

    /* Start KEYSCAN function*/
    KEYSCAN_Start();
}

#else

/*******************************************************************************
* @brief      
* @param      
* @retval      
* @attention   
*******************************************************************************/
uint8_t MATRIX_KEYBOARD_ScanX(void)
{
    uint8_t Value = 0;

    stc_port_init_t stcPortInit;

    MEM_ZERO_STRUCT(stcPortInit);
    stcPortInit.enPinMode = Pin_Mode_In;
    stcPortInit.enExInt   = Disable;
    stcPortInit.enPullUp= Disable;

    PORT_Init(KEYIN0_PORT, KEYIN0_PIN, &amp;stcPortInit);
    PORT_Init(KEYIN1_PORT, KEYIN1_PIN, &amp;stcPortInit);
    PORT_Init(KEYIN2_PORT, KEYIN2_PIN, &amp;stcPortInit);

    MEM_ZERO_STRUCT(stcPortInit);
    stcPortInit.enPinMode = Pin_Mode_Out;
    stcPortInit.enExInt   = Enable;
    stcPortInit.enPullUp= Enable;

    PORT_Init(KEYOUT0_PORT, KEYOUT0_PIN, &amp;stcPortInit);
    PORT_Init(KEYOUT1_PORT, KEYOUT1_PIN, &amp;stcPortInit);
    PORT_Init(KEYOUT2_PORT, KEYOUT2_PIN, &amp;stcPortInit);

    PORT_SetBits(KEYOUT0_PORT, KEYOUT0_PIN);
    PORT_SetBits(KEYOUT1_PORT, KEYOUT1_PIN);
    PORT_SetBits(KEYOUT2_PORT, KEYOUT2_PIN);

    if(PORT_GetBit(KEYIN0_PORT, KEYIN0_PIN) == Set) Value |= 0x01;
    if(PORT_GetBit(KEYIN1_PORT, KEYIN1_PIN) == Set) Value |= 0x02;
    if(PORT_GetBit(KEYIN2_PORT, KEYIN2_PIN) == Set) Value |= 0x04;

    return Value;
}


/*******************************************************************************
* @brief      
* @param      
* @retval      
* @attention   
*******************************************************************************/
uint8_t MATRIX_KEYBOARD_ScanY(void)
{
    uint8_t Value = 0;

    stc_port_init_t stcPortInit;

    MEM_ZERO_STRUCT(stcPortInit);
    stcPortInit.enPinMode = Pin_Mode_Out;
    stcPortInit.enExInt   = Enable;
    stcPortInit.enPullUp= Enable;

    PORT_Init(KEYIN0_PORT, KEYIN0_PIN, &amp;stcPortInit);
    PORT_Init(KEYIN1_PORT, KEYIN1_PIN, &amp;stcPortInit);
    PORT_Init(KEYIN2_PORT, KEYIN2_PIN, &amp;stcPortInit);

    PORT_ResetBits(KEYIN0_PORT, KEYIN0_PIN);
    PORT_ResetBits(KEYIN1_PORT, KEYIN1_PIN);
    PORT_ResetBits(KEYIN2_PORT, KEYIN2_PIN);

    MEM_ZERO_STRUCT(stcPortInit);
    stcPortInit.enPinMode = Pin_Mode_In;
    stcPortInit.enExInt   = Enable;
    stcPortInit.enPullUp= Enable;

    PORT_Init(KEYOUT0_PORT, KEYOUT0_PIN, &amp;stcPortInit);
    PORT_Init(KEYOUT1_PORT, KEYOUT1_PIN, &amp;stcPortInit);
    PORT_Init(KEYOUT2_PORT, KEYOUT2_PIN, &amp;stcPortInit);

    if(PORT_GetBit(KEYOUT0_PORT, KEYOUT0_PIN) == Reset) Value |= 0x10;
    if(PORT_GetBit(KEYOUT1_PORT, KEYOUT1_PIN) == Reset) Value |= 0x20;
    if(PORT_GetBit(KEYOUT2_PORT, KEYOUT2_PIN) == Reset) Value |= 0x40;

    return Value;
}


/*******************************************************************************
* @brief      
* @param      
* @retval      
* @attention   
*******************************************************************************/
void MATRIX_KEYBOARD_Scan(void)
{
    static uint8_t OldKeyValue = 0;

    uint8_t KeyIndexTable =
    {
      0x11, 0x21, 0x41,
      0x12, 0x22, 0x42,
      0x14, 0x24, 0x44,
    };

    uint8_t NewKeyValue= MATRIX_KEYBOARD_ScanX() | MATRIX_KEYBOARD_ScanY();

    if(NewKeyValue != 0)
    {
      if(NewKeyValue != OldKeyValue)
      {
            OldKeyValue = NewKeyValue;

            for(uint8_t i = 0; i &lt; sizeof(KeyIndexTable); i++)
            {
                if(NewKeyValue == KeyIndexTable)
                {
                  printf("\r\nMatrix Keyboard : %d\r\n", (i+1));
                  break;
                }
            }
      }
    }
    else
    {
      OldKeyValue = NewKeyValue;
    }
}

#endif


/******************* (C) COPYRIGHT 2021 *************************END OF FILE***/

</code></pre>

<p>&nbsp;</p>

<p><span style="color:#c0392b;">在通过KEYSCAN来扫描矩阵按键时,使用到了EIRQ中断;在官方提供的例程中使用的是Int000_IRQn~Int002_IRQn,但在我们程序中Int000_IRQn和Int001_IRQn已经被串口占用了,这边我们使用了Int002_IRQn~Int004_IRQn,这边需要注意一下,要不然无法正常的进入到相应的中断去处理了;</span></p>

<p>&nbsp;</p>

<p>程序运行结果:</p>

<p>因为程序的运行结果是将扫描到的矩阵按键值通过串口输出打印到电脑终端上的,第一段是KEYSCAN功能实现的,第二段则是GPIO功能实现的,运行结果如下图所示:</p>

<p></p>

<p>工程源代码:</p>

<p></p>

<p>&nbsp;</p>

<p>&nbsp;</p>

<p>&nbsp;</p>

火辣西米秀 发表于 2021-4-19 23:17

<p>代码也够长了</p>

<p>最后的打印在电脑终端上效果咋样呢</p>

xld0932 发表于 2021-4-20 08:30

火辣西米秀 发表于 2021-4-19 23:17
代码也够长了

最后的打印在电脑终端上效果咋样呢

<p>最后一张图上就是运行效果哈,每按下一个按键,然后打印出对应按键扫描的健值哈</p>

xld0932 发表于 2021-4-20 08:37

火辣西米秀 发表于 2021-4-19 23:17
代码也够长了

最后的打印在电脑终端上效果咋样呢

<p>代码实现了2种按键扫描的实现方式,用宏定义值来判断使用哪一种,所以代码长了一些;但代码是没有裁剪的,复制直接可以用;看到之前的一些论坛上的测评,只有说明,代码都没有,或者没有完整的代码,对其它人都没有什么参考价值<img height="49" src="https://bbs.eeworld.com.cn/static/editor/plugins/hkemoji/sticker/facebook/loveliness.gif" width="49" /></p>

littleshrimp 发表于 2021-4-20 08:40

<p>这种矩阵扫描涉及去抖问题不?</p>

xld0932 发表于 2021-4-20 08:43

littleshrimp 发表于 2021-4-20 08:40
这种矩阵扫描涉及去抖问题不?

<p>芯片KEYSCAN模块驱动键盘列,而键盘行检测则有中断控制模块( INTC) 的外部 EIRQ 功能实现,EIRQ 需要选择下降沿检测,并开启数字滤波功能,设定合适的滤波时间,这样就具备硬件上的去抖动功能了</p>

littleshrimp 发表于 2021-4-20 09:26

xld0932 发表于 2021-4-20 08:43
芯片KEYSCAN模块驱动键盘列,而键盘行检测则有中断控制模块( INTC) 的外部 EIRQ 功能实现,EIRQ 需要选 ...

<p>看来这种矩阵键盘模块确实很方便 而且你研究的也很深入{:1_144:}</p>

火辣西米秀 发表于 2021-4-20 20:47

xld0932 发表于 2021-4-20 08:37
代码实现了2种按键扫描的实现方式,用宏定义值来判断使用哪一种,所以代码长了一些;但代码是没有裁剪的 ...

<p>已经够可以了</p>

<p>按键扫描本来就不复杂,几乎所有开发板常规测评内容</p>

xld0932 发表于 2021-4-21 12:00

火辣西米秀 发表于 2021-4-20 20:47
已经够可以了

按键扫描本来就不复杂,几乎所有开发板常规测评内容

<p>牛<img height="29" src="https://bbs.eeworld.com.cn/static/editor/plugins/hkemoji/sticker/facebook/smiling-face-with-sunglasses_1f60e.png" width="29" /></p>
页: [1]
查看完整版本: 【HC32F460开发板测评】05.矩阵按键的识别与处理