Files
gd32e23x_template_cmake_vscode/README.md
T

468 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# GD32E23x 工程模板
本仓库为兆易创新 GD32E23x 系列 MCU 的 CMake + VSCode 工程模板,适合嵌入式开发快速上手和团队协作。
---
## 目录
- [适用范围](#适用范围)
- [默认配置](#默认配置)
- [快速开始](#快速开始)
- [工程结构](#工程结构)
- [板级配置](#板级配置)
- [Flash 偏移配置(配合 Bootloader)](#flash-偏移配置配合-bootloader)
- [工具链准备](#工具链准备)
- [工具链切换](#工具链切换)
- [使用说明](#使用说明)
- [时钟配置说明](#时钟配置说明)
- [vcpkg 依赖管理(可选)](#vcpkg-依赖管理可选)
---
## 适用范围
- 适用于兆易创新 GD32E23x 系列 Cortex-M23 内核单片机
- 支持标准外设库开发
- 推荐开发环境:VSCode + CMake + ARM GCC 工具链
---
## 默认配置
- MCU 主频:内部 RC 振荡器,系统时钟配置为 72MHz
- 调试串口:USART0(PA2 TX / PA3 RX),115200 波特率
- I2C:默认硬件 I2C0(PF0 SDA / PF1 SCL),可通过 `board_config.h` 切换为软件 I2C
---
## 快速开始
### 基于模板创建新项目
1. **克隆或复制本仓库**
```bash
git clone https://gitea.hulk.wang/hulk/gd32e23x_template_cmake_vscode.git my-new-project
cd my-new-project
```
2. **修改项目配置** — 编辑 `cmake/project_config.cmake`:
```cmake
set(PROJECT_NAME "MyProject") # 项目名称
set(BOARD_TYPE_CODE 20) # 板卡类型码(协议帧中的标识)
set(VERSION_MAJOR 1) # 主版本号
set(VERSION_MINOR 0) # 次版本号
set(VERSION_PATCH 0) # 修订号
set(BUILD_VARIANT "APP") # 编译变体
```
3. **添加业务源文件** — 编辑 `CMakeLists.txt`,在 `TARGET_SRC` 中添加你的 `.c` 文件。
4. **配置板级引脚** — 编辑 `Inc/board_config.h`,修改 I2C、UART、LED 等引脚定义。
5. **编译**
```bash
cmake --preset Debug
cmake --build Build/Debug
```
产物在 `Build/Debug/` 下,包含 `.elf`、`.hex`、`.bin`、`.map`、`.list`。
### 分支说明
| 分支 | 用途 |
|------|------|
| `main` | **主模板**(推荐),用于桌面端 VSCode 开发 |
| `codex/*` | 功能开发和合并前验证分支 |
---
## 工程结构
```
.
├── CMakeLists.txt # 主构建文件
├── CMakePresets.json # CMake 预设(Debug/Release)
├── cmake/
│ ├── arm-none-eabi-gcc.cmake # ARM GCC 工具链配置
│ ├── project.cmake # 编译选项(-Os/-O0, -mcpu=cortex-m23)
│ ├── project_config.cmake # 项目名/版本号/编译变体
│ └── version.h.in # 自动生成固件版本头
├── Inc/ # 头文件
│ ├── board_config.h # 板级引脚定义 + 功能开关
│ ├── command.h # 串口命令协议
│ ├── i2c.h / led.h / systick.h / uart.h
│ └── uart_ring_buffer.h / gd32e23x_it.h / gd32e23x_libopt.h
├── Src/ # 源码
│ ├── main.c # 入口函数
│ ├── command.c # 命令解析处理
│ ├── board_config.c # MCU 型号自动检测
│ ├── i2c.c / led.c / systick.c / uart.c / uart_ring_buffer.c
│ └── gd32e23x_it.c / system_gd32e23x.c / syscalls.c
├── SDK/
│ ├── CMSIS/ # ARM CMSIS Core (Cortex-M23) + GD 启动文件
│ └── GD32E23x_standard_peripheral/ # GD32 标准外设库
├── LD/gd32e23x_flash.ld # 链接脚本
├── doc/ # 芯片数据手册
└── .vscode/ # VSCode 调试/烧录配置
```
---
## 板级配置(`Inc/board_config.h`)
所有功能开关和引脚定义集中在 `Inc/board_config.h`。功能开关均为单行数值宏:将右侧的 `ENABLE` 或 `DISABLE` 改为另一值即可;其中 `ENABLE` 为 `1`,`DISABLE` 为 `0`。以下为完整的宏开关说明和推荐使用方式。
### 功能开关速查表
| 宏 | 作用 | 默认值 | Release 建议 |
|---|------|:---:|:---:|
| `SOFTWARE_IIC` | I2C 实现方式 | `DISABLE`(硬件) | `DISABLE` |
| `DEBUG_MODE` | printf 串口输出 | `DISABLE` | `DISABLE` |
| `COM_DEBUG` | 命令帧调试打印 | `DISABLE` | `DISABLE` |
| `DEBUG_VERBOSE` | 详细调试信息 | `DISABLE` | `DISABLE` |
| `SEGGER_RTT_DETECTION` | SEGGER RTT 支持 | `ENABLE` | `DISABLE` |
---
### `SOFTWARE_IIC` — I2C 实现方式
选择 I2C 使用硬件外设还是软件 GPIO 模拟。
> 当前工程尚未实现 `SOFTWARE_IIC` 的条件编译驱动选择;该宏已迁移为数值配置,但改值不会在此版本切换 I2C 实现。
```c
#define SOFTWARE_IIC DISABLE // DISABLE: 硬件 I2C(默认);ENABLE: 软件 I2C(GPIO 模拟)
```
| 选项 | 优点 | 缺点 |
|------|------|------|
| 硬件 I2C | DMA 支持、CPU 占用低 | 仅限固定引脚、调试复杂 |
| 软件 I2C | 任意 GPIO、移植方便 | CPU 占用高、速率受限 |
> 切换后需同步修改下方 I2C 引脚定义。
---
### `DEBUG_MODE` — 调试模式
开启后 USART0 输出 printf 调试信息。**Release 固件必须关闭。**
```c
#define DEBUG_MODE DISABLE // DISABLE: 关闭(默认);ENABLE: 开启调试输出
```
**影响范围:**
- 使能 `USART0` 初始化和 `printf` 重定向到串口
- 会占用 PA2/PA3 引脚和 USART0 硬件资源
- 增加 ROM 约 2~4KB(取决于 printf 调用量)
---
### `COM_DEBUG` — 命令帧调试打印
开启后串口命令解析过程打印每帧的详细内容(地址、长度、数据、校验)。**仅调试通信协议时开启。**
```c
#define COM_DEBUG DISABLE // DISABLE: 关闭(默认);ENABLE: 开启命令帧调试
```
**依赖:** 需要先开启 `DEBUG_MODE`,否则输出无法外发。
**输出示例:**
```text
[CMD] ADDR=01 LEN=05 DATA: AA BB CC DD EE CHK=OK
```
> ⚠️ Release 必须关闭,否则大量串口输出会严重拖慢主循环。
---
### `DEBUG_VERBOSE` — 详细调试信息
在 `DEBUG_MODE` 基础上输出更底层的信息,如 I2C 总线扫描结果、MCU 型号识别等。
```c
#define DEBUG_VERBOSE DISABLE // DISABLE: 关闭(默认);ENABLE: 开启详细调试
```
**额外输出:**
- 启动时打印 MCU Flash 容量检测结果
- I2C 初始化时扫描总线上的设备地址
- 其他诊断信息
> 依赖 `DEBUG_MODE`,开启后 ROM 进一步增加约 1~2KB。
---
### `SEGGER_RTT_DETECTION` — SEGGER RTT 支持
RTT(Real-Time Transfer)是 SEGGER 的调试通道技术,通过 SWD 接口传输数据,不占用串口引脚,速度远超 UART。
```c
#define SEGGER_RTT_DETECTION ENABLE // DISABLE: 禁用 RTT;ENABLE: 启用 RTT(默认)
```
**启用时:**
- 自动包含 `SEGGER_RTT.h`,提供 `RTT_printf` / `RTT_WriteString` / `RTT_PutChar` 宏
- `SDK/SEGGER_RTT/` 模块参与编译和链接
- 可用 J-Link RTT Viewer 或 VSCode + cortex-debug 查看实时日志
**禁用时:**
- 应用层 RTT 头文件引用与调用代码均在预处理阶段排除
- RTT 宏展开为空操作;应用目标不生成 RTT 调用代码
- 为保持现有 CMake SDK 加载方式,`SDK/SEGGER_RTT/` 仍会参与构建;静态库中未被引用的对象不会被链接器提取
> **Release 建议关闭** — RTT 依赖调试器连接,量产固件中无意义且占用 ROM。
**依赖关系总览:**
```
COM_DEBUG ──── 依赖 ──→ DEBUG_MODE
DEBUG_VERBOSE ─ 依赖 ──→ DEBUG_MODE
SEGGER_RTT_DETECTION ─ 独立,与 DEBUG_MODE 并行
```
---
### 引脚定义
所有引脚宏集中在 `board_config.h` 尾部,按需修改:
```c
/* I2C */
#define I2C_SCL_PORT GPIOF
#define I2C_SCL_PIN GPIO_PIN_1
#define I2C_SDA_PORT GPIOF
#define I2C_SDA_PIN GPIO_PIN_0
/* LED */
#define LED_RCU RCU_GPIOB
#define LED_PORT GPIOB
#define LED_PIN GPIO_PIN_1
/* UART */
#define UART_GPIO_PORT GPIOA
#define UART_TX_PIN GPIO_PIN_2
#define UART_RX_PIN GPIO_PIN_3
#define UART_BAUDRATE 115200U
```
### MCU 型号自动检测
`board_config.c` 中的 `mcu_detect_and_config()` 上电自动识别 GD32E230 的 Flash 容量(F4=16K / F6=32K / F8=64K),结果存入全局变量 `g_mcu_flash_size`,并自动选择对应的 UART 外设(USART0 或 USART1)。
---
## Flash 偏移配置(配合 Bootloader)
如果固件需要通过 Bootloader 启动(Bootloader 占用 Flash 前部区域),需修改两处:
**1. 链接脚本** — `LD/gd32e23x_flash.ld` 第 15 行:
```c
// 默认:从 Flash 起始运行
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 16K
// 配合 Bootloader:前 8KB 留给 Bootloader,App 从 0x08002000 开始
FLASH (rx) : ORIGIN = 0x08002000, LENGTH = 8K
```
**2. 向量表偏移** — `Src/system_gd32e23x.c` 第 44 行:
```c
// 默认
#define VECT_TAB_OFFSET (uint32_t)0x00
// 配合 Bootloader(值 = Flash 偏移量,不含 0x0800 前缀)
#define VECT_TAB_OFFSET (uint32_t)0x2000
```
> ⚠️ 两个偏移值必须对应修改:`LD` 中的 `ORIGIN` 减去 `0x08000000` 应等于 `VECT_TAB_OFFSET`。
---
## 工具链准备
### 1. ARM GCC 工具链
- **版本**:xpack-arm-none-eabi-gcc-11.3.1-1.1
- **安装位置**:每台电脑本机任意目录,工具包不放入工程仓库
- **官方下载地址**:https://github.com/xpack-dev-tools/arm-none-eabi-gcc-xpack/releases
- **活动变量**:`GD32_GCC_BIN`,值为工具链的 `bin` 目录
### 2. OpenOCD(调试/烧录)
- **版本**:xpack-openocd-0.11.0-3
- **安装位置**:每台电脑本机任意目录,工具包不放入工程仓库
- **获取地址**:https://github.com/burakenez/gd32-tools-xpack-openocd/tree/v0.11.0-3
- **活动变量**:`GD32_OPENOCD_BIN`、`GD32_OPENOCD_SCRIPTS`
- **版本说明**:GD32 支持依赖发行版补丁,优先使用已验证的 0.11.0-3;更换版本前应先单独验证目标芯片和调试器。
OpenOCD 的 cfg 文件由工程自己的 `.vscode/settings.json` 指定,环境变量只提供 `scripts` 根目录。模板默认使用 `target/openocd_gdlink_gd32e23x.cfg`;其它芯片工程只需修改 `gd32.openocd.targetConfig`,例如 `target/openocd_gdlink_gd32f4xx.cfg`,不需要修改环境变量。
cfg 必须可以直接启动完整会话,包含调试器 interface 和目标芯片配置;只有 `target/gd32e23x.cfg` 这种 target-only 文件不能单独启动调试会话。
例如 CMSIS-DAP 的 wrapper cfg 可以写成(使用下述硬件复位任务时,还需追加后面的 RST 配置):
```tcl
source [find interface/cmsis-dap.cfg]
source [find target/gd32e23x.cfg]
```
模板的 `Flash MCU`、`Reset MCU` 和 `Mass Erase MCU` 任务默认使用 RST 硬件复位:OpenOCD
连接时保持复位,连接后松开 RST,并等待 200 ms 后执行烧录、复位或擦除操作。每台开发机需要满足以下条件:
- 调试器的 RST 线连接到 MCU 的 NRST。
- `gd32.openocd.targetConfig` 指定的 cfg 包含以下配置:
```tcl
adapter srst delay 100
reset_config srst_only srst_nogate connect_assert_srst
```
- 若固件会复用 SWD 引脚,松开复位后应保留至少 2 秒的调试挂接窗口。
cfg 位于 `GD32_OPENOCD_SCRIPTS` 指向的工具目录中,不随工程仓库分发;更换电脑或 OpenOCD
版本后需要再次确认上述配置。未连接 RST 线的硬件应改用软复位配置。
每台开发电脑配置一次用户环境变量(路径按本机实际位置填写):
```powershell
[Environment]::SetEnvironmentVariable("GD32_GCC_BIN", "C:\toolchain\xpack-arm-none-eabi-gcc-11.3.1-1.1\bin", "User")
[Environment]::SetEnvironmentVariable("GD32_OPENOCD_BIN", "D:\Tools\xpack-openocd-0.11.0-3\bin", "User")
[Environment]::SetEnvironmentVariable("GD32_OPENOCD_SCRIPTS", "D:\Tools\xpack-openocd-0.11.0-3\scripts", "User")
```
重新打开 VSCode,使新环境变量进入 VSCode 进程。`.vscode` 中的编译、GDB、OpenOCD 任务只读取上述 `GD32_*` 变量;OpenOCD 的通用变量 `OPENOCD_SCRIPTS` 仅在任务/调试子进程中覆盖,因此不会继承 STM32Cube 或 ESP-IDF 的全局值。
## 工具链切换
环境变量只是字符串,不同变量指向同一个目录完全没有问题。建议给每套工具链保留一个有意义的变量名,再把其中一套设为活动变量:
```powershell
[Environment]::SetEnvironmentVariable("GD32_GCC_XPACK11_BIN", "C:\toolchain\xpack-arm-none-eabi-gcc-11.3.1-1.1\bin", "User")
[Environment]::SetEnvironmentVariable("GD32_GCC_GNU12_BIN", "C:\toolchain\gcc-arm-none-eabi\bin", "User")
# 当前使用 xPack GCC 11.3.1
[Environment]::SetEnvironmentVariable("GD32_GCC_BIN", "C:\toolchain\xpack-arm-none-eabi-gcc-11.3.1-1.1\bin", "User")
# 切换到 GNU Arm GCC 12.3.1 时,把 GD32_GCC_BIN 改为对应路径
[Environment]::SetEnvironmentVariable("GD32_GCC_BIN", "C:\toolchain\gcc-arm-none-eabi\bin", "User")
```
切换后重新打开 VSCode,并对 Debug/Release 执行一次 `Delete Cache and Reconfigure`,因为 CMake 会把编译器绝对路径写入构建目录缓存。CMake 也支持临时覆盖环境变量:
```powershell
cmake --preset Debug -DGD32_GCC_ENV_VAR=GD32_MY_GCC_BIN
cmake --preset Debug -DTOOLCHAIN_DIRECTORY="C:/toolchain/gcc-arm-none-eabi/bin"
```
工程变量统一使用 `GD32_` 前缀。`GD32_GCC_ENV_VAR` 只影响 CMake 配置;`.vscode` 的 GDB 路径仍使用规范变量 `GD32_GCC_BIN`,如采用自定义变量名,建议同时让 `GD32_GCC_BIN` 指向同一目录。GCC、GDB、`ar`、`ld`、`objcopy` 等 binutils 必须来自同一套发行版,不要混用不同版本的组件。
### 环境要求清单
- **必须配置的环境变量**
| 变量名 | 值 | 用途 |
|---|---|---|
| `GD32_GCC_BIN` | 活动 ARM GCC 的 `bin` 目录 | CMake 编译、GDB 调试、binutils |
| `GD32_OPENOCD_BIN` | 包含 `openocd.exe` 的 `bin` 目录 | OpenOCD 任务/调试 |
| `GD32_OPENOCD_SCRIPTS` | OpenOCD `scripts` 根目录 | `-s` 脚本搜索路径 |
- **可选的环境变量**:为不同 GCC 版本分别建立 `GD32_GCC_XPACK11_BIN`、`GD32_GCC_GNU12_BIN` 等变量,需要切换时手动把选中的路径写入 `GD32_GCC_BIN`。`GD32_GCC_ENV_VAR` 仅在需要让 CMake 使用另一个 `GD32_*` 变量名时设置。
- ARM GCC 必须是 `arm-none-eabi` 交叉工具链;`GD32_GCC_BIN` 及其它 `GD32_*_BIN` 变量必须指向 `bin` 目录,以同时支持 CMake 和 VSCode 调试。仅通过 CMake 的 `TOOLCHAIN_DIRECTORY` 参数配置时,可以传工具链根目录或 `bin` 目录。`gcc`、`g++`、`gdb`、`ar`、`as`、`ld`、`objcopy`、`objdump`、`size`、`ranlib` 必须齐全。
- 当前工程已验证 xPack GCC 11.3.1 和 GNU Arm GCC 12.3.1;链接脚本使用 GCC 11+ 支持的 `READONLY` 语法。更换更新版本仍需在目标板上验证编译、链接和调试。
- OpenOCD 需要同时提供 `GD32_OPENOCD_BIN`、`GD32_OPENOCD_SCRIPTS`;任务会在 bin 目录下使用固定文件名 `openocd.exe`,每个工程在 `.vscode/settings.json` 中设置相对 `scripts` 根目录的 `gd32.openocd.targetConfig`,不依赖全局 `OPENOCD_SCRIPTS`。
- CMake ≥ 3.20、Ninja、VSCode CMake Tools,以及 cortex-debug(使用 OpenOCD 调试时)必须已安装;J-Link 任务另需 `JLink.exe` 在 PATH 中。
- J-Link 的 `device`、`interface`、`speed` 是工程属性,配置在 `.vscode/settings.json` 的 `gd32.jlink.*` 中;换 GD32F405 等芯片时只修改这些设置和对应的 OpenOCD target cfg。OpenOCD 的擦除命令同样通过 `gd32.openocd.massEraseCommand` 按芯片调整。
- 环境变量建议写入用户级环境(`User`),设置后重启 VSCode;切换 GCC 后必须清理对应 Debug/Release 构建缓存。
---
## 使用说明
### 编译
```bash
# Debug 构建(-O0, -g3)
cmake --preset Debug
cmake --build Build/Debug
# Release 构建(-Os, -g0)
cmake --preset Release
cmake --build Build/Release
```
### 烧录
通过 VSCode 任务栏运行 `Flash MCU` 任务,或使用同样的硬件复位时序从命令行烧录:
```powershell
& "$env:GD32_OPENOCD_BIN\openocd.exe" -s "$env:GD32_OPENOCD_SCRIPTS" -f "target/openocd_gdlink_gd32e23x.cfg" -c "init; adapter deassert srst; sleep 200; catch {reset halt}; reset halt; flash write_image erase Build/Debug/Application/Application.elf; reset; exit"
```
### 产物
编译输出位于 `Build/<Config>/`:
| 文件 | 说明 |
|------|------|
| `Application.elf` | ELF 固件(调试用) |
| `{项目名}_{版本}_{编译条件}_{日期}.hex` | Hex 文件 |
| `{项目名}_{版本}_{编译条件}_{日期}.bin` | 二进制文件 |
| `{项目名}_{版本}_{编译条件}_{日期}.list` | 反汇编清单 |
| `{项目名}_{版本}_{编译条件}_{日期}.map` | 内存映射 |
---
## 时钟配置说明
本工程默认系统时钟为内部 IRC8M 振荡器经 PLL 倍频后的 72MHz。
如需修改主频或时钟源,请编辑 `Src/system_gd32e23x.c` 文件:
1. 查找如下宏定义区:
```c
// #define __SYSTEM_CLOCK_8M_HXTAL (__HXTAL)
// #define __SYSTEM_CLOCK_8M_IRC8M (__IRC8M)
// #define __SYSTEM_CLOCK_72M_PLL_HXTAL (uint32_t)(72000000)
#define __SYSTEM_CLOCK_72M_PLL_IRC8M_DIV2 (uint32_t)(72000000)
```
2. 取消你需要的时钟方案的注释,并注释掉其它方案。
3. 保存后重新编译工程即可生效。
详细时钟初始化流程可参考 `Src/system_gd32e23x.c` 文件中的 `system_clock_config` 及相关函数实现。
---
## vcpkg 依赖管理(可选)
本工程可选支持 vcpkg 作为 C/C++ 工具链和构建工具的自动化依赖管理方案。
- 自动下载和管理如 CMake、Ninja 等构建工具,简化环境配置。
- 可扩展用于第三方 C/C++ 库的统一管理。
**启用方法**:
1. 在项目根目录创建 `vcpkg-configuration.json` 文件,内容如下:
```json
{
"registries": [
{
"name": "microsoft",
"location": "https://aka.ms/vcpkg-ce-default",
"kind": "artifact"
},
{
"name": "arm",
"location": "https://aka.ms/vcpkg-artifacts-arm",
"kind": "artifact"
}
],
"requires": {
"arm:tools/ninja-build/ninja": "^1.12.0",
"arm:tools/kitware/cmake": "^3.28.4"
}
}
```
2. 启动 VSCode 或命令行,vcpkg 会自动检测并安装所需工具。
如不需要 vcpkg,可忽略本文件。