From 24ddb7046e45dcd3a631778dae67b037f8609df1 Mon Sep 17 00:00:00 2001 From: hulk Date: Fri, 9 Oct 2026 19:13:15 +0800 Subject: [PATCH] FilmForge: RS485 data link (USART0+DE PA8) and NTC temp (PC5 ADC0) --- AGENTS.md | 141 +++++++++++++++---------------------- CMakeLists.txt | 2 + Inc/board_config.h | 44 ++++++++++-- Inc/ntc.h | 20 ++++++ Inc/rs485.h | 49 +++++++++++++ Inc/uart.h | 11 ++- Src/gd32f4xx_it.c | 11 ++- Src/main.c | 69 +++++++----------- Src/ntc.c | 70 ++++++++++++++++++ Src/rs485.c | 126 +++++++++++++++++++++++++++++++++ Src/uart.c | 52 ++------------ cmake/project_config.cmake | 4 +- 12 files changed, 402 insertions(+), 197 deletions(-) create mode 100644 Inc/ntc.h create mode 100644 Inc/rs485.h create mode 100644 Src/ntc.c create mode 100644 Src/rs485.c diff --git a/AGENTS.md b/AGENTS.md index ce84f0e..1d67af4 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -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/`),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`(协议/驱动参考)。 diff --git a/CMakeLists.txt b/CMakeLists.txt index 22eddf9..154f7ab 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/Inc/board_config.h b/Inc/board_config.h index 26340c1..896f700 100644 --- a/Inc/board_config.h +++ b/Inc/board_config.h @@ -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 -#define RS485_DE_RCU RCU_GPIOA -#define RS485_DE_PORT GPIOA -#define RS485_DE_PIN GPIO_PIN_1 // 根据实际硬件修改 DE 引脚 +/* 数据口 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_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); /******************************************************************************/ diff --git a/Inc/ntc.h b/Inc/ntc.h new file mode 100644 index 0000000..c766fe3 --- /dev/null +++ b/Inc/ntc.h @@ -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 diff --git a/Inc/rs485.h b/Inc/rs485.h new file mode 100644 index 0000000..f1bc398 --- /dev/null +++ b/Inc/rs485.h @@ -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 diff --git a/Inc/uart.h b/Inc/uart.h index c176ea7..03206ce 100644 --- a/Inc/uart.h +++ b/Inc/uart.h @@ -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 diff --git a/Src/gd32f4xx_it.c b/Src/gd32f4xx_it.c index 790c72f..c1ab03e 100644 --- a/Src/gd32f4xx_it.c +++ b/Src/gd32f4xx_it.c @@ -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(); -} diff --git a/Src/main.c b/Src/main.c index 61d7d1b..efd7ff3 100644 --- a/Src/main.c +++ b/Src/main.c @@ -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 #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); } } diff --git a/Src/ntc.c b/Src/ntc.c new file mode 100644 index 0000000..407f013 --- /dev/null +++ b/Src/ntc.c @@ -0,0 +1,70 @@ +#include "ntc.h" +#include "gd32f4xx_adc.h" +#include "gd32f4xx_rcu.h" +#include "gd32f4xx_gpio.h" +#include "board_config.h" +#include + +/** + * @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; +} diff --git a/Src/rs485.c b/Src/rs485.c new file mode 100644 index 0000000..98f4767 --- /dev/null +++ b/Src/rs485.c @@ -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); + } +} diff --git a/Src/uart.c b/Src/uart.c index 1bfc145..0567d18 100644 --- a/Src/uart.c +++ b/Src/uart.c @@ -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) { diff --git a/cmake/project_config.cmake b/cmake/project_config.cmake index 416c0aa..235dd31 100644 --- a/cmake/project_config.cmake +++ b/cmake/project_config.cmake @@ -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)