编译构建指南
本指南介绍 fw-AD23N_GP-MCU_SDK 的完整编译构建流程,包括环境搭建、Code::Blocks 图形化编译、Makefile 命令行编译、编译产物与后处理,以及常见编译错误的排查方法。
Purpose and Scope
本页覆盖 AD23N SDK 从源码到固件的完整构建链路:
- 编译工具链的安装与环境变量配置(Windows / Linux / macOS)
- 两种编译方式:Code::Blocks 图形化编译与 Makefile 命令行编译
- 顶层
sdk/Makefile的构建系统设计:工具链探测、编译参数、特性宏、头文件搜索路径 - 编译产物(
sdk.elf)与post_build后处理流程 - 常见编译错误与性能调优建议
以下相关主题由同级页面分别介绍,不在本页展开:
- 固件烧录、生产烧写与 OTA 升级,请参见「烧录与升级」页面
- SDK 目录结构与各应用代码入口,请参见「工程结构」页面
app_config.h功能开关等运行时配置,请参见「配置说明」页面
概述
fw-AD23N_GP-MCU_SDK 是杰理科技为 AD23N 系列芯片(AD232A / AD232S / AD235A / AD236A / AD236B / AD238A / AD238B)提供的通用 MCU SDK。该 SDK 以 Release 形式发布源码与预编译库(include_lib/liba/ 下的 .a 文件),开发者通过 make 或 Code::Blocks 将应用源码、BSP 源码与预编译库链接为最终固件。
构建系统围绕以下设计意图构建:
- 跨平台统一入口:
sdk/Makefile通过检测OS环境变量自动切换 Windows 与 Linux 工具链路径,同一份 Makefile 在两个平台均可直接使用。 - LTO(Link-Time Optimization)链接:编译与链接统一使用
clang系工具链(clang+lto-wrapper),通过-flto实现跨编译单元的全局优化,这对内存紧张的 MCU 工程至关重要。 - 功能宏裁剪:通过
DEFINES中大量的-DHAS_xxx宏控制音频解码器、编码器、音效算法、文件系统等模块的编译,实现按需裁剪、节省代码空间。 - 后处理自动生成固件:链接完成后自动执行
app/post_build/sh59/下的下载/打包脚本,产出可直接烧录的固件文件。
注意:本仓库是 SDK Release 版本,需配合对应命名规则的库文件(
lib.a)才能完成链接。
架构
构建流水线架构
flowchart TD
subgraph sg_Env["构建环境层"]
CB["Code::Blocks IDE<br/>AD23N_mbox_flash.cbp"]
MK["make 命令行<br/>make_prompt.bat / bash"]
end
subgraph sg_Toolchain["杰理编译工具链 (clang / pi32v2)"]
CC["clang<br/>-target pi32v2 -mcpu=r3v2"]
LD["lto-wrapper<br/>LTO 链接"]
AR["llvm-ar / lto-ar<br/>静态库"]
end
subgraph sg_Src["源码与库输入"]
APP["app/src + app/bsp<br/>应用与 BSP 源码"]
LIB["include_lib/liba<br/>预编译库 .a"]
HDR["include_lib 头文件<br/>系统头文件"]
end
subgraph sg_Out["编译产物层"]
ELF["sdk.elf<br/>app/post_build/sh59/"]
POST["post_build 脚本<br/>download.bat / download.sh"]
FW["固件文件<br/>可烧录镜像"]
end
CB --> MK
MK --> CC
CC --> LD
APP --> CC
HDR --> CC
LIB --> LD
LD --> ELF
ELF --> POST
POST --> FW
AR --> LD
各层职责说明:
- 构建环境层:开发者通过 Code::Blocks 工程(
.cbp)或 Makefile 两种入口发起构建。Code::Blocks 底层同样调用杰理工具链,Makefile 方式更适合脚本化、CI 化构建。 - 工具链层:
Makefile中CC/LD/AR指向clang、lto-wrapper、llvm-ar(Linux 为lto-ar),目标架构为pi32v2、CPU 为r3v2(详见 sdk/Makefile)。 - 源码与库输入层:应用/BSP 源码与
include_lib下的预编译库共同参与编译链接,头文件通过INCLUDES中的-I参数注入编译命令。 - 编译产物层:链接产物为
app/post_build/sh59/sdk.elf,随后post_build脚本(Windows 为download.bat,Linux 为download.sh)将其处理为最终固件(详见 sdk/Makefile)。
平台探测与工具链切换
Makefile 的第一项关键设计是平台自动探测:通过 ifeq ($(OS), Windows_NT) 分支为 Windows 与 Linux 分别配置工具链路径、后处理脚本与额外的编译宏(详见 sdk/Makefile):
| 配置项 | Windows | Linux |
|---|---|---|
TOOL_DIR | C:/JL/pi32/bin | /opt/jieli/pi32v2/bin |
CC / CXX | clang.exe | clang |
LD | pi32v2-lto-wrapper.exe | lto-wrapper |
AR | llvm-ar.exe | lto-ar |
SYS_LIB_DIR | C:/JL/pi32/pi32v2-lib/r3 | $(TOOL_DIR)/../lib/r3 |
SYS_INC_DIR | C:/JL/pi32/pi32v2-include | $(TOOL_DIR)/../include |
EXT_CFLAGS | 空 | -D__SHELL__ |
| 后处理脚本 | download.bat(经 fixbat.exe 修正 UTF-8→GBK 编码) | download.sh |
设计意图:
EXT_CFLAGS在 Linux 下追加-D__SHELL__,是为了让download.c等后处理相关源码在无 Windows 批处理环境的平台上正确编译;Windows 下则用fixbat.exe处理批处理脚本的中文编码问题。
环境搭建
前提条件
根据目标开发平台选择编译环境(详见 README.md):
| 系统 | 说明 |
|---|---|
| Windows | 推荐使用 Code::Blocks IDE 编译,或双击 sdk/make_prompt.bat 进入预配置的命令行环境 |
| Linux | Makefile 命令行编译(需自行改写 download_bat.c 脚本适配 Linux 环境) |
| macOS | 需自行配置交叉编译工具链 |
安装编译工具链
- 从 杰理编译工具链下载页 下载并安装工具链。
- Linux 用户可从
pkgman.jieliapp.com获取,解压到/opt/jieli目录,并确保/opt/jieli/pi32/bin/clang存在。 - 安装完成后验证:
# 验证工具链是否安装成功
clang --version
Linux 特别注意事项(详见 sdk/Makefile):工具链解压后需保证
/opt/jieli/common/bin/clang存在(注意目录层次);同时需确认ulimit -n足够大(建议大于 8096),否则链接阶段可能因打开文件过多而失败,可通过ulimit -n 8096调大。
烧录与音频辅助工具
| 工具 | 用途 |
|---|---|
| USB 升级工具 | 将固件烧录到目标板(量产场景使用生产烧写工具) |
| 音频工具 | 打包、音频文件转换、MIDI 等通用音频工具 |
编译方式一:Code::Blocks(推荐 Windows 用户)
- 确保已安装杰理编译工具链。
- 双击打开
sdk/根目录下的工程文件AD23N_mbox_flash.cbp(该工程面向 AD23N 全系列,覆盖小音箱 / 语音玩具 / MIDI 琴应用,详见 README.md)。 - 点击 Build → Build(快捷键 Ctrl+F9)执行编译。
- 编译成功后,在
app/post_build/sh59/目录下生成固件。
编译方式二:Makefile 命令行
所有命令均在 sdk/ 目录下执行(详见 README.md):
# Windows 用户:双击 sdk/make_prompt.bat 打开命令行环境
# 该脚本已设置好所有环境变量和 make 的路径
# 编译(-j4 表示 4 个并行任务)
make -j4
# 显示编译详细过程
make VERBOSE=1 -j4
# 清理编译临时文件(清除 objs/ 等中间产物)
make clean
# Linux 用户(需先按前述步骤安装工具链到 /opt/jieli)
cd sdk
make -j`nproc`
Makefile 目标速查:
| 目标 | 作用 |
|---|---|
make / make -jN | 编译并生成固件(-jN 指定并行任务数) |
make VERBOSE=1 | 输出完整编译命令行,便于排查参数问题 |
make clean | 清除编译临时文件 |
核心流程
一次完整构建的执行过程
sequenceDiagram
participant Dev as 开发者
participant Make as Makefile 入口
participant CC as clang (pi32v2/r3v2)
participant LD as lto-wrapper
participant Post as post_build 脚本
participant FW as 固件产物
Dev->>Make: make -j4
activate Make
Make->>Make: 探测 OS 选择工具链与脚本
Make->>Make: 展开 DEFINES / INCLUDES / 源码列表
loop 每个 .c 源文件
Make->>CC: clang -target pi32v2 -mcpu=r3v2 -flto -Oz -g
CC-->>Make: 生成 LTO 中间对象 (objs/)
end
Make->>LD: 链接所有对象 + include_lib/liba/*.a
activate LD
LD-->>Make: app/post_build/sh59/sdk.elf
deactivate LD
Make->>Post: 执行 download.bat / download.sh
activate Post
Post->>Post: fixbat(UTF-8→GBK) / 打包处理
Post-->>FW: 生成可烧录固件
deactivate Post
Make-->>Dev: 构建完成
deactivate Make
关键步骤说明:
- 平台探测:
Makefile依据$(OS)选择 Windows/Linux 工具链路径、EXT_CFLAGS与后处理脚本,并将TOOL_DIR加入PATH。 - 参数展开:
CFLAGS(架构、优化等级)、DEFINES(功能宏)、INCLUDES(-I头文件路径)、c_SRC_FILES(需编译的.c文件列表)组成最终编译命令。 - 编译:每个
.c文件经clang以-target pi32v2 -mcpu=r3v2交叉编译,启用-flto生成 LTO 中间表示,产物落入objs/(BUILD_DIR)。 - 链接:
lto-wrapper将所有对象与include_lib/liba/预编译库做 LTO 全局优化后链接,输出app/post_build/sh59/sdk.elf(OUT_ELF)。 - 后处理:执行
download.bat(Windows,先经fixbat.exe修正编码)或download.sh(Linux),将 ELF 转换为最终固件。
编译参数与特性宏
CFLAGS 的核心参数(详见 sdk/Makefile):
| 参数 | 含义 |
|---|---|
-target pi32v2 | 指定交叉编译目标架构为 pi32v2 |
-mcpu=r3v2 | 指定 CPU 内核为 r3v2(对应 AD23N 系列 DSP 内核) |
-flto | 启用链接时优化,跨编译单元全局优化 |
-Oz | 面向代码体积的优化(MCU Flash 有限,优先减小体积) |
-g | 生成调试信息 |
-fno-common | 禁止公共块合并,规避未初始化全局变量链接歧义 |
-fprefer-gnu-section | 为函数/数据生成独立 section,配合链接脚本裁剪无用代码 |
-Wundef / -Wuninitialized | 启用未定义宏、未初始化变量告警 |
DEFINES 中的特性宏(节选,完整列表见 sdk/Makefile):
| 宏 | 作用 |
|---|---|
-DCPU_SH59=1 | 指定芯片平台为 SH59(AD23N 内部代号) |
-DFPGA=0 | 关闭 FPGA 仿真模式,按量产芯片编译 |
-DROM_SECURE_BOOT | 启用安全启动 |
-DHAS_MP3_DECODER / -DHAS_WAV_DECODER / -DHAS_MIDI_DECODER 等 | 按需启用各音频解码器 |
-DHAS_MP3_ENCODER / -DHAS_UMP3_ENCODER / -DHAS_A_ENCODER | 按需启用录音编码器 |
-DHAS_ANS_EN / -DHAS_ECHO_EN / -DHAS_VOICE_PITCH_EN / -DHAS_VOICE_CHANGER_EN / -DHAS_PCM_EQ_FLOAT_EN | 启用音效算法(降噪/混响/变调/变声/浮点 EQ) |
-DHAS_NORFS_EN / -DHAS_FATFS_EN / -DHAS_FREEFS_EN | 启用文件系统(NorFs/FATFS/FreeFs) |
-DHAS_UPDATE_EN=1 | 启用固件升级(OTA 双备份) |
-DHAS_USB_EN=0 | 关闭 USB 功能 |
-DNOFLOAT | 禁用浮点运算路径(音频算法多为定点实现) |
使用示例
示例一:从零开始克隆并编译
以下命令序列来自 README.md 的快速开始章节:
# 克隆仓库并进入 SDK 目录
git clone https://gitee.com/Jieli-Tech/AD23N.git
cd AD23N/sdk
# 方式一:Code::Blocks 图形化编译
# 1. 双击打开 AD23N_mbox_flash.cbp
# 2. 点击 Build → Build(Ctrl+F9)
# 方式二:Makefile 命令行编译
# Windows 用户先双击 make_prompt.bat 打开命令行环境
make -j4
# 显示编译详情(排查参数问题时使用)
make VERBOSE=1 -j4
Source: README.md
示例二:Makefile 平台探测逻辑
以下代码展示了 Makefile 如何根据操作系统切换工具链(摘自 sdk/Makefile):
# 工具路径设置
ifeq ($(OS), Windows_NT)
# Windows 下工具链位置
TOOL_DIR := C:/JL/pi32/bin
CC := clang.exe
CXX := clang.exe
LD := pi32v2-lto-wrapper.exe
AR := llvm-ar.exe
MKDIR := mkdir_win -p
RM := rm -rf
SYS_LIB_DIR := C:/JL/pi32/pi32v2-lib/r3
SYS_INC_DIR := C:/JL/pi32/pi32v2-include
EXT_CFLAGS := # Windows 下不需要 -D__SHELL__
export PATH:=$(TOOL_DIR);$(PATH)
## 后处理脚本
FIXBAT := tools/utils/fixbat.exe # 用于处理 utf8->gbk 编码问题
POST_SCRIPT := app/post_build/sh59/download.bat
RUN_POST_SCRIPT := app\\post_build\\sh59\\download.bat
else
# Linux 下工具链位置
TOOL_DIR := /opt/jieli/pi32v2/bin
CC := clang
CXX := clang
LD := lto-wrapper
AR := lto-ar
MKDIR := mkdir -p
RM := rm -rf
export OBJDUMP := $(TOOL_DIR)/objdump
export OBJCOPY := $(TOOL_DIR)/objcopy
export OBJSIZEDUMP := $(TOOL_DIR)/objsizedump
SYS_LIB_DIR := $(TOOL_DIR)/../lib/r3
SYS_INC_DIR := $(TOOL_DIR)/../include
EXT_CFLAGS := -D__SHELL__ # Linux 下需要这个保证正确处理 download.c
export PATH:=$(TOOL_DIR):$(PATH)
## 后处理脚本
FIXBAT := touch # Linux下不需要处理 bat 编码问题
POST_SCRIPT := app/post_build/sh59/download.sh
RUN_POST_SCRIPT := bash $(POST_SCRIPT)
endif
CC := $(TOOL_DIR)/$(CC)
CXX := $(TOOL_DIR)/$(CXX)
LD := $(TOOL_DIR)/$(LD)
AR := $(TOOL_DIR)/$(AR)
Source: sdk/Makefile
代码解读:ifeq ($(OS), Windows_NT) 是 GNU Make 的条件分支语法。Windows 下工具链位于 C:/JL/pi32/bin(由安装包固定路径),并导出 OBJDUMP/OBJCOPY/OBJSIZEDUMP 供后处理脚本使用;Linux 下工具链位于 /opt/jieli/pi32v2/bin,且通过 -D__SHELL__ 宏适配无批处理环境。FIXBAT 在 Windows 下指向 fixbat.exe 修正 download.bat 的 UTF-8→GBK 编码,Linux 下退化为 touch(空操作),体现了"同一套 Makefile 双平台复用"的设计目标。
示例三:编译参数与输出定义
以下代码片段展示了输出文件与核心编译参数的定义方式(摘自 sdk/Makefile):
# 输出文件设置
OUT_ELF := app/post_build/sh59/sdk.elf
OBJ_FILE := $(OUT_ELF).objs.txt
# 编译路径设置
BUILD_DIR := objs
# 编译参数设置
CFLAGS := \
-target pi32v2 \
-mcpu=r3v2 \
-integrated-as \
-flto \
-Wuninitialized \
-Wno-invalid-noreturn \
-fno-common \
-integrated-as \
-Oz \
-g \
-flto \
-fallow-pointer-null \
-fprefer-gnu-section \
-Wno-shift-negative-value \
-Wundef \
Source: sdk/Makefile
代码解读:OUT_ELF 直接指向 post_build 目录,使后处理脚本可以就地处理链接产物;BUILD_DIR := objs 将中间对象集中存放,make clean 只需删除该目录。CFLAGS 中的 -mcpu=r3v2 与 -target pi32v2 是杰理芯片专用架构参数,-flto 出现两次是历史遗留写法(等价于一次启用),-Oz 表明该 SDK 对固件体积的重视程度高于运行速度。
配置选项
Makefile 核心变量
| 变量 | 类型 | 默认值 | 说明 |
|---|---|---|---|
CC / CXX | string | clang / clang.exe | C/C++ 编译器(按平台自动选择) |
LD | string | lto-wrapper / pi32v2-lto-wrapper.exe | LTO 链接器 |
AR | string | lto-ar / llvm-ar.exe | 静态库打包工具 |
TOOL_DIR | string | /opt/jieli/pi32v2/bin 或 C:/JL/pi32/bin | 工具链根目录 |
SYS_LIB_DIR | string | $(TOOL_DIR)/../lib/r3 | 系统运行库目录(r3 内核) |
SYS_INC_DIR | string | $(TOOL_DIR)/../include | 系统头文件目录 |
EXT_CFLAGS | string | -D__SHELL__(Linux) | 平台附加宏定义 |
OUT_ELF | string | app/post_build/sh59/sdk.elf | 链接产物路径 |
BUILD_DIR | string | objs | 中间对象输出目录 |
POST_SCRIPT | string | download.bat / download.sh | 后处理脚本路径 |
VERBOSE | bool | 关闭 | make VERBOSE=1 输出详细编译命令 |
特性宏(DEFINES)裁剪
完整的特性宏列表定义于 sdk/Makefile,开发者可通过增删 -DHAS_xxx 控制编译进固件的模块,例如:
- 解码器家族:
HAS_A_DECODER、HAS_MP3_ST_DECODER、HAS_UMP3_DECODER、HAS_WAV_DECODER、HAS_F1A_DECODER、HAS_MIDI_DECODER、HAS_MIDI_KEYBOARD_DECODER,其中HAS_MAX_F1A_NUMBER=2限制 F1A 解码路数。 - 编码器家族:
HAS_MP3_ENCODER、HAS_UMP3_ENCODER、HAS_A_ENCODER。 - 音效:
HAS_ANS_EN(降噪)、HAS_SPEED_EN(变速)、HAS_ECHO_EN(混响)、HOWLING_EN(啸叫抑制)。 - 外设与存储:
HAS_EXT_FLASH_EN、HAS_SDMMC_EN、HAS_HW_SRC_MODULE=1、HAS_NORFS_EN、HAS_FATFS_EN、HAS_FREEFS_EN、SIMPLE_FATFS_ENABLE=0、SYS_VM_EN=0。 - 系统:
ROM_SECURE_BOOT(安全启动)、HAS_UPDATE_EN=1(升级)、SPEAKER_EN、AUX_EN、ENCODER_EN、AUDIO_ADC_EN=1。
应用层功能开关(如
app_config.h)与 Makefile 宏是两个层面:Makefile 宏决定模块是否参与编译链接,app_config.h决定运行时行为。
故障模式、边界情况与并发
常见编译错误及排查
以下错误表来自 README.md 的常见编译错误章节:
| 错误提示 | 解决方法 |
|---|---|
clang: command not found | 未安装杰理编译工具链,或环境变量未配置(检查 TOOL_DIR 是否正确加入 PATH) |
cannot find -lxxx | 缺少对应的 .a 库文件,检查 include_lib/liba/ 目录是否与当前芯片型号匹配 |
make: command not found | Windows 下使用 tools/make_prompt.bat 打开预配置的编译命令环境 |
| 链接错误 | 检查 Makefile 的 target 是否匹配当前芯片型号(-mcpu=r3v2、CPU_SH59 等) |
Linux 特有边界条件
ulimit -n限制:LTO 链接阶段会同时打开大量文件(对象 + 库 + 头文件),若ulimit -n小于 8096 可能链接失败,需执行ulimit -n 8096(详见 sdk/Makefile)。- 工具链目录层次:Linux 安装时必须保证
/opt/jieli/common/bin/clang存在,注意是common层级而非pi32v2层级,否则 Makefile 找不到编译器。 - 后处理脚本差异:
download_bat.c是按 Windows 批处理环境编写的,Linux 下需自行改写适配(README 明确提示)。
并行编译的并发注意
make -jN 将多个编译任务并行执行。由于 objs/ 目录内每个源文件生成独立目标文件,并行编译本身是安全的;但若在并行模式下同时执行 make clean 或修改源码列表,可能产生竞态(中间文件被删除/重建),建议:编译与清理操作串行执行,CI 脚本中先 make clean 再 make -jN。
性能与运维建议
- 加速编译:使用
make -j4(或-j+ 本机 CPU 核数),README 明确推荐该方式(详见 README.md)。 - 定位编译问题:
make VERBOSE=1输出完整命令行,可直接检查-I路径、宏定义与链接库参数是否正确。 - 固件体积控制:通过裁剪
DEFINES中的-DHAS_xxx宏关闭不需要的模块,配合-Oz与-flto可显著减小固件体积;若需确认空间占用,Linux 下可借助OBJSIZEDUMP工具(Makefile 已导出)。 - CI 集成:Linux 环境 + Makefile 是最适合 CI 的构建方式,脚本化构建建议固定
TOOL_DIR=/opt/jieli/pi32v2/bin并预先调大ulimit -n。
扩展点
- 新增应用工程:基于现有
AD23N_mbox_flash.cbp工程与app/src/中的应用代码修改,配置对应用例即可(README.md)。新增.c文件需追加到 Makefile 的c_SRC_FILES列表,或将应用目录加入INCLUDES。 - 切换芯片型号:SDK 已为全系列预配置统一编译入口,在配置中选择对应芯片型号即可;注意核对
-mcpu与CPU_SH59宏是否匹配目标型号。 - 定制后处理:修改
app/post_build/sh59/下的download.bat/download.sh,可定制固件打包、校验、重命名等步骤;FIXBAT机制保证 Windows 脚本编码正确。 - 功能裁剪:增删
DEFINES中的特性宏是最常用的扩展手段,需同步确认include_lib/liba/中存在对应预编译库(否则报cannot find -lxxx)。