单线UART矩阵键盘驱动:基于 C 语言的 UART 单线矩阵键盘驱动库

使用UART单线接口的矩阵键盘驱动,支持按键按下、释放、组合键、长按键以及无键盘活动的检测。

Branch1Tags0
FilesLast commitLast update
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago

UART 单线矩阵键盘驱动

这是一个轻量级 C 语言库,适用于 BC6xxx 和 BC759x 系列 UART 单线键盘接口芯片。BC6xxx 系列仅提供键盘接口功能,BC759x 系列则集成了 LED 显示驱动。支持的型号包括:

本驱动可将芯片UART输出的原始键码转换为应用程序易于使用的按键事件,支持普通按键按下和释放检测、组合键、长按键以及无按键活动检测。

典型应用电路如下。所有支持的芯片都使用相同的接口和协议,因此在扩大或缩小键盘矩阵规模时,只需要进行很少的软件修改。


功能特点

  • 标准 C 语言实现
  • 兼容 C89 及后续版本的 C 编译器
  • 适用于 MCU、嵌入式 Linux、PC 应用以及其他 C 语言环境
  • 内存占用非常小
  • 简单的 UART 事件输入接口
  • 单独按键按下检测
  • 可选的按键释放检测
  • 支持任意组合键
  • 支持长按键
  • 可选的回调模式,适用于事件驱动型应用
  • 支持无键盘活动检测
  • 用户代码量很少

工作原理

每当键盘矩阵状态发生变化时,芯片都会输出一个字节,每个接收到的字节表示一个独立按键开关的状态变化,其数值为按键编号。

数值范围 含义
0x00 - 0x7F 按键按下事件,数值为按键编号。
0x80 - 0xFF 按键释放事件。最高位为释放标志位。

例如:

  • 0x03 表示按键 3 被按下。
  • 0x83 表示按键 3 被释放,因为 0x83 = 0x80 | 0x03

对于简单应用,本库并不是必需的。用户可以直接根据接收到的键值执行对应操作即可。

本库提供了组合键和长按检测等高级功能,使应用程序可以像处理普通单键一样处理复杂的键盘事件。


辅助软件

随库提供了辅助软件,将单线UART矩阵键盘电路通过USB-串口连接电脑后,可在电脑上实时看到键盘工作状态。

Screenshot-bc6561cn

典型工作流程

典型应用通常按以下三个步骤使用本库:

  1. 当接收到一个 UART 字节时,调用 update_key_status()
  2. 在主循环中调用 is_key_changed(),检查是否有新的按键事件。
  3. 调用 get_key_value() 读取按键值,并清除新事件标志。

文件

本驱动包中的核心文件如下:

key_scan.c    驱动实现文件
key_scan.h    驱动头文件

请将 key_scan.ckey_scan.h 一起加入你的项目。


快速开始

完整用户手册在这里(PDF)

#include "key_scan.h"

int main(void)
{
    while (1)
    {
        /* 你的主应用任务 */
        do_something();

        /* 查询键盘状态 */
        if (is_key_changed())
        {
            unsigned char key_value = get_key_value();

            switch (key_value)
            {
                case 3:
                    /* 按键 3 被按下 */
                    do_key3_job();
                    break;

                case 24:
                    /* 按键 24 被按下 */
                    do_key24_job();
                    break;

                default:
                    break;
            }
        }
    }
}

void UART_ISR(void)
{
    if (RI)
    {
        unsigned char rx_data = RxData;
        update_key_status(rx_data);
    }
}

默认情况下,本库只报告按键按下事件。除非启用释放检测,否则按键释放事件会被忽略。


启用按键释放检测

#include "key_scan.h"

int main(void)
{
    set_detect_mode(1);   /* 1 = 同时检测按键按下和按键释放 */

    while (1)
    {
        if (is_key_changed())
        {
            unsigned char key_value = get_key_value();

            switch (key_value)
            {
                case 3:
                    /* 按键 3 被按下 */
                    do_key3_press_job();
                    break;

                case 0x83:
                    /* 按键 3 被释放:0x80 | 3 */
                    do_key3_release_job();
                    break;

                default:
                    break;
            }
        }
    }
}

长按键示例

长按检测基于按键保持有效期间 long_press_tick() 被调用的次数。

#include "key_scan.h"

const unsigned char lp1[] = {5, 120};    /* 按键 5 长按 -> 报告 120 */
const unsigned char lp2[] = {20, 121};   /* 按键 20 长按 -> 报告 121 */

const unsigned char* LPDef[] = {lp1, lp2};

int main(void)
{
    set_longpress_count(60);      /* 如果每 50 ms 调用一次 long_press_tick(),则为 3 秒 */
    def_longpress_key(LPDef, 2);

    while (1)
    {
        if (is_key_changed())
        {
            unsigned char key_value = get_key_value();

            switch (key_value)
            {
                case 120:
                    /* 按键 5 长按 */
                    do_key5_longpress_job();
                    break;

                case 121:
                    /* 按键 20 长按 */
                    do_key20_longpress_job();
                    break;

                default:
                    break;
            }
        }
    }
}

void TIMER_ISR(void)
{
    /* 以固定间隔调用,例如每 50 ms 调用一次 */
    long_press_tick();
}

组合键示例

用户可以定义任意按键组合,并为该组合分配一个特殊按键值。之后,该组合键就可以像普通单个按键一样处理。组合键使用如下格式的数组定义:

{key_count, defined_value, key1, key2, ...}

示例:

#include "key_scan.h"

const unsigned char cb1[] = {2, 122, 0, 7};
/* 按键 0 + 按键 7 -> 报告 122 */

const unsigned char cb2[] = {3, 123, 11, 15, 20};
/* 按键 11 + 按键 15 + 按键 20 -> 报告 123 */

const unsigned char* CBDef[] = {cb1, cb2};
unsigned char CBMap[2];

int main(void)
{
    def_combined_key(CBDef, CBMap, 2);

    while (1)
    {
        if (is_key_changed())
        {
            unsigned char key_value = get_key_value();

            switch (key_value)
            {
                case 122:
                    /* 按键 0 + 按键 7 组合 */
                    do_combination_1_job();
                    break;

                case 123:
                    /* 按键 11 + 按键 15 + 按键 20 组合 */
                    do_combination_2_job();
                    break;

                default:
                    break;
            }
        }
    }
}

每个组合键都需要在映射数组中占用 1 字节 RAM。


检测无键盘活动

可以使用按键值为 0xFF 的特殊长按定义来检测键盘无活动状态。因此,当用户代码收到值为 0xFF 的按键事件时,表示键盘状态已经有一段时间没有发生变化。

#include "key_scan.h"

const unsigned char lp1[] = {5, 120};
const unsigned char lp2[] = {20, 121};
const unsigned char no_key[] = {255, 255};

const unsigned char* LPDef[] = {lp1, lp2, no_key};

unsigned char no_key_time = 0;

int main(void)
{
    set_longpress_count(60);
    def_longpress_key(LPDef, 3);

    while (1)
    {
        if (is_key_changed())
        {
            unsigned char key_value = get_key_value();

            if (key_value == 255)
            {
                no_key_time++;

                if (no_key_time >= 10)
                {
                    sleep();
                }
            }
            else
            {
                no_key_time = 0;
            }
        }
    }
}

回调模式

当检测到有效键盘事件时,本库可以自动调用用户函数。

#include "key_scan.h"

void keyboard_callback(unsigned char key_value)
{
    switch (key_value)
    {
        case 3:
            do_key3_job();
            break;

        default:
            break;
    }
}

int main(void)
{
    set_callback(keyboard_callback);

    while (1)
    {
        do_something();
    }
}

注意:如果 update_key_status() 在 UART 中断中调用,回调函数也会在该中断上下文中执行。请保持回调函数简短,以免增加中断延迟。


API 参考

void set_detect_mode(uint8_t Mode)

设置是否报告按键释放事件。

Mode 说明
0 只报告按键按下事件。这是默认模式。
1 同时报告按键按下和按键释放事件。

void set_longpress_count(uint16_t CountLimit)

设置长按阈值。实际长按时间为:

长按时间 = CountLimit x long_press_tick() 的调用间隔

默认值:2000

void set_callback(void (*pCallbackFunc)(uint8_t))

设置键盘事件回调函数。传入 NULL 可禁用回调模式。

void update_key_status(uint8_t RxData)

将接收到的 UART 字节传递给驱动。该函数通常在 UART 接收中断中调用。

void long_press_tick(void)

递增内部长按计时器。请以固定间隔调用该函数,通常在定时器中断中调用。

uint8_t is_key_changed(void)

当有新的按键事件可用时,返回非零值。

uint8_t get_key_value(void)

返回最新按键值,并清除新事件标志。

void def_combined_key(const uint8_t** pCBKeyList, uint8_t* pCBKeyMap, uint8_t CBKeyCount)

定义用户组合键。

  • pCBKeyList:指向组合键定义数组的指针数组
  • pCBKeyMap:用于跟踪组合键状态的 RAM 数组
  • CBKeyCount:组合键数量

组合键定义格式:

{count, defined_value, key1, key2, ...}

void def_longpress_key(const uint8_t** pLPKeyList, uint8_t LPKeyCount)

定义用户长按键。

长按键定义格式:

{original_key_value, defined_longpress_value}

使用 original_key_value = 0xFF 可检测长时间无键盘活动。


集成说明

  • 请根据芯片数据手册,将芯片的 UART 输出连接到主机 MCU 的 UART 接收输入。
  • 对每个接收到的 UART 字节调用 update_key_status()
  • 使用未被占用的按键值作为自定义组合键或长按事件值。
  • 如果未启用释放检测,按键释放事件不会报告给应用程序。
  • 如果启用了回调模式,is_key_changed() 通常不会报告新的按键,因为事件已被回调函数消费。
  • 请保持中断服务程序简短,尤其是在使用回调模式时。

支持的应用场景

本库适用于:

  • MCU 键盘输入接口
  • Arduino 风格的嵌入式应用
  • 工业控制面板
  • IoT 设备输入面板
  • 智能家居控制设备
  • UART 键盘模块
  • 基于 BC759x 芯片的七段数码管显示与键盘接口模块

Introduction

使用UART单线接口的矩阵键盘驱动,支持按键按下、释放、组合键、长按键以及无键盘活动的检测。

Customize your domain