本文档说明如何为 安信可 Ra-11 / Ra-11H(射频芯片 lr1121)搭建 USP / RAC 示例工程的 CMake 交叉编译环境,覆盖 Linux 与 Windows,并以 examples/main_examples/ping_pong_example 为例说明编译与固件产物;同时说明如何在本工程内 新建自定义示例工程。
开发前请先从 GitHub 克隆本工程(安信可 Ra-11 / Ra-11H / Ra-20 / Ra-20H USP / RAC SDK):
| 方式 | 地址 |
|---|---|
| HTTPS | https://github.com/Ai-Thinker-AIoT/ra11-ra20.git |
# 任选目录,例如 ~/project
mkdir -p ~/project && cd ~/project
# 或使用 HTTPS
git clone https://github.com/Ai-Thinker-AIoT/ra11-ra20.git
cd ra11-ra20
后续文档中的 cmake / ninja 命令均在该仓库根目录下执行(即包含 examples/、smtc_rac_lib/ 的目录)。
提示:若尚未安装 Git,可先按下文 第 3 节(Linux) 或 第 4 节(Windows) 安装依赖,再回来执行克隆。
本工程通过 CMake 参数 -DBOARD= 选择 MCU 与对应 HAL / 工具链。Ra-11 开发推荐:
-DBOARD= 取值 |
MCU / 开发板 | HAL 目录 | 说明 |
|---|---|---|---|
STM32F103CB |
STM32F103CBT6(Ai-Thinker Ra-11 / Ra-11H 常用) | examples/smtc_hal_f1 |
Ra-11 默认推荐 |
NUCLEO_L476 |
ST NUCLEO-L476RG | examples/smtc_hal_l4 |
Semtech 官方验证常用板(非 Ai-Thinker 板默认) |
NUCLEO_L073 |
ST NUCLEO-L073RZ | examples/smtc_hal_l0_LL |
可用,验证力度较弱 |
注意:更换
BOARD后必须 重新 cmake 配置(建议清空或换新的build/目录),否则可能沿用旧工具链缓存。
Ra-11 / Ra-11H 使用 LR11xx 系列,编译时固定选择:
-DRAC_RADIO= |
芯片家族 | 典型模组 |
|---|---|---|
lr1121 |
LR11xx | Ai-Thinker Ra-11 Ra-11H |
对应驱动与抽象层位于:
smtc_rac_lib/radio_drivers/ — 芯片寄存器级驱动 smtc_rac_lib/smtc_ral/ — Radio Abstraction Layer(RAL) smtc_rac_lib/smtc_rac/ — Radio Access Controller(RAC / USP)部分示例仅对特定芯片开放(例如 cad_side_detector 支持 lr1121)。配置 cmake 后终端会打印 Available examples,以实际列表为准。
无论 Linux 还是 Windows,CMake 交叉编译链路本质相同:
源码 (C/ASM)
→ cmake 配置(选 BOARD / RAC_RADIO / 工具链)
→ ninja(或 Make)编译
→ arm-none-eabi-gcc 链接出 ELF
→ arm-none-eabi-objcopy 生成 .hex / .bin
→ (可选)st-flash / STM32CubeProgrammer 烧录
| 工具 | 作用 |
|---|---|
ARM GNU Toolchain(arm-none-eabi-gcc 等) |
面向 Cortex-M 的交叉编译器、汇编器、链接器 |
| arm-none-eabi-objcopy | 将 ELF 转为 Intel HEX(.hex)或原始二进制(.bin) |
| CMake(≥ 3.25,工程要求) | 生成构建系统(Ninja / Makefile),管理依赖与板级/芯片选项 |
| Ninja(推荐)或 Make | 实际执行编译命令;Ninja 更快、日志更清晰 |
| Git | 拉取/管理源码 |
stlink-tools(st-flash)或 STM32CubeProgrammer |
通过 ST-Link 将固件写入 MCU Flash |
| 串口终端(minicom / screen / PuTTY / 串口助手) | 查看日志,验证 ping-pong 等示例 |
可选:
| 工具 | 作用 |
|---|---|
| ccache | 加速重复编译 |
| OpenOCD / gdb-multiarch | 在线调试(非编译必需) |
| Python3 + pyserial | 串口脚本、辅助烧录/日志工具 |
官方 README 建议参考版本大致为:gcc ≥ 13.3(发行版自带 10.x 一般也可用)、CMake ≥ 3.28(本工程最低 3.25)、Ninja ≥ 1.11。
以下以 Ubuntu / Debian / WSL2 为例。
sudo apt update
sudo apt install -y \
gcc-arm-none-eabi \
binutils-arm-none-eabi \
gdb-multiarch \
cmake \
ninja-build \
git \
stlink-tools \
python3 python3-pip
# 可选
sudo apt install -y ccache
pip3 install --user pyserial
| 软件包 | 作用 |
|---|---|
gcc-arm-none-eabi |
提供 arm-none-eabi-gcc / g++ / as,编译 STM32 固件 |
binutils-arm-none-eabi |
提供 arm-none-eabi-objcopy、objdump、size 等,生成 hex/bin、查看段信息 |
cmake |
读取 examples/CMakeLists.txt,配置 BOARD/RAC_RADIO 并生成 build.ninja |
ninja-build |
按依赖并行编译目标(如 ping_pong) |
stlink-tools |
st-flash / st-info,通过 ST-Link 烧录 .bin |
gdb-multiarch |
多架构 GDB,配合 OpenOCD/ST-Link 调试(可选) |
git |
版本管理 |
python3 + pyserial |
串口监视等辅助脚本(可选) |
arm-none-eabi-gcc --version
arm-none-eabi-objcopy --version
cmake --version # 建议 ≥ 3.25
ninja --version
st-info --version # 有 ST-Link 时
若工具链不在默认 PATH,可设置:
export PATH=/path/to/gcc-arm-none-eabi/bin:$PATH
# 假设已按上文克隆到 ~/project/ra11-ra20:
cd ~/project/ra11-ra20
# 首次或更换 BOARD / RAC_RADIO 时建议清理旧缓存
rm -rf build
# 配置(Ra-11:STM32F103CB + lr1121)
cmake -S examples -B build \
-G Ninja \
-DCMAKE_BUILD_TYPE=MinSizeRel \
-DBOARD=STM32F103CB \
-DRAC_RADIO=lr1121
# 编译目标
cmake --build build --target ping_pong
产物默认在 build/ 目录(见第 5 节)。
Windows 有两种常用路径:原生 Windows,或 WSL2(推荐,与 Linux 命令完全一致)。
wsl --install)。 ~/project/...),避免放在 /mnt/c/... 导致编译变慢。 st-flash(需 USB 透传),或在 Windows 侧用 STM32CubeProgrammer 烧录 WSL 生成的 .bin/.hex(通过 \\wsl$\... 路径访问)。编译命令与 Linux 相同,直接使用第 3.4 / 第 5 节的 cmake 指令。
| 软件 | 作用 | 安装要点 |
|---|---|---|
| ARM GNU Toolchain(arm-none-eabi) | 交叉编译 STM32 | 从 ARM Developer 下载 Windows 版;安装后把 bin 加入系统 PATH |
| CMake | 工程配置 | 安装时勾选 Add CMake to the system PATH;版本建议 ≥ 3.25 |
| Ninja | 构建执行 | 下载 ninja.exe 放到 PATH,或用 winget install Ninja-build.Ninja / Scoop / Chocolatey |
| Git for Windows | 拉取源码 | 可选附带 Git Bash |
| STM32CubeProgrammer 或 ST-Link Utility / stlink | 烧录固件 | CubeProgrammer 官方图形/CLI 最稳妥 |
| 串口工具(PuTTY、MobaXterm、串口助手等) | 查看日志 | 选择正确 COM 口与波特率 |
| (可选)Python 3 | 辅助脚本 | 勾选 Add to PATH;pip install pyserial |
可选 IDE:
arm-none-eabi-gcc --version
cmake --version
ninja --version
# 假设已按上文克隆到 D:\project\ra11-ra20:
cd D:\project\ra11-ra20
# 首次或更换芯片/板型时清理旧缓存
Remove-Item -Recurse -Force build -ErrorAction SilentlyContinue
cmake -S examples -B build `
-G Ninja `
-DCMAKE_BUILD_TYPE=MinSizeRel `
-DBOARD=STM32F103CB `
-DRAC_RADIO=lr1121
cmake --build build --target ping_pong
STM32_Programmer_CLI -c port=SWD -w build\ping_pong.bin 0x08000000 -v -rst
或在 CubeProgrammer 图形界面中打开 build\ping_pong.hex / .bin,接口选 ST-Link SWD,地址 0x08000000。
提示:若 cmake 找不到编译器,可先设置:
$env:Path = "C:\Program Files (x86)\Arm GNU Toolchain arm-none-eabi\...\bin;" + $env:Path
本工程一般通过工具链文件自动设置arm-none-eabi-gcc。
也可在 MSYS2 中安装:
pacman -S mingw-w64-ucrt-x86_64-cmake mingw-w64-ucrt-x86_64-ninja
# ARM 工具链仍建议使用官方 arm-none-eabi,不要用主机 gcc 编 STM32
交叉编译器必须是 arm-none-eabi-*,不能用 x86_64 的 gcc 直接编固件。
示例源码目录:examples/main_examples/ping_pong_example/
CMake 目标名:ping_pong
Ping-Pong 需要 两块板角色不同:
| 设备 | 角色 | 行为 |
|---|---|---|
| 设备 A | Manager(默认) | 上电发送 PING,等待对方回 PONG |
| 设备 B | Subordinate | 上电先监听,收到 PING 后回 PONG |
两端射频参数须一致(频率、SF、BW、编码率、sync word 等),见 main_ping_pong.h。
不要 两块板都用默认 Manager 固件,否则双方都发
PING、都等PONG,无法正常互通。
步骤 1 — 编译设备 A(Manager,默认)
Ra-11(lr1121)+ STM32F103CB:
rm -rf build
cmake -S examples -B build \
-G Ninja \
-DCMAKE_BUILD_TYPE=MinSizeRel \
-DBOARD=STM32F103CB \
-DRAC_RADIO=lr1121
cmake --build build --target ping_pong
产物:build/ping_pong.bin、build/ping_pong.hex。请先保存/烧录到设备 A,例如:
cp build/ping_pong.bin build/ping_pong_manager.bin
cp build/ping_pong.hex build/ping_pong_manager.hex
步骤 2 — 编译设备 B(Subordinate)
在配置中打开 -DPING_PONG_START_AS_SUBORDINATE=ON 后重新编译(需重新 configure):
rm -rf build
cmake -S examples -B build \
-G Ninja \
-DCMAKE_BUILD_TYPE=MinSizeRel \
-DBOARD=STM32F103CB \
-DRAC_RADIO=lr1121 \
-DPING_PONG_START_AS_SUBORDINATE=ON
cmake --build build --target ping_pong
产物仍为 build/ping_pong.bin / .hex,烧录到设备 B。
不要使用 -DBOARD=NUCLEO_L476 测试 Ai-Thinker Ra-11 板。 NUCLEO_L476 面向 ST NUCLEO-L476RG;在本仓库当前引脚配置下,该板型编译 ping_pong 可能因 HW_MODEM_* 宏未定义而失败。
Ra-11 请统一使用 -DBOARD=STM32F103CB 与 -DRAC_RADIO=lr1121。
列出当前配置下可用目标:
cmake --build build --target help
编译 ping_pong 成功后,build/ 下通常包含:
| 文件 | 格式 | 说明 |
|---|---|---|
build/ping_pong(或 ping_pong.elf 视生成器而定) |
ELF | 链接器直接产物,含调试符号,供 GDB 使用 |
build/ping_pong.bin |
原始二进制 | 从 Flash 起始地址起的连续镜像;st-flash 常用 |
build/ping_pong.hex |
Intel HEX | 带地址记录的文本格式;CubeProgrammer / 部分烧录器常用 |
默认情况:本工程在 add_example() 的 POST_BUILD 中会同时生成 .hex 与 .bin,并非只生成一种。
对应逻辑(摘自 examples/CMakeLists.txt):
# HEX
COMMAND ${CMAKE_OBJCOPY} -O ihex
$<TARGET_FILE:${TARGET_NAME}>
${CMAKE_BINARY_DIR}/${TARGET_NAME}.hex
# BIN
COMMAND ${CMAKE_OBJCOPY} -O binary -S
$<TARGET_FILE:${TARGET_NAME}>
${CMAKE_BINARY_DIR}/${TARGET_NAME}.bin
因此:
flash_<target> 使用 st-flash + .bin;很多 GUI 工具更偏好 .hex。两种都已生成,按工具选择即可。走本工程标准目标(推荐)
使用 add_example 注册的目标(如 ping_pong),POST_BUILD 已配置,默认同时输出 hex + bin。
手动用 objcopy(调试或自定义目标时)
# 假设 ELF 为 build/ping_pong
arm-none-eabi-objcopy -O ihex build/ping_pong build/ping_pong.hex
arm-none-eabi-objcopy -O binary -S build/ping_pong build/ping_pong.bin
ping_pong 等 add_example 目标,而不是未挂钩 POST_BUILD 的临时目标。 arm-none-eabi-objcopy(即 CMake 的 CMAKE_OBJCOPY)。 Creating ping_pong.hex... / Creating ping_pong.bin...。# Linux:烧录 bin(地址 0x08000000)
cmake --build build --target flash_ping_pong
# 或
st-flash --connect-under-reset write build/ping_pong.bin 0x8000000
建议流程:
PING_PONG_START_AS_SUBORDINATE) -DPING_PONG_START_AS_SUBORDINATE=ON) 更换 BOARD 或重新配置参数后务必清理再编译:
rm -rf build
cmake -S examples -B build \
-G Ninja \
-DCMAKE_BUILD_TYPE=MinSizeRel \
-DBOARD=STM32F103CB \
-DRAC_RADIO=lr1121
cmake --build build --target ping_pong
本仓库的“工程”对应 examples/main_examples/ 下的一个示例目录,并通过 examples/CMakeLists.txt 的 add_example() 注册为可独立编译的目标。推荐做法:复制一个最接近的现有示例改名,再改业务逻辑,而不是从零搭 HAL/工具链。
假设新示例名为 hello_lora(可自定,下文以此为例):
examples/
├── main.c # 公共入口,按 MAKEFILE_APP 分发到各示例
├── main.h # 各示例 APP 编号与 main_xxx() 声明
├── CMakeLists.txt # add_example() 注册目标
└── main_examples/
└── hello_lora_example/ # 新建目录
├── main_hello_lora.h
├── main_hello_lora.c # 初始化 HAL / RAC,主循环
├── app_hello_lora.h
└── app_hello_lora.c # 射频业务(TX/RX/CAD 等)
可参考的模板:
| 需求 | 建议参考 |
|---|---|
| LoRa 收发、状态机 | ping_pong_example |
| 连续发波 / 简单 TX | tx_cw_example |
步骤 1 — 复制并改名
cd examples/main_examples
cp -r ping_pong_example hello_lora_example
cd hello_lora_example
# 按需重命名文件,例如:
# main_ping_pong.c -> main_hello_lora.c
# app_ping_pong.c -> app_hello_lora.c
# 并全局替换函数名 / 头文件名 / 日志前缀
步骤 2 — 实现入口函数
在 main_hello_lora.c 中提供对外入口(名称需与 main.h / main.c 一致),典型结构:
void main_hello_lora( void )
{
hal_mcu_init( );
smtc_rac_init( );
/* 初始化 LED、按键等(可选) */
hello_lora_init( ); /* 在 app_hello_lora.c 中实现 */
while( true )
{
hal_watchdog_reload( );
smtc_rac_run_engine( ); /* 必须周期调用,驱动 RAC / radio_planner */
}
}
业务侧通过 smtc_rac_open_radio()、smtc_rac_get_context()、smtc_rac_submit_radio_transaction() 等 API 配置 LoRa 参数并收发(写法对齐 app_ping_pong.c)。
步骤 3 — 在 examples/main.h 注册应用编号
在现有 #define 列表末尾增加 未占用 的编号,并声明入口:
#define HELLO_LORA 19 /* 数值需唯一,勿与已有 APP 冲突 */
void main_hello_lora( void );
步骤 4 — 在 examples/main.c 增加分支
add_example() 会给该目标定义 MAKEFILE_APP=HELLO_LORA,公共 main() 据此调用你的入口:
#elif MAKEFILE_APP == HELLO_LORA
main_hello_lora( );
放在现有 #elif 链中、#else 的 #error 之前。
步骤 5 — 在 examples/CMakeLists.txt 注册目标
在「Define all examples」区域增加(路径按你的目录调整):
add_example(hello_lora "HELLO_LORA"
"${CMAKE_CURRENT_SOURCE_DIR}/main_examples/hello_lora_example/main_hello_lora.c;${CMAKE_CURRENT_SOURCE_DIR}/main_examples/hello_lora_example/app_hello_lora.c"
)
target_include_directories(hello_lora PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/main_examples/hello_lora_example
)
说明:
hello_lora:Ninja/CMake 目标名(编译、flash_hello_lora 均用此名) "HELLO_LORA":必须与 main.h 中的宏名一致(大小写不敏感,cmake 内会 TOUPPER) .c 源文件列表(分号分隔);main.c、HAL、RAC 等由 add_example() 自动加入可选(便于 all_examples / 日志提示):
# 将 hello_lora 加入 ALL_EXAMPLES 列表
list(APPEND ALL_EXAMPLES hello_lora)
message(STATUS " - hello_lora : custom hello LoRa example")
若示例仅支持 Ra-11(lr1121),可用条件包裹,例如:
if(${RAC_RADIO} MATCHES "^lr1121$")
add_example(hello_lora "HELLO_LORA" "...")
target_include_directories(hello_lora PRIVATE ...)
endif()
步骤 6 — 重新配置并编译
修改 CMakeLists.txt / main.h / main.c 后需重新 configure:
rm -rf build
cmake -S examples -B build \
-G Ninja \
-DCMAKE_BUILD_TYPE=MinSizeRel \
-DBOARD=STM32F103CB \
-DRAC_RADIO=lr1121
cmake --build build --target hello_lora
产物:
build/hello_lora(ELF) build/hello_lora.bin build/hello_lora.hex烧录:
cmake --build build --target flash_hello_lora
# 或
st-flash --connect-under-reset write build/hello_lora.bin 0x8000000
| 项 | 说明 |
|---|---|
main.h APP 编号唯一 |
与其它 #define 不重复 |
main.h 声明与 main.c 分支 |
名称与 HELLO_LORA / main_hello_lora 一致 |
add_example 第二参数 |
与 main.h 宏名对应(如 "HELLO_LORA") |
target_include_directories |
能找到本目录头文件 |
主循环调用 smtc_rac_run_engine() |
否则收发回调不会执行 |
| 更换 BOARD/RAC_RADIO 后清 build | 避免缓存错误 |
BOARD / HAL,而不是旁路现有 add_example)。 example 聚合目标里的 -DAPP= 却忘记用 add_example 注册独立目标(日常开发更建议独立目标,如 ping_pong)。 examples/main.c;业务放在 main_examples/<your>_example/。| 参数 | 含义 | 示例 |
|---|---|---|
-DBOARD= |
MCU 板型 | STM32F103CB(Ra-11 推荐) |
-DRAC_RADIO= |
LoRa 芯片 | lr1121 |
-DCMAKE_BUILD_TYPE= |
优化级别 | MinSizeRel(推荐)、Debug、Release |
-G Ninja |
生成器 | 也可用 Unix Makefiles |
-DPING_PONG_START_AS_SUBORDINATE= |
ping_pong 角色 | OFF=Manager(默认),ON=Subordinate |
Q: cmake 报错 “You have to define a -DBOARD=…”
A: 必须显式传入 -DBOARD=STM32F103CB(或其它支持板型)。
Q: cmake 报错 “You must select the radio with -DRAC_RADIO=”
A: Ra-11 必须传入 -DRAC_RADIO=lr1121。
Q: 更换配置后仍链接旧驱动 / 行为异常
A: 删除 build/ 后重新 configure。CMakeCache 会缓存旧的 RAC_RADIO/BOARD。
Q: 使用 -DBOARD=NUCLEO_L476 配置/编译失败?
A: Ra-11 请用 -DBOARD=STM32F103CB。NUCLEO_L476 走另一套 HAL;当前 modem_pinout.h 中 HW_MODEM_* 被注释时,L4 UART 可能编译失败。这不是 ping_pong 指令语法错误,而是板型与引脚配置不匹配。
Q: cmake 报错找不到 axk_cmd_example/...?
A: 该可选示例目录不在本树中。CMakeLists.txt 已在源码缺失时跳过该目标;请 rm -rf build 后重新配置。
Q: 两块板 ping-pong 一直 unexpected payload
A: 多为两块都烧了默认 Manager 固件。请一块用默认配置编译,另一块加 -DPING_PONG_START_AS_SUBORDINATE=ON 后重新 configure 再编译。日志里 prefix_received='80' 用十进制打印时对应 ASCII 'P'(PING/PONG 的首字节)。
Q: 新建工程后链接报 undefined reference to main_xxx?
A: 检查 main.c 是否已 #elif MAKEFILE_APP == YOUR_APP 调用 main_xxx(),且 add_example 的 APP 字符串与 main.h 宏一致。
Q: 新建工程编译通过但射频无反应?
A: 确认主循环调用了 smtc_rac_run_engine(),并检查 RAC_RADIO=lr1121、频率/SF/BW 与硬件天线匹配。
Q: Windows 下如何编译?
A: 推荐 WSL2,命令与 Linux 相同;或按第 4.2 节在原生 Windows 安装工具链后直接使用 cmake + ninja。
| 文档 | 内容 |
|---|---|
| 根目录 README_cn.md | USP 总览、官方板级与示例列表 |
| 开发环境搭建说明 | CMake开发环境搭建 |
| examples/main_examples/ping_pong_example/readme.md | Ping-Pong 中文详细说明 |
| examples/main_examples/ping_pong_example/FAQ.md | Ping-Pong 常见问题 |
| doc/usp_porting_guide.md | 移植与 PA/频段注意点 |
文档对应工程:USP/RAC 示例 — Ai-Thinker Ra-11 / Ra-11H(-DBOARD=STM32F103CB -DRAC_RADIO=lr1121)。