杰理 SDK 文档中心
首页
首页
  • 概述与快速入门

    • 芯片平台与 SDK 概述
    • 环境搭建与编译工具链
    • 快速开始:选型、编译与烧录
    • 烧录与量产工具
  • 构建系统与板级工程

    • 顶层 Makefile 与编译目标
    • 板级工程与配置
    • 后处理与配置工具
  • HID 人机交互应用

    • HID 应用架构总览
    • 键盘、翻页器与遥控应用
    • 鼠标应用:单模、双模与低延迟
    • 空闲应用与初始化流程
  • BLE 透传与数传应用

    • 透传应用总览
    • 多连接与无连接传输
    • AT 命令模组应用
    • Dongle 适配器应用
  • BSP 公共模块

    • 蓝牙公共处理
    • 按键、LED 与红外
    • 传感器与编码器
    • 存储、VM 与文件系统
    • 电源管理与低功耗
    • 消息调度与通信外设
  • 协议栈与预编译库

    • 蓝牙协议栈库
    • 设备驱动与文件系统库
    • 音频、升级与其他库
  • 开发资料与补丁发布

    • 文档资料中心
    • 版本补丁与兼容性修复

顶层 Makefile 与编译目标

本文档介绍 fw-AW31N_BLE_SDK 仓库根目录下顶层 Makefile 的结构、支持的编译目标(aw31n_transfer / aw31n_hid 等),以及它们如何分发到各子工程 Makefile 完成编译、链接与烧录的完整构建链路。

Purpose and Scope

本页覆盖的内容:

  • 仓库根目录 Makefile 的全部目标(all、clean、aw31n_transfer、aw31n_hid 及对应的 clean 目标)与其分发逻辑;
  • 子工程 Makefile(apps/demo/transfer/board/bd47/Makefile 与 apps/demo/hid/board/bd47/Makefile)中的工具链切换(Windows / Linux)、编译参数(CFLAGS / DEFINES / INCLUDES)与构建产物;
  • 构建前置条件(工具链安装路径、ulimit -n 限制)与常见失败模式。

不纳入本页的内容(属于兄弟页面):

  • 各 demo 应用的功能实现(transfer 数据透传、HID 人机交互设备等);
  • bd47 板级的硬件驱动与 BSP 细节;
  • apps/app/post_build/bd47/ 下 download.sh / download.bat 烧录脚本的内部实现。

Overview

该 SDK 采用「顶层分发器 + 子工程 Makefile」的两级构建结构。仓库根目录只有一个 30 行的 Makefile,它本身不直接编译任何代码,而是通过 $(MAKE) -C <子工程目录> -f Makefile 递归调用各 demo 子工程的 Makefile。

当前支持两个子工程:

目标名对应子工程目录说明
aw31n_transferapps/demo/transfer/board/bd47BLE 数据透传(transfer)demo
aw31n_hidapps/demo/hid/board/bd47HID 设备 demo

子工程 Makefile 负责真正的构建工作:按操作系统选择 clang 工具链(Windows 下位于 C:/JL/pi32/bin,Linux 下位于 /opt/jieli/q32s/bin)、编译 c_SRC_FILES 中列出的全部 .c 源文件、链接生成 sdk.elf,并调用后处理脚本(download.bat / download.sh)完成固件烧录。

这种设计意图在于:一个仓库内同时维护多个 demo 工程时,根目录只需提供统一、简单的入口;新增一个 demo 只需在顶层 Makefile 增加一个目标与一次递归调用,构建逻辑集中在各子工程内,互不干扰。

Architecture

下图展示从顶层目标到最终烧录的完整构建架构:

flowchart TD
    subgraph sg_Top["顶层 Makefile(仓库根目录)"]
        T_all["all"]
        T_transfer["aw31n_transfer"]
        T_hid["aw31n_hid"]
        T_clean["clean"]
    end

    subgraph sg_Sub["子工程 Makefile(bd47 板级)"]
        M_transfer["apps/demo/transfer/board/bd47/Makefile"]
        M_hid["apps/demo/hid/board/bd47/Makefile"]
    end

    subgraph sg_Toolchain["工具链与系统库"]
        TC["clang / lto-wrapper / lto-ar"]
        LIBS["SYS_LIB_DIR / SYS_INC_DIR"]
    end

    subgraph sg_Output["构建产物与后处理"]
        OBJ["objs/ 中间目标文件"]
        ELF["sdk.elf"]
        POST["download.sh / download.bat"]
    end

    T_all --> T_transfer
    T_all --> T_hid
    T_transfer --> M_transfer
    T_hid --> M_hid
    T_clean -->|"clean 目标"| M_transfer
    T_clean -->|"clean 目标"| M_hid
    M_transfer --> TC
    M_hid --> TC
    TC --> LIBS
    M_transfer --> OBJ
    M_hid --> OBJ
    OBJ --> ELF
    ELF --> POST

架构要点:

  • 顶层 Makefile 只做目标聚合与递归分发,all 聚合两个 demo,clean 聚合两个清理目标;
  • 子工程 Makefile 是唯一的构建逻辑所有者,负责工具链选择、编译参数、源文件清单与产物路径;
  • 工具链 由操作系统环境变量 OS 决定,Windows 使用 .exe 工具,Linux 使用同名 ELF 工具;
  • 后处理脚本 位于 apps/app/post_build/bd47/,输出固件路径为同目录下的 sdk.elf,最终由 download 脚本烧录到芯片。

顶层 Makefile 解析

根目录 Makefile 全文只有 30 行,其核心内容如下:

# 总的 Makefile,用于调用目录下各个子工程对应的 Makefile
# 支持的目标
# make aw31n_transfer
# make aw31n_hid

.PHONY: all clean aw31n_transfer aw31n_hid clean_aw31n_transfer clean_aw31n_hid

all: aw31n_transfer aw31n_hid
	@echo +ALL DONE

clean: clean_aw31n_transfer clean_aw31n_hid
	@echo +CLEAN DONE

aw31n_transfer:
	$(MAKE) -C apps/demo/transfer/board/bd47 -f Makefile

clean_aw31n_transfer:
	$(MAKE) -C apps/demo/transfer/board/bd47 -f Makefile clean

aw31n_hid:
	$(MAKE) -C apps/demo/hid/board/bd47 -f Makefile

clean_aw31n_hid:
	$(MAKE) -C apps/demo/hid/board/bd47 -f Makefile clean

Source: Makefile

目标一览

目标前置目标行为
all(默认目标)aw31n_transfer、aw31n_hid依次构建全部两个子工程,完成后打印 +ALL DONE
cleanclean_aw31n_transfer、clean_aw31n_hid清理全部子工程,完成后打印 +CLEAN DONE
aw31n_transfer—递归进入 transfer 子工程目录执行 make
clean_aw31n_transfer—递归进入 transfer 子工程目录执行 make clean
aw31n_hid—递归进入 hid 子工程目录执行 make
clean_aw31n_hid—递归进入 hid 子工程目录执行 make clean

设计细节说明:

  • 所有目标均声明为 .PHONY,因为目标名(如 aw31n_transfer)与磁盘上的目录名不同,且 all/clean 是约定俗成的伪目标,必须避免与同名文件冲突导致 make 误判「无需更新」;
  • 使用 $(MAKE) 而非直接写 make,是为了透传命令行变量与 -j 等参数到子进程,并保证递归 make 的可追踪性;
  • -C apps/demo/transfer/board/bd47 指定工作目录,-f Makefile 显式指定构建文件——子工程目录里就是默认文件名,这里的写法保证了入口明确。

编译前置条件(文件头注释)

顶层 Makefile 头部的注释是构建系统最重要的运维文档,它记录了 Linux 下的三条硬性要求:

  1. 从 http://pkgman.jieliapp.com/doc/all 获取工具链下载链接;
  2. 解压到 /opt/jieli 目录,确保 /opt/jieli/common/bin/clang 存在(注意目录层次,不同版本的 SDK 工具链安装路径可能不同);
  3. 确认 ulimit -n(文件描述符上限)足够大(建议 > 8096),否则链接阶段可能因为打开文件过多而失败;可通过 ulimit -n 8096 临时调大。

Source: Makefile

子工程 Makefile 解析

两个子工程的 Makefile 在前 100 行完全相同(工具链与环境部分),差异体现在后续的 DEFINES、INCLUDES 与 c_SRC_FILES 列表(例如 transfer 工程定义了 -DAPP_CASE_TRANSFER,且头文件路径包含 apps/demo/transfer 与 rcsp_update 等透传相关目录)。下面以 transfer 工程为例说明。

双平台工具链切换

# 工具路径设置
ifeq ($(OS), Windows_NT)
# Windows 下工具链位置
TOOL_DIR := C:/JL/pi32/bin
CC    := clang.exe
CXX   := clang.exe
LD    := q32s-lto-wrapper.exe
AR    := llvm-ar.exe
MKDIR := mkdir_win -p
RM    := rm -rf

SYS_LIB_DIR := C:/JL/pi32/q32s-lib
SYS_INC_DIR := C:/JL/pi32/q32s-include
EXT_CFLAGS  := # Windows 下不需要 -D__SHELL__
export PATH:=$(TOOL_DIR);$(PATH)
else
# Linux 下工具链位置
TOOL_DIR := /opt/jieli/q32s/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
SYS_INC_DIR := $(TOOL_DIR)/../include
EXT_CFLAGS  := -D__SHELL__ # Linux 下需要这个保证正确处理 download.c
export PATH:=$(TOOL_DIR):$(PATH)
endif

Source: apps/demo/transfer/board/bd47/Makefile

该分支的设计意图:

  • 通过 make 内置变量 OS(Windows 下 make 会将其设为 Windows_NT)做平台判断,一套 Makefile 同时支持 Windows 与 Linux 开发环境;
  • Windows 工具链各工具带 .exe 后缀,且链接器为 q32s-lto-wrapper.exe(LLVM LTO 链接包装器),Linux 下对应 lto-wrapper;归档器分别为 llvm-ar.exe 与 lto-ar;
  • Linux 分支额外导出 OBJDUMP / OBJCOPY / OBJSIZEDUMP,供后续链接/后处理脚本使用;
  • EXT_CFLAGS 的差异是关键:Linux 下必须追加 -D__SHELL__,用于保证 download.c 中烧录相关代码在 Linux 下被正确编译;Windows 下则为空;
  • 两种平台都把工具链目录 export 到 PATH,使构建中调用的其他辅助程序(如 post_build 脚本)能找到工具。

构建产物与编译参数

CC  := $(TOOL_DIR)/$(CC)
CXX := $(TOOL_DIR)/$(CXX)
LD  := $(TOOL_DIR)/$(LD)
AR  := $(TOOL_DIR)/$(AR)
# 输出文件设置
OUT_ELF   := ../../../../../apps/app/post_build/bd47/sdk.elf
OBJ_FILE  := $(OUT_ELF).objs.txt
# 编译路径设置
BUILD_DIR := objs
# 工程路径前缀
ROOT_PREFIX := ../../../../..

Source: apps/demo/transfer/board/bd47/Makefile

要点:

  • 产物路径统一收敛:OUT_ELF 指向 apps/app/post_build/bd47/sdk.elf,即最终固件 ELF 与后处理脚本放在同一目录,方便 download 脚本就近取用;OBJ_FILE(sdk.elf.objs.txt)用于记录链接输入的目标文件清单,供 LTO 链接器使用;
  • ROOT_PREFIX := ../../../../..:从 apps/demo/transfer/board/bd47 回到仓库根目录的相对路径,后续所有源文件与头文件路径都以此为前缀拼接,保证无论在哪个子工程构建都能定位到仓库内的共享代码;
  • BUILD_DIR := objs 为当前目录下的中间文件目录。

CFLAGS 采用 LLVM/clang 的 q32s 目标:

CFLAGS := \
	-flto \
	-target q32s \
	-integrated-as \
	-fno-builtin \
	-mllvm -inline-threshold=5 \
	-Oz \
	-integrated-as \
	-g \
	-O0 \
	-flto \
	-Os \
	-Wcast-align \
	-fallow-pointer-null \
	-Wincompatible-pointer-types \
	-Wundef \
	-fprefer-gnu-section \
	-Wframe-larger-than=256 \
	-Wreturn-type \
	-Wimplicit-function-declaration \
	-Werror=jl-arg-struct-check \
	-Werror \
	-Werror=implicit-function-declaration \
	-Werror=return-type \
	-Werror=undef \
	-fms-extensions \
	-Wno-format

Source: apps/demo/transfer/board/bd47/Makefile

参数意图解读:

  • -target q32s 指定交叉编译目标架构(杰理 q32s CPU 内核);-integrated-as 使用 clang 内置汇编器,避免依赖外部 as;
  • -flto 开启链接期优化(Link-Time Optimization),配合 lto-wrapper / q32s-lto-wrapper.exe 链接器使用;-mllvm -inline-threshold=5 压低内联阈值以控制代码体积;
  • -Oz(优化尺寸)与 -Os 同时出现,且保留了 -g -O0,说明该构建以代码体积优先、兼顾可调试为原则——BLE 固件通常受 Flash/RAM 容量约束;
  • 大量 -Werror=... 将常见错误(隐式函数声明、未定义宏、错误返回类型)升级为编译失败,保证 SDK 代码质量;-Werror=jl-arg-struct-check 是杰理自定义的检查(结构体参数传递约定),-Wframe-larger-than=256 限制栈帧大小以适配受限 RAM;
  • -fno-builtin、-fprefer-gnu-section 服务于链接期的裁剪与重排。

宏定义与头文件搜索路径

DEFINES := \
	-D__FPGA=0 \
	-DCONFIG_CPU_BD47=1 \
	-DAPP_BT_BLE=1 \
	-DSUPPORT_MS_EXTENSIONS \
	-DSUPPORT_RESERVATION_RAM \
	-DAPP_CASE_TRANSFER \
	-DSDK_VERSION_CFG_DEFINE=0x130001 \
	-DSDK_VERSION_DATE_DEFINE=20250822 \
	-DCONFIG_RELEASE_ENABLE \
	-DHAS_VFS_EN=1 \
	-DNOFLOAT

DEFINES += $(EXT_CFLAGS) # 额外的一些定义

Source: apps/demo/transfer/board/bd47/Makefile

宏定义含义:

  • CONFIG_CPU_BD47=1 声明目标 CPU 为 BD47;APP_BT_BLE=1 使能 BLE 应用;APP_CASE_TRANSFER 标识本工程为「充电仓/透传盒」应用场景,驱动代码中对应场景的条件编译;
  • SDK_VERSION_CFG_DEFINE=0x130001 与 SDK_VERSION_DATE_DEFINE=20250822 把 SDK 版本与日期编译进固件,供运行时上报/升级校验;
  • SUPPORT_RESERVATION_RAM 保留 RAM 段;NOFLOAT 禁用浮点以减小体积;HAS_VFS_EN=1 使能虚拟文件系统;__FPGA=0 表示非 FPGA 验证平台;
  • 末尾 DEFINES += $(EXT_CFLAGS) 把平台相关的 -D__SHELL__(Linux)并入总宏定义。

头文件搜索路径(INCLUDES)则以 ROOT_PREFIX 为基址展开约 60 个 -I 目录,覆盖:

  • apps/include_lib/*(各功能模块对外头文件:bt_include、device、fs、audio、msg、update 等);
  • apps/app/bsp/*(BSP 源码自带头文件:common、cpu/bd47、start、usb、key、power_manage 等);
  • apps/demo/transfer(本工程私有头文件);
  • 系统目录 $(SYS_INC_DIR)。

Source: apps/demo/transfer/board/bd47/Makefile

源文件清单

c_SRC_FILES 以 ROOT_PREFIX 前缀列出本工程参与编译的全部 .c 文件,transfer 工程覆盖了 apps/app/bsp/common/ 下的共享模块(bt_common、code_switch、common_uart、fs/vfs、ir、key、led、mouse_sensor、msg、power_manage、third_party_profile/jieli(含 JL_rcsp 的 rcsp_update / rcsp_hid)、update、usb 设备类等),以及 apps/demo/transfer 下的 demo 代码。

Source: apps/demo/transfer/board/bd47/Makefile

该文件在 240 行之后继续罗列剩余源文件、C++ 源文件、汇编源文件,并定义目标文件生成规则与 all / clean / download 等实际执行目标(make 默认行为即「编译并下载」,make clean 清除 objs/ 等临时文件,make VERBOSE=1 显示详细编译过程,见文件头部注释)。

核心构建流程

以执行 make aw31n_transfer 为例,完整调用链如下:

sequenceDiagram
    participant U as 开发者
    participant T as 顶层 Makefile
    participant S as 子工程 Makefile
    participant CC as clang 工具链
    participant P as post_build 脚本

    U->>T: make aw31n_transfer
    T->>S: $(MAKE) -C apps/demo/transfer/board/bd47 -f Makefile
    activate S
    S->>S: 判断 OS 选择工具链(Windows / Linux)
    S->>CC: 按 c_SRC_FILES 逐个编译 .c → objs/*.o
    CC-->>S: 目标文件 + sdk.elf.objs.txt
    S->>S: lto-wrapper 链接生成 sdk.elf
    S->>P: 调用 download.sh / download.bat
    deactivate S
    P-->>U: 固件烧录完成

关键路径说明:

  1. 开发者执行 make aw31n_transfer(或直接 make 触发默认目标 all,顺序构建两个工程);
  2. 顶层 Makefile 用 $(MAKE) -C ... -f Makefile 递归调用子工程,工作目录切换到 apps/demo/transfer/board/bd47;
  3. 子工程 Makefile 解析 OS 变量选择工具链目录,将工具链加入 PATH;
  4. 对 c_SRC_FILES 中的每个源文件调用 $(CC)(clang,-target q32s -flto -Os/-Oz)生成目标文件到 objs/,同时生成链接输入清单 sdk.elf.objs.txt;
  5. 链接器 lto-wrapper 进行 LTO 链接,输出 apps/app/post_build/bd47/sdk.elf;
  6. 最后执行后处理脚本(Linux 为 bash download.sh,Windows 为 download.bat,且 Windows 下先用 fixbat.exe 处理脚本的 utf8→gbk 编码问题)完成固件烧录。

平台分支逻辑可简化为下图:

flowchart TD
    Q{"OS == Windows_NT ?"}
    Q -->|"是"| W["工具链 C:/JL/pi32/bin<br/>clang.exe / q32s-lto-wrapper.exe"]
    Q -->|"否(Linux)"| L["工具链 /opt/jieli/q32s/bin<br/>clang / lto-wrapper"]
    W --> W2["download.bat + fixbat.exe<br/>EXT_CFLAGS 为空"]
    L --> L2["download.sh<br/>EXT_CFLAGS += -D__SHELL__"]

使用示例

构建全部子工程

在仓库根目录执行:

make
# 等价于 make all,依次构建 aw31n_transfer 与 aw31n_hid
# 完成后输出 +ALL DONE

单独构建并烧录某个工程

make aw31n_transfer   # 构建 transfer 工程并烧录
make aw31n_hid        # 构建 hid 工程并烧录

清理构建产物

make clean                  # 清理全部子工程
make clean_aw31n_transfer   # 仅清理 transfer 工程

Source: Makefile

子工程内的高级用法

进入子工程目录后可直接调用其 Makefile:

cd apps/demo/transfer/board/bd47
make               # 编译并下载(默认行为,见文件头注释)
make VERBOSE=1     # 显示详细编译过程
make clean         # 清除编译临时文件

Source: apps/demo/transfer/board/bd47/Makefile

配置选项

以下为构建系统的关键可配置项:

配置项类型默认值说明
OS(环境变量)string由系统决定Windows_NT 时走 Windows 工具链分支,否则按 Linux 处理
VERBOSE=1(命令行变量)bool关闭子工程 make 显示详细编译过程
TOOL_DIRpathWindows: C:/JL/pi32/bin;Linux: /opt/jieli/q32s/binclang 交叉工具链目录,会被 export 进 PATH
SYS_LIB_DIRpathWindows: C:/JL/pi32/q32s-lib;Linux: $(TOOL_DIR)/../lib系统库目录
SYS_INC_DIRpathWindows: C:/JL/pi32/q32s-include;Linux: $(TOOL_DIR)/../include系统头文件目录
EXT_CFLAGSstringWindows: 空;Linux: -D__SHELL__平台额外宏定义,追加到 DEFINES
OUT_ELFpathapps/app/post_build/bd47/sdk.elf链接产物路径
BUILD_DIRpathobjs中间目标文件目录
ROOT_PREFIXpath../../../../..从子工程目录回到仓库根的相对前缀
ulimit -n(系统限制)int视系统而定需 > 8096,否则链接阶段可能因打开文件过多失败

失败模式、边界情况与并发注意

依据 Makefile 源码注释与结构,构建系统存在以下已知失败模式与边界情况:

工具链未安装或路径不符

顶层 Makefile 注释明确要求 Linux 下 clang 必须位于 /opt/jieli/common/bin/clang,而子工程 Makefile 使用的 Linux 工具链目录是 /opt/jieli/q32s/bin。二者不一致是最常见的坑:工具链解压层次不对(例如多套了一层目录),会导致 make 报 clang: command not found 或链接器找不到。解决方法是严格按 pkgman 文档解压,确保两个路径都存在。

Source: Makefile

链接期文件描述符耗尽

文件头注释明确警告:ulimit -n 结果不够大(建议大于 8096)时,链接阶段会因打开文件太多而失败。LTO 链接需要同时打开大量目标文件与 IR 文件,尤其在大工程下容易触顶。出现 Too many open files 类错误时应先执行 ulimit -n 8096 再重试。

Source: Makefile

平台差异导致的编译行为不同

EXT_CFLAGS 仅在 Linux 分支追加 -D__SHELL__。若在 Linux 下移除了该宏(或误改 Makefile),download.c 中依赖 __SHELL__ 的代码路径将不会被编译,可能导致烧录功能异常。同理,Windows 下不应添加该宏。这是刻意设计的平台差异,修改时需保持两分支一致语义。

Windows 下脚本编码问题

Windows 分支引入 FIXBAT := ../../../../../tools/utils/fixbat.exe,专门处理 post_build 脚本的 utf8→gbk 编码问题;Linux 下 FIXBAT := touch 为空操作。如果 Windows 构建出现批处理乱码/语法错误,应确认 fixbat.exe 存在且被执行。

Source: apps/demo/transfer/board/bd47/Makefile

并发构建

顶层 all 目标按依赖顺序(先 transfer 后 hid)串行递归调用子 make,两个子工程产物路径不同(同名的 objs/ 各自位于子目录内),因此不会相互覆盖。但两个工程都向 apps/app/post_build/bd47/ 输出同名 sdk.elf——若用户手动并行执行 make -j2 aw31n_transfer aw31n_hid,后写者会覆盖前者的 ELF,因此不应并行构建两个子工程;顶层默认串行行为是安全的。

扩展点

新增 demo 子工程

顶层 Makefile 的设计使新增工程成本极低:在 apps/demo/<name>/board/bd47/ 下创建仿照现有工程的 Makefile(复用工具链分支、CFLAGS、INCLUDES 模板),然后在顶层 Makefile 的 .PHONY 中增加 <name> 与 clean_<name> 两个目标,并加入 all / clean 的依赖列表即可。子工程内的 ROOT_PREFIX、OUT_ELF、POST_SCRIPT 相对路径按目录层级对应调整。

修改构建参数

  • 切换优化策略:修改 CFLAGS 中的 -Oz / -Os / -O0 组合;
  • 裁剪功能:通过调整 DEFINES 中的宏(如 APP_CASE_TRANSFER、NOFLOAT)或增删 c_SRC_FILES 条目控制固件内容与体积;
  • 版本号:SDK_VERSION_CFG_DEFINE 与 SDK_VERSION_DATE_DEFINE 决定编译进固件的版本信息,发版前需同步更新。

相关链接

  • 顶层 Makefile
  • transfer 子工程 Makefile
  • hid 子工程 Makefile
  • 相关目录:apps/demo/transfer(透传 demo)、apps/demo/hid(HID demo)、apps/app/post_build/bd47/(烧录脚本与产物)、apps/app/bsp(BSP 源码)
Next
板级工程与配置