# 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`。

### 分支说明

| 分支 | 用途 |
|------|------|
| `template_pc` | **主模板**（推荐），用于桌面端 VSCode 开发 |
| `template_xl` | 小琅适配版 |
| `main` | 早期版本，不推荐使用 |

---

## 工程结构

```
.
├── 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
- **建议解压路径**：工程根目录下 `Toolchain/xpack-arm-none-eabi-gcc-11.3.1-1.1`
- **官方下载地址**：https://github.com/xpack-dev-tools/arm-none-eabi-gcc-xpack/releases
- **路径自定义**：
  如需自定义工具链路径，修改 `cmake/arm-none-eabi-gcc.cmake` 中的 `_TOOLCHAIN_CANDIDATES` 列表，或通过 CMake 参数传入：
  ```bash
  cmake --preset Debug -DTOOLCHAIN_DIRECTORY=/your/path/bin
  ```

### 2. OpenOCD（调试/烧录）

- **版本**：xpack-openocd-0.11.0-3
- **建议解压路径**：任意位置（在 `.vscode/launch.json` 中配置路径）
- **获取地址**：https://github.com/burakenez/gd32-tools-xpack-openocd/tree/v0.11.0-3
- **说明**：
  - 本版本提取自 Embedded Builder V1.4.1.23782。
  - ⚠️ 请勿随意更换版本，因 GD32 MCU 支持有限，推荐严格使用此版本。
- **路径自定义**：
  修改 `.vscode/launch.json` 中 `miDebuggerPath` 和 `serverpath` 指向你的 OpenOCD 路径。

---

## 使用说明

### 编译

```bash
# Debug 构建（-O0, -g3）
cmake --preset Debug
cmake --build build/Debug

# Release 构建（-Os, -g0）
cmake --preset Release
cmake --build build/Release
```

### 烧录

通过 VSCode 任务栏运行 `Flash MCU` 任务，或命令行：

```bash
openocd -f interface/cmsis-dap.cfg -f target/gd32e23x.cfg -c "program build/Debug/Application.elf verify 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，可忽略本文件。
