打印
[应用方案]

单片机的按键处理模块

[复制链接]
1229|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
sdlls|  楼主 | 2024-6-21 08:35 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
  • key_board介绍key_board用于单片机中的小巧多功能按键支持,软件采用了分层的思想,并且做到了与平台无关,用户只需要提供按键的基本信息和读写io电平的函数即可,非常方便移植,同时支持多个矩阵键盘及多个单io控制键盘。目前已实现按下触发、弹起触发、长按自动触发、长按弹起触发、多击触发、连续触发等功能,并且能够随意组合(支持状态的同一时间轴和非同一时间轴),后续还会添加更多的功能。使用说明
    • 初始化相关的硬件资源。
    • 提供一个1ms的定时器,用于周期性的调用'key_check'函数。
    • 提供按键的描述及读写io的函数。
    • 将键盘注册到系统。
    • 具体的操作参考提供的stm32例程。
    • 因为程序默认使用了堆内存,当发现程序运行结果不正常时,尝试增大你的程序堆空间,或者注册调试接口查看原因。
    • 更详细的使用教程见详细使用说明或者提供的stm32例程。
    已支持的键盘
    • 矩阵键盘
    矩阵键盘
    • 单io按键
    单io按键详细使用说明将key_board.c,key_board.h,key_board_config.h放进key_board文件夹中并包含进你的工程,添加头文件路径。基础功能移植(以stm32矩阵键盘为例)首先需要一个可使用的定时器(如果不想使用定时器也可直接放到主循环中,但不推荐,会导致时基不准确),固定为1ms触发一次;准备待检测的按键的基本信息,可参考key_board_sample.c文件中的struct key_pin_t结构体,如:


    • struct key_pin_t {


    •     GPIO_TypeDef *port;     //按键端口号


    •     uint16_t pin;           //按键的引脚号


    •     GPIO_PinState valid;    //按键的有效电平(即按键按下时的电平)


    •     GPIO_PinState invalid;  //按键的无效电平(即按键空闲时的电平)


    •     /*


    •     可添加你的其它参数


    •     */


    • };

    定义待检测的按键信息,可参考key_board_sample.c文件中的const struct key_pin_t key_pin_sig[]结构体数组,对应头文件为key_board_sample.h,如:


    • //全局变量


    • const struct key_pin_t key_pin_sig[] = {


    •     {


    •         .port = KEY_PORT_J12,


    •         .pin = KEY_PIN_J12,


    •         .valid = KEY_PRESS_LEVEL_J12,


    •         .invalid = KEY_RELEASE_LEVEL_J12


    •     },


    •     {


    •         .port = KEY_PORT_J34,


    •         .pin = KEY_PIN_J34,


    •         .valid = KEY_PRESS_LEVEL_J34,


    •         .invalid = KEY_RELEASE_LEVEL_J34


    •     },


    •     {


    •         .port = KEY_PORT_J56,


    •         .pin = KEY_PIN_J56,


    •         .valid = KEY_PRESS_LEVEL_J56,


    •         .invalid = KEY_RELEASE_LEVEL_J56


    •     },


    • };

    如果为矩阵键盘还需要定义控制io的相关信息,可参考key_board_sample.c文件中的const struct key_pin_t key_pin_ctrl[]结构体数组,对应头文件为key_board_sample.h,如:


    • const struct key_pin_t key_pin_ctrl[] = {


    •     {


    •         .port = KEY_PORT_J135,


    •         .pin = KEY_PIN_J135,


    •         .valid = KEY_CTL_LINE_ENABLE,


    •         .invalid = KEY_CTL_LINE_DISABLE


    •     },


    •     {


    •         .port = KEY_PORT_J246,


    •         .pin = KEY_PIN_J246,


    •         .valid = KEY_CTL_LINE_ENABLE,


    •         .invalid = KEY_CTL_LINE_DISABLE


    •     },


    • };

    实现按键io的电平读取函数,可参考key_board_sample.c文件中的pin_level_get函数,如:


    • static inline bool pin_level_get(const void *desc)


    • {


    •     struct key_pin_t *pdesc;





    •     pdesc = (struct key_pin_t *)desc;


    •     return HAL_GPIO_ReadPin(pdesc->port, pdesc->pin) == pdesc->valid;


    • }

    如果为矩阵键盘还需要实现按键io的电平写入函数,可参考key_board_sample.c文件中的pin_level_set函数,如:


    • static inline void pin_level_set(const void *desc, bool flag)


    • {


    •     struct key_pin_t *pdesc;





    •     pdesc = (struct key_pin_t *)desc;


    •     HAL_GPIO_WritePin(pdesc->port, pdesc->pin, flag ? pdesc->valid : pdesc->invalid);


    • }

    定义按键的id及功能结构体struct key_public_sig_t,可参考key_board_sample.c文件中的const struct key_public_sig_t key_public_sig[]结构体数组,对应头文件key_board.h,如:


    • const struct key_public_sig_t key_public_sig[] = {


    •     KEY_PUBLIC_SIG_DEF(KEY_UP, &key_pin_sig[0], pin_level_get, KEY_FLAG_NONE),


    •     KEY_PUBLIC_SIG_DEF(KEY_LEFT, &key_pin_sig[1], pin_level_get, KEY_FLAG_NONE),


    •     KEY_PUBLIC_SIG_DEF(KEY_DOWN, &key_pin_sig[2], pin_level_get, KEY_FLAG_NONE),


    •     //下面的是因为使用的矩阵键盘而扩展出来的三个按键


    •     KEY_PUBLIC_SIG_DEF(KEY_ENTER, &key_pin_sig[0], pin_level_get, KEY_FLAG_NONE),


    •     KEY_PUBLIC_SIG_DEF(KEY_RIGHT, &key_pin_sig[1], pin_level_get, KEY_FLAG_NONE),


    •     KEY_PUBLIC_SIG_DEF(KEY_EXIT, &key_pin_sig[2], pin_level_get, KEY_FLAG_NONE),


    • };

    如果为矩阵键盘还需要定义控制io的id及功能结构体struct key_public_ctrl_t,可参考key_board_sample.c文件中的const struct key_public_ctrl_t key_public_ctrl[]结构体数组,对应头文件key_board.h,如:


    • const struct key_public_ctrl_t key_public_ctrl[] = {


    •     KEY_PUBLIC_CTRL_DEF(&key_pin_ctrl[0], pin_level_set),


    •     KEY_PUBLIC_CTRL_DEF(&key_pin_ctrl[1], pin_level_set),


    • };

    初始化键盘,可参考key_board_sample.c文件中的GPIO_Key_Board_Init函数,如:


    • void GPIO_Key_Board_Init(void)


    • {


    •     //硬件io的初始化


    •     GPIO_InitTypeDef GPIO_InitStruct;


    •     unsigned int i;





    •     RCC_KEY_BOARD_CLK_ENABLE();





    •     GPIO_InitStruct.Pull  = GPIO_PULLUP;


    •     GPIO_InitStruct.Mode  = GPIO_MODE_INPUT;


    •     for(i = 0;i < ARRAY_SIZE(key_pin_sig);i++)


    •     {


    •         GPIO_InitStruct.Pin   = key_pin_sig.pin;


    •         HAL_GPIO_Init(key_pin_sig.port, &GPIO_InitStruct);


    •     }







    •     GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;



    •     GPIO_InitStruct.Pull  = GPIO_NOPULL;



    •     GPIO_InitStruct.Mode  = GPIO_MODE_OUTPUT_PP;



    •     for(i = 0;i < ARRAY_SIZE(key_pin_ctrl);i++)



    •     {



    •         GPIO_InitStruct.Pin   = key_pin_ctrl.pin;


    •         HAL_GPIO_Init(key_pin_ctrl.port, &GPIO_InitStruct);


    •     }







    •     //初始化键盘



    •     key_board_init();



    •     //注册键盘到系统中(矩阵键盘)



    •     key_board_register(KEY_BOARD_MATRIX, key_public_sig, ARRAY_SIZE(key_public_sig), key_public_ctrl, ARRAY_SIZE(key_public_ctrl));



    • }



    主流程伪代码框架,更多例子参考main_test.c文件:


    • int main(void)


    • {


    •     //初始化硬件io,并注册键盘


    •     GPIO_Key_Board_Init();


    •     //初始化定时器,用于按键扫描(1ms)


    •     init_tmr();





    •     for(;;)


    •     {


    •         if(key_check_state(KEY_UP, KEY_RELEASE))


    •         {


    •             PRINTF("KEY_UP KEY_RELEASE\r\n");


    •         }


    •         if(key_check_state(KEY_UP, KEY_PRESS))


    •         {


    •             PRINTF("KEY_UP KEY_PRESS\r\n");


    •         }


    •     }


    • }





    • //定时器到期回调处理函数


    • void tmr_irq_callback(void)


    • {


    •     //调用按键扫描核心函数


    •     key_check();


    • }

    扩展功能长按的使用首先确保key_board_config.h文件中宏KEY_LONG_SUPPORT已处于使能状态,并且正确设置了宏KEY_DEFAULT_LONG_TRRIGER_TIME的值;设置按键功能需要对长按进行检测,如:KEY_PUBLIC_SIG_DEF(KEY_UP, &key_pin_sig[0], pin_level_get, KEY_FLAG_PRESS_LONG | KEY_FLAG_RELEASE_LONG)使用例程:


    • if(key_check_state(KEY_UP, KEY_PRESS_LONG))


    • {


    •     PRINTF("KEY_UP KEY_PRESS_LONG\r\n");


    • }


    • if(key_check_state(KEY_UP, KEY_RELEASE_LONG))


    • {


    •     PRINTF("KEY_UP KEY_RELEASE_LONG\r\n");


    • }

    扩展功能连按的使用首先确保key_board_config.h文件中宏KEY_CONTINUOUS_SUPPORT已处于使能状态,并且正确设置了宏KEY_DEFAULT_CONTINUOUS_INIT_TRRIGER_TIME和KEY_DEFAULT_CONTINUOUS_PERIOD_TRRIGER_TIME的值;设置按键功能需要对连按进行检测,如:KEY_PUBLIC_SIG_DEF(KEY_UP, &key_pin_sig[0], pin_level_get, KEY_FLAG_PRESS_CONTINUOUS)使用例程:


    • if(key_check_state(KEY_UP, KEY_PRESS_CONTINUOUS))


    • {


    •     PRINTF("KEY_UP KEY_PRESS_CONTINUOUS\r\n");


    • }

    扩展功能多击的使用首先确保key_board_config.h文件中宏KEY_MULTI_SUPPORT已处于使能状态,并且正确设置了宏KEY_DEFAULT_MULTI_INTERVAL_TIME的值;设置按键功能需要多击进行检测,如:KEY_PUBLIC_SIG_DEF(KEY_UP, &key_pin_sig[0], pin_level_get, KEY_FLAG_PRESS_MULTI | KEY_FLAG_RELEASE_MULTI)使用例程:


    • unsigned int res;


    • res = key_check_state(KEY_UP, KEY_PRESS_MULTI);


    • if(res)


    • {


    •     PRINTF("KEY_UP KEY_PRESS_MULTI:%d\r\n", res);


    • }


    • res = key_check_state(KEY_UP, KEY_RELEASE_MULTI);


    • if(res)


    • {


    •     PRINTF("KEY_UP KEY_RELEASE_MULTI:%d\r\n", res);


    • }

    扩展功能组合状态(同一时间轴)感谢网友:石玉虎[@shi-yuhu]的反馈,已更正之前错误的使用案例。使用例程:


    • unsigned int key_down_release_long, key_up_release_long;


    • key_down_release_long = key_check_state(KEY_DOWN, KEY_RELEASE_LONG);


    • key_up_release_long = key_check_state(KEY_UP, KEY_RELEASE_LONG);


    • if(key_down_release_long && key_up_release_long)


    • {


    •     PRINTF("KEY_DOWN KEY_RELEASE_LONG && KEY_UP KEY_RELEASE_LONG\n");


    • }

    扩展功能组合状态(非同一时间轴)首先确保key_board_config.h文件中宏KEY_COMBINE_SUPPORT已处于使能状态,并且正确设置了宏KEY_DEFAULT_COMBINE_INTERVAL_TIME的值;使用例程:


    • //用于保存注册后的组合状态id


    • static unsigned int test_id1, test_id2;





    • //定义要检测的状态


    • const struct key_combine_t test_combine1[] = {


    •     { .id = KEY_UP,     .state = KEY_PRESS },


    •     { .id = KEY_DOWN,   .state = KEY_PRESS_LONG },


    •     { .id = KEY_UP,     .state = KEY_PRESS },


    • };


    • //注册组合状态


    • test_id1 = key_combine_register(test_combine1, ARRAY_SIZE(test_combine1));





    • const struct key_combine_t test_combine2[] = {


    •     { .id = KEY_UP,     .state = KEY_PRESS },


    •     { .id = KEY_DOWN,   .state = KEY_PRESS },


    •     { .id = KEY_UP,     .state = KEY_PRESS },


    •     { .id = KEY_DOWN,   .state = KEY_PRESS },


    • };


    • test_id2 = key_combine_register(test_combine2, ARRAY_SIZE(test_combine2));





    • if(key_check_combine_state(test_id1))


    • {


    •     PRINTF("combine test_id1\r\n");


    • }





    • if(key_check_combine_state(test_id2))


    • {


    •     PRINTF("combine test_id2\r\n");


    • }



使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

34

主题

4849

帖子

1

粉丝