使用UART单线接口的矩阵键盘驱动,支持按键按下、释放、组合键、长按键以及无键盘活动的检测。
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-串口连接电脑后,可在电脑上实时看到键盘工作状态。

典型工作流程
典型应用通常按以下三个步骤使用本库:
- 当接收到一个 UART 字节时,调用
update_key_status()。 - 在主循环中调用
is_key_changed(),检查是否有新的按键事件。 - 调用
get_key_value()读取按键值,并清除新事件标志。
文件
本驱动包中的核心文件如下:
key_scan.c 驱动实现文件
key_scan.h 驱动头文件
请将 key_scan.c 和 key_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 芯片的七段数码管显示与键盘接口模块