杰理 SDK 文档中心
首页
首页
  • 项目概览与快速开始

    • 项目概述与芯片支持
    • 环境搭建与工具链
    • 工程与构建系统
    • 烧录与升级工具
    • 文档与硬件资料
  • 系统架构与芯片平台

    • 芯片平台与启动流程
    • 预编译库与头文件体系
    • 消息、定时器与中断服务
    • 通用外设驱动
  • 存储与文件系统

    • 文件系统实现
    • 存储设备驱动
    • VM 参数存储系统
  • 音频处理

    • 音频解码器
    • 音频编码器
    • MIDI 合成与播放
    • 音效、变速变调与降噪
  • 语音玩具应用

    • 应用框架与状态机
    • 音乐播放与外部音源
    • MIDI 乐器模式
    • 录音应用
    • 待机、电源管理与 USB 从机
  • 小音箱应用

    • 应用框架与模式管理
    • 播放源:音乐、FM、录音与 LineIn
  • 应用层与示例工程

    • 通用 MCU 应用
  • 固件更新与补丁

    • 固件升级机制
    • AD14N 主动降噪补丁

通用外设驱动

本文档介绍杰理 AD15N/AD1NN SDK 中 sdk/app/bsp/common 下的通用外设驱动层:按键驱动(IO/AD/矩阵/IR/触摸)、软件 I2C(iic_soft)、FM 收音驱动、NOR Flash 驱动与编码器驱动,说明其架构、接口、配置方式与工作机制。

Purpose and Scope

本页覆盖 SDK 中与应用无关、可复用的"通用外设驱动"能力,即 sdk/app/bsp/common 目录下各外设子模块:

  • 按键驱动(key/):IO 按键、AD 按键、矩阵按键、IR 按键、触摸按键的统一扫描与状态机;
  • 软件 I2C(iic_soft/):用 GPIO 位操作模拟 I2C 时序的通用总线驱动;
  • FM 收音(fm/):BK1080、QN8035、RDA5807 等收音芯片驱动及 I2C 读写适配;
  • NOR Flash(norflash/):外部 SPI NOR Flash 与 NORFS 文件系统的设备驱动;
  • 编码器(encoder/):旋转编码器 API。

以下内容不在本页展开,属于兄弟页面范畴:音频解码器(decoder/)、文件系统与虚拟文件层(fs/)、电源管理(power_manage/)、USB 主机/从机(usb/)、VM 参数管理(vm/)等。本页聚焦"驱动如何被配置、注册、调用"这一通用机制,以及各外设驱动的典型用法。

Overview

在 AD15N SDK 中,外设访问遵循"平台数据配置 → 驱动初始化 → 统一接口调用"的三层模式:

  1. 平台数据(Platform Data):每个外设驱动都定义一个配置结构体(如 struct soft_iic_config、struct norflash_dev_platform_data),由板级代码通过 XXX_PLATFORM_DATA_BEGIN/END 宏实例化,描述引脚、时钟、延时等硬件相关参数。这种"数据与逻辑分离"的设计让同一份驱动代码可以在不同硬件板上复用。
  2. 驱动层:每个外设模块提供 init/uninit/suspend/resume 以及业务读写接口(如 soft_iic_read_buf、key_scan),并通过 key_interface_t 之类的接口结构体暴露能力,便于上层做多态分发。
  3. 设备管理层(dev_mg):需要接入统一设备模型的外设(如 NOR Flash)通过 device_operations 结构体向 dev_mg/device.h 注册,应用层用 dev_open/dev_read/dev_write 等统一接口访问,实现"换设备不改应用"。

按键驱动是本 SDK 中最典型的通用外设驱动:key.h 定义了 5 种按键类型(IO/AD/矩阵/IR/触摸)、6 种按键状态(短按/短按释放/长按/连按/长按释放/双击),并给出滤波与状态判定的门槛值。这些状态统一通过 key_emit_t 回调输出到应用,应用只需实现消息过滤与响应,无需关心底层是哪种按键物理实现。

Architecture

下图展示通用外设驱动在 SDK 中的位置与依赖关系:

flowchart TD
    subgraph sg_App["应用层 (app)"]
        AppTask["应用任务/消息处理"]
    end

    subgraph sg_Drv["通用外设驱动层 (bsp/common)"]
        Key["key 按键驱动<br/>IO / AD / 矩阵 / IR / 触摸"]
        IicSoft["iic_soft 软件 I2C"]
        Fm["fm 收音驱动<br/>BK1080 / QN8035 / RDA5807"]
        Norflash["norflash 驱动"]
        Encoder["encoder 编码器"]
    end

    subgraph sg_DevMg["设备管理层 (dev_mg)"]
        DevMg["device.h / device_operations"]
    end

    subgraph sg_Hw["硬件层"]
        Gpio["GPIO"]
        Adc["ADC"]
        Spi["SPI1 / SPI2"]
        Chip["外设芯片"]
    end

    AppTask -->|"按键消息"| Key
    AppTask -->|"read/write 接口"| IicSoft
    AppTask -->|"read/write 接口"| Fm
    AppTask -->|"dev_open / dev_read"| DevMg
    DevMg -->|"device_operations"| Norflash
    Key --> Gpio
    Key --> Adc
    IicSoft --> Gpio
    Fm --> IicSoft
    Fm --> Chip
    Norflash --> Spi
    Encoder --> Gpio

各节点职责说明:

  • key 按键驱动:key.c 为总控,key_drv_io.c / key_drv_ad.c / key_matrix.c / key_ir.c / key_touch.c 为各类型底层采集,统一实现 key_interface_t(key_init + key_get_value)。
  • iic_soft 软件 I2C:完全由 GPIO 位操作实现,soft_iic_config 指定 scl、sda、delay 与是否内部上拉。FM 驱动等芯片驱动依赖它进行总线访问。
  • fm 收音驱动:fm_iic_dri.h 提供读写适配,BK1080/QN8035/RDA5807 是具体芯片的寄存器级实现,彼此独立、可替换。
  • norflash 驱动:通过 norflash_dev_ops / norfs_dev_ops 两个 device_operations 实例接入设备管理层,支持 SPI1/SPI2 硬件接口,是"驱动接入统一设备模型"的范例。
  • encoder 编码器:encoder_api.c 提供旋转编码器/正交解码的 API 封装。

设计意图:驱动层刻意保持"平台无关",所有硬件差异被收敛到平台数据结构体与回调函数指针中。这样应用层(解码、FM、按键响应)可以跨板移植,同时各驱动又可独立开启/关闭(见 key.h 中 KEY_XXX_EN 开关宏),有效控制代码体积与功耗。

按键驱动(key)

按键驱动位于 sdk/app/bsp/common/key/,是整个 SDK 中复用度最高、机制最完整的通用外设驱动。它把"物理按键采集"与"业务响应"彻底解耦:底层驱动只负责读取电平/ADC/矩阵/红外/触摸值,上层状态机负责滤波与事件判定,最终通过回调把事件投递给应用。

按键类型与使能开关

key.h 用一组编译期宏控制各类型按键是否参与编译,并用枚举定义按键类型:

#define KEY_IO_EN         0   ///<IO按键使能
#define KEY_AD_EN         1   ///<AD按键使能
#define KEY_MATRIX_EN     0   ///<矩阵按键使能
#define KEY_IR_EN         0   ///<IR按键使能
#define KEY_TOUCH_EN      0   ///<触摸按键使能

/*按键类型*/
typedef enum {
    KEY_TYPE_IO,
    KEY_TYPE_AD,
    KEY_TYPE_MATRIX,
    KEY_TYPE_IR,
    KEY_TYPE_TOUCH,
} KEY_TYPE;

Source: key.h

设计意图:MCU 资源有限,用宏裁剪可以让未使用的按键驱动代码完全不参与链接,同时保留 KEY_TYPE_* 枚举作为运行时类型标签,供 key_table_sel() 分发时识别事件来源。KEY_DOUBLE_CLICK_EN 单独控制双击功能(默认关闭),避免无需求时增加状态机复杂度。

按键状态机与门槛值

key.h 定义了统一的按键事件集合,任何类型的按键最终都映射到这 6 种状态:

/*按键门槛值*/
#define KEY_BASE_CNT  2
#define KEY_LONG_CNT  75
#define KEY_HOLD_CNT  15
#define KEY_SHORT_CNT 3

/*按键状态*/
enum {
    KEY_SHORT    =   0x0,
    KEY_SHORT_UP =   0x1,
    KEY_LONG     =   0x2,
    KEY_HOLD     =   0x3,
    KEY_LONG_UP  =   0x4,
    KEY_DOUBLE   =   0x5,

    KEY_MAX_STATUS,
};

Source: key.h

各门槛值含义(以 10ms 扫描周期计):

宏值含义
KEY_BASE_CNT2消抖计数:连续 2 次采样一致才确认按下,滤除机械抖动
KEY_SHORT_CNT3短按判定基准
KEY_LONG_CNT75长按触发阈值(约 750ms)
KEY_HOLD_CNT15长按后的连按(HOLD)周期(约 150ms 触发一次 HOLD 事件)
KEY_DOUBLE_CLICK_CNT35双击判定窗口(约 350ms),配合 KEY_DOUBLE_CLICK_EN 使用

状态设计说明:KEY_SHORT(按下即报)与 KEY_SHORT_UP(释放才报)并存,让应用既可以在按下瞬间立即响应(如音量键连点),也可以在释放时确认非长按再响应;KEY_LONG 之后每 KEY_HOLD_CNT 周期重复上报 KEY_HOLD,实现"长按连续调节"效果;KEY_LONG_UP 让应用在长按结束时收到收尾事件(如停止快进)。

接口抽象:key_interface_t

各按键类型驱动通过同一个函数指针结构对外暴露,实现多态采集:

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

Source: key.h

  • key_type:标识该接口实例属于哪类按键(KEY_TYPE_IO 等),用于 key_table_sel() 的路由;
  • key_init:初始化对应引脚/ADC 通道/定时器等资源;
  • key_get_value:返回当前采到的键值(无键按下返回 NO_KEY,即 0xff)。

对应实现文件:key_drv_io.c(读 GPIO)、key_drv_ad.c(读 ADC 分压,支持最多 AD_KEY_MAX_NUM=10 键)、key_matrix.c(行列扫描)、key_ir.c(红外解码,支持最多 IR_KEY_MAX_NUM=21 键)、key_touch.c(触摸检测)。每种类型均有独立的按键数量上限宏(IO_KEY_MAX_NUM/AD_KEY_MAX_NUM/MATRIX_KEY_MAX_NUM/IR_KEY_MAX_NUM/TOUCH_KEY_MAX_NUM,均为 10,IR 为 21)。

事件输出与调试

/*按键输出注册接口*/
typedef int (*key_emit_t)(u8 key_status, u8 key_num, u8 key_type);

Source: key.h

key_emit_t 是按键事件的统一出口:key_status 为上述状态枚举值,key_num 为键位号,key_type 为类型。应用通过 key_table_sel(void *msg_filter) 注册过滤/映射表,把"键位号+状态"翻译成业务消息。key.h 还提供 KEY_UART_DEBUG 调试开关,开启后 key_puts/key_printf/key_buf 映射到 log_info 系列日志,便于在串口上观察扫描过程(默认关闭以减小代码体积)。

软件 I2C(iic_soft)

iic_soft 用两个 GPIO 位操作完整模拟 I2C 主机时序(START/STOP/ACK/字节收发),是 FM、触摸等外部芯片访问的基础总线。其优势是任意 GPIO 均可作为 SCL/SDA,不占用硬件 I2C 外设,代价是速率受 delay 与 CPU 频率影响。

typedef const int soft_iic_dev;

struct soft_iic_config {
    int scl;
    int sda;
    u32 delay;
    u8 io_pu;
};

Source: iic_soft.h

  • scl / sda:使用的 GPIO 引脚号;
  • delay:每个位周期的延时(决定时钟频率,值越大速率越低);
  • io_pu:是否使能内部上拉(接开漏/无上拉器件时置 1)。

soft_iic_dev 被定义为 const int,本质是一个设备实例索引,指向 soft_iic_cfg[] 配置表中的某一项——soft_iic_cfg 由板级代码定义(extern const struct soft_iic_config soft_iic_cfg[])。这样同一份驱动可管理多路软件 I2C(如一路接 FM、一路接触摸),彼此通过索引区分。驱动同时提供 soft_iic_suspend/resume,配合电源管理在低功耗前后保存/恢复 GPIO 状态。

FM 收音驱动(fm)

fm/ 目录下按芯片厂商拆分驱动,fm_iic_dri.h 提供统一的 I2C 读写适配层,各芯片驱动(BK1080.h、QN8035.h、RDA5807.h)通过宏定义芯片参数,例如 BK1080 的器件地址 CHIP_DEV_ID 0x80、晶振配置 XTAL_CLOCK 1:

#define CHIP_DEV_ID     0x80
#define XTAL_CLOCK      1

Source: BK1080.h

设计意图:FM 芯片型号由板级宏选择,驱动源码互不干扰;所有芯片共用 fm_iic_dri 的总线访问方式(底层走 iic_soft),换芯片时只需替换寄存器操作层,应用层的 FM 功能接口保持不变。这也体现了"驱动按芯片隔离、总线按平台抽象"的分层原则:AppTask -> Fm -> fm_iic_dri -> soft_iic。

NOR Flash 驱动(norflash)

norflash 是接入统一设备模型(dev_mg)的典型外设。norflash.h 定义平台数据结构与实例化宏,并把 device_operations 暴露给设备管理层:

struct norflash_dev_platform_data {
    s8 spi_hw_num;         //只支持SPI1或SPI2
    ...
};

#define NORFLASH_DEV_PLATFORM_DATA_BEGIN(data) \
	const struct norflash_dev_platform_data data = {

#define NORFLASH_DEV_PLATFORM_DATA_END()  \
};

extern const struct device_operations norflash_dev_ops;
extern const struct device_operations norfs_dev_ops;

Source: norflash.h

要点:

  • spi_hw_num 限定只支持 SPI1 或 SPI2,即 NOR Flash 必须挂在硬件 SPI 上,不适用软件 SPI;
  • NORFLASH_DEV_PLATFORM_DATA_BEGIN/END 宏把"定义结构体变量 + 赋值"封装成声明式语法,板级代码只需在 BEGIN/END 之间填写字段即可实例化一份平台数据,降低书写错误;
  • norflash_dev_ops(裸 Flash 块设备)与 norfs_dev_ops(NORFS 文件系统设备)是两个独立 device_operations,分别对应"原始读写"与"文件系统挂载"两种使用方式;
  • 驱动使能由 TCFG_NORFLASH_DEV_ENABLE 控制(EXT_FLASH_EN 打开时自动开启),并通过 dev_mg 的 dev_open/dev_read/dev_write/ioctl 统一访问,vfs_fat.h 中 vfs_format() 等接口即基于设备名(如 "sd0")操作这类块设备。

编码器驱动(encoder)

encoder_api.c 位于 sdk/app/bsp/common/encoder/,提供旋转编码器(正交脉冲输入)的应用层 API。编码器常见于音量旋钮、菜单旋钮等场景,驱动负责对 A/B 相脉冲计数并转换为相对步进值,供应用映射为音量加减、列表翻页等操作。它与按键驱动一样属于"采集 + 事件化"模式,底层同样依赖 GPIO 中断或轮询。

驱动层共性机制小结

机制体现目的
平台数据结构体soft_iic_config、norflash_dev_platform_data硬件差异配置化,驱动代码跨板复用
编译期裁剪KEY_IO_EN、TCFG_NORFLASH_DEV_ENABLE、EXT_FLASH_EN未用驱动不链接,节省 Flash/RAM
回调/接口多态key_interface_t、key_emit_t、device_operations上层与具体实现解耦
声明式实例化宏NORFLASH_DEV_PLATFORM_DATA_BEGIN/END减少样板代码,防止结构体初始化漏项
统一设备模型norflash_dev_ops 注册进 dev_mg应用以 dev_open/dev_read 访问,换设备不改应用
电源管理挂钩soft_iic_suspend/resume低功耗切换时保存/恢复外设状态

Core Flow

按键扫描与事件分发流程

按键驱动由系统定时器周期驱动(KEY_DOUBLE_CLICK_CNT 35 注释表明扫描 tick 为 10ms),key_scan() 完成"采样 → 滤波 → 状态判定 → 事件输出"的完整链路:

sequenceDiagram
    participant T as 系统定时器 (10ms tick)
    participant K as key_scan()
    participant D as key_drv_io / key_drv_ad / matrix / ir / touch
    participant S as 按键状态机 (消抖/短按/长按/连按)
    participant F as key_table_sel() 事件过滤表
    participant A as 应用消息队列

    loop 每个扫描周期
        T->>K: 周期调用 key_scan()
        K->>D: key_get_value() 采集键值
        D-->>K: key_num / NO_KEY(0xff)
        K->>S: 计数滤波 (KEY_BASE_CNT) 与状态迁移
        S-->>K: key_status (KEY_SHORT/LONG/HOLD/DOUBLE...)
        K->>F: key_emit_t(key_status, key_num, key_type)
        F->>A: 映射为业务消息并投递
        A-->>F: 消息已处理
    end

按键状态迁移

stateDiagram-v2
    [*] --> Idle: 初始化
    Idle --> Press: 连续采样达到 KEY_BASE_CNT
    Press --> Short: 短按释放 (KEY_SHORT / KEY_SHORT_UP)
    Press --> Long: 达到 KEY_LONG_CNT 触发 KEY_LONG
    Long --> Hold: 每 KEY_HOLD_CNT 周期上报 KEY_HOLD
    Hold --> LongUp: 释放上报 KEY_LONG_UP
    Short --> Double: 窗口内再次按下 (需 KEY_DOUBLE_CLICK_EN)
    Double --> Idle: 上报 KEY_DOUBLE
    LongUp --> Idle
    Short --> Idle

软件 I2C 读写时序

flowchart LR
    A["soft_iic_start()<br/>SCL高时SDA拉低"] --> B["soft_iic_tx_byte()<br/>发送地址+方向位"]
    B --> C{"收到 ACK?"}
    C -->|"是"| D["soft_iic_write_buf() /<br/>soft_iic_read_buf() 数据阶段"]
    C -->|"否"| E["返回错误<br/>总线释放"]
    D --> F["soft_iic_stop()<br/>SCL高时SDA拉高"]
    E --> F

Usage Examples

按键驱动:配置开关与门槛值

#define KEY_IO_EN         0   ///<IO按键使能
#define KEY_AD_EN         1   ///<AD按键使能
#define KEY_MATRIX_EN     0   ///<矩阵按键使能
#define KEY_IR_EN         0   ///<IR按键使能
#define KEY_TOUCH_EN      0   ///<触摸按键使能

Source: key.h

上例为典型的板级配置:仅开启 AD 按键(ADC 分压按键),其余类型全部裁剪。应用侧随后调用:

void key_init(void);
void key_scan();
void key_table_sel(void *msg_filter);

Source: key.h

key_init() 初始化所有使能的按键类型;key_scan() 放入定时器/任务循环周期执行;key_table_sel() 在初始化早期注册事件过滤表(msg_filter 指向将 key_status/key_num 翻译为应用消息的映射)。

软件 I2C:总线初始化与读写

typedef const int soft_iic_dev;

struct soft_iic_config {
    int scl;
    int sda;
    u32 delay;
    u8 io_pu;
};

Source: iic_soft.h

板级代码先定义 soft_iic_cfg[](每项是一路 I2C 的引脚/延时/上拉配置),再以索引调用驱动接口:

int soft_iic_init(soft_iic_dev iic);
int soft_iic_write_buf(soft_iic_dev iic, const void *buf, int len);
int soft_iic_read_buf(soft_iic_dev iic, void *buf, int len);
void soft_iic_start(soft_iic_dev iic);
void soft_iic_stop(soft_iic_dev iic);

Source: iic_soft.h

典型的芯片寄存器写流程为:soft_iic_init(iic) → soft_iic_start(iic) → soft_iic_tx_byte(iic, 芯片地址|写) → soft_iic_tx_byte(iic, 寄存器地址) → soft_iic_tx_byte(iic, 数据) → soft_iic_stop(iic)。FM 驱动(fm_iic_dri.h)正是封装了这套流程供 BK1080/QN8035/RDA5807 复用。

NOR Flash:平台数据实例化与设备注册

#define NORFLASH_DEV_PLATFORM_DATA_BEGIN(data) \
	const struct norflash_dev_platform_data data = {

#define NORFLASH_DEV_PLATFORM_DATA_END()  \
};

extern const struct device_operations norflash_dev_ops;
extern const struct device_operations norfs_dev_ops;

Source: norflash.h

板级代码使用宏声明一个平台数据实例(如指定 spi_hw_num 为 SPI1 或 SPI2),驱动通过 norflash_dev_ops 接入 dev_mg;上层随后可用 vfs_format()/vfs_mount 等文件系统接口对设备名(如 "sd0")操作,实现"驱动-设备-文件系统"三级贯通。

Configuration Options

按键驱动(key.h)

宏类型默认值说明
KEY_IO_EN编译期开关0IO 按键使能
KEY_AD_EN编译期开关1AD(ADC 分压)按键使能
KEY_MATRIX_EN编译期开关0矩阵按键使能
KEY_IR_EN编译期开关0红外按键使能
KEY_TOUCH_EN编译期开关0触摸按键使能
KEY_DOUBLE_CLICK_EN编译期开关0双击功能使能
KEY_BASE_CNT计数门槛2消抖采样次数
KEY_SHORT_CNT计数门槛3短按判定基准
KEY_LONG_CNT计数门槛75长按触发阈值(约 750ms @10ms tick)
KEY_HOLD_CNT计数门槛15长按后连按上报周期
KEY_DOUBLE_CLICK_CNT计数门槛35双击判定窗口(约 350ms)
IO_KEY_MAX_NUM / AD_KEY_MAX_NUM / MATRIX_KEY_MAX_NUM上限10各类型最大键位数
IR_KEY_MAX_NUM上限21IR 最大键位数
NO_KEY常量0xff无按键采样的返回值

Source: key.h

软件 I2C(iic_soft.h)

配置项类型说明
soft_iic_cfg[].sclintSCL GPIO 引脚号
soft_iic_cfg[].sdaintSDA GPIO 引脚号
soft_iic_cfg[].delayu32位周期延时(决定总线速率)
soft_iic_cfg[].io_puu8是否使能内部上拉
soft_iic_devconst int设备实例索引(指向 soft_iic_cfg[] 表项)

Source: iic_soft.h

NOR Flash(norflash.h)

宏/字段类型说明
EXT_FLASH_EN开关外部 Flash 总开关,开启后自动定义 TCFG_NORFLASH_DEV_ENABLE
TCFG_NORFLASH_DEV_ENABLE开关启动 NOR Flash 设备
norflash_dev_platform_data.spi_hw_nums8硬件 SPI 编号,只支持 SPI1/SPI2
norflash_dev_opsdevice_operations裸 Flash 块设备操作集
norfs_dev_opsdevice_operationsNORFS 文件系统设备操作集

Source: norflash.h, norflash.h

API Reference

按键驱动

  • void key_init(void) — 初始化所有使能的按键类型(对应 key_interface_t.key_init 实现)。
  • void key_scan(void) — 周期扫描入口,完成采样、滤波、状态机判定与事件输出,须由定时器/任务以固定周期(10ms)调用。
  • void key_table_sel(void *msg_filter) — 注册事件过滤/映射表,决定按键事件如何翻译为应用消息。

Source: key.h

  • typedef int (*key_emit_t)(u8 key_status, u8 key_num, u8 key_type) — 按键事件回调原型;key_status 取值 KEY_SHORT(0x0)、KEY_SHORT_UP(0x1)、KEY_LONG(0x2)、KEY_HOLD(0x3)、KEY_LONG_UP(0x4)、KEY_DOUBLE(0x5)。返回非 0 通常表示事件已被消费。

Source: key.h, key.h

软件 I2C

函数参数返回说明
soft_iic_initiic 设备索引int初始化该路 I2C 的 GPIO 与上拉配置;失败返回错误码
soft_iic_uninitiicvoid释放该路 I2C 资源
soft_iic_suspend / soft_iic_resumeiicvoid低功耗挂起/恢复(保存/恢复 GPIO 状态)
soft_iic_start / soft_iic_stopiicvoid生成 START/STOP 时序
soft_iic_tx_byteiic, byteu8发送一字节,返回 ACK 状态
soft_iic_rx_byteiic, acku8接收一字节,ack 控制应答位
soft_iic_read_buf / soft_iic_write_bufiic, buf, lenint批量读写,返回实际传输长度或错误

Source: iic_soft.h

Failure Modes, Edge Cases & Concurrency

  • 按键抖动与误触发:机械按键按下/释放瞬间会产生多次电平跳变,KEY_BASE_CNT 的连续采样计数在状态机入口做消抖;NO_KEY(0xff)作为"无键"哨兵值,避免 0 号键位与无键混淆。若抖动严重,应提高 KEY_BASE_CNT,代价是响应延迟增加。
  • 长按与短按竞争:KEY_LONG_CNT(750ms)与短按窗口相互制约——用户按住超过长按阈值后只上报 KEY_LONG/HOLD 系列事件,不再上报 KEY_SHORT;应用需在 KEY_LONG_UP 处做收尾,否则可能出现"快进未停止"的悬挂状态。
  • 双击误判:KEY_DOUBLE_CLICK_EN 默认关闭;开启后若第二次按下发生在 KEY_DOUBLE_CLICK_CNT(350ms)窗口之外,会被判定为两次独立短按,应用需容忍该边界。
  • I2C 无应答(NACK):soft_iic_tx_byte 返回 ACK 状态、read/write_buf 返回错误码,调用方(如 FM 驱动)应据此重试或上报芯片异常;delay 过小会导致总线速率超限、从机无法响应,io_pu 未开且外部无上拉时 SDA 无法拉高产生误读。
  • 多路 I2C 并发:soft_iic_dev 是 const int 索引,多路设备共用同一份驱动代码;若应用多任务同时访问不同索引,需保证 soft_iic_cfg[] 表项与访问互斥(通常由上层任务模型保证单线程访问)。
  • NOR Flash 写保护/擦写中断:擦写期间掉电会损坏数据,norfs_dev_ops 与文件系统层配合处理坏块与掉电恢复;spi_hw_num 限定 SPI1/SPI2,挂错 SPI 通道会导致设备无法枚举。
  • 扫描任务优先级:key_scan() 的周期抖动会影响按键计数的精确性(门槛值按 10ms 计算),若被高优先级任务长时间抢占,KEY_LONG_CNT 等阈值需按实际周期重新标定。

Performance & Operational Considerations

  • 扫描开销:按键扫描在 10ms 周期内完成所有使能类型的采集与状态机判定,IO/AD 采集为微秒级操作,整体 CPU 占用极低;未使能的类型不参与编译(KEY_XXX_EN=0),进一步减少 RAM/Flash 占用。
  • 软件 I2C 速率:delay 每增加一位周期的延时即降低总线速率;对速率敏感器件(如某些 FM 芯片)需在数据手册允许范围内调小 delay,但过小会引入时序违例,建议预留余量。
  • 低功耗配合:外设驱动提供 suspend/resume 钩子(如 soft_iic_suspend/resume),系统进入低功耗前必须调用,避免 GPIO 悬空漏电;恢复后需重新确认总线状态。
  • 调试手段:KEY_UART_DEBUG 可将按键扫描过程输出到串口(log_info),是排查"按键没反应/误触发"的第一手段。

Extension Points

  • 新增按键类型:实现 key_interface_t(提供 key_type、key_init、key_get_value),在 KEY_TYPE 枚举中增加类型值,并在 key.c 的扫描分发中加入该接口实例即可,无需改动应用层。
  • 新增事件形态:在 KEY_MAX_STATUS 前追加状态枚举值,并在状态机中补充迁移逻辑;同时评估 key_emit_t 回调语义是否需要扩展。
  • 替换 FM 芯片:新建芯片驱动目录,实现基于 fm_iic_dri 的寄存器操作,板级宏切换芯片型号即可,应用层 FM 接口不变。
  • 接入统一设备模型:仿照 norflash_dev_ops/norfs_dev_ops,实现 device_operations 并注册到 dev_mg,即可获得 dev_open/dev_read/dev_write/ioctl 与文件系统(vfs_format 等)的通用访问能力。
  • 多路软件 I2C:向 soft_iic_cfg[] 增加表项即可扩展第二/第三路 I2C 总线,驱动代码零改动。

Tests

SDK 中未发现针对通用外设驱动的独立单元测试工程(嵌入式板级驱动通常依赖整机联调验证)。可验证的行为包括:按键各状态事件序列(短按/长按/连按/双击)、I2C 读写回环(对芯片寄存器写后读回)、NOR Flash 格式化挂载后读写文件。联调时可借助 KEY_UART_DEBUG 串口日志与 soft_iic_tx_byte 的 ACK 返回值作为第一层断言。

Related Links

  • 按键驱动头文件 key.h
  • 按键实现 key.c
  • 软件 I2C 头文件 iic_soft.h
  • NOR Flash 驱动 norflash.h
  • BK1080 收音驱动 BK1080.h
  • 编码器驱动 encoder_api.c
  • 相关兄弟页面:音频解码器(decoder)、文件系统与 VFS(fs)、电源管理(power_manage)、USB 驱动(usb)、VM 参数管理(vm)
Prev
消息、定时器与中断服务