Ra-11 / Ra-11H CMake 开发环境搭建说明

本文档说明如何为 安信可 Ra-11 / Ra-11H(射频芯片 lr1121)搭建 USP / RAC 示例工程的 CMake 交叉编译环境,覆盖 Linux 与 Windows,并以 examples/main_examples/ping_pong_example 为例说明编译与固件产物;同时说明如何在本工程内 新建自定义示例工程。


获取工程源码(SDK)

开发前请先从 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) 安装依赖,再回来执行克隆。


1. 适用范围

1.1 适用单片机(MCU)

本工程通过 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/ 目录),否则可能沿用旧工具链缓存。

1.2 LoRa 射频芯片(驱动)

Ra-11 / Ra-11H 使用 LR11xx 系列,编译时固定选择:

-DRAC_RADIO= 芯片家族 典型模组
lr1121 LR11xx Ai-Thinker Ra-11 Ra-11H

对应驱动与抽象层位于:

部分示例仅对特定芯片开放(例如 cad_side_detector 支持 lr1121)。配置 cmake 后终端会打印 Available examples,以实际列表为准。


2. 工具总览(两端通用)

无论 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。


3. Linux 端环境搭建

以下以 Ubuntu / Debian / WSL2 为例。

3.1 安装依赖

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

3.2 各软件作用说明

软件包 作用
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 串口监视等辅助脚本(可选)

3.3 验证安装

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

3.4 推荐工作流(Linux)

# 假设已按上文克隆到 ~/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 节)。


4. Windows 端环境搭建

Windows 有两种常用路径:原生 Windows,或 WSL2(推荐,与 Linux 命令完全一致)。

4.1 方案一:WSL2(推荐)

  1. 安装 WSL2 与 Ubuntu(Microsoft Store 或 wsl --install)。
  2. 在 WSL 内按 第 3 节 Linux 安装工具即可。
  3. 源码可放在 Linux 文件系统(如 ~/project/...),避免放在 /mnt/c/... 导致编译变慢。
  4. ST-Link 烧录:可在 WSL 内使用 st-flash(需 USB 透传),或在 Windows 侧用 STM32CubeProgrammer 烧录 WSL 生成的 .bin/.hex(通过 \\wsl$\... 路径访问)。

编译命令与 Linux 相同,直接使用第 3.4 / 第 5 节的 cmake 指令。

4.2 方案二:原生 Windows

4.2.1 需要安装的软件

软件 作用 安装要点
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:

4.2.2 操作步骤(PowerShell 或 Git Bash)

  1. 确认工具在 PATH 中
arm-none-eabi-gcc --version
cmake --version
ninja --version
  1. 进入仓库并配置
# 假设已按上文克隆到 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
  1. 编译示例
cmake --build build --target ping_pong
  1. 烧录(示例:STM32CubeProgrammer CLI)
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。

4.2.3 使用 MSYS2 / MinGW 的补充说明

也可在 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 直接编固件。


5. 以 ping_pong 为例:编译与双设备收发

示例源码目录:examples/main_examples/ping_pong_example/
CMake 目标名:ping_pong

5.1 双设备如何配合

Ping-Pong 需要 两块板角色不同:

设备 角色 行为
设备 A Manager(默认) 上电发送 PING,等待对方回 PONG
设备 B Subordinate 上电先监听,收到 PING 后回 PONG

两端射频参数须一致(频率、SF、BW、编码率、sync word 等),见 main_ping_pong.h。

不要 两块板都用默认 Manager 固件,否则双方都发 PING、都等 PONG,无法正常互通。

5.2 编译命令(纯 CMake)

步骤 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

5.3 生成哪些固件?默认是什么格式?

编译 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

因此:

5.4 如何保证同时生成 hex 与 bin?

  1. 走本工程标准目标(推荐)
    使用 add_example 注册的目标(如 ping_pong),POST_BUILD 已配置,默认同时输出 hex + bin。

  2. 手动用 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
  1. 若只看到 ELF、没有 hex/bin
    • 确认编译的是 ping_pong 等 add_example 目标,而不是未挂钩 POST_BUILD 的临时目标。
    • 确认 PATH 中有 arm-none-eabi-objcopy(即 CMake 的 CMAKE_OBJCOPY)。
    • 查看编译日志是否出现 Creating ping_pong.hex... / Creating ping_pong.bin...。

5.5 烧录与运行

# Linux:烧录 bin(地址 0x08000000)
cmake --build build --target flash_ping_pong
# 或
st-flash --connect-under-reset write build/ping_pong.bin 0x8000000

建议流程:

  1. 设备 A 烧录 Manager 固件(未开 PING_PONG_START_AS_SUBORDINATE)
  2. 设备 B 烧录 Subordinate 固件(-DPING_PONG_START_AS_SUBORDINATE=ON)
  3. 先上电设备 B,再上电设备 A
  4. 打开串口查看日志,应看到收发成功与计数递增

更换 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

6. 新建工程(自定义示例)操作方法

本仓库的“工程”对应 examples/main_examples/ 下的一个示例目录,并通过 examples/CMakeLists.txt 的 add_example() 注册为可独立编译的目标。推荐做法:复制一个最接近的现有示例改名,再改业务逻辑,而不是从零搭 HAL/工具链。

6.1 目录与文件约定

假设新示例名为 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

6.2 操作步骤

步骤 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
)

说明:

可选(便于 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

产物:

烧录:

cmake --build build --target flash_hello_lora
# 或
st-flash --connect-under-reset write build/hello_lora.bin 0x8000000

6.3 检查清单

项 说明
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 避免缓存错误

6.4 不推荐的做法


7. 常用 CMake 参数速查

参数 含义 示例
-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

8. 常见问题

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。


9. 相关文档

文档 内容
根目录 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)。