杰理 SDK 文档中心
首页
首页
  • SDK 概述与入门

    • SDK 总览
    • 支持芯片与蓝牙认证
    • 工程结构导航
  • 开发环境与构建

    • 环境搭建与工具链安装
    • 编译指南与工程选择
    • 烧录与生产工具
  • BLE 透传/数传应用

    • 透传应用框架与处理模块
    • 透传与数传示例
    • 多连接与自定义服务示例
    • FindMy 与查找网络示例
  • HID 人机交互应用

    • 键盘与按键设备示例
    • 鼠标设备示例
    • 遥控器示例
    • HID 蓝牙应用模块
  • 公共 BSP 模块

    • 按键、编码器与红外输入
    • 传感器驱动
    • LED 与显示控制
    • 串口与 USB 通信
    • 存储、参数与时钟
    • 电源与温度管理
    • 消息、内存与系统配置
    • OTA 升级框架
  • 蓝牙协议栈与库

    • BLE 控制器与协议栈适配
    • 经典蓝牙 BR/EDR 支持
    • 第三方蓝牙协议
    • 设备管理框架
    • DUT 测试与射频认证
  • 构建系统与开发工具

    • Makefile 构建系统
    • 固件后处理与配置工具
    • 辅助脚本与库合并
  • 文档与硬件资料

    • AT 命令参考
    • 硬件参考资料
    • SDK 文档与在线资源

按键、编码器与红外输入

本文档全面介绍 AW33N BLE SDK 中的人机输入子系统:按键(IO/AD/矩阵)扫描核心引擎、旋转编码器接入方式、红外(IR)解码/编码接口,以及配套的配置工具脚本与示例应用,覆盖从硬件引脚到系统事件分发的完整链路。

Purpose and Scope

本页面向「输入设备」这一能力边界,涵盖:

  • 按键核心引擎 key.c / key.h:多驱动注册、消抖、单击/连击/长按/HOLD 事件判定;
  • 三种按键底层驱动:key_drv_io(IO 直连)、key_drv_ad(ADC 分压,也常用于编码器类输入)、key_drv_matrix(矩阵扫描);
  • 红外输入:ir_decoder 解码接口与 ir_encoder 发射接口;
  • 配置工具脚本(iokey_v1.lua、adkey_v1.lua、key_common.lua、key_msg.lua、irflt_v1.lua)与 HID demo(app_keyboard.c、app_keyfob.c、app_keypage.c)。

以下内容不属于本页边界,请参见对应页面:HID 键鼠上报协议(hid_keyboard.c)、系统消息投递(msg.h / sys_event)、电源管理与唤醒(P33_IO_WKUP_EDGE 相关)、触摸/麦克风输入(KEY_TYPE_TOUCH / KEY_TYPE_MIC / KEY_TYPE_LPTOUCH 仅枚举预留)。

Overview

输入子系统是 BLE 设备用户交互的入口。无论是键盘、遥控器还是带旋钮的穿戴设备,硬件输入最终都要转化为统一的「按键事件」消息,再由应用层映射为蓝牙 HID 上报、音量调节、开机/唤醒等行为。

SDK 采用「驱动接口 + 核心扫描引擎」的分层设计:

  • 每种物理输入(IO 电平、ADC 电压、矩阵行列、红外码值)实现统一的 key_interface_t 接口(key_init + key_get_value);
  • 核心引擎 key.c 以固定周期(10ms tick)调用 key_driver_scan(),依次查询所有注册驱动,完成消抖、按下/抬起判定、连击计数、长按/HOLD 计时;
  • 判定结果通过弱符号函数 key_event_remap()(可被用户重写实现组合键)后封装为 sys_event 投递给应用。

关键概念:

术语含义
NO_KEY(0xff)表示当前无有效按键值
KEY_BASE_CNT(2)消抖基础计数门槛
filter_time / filter_cnt消抖周期数与累加器,连续 filter_time 次采样一致才认为按键有效
long_time / hold_time长按与 HOLD 判定计数(单位:扫描周期)
click_cnt / click_delay_time连击次数与抬起后的连击等待窗口
KEY_TYPE_*按键来源类型枚举(IO/AD/MATRIX/IR/TOUCH/MIC/LPTOUCH)

Architecture

flowchart TD
    subgraph sg_HW["硬件层"]
        GPIO["GPIO 引脚"]
        ADC["ADC 通道"]
        IR_RX["红外接收头"]
    end

    subgraph sg_DRV["驱动层 (apps/app/bsp/common/key 与 ir)"]
        IO_DRV["key_drv_io"]
        AD_DRV["key_drv_ad"]
        MATRIX_DRV["key_drv_matrix"]
        IR_DEC["ir_decoder"]
    end

    subgraph sg_CORE["核心扫描引擎"]
        KEY_C["key.c / key.h"]
        PARA["key_driver_para 扫描参数"]
    end

    subgraph sg_EVT["事件层"]
        REMAP["key_event_remap (weak)"]
        SYS_EVT["sys_event 消息"]
    end

    subgraph sg_APP["应用层"]
        APP["app_keyboard / app_keyfob / app_keypage"]
    end

    GPIO --> IO_DRV
    ADC --> AD_DRV
    GPIO --> MATRIX_DRV
    IR_RX --> IR_DEC
    IO_DRV --> KEY_C
    AD_DRV --> KEY_C
    MATRIX_DRV --> KEY_C
    IR_DEC --> KEY_C
    PARA --> KEY_C
    KEY_C --> REMAP
    REMAP --> SYS_EVT
    SYS_EVT --> APP

架构说明:

  • 驱动层通过 key_interface_t 向核心引擎暴露统一接口,编译宏 KEY_IO_EN / KEY_AD_EN / KEY_MATRIX_EN 决定哪些驱动被编入 key_list 注册表(见 key.c);
  • 核心引擎 key_driver_scan() 是唯一扫描入口,所有事件语义(消抖、连击、长按、HOLD)都在这里收敛,保证上层只面对统一的事件流;
  • 红外解码器 ir_decoder 独立于按键驱动目录(apps/app/bsp/common/ir/),其解码结果可映射为 KEY_TYPE_IR 的按键值进入同一事件管道;
  • 事件层与系统消息总线解耦:key_event_remap 是弱符号钩子,未重定义时原样放行,重定义后即可实现组合键重映射。

按键核心引擎:key.c 实现详解

驱动注册表与初始化

核心引擎维护一个静态驱动注册表 key_list,数组元素是 key_interface_t(含 key_type、key_init 函数指针、key_get_value 函数指针),按编译宏条件装配:

static const key_interface_t *key_list[] = {
#if KEY_IO_EN
    &key_io_info,
#endif
#if KEY_AD_EN
    &key_ad_info,
#endif
#if KEY_MATRIX_EN
    &key_matrix_info,
#endif
};

来源:key.c

key_init() 遍历注册表调用每个驱动的 key_init(),完成 GPIO/ADC 硬件初始化。get_key_value() 则按注册顺序依次调用 key_get_value(),第一个返回非 NO_KEY 的驱动胜出,其 key_type 与 key_num 即当前扫描结果——这意味着驱动顺序决定多输入源的优先级:

static key_io_t get_key_value(void)
{
    key_io_t key;
    key.key_type = NO_KEY;

#if (KEY_IO_EN || KEY_AD_EN || KEY_MATRIX_EN)
    for (uint8_t i = 0; i < (sizeof(key_list) / sizeof(key_list[0])); i++) {
        if (key_list[i]->key_get_value) {
            key.key_num = key_list[i]->key_get_value();
            if (NO_KEY != key.key_num) {
                key.key_type = key_list[i]->key_type;
                return key;
            }
        }
    }
#endif
    key.key_num = NO_KEY;
    return key;
}

来源:key.c

扫描参数与事件语义

struct key_driver_para 承载全部扫描状态与阈值,key_scan_para 是其全局实例。普通模式与鼠标模式(MOUSE_KEY_SCAN_MODE)下的默认值差异显著:

#if MOUSE_KEY_SCAN_MODE
struct key_driver_para key_scan_para = {
    .last_key         = NO_KEY,   //上一次get_value按键值, 初始化为NO_KEY;
    .filter_time      = 2,        //按键消抖延时;
    .long_time        = 3,        //按键判定长按数量
    .hold_time        = (3),      //按键判定HOLD数量
    .click_delay_time = 20,       //按键被抬起后等待连击延时数量
};
#else
struct key_driver_para key_scan_para = {
    .last_key         = NO_KEY,
    .filter_time      = 2,
    .long_time        = 100,      //按键判定长按数量
    .hold_time        = (100 + 50), //按键判定HOLD数量
    .click_delay_time = 20,
};
#endif

来源:key.c

设计意图:以 10ms 扫描周期为基准,long_time = 100 意味着约 1 秒判定长按,hold_time = 150 意味着长按后再持续 500ms 判定 HOLD(持续事件,用于音量连续调节等场景);鼠标模式把长按阈值压到 3 个周期,适合快速点击类外设。filter_time = 2 保证连续两次采样一致才确认按键,兼顾响应速度与机械抖动。

消抖与事件判定状态机

key_driver_scan() 是每次 tick 的核心处理函数,完整流程如下:

flowchart TD
    S0["等待按键 (last_key = NO_KEY)"] -->|"get_value 返回有效键值"| S1["消抖: filter_cnt 累加<br/>(filter_value 记录上次值)"]
    S1 -->|"filter_cnt < filter_time"| S1
    S1 -->|"filter_cnt >= filter_time"| S2{"cur_key != last_key?"}
    S2 -->|"是, 且 cur_key = NO_KEY: 抬起"| S3{"press_cnt >= long_time?"}
    S3 -->|"是 (长按/HOLD 后抬起)"| S4["上报 KEY_EVENT_UP"]
    S3 -->|"否 (短按抬起)"| S5["click_delay_cnt = 1<br/>启动连击等待窗口"]
    S2 -->|"是, 且 cur_key 为有效键: 按下"| S6["press_cnt = 1<br/>click_cnt 按 notify_value 累加"]
    S6 --> S7["press_cnt 每 tick 递增"]
    S7 -->|"press_cnt >= long_time"| S8["长按事件 (long_event)"]
    S7 -->|"press_cnt >= hold_time"| S9["HOLD 事件 (hold_event)"]
    S8 --> S7
    S9 --> S7
    S5 -->|"窗口内再次按下同一按键"| S6
    S5 -->|"窗口超时 (click_delay_time)"| S10["上报单击/多击事件"]
    S10 --> S0
    S4 --> S0

核心判定代码(节选):

//===== 按键消抖处理
    if (cur_key_value != scan_para->filter_value && scan_para->filter_time) {	//当前按键值与上一次按键值如果不相等, 重新消抖处理
        scan_para->filter_cnt = 0; 			//消抖次数清0, 重新开始消抖
        scan_para->filter_value = cur_key_value;	//记录上一次的按键值
        return; 			//第一次检测, 返回不做处理
    }
    if (scan_para->filter_cnt < scan_para->filter_time) {
        scan_para->filter_cnt++;
        return;
    }
//===== 按键消抖结束, 开始判断按键类型(单击, 双击, 长按, 多击, HOLD, (长按/HOLD)抬起)
    if (cur_key_value != scan_para->last_key) {
        if (cur_key_value == NO_KEY) {  //cur_key = NO_KEY; last_key = valid_key -> 按键被抬起
            if (scan_para->press_cnt >= scan_para->long_time) { //长按/HOLD状态之后被按键抬起;
                key_event = KEY_EVENT_UP;
                key_value = scan_para->last_key;
                goto _notify;  	//发送抬起消息
            }
            scan_para->click_delay_cnt = 1;  //按键等待下次连击延时开始
        } else {  //cur_key = valid_key, last_key = NO_KEY -> 按键被按下
            scan_para->press_cnt = 1;  //用于判断long和hold事件的计数器重新开始计时;
            if (cur_key_value != scan_para->notify_value) {  //第一次单击/连击时按下的是不同按键, 单击次数重新开始计数
                scan_para->click_cnt = 1;
                scan_para->notify_value = cur_key_value;
            } else {
                scan_para->click_cnt++;  //单击次数累加
            }

来源:key.c

要点:

  • 消抖采用「连续采样一致」策略:第一次采样到新值先记录 filter_value 并返回,随后 filter_cnt 每 tick 累加,达到 filter_time 后才进入事件判定,从机制上滤除机械抖动与电气毛刺;
  • 抬起判定区分短按抬起与长按/HOLD 后抬起:短按抬起不立即上报,而是启动 click_delay_cnt 连击窗口,等待 click_delay_time(20 tick ≈ 200ms)内是否再次按下,从而合成单击/双击/多击;长按/HOLD 后抬起则直接上报 KEY_EVENT_UP;
  • 连击计数以 notify_value 区分「同一按键的多次点击」与「不同按键的切换」,只有同一键值才累加 click_cnt;
  • 每次检测到有效按键时 is_key_active = 35(35×10ms 的活动窗口),用于系统判断「按键是否在活动」——这是唤醒/省电策略的输入信号之一。

开机唤醒与运行开关

引擎提供三组对外控制原语:

  • set_key_poweron_flag / get_key_poweron_flag / clear_key_poweron_flag:维护按键开机标志,供上电流程判断「是否由按键触发开机」(见 key.c);
  • set_key_driver_scan_enable(enable):全局暂停/恢复按键扫描(例如进入低功耗或充电检测时关闭扫描,见 key.c);
  • key_active_set(P33_IO_WKUP_EDGE edge) / key_active_num_set(u8 key_active):配置按键唤醒边沿与唤醒按键号(声明于 key.h)。

事件出口:key_event_remap 与 sys_event

key_event_remap() 是弱符号(__attribute__((weak)))函数,默认返回 true 原样放行。用户可在自己的代码中强符号重定义该函数,把原始键值改写为组合键键值——例如「音量+」与「播放」同时按下时映射为「下一曲」:

int __attribute__((weak)) key_event_remap(struct sys_event *e)
{
    return true;
}

来源:key.c

配套的 struct key_remap / key_remap_data(声明于 key.h)提供「位组合 → 新键值」的静态映射表结构,供实现组合键查表使用。事件最终封装为 sys_event 消息(key.h 中 key_emit_t 回调签名 int (*)(uint8_t key_status, uint8_t key_num, uint8_t key_type),见 key.h),由系统消息总线投递到应用层。

按键驱动层:IO / AD / 矩阵

三种底层驱动均实现 key_interface_t 接口(key_type、key_init、key_get_value),差异只在「如何读取物理电平」:

驱动文件物理原理典型场景
key_drv_io.c/h直接读取 GPIO 电平,每个按键占用一个引脚少量独立按键、开机键
key_drv_ad.c/h通过 ADC 采样分压电阻网络,多个按键共享一路 ADC,按电压区间区分键值遥控器、带旋钮的紧凑面板
key_drv_matrix.c/h行列扫描(Row/Column),N+M 引脚支持 N×M 按键矩阵全键盘、多按键面板

设计意图:key_drv_ad 采用电压区间判键,机械一致性由电阻精度保证,因此消抖参数(filter_time)与阈值由配置工具生成的代码决定;key_drv_io 响应最快但引脚开销大;key_drv_matrix 用扫描换取引脚数,扫描周期天然与核心引擎的 10ms tick 对齐。

三个驱动的具体引脚配置、电压阈值与键值表由配置工具脚本生成,见下文「配置工具」。驱动头文件中 key_io_info / key_ad_info / key_matrix_info 即为注册到 key_list 的实例符号(条件编译宏 KEY_IO_EN / KEY_AD_EN / KEY_MATRIX_EN 由配置工具输出)。

旋转编码器(编码器)的接入

旋转编码器(Rotary Encoder)在本 SDK 中没有独立的专用驱动文件,按仓库现有实现,编码器 A/B 两相信号通常复用 key_drv_ad(两路 ADC 通道)或 key_drv_io(两路 GPIO 中断/轮询)接入按键系统,再通过 key_event_remap 或应用层根据 A/B 相位差(超前/滞后)区分正反转方向并映射为「音量+/-」等事件。

相关实现位置(详见对应文件):

  • AD 通道式编码器:apps/app/bsp/common/key/key_drv_ad.c、key_drv_ad.h;
  • 配置侧:apps/app/post_build/bd57/AW33N_config_tool/conf/source/board/adkey_v1.lua(AD 按键/旋钮配置)与 apps/app/post_build/bd57/AW33N_config_tool/conf/source/board_common/adkey_v1.lua;
  • 方向判定与键值映射的推荐扩展点是弱符号 key_event_remap()(见上文)。

说明:编码器方向判定与脉冲计数的具体算法位于 key_drv_ad 驱动的实现细节中,本次文档采样未展开该文件内容;接入编码器时请以 key_drv_ad.c 实际代码与 adkey_v1.lua 配置项为准。

红外输入:ir_decoder / ir_encoder

红外输入与发射由 apps/app/bsp/common/ir/ 下的解码器与编码器提供,解码 API 声明于 ir_decoder.h:

/**@brief 红外解码功能初始化
  * @param[in]  gpio    接收引脚
  * @return     无
  */
void ir_decoder_init(u32 gpio);

/**@brief 获取红外原始数据
  * @param[in]  无
  * @return     红外原始数据,4*8bit = (命令反码 + 命令码 + 地址反码 +地址码)
  */
u32 ir_decoder_get_data(void);

/**@brief 获取红外命令码,内部校验
  * @return     红外命令码 cmd
  */
u32 ir_decoder_get_command_value(void);

/**@brief 获取红外地址码,内部校验
  * @return     红外地址码 addr
  */
u32 ir_decoder_get_address_value(void);

来源:ir_decoder.h

红外输入链路要点:

  • ir_decoder_init(gpio) 指定接收引脚后开始解码;解码结果按 NEC 类协议组织为 32bit 原始数据:命令反码 + 命令码 + 地址反码 + 地址码;
  • 提供「内部校验」与「不校验」两套读取接口:ir_decoder_get_command_value() 会做反码校验(抗干扰、防止误触发),..._uncheck() 则直接返回原始码值(用于调试或非标准协议);
  • 红外键值通过 KEY_TYPE_IR(枚举定义见 key.h)进入按键事件管道;遥控器码表过滤可在 irflt_v1.lua 中配置(apps/app/post_build/bd57/AW33N_config_tool/conf/source/board/irflt_v1.lua);
  • 发射侧使用 ir_encoder.c(apps/app/bsp/common/ir/ir_encoder.c),实现遥控/对码等场景的红外发送。

配置工具与编译开关

按键/红外参数由 AW33N 配置工具(AW33N_config_tool)生成代码,源头脚本位于 conf/source:

脚本作用
board_common/key_common.lua按键公共参数(消抖、长按/HOLD 阈值等)
board_common/key_msg.lua按键事件消息映射定义
board/iokey_v1.luaIO 按键引脚与键值表
board/adkey_v1.luaAD 按键/旋钮的 ADC 通道、电压区间与键值表
board/irflt_v1.lua红外遥控码过滤/映射表

配置工具输出会生成 KEY_IO_EN / KEY_AD_EN / KEY_MATRIX_EN / MOUSE_KEY_SCAN_MODE 等编译宏以及 key_io_info 等驱动实例,直接决定 key.c 中 key_list 的装配内容。修改按键映射的正确方式是改 lua 配置并重新生成代码,而不是手改驱动文件——这是 SDK 保持「配置驱动代码」一致性的设计约束。

示例应用

仓库提供三个 HID 输入示例,可作为按键事件到蓝牙上报的完整参考:

  • apps/demo/hid/examples/keyboard/app_keyboard.c:标准键盘按键 → HID 键盘报告;
  • apps/demo/hid/examples/keyfob/app_keyfob.c:遥控器/媒体键应用(典型 IR 或 AD 按键场景);
  • apps/demo/hid/examples/keypage/app_keypage.c/.h:翻页/多媒体按键应用。

这些示例展示了在 sys_event 回调中按 key_type + key_num 分派到蓝牙 HID 上报的惯用法。

核心流程:一次按键的完整生命周期

sequenceDiagram
    participant T as Tick Timer (10ms)
    participant K as key.c (key_driver_scan)
    participant D as 按键驱动 (io/ad/matrix)
    participant E as 事件层 (remap / sys_event)
    participant A as 应用 (app_key*)

    T->>K: 周期调用 key_driver_scan()
    K->>K: get_key_driver_scan_enable() 检查
    K->>D: get_key_value()
    D-->>K: key_io_t {key_type, key_num}
    K->>K: 消抖 (filter_value / filter_cnt)
    K->>K: 事件判定: 按下 / 抬起 / 连击 / 长按 / HOLD
    K->>E: key_event_remap(e) 组合键重映射
    E->>E: 封装为 sys_event 消息
    E-->>A: 消息投递
    A->>A: 业务处理 (HID 上报 / 音量 / 唤醒)

流程要点:

  1. 驱动使能检查:若 set_key_driver_scan_enable(0) 被调用(如低功耗),整个扫描直接返回,保证省电与安全;
  2. 轮询采集:按 key_list 顺序查询驱动,首个非 NO_KEY 结果即为本次采样值;
  3. 消抖:连续 filter_time 次一致才进入判定,机械抖动被挡在事件层之外;
  4. 事件合成:短按抬起进入 click_delay_time 窗口合成单击/双击/多击;长按/HOLD 独立计时并在抬起时上报 KEY_EVENT_UP;
  5. 重映射钩子:key_event_remap 允许把原始键值改写为组合键值,是扩展按键语义的标准扩展点;
  6. 消息投递:最终事件以 sys_event 形式到达应用层(如 app_keyboard.c),由应用决定是 HID 上报、媒体控制还是唤醒系统。

使用示例

示例 1:注册一个按键驱动接口

任何新的输入源(自研传感器、红外码表等)只需实现 key_interface_t 即可接入核心引擎:

typedef struct {
    KEY_TYPE key_type;
    void (*key_init)(void);
    uint8_t(*key_get_value)(void);
} key_interface_t;

typedef struct {
    uint8_t key_type;
    uint8_t key_num;
} key_io_t;

来源:key.h

示例 2:组合键重映射钩子

应用层重定义弱符号函数,实现组合按键的键值改写(例如 bit_value 位组合 → remap_value 新键值):

struct key_remap {
    uint8_t bit_value;
    uint8_t remap_value;
};

struct key_remap_data {
    uint8_t remap_num;
    const struct key_remap *table;
};

来源:key.h

示例 3:初始化与驱动启停

void key_init(void);
void key_scan();
void key_driver_scan(void *_scan_para);
void key_active_set(P33_IO_WKUP_EDGE edge);
void key_active_num_set(u8 key_active);
void set_key_driver_scan_enable(uint8_t enable);

来源:key.h

key_init() 在系统上电阶段调用;key_driver_scan() 由 10ms tick 定时器周期调用(传入 &key_scan_para);进入睡眠前调用 set_key_driver_scan_enable(0) 暂停扫描,配合 key_active_set 配置唤醒边沿。

示例 4:红外解码接口

void ir_decoder_init(u32 gpio);
u32 ir_decoder_get_data(void);                    // 4*8bit = 命令反码+命令码+地址反码+地址码
u32 ir_decoder_get_command_value(void);           // 带反码校验
u32 ir_decoder_get_command_value_uncheck(void);   // 不带校验
u32 ir_decoder_get_address_value(void);           // 带反码校验
u32 ir_decoder_get_address_value_uncheck(void);   // 不带校验

来源:ir_decoder.h

典型用法:初始化时传入红外接收头所在 GPIO;应用轮询或中断中调用 ir_decoder_get_command_value() 获取命令码,与 irflt_v1.lua 配置的码表比对后映射为 KEY_TYPE_IR 键值送入按键事件管道。

配置选项

运行时扫描参数(struct key_driver_para / key_scan_para)

字段类型默认值说明
last_keyuint8_tNO_KEY上一次确认的按键值,用于事件边沿判定
filter_valueuint8_t-消抖过程中的暂存采样值
filter_cntuint8_t-消抖累加计数
filter_timeconst uint8_t2消抖门槛:连续 filter_time 次采样一致才有效
long_timeconst uint8_t100(鼠标模式 3)长按判定计数(≈1s @10ms)
hold_timeconst uint8_t150(鼠标模式 3)HOLD 判定计数(长按 + 500ms)
press_cntuint8_t-按下持续时间计数,与 long/hold_time 对比
click_cntuint8_t-单击次数(用于单击/双击/多击合成)
click_delay_cntuint8_t-抬起后连击等待窗口计数
click_delay_timeconst uint8_t20连击等待窗口长度(≈200ms)
notify_valueuint8_t-连击判定中的目标按键值

默认值来源:key.c key_scan_para;结构定义见 key.h。

编译宏与常量

宏/常量默认值说明
KEY_IO_EN / KEY_AD_EN / KEY_MATRIX_EN配置工具生成决定 key_list 装配哪些驱动
MOUSE_KEY_SCAN_MODE0切换鼠标模式扫描参数(快速长按判定)
KEY_BASE_CNT2消抖基础计数门槛(key.h)
KEY_PRESS_CNT35按键按下有效计数(key.h)
NO_KEY0xff无有效按键值标志
KEY_UART_DEBUG注释态打开后按键日志走 log_info(key.h)

API 参考

按键核心(key.h / key.c)

void key_init(void) 初始化所有已注册驱动(调用各驱动的 key_init)。上电阶段调用一次。

void key_driver_scan(void *_scan_para) 每 10ms tick 调用一次;入参为 struct key_driver_para *(传 &key_scan_para)。完成采样、消抖与事件判定,内部经 key_event_remap 投递消息。当 key_driver_scan_enable == 0 时直接返回。

void key_scan() 扫描入口的便捷包装(声明于 key.h)。

void set_key_driver_scan_enable(uint8_t enable)1 恢复扫描,0 暂停扫描。用于低功耗/充电检测等场景临时禁用按键。

void key_active_set(P33_IO_WKUP_EDGE edge) 配置按键唤醒边沿(上升/下降沿唤醒)。

void key_active_num_set(u8 key_active) 配置参与唤醒的按键号。

void set_key_poweron_flag(uint8_t flag) / uint8_t get_key_poweron_flag(void) / void clear_key_poweron_flag(void) 维护「按键开机」标志,供启动流程区分按键唤醒与复位开机(key.c)。

int key_event_remap(struct sys_event *e)(weak,可重定义) 事件出口钩子:返回 true 放行;重定义后可改写事件键值实现组合键。

红外解码(ir_decoder.h)

函数参数返回说明
ir_decoder_initu32 gpio 接收引脚无启动红外解码
ir_decoder_deinit无无释放解码功能
ir_decoder_get_data无u32原始 32bit 数据(命令反码+命令码+地址反码+地址码)
ir_decoder_get_command_value无u32命令码(内部反码校验)
ir_decoder_get_command_value_uncheck无u32命令码(不校验)
ir_decoder_get_address_value无u32地址码(内部反码校验)
ir_decoder_get_address_value_uncheck无u32地址码(不校验)

失败模式、边界情况与并发注意

  • 误触发与消抖:消抖窗口(filter_time=2)只保证连续两次一致。AD 键与 MIC 共用电容充放电时可能出现开机误判——key.c 中保留了 poweron_cnt 抑制逻辑的注释(key.c),Type-C 耳机类硬件可恢复该逻辑;
  • 长按/HOLD 后抬起:只有 press_cnt >= long_time 的抬起才上报 KEY_EVENT_UP;短按抬起进入连击窗口,若窗口内无后续按下则按单击/多击上报。若应用只监听单击而忽略抬起,注意连击窗口(200ms)会延迟短按事件的最终确认;
  • 多驱动优先级:get_key_value 按 key_list 顺序短路返回,多个驱动同时命中时只有先注册者生效,驱动顺序即优先级;
  • 扫描暂停竞态:key_driver_scan_enable 是普通全局变量,set_key_driver_scan_enable 与 tick 扫描在不同上下文(任务/中断)调用时存在读取时序窗口,应在同一上下文(如任务中)控制启停;
  • 红外干扰:解码数据带反码校验,get_command_value() 可滤除多数环境红外干扰;若使用 _uncheck 接口需应用层自行加码表白名单(对应 irflt_v1.lua);
  • ADC 分压边界:AD 键相邻电压区间过近时,供电波动或器件误差会导致键值跳变,区间划分应在 adkey_v1.lua 中保留足够裕量。

性能与运维注意事项

  • 扫描周期固定 10ms(is_key_active = 35 即 350ms 活动窗口),每次扫描只做几次比较与一次驱动查询,CPU 开销极小;低功耗场景应调用 set_key_driver_scan_enable(0) 并配合 key_active_set 使用硬件唤醒边沿,避免空转;
  • KEY_UART_DEBUG 宏开启后按键轨迹打印到日志,是排查「按键没反应/重复触发」的首选手段;
  • 长按/HOLD 阈值(long_time/hold_time)与产品交互手感直接相关,调整时应同步评估连击窗口 click_delay_time 的配合(例如 HOLD 用于连续音量时,抬起事件 KEY_EVENT_UP 用于停止)。

扩展点

  1. 新增输入源驱动:实现 key_interface_t(key_init + key_get_value),在 key_list 中按编译宏加入注册表即可复用全部消抖/连击/长按逻辑;
  2. 组合键重映射:重定义弱符号 key_event_remap(),配合 key_remap_data 静态映射表;
  3. 红外码表扩展:在 irflt_v1.lua 中扩展遥控码映射,映射目标使用 KEY_TYPE_IR 键值;
  4. 事件语义扩展:KEY_TYPE 枚举预留 TOUCH / MIC / LPTOUCH 类型,触摸与麦克风输入可作为独立驱动接入同一管道。

Related Links

  • 按键核心引擎:key.c / key.h
  • 按键驱动:apps/app/bsp/common/key/key_drv_io.c、key_drv_ad.c、key_drv_matrix.c(含同名 .h)
  • 红外:apps/app/bsp/common/ir/ir_decoder.c、ir_encoder.c;接口头文件 ir_decoder.h
  • HID 键盘上报(兄弟页面):apps/app/bsp/common/usb/device/hid_keyboard.c
  • 配置工具脚本:apps/app/post_build/bd57/AW33N_config_tool/conf/source/board/iokey_v1.lua、adkey_v1.lua、irflt_v1.lua、key_common.lua、key_msg.lua
  • 示例应用:apps/demo/hid/examples/keyboard/app_keyboard.c、apps/demo/hid/examples/keyfob/app_keyfob.c、apps/demo/hid/examples/keypage/app_keypage.c
  • 系统消息与唤醒(兄弟页面):msg.h / sys_event / power_interface.h(P33_IO_WKUP_EDGE)
Next
传感器驱动