外设驱动
本页介绍 fw-AC630N_BT_SDK 中面向 AC630N(bd29 内核)芯片的外设驱动体系:UART、ADC、PWM/LED、SPI Flash、SDIO 等设备驱动的分层结构、核心 API、初始化流程与配置方式。
Purpose and Scope
本页覆盖 AC630N 蓝牙 SDK 中**外设驱动(Peripheral Drivers)**这一完整能力,包括:
- 驱动分层架构:应用层 → 设备抽象层(
include_lib/driver/device/)→ CPU 驱动层(include_lib/driver/cpu/bd29/asm/与cpu/bd29/)→ 硬件寄存器 - UART 串口驱动:控制器数量、平台数据配置宏、设备操作接口
- ADC 采样驱动:通道映射、PMU 电源通道、采样与电压换算 API
- PWM/LED 驱动:闪烁模式、亮度控制、时钟源配置
- SPI Flash 与 SDIO 主机初始化等存储类外设
- 设备操作抽象(
struct device_operations)与设备注册机制
以下内容不属于本页范围,由其他目录页覆盖:蓝牙协议栈(GATT/SPP)、音频编解码、电源管理策略、系统时钟与中断控制器。本页只描述外设本身的驱动实现与使用方式。
Overview
AC630N 是杰理科技(Jieli Tech)面向 TWS 耳机、音箱等低功耗蓝牙音频应用的 SoC。其 SDK 采用经典的三层驱动模型:
- 设备抽象层(
include_lib/driver/device/):定义统一设备接口(如device/uart.h、device/spiflash.h),通过struct device_operations描述设备的打开、读、写、控制、关闭等标准操作,使上层应用可以以统一方式访问不同外设。 - CPU 驱动层(
include_lib/driver/cpu/bd29/asm/+cpu/bd29/):针对 bd29 内核实现具体外设,如uart_dev.c实现 UART 控制器寄存器级驱动,adc_api.h声明 ADC 采样 API,pwm_led.h提供 LED 呼吸/闪烁控制。 - 应用层(
apps/):如apps/spp_and_le/at_uart.c基于 UART 设备接口实现 AT 命令交互。
这种分层设计的意图在于:上层业务代码只依赖稳定的设备接口,不感知具体芯片的寄存器细节,从而保证 SDK 跨芯片(bd19/bd29 等)的可移植性;底层驱动通过 device_operations 结构体向设备框架注册,由设备框架统一管理生命周期(init → open → read/write/ioctrl → close)。
flowchart TD
subgraph sg_App["应用层 apps/"]
APP["应用代码<br/>spp_and_le/at_uart.c"]
end
subgraph sg_Dev["设备抽象层 include_lib/driver/device/"]
UART_IF["device/uart.h"]
SPIFLASH_IF["device/spiflash.h"]
SDIO_IF["device/sdio_host_init.h"]
OPS["device/device.h<br/>struct device_operations"]
end
subgraph sg_Cpu["CPU 驱动层 cpu/bd29/"]
UART_IMPL["uart_dev.c / uart_test.c"]
ADC_API["asm/adc_api.h"]
PWM_LED["asm/pwm_led.h"]
CRC16["asm/crc16.h (spi_crc16)"]
end
subgraph sg_HW["硬件外设"]
HW_UART["UART0 / UART1 / UART2"]
HW_ADC["ADC 通道<br/>PA1..SYS_PLL / PMU"]
HW_PWM["PWM / LED 引脚"]
HW_FLASH["SPI Flash / SD 卡"]
end
APP -->|"统一设备接口"| UART_IF
UART_IF --> OPS
UART_IMPL -->|"注册 uart_dev_ops"| OPS
ADC_API --> HW_ADC
PWM_LED --> HW_PWM
SPIFLASH_IF --> HW_FLASH
SDIO_IF --> HW_FLASH
UART_IMPL --> HW_UART
上图中各层职责:
- 设备抽象层是"契约":
device_operations定义了每个外设必须提供的标准操作集,device/uart.h等文件给出上层可见的数据结构与操作声明。 - CPU 驱动层是"实现":
uart_dev.c完成寄存器配置、FIFO 收发、中断处理;adc_api.h封装 ADC 通道选择与采样;pwm_led.h封装 LED 时序控制。它们向框架导出uart_dev_ops等操作表。 - 硬件层是最终操作对象:AC630N 提供 3 个 UART 控制器、多路 ADC 输入、PWM 引脚以及 SPI Flash/SD 存储接口。
UART 串口驱动
硬件资源与平台数据
AC630N(bd29)提供 3 个 UART 控制器(UART_NUM = 3)与 4 个输出端口(UART_OUTPORT_NUM = 4),定义于 CPU 驱动层头文件:
#define UART_NUM 3
#define UART_OUTPORT_NUM 4
#include "device/uart.h"
#include "device/device.h"
Source: uart.h
UART_OUTPORT_NUM 表示可映射的输出引脚端口数量,意味着同一个 UART 控制器可以通过引脚重映射接到不同的 IO 上,便于 PCB 布局灵活布线。
平台数据配置宏
SDK 通过一组平台数据宏(platform data macro)在编译期静态声明 UART 配置。每个控制器对应一对 BEGIN/END 宏,宏内部自动填充该 UART 的中断号:
#define UART0_PLATFORM_DATA_BEGIN(data) \
static const struct uart_platform_data data = {
#define UART0_PLATFORM_DATA_END() \
.irq = IRQ_UART0_IDX, \
};
#define UART1_PLATFORM_DATA_BEGIN(data) \
static const struct uart_platform_data data = {
#define UART1_PLATFORM_DATA_END() \
.irq = IRQ_UART1_IDX, \
};
#define UART2_PLATFORM_DATA_BEGIN(data) \
static const struct uart_platform_data data = {
#define UART2_PLATFORM_DATA_END() \
.irq = IRQ_UART2_IDX, \
};
Source: uart.h
设计意图:把"哪个中断属于哪个控制器"这类芯片强相关的细节固化在宏中,板级代码(board 配置)只需在 BEGIN/END 之间填写波特率、IO 引脚等板级参数,既避免遗漏必填字段,又保持板级配置的简洁。struct uart_platform_data 的具体字段定义在 include_lib/driver/device/uart.h(设备抽象层),供板级与应用层共同引用。
设备操作注册
UART 驱动向设备框架导出一个 device_operations 实例:
extern const struct device_operations uart_dev_ops;
Source: uart.h
uart_dev_ops 在 cpu/bd29/uart_dev.c 中实现,封装了 open/read/write/ioctrl/close 等标准操作。上层通过设备框架以统一方式访问 UART,例如 AT 命令模块 apps/spp_and_le/at_uart.c 就是基于该接口实现串口命令交互的。cpu/bd29/uart_test.c 则提供了驱动自测入口,演示设备打开、收发、控制命令的完整调用序列。
初始化入口
extern int uart_init(const struct uart_platform_data *);
Source: uart.h
uart_init 接收平台数据指针,完成控制器时钟使能、引脚功能复用(mux)、波特率分频、FIFO 与中断初始化。系统启动早期由驱动初始化流程调用,返回 0 表示成功。
ADC 采样驱动
通道映射
ADC 驱动把所有可采样输入统一编码为 32 位通道号,低 4 位为模拟输入引脚/内部信号,高 16 位为 PMU 子通道:
//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_PB2 (0x8)
#define AD_CH_PB4 (0x9)
#define AD_CH_DP0 (0xA)
#define AD_CH_DM0 (0xB)
#define AD_CH_DB6 (0xC)
#define AD_CH_PMU (0xD)
#define AD_CH_BT (0xE)
#define AD_CH_SYS_PLL (0xF)
Source: adc_api.h
可见 ADC 输入既包含普通 GPIO 引脚(PA1/PA3/PA5/PA7/PA8、PB1/PB2/PB4、DB6),也包含差分输入(DP0/DM0、DP1/DM1)以及内部信号(PMU 电源域、蓝牙射频、系统 PLL)。
PMU 电源监控通道
电源类监控通过 AD_CH_PMU 与 PMU 子通道组合实现:
#define ADC_PMU_CH_VBG (0x0<<16)
#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_PROGF (0x4<<16)
#define ADC_PMU_CH_VBAT (0x5<<16) //1/4vbat
#define ADC_PMU_CH_LDO5V (0x6<<16) //1/4 LDO5V
#define ADC_PMU_CH_WVDD (0x7<<16)
#define AD_CH_PMU_VBG (AD_CH_PMU | ADC_PMU_CH_VBG)
#define AD_CH_VDC13 (AD_CH_PMU | ADC_PMU_CH_VDC13)
#define AD_CH_SYSVDD (AD_CH_PMU | ADC_PMU_CH_SYSVDD)
#define AD_CH_DTEMP (AD_CH_PMU | ADC_PMU_CH_DTEMP)
#define AD_CH_PROGF (AD_CH_PMU | ADC_PMU_CH_PROGF)
#define AD_CH_VBAT (AD_CH_PMU | ADC_PMU_CH_VBAT)
#define AD_CH_LDO5V (AD_CH_PMU | ADC_PMU_CH_LDO5V)
#define AD_CH_WVDD (AD_CH_PMU | ADC_PMU_CH_WVDD)
Source: adc_api.h
设计意图:电池电压(VBAT)按 1/4 分压采样,以适配 ADC 满量程;AD_CH_LDOREF 别名指向 AD_CH_PMU_VBG,供内部基准校准使用。这样上层只需传一个通道号常量,无需关心分压比与子通道编码细节。
采样控制 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);
void adc_dump(void);
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);
Source: adc_api.h
adc_init():初始化 ADC 硬件(时钟、采样时序)。adc_vbg_init():初始化带隙基准(VBG),为电压换算提供基准值。adc_pmu_ch_select(ch)/adc_pmu_detect_en(ch):选择/使能 PMU 内部通道检测。adc_add_sample_ch(ch)/adc_remove_sample_ch(ch):动态增删轮询采样通道。SDK 的 ADC 采用周期轮询机制,ADC_MAX_CH定义为 10,即最多同时轮询 10 个通道;新增通道返回状态码,可用于判断是否超出容量。adc_get_value(ch):返回指定通道的原始采样值(ADC 码)。adc_get_voltage(ch):返回换算后的电压值(基于 VBG 基准与分压系数),上层(如电量显示)应优先使用此接口。adc_dump():调试用,打印各通道采样状态。
此外还提供电源目标档位换算接口 get_sysvdd_aims_level(aims_mv)、get_vdc13_aims_level(aims_mv)、get_mvddio_aims_level(aims_mv),以及低电量检测 adc_check_vbat_lowpower() 与基准校准标志 set_change_vbg_value_flag(),供电源管理模块调用。
PWM/LED 驱动
PWM 驱动在 AC630N 上主要面向 LED 指示灯控制,提供基于 PWM 的亮度调节与多种闪烁时序。其 API 按功能可分为四组。
初始化与开关
void pwm_led_init(const struct led_platform_data *user_data);
void pwm_led_set_on(void);
void pwm_led_set_off(void);
pwm_led_init 接收 struct led_platform_data(LED 引脚映射、默认亮度等板级参数),完成 PWM 定时器与 IO 初始化。pwm_led_set_on/off 用于全局开关 PWM 模块——例如进入低功耗前关闭 PWM 以节省电流。
闪烁模式与显示时序
void pwm_led_mode_set(u8 fre_mode); // LED 闪烁模式切换
void pwm_led_display_mode_reset(void); // 闪烁状态复位, 重新开始一个周期
void pwm_led_one_flash_display(u8 led_index, u16 led0_bright, u16 led1_bright,
u32 period, u32 start_light_time, u32 light_time);
void pwm_led_double_flash_display(u8 led_index, u16 led0_bright, u16 led1_bright,
u32 period, u32 first_light_time, u32 gap_time, u32 second_light_time);
Sources: pwm_led.h L136、pwm_led.h L146、pwm_led.h L180-L181、pwm_led.h L196-L197
设计意图:驱动把"闪烁模式"抽象为可编程时序参数,而不是硬编码若干固定模式。上层(如连接状态指示)只需指定周期、亮灯时间、灭灯间隔,即可表达"单闪/双闪/呼吸"等任意时序:
period:闪烁周期总长start_light_time/first_light_time:周期内首次点亮持续时间gap_time:双闪模式下两次点亮之间的间隔light_time/second_light_time:点亮时长led0_bright/led1_bright:两路 LED 的 PWM 亮度占空比参数
pwm_led_display_mode_reset() 用于在状态切换时立即从头开始一个周期,避免闪烁相位漂移造成观感混乱。
时钟与 IO 驱动能力
void pwm_led_clk_set(enum pwm_led_clk_source src);
void pwm_led_io_max_drive_set(u8 strength);
Sources: pwm_led.h L143、pwm_led.h L158
pwm_led_clk_set 选择 PWM 计数时钟源(影响可实现的闪烁频率精度与功耗);pwm_led_io_max_drive_set 调节 LED IO 的最大驱动电流档位,用于适配不同规格的 LED 灯珠(驱动能力过强会增大功耗,过弱则亮度不足)。
存储类外设:SPI Flash 与 SDIO
SDK 将存储设备同样纳入设备抽象层:
include_lib/driver/device/spiflash.h:SPI Flash 设备接口,供代码下载、参数存储、升级镜像存放等场景使用。include_lib/driver/device/sdio_host_init.h:SDIO 主机初始化接口,用于外接 SD 卡等存储介质。include_lib/driver/cpu/bd29/asm/crc16.h中提供与 SPI 数据完整性相关的校验接口:
void spi_crc16_set(u16 crc);
u16 spi_crc16_get(void);
Source: crc16.h
CRC16 校验用于 SPI Flash 读写链路的错误检测(例如 OTA 升级包校验、参数区写后回读校验),spi_crc16_set 注入校验初值,spi_crc16_get 读取计算后的校验结果。
核心流程
外设初始化时序
sequenceDiagram
participant SYS as "系统启动 (sys_init)"
participant UART as "uart_init / uart_dev_ops"
participant ADC as "adc_init / adc_vbg_init"
participant PWM as "pwm_led_init"
participant FLASH as "spiflash 设备"
participant APP as "应用 (apps/)"
SYS->>UART: 传递 uart_platform_data<br/>(波特率/IO/IRQ 宏)
UART-->>SYS: 注册 uart_dev_ops 到设备框架
SYS->>ADC: adc_init() + adc_vbg_init()
ADC-->>SYS: 建立 VBG 基准与轮询通道表
SYS->>PWM: 传递 led_platform_data
PWM-->>SYS: PWM 定时器与 LED IO 就绪
SYS->>FLASH: 打开 SPI Flash 设备
APP->>UART: dev_open/read/write (AT 命令)
APP->>ADC: adc_get_voltage(AD_CH_VBAT)
APP->>PWM: pwm_led_one_flash_display(...)
启动顺序的意图:先基准、后应用——ADC 的电压换算依赖 VBG 基准,因此 adc_vbg_init 必须在任何 adc_get_voltage 调用之前完成;UART 的中断号由平台数据宏保证与控制器一一对应,避免手动填写中断号出错。
ADC 轮询采样流程
flowchart TD
Start([应用需要采样]) --> Add["adc_add_sample_ch(ch)"]
Add --> Cap{"通道数 < ADC_MAX_CH?"}
Cap -->|"否"| Fail["返回失败, 通道未加入"]
Cap -->|"是"| Poll["ADC 周期轮询采样"]
Poll --> Ready{"采样完成?"}
Ready -->|"是"| Val["adc_get_value(ch) 原始码"]
Val --> Volt["adc_get_voltage(ch)<br/>基于 VBG 换算电压"]
Volt --> Done([上层使用: 电量/按键/检测])
Ready -->|"否"| Poll
该流程说明 ADC 采用集中轮询 + 按需读取模型:所有通道由同一个采样任务周期驱动,上层通过 adc_add_sample_ch 注册关注通道、adc_remove_sample_ch 注销,避免多任务同时操作 ADC 寄存器造成竞争。这也解释了 ADC_MAX_CH = 10 的容量上限——轮询周期内可容纳的通道数是有限的,注册过多通道会拉长单轮采样周期,影响低时延应用。
使用示例
UART 平台数据声明(板级配置)
在板级 board 配置文件中,通过平台数据宏静态声明 UART0 的配置(波特率、引脚等字段由 struct uart_platform_data 定义,此处展示宏骨架及其自动填充的中断号):
UART0_PLATFORM_DATA_BEGIN(uart0_data)
.baudrate = 115200,
/* .tx_pin / .rx_pin 等板级字段按实际硬件填写 */
UART0_PLATFORM_DATA_END()
extern int uart_init(const struct uart_platform_data *);
/* 初始化时: uart_init(&uart0_data); */
Source: uart.h(宏定义)与 uart.h L51(init 声明)
UART0_PLATFORM_DATA_END() 会自动补上 .irq = IRQ_UART0_IDX,这是该宏设计的关键价值:板级代码永远不会写错中断号。
ADC 电压采样(典型调用序列)
电池电压检测是 ADC 最典型的应用。先使能通道,待采样就绪后取电压:
/* 使能 VBAT 通道采样(1/4 分压) */
adc_add_sample_ch(AD_CH_VBAT);
/* 采样完成后获取换算电压 */
u32 vbat_mv = adc_get_voltage(AD_CH_VBAT);
/* 不再需要时移出轮询通道表 */
adc_remove_sample_ch(AD_CH_VBAT);
Source: adc_api.h(API 声明)与 adc_api.h L29(AD_CH_VBAT 定义)
注意 AD_CH_VBAT 是 AD_CH_PMU | ADC_PMU_CH_VBAT 的组合值(0xD | (0x5<<16)),上层无需关心分压系数,驱动在 adc_get_voltage 内部完成换算。
LED 单闪/双闪指示
连接状态指示常用双闪(如配对模式"快闪两下、停顿"):
/* 单闪: 周期 1s, 前 0.1s 点亮 */
pwm_led_one_flash_display(0, 80, 0, 1000, 100, 100);
/* 双闪: 周期 2s, 亮 100ms, 间隔 200ms, 再亮 100ms */
pwm_led_double_flash_display(0, 80, 0, 2000, 100, 200, 100);
/* 状态切换时复位闪烁相位 */
pwm_led_display_mode_reset();
Sources: pwm_led.h L180-L181、pwm_led.h L196-L197、pwm_led.h L146
配置选项
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
UART_NUM | 宏(int) | 3 | AC630N 可用的 UART 控制器数量 |
UART_OUTPORT_NUM | 宏(int) | 4 | UART 可映射的输出端口(引脚复用)数量 |
UARTx_PLATFORM_DATA_BEGIN/END | 宏 | — | UARTx 平台数据声明骨架,END 自动填充 IRQ_UARTx_IDX |
uart_platform_data.irq | 枚举(int) | IRQ_UARTx_IDX | 控制器对应中断号(由宏自动写入) |
ADC_MAX_CH | 宏(int) | 10 | ADC 轮询通道表最大容量 |
AD_CH_* | 宏(u32) | 见头文件 | ADC 输入通道编码(低 4 位引脚/信号,高 16 位 PMU 子通道) |
ADC_PMU_CH_* | 宏(u32) | 0x0<<16 ~ 0x7<<16 | PMU 电源监控子通道,VBAT/LDO5V 为 1/4 分压 |
led_platform_data | struct | 板级定义 | LED 引脚映射与默认亮度参数 |
| PWM 时序参数 | u32/u16 | 调用方指定 | period、light_time、gap_time、led0/1_bright 等 |
API 参考
UART
int uart_init(const struct uart_platform_data *data)
- 描述:初始化指定 UART 控制器(时钟、引脚、波特率、FIFO、中断)。
- 参数:
data— 板级 UART 平台数据(由UARTx_PLATFORM_DATA_BEGIN/END宏生成)。 - 返回:0 成功;非 0 失败。
const struct device_operations uart_dev_ops
- 描述:UART 设备操作表,经设备框架供上层以统一接口调用(open/read/write/ioctrl/close)。
ADC
void adc_init(void)
- 描述:初始化 ADC 硬件与轮询采样任务。系统启动早期调用。
void adc_vbg_init(void)
- 描述:初始化带隙基准(VBG),必须在任何电压换算前完成。
void adc_pmu_ch_select(u32 ch) / void adc_pmu_detect_en(u32 ch)
- 参数:
ch—ADC_PMU_CH_*子通道。 - 描述:选择/使能 PMU 内部通道检测(VBAT、SYSVDD、DTEMP 等)。
u32 adc_add_sample_ch(u32 ch) / u32 adc_remove_sample_ch(u32 ch)
- 参数:
ch—AD_CH_*通道编码。 - 返回:非 0 表示操作成功(0 可能表示通道已满或未注册)。
- 描述:将通道加入/移出周期轮询采样表;加入时受
ADC_MAX_CH容量限制。
u32 adc_get_value(u32 ch)
- 参数:
ch—AD_CH_*通道编码。 - 返回:ADC 原始采样码。
u32 adc_get_voltage(u32 ch)
- 参数:
ch—AD_CH_*通道编码。 - 返回:换算后的电压值(mV 量纲,基于 VBG 基准与分压系数)。
u8 get_sysvdd_aims_level(u16 aims_mv) / get_vdc13_aims_level(u16 aims_mv) / get_mvddio_aims_level(u16 aims_mv)
- 参数:
aims_mv— 目标电压(mV)。 - 返回:达到该目标所需的电源档位值,供电源管理 LDO/DCDC 配置。
u32 adc_check_vbat_lowpower(void)
- 返回:电池低电量状态标志,供低功耗策略判断。
PWM/LED
void pwm_led_init(const struct led_platform_data *user_data)
- 参数:
user_data— LED 板级配置(引脚、默认亮度)。 - 描述:初始化 PWM 定时器与 LED IO。
void pwm_led_mode_set(u8 fre_mode)
- 参数:
fre_mode— 闪烁频率模式。 - 描述:切换 LED 闪烁模式(不同频率档位)。
void pwm_led_clk_set(enum pwm_led_clk_source src)
- 参数:
src— PWM 计数时钟源。 - 描述:选择 PWM 时钟源,影响可实现的频率精度与功耗。
void pwm_led_one_flash_display(u8 led_index, u16 led0_bright, u16 led1_bright, u32 period, u32 start_light_time, u32 light_time)
- 描述:配置单闪时序:周期
period,起始延时start_light_time后点亮light_time;led0_bright/led1_bright为两路 PWM 亮度。
void pwm_led_double_flash_display(u8 led_index, u16 led0_bright, u16 led1_bright, u32 period, u32 first_light_time, u32 gap_time, u32 second_light_time)
- 描述:配置双闪时序:亮
first_light_time→ 灭gap_time→ 亮second_light_time,总周期period。
void pwm_led_set_on(void) / void pwm_led_set_off(void) / void pwm_led_io_max_drive_set(u8 strength)
- 描述:PWM 模块全局开关;IO 最大驱动电流档位设置。
SPI Flash 校验
void spi_crc16_set(u16 crc) / u16 spi_crc16_get(void)
- 描述:设置/读取 SPI 链路 CRC16 校验值,用于 Flash 读写完整性验证。
故障模式、边界情况与并发
故障模式
| 故障场景 | 表现 | 处理方式 |
|---|---|---|
| ADC 通道表溢出 | adc_add_sample_ch 在注册第 11 个通道时失败 | 调用方需检查返回值;ADC_MAX_CH = 10 是硬上限,应优先移除低频通道 |
| UART 中断号错配 | 收发无响应或数据错乱 | 由 UARTx_PLATFORM_DATA_END() 宏强制注入 IRQ_UARTx_IDX,从机制上消除手写错误 |
| VBG 基准未初始化 | adc_get_voltage 换算值异常(偏大/偏小) | 严格保证启动顺序:adc_vbg_init() 先于任何电压读取 |
| SPI Flash 读写丢数据 | OTA 升级失败、参数丢失 | 使用 spi_crc16_set/get 做写后回读校验,发现 CRC 不匹配则重写或报错 |
| LED 闪烁相位漂移 | 状态切换后闪烁节拍错乱 | 调用 pwm_led_display_mode_reset() 复位时序,从头开始新周期 |
边界情况与并发
- ADC 轮询模型天然串行:所有通道由单一轮询任务驱动,上层不直接操作 ADC 寄存器,因此无需加锁即可多任务共享 ADC;代价是单通道采样频率受通道总数制约。低时延应用应控制注册通道数。
- UART 中断上下文:
uart_dev_ops的读写接口在中断驱动下工作,上层在中断回调中做耗时操作会阻塞串口吞吐,应只做数据搬运、把业务处理放到任务上下文(如 AT 命令解析即采用此模式,见apps/spp_and_le/at_uart.c)。 - 低功耗约束:PWM 在睡眠前应通过
pwm_led_set_off()关闭,否则 PWM 时钟会阻止进入深睡眠并持续耗电;ADC 的 VBAT 检测是低功耗唤醒后首要任务之一,需在唤醒早期重新使能通道。 - PMU 通道分压:
ADC_PMU_CH_VBAT与ADC_PMU_CH_LDO5V按 1/4 分压,电压换算必须考虑该系数;直接使用adc_get_value拿原始码做阈值判断会得到错误结论,应使用adc_get_voltage。
性能与运维注意事项
- ADC 采样周期:轮询周期与注册通道数成正比。电量显示等低频需求建议间隔读取,不要把高频采样通道与低频通道混在同一轮询表中。
- UART 波特率与 FIFO:波特率在
uart_platform_data中静态配置,运行期修改波特率需要重新走ioctrl流程;AT 调试口与业务口建议分用不同控制器(共 3 个)。 - PWM 亮度与功耗权衡:
pwm_led_io_max_drive_set的驱动能力越高,LED 越亮但功耗越大;量产固件应按灯珠规格书设定档位,避免过驱动导致电池续航下降。 - 驱动自测:
cpu/bd29/uart_test.c提供 UART 驱动自测代码,硬件调试时可借此快速区分"驱动问题"与"硬件连接问题"。 - 配置工具:
cpu/bd29/tools/AC630N_config_tool/conf/source/board/uart_v1.lua表明 UART 配置也参与芯片配置工具(board 配置脚本)的生成流程,修改串口映射时应保持 C 平台数据与配置脚本一致。
扩展点
- 新增外设驱动:实现
struct device_operations(open/read/write/ioctrl/close),在 CPU 驱动层注册后即可被设备框架与上层统一使用——UART 驱动正是这一模式的范例。 - 新增 UART 控制器复用:
UART_OUTPORT_NUM = 4提供引脚重映射能力;扩展板级平台数据即可让同一控制器输出到不同 IO。 - 扩展 ADC 通道:通道编码空间(低 4 位 + 高 16 位 PMU 子通道)留有扩展余量;新增内部信号只需追加
AD_CH_*/ADC_PMU_CH_*宏定义与采样分支。 - 扩展 LED 时序:
pwm_led_*_display系列接口以参数化时序表达闪烁模式,新指示需求(如三闪、呼吸渐变)可在此基础上新增参数化接口,无需改动底层 PWM 定时器。
相关链接
- UART 设备抽象层:device/uart.h
- UART CPU 驱动实现:cpu/bd29/uart_dev.c、cpu/bd29/uart_test.c
- UART 应用示例(AT 命令):apps/spp_and_le/at_uart.c
- ADC 驱动 API:asm/adc_api.h
- PWM/LED 驱动 API:asm/pwm_led.h
- SPI Flash 设备:device/spiflash.h、CRC16:asm/crc16.h
- SDIO 主机初始化:device/sdio_host_init.h
- 外设驱动头文件汇总:include_lib/driver/cpu/bd29/asm/uart.h
说明:本文档基于已读取的驱动头文件与目录结构撰写。部分实现细节(如
uart_dev.c中寄存器级操作、struct uart_platform_data的完整字段列表)位于预编译库或未在本轮探索范围内读取的实现文件中,如需深入可在对应源码文件中进一步核实。