杰理 SDK 文档中心
首页
首页
  • SDK 概述与快速开始

    • SDK 概览与 AC791N 芯片平台
    • 环境搭建与编译指南
    • 烧录与固件升级
    • 工程结构导览
  • 产品方案应用

    • WiFi 摄像头方案
    • WiFi IPC 可视对讲方案
    • WiFi 故事机方案
    • 扫码枪 HID 方案
    • 开发板示例工程
  • 公共应用组件

    • 语音识别 ASR 引擎
    • LLM 与 AI 语音助手接入
    • 摄像头传感器驱动
    • UI 显示框架与驱动
    • USB 主机与设备栈
    • 文件系统与存储管理
    • 系统服务与外设管理
    • 生产测试与射频工具
  • 蓝牙协议栈

    • 经典蓝牙 BR/EDR
    • BLE 低功耗蓝牙
    • 蓝牙 Mesh 网络
    • 蓝牙扩展协议(RCSP/广播/无线麦克风)
  • WiFi 与网络协议栈

    • WiFi 驱动与网络模式
    • lwIP TCP/IP 协议栈
    • 网络安全与加密库
    • 应用层网络协议
    • 流媒体与音视频传输
    • 云平台接入 SDK
    • P2P 远程访问与设备互联
  • 芯片平台与驱动

    • wl82 平台与硬件加速
    • 外设驱动框架
    • 平台配置与固件打包工具
  • 媒体与音频引擎

    • 音频编解码与音源
    • 音效处理引擎
    • 视频与图像处理
  • 操作系统与运行时

    • 实时操作系统与 POSIX 层
    • C/C++ 运行时库
  • 开发资源与文档

    • 文档与规格书
    • 公共示例工程
    • UI 资源工程与打包
    • SDK 辅助工具与脚本

开发板示例工程

本页介绍 AC79 系列 AIoT SDK 中位于 apps/demo/ 的开发板示例工程(demo projects)以及 apps/common/example/ 下的外设例程库,说明每个工程的定位、通用工程结构、启动流程、配置方式与构建方法。

Purpose and Scope

本页面向需要在真实开发板上快速上手或作为二次开发起点的开发者,覆盖以下内容:

  • apps/demo/ 下 11 个示例工程(demo_hello、demo_DevKitBoard、demo_audio、demo_ble、demo_edr、demo_wifi、demo_wifi_ext、demo_video、demo_uvc、demo_ui、demo_matter)的功能定位与源码组织;
  • 每个工程共有的结构:app_main.c 入口、board/<板型>/ 板级目录、include/app_config.h 配置头文件;
  • SDK 的启动机制:irq_info_table 中断注册表与 task_info_table 任务注册表如何被内核消费,app_main() 如何被调度;
  • apps/common/example/readme.md 例程总览所罗列的外设例程(GPIO、TIMER、RTC、ADC、PWM、SPI、IIC、UART 等)。

不纳入本页范围、由兄弟页面承载的内容:各外设例程的逐项 API 细节(见各例程子目录 readme,如 uart/readme.md)、ASR/音频算法等公共组件内部实现(属于 apps/common 组件文档)、SDK 烧录与量产工具链说明。

Overview

AC79 SDK 采用「示例工程 + 公共组件 + 外设例程」三层的组织方式:

  1. 示例工程层(apps/demo):每个目录是一个完整可编译、可烧录到开发板的独立应用,覆盖音频、蓝牙、WiFi、视频、UI、Matter 等不同产品形态,是二次开发最直接的起点。
  2. 外设例程层(apps/common/example):以 readme.md 为总览索引,按外设/功能模块组织大量可复制的最小例程(如 GPIO 输入输出与中断、定时器捕获、RTC 闹钟、ADC 采压、PWM 呼吸灯、SPI 外挂 Flash、IIC 外接 EEPROM、UART 打印/通信等),并给出「常见问题」条目,帮助开发者绕开硬件陷阱。
  3. 公共组件层(apps/common):ASR(aisp/jlkws/roobo/wanson)、音频处理、网络协议栈等公共能力,供示例工程调用。

示例工程的启动不是传统的裸机 main 顺序执行,而是由内核依据两张静态表驱动:irq_info_table 决定中断注册到哪个 CPU 核与优先级,task_info_table 决定创建哪些任务、各自优先级与栈大小。开发者修改这两张表即可裁剪/增加系统能力,这是理解整个 SDK 工程的关键入口。

Architecture

flowchart TD
    subgraph sg_demo["apps/demo 示例工程层"]
        DemoHello["demo_hello<br/>最小系统"]
        DemoDevKit["demo_DevKitBoard<br/>开发板综合工程"]
        DemoAudio["demo_audio<br/>音频/智能音箱"]
        DemoBle["demo_ble<br/>BLE 蓝牙"]
        DemoEdr["demo_edr<br/>经典蓝牙 EDR"]
        DemoWifi["demo_wifi<br/>WiFi 联网"]
        DemoWifiExt["demo_wifi_ext<br/>WiFi 扩展"]
        DemoVideo["demo_video<br/>视频采集"]
        DemoUvc["demo_uvc<br/>UVC 摄像头"]
        DemoUi["demo_ui<br/>LCD UI 显示"]
        DemoMatter["demo_matter<br/>Matter 协议"]
    end

    subgraph sg_example["apps/common/example 例程库"]
        ExCpu["CPU 外设例程<br/>GPIO/TIMER/RTC/ADC/PWM/SPI/IIC/UART"]
        ExOther["音频/视频/网络等例程"]
    end

    subgraph sg_common["apps/common 公共组件"]
        CommonLib["公共组件<br/>ASR/音频/网络/存储/蓝牙"]
        Kernel["系统内核<br/>irq_info_table/task_info_table"]
    end

    DemoHello --> CommonLib
    DemoDevKit --> CommonLib
    DemoAudio --> CommonLib
    DemoBle --> CommonLib
    DemoEdr --> CommonLib
    DemoWifi --> CommonLib
    DemoWifiExt --> CommonLib
    DemoVideo --> CommonLib
    DemoUvc --> CommonLib
    DemoUi --> CommonLib
    DemoMatter --> CommonLib
    ExCpu --> CommonLib
    ExOther --> CommonLib
    DemoHello --> Kernel
    DemoDevKit --> Kernel

上图展示三层结构:上层每个示例工程都通过 app_main.c 接入系统内核并复用 apps/common 公共组件;apps/common/example 例程库则为外设操作提供可直接摘抄的参考实现。示例工程之间通过 app_config.h 中的编译宏(如 CONFIG_BT_ENABLE、CONFIG_WIFI_ENABLE、CONFIG_UI_ENABLE)来开启或裁剪对应组件,因此一个工程目录往往同时包含多套能力的代码,最终产物由宏开关决定。

示例工程清单

apps/demo/ 下共 11 个工程,每个工程都以 app_main.c 作为应用入口。下表依据仓库实际目录整理:

工程目录功能定位关键源码文件
demo_hello最小系统示例,外设裁剪最少,适合验证工具链与烧录流程app_main.c
demo_DevKitBoard开发板综合工程,集成音频、WiFi、蓝牙、视频、UI 等全能力app_main.c
demo_audio音频应用工程:AI 音箱、本地音乐、网络音乐、录音,带模式切换app_main.c、demo/ai_speaker.c、demo/local_music.c、demo/net_music.c、demo/recorder.c、demo/mode.c、wifi_app_task.c
demo_bleBLE 低功耗蓝牙工程app_main.c、bt_ble/ble.c
demo_edr经典蓝牙(EDR)工程app_main.c
demo_wifiWiFi 联网基础工程app_main.c
demo_wifi_extWiFi 扩展应用工程app_main.c
demo_video视频采集/处理工程app_main.c
demo_uvcUVC 摄像头(USB Video Class)工程app_main.c
demo_uiLCD/UI 显示工程app_main.c
demo_matterMatter 智能家居互联协议工程app_main.c

各工程均包含 board/<板型>/ 板级目录(例如 demo_audio 与 demo_ble 都带有 board/wl82/,内含 board.c、Makefile 以及 CodeBlocks 工程文件 AC791N_DEMO_DEMO_AUDIO.cbp / AC791N_DEMO_DEMO_BLE.cbp)和 include/app_config.h 功能配置头文件。

工程通用结构

一个典型示例工程的目录布局如下(以 demo_audio 为例):

  • app_main.c:应用主入口,包含 irq_info_table、task_info_table 与应用逻辑;
  • board/wl82/:板级适配,board.c 做引脚/外设初始化,Makefile 定义编译目标,.cbp 为 CodeBlocks IDE 工程;
  • demo/:业务模块源码(如 ai_speaker、local_music、net_music、recorder、mode 模式切换);
  • include/app_config.h:宏开关集中地,控制外设与协议栈的裁剪;
  • 顶层 wifi_app_task.c 等:工程特有任务(如 WiFi 应用任务)。

这种「入口表驱动 + 板级目录 + 宏配置」的结构设计意图是:换板不换工程、换功能不改入口。适配新开发板只需替换 board/ 目录,裁剪功能只需改 app_config.h 与两张注册表,业务代码保持稳定。

启动流程

sequenceDiagram
    participant ROM as Boot ROM
    participant OS as 系统内核
    participant Irq as 中断控制器
    participant Init as init 任务
    participant App as app_core 任务

    ROM->>OS: 上电/复位,加载固件
    OS->>OS: 读取 irq_info_table 注册中断到 CPU0/1
    OS->>Irq: 设置 IRQ 优先级(0-7)与归属核
    OS->>OS: 读取 task_info_table 创建系统任务
    OS->>Init: 调度 init 任务(优先级30,栈512)
    Init->>Init: 板级初始化(board.c)
    Init->>App: 创建并切换 app_core 任务
    App->>App: 执行 app_main() 应用主流程
    App->>App: 事件循环/模式分发

启动链路可以概括为四步:

  1. 中断注册:内核按 irq_info_table 中每一项的「中断号、优先级(0-7)、注册 CPU 核」配置中断控制器,支持把特定中断强制绑定到 CPU0 或 CPU1(例如 IRQ_SOFT5_IDX 绑 CPU0、IRQ_SOFT4_IDX 绑 CPU1 用于不可屏蔽中断场景)。
  2. 任务创建:内核按 task_info_table 创建任务,每个条目给出任务名、优先级、栈大小与配额(quota)。任务名前缀 #C0 表示强制调度到 CPU0(如双核下的 #C0usb_msd0、#C0btctrler)。
  3. 板级初始化:最高优先级的 init 任务完成时钟、引脚、存储等板级外设初始化。
  4. 业务启动:app_core 任务进入 app_main(),示例工程在此注册业务回调、启动模式分发与事件循环。

核心代码走读:以 demo_DevKitBoard 为例

demo_DevKitBoard 是开发板综合工程,其 app_main.c 完整展示了「中断表 + 任务表 + 应用入口」的 SDK 标准写法。

中断注册表 irq_info_table

/*中断列表 */
const struct irq_info irq_info_table[] = {
    //中断号   //优先级0-7   //注册的cpu(0或1)
#ifdef CONFIG_IPMASK_ENABLE
    //不可屏蔽中断方法:支持写flash,但中断函数和调用函数和const要全部放在内部ram
    { IRQ_SOFT5_IDX,      6,   0    }, //此中断强制注册到cpu0
    { IRQ_SOFT4_IDX,      6,   1    }, //此中断强制注册到cpu1
#if 0 //如下,SPI1使用不可屏蔽中断设置,优先级固定7
    { IRQ_SPI1_IDX,      7,   1    },//中断强制注册到cpu0/1
#endif
#endif
#if CPU_CORE_NUM == 1
    { IRQ_SOFT5_IDX,      7,   0    }, //此中断强制注册到cpu0
    { IRQ_SOFT4_IDX,      7,   1    }, //此中断强制注册到cpu1
    { -2,     \t\t\t-2,   -2   },//如果加入了该行, 那么只有该行之前的中断注册到对应核, 其他所有中断强制注册到CPU0
#endif
    { -1,     -1,    -1    },
};

Source: app_main.c

设计要点:

  • 表以 { -1, -1, -1 } 结尾作为终止标记;
  • 单核(CPU_CORE_NUM == 1)下可通过插入 { -2, -2, -2 } 行,把该行之后所有中断强制注册到 CPU0,用于满足某些外设驱动对中断亲和性的要求;
  • CONFIG_IPMASK_ENABLE 开启不可屏蔽中断模式,此时中断服务函数及其调用的函数、常量必须全部放在内部 RAM,才能保证写 Flash 等临界操作不被中断打断。

任务注册表 task_info_table

/*任务列表 */
const struct task_info task_info_table[] = {
    {"init",                30,     512,   256   },
    {"app_core",            15,     1024,   256   },
    {"sys_event",           29,      512,   0     },
    {"systimer",            14,      256,   0     },
    {"sys_timer",            9,      512,   64    },
    {"mp3_encoder",         13,      768,   0     },
    {"jla_encoder",         13,      768,   0     },
    {"audio_server",        16,      512,   64    },
    {"audio_mix",           28,      512,   0     },
    {"audio_decoder",       30,     1024,   64    },
    {"audio_encoder",       12,      384,   64    },
    {"speex_encoder",       10,     1024,   0     },
    {"opus_encoder",        10,     1536,   0     },
    {"ogg_encoder",         10,     1536,   0     },
    {"vir_dev_task",        13,      256,   0     },
    {"wechat_task",         18,     2048,   64    },
    ...
    {"usb_server",          20,     1024,   64    },
#if CPU_CORE_NUM > 1
    {"#C0usb_msd0",          1,      512,   128   },
#else
    {"usb_msd0",             1,      512,   128   },
#endif
    ...
#ifdef CONFIG_BT_ENABLE
#if CPU_CORE_NUM > 1
    {"#C0btencry",          16,      512,   128   },
    {"#C0btctrler",         19,      512,   384   },
    {"#C0btstack",          18,      1024,  384   },
#else
    {"btencry",             14,      512,   128   },
    {"btctrler",            19,      512,   384   },
    {"btstack",             18,      768,   384   },
#endif
#endif

    {"tcpip_thread",        16,     800,    0},
#ifdef CONFIG_WIFI_ENABLE
    {"tasklet",             10,     1400,    0},//通过调节任务优先级平衡WIFI收发占据总CPU的比重
    {"RtmpMlmeTask",        17,     700,    0},
    {"RtmpCmdQTask",        17,     300,    0},
    {"wl_rx_irq_thread",     5,     256,    0},
#endif

#ifdef CONFIG_UI_ENABLE
    {"ui",                  21,      768,   256   },
    {"lcd_task_0",           9,     1024,   32    },
    {"lcd_task_1",           9,     1024,   32    },
    {"te_task",             10,     1024,   32    },
#endif
    ...
    {"ai_server",           15,     1024,   64    },
    {"asr_server",          15,     1024,   64    },
    {"wake-on-voice",        7,     1024,   0     },
    {"resample_task",        8,     1024,   0     },
    {"video_server",        26,      800,   1024  },
    ...
};

Source: app_main.c

每个任务条目依次为 {名称, 优先级, 栈大小, 配额}。任务名中的 #C0 前缀表示强制绑定 CPU0(双核场景下用于把蓝牙控制器、USB 等实时性敏感的任务与主核隔离)。SDK 通过 CONFIG_BT_ENABLE、CONFIG_WIFI_ENABLE、CONFIG_UI_ENABLE、CPU_CORE_NUM 等宏对任务表做条件编译,实现「一份工程、按宏裁剪能力」。

外设例程库 apps/common/example

apps/common/example/readme.md 是全 SDK 外设例程的总索引(EXAMPLE 例程总览),按模块列出例程主题与常见问题。已确认覆盖的 CPU 外设部分包括:

模块例程覆盖内容
GPIO输入/上下拉配置、输出/强驱配置、中断模式、USB 等特殊引脚复用、双重绑定 IO、无互斥高速 GPIO 操作方法
TIMER可用定时器选择、中断/轮询、开始/暂停/继续/停止、定时器捕获
RTC软硬件配置、走时/设置/读取时间、闹钟、系统运行时间读取
ADC引脚配置、测量电压范围与精度、读取方法、VBAT 电源电压测量
PWM引脚配置、频率/占空比范围、普通与正反转 PWM、动态配置
SPI引脚配置、时钟范围、软/硬件 SPI、SPI1 外挂 FLASH、硬件从机
IIC引脚配置、软/硬件 IIC、外接 EEPROM
UART打印口与通信串口的引脚/波特率配置、读写说明(详见 uart/readme.md)
INPUT_CHANNEL / OUTPUT_CHANNEL硬件模块占用通道数量与系统总量注意事项
PWM_LED呼吸灯引脚与模式配置
PAP / EMI 推屏接口引脚配置、时序控制、读写与中断使用方法

Source: readme.md

总览文件还包含音频、视频、网络等更多例程条目(文件全长数百行),每个条目的典型格式为「功能点列表 + 常见问题」,开发者可直接按模块名跳到对应例程目录复用代码。这种「总览索引 + 子目录 readme + 可复制例程」的组织方式,把外设驱动从业务工程中解耦出来,保证任何示例工程都能独立引用同一套经过验证的底层用法。

配置选项

示例工程的配置主要分布在三个位置:include/app_config.h(功能宏)、board/<板型>/board.c(引脚/外设)、board/<板型>/Makefile 与 .cbp(构建目标)。下表汇总了在 app_main.c 中实际出现、可直接观察到的宏开关及其作用:

宏/符号类型默认行为作用
CPU_CORE_NUM宏1 或 2(按芯片)控制单/双核编译路径,影响任务表 #C0 前缀与中断注册方式
CONFIG_IPMASK_ENABLE宏未定义开启不可屏蔽中断方法,支持中断内写 Flash,要求相关代码与常量放内部 RAM
CONFIG_BT_ENABLE宏按工程是否编译蓝牙协议栈任务(btencry/btctrler/btstack)
CONFIG_WIFI_ENABLE宏按工程是否编译 WiFi 协议栈任务(tasklet/RtmpMlmeTask/RtmpCmdQTask/wl_rx_irq_thread)
CONFIG_UI_ENABLE宏按工程是否编译 UI/LCD 任务(ui/lcd_task_0/lcd_task_1/te_task)
CONFIG_IPMASK_ENABLE宏未定义参见上表(不可屏蔽中断)

任务表条目参数:

参数类型说明
任务名string名称;#C0 前缀表示强制调度到 CPU0
优先级int数值越小优先级越高(如 init 为 30 最高,usb_msd0 为 1 较低)
栈大小int任务栈字节数,音频编解码任务通常较大(1536 及以上)
配额int时间片/配额参数,0 表示不参与轮转或按系统默认

构建与移植

  • 构建方式:每个 board/<板型>/ 下提供 Makefile 与 CodeBlocks 工程文件(如 AC791N_DEMO_DEMO_AUDIO.cbp、AC791N_DEMO_DEMO_BLE.cbp),开发者可直接用 CodeBlocks 打开 .cbp 编译,或按 Makefile 走命令行构建。
  • 换板:复制 board/<板型>/ 目录并修改 board.c 的引脚/外设配置即可,工程源码无需改动——这是板级目录独立成目录的设计意图。
  • 裁剪功能:修改 include/app_config.h 的 CONFIG_* 宏并同步调整 task_info_table(删除对应任务条目)与 irq_info_table,可显著降低内存占用与启动时间。

失败模式与边界情况

  • 任务栈溢出:音频编解码(opus/ogg 1536)、AI 服务(ai_server 1024)、视频服务(video_server 1024 栈 + 1024 配额)等重任务栈较大;自行新增任务时栈大小不足会导致运行期随机崩溃,应参照同类任务取值。
  • 中断亲和性错误:双核下外设中断默认归属可能不符合驱动要求,需在 irq_info_table 中显式指定 CPU;插入 { -2, -2, -2 } 行后其后的中断全部强制到 CPU0,可能引发 CPU0 负载不均。
  • 不可屏蔽中断约束:CONFIG_IPMASK_ENABLE 下中断函数、其调用链与常量必须全部位于内部 RAM,否则写 Flash 期间访问外部存储会失败或死锁。
  • WiFi 任务优先级调优:tasklet 任务注释明确指出「通过调节任务优先级平衡 WIFI 收发占据总 CPU 的比重」——在强实时音频场景下应适当降低 tasklet 优先级,避免 WiFi 抢占音频解码导致卡顿。
  • 单核/双核差异:CPU_CORE_NUM == 1 时蓝牙、USB 任务不带 #C0 前缀且栈需求更小;从双核工程裁剪到单核芯片时必须同步调整任务表,否则任务无法创建。

扩展点

  • 新增示例工程:复制 demo_hello 目录,改写 app_main.c 的任务表与 app_main(),保持 board/、include/ 结构不变即可。
  • 新增板级支持:在 board/ 下新增板型目录,提供 board.c、Makefile、.cbp,即可让同一工程跑在不同硬件上。
  • 复用外设例程:从 apps/common/example 对应模块摘抄初始化与读写代码到工程业务模块,注意其「常见问题」条目列出的引脚互斥与通道总数限制。

相关链接

  • EXAMPLE 例程总览 — 外设例程索引(GPIO/TIMER/RTC/ADC/PWM/SPI/IIC/UART 等)
  • UART 例程说明 — 打印口/通信串口配置与读写示例
  • demo_DevKitBoard/app_main.c — 开发板综合工程入口(中断表/任务表/应用主流程)
  • demo_audio 工程 — 音频/AI 音箱示例,含 ai_speaker、local_music、net_music、recorder 等业务模块
  • demo_ble 工程 — BLE 蓝牙示例
Prev
扫码枪 HID 方案