单片机按键处理模块

news2024/11/17 12:53:05

一 介绍

1.key_board用于单片机中的小巧多功能按键支持,软件采用了分层的思想,并且做到了与平台无关,用户只需要提供按键的基本信息和读写io电平的函数即可,非常方便移植,同时支持多个矩阵键盘及多个单io控制键盘。

2. 目前已实现按下触发、弹起触发、长按自动触发、长按弹起触发、多击触发、连续触发等功能,并且能够随意组合(支持状态的同一时间轴和非同一时间轴),后续还会添加更多的功能。

二 使用说明

1.初始化相关硬件资源
2.提供一个1ms的定时器,用于周期性的调用‘key_check’函数
3.提供按键的描述及读写IO的函数
4.将键盘注册到系统
5.具体的操作参考提供的STM32例程
6.因为程序默认使用了堆内存,当发现程序运行结果不正常时,尝试增大你的程序堆空间,或者注册调试接口查看原因

三 已支持的键盘

矩阵键盘
单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[i].pin;
        HAL_GPIO_Init(key_pin_sig[i].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[i].pin;
        HAL_GPIO_Init(key_pin_ctrl[i].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);
}

八 功能组合状态(同一时间轴)

使用例程:

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");
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1721517.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

LeetCode374猜数字大小

题目描述 我们正在玩猜数字游戏。猜数字游戏的规则如下&#xff1a;我会从 1 到 n 随机选择一个数字。 请你猜选出的是哪个数字。如果你猜错了&#xff0c;我会告诉你&#xff0c;我选出的数字比你猜测的数字大了还是小了。你可以通过调用一个预先定义好的接口 int guess(int n…

从零开始,如何在3个月内吸引3500+用户:我的私域运营秘诀

大家好&#xff0c;我是墨云&#xff0c;一位看起来不像搞技术的IT男。 从去年4月份开始研究AI&#xff0c;去年整整一年都在不停地摸索、怀疑和自我迭代。今年3月份&#xff0c;我决定主攻微信私域&#xff0c;创建了AI觉醒派社群矩阵。3个月内&#xff0c;我成功吸引了3500用…

element-plus中在表格校验输入的值

element-plus中在表格校验输入的值 效果&#xff1a; 注意事项&#xff1a;需要在表单套一个表格的字段 代码&#xff1a; <el-form :model"tableFrom" ref"tableDataRef" :rules"rules" style"margin: 0px !important;">&…

深度神经网络——什么是梯度提升?

在数据科学竞赛中&#xff0c;梯度提升模型&#xff08;Gradient Boosting&#xff09;是一种非常强大的工具&#xff0c;它能够将多个弱学习模型组合起来&#xff0c;形成一个强学习模型。这个过程是通过逐步添加弱学习者来实现的&#xff0c;每个新加入的弱学习者都专注于当前…

电脑缺少运行库,无法启动程序

在我们使用一些软件的时候&#xff0c;由于电脑缺少一些运行库&#xff0c;导致无法启动应用软件&#xff0c;此时需要我们安装缺少的运行库。 比如当电脑提示&#xff1a; Cannot load library Qt5Xlsx.dll 我们就需要下载C得运行库&#xff0c;以满足软件运行需要。 下载链…

SpringBoot前端URL访问本地磁盘文件

SpringBoot前端通过 URL访问本地磁盘文件&#xff0c;其实就是 SpringBoot访问web中的静态资源的处理方式。 SpringBoot 访问web中的静态资源&#xff1a;https://blog.csdn.net/qq_42402854/article/details/90295079 首先&#xff0c;我们知道浏览器访问本地磁盘文件的方式为…

深色系的B端界面,特定场景非常适合。

深色系B端界面有以下几个好处&#xff1a; 提供更好的可读性&#xff1a;深色背景可以提供更高的对比度&#xff0c;使文字和图标更加清晰易读&#xff0c;尤其在低光环境下或者长时间使用的情况下&#xff0c;可以减少眼睛的疲劳。强调重要内容&#xff1a;深色背景可以使重要…

HTML旋转照片盒子

效果图 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><meta http-equiv"X-UA-Compatible" content…

LeeCode热题100(爬楼梯)

爬楼梯这个题我断断续续看了不下5遍&#xff0c;哪次看都是懵逼的&#xff0c;就会说是满足动态规划&#xff0c;满足斐波那契数列&#xff0c;也不说为什么。 本文一定让你明白怎么分析这个题的规律&#xff08;利用数学的递推思想来分析&#xff09;&#xff0c;看不懂来打我…

网络监听技术

网络监听技术 网络监听概述网络监听环境 流量劫持网络环境共享式网络监听原理交换式网络监听交换机的工作方式交换网络监听&#xff1a;交换机集线器交换网络监听&#xff1a;端口镜像交换网络监听&#xff1a;MAC洪泛交换网络监听&#xff1a;MAC洪泛交换网络监听&#xff1a;…

更新详情 | Flutter 3.22 与 Dart 3.4

作者 / Michael Thomsen 过去几个月&#xff0c;Dart & Flutter 部门可谓忙碌非凡&#xff0c;但我们很高兴地宣布&#xff0c;Flutter 3.22 和 Dart 3.4 已经在今年的 Google I/O 大会上精彩亮相&#xff01; Google I/Ohttps://io.google/2024/intl/zh/ 我们始终致力于提…

windows11下将Labelme标注数据转为YOLOV5训练数据集

完整代码&#xff1a; import shutil import os import numpy as np import json from glob import glob import cv2 from sklearn.model_selection import train_test_split from utils.data_dir import root_dirdef convert(size, box):dw 1. / (size[0])dh 1. / (size[1]…

如何仿一个抖音极速版领现金的进度条动画?

效果演示 不仅仅是实现效果&#xff0c;要封装&#xff0c;就封装好 看完了演示的效果&#xff0c;你是否在思考&#xff0c;代码应该怎么实现&#xff1f;先不着急写代码&#xff0c;先想想哪些地方是要可以动态配置的。首先第一个&#xff0c;进度条的形状是不是要可以换&am…

五种主流数据库:常用数据类型

在设计数据库的表结构时&#xff0c;我们需要明确表中包含哪些字段以及字段的数据类型。字段的数据类型定义了该字段能够存储的数据种类以及支持的操作。 本文将会介绍五种主流数据库中常用的数据类型以及如何选择合适的数据类型&#xff0c;包括 MySQL、Oracle、SQL Server、…

零代码本地搭建AI大模型,详细教程!普通电脑也能流畅运行,中文回答速度快,回答质量高...

你好&#xff0c;我是郭震 这篇教程主要解决&#xff1a; 1). 有些读者朋友&#xff0c;电脑配置不高&#xff0c;比如电脑没有配置GPU显卡&#xff0c;还想在本地使用AI&#xff1b; 2). Llama3回答中文问题欠佳&#xff0c;想安装一个回答中文问题更强的AI大模型。 3). 想成为…

汽车IVI中控开发入门及进阶(二十四):杰发科技AC8015

前言: 在此之前的大部分时间,四维图新更多的是以图商的身份在业内出现,但现在四维图新图商之外的技术积累提现在了杰发科技身上,或者是从图商到汽车智能化一体解决方案供应商的角色转变。汽车智能化,可以简单的归为座舱智能化和智能驾驶两个板块。 随着汽车变得越来越智能…

显示器与电脑如何分屏显示?

1.点击电脑屏幕右键--显示设置 2、然后找到屏幕---找到多显示器---选择扩展显示器

重生奇迹MU召唤师如何学习狂暴术?

一、了解狂暴术的基本信息 狂暴术是一种非常强大的技能&#xff0c;可以让召唤师的攻击力和防御力大幅度提高&#xff0c;但同时也会增加自身的伤害。在使用狂暴术之前&#xff0c;召唤师需要仔细考虑自己的状态和对手的情况。 二、学习狂暴术的方法 1.通过任务学习 在游戏…

戴尔科技:一盆冷水浇醒了AIPC

这年头&#xff0c;只要沾上英伟达的公司&#xff0c;不论美股还是大A,都跟着鸡犬升天几轮过&#xff0c;但昨晚英伟达蒸发1064亿美元&#xff0c; 跟着遭罪的也不少&#xff0c;有没有一夜惊魂梦醒的感觉&#xff1f; 今天我们来说说——戴尔科技。 昨晚戴尔科技大跌5.18%&a…