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

    • SDK 概览与产品定位
    • 支持芯片平台与蓝牙认证
    • SDK 架构与目录分层
  • 快速开始

    • 环境搭建与编译工具链
    • 编译构建系统
    • 板级工程与配置
    • 烧录与固件升级工具
  • 应用工程

    • 应用选择与工程总览
    • SPP + BLE 数传应用框架
    • 透传与 AT 指令示例
    • BLE 广播/中心与定位示例
    • 2.4G 私有协议与 Dongle 示例
    • 云平台接入示例
    • HID 人机交互应用框架
    • HID 示例工程(键盘/鼠标/遥控器/手柄)
    • Bluetooth Mesh 应用框架
    • Mesh 模型与 Mesh DFU 固件升级
    • Mesh 音频编解码演示
  • 芯片平台与硬件抽象

    • 芯片平台总览与差异
    • 音频编解码与时钟管理
    • 外设驱动接口(ADC/IIC/SPI/PWM/LED/充电)
    • 芯片配置工具与下载支持
  • 蓝牙协议栈

    • 蓝牙控制器层(btctrler)
    • 蓝牙协议栈与 Profile(btstack)
    • 蓝牙模块选择与配置
  • 媒体与音频框架

    • 音频流框架
    • 音频编解码与 A2DP 媒体
    • 音频效果处理(EQ/频谱/变调/环绕/超低音)
    • 本地 TWS 与音频同步
  • 系统服务与运行时

    • 实时操作系统与任务调度
    • 消息事件机制
    • 电源管理与低功耗
    • 存储与配置系统
    • 设备驱动框架(USB/RTC)
  • 应用公共组件

    • 音频应用组件
    • 设备外设抽象(按键/触摸/传感器/存储)
    • 蓝牙公共模块与消息联动
    • 调试与配置组件
    • 杰理关键词唤醒(jl_kws)
  • 第三方协议与云平台接入

    • 杰理 RCSP 私有协议
    • 低功耗蓝牙 Mesh 方案(llsync_mesh)
    • Sig Mesh 方案
    • 涂鸦协议接入
    • 腾讯连连接入
    • 华为 HiLink 接入
  • 固件升级与维护

    • OTA 升级机制
    • 升级补丁与版本维护
    • 升级工具链(BLE OTA / USB Dongle OTA)
  • 文档与开发资源

    • 数据手册与架构文档
    • 协议与云平台开发文档
    • 常见问题与技术支持

设备驱动框架(USB/RTC)

AC63 系列蓝牙 SoC 的 USB(主机/从机)与 RTC(含虚拟 RTC)设备驱动框架:涵盖 USB 协议栈的配置、DMA 缓冲管理、中断注册,以及 RTC 的时间读写、闹钟与低功耗唤醒机制。

Purpose and Scope

本文档系统性地介绍 AC63_BT_SDK 中的设备驱动框架,聚焦两大子系统:

  • USB 驱动框架:位于 apps/common/device/usb/ 的 USB 从机(Device)与主机(Host)协议栈封装,包括 usb_config.c/h 配置层、端点 DMA 缓冲管理、中断注册接口,以及 U 盘(MSD)、HID、CDC、Audio 等标准设备类。
  • RTC 驱动框架:包括各 CPU 的硬件 RTC 驱动头文件(include_lib/driver/cpu/*/asm/rtc.h)与面向应用的虚拟 RTC 接口(include_lib/driver/device/rtc/virtual_rtc.h),以及基于 VM 存储的系统时间/闹钟读写与低功耗唤醒。

以下内容不在本文档范围内,属于相邻目录页的主题:蓝牙协议栈(GATT/SPP)、音频编解码驱动(cpu/*/audio_dec/)、电源管理(asm/power/)与具体应用示例(如键盘 HID 应用)。对于具体的外设应用开发,可参考对应应用页。

Overview

USB 驱动框架的定位

USB 是 AC63 系列芯片的重要外设接口,既可作**从机(Device)被 PC/手机枚举为 U 盘、HID 键盘、声卡、CDC 串口等,也可作主机(Host)**连接 U 盘、键鼠等外部设备。SDK 将 USB 硬件控制器(asm/usb.h)、传输协议(控制/批量传输)、标准设备类(SCSI、HID、CDC)与上层应用之间用 usb_config.c/h 这一配置层解耦:

  • 应用只需调用 usb_config(usb_id) / usb_host_config(usb_id) 即可完成初始化;
  • 端点缓冲通过统一的 lbuf(轻量内存池)管理,避免各设备类各自分配导致的内存碎片;
  • 中断服务函数(ISR)以表驱动方式注册,支持按 CPU 与优先级挂接。

RTC 驱动框架的定位

RTC(实时时钟)负责系统时间的维护。由于部分低功耗场景下硬件 RTC 需要掉电保持,SDK 提供了虚拟 RTC(Virtual RTC):把系统时间与闹钟时间持久化到 VM(虚拟机参数存储)区域,并用低功耗定时器(LP Timer)模拟 RTC 的走时与闹钟唤醒。这使得 sys_time 结构(年/月/日/时/分/秒)成为上层(闹钟、定时提醒、日志时间戳)唯一的数据契约,而底层是硬件 RTC 还是模拟实现,对应用透明。

关键概念

概念说明
usb_dev usb_idUSB 硬件实例编号,多 USB 控制器芯片按 0/1 区分
usb_ep_addr_t端点地址表,管理端点号与方向
lbuf轻量环形缓冲内存池,USB DMA 缓冲的分配器
struct sys_time统一的日历时间结构(year/month/day/hour/min/sec)
VM 存储掉电保存的参数区,虚拟 RTC 用它持久化时间与闹钟
LP Timer低功耗定时器,虚拟 RTC 的"心跳",支持闹钟唤醒

Architecture

USB 驱动框架架构

flowchart TD
    subgraph sg_App["应用层 (apps)"]
        AppHID["usb_hid_devices.c (HID 设备)"]
        AppStorage["usb_storage.c (U盘/存储)"]
    end

    subgraph sg_Cfg["配置层 (apps/common/device/usb)"]
        CfgC["usb_config.c / usb_config.h"]
        HostCfg["usb_host_config.c"]
    end

    subgraph sg_Dev["从机协议栈"]
        DevStack["usb/device/usb_stack.h"]
        DevC["usb_device.c"]
        StdClass["usb_std_class_def.h / usb_common_def.h"]
    end

    subgraph sg_Host["主机协议栈"]
        HostMain["usb_host.c"]
        CtrlXfer["usb_ctrl_transfer.c (控制传输)"]
        BulkXfer["usb_bulk_transfer.c (批量传输)"]
    end

    subgraph sg_HW["硬件层"]
        AsmUsb["asm/usb.h (USB 控制器)"]
        DmaBuf["usb_dma_buf (DMA 缓冲池)"]
        IsrTable["usb_interrupt_tx/rx (ISR 表)"]
    end

    AppHID --> CfgC
    AppStorage --> CfgC
    CfgC --> DevC
    CfgC --> HostMain
    DevC --> DevStack
    DevC --> StdClass
    HostMain --> CtrlXfer
    HostMain --> BulkXfer
    DevC --> AsmUsb
    CtrlXfer --> AsmUsb
    BulkXfer --> AsmUsb
    DevC --> DmaBuf
    HostMain --> DmaBuf
    DevC --> IsrTable
    HostMain --> IsrTable

架构分层自下而上说明:

  • 硬件层:asm/usb.h 提供寄存器级控制器访问;DMA 缓冲池 usb_dma_buf 与 ISR 表(usb_interrupt_tx[USB_MAX_HW_NUM][MAX_EP_TX]、usb_interrupt_rx[USB_MAX_HW_NUM][MAX_EP_RX])为协议栈提供数据通路与中断分发。
  • 协议栈层:从机侧 usb_device.c 配合 usb_stack.h 处理枚举与各类请求;主机侧 usb_host.c 负责枚举与设备管理,usb_ctrl_transfer.c / usb_bulk_transfer.c 分别实现控制传输与批量传输。
  • 配置层:usb_config.c/h 是上下两层的"胶水",负责分配端点缓冲、注册中断、按编译宏(TCFG_USB_SLAVE_MSD_ENABLE 等)裁剪设备类能力。
  • 应用层:如键盘 HID 设备示例 usb_hid_devices.c,只需调用配置层 API 即可获得枚举好的 USB 通道。

RTC 驱动框架架构

flowchart TD
    subgraph sg_App["应用层"]
        RtcAlarm["rtc_alarm.c (闹钟/定时模块)"]
        VirDemo["virtual_rtc_demo.c (演示代码)"]
    end

    subgraph sg_Vir["虚拟 RTC 层 (include_lib/driver/device/rtc)"]
        VirH["virtual_rtc.h (对外接口)"]
        VirSim["vir_rtc_simulate (时间模拟)"]
        VmIds["VM 分区 (rtc_vm_id / alm_vm_id / sec_vm_id)"]
    end

    subgraph sg_HwRTC["硬件与定时器"]
        AsmRtc["cpu/*/asm/rtc.h (硬件 RTC)"]
        LpTimer["LP Timer (低功耗定时器)"]
        P33["asm/power/p33.h (电源域)"]
    end

    RtcAlarm --> VirH
    VirDemo --> VirH
    VirH --> VirSim
    VirSim --> VmIds
    VirSim --> AsmRtc
    VirSim --> LpTimer
    VirSim --> P33

RTC 架构的关键设计意图:让"时间"成为可插拔的服务。virtual_rtc.h 定义统一的读写接口(vir_read_sys_time / vir_write_sys_time、vir_read_alarm / vir_write_alarm),底层既可由真实硬件 RTC 驱动,也可由"LP Timer + VM 存储"模拟(支持 AC695/AC635 等平台)。闹钟模块 rtc_alarm.c 与上层应用只依赖接口,不关心实现细节。

USB 驱动框架的实现细节

DMA 缓冲池的统一管理

USB 传输要求端点缓冲满足 DMA 对齐要求(8 字节对齐)。usb_config.c 将所有设备类的缓冲需求静态汇总为一个全局数组,再交给 lbuf 内存池动态分配,这是该框架最核心的设计决策:

#define     MAX_EP_TX   5
#define     MAX_EP_RX   5

static usb_interrupt usb_interrupt_tx[USB_MAX_HW_NUM][MAX_EP_TX];// SEC(.usb_g_bss);
static usb_interrupt usb_interrupt_rx[USB_MAX_HW_NUM][MAX_EP_RX];// SEC(.usb_h_bss);

static u8 ep0_dma_buffer[EP0_SETUP_LEN]     __attribute__((aligned(4))) SEC(.usb_ep0)    ;

#if TCFG_USB_SLAVE_MSD_ENABLE
#define     MSD_DMA_SIZE (64*2)
#else
#define     MSD_DMA_SIZE 0
#endif

#if TCFG_USB_SLAVE_HID_ENABLE
#define     HID_DMA_SIZE    64
#if CONFIG_APP_DONGLE
#define     HID_DMA_SIZE    64*CONFIG_BT_GATT_CLIENT_NUM
#endif
#else
#define     HID_DMA_SIZE    0
#endif

#if TCFG_USB_CUSTOM_HID_ENABLE
#define     CUSTOM_HID_DMA_SIZE    64 * 2
#else
#define     CUSTOM_HID_DMA_SIZE    0
#endif

#if TCFG_USB_SLAVE_AUDIO_ENABLE
#define     AUDIO_DMA_SIZE  256+192
#else
#define     AUDIO_DMA_SIZE  0
#endif

Source: usb_config.c

设计意图分析:

  1. 编译期裁剪:每个设备类(MSD/HID/CustomHID/Audio/CDC)都对应一个 TCFG_USB_xxx_ENABLE 编译宏。未使能的类其 DMA 大小被置 0,从而零运行时开销地控制内存占用——这在 RAM 紧张的蓝牙 SoC 上至关重要。
  2. 分段放置:SEC(.usb_ep0)、SEC(.usb_msd_dma)、SEC(.usb_g_bss)、SEC(.usb_h_bss) 等 section 属性把关键缓冲放到指定的内存段,便于链接器统一管理并满足 USB 控制器对物理地址的要求。
  3. 对齐保证:usb_dma_buf 以 aligned(8) 声明,lbuf 以 USB_DMA_BUF_ALIGN(8) 初始化,确保从池中切出的每一块缓冲都满足 DMA 对齐。

缓冲池的初始化与分配:

#define USB_DMA_BUF_ALIGN	(8)
#ifndef USB_DMA_BUF_MAX_SIZE
#define USB_DMA_BUF_MAX_SIZE (HID_DMA_SIZE +USB_DMA_BUF_ALIGN+ AUDIO_DMA_SIZE +USB_DMA_BUF_ALIGN+ MSD_DMA_SIZE*2 + USB_DMA_BUF_ALIGN+CDC_DMA_SIZE + USB_DMA_BUF_ALIGN + CUSTOM_HID_DMA_SIZE + USB_DMA_BUF_ALIGN+ 100)
#endif//USB_DMA_BUF_MAX_SIZE

static u8 usb_dma_buf[USB_DMA_BUF_MAX_SIZE] SEC(.usb_msd_dma) __attribute__((aligned(8)));
struct lbuff_head *usb_dma_lbuf = NULL;
void usb_memory_init()
{
    usb_dma_lbuf = lbuf_init(usb_dma_buf, sizeof(usb_dma_buf), USB_DMA_BUF_ALIGN, 0);
    log_info("%s() total dma size %x @%x", __func__, sizeof(usb_dma_buf), usb_dma_buf);
}

__attribute__((always_inline_when_const_args))
void *usb_alloc_ep_dmabuffer(const usb_dev usb_id, u32 ep, u32 dma_size)
{
    u8 *ep_buffer = NULL;
    ...
}

Sources:

  • usb_config.c
  • usb_config.h

usb_memory_init() 在系统启动早期被调用,把静态数组注册为 lbuf 池;此后各协议栈组件通过 usb_alloc_ep_dmabuffer() 按需申请端点缓冲。USB_DMA_BUF_MAX_SIZE 的求和公式里每个类之间插入 USB_DMA_BUF_ALIGN,是对 lbuf 按块切分时的对齐冗余的预计算,保证总容量足够。

中断注册机制

USB 协议栈与主控的中断系统通过表驱动方式衔接,usb_config.h 暴露了三类中断注册接口:

void usb_h_isr_reg(const usb_dev usb_id, u8 priority, u8 cpu_id);   // 主机模式中断
void usb_g_isr_reg(const usb_dev usb_id, u8 priority, u8 cpu_id);   // 从机模式中断
void usb_sof_isr_reg(const usb_dev usb_id, u8 priority, u8 cpu_id); // SOF 帧起始中断

Source: usb_config.h

参数语义:usb_id 选择硬件实例;priority 设定中断优先级;cpu_id 在多核(如 BR 系列双核)场景下指定中断挂载到哪个核,避免中断与主任务在不同核间频繁竞争。ISR 处理函数存放在 usb_interrupt_tx/rx 表中,按 [USB_MAX_HW_NUM][MAX_EP_TX/RX] 二维索引(每个控制器最多 5 个 TX / 5 个 RX 端点),协议栈把端点事件分发表项后,硬件中断到来时即可 O(1) 定位到对应的处理函数。

USB 配置层对外 API 总览

usb_config.h 是 USB 子系统对外的"门面",核心接口如下:

接口作用关键参数
u32 usb_config(const usb_dev usb_id)从机模式初始化(枚举就绪)返回 0 表示成功
usb_host_config(usb_dev usb_id)主机模式初始化无返回
usb_host_free(usb_dev usb_id)释放主机模式资源无返回
usb_h_get_ep_buffer(usb_dev usb_id, u32 ep)获取主机端点缓冲地址返回 void*
usb_alloc_ep_dmabuffer(usb_dev usb_id, u32 ep, u32 dma_size)从 DMA 池分配端点缓冲返回 void*
usb_h_isr_reg / usb_g_isr_reg / usb_sof_isr_reg注册主机/从机/SOF 中断priority、cpu_id

Source: usb_config.h

该 API 设计刻意保持"薄":初始化与释放、缓冲与中断四个维度即可覆盖从 U 盘、键盘 HID 到 CDC 串口的所有常用场景,上层无需感知 SCSI 命令、描述符等协议细节。

主机协议栈与存储设备

主机侧文件结构(apps/common/device/usb/host/):

  • usb_host.c:主机控制器管理、设备枚举、地址分配;
  • usb_ctrl_transfer.c/h:控制传输(Setup 阶段 + 数据阶段 + 状态阶段);
  • usb_bulk_transfer.c/h:批量传输(U 盘读写的主要通路);
  • usb_storage.c/h:SCSI/BOT 协议封装,把批量传输组织为 U 盘读写命令;
  • usb_hid_keys.h:HID 键值码表,供主机解析键盘/鼠标上报。

从机侧 usb_device.c 与 usb_std_class_def.h(标准设备类定义)、usb_common_def.h(公共描述符/常量)配合,完成设备枚举与类请求分发。usb_host_config.c 则提供主机模式的板级配置入口。

RTC 驱动框架的实现细节

虚拟 RTC 对外接口

include_lib/driver/device/rtc/virtual_rtc.h 定义了完整的虚拟 RTC API:

void vir_rtc_simulate_dump();
void get_lp_timer1_status(void);
int vir_rtc_simulate_init(const struct dev_node *node, void *arg);

void vir_write_alarm(struct sys_time *alarm_time);
void vir_read_alarm(struct sys_time *alarm_time);

void vir_write_sys_time(struct sys_time *curr_time);
void vir_read_sys_time(struct sys_time *curr_time);

void vir_alarm_enable(u8 set_alarm);
u8 vir_get_alarm_enable(void);

void vir_set_vm_id(u8 rtc_vm_id, u8 alm_vm_id, u8 sec_vm_id);

Source: virtual_rtc.h

接口设计要点:

  • 统一时间契约:所有时间参数均为 struct sys_time(来自 system/sys_time.h),应用层无需区分"硬件时间"与"模拟时间"。
  • 初始化即设备注册:vir_rtc_simulate_init(const struct dev_node *node, void *arg) 采用设备节点(dev_node)签名,与 SDK 的设备模型(driver 框架)兼容——虚拟 RTC 被注册为一个标准设备,可被 dev_open 等机制调用。
  • VM 分区解耦:vir_set_vm_id(rtc_vm_id, alm_vm_id, sec_vm_id) 允许调用方把时间、闹钟、秒计数分别落到不同的 VM ID,便于按需控制掉电保存的粒度与频率(秒计数分区通常用于高频写入而不过度磨损 VM)。
  • 闹钟使能状态可查询:vir_get_alarm_enable() 配合 vir_alarm_enable(u8) 支持运行时开关闹钟,供上层在低功耗策略中判断是否需要维持唤醒源。

演示代码:完整的虚拟 RTC 使用流程

apps/spp_and_le/modules/virtual_rtc_demo.c 展示了从初始化到闹钟读写的完整调用序列(该文件整体被 #if 0 关闭,作为可参考的示例代码):

void virtual_rtc_test(void)
{
    sys_timer_add(NULL, vir_rtc_test, 10);
    vir_rtc_simulate_init(NULL, NULL);
    vir_read_sys_time(&read_sys_time_test);
    printf("vir_rtc_read_sys_time>>>>>>>>>>>>>>>>>>>>>>\n");
    printf("vir_rtc_sys_time: %d-%d-%d %d:%d:%d\n",
           read_sys_time_test.year,
           read_sys_time_test.month,
           read_sys_time_test.day,
           read_sys_time_test.hour,
           read_sys_time_test.min,
           read_sys_time_test.sec);

    vir_alarm_enable(1);
    write_alarm_test.year  = 2020;
    write_alarm_test.month = 2;
    write_alarm_test.day   = 28;
    write_alarm_test.hour  = 23;
    write_alarm_test.min   = 59;
    write_alarm_test.sec   = 50;

    vir_write_alarm(&write_alarm_test);
    vir_read_alarm(&read_alarm_test);
    printf("vir_rtc_alarm_time: %d-%d-%d %d:%d:%d\n",
           read_alarm_test.year,
           read_alarm_test.month,
           read_alarm_test.day,
           read_alarm_test.hour,
           read_alarm_test.min,
           read_alarm_test.sec);
}

Source: virtual_rtc_demo.c

同文件中的 alm_wakeup_isr() 是闹钟触发时的中断回调示例,set_rtc_default_time() 给出出厂默认时间的填充方式,vir_rtc_test() 则通过 sys_timer_add(NULL, vir_rtc_test, 10) 每 10ms 周期轮询并打印系统时间,同时检测按键进入软关机(power_set_soft_poweroff()),演示了"虚拟 RTC 走时 + 低功耗入口"的典型组合。

硬件 RTC 与闹钟模块

  • 硬件驱动:include_lib/driver/cpu/{bd19,bd29,br23,br25,br30,br34}/asm/rtc.h 为各芯片平台的 RTC 硬件寄存器与底层读写函数,虚拟 RTC 在支持硬件 RTC 的平台可直接复用其走时与闹钟比较能力。
  • 闹钟模块:apps/{hid,spp_and_le,mesh}/modules/rtc_alarm.c(及对应 include/rtc_alarm.h)是面向应用的闹钟服务,封装了"设置闹钟 → 等待触发 → 唤醒/提示"的完整状态机,不同 APP(HID、SPP+LE、Mesh)各自维护一份副本,说明闹钟服务与应用场景(如蓝牙闹钟提醒)强相关。

核心流程

USB DMA 缓冲分配流程

flowchart TD
    Start["系统启动 (board_init)"] --> MemInit["usb_memory_init()"]
    MemInit --> Lbuf["lbuf_init(usb_dma_buf, USB_DMA_BUF_MAX_SIZE, 8, 0)"]
    Lbuf --> Mode{"USB 模式?"}
    Mode -->|"从机 usb_config(id)"| DevInit["设备枚举, 加载设备类 (MSD/HID/CDC...)"]
    Mode -->|"主机 usb_host_config(id)"| HostInit["主机枚举外部设备"]
    DevInit --> Alloc["usb_alloc_ep_dmabuffer(id, ep, size)"]
    HostInit --> Alloc
    Alloc --> Ok{"lbuf 分配成功?"}
    Ok -->|"是"| Ret["返回 8 字节对齐 DMA 缓冲"]
    Ok -->|"否"| Null["返回 NULL, 上层降级/报错"]

虚拟 RTC 闹钟时序

sequenceDiagram
    participant App as 应用 (virtual_rtc_test)
    participant Vir as virtual_rtc.h 接口
    participant Sim as vir_rtc_simulate
    participant VM as VM 存储分区
    participant Timer as LP Timer

    App->>Vir: vir_rtc_simulate_init(NULL, NULL)
    Vir->>Sim: 初始化模拟器(注册设备节点)
    App->>Vir: vir_write_sys_time(&t)
    Vir->>Sim: 写入系统时间
    Sim->>VM: 持久化到 rtc_vm_id
    App->>Vir: vir_write_alarm(&t)
    Vir->>Sim: 写入闹钟时间
    Sim->>VM: 持久化到 alm_vm_id
    App->>Vir: vir_alarm_enable(1)
    Vir->>Sim: 使能闹钟
    Sim->>Timer: 按闹钟时间设置 LP Timer 唤醒点
    Timer-->>Sim: 时间到, 低功耗唤醒中断
    Sim-->>App: 触发 alm_wakeup_isr 回调

时序说明:初始化必须在任何读写之前完成(vir_rtc_simulate_init);时间与闹钟写入后立即落入 VM,保证掉电不丢;闹钟使能后由 LP Timer 承担"未来某一时刻唤醒芯片"的职责——这正是虚拟 RTC 能在无外部 32K 晶振场景下实现 RTC 功能的关键。周期性的 vir_rtc_test(每 10ms)则利用 vir_read_sys_time 驱动软件走时累计。

配置选项

USB 配置宏(在 app_config.h / 板级配置中定义)

配置宏类型默认值说明
TCFG_USB_SLAVE_MSD_ENABLEbool0使能从机 U 盘(Mass Storage),DMA 缓冲 64×2 字节
TCFG_USB_SLAVE_HID_ENABLEbool0使能从机 HID 设备,DMA 缓冲 64 字节
TCFG_USB_CUSTOM_HID_ENABLEbool0使能自定义 HID,DMA 缓冲 64×2 字节
TCFG_USB_SLAVE_AUDIO_ENABLEbool0使能 USB 声卡类,DMA 缓冲 256+192 字节
TCFG_USB_SLAVE_CDC_ENABLEbool0使能 CDC 虚拟串口,DMA 缓冲 64×2(或 64×3)字节
CONFIG_APP_DONGLEbool0Dongle 固件:HID DMA 放大为 64×GATT 客户端数
USB_MALLOC_ENABLEbool0置 1 时 USB 配置变量走堆分配,否则静态段
TCFG_USE_VIRTUAL_RTCbool0使用虚拟 RTC 模拟(示例中被 #if 0 关闭,仅参考)

虚拟 RTC 运行期配置

配置项类型说明
rtc_vm_idu8系统时间持久化的 VM 分区 ID
alm_vm_idu8闹钟时间持久化的 VM 分区 ID
sec_vm_idu8秒计数持久化的 VM 分区 ID
闹钟使能u8通过 vir_alarm_enable(1/0) 开关

API Reference

USB 配置层

u32 usb_config(const usb_dev usb_id)

USB 从机模式初始化。启动枚举、注册设备类。

  • 参数:usb_id — USB 硬件实例编号
  • 返回:0 表示成功,非 0 表示失败
  • 注意:需在 usb_memory_init() 之后调用,且端点缓冲由 usb_alloc_ep_dmabuffer() 预先分配

void usb_host_config(const usb_dev usb_id)

USB 主机模式初始化,开始枚举总线上的外部设备。

void usb_host_free(const usb_dev usb_id)

释放主机模式占用的资源,退出主机角色。

void *usb_alloc_ep_dmabuffer(const usb_dev usb_id, u32 ep, u32 dma_size)

从全局 DMA 缓冲池(lbuf)为指定端点分配缓冲。

  • 参数:usb_id 硬件实例;ep 端点号;dma_size 期望长度
  • 返回:8 字节对齐的缓冲地址;池耗尽时返回 NULL

void *usb_h_get_ep_buffer(const usb_dev usb_id, u32 ep)

查询主机模式指定端点当前绑定的缓冲地址。

void usb_h_isr_reg / usb_g_isr_reg / usb_sof_isr_reg(const usb_dev usb_id, u8 priority, u8 cpu_id)

分别注册主机、从机、SOF 中断处理。

  • 参数:priority 中断优先级;cpu_id 目标核编号(多核场景)

虚拟 RTC 层

int vir_rtc_simulate_init(const struct dev_node *node, void *arg)

初始化虚拟 RTC 模拟器。以设备节点形式注册,兼容 SDK 设备模型。

  • 参数:node 设备节点(可传 NULL);arg 附加参数
  • 返回:0 成功,非 0 失败
  • 前置条件:必须先于任何读写接口调用

void vir_write_sys_time(struct sys_time *curr_time)

写入系统时间并持久化到 rtc_vm_id。

void vir_read_sys_time(struct sys_time *curr_time)

读取当前系统时间;周期调用可驱动软件走时累计。

void vir_write_alarm(struct sys_time *alarm_time)

写入闹钟时间并持久化到 alm_vm_id。

void vir_read_alarm(struct sys_time *alarm_time)

读取已设置的闹钟时间。

void vir_alarm_enable(u8 set_alarm)

使能/关闭闹钟。使能后由 LP Timer 设置唤醒点。

u8 vir_get_alarm_enable(void)

查询闹钟使能状态(1 为使能)。

void vir_set_vm_id(u8 rtc_vm_id, u8 alm_vm_id, u8 sec_vm_id)

设置时间/闹钟/秒计数三个 VM 分区 ID,必须在读写前完成,用于控制持久化布局与 VM 磨损。

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

USB 侧

  • DMA 池耗尽:usb_alloc_ep_dmabuffer() 在 lbuf 无足够连续空间时返回 NULL。设计上通过 USB_DMA_BUF_MAX_SIZE 的预聚合公式(各设备类大小 + 对齐 + 100 字节余量)尽量规避;若多个设备类同时使能导致超卖,上层必须检查返回值并降级(如关闭对应功能),否则 DMA 写越界会造成内存破坏。
  • 多控制器竞争:ISR 表按 [USB_MAX_HW_NUM][MAX_EP] 索引,不同 usb_id 之间天然隔离;同一控制器的多个端点共享 lbuf 池,因此端点缓冲分配与释放必须串行化(初始化阶段集中分配),运行期动态分配需加临界区保护。
  • 枚举失败/设备热插拔(主机模式):usb_host.c 负责枚举状态机,外部设备未就绪时应由上层周期重试;usb_host_free() 必须与 usb_host_config() 配对,否则 ISR 表残留悬空回调。
  • Dongle 多连接场景:CONFIG_APP_DONGLE 下 HID DMA 缓冲按 64 * CONFIG_BT_GATT_CLIENT_NUM 放大,说明同一 HID 端点需要为每个 GATT 客户端维护独立的 64 字节上报缓冲,缓冲不足会直接表现为 HID 上报失败。

RTC 侧

  • VM 磨损:时间类数据高频写入会加速 VM 闪存擦写损耗,因此 vir_set_vm_id() 单独划分 sec_vm_id 分区,把高频变化的秒计数与低频变化的日期时间分离;上层应避免在 vir_rtc_test 类周期回调中无谓地 vir_write_sys_time()。
  • 初始化顺序:vir_rtc_simulate_init() 之前调用任何读写接口属于未定义行为(设备节点未注册、VM 分区未绑定),示例代码严格遵循"init → read/write → alarm_enable"顺序,上层必须同样保证。
  • 低功耗唤醒竞态:闹钟使能依赖 LP Timer 的唤醒点设置;若在设置唤醒点与进入休眠之间时间被改写(vir_write_sys_time),唤醒时刻可能漂移。设计上要求时间写操作与 power_set_soft_poweroff() 之类的休眠入口互斥或串行执行。
  • 演示代码默认关闭:virtual_rtc_demo.c 整体包在 #if 0 中,且注释指明虚拟 RTC 仅支持 AC695/AC635 等特定平台;在其他芯片(如 BR 系列)移植时必须核对 include_lib/driver/cpu/<chip>/asm/rtc.h 的硬件能力,避免把模拟实现硬套到有真实 RTC 的平台。

性能与运维注意事项

  • 零拷贝 DMA:USB 数据直接经由对齐的 DMA 池缓冲收发,避免中间拷贝;ep0_dma_buffer 独立放置于 .usb_ep0 段,保证 Setup 包处理不被大数据传输阻塞。
  • 内存静态化:默认 USB_MALLOC_ENABLE=0 时,配置变量(usb_config_var_t)位于静态段 .usb_config_var,避免运行期堆分配的不确定性与碎片化;仅在 RAM 极度紧张或需要运行时裁剪时才开启堆分配。
  • 中断亲和性:cpu_id 参数把 USB ISR 绑定到指定核,双核平台建议将 USB 中断与高频音频中断分核放置,降低中断嵌套延迟。
  • 日志开关:usb_config.c 通过 LOG_TAG "[USB]" 与 LOG_ERROR/DEBUG/INFO/CLI_ENABLE 控制日志级别,LOG_DUMP_ENABLE 默认关闭——排查枚举失败时可临时打开 Dump 观察 Setup/数据包,量产固件应保持关闭以节省串口带宽与 Flash 空间。

扩展点

  • 新增 USB 设备类:在 usb_config.c 中仿照 MSD/HID/CDC 增加 TCFG_USB_SLAVE_XXX_ENABLE 宏与对应 XXX_DMA_SIZE,并在 usb_device.c / usb_std_class_def.h 中注册描述符与类请求处理,最后在 USB_DMA_BUF_MAX_SIZE 聚合公式中加入新类缓冲即可。
  • 多核/多控制器:USB_MAX_HW_NUM 与 MAX_EP_TX/RX 是上层可调的编译常量,扩展 USB 控制器数量只需提升数组维度并保持 ISR 表与 usb_id 一一对应。
  • 自定义 RTC 后端:virtual_rtc.h 的读写接口即扩展契约——若平台有真实硬件 RTC,可在 vir_rtc_simulate 内部切换到硬件驱动(cpu/*/asm/rtc.h),对外 API 与 struct sys_time 契约完全不变,应用层零改动。
  • 闹钟业务:rtc_alarm.c 在各 APP 目录独立维护,新应用(如 Mesh 定时广播)可复制该模块并在 alm_wakeup_isr 回调中挂接自己的业务逻辑。

测试

  • USB:apps/hid/examples/standard_keyboard/usb_hid_devices.c 与 apps/spp_and_le/examples/dongle/usb_hid_devices.c(含 usb_second_hid_devices.c)是可编译的从机 HID 示例,覆盖单/双 HID 设备与 Dongle 多 GATT 客户端两种典型形态,可直接作为功能验证与回归基线。
  • RTC:virtual_rtc_demo.c 即为自测程序——初始化后打印系统时间、写入并回读闹钟时间,配合 sys_timer_add 周期打印可验证走时精度;get_lp_timer1_status() 提供 LP Timer 状态查询,用于确认休眠期间定时器未被误关。
  • 未发现针对本框架的独立单元测试工程;协议栈正确性主要依赖上述示例固件在目标板上的枚举/读写联调验证。

Related Links

  • USB 配置头文件 usb_config.h — 配置层全部 API 声明
  • USB 配置实现 usb_config.c — DMA 池与中断表实现
  • USB 主机协议栈目录 — 主机枚举与批量/控制传输
  • 虚拟 RTC 接口 virtual_rtc.h — RTC 抽象层契约
  • 虚拟 RTC 演示 virtual_rtc_demo.c — 完整调用示例
  • RTC 闹钟模块 rtc_alarm.c — 应用级闹钟服务
  • 相邻主题:蓝牙协议栈(GATT/SPP)、电源管理(低功耗休眠入口)、系统时间服务(system/sys_time.h)请参阅对应目录页。
Prev
存储与配置系统