杰理 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)
  • 文档与开发资源

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

外设驱动接口(ADC/IIC/SPI/PWM/LED/充电)

AC63 系列蓝牙 SoC 的外设驱动接口层,覆盖 ADC(模数转换)、IIC(I²C 总线)、SPI、PWM/MCPWM、LED 与充电(Charge)管理的驱动 API、通道定义、配置结构与初始化流程。本文档基于 cpu/bd19 及其 include_lib/driver/cpu/bd19 头文件中的实际实现,说明各外设驱动的调用方式、内部机制与设计意图。

Purpose and Scope

本文档面向需要在 AC63 系列芯片(bd19/bd29/br23/br25/br30/br34)上使用外设驱动的应用开发者与驱动维护者,覆盖:

  • ADC:AD 通道定义(GPIO 通道 / PMU 通道 / 音频通道)、采样 API(adc_get_value、adc_get_voltage、adc_add_sample_ch 等)与 VBAT 低压检测。
  • IIC:硬件 IIC(hw_iic_init + hw_iic_config 平台数据结构)与软件模拟 IIC(soft_iic_init)两套实现。
  • SPI:外设驱动框架中与 IIC 并列的串行外设接口(同属 cpu/<chip> 目录驱动族,实现文件按芯片目录组织)。
  • PWM/MCPWM:mcpwm_init 与 pwm_platform_data 配置(同步/互补波形)。
  • LED:由 GPIO 复用功能与 PWM 定时器驱动的指示类外设(与 PWM/GPIO 驱动共用配置框架)。
  • 充电(Charge):charge_init、charge_config、get_charge_full_value、charge_module_stop/restart 及 charge_dev_ops 设备操作表,包含 LDO5V 检测、满电电压 trim 等。

范围边界:本页聚焦驱动 API 层的接口定义、平台数据结构与初始化/采样/检测流程;各芯片的电源管理策略(power.c)、音频编解码(audio_*)、USB 驱动属于各自的独立主题,不在此页展开。对于驱动框架的通用设备模型(dev_node、device_operations),本文仅在与外设驱动直接相关处说明,完整的设备模型请参阅平台基础驱动相关页面。

Overview

AC63 SDK 采用按芯片分目录、接口头文件集中声明的组织方式:驱动实现位于 cpu/<chip>/(如 cpu/bd19/adc_api.c、cpu/bd19/iic_hw.c、cpu/bd19/iic_soft.c、cpu/bd19/mcpwm.c、cpu/bd19/charge.c),而公开 API 声明集中在 include_lib/driver/cpu/<chip>/asm/ 下(如 asm/adc_api.h)。不同芯片型号共享同一套 API 命名与语义,仅寄存器映射和引脚复用号不同,因此上层应用代码可以跨芯片复用。

驱动设计遵循三条原则:

  1. 平台数据驱动初始化:每个外设驱动通过一个平台数据结构(hw_iic_config、pwm_platform_data、charge_platform_data)接收引脚、波特率、电压档位等参数,驱动本身不硬编码硬件连接,便于不同产品复用同一驱动代码。
  2. 通道宏抽象:ADC 用统一的通道宏(AD_CH_*)屏蔽 GPIO 通道、PMU 内部节点与音频模拟通道的区别,调用方只需传入宏即可。
  3. 设备操作表 + 状态机:充电等需要生命周期管理的驱动注册为 device_operations(含 .init),并通过 init_ok、charge_online_flag 等状态字段管理使能与检测流程。

关键概念

概念说明
AD_CH_*ADC 通道宏,由低 4 位 GPIO 通道号与高 16 位 PMU 通道选择位组合而成
PMU 通道芯片内部电源管理单元的可采样节点(VBAT、LDO5V、SYSVDD、温度等)
hw_iic_dev / soft_iic_dev硬件/软件 IIC 的设备句柄(即 IIC 序号)
platform_data传给驱动 init 的硬件配置结构体
device_operations设备驱动标准操作表,charge_dev_ops 为其示例
trim芯片出厂校准值(如 VBAT trim),用于修正满电电压档位

Architecture

flowchart TD
    subgraph sg_App["应用层"]
        App["应用代码 / 上层服务"]
    end

    subgraph sg_API["驱动 API 层 (include_lib/driver/cpu/bd19/asm)"]
        ADC_API["adc_api.h<br/>adc_init / adc_get_value / adc_get_voltage"]
        IIC_API["iic_hw.h / iic_soft.h<br/>hw_iic_init / soft_iic_init"]
        PWM_API["mcpwm.h / pwm.h<br/>mcpwm_init"]
        CHG_API["charge.h<br/>charge_init / charge_config"]
    end

    subgraph sg_Impl["驱动实现层 (cpu/bd19)"]
        ADC_IMPL["adc_api.c"]
        IIC_HW["iic_hw.c"]
        IIC_SW["iic_soft.c"]
        PWM_IMPL["mcpwm.c / audio_pwm.c"]
        CHG_IMPL["charge.c"]
    end

    subgraph sg_HW["寄存器 / 硬件层"]
        REG["JL_IIC / ADC / MCPWM 寄存器组"]
        GPIO["GPIO 复用功能 (FO_/PFI_ 接口)"]
        PMU["电源管理单元 (PMU)"]
    end

    App --> ADC_API
    App --> IIC_API
    App --> PWM_API
    App --> CHG_API

    ADC_API --> ADC_IMPL
    IIC_API --> IIC_HW
    IIC_API --> IIC_SW
    PWM_API --> PWM_IMPL
    CHG_API --> CHG_IMPL

    IIC_HW --> REG
    IIC_HW --> GPIO
    ADC_IMPL --> PMU
    CHG_IMPL --> PMU
    PWM_IMPL --> REG

架构说明:应用层只接触 asm/*.h 中声明的 API 宏与函数;驱动实现层负责将平台数据结构翻译为寄存器操作与 GPIO 复用配置。硬件 IIC 通过 gpio_set_fun_output_port / gpio_set_fun_input_port 将 SCL/SDA 引脚切换为第二功能(FO_IIC_SCL/FO_IIC_SDA),充电与 ADC 的 VBAT 采样则依赖 PMU 内部通路。这样的分层使芯片换型时只需替换 cpu/<chip> 实现目录,API 层与应用代码保持稳定。

ADC 驱动接口

通道定义

ADC 通道由 adc_api.h 统一定义,分为三类:

  • GPIO 通道(低 4 位 0x0~0xF):直接采样 IO 引脚电平,如 AD_CH_PA1、AD_CH_PA3、AD_CH_PB4 等。
  • PMU 通道(AD_CH_PMU | ADC_PMU_CH_*):采样芯片内部电源节点,如 AD_CH_VBAT(1/4 VBAT)、AD_CH_LDO5V(1/4 LDO5V)、AD_CH_DTEMP(芯片温度)、AD_CH_SYSVDD 等。
  • 音频通道(AD_CH_AUDIO | AD_AUDIO_*):采样音频模拟通路节点,如 AD_CH_VCM、AD_CH_VOUTL/VOUTR、AD_CH_DACVDD。
//AD channel define
#define AD_CH_PA1    (0x0)
#define AD_CH_PA3    (0x1)
#define AD_CH_PA5    (0x2)
#define AD_CH_PA7    (0x3)
#define AD_CH_PA8    (0x4)
#define AD_CH_DP1    (0x5)
#define AD_CH_DM1    (0x6)
#define AD_CH_PB1    (0x7)
#define AD_CH_PA9    (0x8)
#define AD_CH_PB4    (0x9)
#define AD_CH_DP0    (0xA)
#define AD_CH_DM0    (0xB)
#define AD_CH_PB6    (0xC)
#define AD_CH_PMU    (0xD)
#define AD_CH_OSC32K     (0xE)
#define AD_CH_BT     (0xF)

#define ADC_PMU_CH_VBG       (0x0<<16)  //MVBG
#define ADC_PMU_CH_VDC13     (0x1<<16)
#define ADC_PMU_CH_SYSVDD    (0x2<<16)
#define ADC_PMU_CH_DTEMP     (0x3<<16)
#define ADC_PMU_CH_VBAT      (0x5<<16)     //1/4vbat
#define ADC_PMU_CH_LDO5V     (0x6<<16)     //1/4 LDO5V

Source: adc_api.h

设计意图:通道宏将"采样点选择"编码进一个 u32 值——低 4 位选 GPIO 通道,高 16 位选 PMU 内部节点。这样 adc_get_value(ch) 等 API 只需一个参数即可表达"采哪个引脚"或"采哪个电源节点",同时让 AD_CH_VBAT = AD_CH_PMU | ADC_PMU_CH_VBAT 这类组合宏保持可读性。ADC_MAX_CH 10 限定了硬件同时支持的采样通道数。

采样 API 与使用流程

extern void adc_init();
extern void adc_vbg_init();
extern void adc_pmu_ch_select(u32 ch);
extern void adc_pmu_detect_en(u32 ch);
extern void adc_vdc13_save();
extern void adc_vdc13_restore();

u32 adc_get_value(u32 ch);
u32 adc_add_sample_ch(u32 ch);
u32 adc_remove_sample_ch(u32 ch);
u32 adc_get_voltage(u32 ch);
u32 adc_check_vbat_lowpower();
u32 adc_sample(u32 ch);

Source: adc_api.h

典型使用模式:

  1. 系统启动时调用 adc_init()(内部调用 _adc_init(sys_lvd_en),bd19 实现中传入 1 使能系统低电压检测);
  2. 需要周期性采样的通道用 adc_add_sample_ch(ch) 加入轮询,用 adc_remove_sample_ch(ch) 移除;
  3. 单次采样用 adc_sample(ch) 或直接 adc_get_value(ch);
  4. 读取电压值用 adc_get_voltage(ch)(返回 mV 级电压值);
  5. 电池低压判断用 adc_check_vbat_lowpower()。

adc_init 的初始化入口(bd19 实现):

void adc_init()
{
    ...
    _adc_init(1);
}
//late_initcall(adc_init);

Source: adc_api.c

注意 late_initcall(adc_init) 被注释掉,说明 ADC 初始化由板级/应用显式调用,而非内核自动挂载——这样应用可以控制 ADC 的启动时机与功耗。

IIC 驱动接口

SDK 提供硬件 IIC(cpu/bd19/iic_hw.c)与软件模拟 IIC(cpu/bd19/iic_soft.c)两套实现,均以"设备序号 + 平台配置"方式初始化。

硬件 IIC

硬件 IIC 的初始化入口为 hw_iic_init(hw_iic_dev iic),其中 hw_iic_dev 即 IIC 控制器序号(bd19 上 iic_regs 数组将序号映射到 JL_IIC 寄存器组):

static JL_IIC_TypeDef *const iic_regs[IIC_HW_NUM] = {
    JL_IIC,
};

#define iic_get_id(iic)         (iic)

#define iic_info_port(iic, x)   (hw_iic_cfg[iic_get_id(iic)].port[x])
#define iic_info_baud(iic)      (hw_iic_cfg[iic_get_id(iic)].baudrate)
#define iic_info_hdrive(iic)    (hw_iic_cfg[iic_get_id(iic)].hdrive)
#define iic_info_io_filt(iic)   (hw_iic_cfg[iic_get_id(iic)].io_filter)
#define iic_info_io_pu(iic)     (hw_iic_cfg[iic_get_id(iic)].io_pu)
#define iic_info_role(iic)      (hw_iic_cfg[iic_get_id(iic)].role)

Source: iic_hw.c

引脚初始化通过 GPIO 复用功能完成:SCL/SDA 分别切换为输出第二功能 FO_IIC_SCL/FO_IIC_SDA 与输入功能 PFI_IIC_SCL/PFI_IIC_SDA,再按平台配置决定是否开启强驱(hdrive)、上拉(io_pu)与滤波(io_filter)。若引脚位于 USB 口(IO_PORT_DP 之后),还需先调用 usb_iomode(1) 切换 IO 模式:

static int iic_port_init(hw_iic_dev iic)
{
    ...
    if (id == 0) {
        gpio_set_fun_output_port(scl, FO_IIC_SCL, 1, 1);
        gpio_set_fun_output_port(sda, FO_IIC_SDA, 1, 1);
        gpio_set_fun_input_port(scl, PFI_IIC_SCL);
        gpio_set_fun_input_port(sda, PFI_IIC_SDA);
        if (scl >= IO_PORT_DP || sda >= IO_PORT_DP) {
            usb_iomode(1);
        }
        if (iic_info_hdrive(iic)) {
            gpio_set_hd(scl, 1);
            gpio_set_hd(sda, 1);
        } else {
            gpio_set_hd(scl, 0);
            gpio_set_hd(sda, 0);
        }
        if (iic_info_io_pu(iic)) {
            gpio_set_pull_up(scl, 1);
            gpio_set_pull_down(scl, 0);
            gpio_set_pull_up(sda, 1);
            gpio_set_pull_down(sda, 0);
        }
        ...

Source: iic_hw.c

源文件头部注释还强调了一个重要的硬件时序约束:带 cfg_done 的硬件 IIC,START/ACK(NACK) 必须在发送或接收字节的 cfg_done 之前设置(不能跟随 cfg_done 单独发送),而 STOP 应在 cfg_done 之后设置并必须跟随 cfg_done 单独发送。这是驱动开发者在编写事务时序时必须遵守的硬件限制。

软件模拟 IIC

软件 IIC 通过 soft_iic_init(soft_iic_dev iic) 初始化,用 GPIO 位操作模拟 I²C 时序,适合低速、引脚灵活的场合;其平台数据结构与硬件 IIC 类似(引脚、波特率、上拉等),但不需要占用硬件控制器。选择依据:速率要求高、有现成控制器引脚时用硬件 IIC;仅需低速且希望任意 GPIO 可用时用软件 IIC。

PWM/MCPWM 驱动接口

PWM 由 cpu/bd19/mcpwm.c 实现,初始化入口为 mcpwm_init(struct pwm_platform_data *arg)。平台数据结构 pwm_platform_data 描述输出引脚与波形属性,关键字段是 complementary_en:0 表示两引脚输出同步波形,1 表示互补波形(互补波形的占空比体现在 H 引脚上):

mcpwm_init(&pwm_p_data);
#endif
...
mcpwm_init(&pwm_p_data);

Source: mcpwm.c

在 audio_pwm.c 中可以看到 PWM 的另一典型用法——音频 PWM 输出:usr_mcpwm_init() 通过 request_irq(IRQ_MCPWM_TIMER, 3, usr_mcpwm_isr, 0) 注册 MCPWM 定时器中断,用中断服务程序驱动占空比变化(AUDIO_PWM_SOURCE == 1 时选用 MCPWM 定时器):

void usr_mcpwm_init(void)
{
    printf("############ usr_mcpwm_init ########## \n");
    request_irq(IRQ_MCPWM_TIMER, 3, usr_mcpwm_isr, 0);
    ...

Source: audio_pwm.c

设计意图:PWM 驱动把"波形参数"(引脚、周期、占空比、同步/互补)封装在 pwm_platform_data 中,由板级初始化函数填充后调用 mcpwm_init;中断方式则用于音频/呼吸灯这类需要动态调制占空比的场景,与静态配置互补。

充电(Charge)驱动接口

充电驱动是外设驱动中最复杂的一个,因为它在充电检测、电压校准、低功耗唤醒之间协调。bd19 实现位于 cpu/bd19/charge.c。

设备操作表与初始化

驱动以标准设备操作表形式注册,charge_dev_ops 仅暴露 .init(其余操作由上层电源管理逻辑直接调用驱动函数):

const struct device_operations charge_dev_ops = {
    .init  = charge_init,
};

Source: charge.c

charge_init(const struct dev_node *node, void *arg) 是驱动的核心入口,arg 指向 struct charge_platform_data(含 charge_full_V 满电电压档位、charge_full_mA 满电电流、ldo5v_pulldown_lvl/ldo5v_pulldown_en LDO5V 下拉配置等)。初始化流程:

  1. 保存平台数据并复位状态(init_ok = 0、charge_online_flag = 0);
  2. 先关闭充电使能(CHARGE_EN(0)),并禁用充电检测引脚唤醒(power_awakeup_disable_with_port(IO_CHGFL_DET))——等检测到充电插入再开启,避免误触发;
  3. 配置 LDO5V 的 100K 下拉电阻(L5V_RES_DET_S_SEL / L5V_LOAD_EN);
  4. 调用 charge_config() 写满电电压/电流档位;
  5. 若 check_charge_state() 检测到充电插入,则启动 ldo5v_detect 定时器(usr_timer_add(0, ldo5v_detect, 2, 1));否则置 charge_flag = BIT_LDO5V_OFF 并调用 power_exit_charge_mode() 退出充电模式;
  6. 置 init_ok = 1 完成初始化。
int charge_init(const struct dev_node *node, void *arg)
{
    log_info("%s\n", __func__);
    __this->data = (struct charge_platform_data *)arg;
    ASSERT(__this->data);

    __this->init_ok = 0;
    __this->charge_online_flag = 0;

    /*先关闭充电使能,后面检测到充电插入再开启*/
    power_awakeup_disable_with_port(IO_CHGFL_DET);
    CHARGE_EN(0);

    /*LDO5V的100K下拉电阻使能*/
    L5V_RES_DET_S_SEL(__this->data->ldo5v_pulldown_lvl);
    L5V_LOAD_EN(__this->data->ldo5v_pulldown_en);

    charge_config();

    if (check_charge_state()) {
        if (__this->ldo5v_timer == 0) {
            __this->ldo5v_timer = usr_timer_add(0, ldo5v_detect, 2, 1);
        }
    } else {
        charge_flag = BIT_LDO5V_OFF;
        power_exit_charge_mode();
    }

    __this->init_ok = 1;
    return 0;
}

Source: charge.c

满电电压校准(trim)

charge_config() 的核心逻辑是用芯片出厂 VBAT trim 值推算实际满电电压档位:读取 get_vbat_trim(),若为 0xf 说明未校准(回退到默认配置 CHARGE_FULL_V_4222),否则以 trim 档位为基准、按 charge_full_V 与 4.22V 的差值加减偏移,并限制在 0~0xf 范围内,最后写入 CHARGE_FULL_V_SEL / CHARGE_FULL_mA_SEL / CHARGE_mA_SEL 寄存器:

static void charge_config(void)
{
    u8 charge_4202_trim_val = CHARGE_FULL_V_4222;
    u8 offset = 0;
    u8 charge_full_v_val = 0;

    if (get_vbat_trim() == 0xf) {
        log_info("vbat not trim, use default config!!!!!!");
    } else {
        charge_4202_trim_val = get_vbat_trim();		//4.2V对应的trim出来的实际档位
    }
    ...
    if (__this->data->charge_full_V >= CHARGE_FULL_V_4222) {
        offset = __this->data->charge_full_V - CHARGE_FULL_V_4222;
        charge_full_v_val = charge_4202_trim_val + offset;
        if (charge_full_v_val > 0xf) {
            charge_full_v_val = 0xf;
        }
    } else {
        offset = CHARGE_FULL_V_4222 - __this->data->charge_full_V;
        ...
    }
    CHARGE_FULL_V_SEL(charge_full_v_val);
    CHARGE_FULL_mA_SEL(__this->data->charge_full_mA);
    CHARGE_mA_SEL(CHARGE_mA_20);
}

Source: charge.c

生命周期管理

  • charge_module_stop():关闭充电(charge_close())、禁用 IO_LDOIN_DET/IO_VBTCH_DET 唤醒并删除 ldo5v_timer;未初始化(!init_ok)时直接返回,保证幂等。
  • charge_module_restart():重新拉起充电检测流程。
  • get_charge_full_value():返回当前配置的满电电压查表值,入口处用 ASSERT(init_ok) 与 ASSERT(charge_full_V < CHARGE_FULL_V_MAX) 做防御性校验。

LED 驱动

LED 在 AC63 SDK 中没有独立的"LED 外设"寄存器,而是复用 GPIO + PWM/定时器实现:静态指示用 GPIO 输出(gpio_set_direction/gpio_write 等通用 GPIO API),呼吸/闪烁效果通过 MCPWM 或定时器中断动态调制占空比(见 audio_pwm.c 中 usr_mcpwm_isr 的中断驱动模式)。因此 LED 相关配置与 PWM 平台数据、GPIO 复用配置共用同一框架,本页 PWM 一节的接口即为 LED 特效的主要实现载体。

核心流程

充电检测与初始化时序

sequenceDiagram
    participant App as 应用/电源管理
    participant CHG as charge_init (charge.c)
    participant PMU as PMU/充电寄存器
    participant TMR as usr_timer

    App->>CHG: charge_init(node, platform_data)
    CHG->>CHG: ASSERT(data) / 复位 init_ok、charge_online_flag
    CHG->>PMU: CHARGE_EN(0) 先关充电
    CHG->>PMU: L5V_RES_DET_S_SEL / L5V_LOAD_EN 配置下拉
    CHG->>PMU: charge_config() 写满电电压/电流档位 (trim)
    CHG->>CHG: check_charge_state() 检测充电插入
    alt 已插入充电
        CHG->>TMR: usr_timer_add(ldo5v_detect, 2s)
        TMR-->>CHG: 周期回调检测 LDO5V
    else 未插入
        CHG->>PMU: power_exit_charge_mode() + charge_flag=BIT_LDO5V_OFF
    end
    CHG-->>App: return 0 (init_ok=1)

ADC 采样流程

sequenceDiagram
    participant App as 应用
    participant ADC as adc_api (adc_api.c)
    participant HW as ADC 硬件/PMU 通路

    App->>ADC: adc_add_sample_ch(AD_CH_VBAT)
    ADC->>HW: 配置通道并加入轮询
    loop 周期性采样
        ADC->>HW: 触发采样
        HW-->>ADC: 原始 ADC 值
        ADC-->>App: adc_get_voltage(AD_CH_VBAT) 返回 mV
    end
    App->>ADC: adc_remove_sample_ch(AD_CH_VBAT)
    ADC->>HW: 停止该通道采样

使用示例

ADC 读取电池电压

// 将 VBAT 通道加入周期采样,然后读取电压(mV)
adc_init();
adc_add_sample_ch(AD_CH_VBAT);
...
u32 vbat_mv = adc_get_voltage(AD_CH_VBAT);
if (adc_check_vbat_lowpower()) {
    // 进入低压处理
}

Source: adc_api.h(API 声明)与 adc_api.c(初始化实现)

硬件 IIC 平台配置

/* const struct hw_iic_config hw_iic_cfg_test[] = { */
/*     //iic0 data */
/*     { */
/*         //         SCL          SDA */
/*         .port = {IO_PORTA_06, IO_PORTA_07}, */
/*         .baudrate = 100000,      //IIC通讯波特率 */
/*         .hdrive = 0,             //是否打开IO口强驱 */
/*         .io_filter = 1,          //是否打开滤波器(去纹波) */
/*         .io_pu = 1,              //是否打开上拉电阻,如果外部电路没有焊接上拉电阻需要置1 */
/*         .role = IIC_MASTER, */
/*     }, */
/* }; */

Source: iic_hw.c

配置要点:port 数组第 0 项为 SCL、第 1 项为 SDA;baudrate 为总线速率(例中 100kHz);外部电路没有上拉电阻时必须把 io_pu 置 1,否则总线无法可靠工作。

充电驱动注册

const struct device_operations charge_dev_ops = {
    .init  = charge_init,
};

Source: charge.c

charge_init 通过 struct dev_node *node 与 void *arg(即 charge_platform_data)接入设备模型,上层通过 dev_node 查找设备后即可调用。

配置选项

以下为外设平台数据结构中的主要配置项(以 bd19 为例):

配置项所属结构类型/取值默认示例说明
port[0] / port[1]hw_iic_configIO 引脚IO_PORTA_06/IO_PORTA_07SCL / SDA 引脚
baudratehw_iic_configu32 (Hz)100000IIC 通讯波特率
hdrivehw_iic_config0/10是否打开 IO 强驱
io_filterhw_iic_config0/11是否打开滤波器(去纹波)
io_puhw_iic_config0/11是否打开上拉(外部无上拉时必须置 1)
rolehw_iic_config枚举IIC_MASTER主机/从机角色
complementary_enpwm_platform_data0/100=同步波形;1=互补波形(占空比体现在 H 引脚)
charge_full_Vcharge_platform_data档位CHARGE_FULL_V_4222满电电压档位(配合 VBAT trim 校准)
charge_full_mAcharge_platform_data档位—满电电流档位
ldo5v_pulldown_lvl / ldo5v_pulldown_encharge_platform_data0/1/电平—LDO5V 100K 下拉电阻的检测电平与使能

注:charge_platform_data 的完整字段以各芯片 charge.h 实际声明为准;上表取自 bd19 charge.c 中 __this->data 的实际访问项。

API 参考

ADC

函数说明
void adc_init(void)初始化 ADC 子系统,内部调用 _adc_init(sys_lvd_en) 使能系统低电压检测
void adc_vbg_init(void)初始化带隙基准(VBG)
void adc_pmu_ch_select(u32 ch)选择 PMU 采样通道
void adc_pmu_detect_en(u32 ch)使能 PMU 通道检测
void adc_vdc13_save(void) / void adc_vdc13_restore(void)保存/恢复 VDC13 采样配置(低功耗前后使用)
u32 adc_get_value(u32 ch)获取通道原始采样值
u32 adc_add_sample_ch(u32 ch)将通道加入周期采样队列
u32 adc_remove_sample_ch(u32 ch)将通道移出采样队列
u32 adc_get_voltage(u32 ch)获取通道电压(mV 级换算值)
u32 adc_check_vbat_lowpower(void)检查 VBAT 是否处于低压状态
u32 adc_sample(u32 ch)单次触发采样指定通道

参数:ch 为 AD_CH_* 通道宏(如 AD_CH_PA1、AD_CH_VBAT、AD_CH_LDO5V)。
返回:adc_get_value/adc_get_voltage/adc_sample 返回采样结果(原始值或电压值);adc_add/remove_sample_ch 返回操作状态;adc_check_vbat_lowpower 返回非 0 表示低压。

IIC

函数说明
int hw_iic_init(hw_iic_dev iic)初始化硬件 IIC 控制器(引脚复用、波特率、上拉/强驱/滤波)
int soft_iic_init(soft_iic_dev iic)初始化软件模拟 IIC(GPIO 位操作)

参数:iic 为 IIC 控制器序号(bd19 上硬件 IIC 序号 0 映射到 JL_IIC 寄存器组)。
返回:0 表示成功,非 0 表示失败。

PWM / 充电

函数说明
void mcpwm_init(struct pwm_platform_data *arg)按平台数据初始化 MCPWM 输出(同步/互补波形)
void usr_mcpwm_init(void)注册 IRQ_MCPWM_TIMER 中断,用于动态调制占空比(如音频 PWM)
int charge_init(const struct dev_node *node, void *arg)充电驱动初始化入口(charge_dev_ops.init),arg 为 charge_platform_data
u16 get_charge_full_value(void)返回满电电压查表值(ASSERT 校验初始化状态与档位上限)
void charge_module_stop(void) / void charge_module_restart(void)停止/重启充电模块(低功耗进出时调用)

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

充电驱动的防御性检查

  • charge_init 对 __this->data 使用 ASSERT,平台数据为 NULL 时直接断言失败——这是嵌入式驱动常见的"配置错误尽早暴露"策略,避免后续空指针解引用。
  • get_charge_full_value() 双重断言:init_ok 未置位(未初始化即调用)与 charge_full_V >= CHARGE_FULL_V_MAX(档位越界)都会触发断言。
  • VBAT 未校准:get_vbat_trim() == 0xf 表示出厂未烧录校准值,charge_config() 回退到默认满电档位 CHARGE_FULL_V_4222,保证产品仍可充电,但满电精度下降。

充电插入/拔出竞态处理

初始化时先关闭充电使能(CHARGE_EN(0))再配置寄存器,最后才由 check_charge_state() 决定是否开启,避免初始化过程中出现充电误触发。charge_module_stop() 以 init_ok 为幂等保护,重复调用不会产生二次关闭副作用。LDO5V 检测由 usr_timer_add(0, ldo5v_detect, 2, 1) 定时驱动(2 个 tick 周期),定时器回调中更新充电状态,与主流程通过 charge_flag 位标志通信,属于单线程事件驱动的经典模型。

ADC 采样通道上限与低压检测

ADC_MAX_CH 10 表明硬件采样通道数量受限,adc_add_sample_ch 超过上限的通道会被拒绝或忽略(具体行为以实现为准),因此应用应只把真正需要的通道加入周期采样,其余用 adc_sample 单次采样,节省功耗。adc_check_vbat_lowpower 与 _adc_init(1) 中使能的系统低电压检测配合,构成电池保护的第一道防线。

硬件 IIC 时序约束(边界条件)

源文件注释明确警告:带 cfg_done 的硬件 IIC,START/ACK(NACK) 不能跟随 cfg_done 单独发送,STOP 则必须跟随 cfg_done 单独发送。违反该时序会导致总线状态错误;另外 SCL/SDA 位于 USB 口(IO_PORT_DP 之后)时必须先 usb_iomode(1) 切换 IO 模式,否则引脚复用无效。

性能与运维考虑

  • 按需采样:周期采样通道数受 ADC_MAX_CH 限制,采样频率越高功耗越大;低频监控(如 VBAT)建议用周期采样,一次性测量用 adc_sample。
  • 中断驱动 PWM:usr_mcpwm_init 注册的 IRQ_MCPWM_TIMER 中断用于音频 PWM 时,中断回调必须短小(usr_mcpwm_isr),避免影响音频实时性;该中断优先级为 3。
  • 充电定时器:ldo5v_detect 以 2 tick 周期运行,长时间在线充电时该定时器持续运行,低功耗设计应在充电拔出后通过 charge_module_stop() 删除定时器(实现中 usr_timer_del 已处理)。
  • 跨芯片一致性:API 层(asm/*.h)在 bd19/bd29/br23/br25/br30/br34 各芯片间保持一致,换芯片时只需替换 cpu/<chip> 实现目录并调整平台数据结构,应用代码基本不动。

扩展点

  1. 平台数据结构:新增产品时,通过填充 hw_iic_config、pwm_platform_data、charge_platform_data 即可复用驱动代码,无需修改驱动内部逻辑——这是本 SDK 外设驱动的主要定制入口。
  2. 设备操作表:自定义外设可仿照 charge_dev_ops 注册 device_operations(至少提供 .init),接入统一设备模型,由上层通过 dev_node 统一查找与初始化。
  3. GPIO 复用功能接口:gpio_set_fun_output_port / gpio_set_fun_input_port 是引脚功能映射的统一入口(FO_IIC_SCL、PFI_IIC_SCL 等为复用号),新外设或新引脚方案都通过该接口接入。
  4. 软件 IIC 的引脚灵活性:soft_iic_init 允许任意 GPIO 作为 SCL/SDA,适合低成本、低速外设扩展(如触摸、传感器)。

测试情况

  • SDK 提供 audio_adc_demo.c(cpu/br23、cpu/br25)演示 ADC 采集用于音频编码的完整链路,可作为 ADC API 集成测试参考。
  • 充电驱动的 ASSERT 防御逻辑(未初始化调用、档位越界)本身即构成轻量的运行时自检;get_vbat_trim() == 0xf 分支覆盖"未校准芯片"场景。
  • 各芯片目录(bd19/bd29/br23/br25/br30/br34)维护独立的 adc_api.c 实现,同一套 API 在多芯片上编译运行,本身即是跨芯片兼容性的持续验证。

Related Links

  • AC63 芯片平台总览(若存在)——芯片资源、电源域与启动流程
  • 通用 GPIO 驱动(若存在)——引脚复用功能 FO_/PFI_ 接口说明
  • 电源管理与低功耗(若存在)——power_awakeup_disable_with_port、power_exit_charge_mode 等接口的完整语义
  • 关键源码:
    • adc_api.h
    • adc_api.c
    • iic_hw.c
    • iic_soft.c
    • mcpwm.c
    • charge.c
Prev
音频编解码与时钟管理
Next
芯片配置工具与下载支持