杰理 SDK 文档中心
首页
首页
  • 入门指南

    • SDK 概述与芯片平台
    • 环境搭建与开发工具链
    • 编译、烧录与快速开始
  • 应用层开发

    • 语音玩具应用 voice_toy
    • 扩音器应用 voice_enhanced
    • 语音功能状态机 voice_func
    • 应用公共框架与配置
  • 音频子系统

    • 音频解码器与 MIDI 播放
    • 音频编码与录音
    • 音效算法(ANS、变调、变声、混响)
    • 音频输出、功放与硬件重采样
  • 存储与文件系统

    • 文件系统层(FAT、NOR_FS、SYDF 等)
    • 存储设备与设备管理
    • 参数存储 VM 与保留区
  • 系统机制

    • 消息与事件机制
    • 电源管理与低功耗
    • 固件升级机制
    • 外设驱动(按键、红外、SPI、USB)
    • 实时时钟与定时器
  • 构建系统与工具

    • 构建系统(Makefile 与 Code::Blocks)
    • 编译后处理与语音资源打包
  • 硬件平台与文档

    • 芯片平台与启动流程
    • 硬件文档、规格书与原理图

外设驱动(按键、红外、SPI、USB)

本文档系统介绍 AD24N SDK 中 BSP 层的外设驱动能力,覆盖按键(IO/AD 按键、红外遥控按键)、SPI(LED 灯串)与 USB(设备端/主机端协议栈)四类外设的驱动框架、实现机制与配置方式。

Purpose and Scope

本页面向"外设驱动"这一子系统,说明 AD24N 语音 MCU 如何通过统一的驱动接口把物理外设事件转化为系统消息,并解释各驱动的内部实现:

  • 按键子系统:key.c 统一框架 + key_drv_io.c(IO 按键)、key_drv_ad.c(AD 按键)、key_ir.c(红外按键)等具体驱动;
  • SPI 外设:cpu/led_spi.c 中的 SPI 灯串(LED)驱动;
  • USB 协议栈:common/usb/ 下的设备端(CDC/音频/HID/大容量存储)与主机端(控制传输)实现。

不属于本页范围、由其他目录页覆盖的内容:音频解码/播放(decoder/)、电源与低功耗管理(config/lib_power_config.c)、消息系统与任务调度(msg.h/post_msg 仅作为按键事件的消费方被引用)。如需了解这些能力,请参见对应目录页。

Overview

AD24N 的 BSP(板级支持包)位于 sdk/app/bsp/,其中 common/ 存放与具体芯片引脚无关的通用外设逻辑,cpu/ 存放与 CPU 硬件寄存器相关的实现。外设驱动在整个软件栈中处于最底层,向上为应用层提供两类接口:

  1. 轮询型驱动(按键):应用主循环周期性调用 get_key_value(),驱动返回当前键值,框架再通过消息过滤器把键值翻译成 u16 系统消息并投递到消息队列。
  2. 中断/状态机驱动(红外、SPI、USB):依赖硬件定时器捕获(gptimer)、SPI 控制器或 USB PHY 中断驱动。

设计上,按键框架采用**驱动接口表(key_interface_t)**注册机制:每种按键类型导出一个静态接口描述符,框架在编译期用条件编译(KEY_IO_EN/KEY_AD_EN/KEY_IR_EN 等宏)把启用的驱动收集进 key_list 数组,从而做到"新增一种按键类型不改动框架代码"。红外驱动内部又按 SEL_IR_MODE 分成标准 NEC 协议解码与简易自解码两种模式。USB 则按 USB_DEVICE_CLASS_CONFIG 位掩码组合多个设备类(Speaker、MIC、HID、Mass Storage),属于多类复合设备设计。

Architecture

flowchart TD
    subgraph sg_App["应用层"]
        App["应用主循环 / 消息处理"]
        MsgFilter["key_msg_filter 消息过滤器"]
    end

    subgraph sg_Key["按键子系统 common/key/"]
        KeyFramework["key.c 框架<br/>key_init / get_key_value / key2msg_emit"]
        KeyIO["key_drv_io.c<br/>KEY_TYPE_IO"]
        KeyAD["key_drv_ad.c<br/>KEY_TYPE_AD"]
        KeyIR["key_ir.c<br/>KEY_TYPE_IR"]
        KeyMatrix["key_matrix.c<br/>KEY_TYPE_MATRIX"]
        KeyTouch["key_touch.c<br/>KEY_TYPE_TOUCH"]
    end

    subgraph sg_Spi["SPI 外设 cpu/"]
        LedSpi["led_spi.c<br/>HW_SPI1 灯串"]
    end

    subgraph sg_Usb["USB 协议栈 common/usb/"]
        UsbDevice["device/<br/>CDC / Audio / HID / MassStorage"]
        UsbHost["host/<br/>usb_ctrl_transfer"]
        UsbClassDef["usb_std_class_def.h<br/>USB_DEVICE_CLASS_CONFIG"]
    end

    subgraph sg_Hw["硬件层"]
        GPIO["GPIO / ADC 引脚"]
        Timer["GPTimer 捕获"]
        SpiCtrl["SPI 控制器"]
        UsbPhy["USB PHY"]
    end

    App --> MsgFilter
    MsgFilter --> KeyFramework
    KeyFramework -->|"条件编译收集"| KeyIO
    KeyFramework --> KeyAD
    KeyFramework --> KeyIR
    KeyFramework --> KeyMatrix
    KeyFramework --> KeyTouch
    KeyIO --> GPIO
    KeyAD --> GPIO
    KeyIR --> Timer
    App --> LedSpi
    LedSpi --> SpiCtrl
    App --> UsbDevice
    App --> UsbHost
    UsbDevice --> UsbPhy
    UsbClassDef --> UsbDevice

架构说明:按键框架是典型的"注册表 + 轮询"结构——key.c 在编译期把启用的驱动接口(key_interface_t)收集进 key_list,运行期 key_init() 逐个调用 key_init 钩子完成初始化,get_key_value() 按序轮询各驱动的 key_get_value() 钩子,命中即返回。SPI 与 USB 则与应用层直接耦合:led_spi.c 直接操作 SPI 控制器寄存器实现灯串时序,USB 协议栈以 USB_DEVICE_CLASS_CONFIG 位掩码为总开关组织设备类与主机类代码。

按键子系统

统一框架(key.c)

按键框架的核心是驱动接口描述符 key_interface_t 与编译期收集的驱动表。key_list 数组按条件编译宏依次收纳各按键驱动:

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
#if KEY_IR_EN
    &key_ir_info,
#endif
#if KEY_TOUCH_EN
    &key_touch_info,
#endif
};

Source: key.c

这种"接口表 + 条件编译"的设计意图是:新增按键类型(如触摸)只需要实现一个 key_interface_t 并加上对应 KEY_XXX_EN 宏,框架代码完全不需要改动,从而把驱动扩展成本降到最低。

初始化阶段,框架遍历 key_list 依次调用各驱动的初始化钩子:

void key_init(void)
{
    key_puts("key init\n");

#if (KEY_IO_EN || KEY_AD_EN || KEY_MATRIX_EN || KEY_IR_EN || KEY_TOUCH_EN)
    for (int i = 0; i < (sizeof(key_list) / sizeof(key_list[0])); i++) {
        if (key_list[i]->key_init) {
            key_list[i]->key_init();
        }
    }
#endif
}

Source: key.c

事件上报路径由 key2msg_emit() 完成:它先调用应用注册的消息过滤器 key_msg_filter(通过 key_table_sel() 注入),把 (key_status, key_num, key_type) 三元组翻译成 u16 消息;若消息有效(非 NO_MSG),先唤醒语音任务(d_key_voice_kick()),再调用 post_msg(1, msg) 投递到消息队列。该设计把"硬件键值"与"应用语义"解耦——同一个键值在不同产品上可以映射为不同消息。

static void key2msg_emit(u8 key_status, u8 key_num, u8 key_type)
{
    key_printf("key_status:%d key_num:%d key_type:%d \n", key_status, key_num, key_type);

    if (key_msg_filter) {
        u16 msg = key_msg_filter(key_status, key_num, key_type);
        if (msg != NO_MSG) {
            d_key_voice_kick();
            int ret = post_msg(1, msg);
            if (ret != 0) {
                key_printf("error !!! msg pool full \n");
            }
        }
    }
}

Source: key.c

轮询入口 get_key_value() 遍历 key_list,逐个调用 key_get_value();驱动返回 -1 表示该类型未启用/无事件,返回 NO_KEY 表示无按键,否则记录 key_num 与 key_type 并返回 true。注意代码中先调用一次 key_get_value() 判 -1,随后再次调用取真实键值,属于"探测 + 取值"两段式设计,避免在未启用驱动上做无谓处理。

IO 按键驱动(key_drv_io.c)

IO 按键直接读取 GPIO 电平,初始化即调用宏 KEY_INIT() 完成引脚配置:

static void io_key_init(void)
{
    KEY_INIT();
}

Source: key_drv_io.c

其接口描述符把自身注册为 KEY_TYPE_IO:

.key_type = KEY_TYPE_IO,
.key_init = io_key_init,
.key_get_value = get_iokey_value,

Source: key_drv_io.c

KEY_INIT() 与 get_iokey_value() 通常在 app_config.h 中由板级配置展开,按键引脚、有效电平(高/低有效)都通过配置宏定义,保证同一份驱动代码可复用于不同硬件。

AD 按键驱动(key_drv_ad.c)

AD 按键通过 ADC 采样电压区分多个按键(串联电阻分压),只需一根 ADC 引脚即可扩展多个键:

void ad_key_init(void)
{
    ...
}

Source: key_drv_ad.c

.key_type = KEY_TYPE_AD,
.key_init = ad_key_init,
.key_get_value = get_adkey_value,

Source: key_drv_ad.c

get_adkey_value() 内部按电压区间把 ADC 采样值映射为键号,不同按键对应不同分压电阻,因此映射表与硬件电阻网络强相关,属于典型的"用模拟量换引脚数"的取舍——牺牲 ADC 通道,节省 GPIO。

红外按键驱动(key_ir.c)

红外驱动支持两种模式,由 SEL_IR_MODE 宏选择:

  1. 标准 NEC 协议(STANDARD_IR):自动选择一个空闲 gptimer,配置为下降沿捕获(GPTIMER_MODE_CAPTURE_EDGE_FALL),irq_cb = NULL 表示直接使用内置 NEC 解码器(ir_decoder_init)。若用户自定义红外协议,可自行编写中断回调赋给 irq_cb:
#if (STANDARD_IR == SEL_IR_MODE)
/* 标准ENC协议红外按键驱动自动选择空闲timer使用 */
static struct gptimer_config if_dec_cfg;
#else
/*  简易红外按键驱动选择IR_KEY_TIMER使用 */
static const struct irflt_platform_data irflt_data = {
    .irflt_io       = IR_KEY_IO_SEL,
    .timer          = IR_KEY_TIMER,
};
#endif

Source: key_ir.c

static void ir_key_init(void)
{
#if (STANDARD_IR == SEL_IR_MODE)
    log_info("standard ir key init\n");
    memset((void *)&if_dec_cfg, 0, sizeof(struct gptimer_config));
    if_dec_cfg.mode = GPTIMER_MODE_CAPTURE_EDGE_FALL;
    if_dec_cfg.capture.port = (IR_KEY_IO_SEL / 16);
    if_dec_cfg.capture.pin = BIT(IR_KEY_IO_SEL % 16);
    if_dec_cfg.capture.filter = 0;
    if_dec_cfg.capture.max_period = 110 * 1000;
    if_dec_cfg.irq_cb = NULL;   //NULL即为采用标准NEC协议,若用户自定义红外协议,自行编写该中断即可
    if_dec_cfg.irq_priority = IRQ_IRTMR_IP,
               ir_decoder_init(&if_dec_cfg);
#else
    ...
#endif
}

Source: key_ir.c

  1. 简易模式:使用固定 IR_KEY_TIMER 与 IR_KEY_IO_SEL 引脚,驱动内部自行完成载波滤波与键值解析。

解码得到的键值通过查表 IRTabFF00[] 映射为系统按键号(IR_00、IR_01……):

static const u8 IRTabFF00[] = {
    NKEY_00, NKEY_01, NKEY_02, NKEY_03, NKEY_04, NKEY_05, NKEY_06, IR_06, IR_15, IR_08, NKEY_0A, NKEY_0B, IR_12, IR_11, NKEY_0E, NKEY_0F,
    ...
};

Source: key_ir.c

查表之后还有一级软件去抖 key_filter():只有连续 KEY_BASE_CNT 次采样值相同才认定键值稳定,否则保持旧值:

static u8 key_filter(u8 key)
{
    static u8 used_key = NO_KEY;
    static u8 old_key;
    static u8 key_counter;

    if (old_key != key) {
        key_counter = 0;
        old_key = key;
    } else {
        key_counter++;
        if (key_counter == KEY_BASE_CNT) {
            used_key = key;
        }
    }
    return used_key;
}

Source: key_ir.c

该去抖是"计数确认"型:键值变化立即重置计数,连续 N 次相同才更新输出,从而滤除红外信号抖动与边沿毛刺。IR 按键产生的消息顺序为"短按抬起/长按 → 连按",由框架注释明确约定。

SPI 外设驱动(led_spi.c)

led_spi.c 位于 cpu/ 目录,直接面向 CPU 的 SPI 控制器,用于驱动灯串(RGB LED 级联)。其核心是 led_spi_init(),把指定的 SPI 外设与数据输出引脚绑定,配置成适合灯串协议的时序:

void led_spi_init(hw_spi_dev spi, u32 spi_do)
{
    ...
}

Source: led_spi.c

典型调用把 SPI1 与 IO_PORTA_03 绑定为灯串数据线:

led_spi_init(HW_SPI1, IO_PORTA_03);

Source: led_spi.c

设计意图:灯串协议(如 SK6812/WS2812 类)要求纳秒级精确的"0/1 码"脉冲宽度,用普通 GPIO 翻转难以满足;这里复用 SPI 控制器以固定位速率输出编码后的数据流,把时序精确性交给硬件外设,CPU 只需按字节组织颜色数据。hw_spi_dev 枚举(HW_SPI1 等)与 IO_PORTA_03 引脚宏表明该驱动与具体芯片引脚绑定,属于 BSP 层最贴近硬件的实现。

USB 协议栈(common/usb/)

USB 代码按"设备端 / 主机端"分为两个目录:

  • usb/device/:设备端各类(CDC 虚拟串口、USB Audio Speaker/MIC、HID、Mass Storage);
  • usb/host/:主机端(控制传输、枚举等);
  • usb_std_class_def.h:设备类位掩码总开关。

设备类组合由 USB_DEVICE_CLASS_CONFIG 位掩码决定,注释中给出参考组合:

// #define     USB_DEVICE_CLASS_CONFIG (SPEAKER_CLASS|MIC_CLASS|HID_CLASS|MASSSTORAGE_CLASS)

Source: usb_std_class_def.h

该宏是编译期常量,usb_audio_interface.h 等文件据此裁剪代码:

#if USB_DEVICE_CLASS_CONFIG & MIC_CLASS
extern cbuffer_t cbuf_usb_mic_o;
#endif

Source: usb_audio_interface.h

设备端 CDC 类对外暴露回调注册接口,供应用层注入唤醒处理与数据输出处理:

u32 cdc_desc_config(const usb_dev usb_id, u8 *ptr, u32 *itf);
void cdc_set_wakeup_handler(void (*handle)(struct usb_device_t *usb_device));
void cdc_set_output_handle(void *priv, int (*output_handler)(void *priv, u8 *buf, u32 len));

Source: cdc.h

主机端负责设备枚举阶段的标准请求,例如获取设备描述符:

int usb_get_device_descriptor(struct usb_host_device *usb_dev, struct usb_device_descriptor *desc);
int usb_get_string_descriptor(struct usb_host_device *usb_dev, struct usb_device_descriptor *desc);
int set_address(struct usb_host_device *usb_dev, u8 devnum);

Source: usb_ctrl_transfer.h

设计要点:USB 属于事件/中断驱动型外设,与按键的轮询模型不同——设备端依赖 USB 中断(SOF、EP0 请求、端点完成)推进协议状态机;主机端则通过控制传输完成地址分配(set_address)与描述符读取(usb_get_device_descriptor),随后进入配置阶段。设备类按位掩码组合的方式,使一个 USB 口可以同时呈现为"声卡 + 虚拟串口 + 存储盘"的复合设备,适配 AD24N 的语音类应用(如 U 盘播放 + 声卡回采 + 调试串口)。

核心流程

按键事件端到端流程

以"用户按下 IO 按键 → 应用收到消息"为例,完整链路如下:

sequenceDiagram
    participant U as 用户
    participant IO as key_drv_io.c<br/>get_iokey_value
    participant F as key.c 框架<br/>get_key_value
    participant MF as key_msg_filter<br/>消息过滤器
    participant Q as 消息队列<br/>post_msg
    participant A as 应用消息处理

    U->>IO: 按下按键(GPIO 电平变化)
    loop 主循环轮询
        F->>IO: 调用 key_get_value()
        IO-->>F: 返回键值 key_num
        F->>F: 记录 key_type = KEY_TYPE_IO
        F-->>MF: key2msg_emit(status, num, type)
        MF-->>F: 返回翻译后的 u16 消息(NO_MSG 则丢弃)
        F->>Q: d_key_voice_kick() + post_msg(1, msg)
        Q-->>A: 应用循环取出消息并处理
    end

关键点:整个按键识别是轮询模型,由应用主循环驱动;key2msg_emit() 是硬件键值到系统消息的唯一转换点,消息过滤器为 NULL 或返回 NO_MSG 时事件被静默丢弃,这是"按键只做通知、不做业务"的分层设计。

红外按键中断流程

红外路径与 IO 按键不同:标准 NEC 模式下,gptimer 下降沿捕获中断驱动 ir_decoder_init 内置解码器完成 32 位 NEC 帧解析,解码出的键值经 IRTabFF00 查表、key_filter 去抖后,由 get_key_value 轮询入口取走并走相同的 key2msg_emit 上报路径。即:采集用中断,识别用轮询,两者在 key_ir_info 接口处汇合。

配置选项

配置宏类型默认/取值说明
KEY_IO_EN布尔由板级配置使能 IO 按键驱动,决定 key_io_info 是否进入 key_list
KEY_AD_EN布尔由板级配置使能 AD 分压按键驱动
KEY_MATRIX_EN布尔由板级配置使能矩阵键盘驱动
KEY_IR_EN布尔由板级配置使能红外按键驱动
KEY_TOUCH_EN布尔由板级配置使能触摸按键驱动
SEL_IR_MODE枚举STANDARD_IR 或简易模式选择红外解码方式(标准 NEC / 简易自解码)
IR_KEY_IO_SEL引脚宏板级定义红外接收引脚;标准模式用 port = IO/16, pin = BIT(IO%16) 换算
IR_KEY_TIMER定时器号板级定义简易模式使用的 gptimer
KEY_BASE_CNT数值板级定义红外去抖连续确认次数
IRQ_IRTMR_IP中断优先级板级定义红外捕获中断优先级
USB_DEVICE_CLASS_CONFIG位掩码SPEAKER_CLASS|MIC_CLASS|HID_CLASS|MASSSTORAGE_CLASS组合使能 USB 设备类
STANDARD_IR常量板级定义标准 NEC 模式标识
KEY_INIT() / get_iokey_value()宏/函数板级实现IO 按键引脚初始化与读取,由应用配置展开

配置均在 app_config.h(或对应板级配置头文件)中定义,框架通过 #if 预编译裁剪代码,因此未使能的驱动不占用任何代码与 RAM 空间。

API 参考

void key_init(void)

初始化所有已使能的按键驱动。遍历 key_list 并调用各驱动的 key_init 钩子。应在系统上电后、主循环轮询前调用一次。

  • 参数:无
  • 返回:无

void key_table_sel(void *msg_filter)

注册按键消息过滤器。在关中断(local_irq_disable/enable)保护下写入 key_msg_filter,保证与轮询路径并发安全。

  • 参数:msg_filter(u16 (*)(u8, u8, u8))— 过滤器回调,返回翻译后的消息或 NO_MSG
  • 返回:无

bool get_key_value(key_io_t *p_key)

轮询各按键驱动,取回当前有效键值。驱动返回 -1 表示该类型未启用,NO_KEY 表示无按键;命中时填充 p_key->key_num 与 p_key->key_type 并返回 true。

  • 参数:p_key(key_io_t *)— 输出结构,含 key_num、key_type
  • 返回:true 表示取到有效按键,false 表示无事件

void led_spi_init(hw_spi_dev spi, u32 spi_do)

初始化 SPI 灯串外设,绑定 SPI 控制器与数据输出引脚。

  • 参数:spi(hw_spi_dev)— SPI 外设枚举,如 HW_SPI1;spi_do(u32)— 输出引脚宏,如 IO_PORTA_03
  • 返回:无

void cdc_set_wakeup_handler(void (*handle)(struct usb_device_t *usb_device))

注册 USB CDC 设备唤醒回调(例如 USB 挂起后的远程唤醒)。

  • 参数:handle — 唤醒回调函数指针
  • 返回:无

int usb_get_device_descriptor(struct usb_host_device *usb_dev, struct usb_device_descriptor *desc)

主机端枚举阶段获取设备描述符。

  • 参数:usb_dev — 主机设备句柄;desc — 输出描述符缓冲区
  • 返回:0 成功,非零失败

故障模式、边界情况与并发

消息队列满

key2msg_emit() 中 post_msg() 返回非 0 时打印 error !!! msg pool full。这是按键连发风暴下最典型的故障:应用处理不及时导致消息积压。由于按键事件本身是"有损可接受"的(用户重按即可),框架选择直接丢弃并打印日志,而不是阻塞或重试,属于典型的嵌入式"尽力而为"策略。

轮询路径的并发安全

key_table_sel() 通过 local_irq_disable()/local_irq_enable() 保护 key_msg_filter 的写入,与主循环中 key2msg_emit() 的读取形成互斥——因为在单核 MCU 上,唯一可能打断轮询的异步源就是中断,关中断即可保证指针读写的原子性。

红外去抖与连按

key_filter() 使用静态变量保存历史状态,连续 KEY_BASE_CNT 次相同才更新输出。边界情况:若红外信号在阈值附近抖动(偶尔丢一帧),计数会不断复位,表现为按键偶尔不触发;若 KEY_BASE_CNT 过小,则可能把毛刺误判为按键。这是灵敏度与抗干扰之间的经典权衡。

标准/简易红外模式差异

SEL_IR_MODE 切换时,初始化代码路径完全不同(gptimer 捕获 vs 固定 timer 轮询),两种模式不能混用。标准模式 irq_cb = NULL 时使用内置 NEC 解码器,一旦用户自定义协议却忘记实现回调,ir_decoder_init 将按 NEC 解析出错误键值——自定义协议必须自行填充 irq_cb。

AD 按键电压重叠

AD 按键依赖电阻分压,若相邻键分压间隔小于 ADC 噪声幅度,get_adkey_value() 的电压区间映射可能误判为相邻键。该问题由硬件电阻网络决定,软件侧只能通过增大采样稳定时间缓解。

性能与运维考量

  • 轮询开销:get_key_value() 每次遍历最多 5 个驱动并各自采样,应用主循环频率直接决定按键响应延迟。IO/AD 按键适合低频轮询(几 ms 级),红外采集则完全由 gptimer 中断承担,不占用轮询时间。
  • 红外中断实时性:max_period = 110 * 1000(µs)限制捕获最大周期,IRQ_IRTMR_IP 决定中断优先级;NEC 协议 562.5µs 载波周期要求中断响应必须足够快,否则丢帧。
  • SPI 灯串:数据经 SPI 控制器移位输出,CPU 只负责填充颜色缓冲区,刷新率由应用决定;注意 HW_SPI1 与 IO_PORTA_03 的引脚复用是否与其他外设冲突。
  • USB 复合设备:USB_DEVICE_CLASS_CONFIG 每增加一个类,描述符与端点资源随之增加;启用前需确认芯片 USB 端点数量与 RAM 缓冲足够。

扩展点

  1. 新增按键类型:实现一个 key_interface_t(key_type/key_init/key_get_value 三件套),在 key.c 的 key_list 中加入对应条件编译项即可,框架零改动。
  2. 自定义红外协议:标准模式下将 if_dec_cfg.irq_cb 从 NULL 改为自写中断回调,即可替换内置 NEC 解码器。
  3. 按键消息重映射:通过 key_table_sel() 注入自定义 key_msg_filter,把 (status, num, type) 翻译为任意系统消息,实现同一硬件键值在不同产品上的差异化行为。
  4. USB 设备类裁剪:修改 USB_DEVICE_CLASS_CONFIG 位掩码组合类,或参照 cdc.h 的回调注入模式为其他类增加应用层钩子。

测试

测试用例未在 sdk/app/bsp/ 目录中发现独立的单元测试文件;按键、红外与 USB 的验证主要依赖板级 DEMO 与日志(key_printf/log_info)。key_ir.c 的 key_filter 去抖逻辑、IRTabFF00 查表等纯函数部分适合后续补充单元测试。

Related Links

  • 按键框架 key.c
  • IO 按键驱动 key_drv_io.c
  • AD 按键驱动 key_drv_ad.c
  • 红外按键驱动 key_ir.c
  • SPI 灯串驱动 led_spi.c
  • USB 设备类定义 usb_std_class_def.h
  • USB CDC 设备接口 cdc.h
  • USB 主机控制传输 usb_ctrl_transfer.h
  • 相关目录页:解码器与播放链路(sdk/app/bsp/common/decoder/)、电源配置(sdk/app/bsp/common/config/lib_power_config.c)
Prev
固件升级机制
Next
实时时钟与定时器