FilmForge: RS485 data link (USART0+DE PA8) and NTC temp (PC5 ADC0)

This commit is contained in:
2026-10-09 19:13:15 +08:00
parent 8feba5e015
commit 24ddb7046e
12 changed files with 402 additions and 197 deletions
+56 -85
View File
@@ -1,105 +1,76 @@
# GD32F4xx 工程模板 — AGENTS.md
# FilmForge — AGENTS.md
## 项目约定
- **用途**:兆易创新 GD32F4xx 系列 MCU(Cortex-M4)的 CMake + VSCode 工程模板,
组织方式与编译方法沿用 `E:\Hulk-Coding\gd32e23x_template_cmake_vscode`(其源头为
celalkismiroglu/gd32e23x-demo-suites-cmake)。
- **默认目标**:GD32F405RET6(设备宏 `GD32F405`,硬件 FPU fpv4-sp-d16 / hard ABI;
512K Flash / 128K RAM + 64K TCMRAM,链接脚本 xE)。
- **板级实际配置(demo 板实测)**:无外部晶振 → 时钟源 **IRC16M PLL 168MHz**
(`system_gd32f4xx.c` 启用 `__SYSTEM_CLOCK_168M_PLL_IRC16M`;HXTAL 方案在无晶振
时会死循环等待);LED = **PA15**;调试串口 = **USART1(PA2 TX / PA3 RX,AF7,
纯串口非 RS485;GD 外设编号比 STM32 小 1,PA2/PA3=USART1@0x40004400)**,
printf 经 `syscalls.c` 的 `_write`→`__io_putchar` 重定向到
USART1(`DEBUG_UART` 宏),`DEBUG_MODE` 打开后输出(模板现阶段默认开启,
发布前改回 `CFG_DISABLE`)。
- **语言/工具链**:C(gnu11)+ ARM GCC 交叉工具链(本机已验证 xpack 15.2.1;
E23x 侧曾验证 11.3.1 / 12.3.1)、CMake ≥ 3.20 + Ninja、VSCode + CMake Tools +
cortex-debug。工具链路径经环境变量 `GD32_GCC_BIN` 提供,不入仓库。
- **目录约定**:
- `Build/` —— CMake 构建树与产物(`Build/<Debug|Release>`),gitignore,
可随时整目录删除。
- `SDK/` —— CMSIS(core_cm4 + GD32F4xx 设备头 + GCC 启动文件)、标准外设库
V3.3.3(取自 GD32F4xx_Demo_Suites_V3.3.3)、SEGGER_RTT。SDK 代码不做业务修改。
- `LD/` —— 链接脚本(xE 512K / xG 1024K / xK 2048K 三种容量,默认 xE)。
- `Inc/`+`Src/` —— 应用层;板级引脚与功能开关集中在 `Inc/board_config.h`。
- `OpenOCD/` —— 工程内 OpenOCD 会话配置(CMSIS-DAP + stm32f4x target 驱动 GD32F4)。
- **用途**:自制热压模具温控固件——塑型 PC 膜,负责加热驱动与测温(NTC),
对外走 RS485 数据口。基于 GD32F4xx 工程模板
(`https://gitea.hulk.wang/hulk/gd32f4xx_template_cmake_vscode`,源自
celalkismiroglu/gd32e23x-demo-suites-cmake 体系)。
- **命名**:仓库 `FilmForge`,固件工程 `FilmForge_FW`(产物自动命名
`FilmForge_FW_V<版本>_APP_<日期>.bin`),协议板卡类型码 `BOARD_TYPE_CODE 41`。
- **目标芯片**:GD32F405RET6(设备宏 `GD32F405`,IRC16M PLL 168MHz——板上无外部
晶振;硬件 FPU fpv4-sp-d16 / hard ABI;512K Flash,链接脚本 xE)。
- **串口角色与 PC 端映射**:
- debug 串口 = USART1(PA2 TX / PA3 RX,AF7,纯串口)→ PC 端 **COM4**;
printf 重定向到 USART1(`DEBUG_UART` 宏),`DEBUG_MODE` 打开后输出。
- 数据口 RS485 = USART0(PA9 TX / PA10 RX,AF7)+ DE 引脚 PA8(GPIO 手动切换
收发)→ PC 端 **COM5**(115200-8-N-1)。拉通阶段主循环回显。
- 注意 GD 外设编号比 STM32 小 1:PA9/PA10=USART0、PA2/PA3=USART1。
- **NTC 测温**:PC5(ADC0_IN15),硬件 10K 上拉、NTC 接 GND 侧。B 值公式换算,
`NTC_B_VALUE` 宏(3950/3435 未定,默认 3950,确认后修改);`R_PULL/NTC_R25`
均为宏,换料只改宏。
- **语言/工具链**:C(gnu11)+ ARM GCC(xpack 15.2.1 已验证)、CMake ≥ 3.20 +
Ninja、VSCode + CMake Tools + cortex-debug;工具链路径经 `GD32_GCC_BIN`。
- **目录约定**(沿用模板):
- `Build/` —— 构建树与产物(gitignore);
- `SDK/` —— CMSIS、标准外设库 V3.3.3、SEGGER_RTT、USB 库(`GD32_ENABLE_USB`
开关,默认 OFF,当前未用);
- `LD/` —— 链接脚本(默认 xE 512K);
- `Inc/`+`Src/` —— 应用层,板级引脚/开关集中在 `Inc/board_config.h`;
- `OpenOCD/` —— 工程内会话配置(GD-LINK/CMSIS-DAP + stm32f4x);
- `doc/` —— 芯片文档(数据手册/用户手册/固件库指南/AN,已齐备)。
- `GD32F4xx_Demo_Suites_V3.3.3/` —— 官方原始素材包,仅本地参考,已 gitignore。
- **编译方法**(与 E23x 一致):
- **编译方法**:
```bash
cmake --preset Debug && cmake --build Build/Debug
cmake --preset Release && cmake --build Build/Release
```
产物(elf/hex/bin/map/list 及版本化命名文件)全部生成在 `Build/<配置>/` 下。
- **构建特性**:`project_config.cmake` 管版本号/变体;`version.h` 自动生成;
POST_BUILD 产出 `[工程名_版本_变体_日期]` 系列文件;外设库为 OBJECT 库 +
`--gc-sections` 裁剪,未引用模块不进最终镜像。
- **换型号**:改 `GD32F405` 宏(project.cmake 与外设库 CMakeLists)、启动文件
(407_427 / 405_425 / 450_470)、链接脚本(xE/xG/xK),详见 README“更换目标型号”。
- **Git/网络经验**:origin = HTTPS(`https://gitea.hulk.wang/hulk/FilmForge.git`),
该实例 HTTPS/SSH 直传大包均不稳,已在本仓库配置
`http.proxy http://127.0.0.1:6789` + `http.sslVerify=false`(仓库级),
代理端口变化时用 `git config http.proxy http://127.0.0.1:<新端口>` 更新。
## 开发进度
### 已完成
- 模板骨架:目录结构、CMakeLists、CMakePresets(构建树位于工程根 `Build/`)、
工具链文件(cortex-m4 + 硬件 FPU + GCC15 兼容 `--no-warn-rwx-segments`)。
- **目标型号切换为 GD32F405RET6**:设备宏 `GD32F405`、启动文件 `startup_gd32f405_425.S`、
链接脚本 `gd32f405_407_xE_flash.ld`(512K)、J-Link device `GD32F405RE`、
OpenOCD cfg 更名 `gd32f405_gdlink_cmsis_dap.cfg`;切换后全新编译 Debug/Release 通过。
- **Flash 容量改为 CMake 编译期切换**:`GD32_FLASH_CAPACITY`(cache 变量,默认 xE)
决定链接脚本(xE/xG/xK)并注入 `GD32_FLASH_SIZE_KB` 宏;已验证 xG 切换生效。
- **删除 E230 式运行时串口分发结构**:去掉 `usart_config_t`/`g_usart_config`/
`mcu_detect_and_config()`/`Src/board_config.c`,串口静态绑定 USART0(宏直连),
中断直接调用处理函数;LED 改 demo 板实测引脚 **PA15**(复位默认 JTDI,
配 GPIO 输出即接管,不影响 SWD)。
- SDK 就位:CMSIS(core_cm4.h 系)、GD32F4xx 标准外设库 29 模块、SEGGER_RTT、
GCC 启动文件(F405/425)、三种容量链接脚本。
- 应用层移植(自 E23x 模板):main / systick(M4 延时校准系数)/ led(心跳,
demo 板 PA15)/ uart(USART0 PA9/PA10 AF7、RS485 改 GPIO 控 DE)/
uart_ring_buffer / gd32f4xx_it(含 M4 特有 fault)/ syscalls(printf 重定向
USART0)/ system_gd32f4xx(168MHz_HXTAL,SystemInit 含 FPU 使能)。
- `GD32F4xx.svd` 已下载就位(社区镜像,与官方同源,覆盖全 F4 家族)。
- OpenOCD 工程内会话配置(借鉴 metax-sensors/gd32-cmake-example:CMSIS-DAP +
stm32f4x.cfg 驱动 GD32F4 flash)+ .vscode 全套(tasks/launch/settings/jlink 脚本)。
- 编译验证:Debug 与 Release 均通过(GCC 15.2.1),ELF 检查正确(vectors @
0x08000000、hard-float、.data 加载地址正确)。Release text≈7.6KB。
- **上板验证通过(GD-LINK/CMSIS-DAP + OpenOCD stm32f4x)**:芯片识别
`device id 0x16080413`(F405xE/512K);固件烧录+verify OK;LED 心跳周期 2 秒整
(SysTick 1ms 精确,168MHz PLL 生效,SCSS=PLLP 硬件确认);USART1 寄存器实测
CTL0=0x202C(使能+收发+RBNE 中断)、BAUD=365(42MHz APB1/115200 精确)、
TC=1(printf 数据完整发出)。
调试经验:GD32F4xx RCU 寄存器偏移为 CTL0=0x00、**PLL=0x04、CFG0=0x08**;
外设编号比 STM32 小 1(GD USART0=STM32 USART1@0x40011000、GD USART1=STM32
USART2@0x40004400、GD USART2=STM32 USART3@0x40004800),在线排查时勿混淆——
曾因按 STM32 习惯把 PA2/PA3 当 USART2 配置导致串口无输出,后更正为 USART1。
- 模板就位(GD32F405RET6 / IRC16M 168MHz / 硬件 FPU / 上板验证过的模板体系)。
- **debug 串口**:USART1 PA2/PA3 @115200,printf 重定向 + RBNE 中断收环形缓冲
(模板已有,COM4 观测)。
- **RS485 数据口驱动**(`rs485.h/rs485.c`):USART0 PA9/PA10 AF7 + DE PA8 GPIO
手动切换;独立接收环形缓冲(预留一格策略 + PRIMASK 临界区);阻塞发送
(DE→TBE→TC→DE,防尾字节截断);主循环 `rs485_process()` 回显拉通。
- **NTC 测温驱动**(`ntc.h/ntc.c`):PC5 模拟输入 + ADC0 常规通道(软件触发单次
转换 + 校准),16 次采样均值 + 分压反解阻值 + B 值公式换算摄氏度。
- `debug_uart_write()` 助手(uart.c):debug 口阻塞单字节发送。
- 工程改名:`FilmForge_FW`、`BOARD_TYPE_CODE 41`。
### 计划 / TODO(已提出尚未实现)
- [ ] **i2c 驱动移植**:从 E23x 工程 `Src/i2c.c`(50KB)/`Inc/i2c.h` 移植到 F4
(API 同名:i2c_clock_config/i2c_mode_addr_config 等已确认存在;引脚已定义
PB6/PB7 AF4;需将 include 的 gd32e23x* 改为 gd32f4xx*,并按 F4 时序实测
20kHz 总线速度与超时参数)。完成后在 CMakeLists 的 TARGET_SRC 启用 `Src/i2c.c`。
- [ ] **串口命令协议移植**:从 E23x 工程 `Src/command.c`/`Inc/command.h` 移植
(include 改 gd32f4xx_usart.h、core_cm23.h→core_cm4.h),启用后 main.c 中
打开 `command_process()` 调用。
- [ ] **上板验证(剩余项)**:串口助手(PA2/PA3,115200-8-N-1)实测 printf 输出与
收发;`stm32f4x mass_erase 0` 整片擦除命令实测;RTT 通道实测。
(已通过:烧录/verify、LED 心跳 2s、168MHz PLL、USART2 初始化与 printf 发送。)
- [ ] **delay_10us 校准**:M4@168MHz 系数为估算值(/300000),需逻辑分析仪实测修正。
- [x] **doc/ 已齐备**:GD32F405 数据手册 Rev2.8、GD32F4xx 用户手册 Rev3.3、
固件库使用指南 Rev1.2(中英)、AN059/AN200(用户补充+官方库包 Docs 拷贝)。
- **SDK 版本核对(2026-10)**:官网最新 GD32F4xx_Firmware_Library_V3.3.3(2026-02-05)
与工程 SDK(取自 Demo_Suites_V3.3.3)经逐文件 SHA1 对比**完全一致**,无需更新。
- **GD32F4xx_usb_library 已搬入 SDK**(源自官方包,temp 删除后不依赖):含
driver/device/host/ustd 全量源码 + conf/ 模板配置(cdc_acm 例程基准)。
默认 `GD32_ENABLE_USB=OFF` 不参与构建;开启方式与限制见 SDK/GD32F4xx_usb_library/
CMakeLists.txt 头部注释(默认编译范围 = driver + device core + CDC 虚拟串口,
GCC 15 已验证;其余 class/host 依赖例程级 conf 宏,按注释从 Examples 取)。
- [ ] **RS485 拉通上板验证**:COM5(RS485)发数据回显 + COM4(debug)观测,
DE 时序实测(逻辑分析仪看 PA8 与 TX 时序)。
- [ ] **NTC 测温上板验证**:COM4 观测打印温度,与室温计对比;`NTC_B_VALUE`
确认(B3950 vs B3435——待两点法实测判定)。
- [ ] **加热驱动**:加热功率器件的驱动与控制(方案待定:MOS/PWM/继电器),
与测温闭环(控温策略)。
- [ ] **正式数据协议**:RS485 从回显切换到正式协议(沿用 D5/B5 帧体系或新定义),
即 E23x `command.c` 的移植/重写。
- [ ] **LED 引脚确认**:FilmForge 自制板 LED 引脚未定(当前沿用模板默认 PA15,
仅为构建占位),按原理图修改。
- [ ] **delay_10us 校准**:M4@168MHz 系数为估算值(/300000),需实测修正。
## 备注
- 本机凭据/发布约定见用户级 `C:\Users\Lenovo\.zcode\AGENTS.md`。
- E23x 工程位置:`E:\Hulk-Coding\gd32e23x_template_cmake_vscode`(模板对标工程)。
- 模板工程位置:`E:\Hulk-Coding\gd32f4xx_template_cmake_vscode`(模板对标工程,
Gitea: gd32f4xx_template_cmake_vscode)。
- E23x 工程位置:`E:\Hulk-Coding\gd32e23x_template_cmake_vscode`(协议/驱动参考)。
+2
View File
@@ -36,6 +36,8 @@ set(TARGET_SRC
Src/uart.c
Src/led.c
Src/uart_ring_buffer.c
Src/rs485.c
Src/ntc.c
# TODO: 待从 GD32E23x 工程移植后启用
# Src/command.c
+35 -5
View File
@@ -51,13 +51,44 @@
/* >>>>>>>>>>>>>>>>>>>>[RS485 MODE]<<<<<<<<<<<<<<<<<<<< */
#define RS485_MODE CFG_DISABLE // CFG_DISABLE: RS485 off; CFG_ENABLE: RS485 on
#define RS485_MODE CFG_ENABLE // CFG_DISABLE: RS485 off; CFG_ENABLE: RS485 on
/* GD32F4xx USART 无硬件 RS485 DE 驱动(E23x 特有),DE 引脚由 GPIO 手动控制 */
#if RS485_MODE == CFG_ENABLE
/* 数据口 RS485:USART0(PA9 TX / PA10 RX,AF7),DE 引脚 PA8(GPIO 手动切换收发) */
#define RS485_RCU RCU_USART0
#define RS485_PHY USART0
#define RS485_IRQ USART0_IRQn
#define RS485_GPIO_RCU RCU_GPIOA
#define RS485_GPIO_PORT GPIOA
#define RS485_TX_PIN GPIO_PIN_9
#define RS485_RX_PIN GPIO_PIN_10
#define RS485_BAUDRATE 115200U
#define RS485_DE_RCU RCU_GPIOA
#define RS485_DE_PORT GPIOA
#define RS485_DE_PIN GPIO_PIN_1 // 根据实际硬件修改 DE 引脚
#define RS485_DE_PIN GPIO_PIN_8
#endif
/* >>>>>>>>>>>>>>>>>>>>[NTC TEMPERATURE]<<<<<<<<<<<<<<<<<<<< */
#define NTC_MODE CFG_ENABLE // CFG_DISABLE: NTC off; CFG_ENABLE: NTC on
#if NTC_MODE == CFG_ENABLE
/* 10K NTC 测温:PC5(ADC0_IN15),硬件 10K 上拉,NTC 接 GND 侧 */
#define NTC_ADC_RCU RCU_ADC0
#define NTC_GPIO_RCU RCU_GPIOC
#define NTC_GPIO_PORT GPIOC
#define NTC_GPIO_PIN GPIO_PIN_5
#define NTC_ADC ADC0
#define NTC_ADC_CHANNEL ADC_CHANNEL_15
#define NTC_SAMPLETIME ADC_SAMPLETIME_480 /* 长采样时间(高阻 NTC 分压源) */
#define NTC_SAMPLE_COUNT 16U /* 单次测温的采样次数(均值降噪) */
#define NTC_VREF 3.3f /* ADC 参考电压 V */
#define NTC_ADC_MAX 4095.0f /* 12bit ADC 满量程 */
#define NTC_R_PULL 10000.0f /* 上拉电阻(硬件) */
#define NTC_R25 10000.0f /* NTC 标称阻值 @25C */
#define NTC_T25 298.15f /* 标称温度 K */
#define NTC_B_VALUE 3950 /* B 值:3950 或 3435,按实测确认后修改 */
#endif
/******************************************************************************/
@@ -74,10 +105,9 @@
#define UART_RX_PIN GPIO_PIN_3
#define UART_BAUDRATE 115200U
/* USART中断处理函数声明 */
void usart0_irq_handler(void);
/* USART中断处理函数声明(USART0 的处理在 rs485.c,USART1 的处理在 uart.c) */
void rs485_irq_handler(void);
void usart1_irq_handler(void);
void usart2_irq_handler(void);
/******************************************************************************/
+20
View File
@@ -0,0 +1,20 @@
#ifndef NTC_H
#define NTC_H
#include "gd32f4xx.h"
#include "board_config.h"
/**
* @brief 初始化 NTC 测温(PC5 模拟输入 + ADC0 常规通道,软件触发单次转换)。
*/
void ntc_init(void);
/**
* @brief 读取一次 NTC 温度(多次采样均值 + B 值公式换算)。
* @details 分压关系:V = VREF * R_ntc / (R_PULL + R_ntc)(硬件上拉在 VREF 侧,
* NTC 接 GND 侧),再由 B 值公式换算温度,单位摄氏度。
* @return 温度值(摄氏度,float)
*/
float ntc_read_temperature(void);
#endif // NTC_H
+49
View File
@@ -0,0 +1,49 @@
#ifndef RS485_H
#define RS485_H
#include "gd32f4xx.h"
#include "board_config.h"
/* RS485 接收环形缓冲区容量(单位:字节),采用"预留一格"区分空/满策略 */
#define RS485_RX_BUFFER_SIZE 128
/**
* @brief 初始化 RS485 接口(USART0 PA9/PA10 + DE PA8)。
* @details 配置 GPIO 复用、DE 引脚推挽输出(默认接收态)、USART 参数、
* 接收非空中断(RBNE)与独立接收环形缓冲区。
*/
void rs485_init(void);
/**
* @brief RS485 阻塞发送。
* @details DE 置位进入发送态 → 逐字节发送(等待 TBE)→ 等待发送完成(TC)→
* DE 清零回到接收态。
* @param[in] data 待发送数据指针
* @param[in] len 发送字节数
*/
void rs485_send(const uint8_t *data, uint16_t len);
/**
* @brief RS485 主循环处理(拉通阶段:原样回显)。
* @details 从接收环形缓冲取字节并回发到 RS485,同时转发到 debug 串口。
* 在主循环中周期调用。
*/
void rs485_process(void);
/**
* @brief 获取当前 RS485 接收环形缓冲区可读字节数。
*/
uint8_t rs485_rx_available(void);
/**
* @brief 从 RS485 接收环形缓冲区读取一个字节。
* @return 读取到的字节(0..255),或 -1 表示缓冲区为空。
*/
int rs485_rx_get(void);
/**
* @brief USART0 接收中断处理(由 gd32f4xx_it.c 的 USART0_IRQHandler 调用)。
*/
void rs485_irq_handler(void);
#endif // RS485_H
+5 -6
View File
@@ -13,6 +13,11 @@
void uart_init(void);
void debug_usart_init(void);
/**
* @brief debug 串口阻塞发送一个字节(printf 重定向亦走此通路)。
*/
void debug_uart_write(uint8_t byte);
/*!
\brief pause UART RX interrupt (RBNE)
\param[in] none
@@ -29,10 +34,4 @@ void uart_rx_irq_pause(void);
*/
void uart_rx_irq_resume(void);
#if RS485_MODE == CFG_ENABLE
/* RS485 DE 引脚手动控制(GD32F4xx USART 无硬件 DE 驱动) */
void rs485_de_set(void);
void rs485_de_clear(void);
#endif
#endif // UART_H
+4 -7
View File
@@ -36,6 +36,7 @@ OF SUCH DAMAGE.
#include "systick.h"
#include "uart.h"
#include "uart_ring_buffer.h"
#include "rs485.h"
#include "led.h"
#include "board_config.h"
@@ -141,16 +142,12 @@ void SysTick_Handler(void) {
delay_decrement();
}
/* 调试串口静态绑定 USART1(PA2/PA3),直接调用其处理函数。
若业务改用其它串口,同步修改 board_config.h 的 UART_* 并在此补对应 IRQHandler。 */
/* 串口中断静态绑定:USART0 = RS485 数据口(rs485.c),USART1 = debug 串口(uart.c)。
若业务改用其它串口,同步修改 board_config.h 并在此调整对应 IRQHandler。 */
void USART0_IRQHandler(void) {
usart0_irq_handler();
rs485_irq_handler();
}
void USART1_IRQHandler(void) {
usart1_irq_handler();
}
void USART2_IRQHandler(void) {
usart2_irq_handler();
}
+25 -44
View File
@@ -1,51 +1,21 @@
/*!
\file main.c
\brief GD32F4xx 工程模板入口(LED 心跳 + 串口收发框架)
\brief FilmForge 热压模具温控固件入口
基于 GD32E23x 工程模板移植,目标芯片 GD32F405RET6(Cortex-M4 @168MHz,512KB Flash)。
*/
/*
Copyright (c) 2025, GigaDevice Semiconductor Inc.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
OF SUCH DAMAGE.
自制热压模具(塑型 PC 膜):加热驱动 + 测温(NTC)+ RS485 数据口。
基于 GD32F4xx 工程模板,目标芯片 GD32F405RET6(Cortex-M4 @168MHz)。
*/
#include "gd32f4xx.h"
#include "systick.h"
#include "uart.h"
#include "uart_ring_buffer.h"
#include "rs485.h"
#include "ntc.h"
#include "led.h"
#include <stdio.h>
#include "board_config.h"
/* TODO: 以下模块尚未从 GD32E23x 工程移植,编译验证通过后逐步补充:
* - i2c.c/i2c.h I2C 主机读写驱动(硬件 I2C0 + 总线恢复)
* - command.c/h 串口命令协议解析(D5 帧格式)
* 移植时将对应文件加入 CMakeLists.txt 的 TARGET_SRC 即可。
*/
/*!
\brief main function
\param[in] none
@@ -61,22 +31,33 @@ int main(void)
uart_ring_buffer_init();
uart_init();
rs485_init();
ntc_init();
#if DEBUG_MODE == CFG_ENABLE
printf("Hello World!\r\n");
printf("GD32F405 template, flash size: %u KB\r\n", GD32_FLASH_SIZE_KB);
printf("FilmForge FW %s, built %s\r\n", PROJECT_VERSION, BUILD_DATE);
printf("RS485 data link ready, NTC ready\r\n");
#endif
#if SEGGER_RTT_DETECTION == CFG_ENABLE
RTT_printf(0, "Hello World!\r\n");
RTT_printf(0, "FilmForge FW\r\n");
#endif
printf("Hello World!\r\n");
/* ========== Command Testing ========== */
/* ========== */
while(1){
/* TODO: command_process(); 命令模块移植后启用 */
/* RS485 数据口处理(拉通阶段:回显) */
rs485_process();
/* debug 串口周期打印 NTC 温度(约 1s 一次) */
#if DEBUG_MODE == CFG_ENABLE
{
static uint16_t temp_report_counter = 0;
if (++temp_report_counter >= 100U) { /* 主循环 10ms 一圈 → 1s */
temp_report_counter = 0;
printf("NTC temp: %.1f C\r\n", ntc_read_temperature());
}
}
#endif
delay_ms(10);
}
}
+70
View File
@@ -0,0 +1,70 @@
#include "ntc.h"
#include "gd32f4xx_adc.h"
#include "gd32f4xx_rcu.h"
#include "gd32f4xx_gpio.h"
#include "board_config.h"
#include <math.h>
/**
* @brief 配置 NTC 引脚(PC5 模拟输入)与 ADC0 常规通道。
*/
static void ntc_gpio_adc_config(void) {
/* 使能 GPIOC 与 ADC0 时钟 */
rcu_periph_clock_enable(NTC_GPIO_RCU);
rcu_periph_clock_enable(NTC_ADC_RCU);
/* PC5 模拟输入模式 */
gpio_mode_set(NTC_GPIO_PORT, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, NTC_GPIO_PIN);
/* ADC0 独立模式,常规通道软件触发、单次转换、右对齐 */
adc_sync_mode_config(ADC_SYNC_MODE_INDEPENDENT);
adc_special_function_config(NTC_ADC, ADC_CONTINUOUS_MODE, DISABLE);
adc_special_function_config(NTC_ADC, ADC_INSERTED_CHANNEL, DISABLE);
adc_external_trigger_config(NTC_ADC, ADC_ROUTINE_CHANNEL, EXTERNAL_TRIGGER_DISABLE);
adc_data_alignment_config(NTC_ADC, ADC_DATAALIGN_RIGHT);
adc_channel_length_config(NTC_ADC, ADC_ROUTINE_CHANNEL, 1U);
adc_routine_channel_config(NTC_ADC, 0U, NTC_ADC_CHANNEL, NTC_SAMPLETIME);
adc_enable(NTC_ADC);
adc_calibration_enable(NTC_ADC);
}
void ntc_init(void) {
ntc_gpio_adc_config();
}
/**
* @brief 触发一次转换并读取 12bit 原始值。
*/
static uint16_t ntc_read_raw_once(void) {
adc_flag_clear(NTC_ADC, ADC_FLAG_EOC);
adc_software_trigger_enable(NTC_ADC, ADC_ROUTINE_CHANNEL);
/* 等待转换完成(EOC) */
while (SET != adc_flag_get(NTC_ADC, ADC_FLAG_EOC)) {}
return (uint16_t)adc_routine_data_read(NTC_ADC);
}
float ntc_read_temperature(void) {
uint32_t sum = 0U;
/* 多次采样均值降噪 */
for (uint8_t i = 0; i < NTC_SAMPLE_COUNT; i++) {
sum += ntc_read_raw_once();
}
float adc_val = (float)sum / (float)NTC_SAMPLE_COUNT;
/* 分压点电压:上拉 R_PULL 在 VREF 侧,NTC 接 GND 侧 */
float voltage = adc_val * NTC_VREF / NTC_ADC_MAX;
/* 反解 NTC 阻值:V = VREF * R / (R_PULL + R) => R = R_PULL * V / (VREF - V) */
if (voltage <= 0.0f || voltage >= NTC_VREF) {
/* 开路/短路等异常,返回 -273.15 表示无效 */
return -273.15f;
}
float r_ntc = NTC_R_PULL * voltage / (NTC_VREF - voltage);
/* B 值公式:1/T = 1/T25 + ln(R/R25)/B */
float t_k = 1.0f / (1.0f / NTC_T25 + logf(r_ntc / NTC_R25) / (float)NTC_B_VALUE);
return t_k - 273.15f;
}
+126
View File
@@ -0,0 +1,126 @@
#include "rs485.h"
#include "gd32f4xx_usart.h"
#include "gd32f4xx_rcu.h"
#include "gd32f4xx_gpio.h"
#include "board_config.h"
#include "uart.h"
/* 独立接收环形缓冲区(与 debug 串口的 uart_ring_buffer 隔离),
采用"预留一格"区分空/满,最大可用容量为 RS485_RX_BUFFER_SIZE-1 */
static __IO uint8_t rs485_rx_buffer[RS485_RX_BUFFER_SIZE];
static __IO uint8_t rs485_write_index = 0;
static __IO uint8_t rs485_read_index = 0;
static inline uint32_t rs485_irq_save(void) {
uint32_t primask = __get_PRIMASK();
__disable_irq();
return primask;
}
static inline void rs485_irq_restore(uint32_t primask) {
if ((primask & 0x1) == 0) {
__enable_irq();
}
}
/**
* @brief 配置 RS485 收发引脚(PA9 TX / PA10 RX 复用,DE PA8 推挽输出)。
*/
static void rs485_gpio_config(void) {
/* 使能 GPIOA 和 USART0 时钟 */
rcu_periph_clock_enable(RS485_GPIO_RCU);
rcu_periph_clock_enable(RS485_RCU);
/* PA9 为 USART0_TX,PA10 为 USART0_RX(AF7) */
gpio_af_set(RS485_GPIO_PORT, GPIO_AF_7, RS485_TX_PIN | RS485_RX_PIN);
gpio_mode_set(RS485_GPIO_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, RS485_TX_PIN | RS485_RX_PIN);
gpio_output_options_set(RS485_GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, RS485_TX_PIN | RS485_RX_PIN);
/* DE 引脚为普通推挽输出,默认接收态 */
rcu_periph_clock_enable(RS485_DE_RCU);
gpio_mode_set(RS485_DE_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, RS485_DE_PIN);
gpio_output_options_set(RS485_DE_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, RS485_DE_PIN);
gpio_bit_reset(RS485_DE_PORT, RS485_DE_PIN);
}
void rs485_init(void) {
rs485_gpio_config();
/* 配置波特率、数据位、停止位等 */
usart_deinit(RS485_PHY);
usart_word_length_set(RS485_PHY, USART_WL_8BIT);
usart_stop_bit_set(RS485_PHY, USART_STB_1BIT);
usart_parity_config(RS485_PHY, USART_PM_NONE);
usart_baudrate_set(RS485_PHY, RS485_BAUDRATE);
usart_receive_config(RS485_PHY, USART_RECEIVE_ENABLE);
usart_transmit_config(RS485_PHY, USART_TRANSMIT_ENABLE);
usart_enable(RS485_PHY);
/* 使能接收非空中断 */
nvic_irq_enable(RS485_IRQ, 0, 0);
usart_interrupt_enable(RS485_PHY, USART_INT_RBNE);
}
void rs485_send(const uint8_t *data, uint16_t len) {
/* 进入发送态 */
gpio_bit_set(RS485_DE_PORT, RS485_DE_PIN);
for (uint16_t i = 0; i < len; i++) {
/* 等待发送缓冲区空 */
while (RESET == usart_flag_get(RS485_PHY, USART_FLAG_TBE)) {}
usart_data_transmit(RS485_PHY, data[i]);
}
/* 等待最后一字节移位完成,再切回接收态(避免截断尾字节) */
while (RESET == usart_flag_get(RS485_PHY, USART_FLAG_TC)) {}
/* 回到接收态 */
gpio_bit_reset(RS485_DE_PORT, RS485_DE_PIN);
}
uint8_t rs485_rx_available(void) {
uint8_t w = rs485_write_index;
uint8_t r = rs485_read_index;
return (uint8_t)((w + RS485_RX_BUFFER_SIZE - r) % RS485_RX_BUFFER_SIZE);
}
int rs485_rx_get(void) {
if (rs485_read_index == rs485_write_index) return -1; /* 空 */
uint8_t data = rs485_rx_buffer[rs485_read_index];
rs485_read_index = (rs485_read_index + 1) % RS485_RX_BUFFER_SIZE;
return data;
}
void rs485_process(void) {
while (rs485_rx_available() > 0) {
int b = rs485_rx_get();
if (b < 0) {
break;
}
/* 拉通阶段:原样回发 RS485,同时转发 debug 串口便于观察 */
rs485_send((const uint8_t *)&b, 1);
debug_uart_write((uint8_t)b);
}
}
void rs485_irq_handler(void) {
/* 处理USART0的接收中断 */
if (usart_interrupt_flag_get(RS485_PHY, USART_INT_FLAG_RBNE)) {
uint8_t data = usart_data_receive(RS485_PHY);
uint32_t primask = rs485_irq_save();
uint8_t next = (rs485_write_index + 1) % RS485_RX_BUFFER_SIZE;
if (next != rs485_read_index) {
/* 缓冲区未满 */
rs485_rx_buffer[rs485_write_index] = data;
rs485_write_index = next;
}
/* 缓冲满时丢弃 */
rs485_irq_restore(primask);
}
/* 处理USART0的空闲中断(帧间隔提示,后续协议解析用) */
if (usart_interrupt_flag_get(RS485_PHY, USART_INT_FLAG_IDLE)) {
usart_interrupt_flag_clear(RS485_PHY, USART_INT_FLAG_IDLE);
}
}
+6 -46
View File
@@ -10,20 +10,12 @@ void uart_init(void) {
rcu_periph_clock_enable(UART_GPIO_RCU);
rcu_periph_clock_enable(UART_RCU);
/* 配置 PA9 为 USART0_TX,PA10 为 USART0_RX(AF7) */
/* 配置 PA2 为 USART1_TX,PA3 为 USART1_RX(AF7),debug 口纯串口 */
gpio_af_set(UART_GPIO_PORT, GPIO_AF_7, UART_TX_PIN | UART_RX_PIN);
gpio_mode_set(UART_GPIO_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, UART_TX_PIN | UART_RX_PIN);
gpio_output_options_set(UART_GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, UART_TX_PIN | UART_RX_PIN);
#if RS485_MODE == CFG_ENABLE
/* 配置 RS485 DE 引脚为普通推挽输出(默认接收态) */
rcu_periph_clock_enable(RS485_DE_RCU);
gpio_mode_set(RS485_DE_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, RS485_DE_PIN);
gpio_output_options_set(RS485_DE_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, RS485_DE_PIN);
gpio_bit_reset(RS485_DE_PORT, RS485_DE_PIN);
#endif
/* 配置波特率、数据位、停止位等 */
usart_deinit(UART_PHY);
usart_word_length_set(UART_PHY, USART_WL_8BIT);
@@ -47,40 +39,21 @@ void uart_rx_irq_resume(void) {
usart_interrupt_enable(UART_PHY, USART_INT_RBNE);
}
#if RS485_MODE == CFG_ENABLE
void rs485_de_set(void) {
gpio_bit_set(RS485_DE_PORT, RS485_DE_PIN); /* 发送态 */
void debug_uart_write(uint8_t byte) {
/* 等待发送缓冲区空后写入 debug 串口 */
while (RESET == usart_flag_get(DEBUG_UART, USART_FLAG_TBE)) {}
usart_data_transmit(DEBUG_UART, byte);
}
void rs485_de_clear(void) {
gpio_bit_reset(RS485_DE_PORT, RS485_DE_PIN); /* 接收态 */
}
#endif
/******************************************************************************/
/* 具体的中断处理函数实现 */
/******************************************************************************/
void usart0_irq_handler(void) {
// 处理USART0的接收中断
if(usart_interrupt_flag_get(USART0, USART_INT_FLAG_RBNE)) {
uint8_t data = usart_data_receive(USART0);
// 使用原有的环形缓冲区处理逻辑
(void)uart_ring_buffer_put(data); // 缓冲满时丢弃,返回值可用于统计
}
// 处理USART0的空闲中断
if(usart_interrupt_flag_get(USART0, USART_INT_FLAG_IDLE)) {
usart_interrupt_flag_clear(USART0, USART_INT_FLAG_IDLE);
// 在这里添加空闲中断处理逻辑
}
}
void usart1_irq_handler(void) {
// 处理USART1的接收中断
if(usart_interrupt_flag_get(USART1, USART_INT_FLAG_RBNE)) {
uint8_t data = usart_data_receive(USART1);
(void)uart_ring_buffer_put(data);
(void)uart_ring_buffer_put(data); // 缓冲满时丢弃,返回值可用于统计
}
// 处理USART1的空闲中断
@@ -89,19 +62,6 @@ void usart1_irq_handler(void) {
}
}
void usart2_irq_handler(void) {
// 处理USART2的接收中断
if(usart_interrupt_flag_get(USART2, USART_INT_FLAG_RBNE)) {
uint8_t data = usart_data_receive(USART2);
(void)uart_ring_buffer_put(data);
}
// 处理USART2的空闲中断
if(usart_interrupt_flag_get(USART2, USART_INT_FLAG_IDLE)) {
usart_interrupt_flag_clear(USART2, USART_INT_FLAG_IDLE);
}
}
/* 调试串口初始化(与 uart_init 相同配置的独立入口,便于单独初始化打印口) */
void debug_usart_init(void)
{
+2 -2
View File
@@ -1,6 +1,6 @@
# Project basic info
set(PROJECT_NAME "GD32F4xx_StdPeriph_Template")
set(BOARD_TYPE_CODE 40)
set(PROJECT_NAME "FilmForge_FW")
set(BOARD_TYPE_CODE 41)
set(VERSION_MAJOR 0)
set(VERSION_MINOR 0)
set(VERSION_PATCH 1)