杰理 SDK 文档中心
首页
首页
  • 项目概览

    • AD16N 系列芯片与 SDK 能力总览
  • 快速开始

    • 环境搭建与编译工具链
    • 编译构建指南
    • 烧录与固件升级
  • SDK 工程架构

    • SDK 目录结构与模块分层
    • 构建系统与批处理工具
    • BSP 板级支持包
  • mbox_flash 小音箱应用

    • 应用初始化与启动流程
    • 应用配置系统
    • 按键、UI 与用户交互
  • 音频子系统

    • 音频解码框架与调度
    • 音频格式解码器实现
    • MIDI 合成与播放
    • 音频编码与录音
    • EQ/DRC 与音效处理
    • DAC/ADC 音频接口与采样
  • 存储与文件系统

    • 媒体 IO 抽象层 MIO
    • 存储设备驱动
    • 文件系统支持
  • 平台系统库

    • 系统基础服务
    • CPU 平台与运行库
    • 固件升级与更新机制
    • 蓝牙与扩展连接接口
  • 电源与低功耗管理

    • 电源管理与低功耗设计
    • 锂电池充电管理
  • 硬件与文档参考

    • SDK 文档中心与版本发布记录
    • 芯片数据手册与硬件设计参考

CPU 平台与运行库

AD16N(UC03 内核)的 CPU 平台层是整个 BSP 的最底层运行支撑:定义基础数据类型与平台宏、提供中断/临界区/自旋锁等并发原语、硬件随机数、以及基于 PLL 的时钟树管理和延时函数;并与预编译运行库 cpu_lib.a(含中断向量、任务切换、看门狗等固化代码)协同工作,为上层的驱动、音频、解码器等模块提供统一的 CPU 抽象。

Purpose and Scope

本页覆盖 AD16N 工程中与 CPU 平台和运行库直接相关的部分:

  • sdk/apps/include_lib/cpu/uc03/cpu.h:基础类型、平台常量(大小端、核数、中断嵌套深度)、任务切换宏、中断状态查询、原子读、自旋锁、临界区、断言宏。
  • sdk/apps/app/bsp/cpu/uc03/clock.c:系统时钟源选择、PLL 分频表、电压-频率上限表、udelay/mdelay 延时、总线时钟计算、Flash 控制器挂起。
  • sdk/apps/app/bsp/cpu/uc03/ 下的外设驱动层(ADC、IIC、LEDC、MCPWM、充电、LCD 段码、音频 ADC/DAC 等)的整体组织方式。
  • 预编译运行库 sdk/apps/include_lib/liba/uc03/mbox_flash/cpu_lib.a 的角色与接入方式。

不在此页范围:音频编解码(见解码器相关页面)、电源/充电策略细节、具体外设协议(IIC/SPI/UART 时序)的逐字节说明、以及 bd49 芯片平台的差异(仅在必要时交叉引用)。这些主题各自有独立的目录页。

相关页面指引:外设驱动细节参见「外设驱动」类目录页;音频通路参见「音频子系统」类目录页;系统启动与内存布局参见「启动与引导」类目录页。

Overview

AD16N 平台采用单核 DSP 架构(CPU_CORE_NUM = 1,即 q32DSP 单核),小端序(CPU_ENDIAN = LITTLE_ENDIAN),并支持 2 级中断嵌套(CPU_INT_NESTING = 2)。这意味着:

  • 单核模型下 current_cpu_id() 恒为 0,代码中仍保留了多核判断(#if CPU_CORE_NUM > 1)以备平台扩展;
  • 并发保护不需要 SMP 语义的复杂锁,自旋锁实现退化为基于 csync(缓存同步指令)的简单标志位互斥;
  • 临界区直接通过关闭/打开全局中断实现,开销极小,适合音频等对实时性敏感的场景。

运行库部分以静态库 cpu_lib.a 形式提供,包含 wdt_clear()(喂狗)、中断服务入口、以及由 CPU_TASK_SW 宏触发的任务切换辅助代码等固化实现,链接期随应用一并编入固件。

时钟子系统是 CPU 平台层的核心:系统上电后由 16 MHz 默认时钟(sys_clock)起步,clock.c 根据电压档位(1.20V/1.32V/1.38V)和 PLL IVCO 档位查表选择最高可用频率,并通过 CLK_CON1/SYS_DIV 寄存器实现 PLL→HSB→LSB 的分频树,最终由 sys_clock_get() 提供给全系统(延时、外设时序、功耗计算)使用。

Architecture

flowchart TD
    subgraph sg_App["应用层"]
        App["App / 业务模块"]
    end

    subgraph sg_BSP["BSP 平台层 (bsp/cpu/uc03)"]
        Clock["clock.c 时钟管理"]
        Adc["adc_api.c"]
        Iic["iic_api.c / iic_hw / iic_soft"]
        Ledc["ledc.c"]
        Mcpwm["mcpwm.c"]
        Charge["charge.c"]
        Audio["audio_adc/dac_cpu.c"]
        Dev["device.c"]
    end

    subgraph sg_Cpu["CPU 平台抽象 (include_lib/cpu/uc03)"]
        CpuH["cpu.h 类型/宏/并发原语"]
        CpuLib["cpu_lib.a 预编译运行库<br/>(中断向量/任务切换/喂狗)"]
    end

    subgraph sg_Hw["硬件寄存器层"]
        Regs["JL_CLOCK / JL_PLL0 / JL_RAND / JL_FPGA<br/>CSFR 寄存器"]
    end

    App --> Clock
    App --> Adc
    App --> Iic
    App --> Ledc
    App --> Mcpwm
    App --> Charge
    App --> Audio
    App --> Dev

    Clock --> CpuH
    Adc --> CpuH
    Iic --> CpuH
    Audio --> CpuH
    Dev --> CpuH

    CpuH --> Regs
    CpuLib --> Regs
    CpuLib --> CpuH
    Clock --> Regs

架构说明:cpu.h 是所有 C 代码的公共依赖——即使只是取用 u8/u32 类型定义,也必须包含它。cpu_lib.a 与 cpu.h 中的宏(CPU_TASK_SW、wdt_clear)配套使用:宏负责写入 ILAT_SET 触发任务切换,库内固化代码负责实际的中断现场保存与恢复。clock.c 是唯一直接驱动 JL_CLOCK/JL_PLL0 寄存器并向外暴露 sys_clock_get() 的模块,所有依赖时间概念的模块(udelay、外设波特率、音频采样率)都间接依赖它。

主内容:CPU 平台抽象(cpu.h)

基础类型与平台常量

cpu.h 在 #ifndef __ASSEMBLY__ 保护下定义了一套与编译器宽度绑定的定长整型,供汇编与 C 两侧共享平台宏:

typedef unsigned char    u8;
typedef char             s8;
typedef unsigned short   u16;
typedef signed short     s16;
typedef unsigned int     u32;
typedef signed int       s32;
typedef unsigned long long u64;
typedef u32              FOURCC;
typedef long long        s64;

#define CPU_ENDIAN       LITTLE_ENDIAN   // 0x4576
#define CPU_CORE_NUM     1               // 单核
#define CPU_INT_NESTING  2               // 支持 2 级中断嵌套

来源:cpu.h

设计意图:全工程(BSP、音频、解码器、应用)统一使用这些类型,避免 int 宽度随编译目标漂移;FOURCC 用于四字符文件格式标签。CPU_ENDIAN 以魔数而非 0/1 表示大小端,可在编译期检测配置与硬件是否一致。

任务切换与中断深度

#define  CPU_TASK_SW(a)  \
    do { \
        extern void wdt_clear(); \
        q32DSP(0)->ILAT_SET |= BIT(7-a); \
        wdt_clear(); \
    } while (0)

#define  CPU_INT_NESTING 2

来源:cpu.h

CPU_TASK_SW(a) 是协作式任务切换的原语:向 ILAT_SET 置位触发软件中断,中断服务程序(固化在 cpu_lib.a)保存当前任务上下文并切换到任务 a。宏内同时调用 wdt_clear() 喂狗,防止切换路径上的看门狗误复位——这是把"喂狗"与"调度"耦合的务实设计:任何长时间运行的任务切换都不会饿死看门狗。

中断状态查询与临界区

static inline int cpu_in_irq()
{
    int flag;
    __asm__ volatile("%0 = icfg" : "=r"(flag));
    return flag & 0xff;
}

static inline int cpu_irq_disabled()
{
    int flag;
    __asm__ volatile("%0 = icfg" : "=r"(flag));
    return (flag & 0x300) != 0x300;
}

#define CPU_CRITICAL_ENTER()  do { local_irq_disable(); } while(0)
#define CPU_CRITICAL_EXIT()   do { local_irq_enable();  } while(0)

来源:cpu.h

通过读 icfg 特殊寄存器判断当前是否处于中断上下文(低 8 位)以及中断是否被屏蔽(0x300 掩码位)。CPU_CRITICAL_ENTER/EXIT 是平台统一临界区接口,底层就是 local_irq_disable/enable()。由于 CPU_INT_NESTING = 2,中断服务程序内再进入临界区是被支持的,但临界区内应避免长时间占用,否则会推迟 2 级中断响应。

原子读与自旋锁

#define arch_atomic_read(v) \
    ({ \
        __asm_csync(); \
        (*(volatile int *)&(v)->counter); \
     })

#define arch_spin_lock(lock) \
    do { \
        int ret = false; \
        __asm_csync(); \
        if (!(lock)->rwlock) { \
            ret = true; \
            (lock)->rwlock = 1; \
        } \
        if (ret) \
            break; \
    } while(1)

#define arch_spin_unlock(lock) \
    do { (lock)->rwlock = 0; } while(0)

来源:cpu.h

__asm_csync() 是 DSP 的缓存同步指令,保证后续内存访问看到一致视图。单核平台上自旋锁实际只是"标志位互斥"——因为没有第二个核会同时竞争,锁的作用是防止中断上下文与任务上下文并发访问共享数据(配合临界区使用)。arch_spin_trylock 与 arch_spin_lock 的唯一区别是前者不等候、一次尝试即返回。

硬件随机数

static inline u32 rand32()    { return JL_RAND->R64L; }
static inline u32 rand32_2()  { return JL_RAND->R64H; }

来源:cpu.h

芯片内置硬件真随机数发生器(JL_RAND 寄存器组),64 位随机值分为低/高 32 位两次读取。相比软件 LCG,硬件 RNG 适合用于安全校验、配对码生成等场景;注意两个调用之间 RNG 会持续刷新,连续读取两次得到的是不同的随机数。

断言宏

#define ASSERT(a,...) \
    do { \
        if(!(a)){ \
            printf("ASSERT: "#a" "__VA_ARGS__); \
        } \
    }while(0);

来源:cpu.h

平台断言直接输出到 printf 而非触发复位,便于在日志中定位失败条件;#a 字符串化保证日志打印原始表达式。

主内容:时钟系统(clock.c)

时钟源与频率上限

#define     SPI_TSHSL        80
static u32 LSB_MAX_CLK = 96 * MHz_UNIT;
static u32 SPI_MAX_CLK = 50 * MHz_UNIT;
static u32 OTP_MAX_CLK = 21 * MHz_UNIT;

static u32 sys_clock = 16000000;   // 上电默认 16MHz

const u32 pll_clock_tab0[4][6] = {
    //0  d3p5        d2p5        d2p0        d1p5        d1p0
    {0,  27400000L,  38400000L,  48000000L,  64000000L,  96000000L},
    {0,  41100000L,  57600000L,  72000000L,  96000000L,  144000000L},
    {0,  54800000L,  76800000L,  96000000L,  128000000L, 192000000L},
    {0,  68600000L,  96000000L,  120000000L, 160000000L, 240000000L}
};

const u32 pll_max_clock_tab[MAX_RANGE][4] = {
    //dvdd                sys max     lsb max     otp max
    {SYSVDD_VOL_SEL_120V, 128000000L, 64000000L,  21333334L}, // 1.20v
    {SYSVDD_VOL_SEL_132V, 160000000L, 80000000L,  20000000L}, // 1.32v
    {SYSVDD_VOL_SEL_138V, 192000000L, 96000000L,  19200000L}  // 1.38v
};

来源:clock.c

设计意图:电压决定频率上限。pll_max_clock_tab 把系统最高频率与 SYSVDD 电压档位绑定——电压越高可跑得越快,代价是功耗上升。pll_clock_tab0 是 PLL 输出频率查找表,行索引为 IVCO 档位(从 PLL_CON0 读出),列索引为 PLL 分频选择(d3p5/d2p5/d2p0/d1p5/d1p0)。clock_sys_src_use_lrc_hw = 1 表示系统时钟源硬件上使用 LRC 作为低速基准。

时钟读取与分频树

int get_sys_pll_sel()          { return JL_CLOCK->CLK_CON1 & 0x7; }

u32 sys_clock_get(void)        { return sys_clock; }

u32 get_sys_pll_clk()
{
    u32 t_sel;
    u32 clock = 0;
    u8 pll_ivco = (JL_PLL0->PLL_CON0 >> 11) & 0x07;
    if (pll_ivco > 2) {
        pll_ivco /= 2;
    }
    t_sel = get_sys_pll_sel();
    clock = pll_clock_tab0[pll_ivco][t_sel];
    if (0 == clock) {
        log_error(" sys clock info err\n");
        return 0;
    }
    u32 t_diva = div_taba[(JL_CLOCK->CLK_CON1 >> 4) & 0x3];  // {1,3,5,7}
    u32 t_divb = div_tabb[(JL_CLOCK->CLK_CON1 >> 6) & 0x3];  // {1,2,4,8}
    return clock / (t_diva * t_divb);
}

u32 sys_clock_peration(void)
{
    u32 hsb_div = (JL_CLOCK->SYS_DIV & 0xff) + 1;
    u32 pll_sys_clk = get_sys_pll_clk();
    u32 hsb_clk = pll_sys_clk / (hsb_div);
    return hsb_clk;
}

u32 lsb_clk_get(void)
{
    u32 div42 = ((JL_CLOCK->SYS_DIV >> 8) & 0xf) + 1;
    u32 lsb_clk = sys_clock_get() / (div42);
    return lsb_clk;
}

来源:clock.c

分频树为 PLL → HSB(高速总线)→ LSB(低速总线):get_sys_pll_clk() 反推 PLL 实际输出(IVCO 档位 × 分频选择 ÷ div_a×div_b),sys_clock_peration() 计算 HSB 时钟(再除 hsb_div),lsb_clk_get() 计算 LSB 时钟。外设驱动根据目标频率(如 SPI 50MHz、LSB 96MHz、OTP 21MHz 上限)选择合适的除数和电压档位。

延时函数

AT(.clock.text.cache.L1)
void udelay(u32 us)
{
    u32 mips = us * (sys_clock / 1000000 / 8);
    while (mips--) {
        asm("nop");
    }
}

AT(.clock.text.cache.L1)
void mdelay(u32 ms)
{
    for (int i = 0; i < ms; i++) {
        udelay(1000);
    }
}

来源:clock.c

udelay 是软件忙等延时:根据当前 sys_clock 换算需要的 NOP 循环次数,因此对时钟精度敏感——任何修改 sys_clock 的代码必须同步更新该变量。AT(.clock.text.cache.L1) 将延时代码固定放置在 L1 指令缓存区,避免延时过程中因缓存未命中导致的抖动(延时本身被用于外设时序,如 SPI Flash 时序 SPI_TSHSL=80)。

主内容:外设驱动层与预编译运行库

bsp/cpu/uc03/ 是 CPU 平台下的外设驱动集合,每个模块面向一个外设族:

文件职责
adc_api.c/h通用 ADC 采样 API
audio_adc_cpu.c、audio_dac_cpu.c、audio_dac_api.c音频 ADC/DAC 底层(与音频子系统页面呼应)
iic_api.c、iic_hw.c、iic_soft.c、iic_eeprom.cIIC 总线 API + 硬件/软件两种实现 + EEPROM 封装
ledc.cLED 恒流驱动(PWM 调光)
mcpwm.c电机控制 PWM(无刷电机驱动)
charge.c/h充电管理
lcd_seg4x8_driver.c4x8 段码 LCD 驱动
device.c设备抽象/注册
periph_demo/IIC/SPI/UART 外设使用示例

来源:bsp/cpu/uc03 目录

预编译运行库 sdk/apps/include_lib/liba/uc03/mbox_flash/cpu_lib.a 以静态库形式随 SDK 发布,配合 cpu.h 中的宏一起工作:CPU_TASK_SW 中的 wdt_clear()、中断入口、local_irq_enable/disable() 的汇编实现、任务上下文切换等固化逻辑都在该库内。应用代码通过声明 extern(如 extern void wdt_clear();)调用,链接期由构建脚本将 .a 与用户代码合并为最终固件。同一平台目录下还存在 include_lib/cpu/bd49/cpu.h,表明 SDK 同时支持 bd49 芯片平台,两个平台的 cpu.h 接口保持一致以便 BSP 上层代码可移植。

核心流程:时钟初始化与系统启动

sequenceDiagram
    participant Boot as "启动代码 (cpu_lib.a)"
    participant Clock as "clock.c"
    participant CpuH as "cpu.h 原语"
    participant Pll as "JL_PLL0/JL_CLOCK 寄存器"
    participant App as "应用/外设驱动"

    Boot->>Clock: 调用时钟初始化 (读 efuse/电压档位)
    Clock->>Pll: 读 PLL_CON0 得到 IVCO 档位
    Clock->>Pll: 读 CLK_CON1 得到分频选择
    Clock->>Clock: 查 pll_max_clock_tab 按电压限频
    Clock->>Clock: 更新全局 sys_clock 变量
    Clock-->>Boot: 返回当前系统时钟
    Boot->>CpuH: CPU_CRITICAL_ENTER() 配置外设
    CpuH->>CpuH: local_irq_disable() (写 icfg)
    Boot->>App: 外设初始化 (读 sys_clock_get / lsb_clk_get)
    App->>Clock: udelay/mdelay 忙等延时
    Clock->>Clock: 按 sys_clock 换算 NOP 次数
    App->>CpuH: CPU_CRITICAL_EXIT() 恢复中断

流程说明:

  1. 上电后 cpu_lib.a 中的启动代码先建立最小运行环境,再调用 clock.c 完成时钟树配置;
  2. clock.c 通过寄存器反推 PLL 当前输出,并按电压档位(SYSVDD_VOL_SEL_*)限制最高频率,防止超压超频损坏芯片;
  3. 全局 sys_clock 变量被同步更新,此后 udelay/mdelay/sys_clock_get 等所有依赖时钟的接口都以此为准;
  4. 外设初始化期间使用 CPU_CRITICAL_ENTER/EXIT 保护寄存器配置序列,避免中断插入破坏配置一致性;
  5. 运行时任务切换通过 CPU_TASK_SW 触发 ILAT_SET 软件中断,中断现场由运行库保存/恢复,宏内附带喂狗保证调度路径不触发看门狗。

用法示例

示例 1:临界区保护共享数据(中断与任务并发)

#define CPU_CRITICAL_ENTER()  \
    do { \
        local_irq_disable(); \
    }while(0)

#define CPU_CRITICAL_EXIT() \
    do { \
        local_irq_enable(); \
    }while(0)

来源:cpu.h

典型用法:任务上下文修改被中断服务程序读取的计数/状态变量时,先 CPU_CRITICAL_ENTER() 再修改再 CPU_CRITICAL_EXIT();由于平台支持 2 级中断嵌套,临界区应尽量短小。

示例 2:读取系统时钟与硬件随机数

__attribute__((always_inline))
u32 sys_clock_get(void)
{
    return sys_clock;
}

static inline u32 rand32()
{
    return JL_RAND->R64L;
}

来源:clock.c 与 cpu.h

sys_clock_get() 是内联的全局变量读取,零开销,任何模块(如波特率计算、音频采样率校准)都可直接调用;rand32() 从硬件 RNG 读取 64 位随机值的低半部分,用于配对/校验场景。

示例 3:总线时钟推导(外设频率规划)

u32 lsb_clk_get(void)
{
    u32 div42 = ((JL_CLOCK->SYS_DIV >> 8) & 0xf) + 1;
    u32 lsb_clk = sys_clock_get() / (div42);
    return lsb_clk;
}

来源:clock.c

外设驱动在初始化时应以 lsb_clk_get()/sys_clock_peration() 的返回值作为波特率/分频计算基准,而不是硬编码频率——这样系统降频或超频后外设时序仍然正确。

配置选项

CPU 平台层的"配置"以源码常量和查表为主,构建期决定:

配置项类型默认值说明
CPU_CORE_NUM宏1核数;为 1 时 current_cpu_id() 恒返回 0
CPU_INT_NESTING宏2允许的中断嵌套深度
CPU_ENDIAN宏LITTLE_ENDIAN平台字节序(0x4576 魔数)
sys_clock全局变量16000000上电默认系统时钟(Hz),时钟初始化后更新
LSB_MAX_CLK静态变量96 * MHz_UNITLSB 总线频率上限
SPI_MAX_CLK静态变量50 * MHz_UNITSPI 外设频率上限
OTP_MAX_CLK静态变量21 * MHz_UNITOTP 读取频率上限
SPI_TSHSL宏80SPI Flash 时序参数(片选建立/保持)
pll_max_clock_tab查表见下表电压档位 → 系统/LSB/OTP 最高频率
clock_sys_src_use_lrc_hw常量1系统时钟源硬件使用 LRC 基准

电压-频率上限表(pll_max_clock_tab):

电压档位系统最高频率LSB 最高频率OTP 最高频率
SYSVDD_VOL_SEL_120V (1.20V)128 MHz64 MHz21.3 MHz
SYSVDD_VOL_SEL_132V (1.32V)160 MHz80 MHz20 MHz
SYSVDD_VOL_SEL_138V (1.38V)192 MHz96 MHz19.2 MHz

API 参考

平台原语(cpu.h,均为 static inline 或宏)

接口签名说明
current_cpu_idint current_cpu_id(void)返回当前核 ID;单核平台恒为 0
cpu_in_irqint cpu_in_irq(void)读 icfg 低 8 位,非 0 表示处于中断上下文
cpu_irq_disabledint cpu_irq_disabled(void)检查 icfg 的 0x300 掩码位,判断中断是否被屏蔽
rand32u32 rand32(void)硬件 RNG 低 32 位随机数
rand32_2u32 rand32_2(void)硬件 RNG 高 32 位随机数
CPU_CRITICAL_ENTER/EXIT宏关/开全局中断进入/退出临界区
arch_atomic_read宏带 csync 的原子读(counter 字段)
arch_spin_lock/unlock/trylock宏自旋锁三件套(基于 rwlock 标志位)
ASSERT(a, ...)宏条件失败时 printf 打印表达式与附加信息
CPU_TASK_SW(a)宏触发 ILAT_SET 软件中断切换至任务 a,并喂狗

时钟接口(clock.c)

接口签名返回/说明
sys_clock_getu32 sys_clock_get(void)当前系统时钟 Hz(内联读全局变量)
get_sys_pll_selint get_sys_pll_sel(void)CLK_CON1 低 3 位 PLL 分频选择
get_sys_pll_clku32 get_sys_pll_clk(void)反推 PLL 实际输出频率;查表为 0 时打错误日志并返回 0
sys_clock_perationu32 sys_clock_peration(void)HSB 总线时钟 = PLL ÷ (hsb_div+1)
lsb_clk_getu32 lsb_clk_get(void)LSB 总线时钟 = sys_clock ÷ (div42+1)
udelayvoid udelay(u32 us)软件忙等微秒延时(L1 缓存驻留)
mdelayvoid mdelay(u32 ms)毫秒延时,内部循环调用 udelay(1000)
sfc_suspendvoid sfc_suspend(u32 enable_spi)挂起 SPI Flash 控制器(时钟切换期间保护 Flash 时序)

故障模式、边界与并发

时钟配置错误

get_sys_pll_clk() 查表得到 0(IVCO 档位或分频选择非法)时,记录 log_error(" sys clock info err\n") 并返回 0。上游风险:若调用方未检查返回值而继续做除法,会得到 0 频率的系统时钟,导致所有基于 sys_clock 的延时和波特率失效。实际代码中该错误通常意味着 efuse/寄存器损坏或配置表与芯片版本不匹配。

电压-频率越界

pll_max_clock_tab 明确给出各电压档位下系统/LSB/OTP 的频率上限。超过上限运行属于未定义行为(时序违例),典型表现为 Flash 读错、外设偶发失败。OTP 上限随电压升高反而降低(21.3→20→19.2 MHz),说明 OTP 读取时序与电压弱相关,规划 OTP 频率时不能只按系统频率上限推断。

并发与中断嵌套

  • 临界区非可重入:CPU_CRITICAL_ENTER 直接关中断,不支持嵌套计数;嵌套使用会导致提前开中断。由于 CPU_INT_NESTING = 2,中断服务程序中再次进入临界区是合法的,但必须保证成对退出。
  • 自旋锁无等待上限:arch_spin_lock 是忙等循环,若持有者在关中断状态下长时间运行,其他任务/中断会饥饿。使用原则:锁内只做短操作,配合临界区保护。
  • 延时函数中断敏感:udelay 是纯 NOP 忙等,不依赖定时器,因此不会被中断打断而"超时",但耗时与 sys_clock 强相关——任何动态变频必须同步更新全局 sys_clock,否则延时偏差。

单核假设

CPU_CORE_NUM = 1 下 current_cpu_id() 恒为 0;代码保留了多核编译分支(读 cnum 寄存器),若未来芯片核数变化,仅需修改该宏并保证 cpu_lib.a 配套升级。

性能与运维要点

  • 延时精度:udelay/mdelay 被放置在 L1 缓存段(AT(.clock.text.cache.L1)),避免取指抖动,适用于对时序敏感的外设初始化(如 SPI Flash SPI_TSHSL)。但忙等不省电,长延时场景应改用任务睡眠。
  • 热路径:sys_clock_get、lsb_clk_get、arch_atomic_read 均为内联/宏实现,零函数调用开销,可在音频中断等热路径中直接使用。
  • 随机数:硬件 RNG 读取为寄存器访问,开销低;但 RNG 模块在上电后需要稳定时间,量产测试中应在初始化完成后延时再取数(配合 mdelay)。
  • 运行库版本匹配:cpu_lib.a 与 cpu.h 必须来自同一 SDK 版本,宏展开与库内汇编(中断向量、任务切换)接口不一致会导致难以排查的链接期/运行期故障。

扩展点

  • 新增外设驱动:在 bsp/cpu/uc03/ 下新增 xxx_api.c/h,对外暴露 xxx_init/xxx_read/xxx_write,内部使用 cpu.h 临界区原语保护寄存器操作,频率计算一律通过 sys_clock_get/lsb_clk_get 推导。
  • 多核适配:修改 CPU_CORE_NUM 并实现 current_cpu_id() 的多核分支(读 cnum),同时需要运行库提供多核调度支持。
  • 频率策略:替换/扩展 pll_max_clock_tab 可定制电压-频率策略(如低功耗模式限制最大频率);clock_sys_src_use_lrc_hw 可切换时钟源硬件策略。
  • 断言策略:ASSERT 目前只打印不复位;可按产品需求改为打印后 while(1)(源码中 cpu_reset 的注释实现即示例),或接入日志系统持久化。

相关链接

  • cpu.h(UC03 平台头文件)
  • clock.c(时钟管理实现)
  • bsp/cpu/uc03 外设驱动目录
  • cpu_lib.a(预编译运行库)
  • bd49 平台 cpu.h(跨平台对比)
  • cpu_config.c(mbox_flash 场景下的 CPU 配置)
  • 外设驱动细节 → 参见「外设驱动」目录页
  • 音频通路与解码 → 参见「音频子系统」「解码器」目录页
Prev
系统基础服务
Next
固件升级与更新机制