外设驱动接口(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 命名与语义,仅寄存器映射和引脚复用号不同,因此上层应用代码可以跨芯片复用。
驱动设计遵循三条原则:
- 平台数据驱动初始化:每个外设驱动通过一个平台数据结构(
hw_iic_config、pwm_platform_data、charge_platform_data)接收引脚、波特率、电压档位等参数,驱动本身不硬编码硬件连接,便于不同产品复用同一驱动代码。 - 通道宏抽象:ADC 用统一的通道宏(
AD_CH_*)屏蔽 GPIO 通道、PMU 内部节点与音频模拟通道的区别,调用方只需传入宏即可。 - 设备操作表 + 状态机:充电等需要生命周期管理的驱动注册为
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
典型使用模式:
- 系统启动时调用
adc_init()(内部调用_adc_init(sys_lvd_en),bd19 实现中传入1使能系统低电压检测); - 需要周期性采样的通道用
adc_add_sample_ch(ch)加入轮询,用adc_remove_sample_ch(ch)移除; - 单次采样用
adc_sample(ch)或直接adc_get_value(ch); - 读取电压值用
adc_get_voltage(ch)(返回 mV 级电压值); - 电池低压判断用
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 下拉配置等)。初始化流程:
- 保存平台数据并复位状态(
init_ok = 0、charge_online_flag = 0); - 先关闭充电使能(
CHARGE_EN(0)),并禁用充电检测引脚唤醒(power_awakeup_disable_with_port(IO_CHGFL_DET))——等检测到充电插入再开启,避免误触发; - 配置 LDO5V 的 100K 下拉电阻(
L5V_RES_DET_S_SEL/L5V_LOAD_EN); - 调用
charge_config()写满电电压/电流档位; - 若
check_charge_state()检测到充电插入,则启动ldo5v_detect定时器(usr_timer_add(0, ldo5v_detect, 2, 1));否则置charge_flag = BIT_LDO5V_OFF并调用power_exit_charge_mode()退出充电模式; - 置
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()) {
// 进入低压处理
}
硬件 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_config | IO 引脚 | IO_PORTA_06/IO_PORTA_07 | SCL / SDA 引脚 |
baudrate | hw_iic_config | u32 (Hz) | 100000 | IIC 通讯波特率 |
hdrive | hw_iic_config | 0/1 | 0 | 是否打开 IO 强驱 |
io_filter | hw_iic_config | 0/1 | 1 | 是否打开滤波器(去纹波) |
io_pu | hw_iic_config | 0/1 | 1 | 是否打开上拉(外部无上拉时必须置 1) |
role | hw_iic_config | 枚举 | IIC_MASTER | 主机/从机角色 |
complementary_en | pwm_platform_data | 0/1 | 0 | 0=同步波形;1=互补波形(占空比体现在 H 引脚) |
charge_full_V | charge_platform_data | 档位 | CHARGE_FULL_V_4222 | 满电电压档位(配合 VBAT trim 校准) |
charge_full_mA | charge_platform_data | 档位 | — | 满电电流档位 |
ldo5v_pulldown_lvl / ldo5v_pulldown_en | charge_platform_data | 0/1/电平 | — | LDO5V 100K 下拉电阻的检测电平与使能 |
注:
charge_platform_data的完整字段以各芯片charge.h实际声明为准;上表取自 bd19charge.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>实现目录并调整平台数据结构,应用代码基本不动。
扩展点
- 平台数据结构:新增产品时,通过填充
hw_iic_config、pwm_platform_data、charge_platform_data即可复用驱动代码,无需修改驱动内部逻辑——这是本 SDK 外设驱动的主要定制入口。 - 设备操作表:自定义外设可仿照
charge_dev_ops注册device_operations(至少提供.init),接入统一设备模型,由上层通过dev_node统一查找与初始化。 - GPIO 复用功能接口:
gpio_set_fun_output_port/gpio_set_fun_input_port是引脚功能映射的统一入口(FO_IIC_SCL、PFI_IIC_SCL等为复用号),新外设或新引脚方案都通过该接口接入。 - 软件 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等接口的完整语义 - 关键源码: