From 7bc616cf9b7513e3049790d8e2b4578cea89c5d8 Mon Sep 17 00:00:00 2001
From: wandering1 <1624155937@qq.com>
Date: Sat, 29 Aug 2026 09:45:54 +0800
Subject: [PATCH] =?UTF-8?q?=E6=95=B4=E7=90=86=E4=BA=86main=20=E5=87=BD?=
=?UTF-8?q?=E6=95=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
App/app_main.c | 81 +++++++++++++++++++++++++-
App/task/net_task.c | 4 +-
App/task/net_task.h | 2 +-
Drivers/BSP/RS485/rs485.c | 97 ++++++++++++++++++++-----------
Drivers/BSP/RS485/rs485.h | 34 ++++++++---
MDK-ARM/STM32F407-Demo.uvprojx | 5 ++
MDK-ARM/build_log.txt | 97 ++++++++++++++++---------------
STM32F407-Demo.ioc | 2 +-
Src/freertos.c | 28 +++++----
Src/main.c | 103 ++-------------------------------
rx_100k.bin | Bin 102400 -> 0 bytes
test/ch395f_test.h | 4 +-
test/ch395f_test_task.c | 22 ++-----
test/ch395f_test_task.h | 3 +-
test/net_test_task.c | 2 +-
test/net_test_task.h | 2 +-
test/sd2506_test.h | 2 +-
test/sd2506_test_task.c | 2 +-
test/storage_test.h | 2 +-
test/storage_test_task.c | 2 +-
test/test_config.c | 50 ++++++++++++++++
test/test_config.h | 2 +-
22 files changed, 311 insertions(+), 235 deletions(-)
delete mode 100644 rx_100k.bin
create mode 100644 test/test_config.c
diff --git a/App/app_main.c b/App/app_main.c
index 70a414ec..d300dadf 100644
--- a/App/app_main.c
+++ b/App/app_main.c
@@ -6,23 +6,102 @@
* 创建日期:2026-07-19
* 修改记录:
* v1.0 2026-07-19 王建锋 创建初始版本
+ * v1.1 2026-08-29 王建锋 将原 main.c 中各外设初始化统一收归 app_main_init,
+ * 并按"时间子系统优先"的顺序编排启动流程
*/
#include "app_main.h"
#include "app_cfg.h"
+#include "main.h" /* ST_DIR4_GPIO_Port / ST_DIR4_Pin 等 CubeMX 宏 */
+#include "usart.h" /* huart5 */
+#include "rs485.h"
+#include "sd2506.h"
+#include "sys_clock.h"
+#include "gd5f2gq5ue.h"
+#include "tpafe5160.h"
+
#define DBG_TAG "[APP]"
#include "dbg_log.h"
+/* g_rs485 由 rs485.c 定义,经 rs485.h 的 extern 引用;接收缓冲内聚于句柄 rx_buf */
+
/*
* 函数功能:应用层初始化
* 入口参数:无
* 返 回 值:0 - 成功,-1 - 失败
* 限定条件:CubeMX 外设初始化已完成
- * 函数说明:在 CubeMX 外设初始化之后、FreeRTOS 调度器启动之前调用
+ * 函数说明:在 CubeMX 外设初始化之后、FreeRTOS 调度器启动之前调用。
+ * 启动顺序以"时间子系统优先"为原则,使后续所有日志均带时间戳:
+ * 1) 时间子系统(SD2506 RTC → 墙钟基准)
+ * 2) RS-485
+ * 3) GD5F NAND
+ * 4) TPAFE5160 ADC
+ * 5) SD2506 诊断信息
+ * 6) 触发首次 ADC 转换
*/
int app_main_init(void)
{
+ /*
+ * 1) 时间子系统初始化(尽早完成,后续所有日志带时间戳)
+ * sd2506_init() 从 RTC 读取初始时间
+ * sys_clock_init() 将 RTC 时间转为 Unix epoch,建立墙钟基准
+ */
+ sd2506_init();
+ sys_clock_init();
+
+ /* 2) RS-485 初始化(UART5 + PD0 方向控制),初始化即自动开启接收 */
+ rs485_init(&g_rs485, &huart5, ST_DIR4_GPIO_Port, ST_DIR4_Pin);
+ DBG_INFO("RS485 init: OK (UART5, DE=PD0)");
+
+ /* 3) GD5F 初始化测试 */
+ int ret = gd5f2gq5ue_init();
+ DBG_INFO("NAND init: %s",
+ (ret == GD5F_OK) ? "OK" :
+ (ret == GD5F_ERROR) ? "ERROR" :
+ (ret == GD5F_BUSY_TIMEOUT) ? "BUSY TIMEOUT" :
+ (ret == GD5F_ECC_ERROR) ? "ECC ERROR" :
+ (ret == GD5F_PROGRAM_FAIL) ? "PROGRAM FAIL" :
+ (ret == GD5F_ERASE_FAIL) ? "ERASE FAIL" :
+ (ret == GD5F_ID_MISMATCH) ? "ID MISMATCH" : "UNKNOWN");
+
+ /* 4) TPAFE5160 ADC 测试 */
+ int tp_ret = tpafe5160_init();
+ DBG_INFO("ADC init: %s",
+ (tp_ret == TPAFE5160_OK) ? "OK" : "TIMEOUT");
+
+ /* 5) SD2506 RTC 诊断信息 */
+ {
+ /* 读取 ID */
+ uint8_t sd_id[8];
+ sd2506_get_id(sd_id);
+ DBG_INFO("SD2506 ID: %02X%02X%02X%02X%02X%02X%02X%02X",
+ sd_id[0], sd_id[1], sd_id[2], sd_id[3],
+ sd_id[4], sd_id[5], sd_id[6], sd_id[7]);
+
+ /* 读取当前时间 */
+ sd2506_time_t sd_time;
+ sd2506_get_time(&sd_time);
+ DBG_INFO("SD2506 time: %04d-%02d-%02d %02d:%02d:%02d (week %d)",
+ sd_time.year, sd_time.month, sd_time.day,
+ sd_time.hour, sd_time.minute, sd_time.second,
+ sd_time.week);
+
+ /* 读取温度 */
+ int8_t sd_temp;
+ sd2506_get_temperature(&sd_temp);
+ DBG_INFO("SD2506 temp: %d C", sd_temp);
+
+ /* 读取电池电压 */
+ uint16_t sd_vbat;
+ sd2506_get_battery_voltage(&sd_vbat);
+ DBG_INFO("SD2506 battery: %d.%02d V",
+ sd_vbat / 1000, (sd_vbat % 1000) / 10);
+ }
+
+ /* 6) 启动第一次转换(CubeMX 已配置 BUSY EXTI 中断) */
+ tpafe5160_start_conv_irq();
+
DBG_INFO("App init OK");
return 0;
}
diff --git a/App/task/net_task.c b/App/task/net_task.c
index 772cdc10..c9283240 100644
--- a/App/task/net_task.c
+++ b/App/task/net_task.c
@@ -31,7 +31,7 @@ volatile uint8_t g_net_ready = 0;
* 返 回 值:0 - 成功,-1 - 失败
* 函数说明:net_init → MAC 打印 → PHY 强制 100M 全双工 → 置 g_net_ready。
* NET 层模式由 netTask 调用;硬件层模式(阶段 1~5)netTask 不创建,
- * 由 ch395fTestTask 调用后独占 CH395F。
+ * 由 testTask 调用后独占 CH395F。
*/
int net_stack_bring_up(void)
{
@@ -89,7 +89,7 @@ int net_task_init(void)
* 函数说明:启动后先调用 net_stack_bring_up() 初始化 CH395F 协议栈,
* 再进入轮询循环处理网络事件。
* 仅 NET 层测试模式(阶段 6~10)会创建本任务;
- * 硬件层模式下 netTask 不创建,CH395F 由 ch395fTestTask 独占。
+ * 硬件层模式下 netTask 不创建,CH395F 由 testTask 独占。
*/
void net_task_func(void const *arg)
{
diff --git a/App/task/net_task.h b/App/task/net_task.h
index da32062c..243d27be 100644
--- a/App/task/net_task.h
+++ b/App/task/net_task.h
@@ -18,7 +18,7 @@ extern "C" {
/* CH395F 协议栈一次性初始化(net_init + MAC 打印 + PHY 配置 + g_net_ready 置位)。
* 返回 0 成功 / -1 失败。NET 层模式由 netTask 调用;硬件层模式(阶段 1~5)
- * netTask 不创建,由 ch395fTestTask 调用后独占 CH395F。 */
+ * netTask 不创建,由 testTask 调用后独占 CH395F。 */
int net_stack_bring_up(void);
int net_task_init(void);
diff --git a/Drivers/BSP/RS485/rs485.c b/Drivers/BSP/RS485/rs485.c
index faee6b23..317fdb58 100644
--- a/Drivers/BSP/RS485/rs485.c
+++ b/Drivers/BSP/RS485/rs485.c
@@ -14,27 +14,10 @@
#include "rs485.h"
-/* ======================== 内部辅助函数 ======================== */
+/* ======================== 全局实例 ======================== */
-/*
- * 函数功能:DWT 微秒级延时
- * 入口参数:us - 延时微秒数 uint32_t 1 - N
- * 返回值:无
- * 限定条件:无(首次调用自动初始化 DWT CYCCNT)
- * 函数说明:利用 Cortex-M4 DWT CYCCNT 实现精确微秒延时,168MHz 下 1us = 168 计数。
- * 首次调用时自动使能 DWT 和 CYCCNT,后续只读 CYCCNT 寄存器,不影响其他模块。
- */
-static void delay_us(uint32_t us)
-{
- if (!(CoreDebug->DEMCR & CoreDebug_DEMCR_TRCENA_Msk)) {
- CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
- DWT->CYCCNT = 0;
- DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
- }
- uint32_t start = DWT->CYCCNT;
- uint32_t ticks = us * (SystemCoreClock / 1000000uL);
- while ((DWT->CYCCNT - start) < ticks) { ; }
-}
+/* RS-485 全局实例(接收缓冲内聚于句柄的 rx_buf 字段) */
+rs485_handle_t g_rs485;
/* ======================== 方向控制宏 ======================== */
@@ -51,7 +34,8 @@ static void delay_us(uint32_t us)
* dir_pin - 方向控制 GPIO 引脚号(如 GPIO_PIN_0) uint16_t
* 返回值:无
* 限定条件:UART 和 GPIO 须先由 CubeMX 完成初始化
- * 函数说明:初始化后默认处于接收状态(DE 引脚拉低)
+ * 函数说明:初始化后默认处于接收状态(DE 引脚拉低),
+ * 并自动启动 IDLE 中断接收,无需再手动调用 rs485_receive_start。
*/
void rs485_init(rs485_handle_t *p_handle,
UART_HandleTypeDef *p_huart,
@@ -64,6 +48,9 @@ void rs485_init(rs485_handle_t *p_handle,
p_handle->rx_size = 0;
RS485_DIR_RX(p_handle);
+
+ /* 自动开启 IDLE 中断接收(后续在回调中重新开启) */
+ (void)rs485_receive_start(p_handle);
}
/*
@@ -76,14 +63,15 @@ void rs485_init(rs485_handle_t *p_handle,
* HAL_TIMEOUT - 发送超时
* HAL_ERROR - 发送错误
* 限定条件:须在主循环或 FreeRTOS 任务中调用,不可在 UART 中断中调用
- * 函数说明:发送前拉高 DE,等待 50us 确保 RS485 收发器驱动使能稳定后再启动 UART 发送。
+ * 函数说明:发送前拉高 DE,等待 HAL 时基延时确保 RS485 收发器驱动使能稳定后再启动
+ * UART 发送(HAL 时基最小粒度 1ms,此处延时 1ms,远大于 50us 的 DE 建立时间)。
* HAL_UART_Transmit 内部等待 TC(发送完成)标志,确保最后一字节完全移出后
* 再拉低 DE 切回接收状态。
*/
HAL_StatusTypeDef rs485_transmit(rs485_handle_t *p_handle,
- const uint8_t *p_data,
- uint16_t len,
- uint32_t timeout)
+ const uint8_t *p_data,
+ uint16_t len,
+ uint32_t timeout)
{
HAL_StatusTypeDef status = HAL_OK;
@@ -93,7 +81,8 @@ HAL_StatusTypeDef rs485_transmit(rs485_handle_t *p_handle,
RS485_DIR_TX(p_handle);
- delay_us(50);
+ /* DE 建立延时:HAL 时基为 1ms 粒度,1ms 已充分覆盖收发器驱动使能稳定时间 */
+ HAL_Delay(1);
status = HAL_UART_Transmit(p_handle->huart,
(uint8_t *)p_data,
@@ -108,22 +97,19 @@ HAL_StatusTypeDef rs485_transmit(rs485_handle_t *p_handle,
/*
* 函数功能:启动中断方式接收(IDLE 空闲帧检测)
* 入口参数:p_handle - RS-485 句柄指针 rs485_handle_t*(非空)
- * p_buf - 接收缓冲区 uint8_t*(非空)
- * buf_size - 缓冲区大小 uint16_t
* 返回值:HAL_OK - 启动成功 HAL_StatusTypeDef
* HAL_ERROR - 启动失败
* 限定条件:UART 须已开启全局中断(NVIC 使能)
- * 函数说明:使用 HAL_UARTEx_ReceiveToIdle_IT 实现变长帧接收。
+ * 函数说明:使用 HAL_UARTEx_ReceiveToIdle_IT 实现变长帧接收,
+ * 接收缓冲固定使用句柄内部的 rx_buf(RS485_RX_BUF_SIZE)。
* 当 UART 总线空闲超过 1 个字符时间后硬件触发 IDLE 中断,
* HAL 自动调用 HAL_UARTEx_RxEventCallback 并返回已接收字节数。
* 用户须在回调中重新调用本函数重新开启接收。
*/
-HAL_StatusTypeDef rs485_receive_start(rs485_handle_t *p_handle,
- uint8_t *p_buf,
- uint16_t buf_size)
+HAL_StatusTypeDef rs485_receive_start(rs485_handle_t *p_handle)
{
p_handle->rx_size = 0;
- return HAL_UARTEx_ReceiveToIdle_IT(p_handle->huart, p_buf, buf_size);
+ return HAL_UARTEx_ReceiveToIdle_IT(p_handle->huart, p_handle->rx_buf, RS485_RX_BUF_SIZE);
}
/*
@@ -150,3 +136,48 @@ uint16_t rs485_rx_get_size(const rs485_handle_t *p_handle)
{
return p_handle->rx_size;
}
+
+/* ======================== HAL 回调(UART5 / RS-485 专属) ======================== */
+
+/*
+ * 应用层接收钩子默认实现(弱符号,可被应用重定义)。
+ * 在中断上下文调用,仅做占位,业务解析由应用提供。
+ */
+__weak void rs485_on_rx(uint8_t *buf, uint16_t size)
+{
+ (void)buf;
+ (void)size;
+}
+
+/*
+ * HAL UART 扩展接收回调(IDLE 空闲帧中断触发)。
+ * 当 RS-485 总线空闲超过 1 个字符时间后触发,由 HAL 自动调用。
+ * 注意:回调按实例分发,仅处理本驱动绑定的 UART,不写死 UART5。
+ */
+void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
+{
+ if (huart == g_rs485.huart) {
+ rs485_rx_set_size(&g_rs485, Size);
+
+ /* 通知应用处理本次接收(中断上下文,应尽快返回) */
+ rs485_on_rx(g_rs485.rx_buf, Size);
+
+ /* 重新开启接收 */
+ rs485_receive_start(&g_rs485);
+ }
+}
+
+/*
+ * HAL UART 错误回调(ORE/NE/FE/PE 等)。
+ * 读 SR + DR 清除错误标志后重新开启接收,避免中断锁死。
+ */
+void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
+{
+ if (huart == g_rs485.huart) {
+ /* STM32F4 通过读 SR + DR 清除 ORE/NE/FE/PE 错误标志 */
+ (void)huart->Instance->SR;
+ (void)huart->Instance->DR;
+ /* 重新开启接收 */
+ rs485_receive_start(&g_rs485);
+ }
+}
diff --git a/Drivers/BSP/RS485/rs485.h b/Drivers/BSP/RS485/rs485.h
index 4a1f0996..18964d5c 100644
--- a/Drivers/BSP/RS485/rs485.h
+++ b/Drivers/BSP/RS485/rs485.h
@@ -24,6 +24,7 @@ extern "C" {
* 常量定义
*/
#define RS485_TIMEOUT_DEFAULT 1000U /* 默认发送超时(ms) */
+#define RS485_RX_BUF_SIZE 256U /* 接收缓冲区大小 */
/*
* RS-485 句柄结构体
@@ -34,6 +35,7 @@ typedef struct {
GPIO_TypeDef *dir_port; /* 方向控制 GPIO 端口 */
uint16_t dir_pin; /* 方向控制 GPIO 引脚号 */
volatile uint16_t rx_size; /* 最近一次接收的字节数 */
+ uint8_t rx_buf[RS485_RX_BUF_SIZE]; /* 接收缓冲区 */
} rs485_handle_t;
/*
@@ -48,7 +50,8 @@ typedef struct {
* dir_pin - 方向控制 GPIO 引脚号(如 GPIO_PIN_0)
* 返回值:无
* 限定条件:UART 和 GPIO 须先由 CubeMX 完成初始化
- * 函数说明:初始化后默认处于接收状态(DE 引脚拉低)
+ * 函数说明:初始化后默认处于接收状态(DE 引脚拉低),并自动启动 IDLE
+ * 中断接收,无需再手动调用 rs485_receive_start。
*/
void rs485_init(rs485_handle_t *p_handle,
UART_HandleTypeDef *p_huart,
@@ -73,18 +76,16 @@ HAL_StatusTypeDef rs485_transmit(rs485_handle_t *p_handle,
/*
* 函数功能:启动中断方式接收(IDLE 空闲帧检测)
- * 入口参数:handle - RS-485 句柄指针
- * buf - 接收缓冲区
- * buf_size - 缓冲区大小
+ * 入口参数:handle - RS-485 句柄指针
* 返回值:HAL_OK / HAL_ERROR
* 限定条件:UART 须已开启全局中断,NVIC 中须使能对应 UART 中断
* 函数说明:使用 HAL_UARTEx_ReceiveToIdle_IT 实现变长帧接收,
- * 收到完整帧后在 RxEventCallback 中通知用户。
- * 用户须在回调中重新调用本函数重新开启接收
+ * 接收缓冲固定使用句柄内部的 rx_buf(RS485_RX_BUF_SIZE)。
+ * 当 UART 总线空闲超过 1 个字符时间后硬件触发 IDLE 中断,
+ * HAL 自动调用 HAL_UARTEx_RxEventCallback 并返回已接收字节数。
+ * 用户须在回调中重新调用本函数重新开启接收。
*/
-HAL_StatusTypeDef rs485_receive_start(rs485_handle_t *p_handle,
- uint8_t *p_buf,
- uint16_t buf_size);
+HAL_StatusTypeDef rs485_receive_start(rs485_handle_t *p_handle);
/*
* 函数功能:将 RS-485 句柄与 HAL 回调关联
@@ -103,6 +104,21 @@ void rs485_rx_set_size(rs485_handle_t *p_handle, uint16_t size);
*/
uint16_t rs485_rx_get_size(const rs485_handle_t *p_handle);
+/*
+ * 全局 RS-485 实例(由 rs485.c 定义,应用层通过 extern 引用)。
+ * 接收缓冲已内聚到 rs485_handle_t 的 rx_buf 字段,随实例统一管理。
+ */
+extern rs485_handle_t g_rs485;
+
+/*
+ * 应用层接收钩子:RS-485 收到完整帧(IDLE 空闲帧)后由驱动回调调用,
+ * 传入本次接收缓冲区与有效字节数。默认弱实现为空,应用可重定义此函数
+ * 处理业务数据。
+ * 注意:此函数在 UART 中断上下文执行,应尽快拷贝数据后返回,不要在内部
+ * 调用阻塞式 API(如 rs485_transmit 会切换 DE 并等待 TC)。
+ */
+void rs485_on_rx(uint8_t *buf, uint16_t size);
+
#ifdef __cplusplus
}
#endif
diff --git a/MDK-ARM/STM32F407-Demo.uvprojx b/MDK-ARM/STM32F407-Demo.uvprojx
index ec454e3a..8c990ee5 100644
--- a/MDK-ARM/STM32F407-Demo.uvprojx
+++ b/MDK-ARM/STM32F407-Demo.uvprojx
@@ -2014,6 +2014,11 @@
1
..\test\ch395f_test_task.c
+
+ test_config.c
+ 1
+ ..\test\test_config.c
+
net_test_task.c
1
diff --git a/MDK-ARM/build_log.txt b/MDK-ARM/build_log.txt
index c8e36657..88bb6005 100644
--- a/MDK-ARM/build_log.txt
+++ b/MDK-ARM/build_log.txt
@@ -1,76 +1,77 @@
*** Using Compiler 'V5.06 update 7 (build 960)', folder: 'C:\Keil_v5\ARM\ARMCC\Bin'
Rebuild target 'STM32F407-Demo'
assembling startup_stm32f407xx.s...
-compiling crc.c...
compiling ringbuf.c...
-compiling stm32f4xx_it.c...
-compiling app_main.c...
-compiling dma.c...
+compiling crc.c...
compiling stm32f4xx_hal_flash_ramfunc.c...
-compiling i2c.c...
-compiling sys_clock.c...
-compiling stm32f4xx_hal_timebase_tim.c...
-compiling stm32f4xx_hal_gpio.c...
-compiling stm32f4xx_hal_dma_ex.c...
-compiling stm32f4xx_hal_dma.c...
compiling stm32f4xx_hal_flash_ex.c...
-compiling stm32f4xx_hal_pwr.c...
-compiling spi.c...
+compiling i2c.c...
+compiling app_main.c...
+compiling stm32f4xx_hal_gpio.c...
+compiling dma.c...
+compiling stm32f4xx_hal_dma_ex.c...
compiling stm32f4xx_hal_msp.c...
-compiling stm32f4xx_hal_rcc.c...
-compiling stm32f4xx_hal_flash.c...
compiling gpio.c...
-compiling usart.c...
-compiling stm32f4xx_hal_tim_ex.c...
-compiling net_task.c...
-compiling freertos.c...
+compiling spi.c...
+compiling stm32f4xx_it.c...
compiling stm32f4xx_hal_rcc_ex.c...
+compiling stm32f4xx_hal_flash.c...
+compiling usart.c...
+compiling stm32f4xx_hal_timebase_tim.c...
+compiling sys_clock.c...
+compiling net_task.c...
compiling rs485_task.c...
-compiling main.c...
-compiling adc_task.c...
compiling time_sync.c...
-compiling lftpd_string.c...
-compiling croutine.c...
+compiling adc_task.c...
+compiling stm32f4xx_hal_rcc.c...
+compiling main.c...
+compiling freertos.c...
+compiling stm32f4xx_hal_tim_ex.c...
compiling stm32f4xx_hal_tim.c...
-compiling list.c...
+compiling lftpd_string.c...
+compiling stm32f4xx_hal_dma.c...
+compiling croutine.c...
compiling event_groups.c...
-compiling queue.c...
-compiling stream_buffer.c...
-compiling timers.c...
-compiling tasks.c...
-compiling heap_4.c...
-compiling port.c...
-compiling stm32f4xx_hal_pwr_ex.c...
-compiling journal.c...
-compiling map.c...
-compiling ff.c...
+compiling list.c...
compiling stm32f4xx_hal.c...
-compiling system_stm32f4xx.c...
-compiling lftpd_io.c...
-compiling stm32f4xx_hal_cortex.c...
-compiling stm32f4xx_hal_i2c_ex.c...
+compiling stm32f4xx_hal_pwr_ex.c...
+compiling stream_buffer.c...
+compiling queue.c...
+compiling timers.c...
compiling stm32f4xx_hal_exti.c...
+compiling tasks.c...
+compiling stm32f4xx_hal_pwr.c...
+compiling stm32f4xx_hal_cortex.c...
+compiling heap_4.c...
+compiling map.c...
+compiling journal.c...
+compiling port.c...
+compiling stm32f4xx_hal_i2c_ex.c...
+compiling stm32f4xx_hal_i2c.c...
+compiling ff.c...
compiling rs485.c...
-compiling tpafe5160.c...
+compiling system_stm32f4xx.c...
compiling sd2506.c...
-compiling ch395f.c...
-compiling nand_ftl.c...
-compiling stm32f4xx_hal_spi.c...
-compiling lftpd_inet.c...
compiling gd5f2gq5ue.c...
-compiling net_select.c...
+compiling ch395f.c...
+compiling stm32f4xx_hal_spi.c...
+compiling lftpd_io.c...
+compiling nand_ftl.c...
compiling stm32f4xx_hal_uart.c...
+compiling net_select.c...
+compiling tpafe5160.c...
+compiling lftpd_inet.c...
compiling lftpd.c...
compiling net_socket.c...
-compiling stm32f4xx_hal_i2c.c...
compiling cmsis_os2.c...
compiling ch395f_test_task.c...
-compiling net_test_task.c...
compiling gd5f_test_task.c...
-compiling storage_test_task.c...
+compiling test_config.c...
compiling sd2506_test_task.c...
+compiling net_test_task.c...
+compiling storage_test_task.c...
linking...
-Program Size: Code=64436 RO-data=3936 RW-data=420 ZI-data=51868
+Program Size: Code=59916 RO-data=2552 RW-data=412 ZI-data=51868
FromELF: creating hex file...
".\STM32F407-Demo\STM32F407-Demo.axf" - 0 Error(s), 0 Warning(s).
-Build Time Elapsed: 00:00:14
+Build Time Elapsed: 00:00:16
diff --git a/STM32F407-Demo.ioc b/STM32F407-Demo.ioc
index bebe7630..a1904de8 100644
--- a/STM32F407-Demo.ioc
+++ b/STM32F407-Demo.ioc
@@ -72,7 +72,7 @@ Dma.USART1_TX.0.RequestParameters=Instance,Direction,PeriphInc,MemInc,PeriphData
FREERTOS.FootprintOK=true
FREERTOS.IPParameters=Tasks01,configENABLE_FPU,FootprintOK,configTOTAL_HEAP_SIZE,Queues01
FREERTOS.Queues01=netMsgQueue,8,net_msg_t,0,Dynamic,NULL,NULL
-FREERTOS.Tasks01=defaultTask,24,512,StartDefaultTask,Default,NULL,Dynamic,NULL,NULL;netTask,24,1024,StartNetTask,Default,NULL,Dynamic,NULL,NULL;ftpTask,24,1024,StartFtpTask,As weak,NULL,Dynamic,NULL,NULL;ch395fTestTask,16,4096,StartCh395fTestTask,As weak,NULL,Dynamic,NULL,NULL
+FREERTOS.Tasks01=defaultTask,24,512,StartDefaultTask,Default,NULL,Dynamic,NULL,NULL;netTask,24,1024,StartNetTask,Default,NULL,Dynamic,NULL,NULL;ftpTask,24,1024,StartFtpTask,As weak,NULL,Dynamic,NULL,NULL;testTask,16,4096,start_test_task,As weak,NULL,Dynamic,NULL,NULL
FREERTOS.configENABLE_FPU=1
FREERTOS.configTOTAL_HEAP_SIZE=30720
File.Version=6
diff --git a/Src/freertos.c b/Src/freertos.c
index 516c6106..90a0d5db 100644
--- a/Src/freertos.c
+++ b/Src/freertos.c
@@ -84,10 +84,10 @@ const osThreadAttr_t ftpTask_attributes = {
.stack_size = 1024 * 4,
.priority = (osPriority_t) osPriorityNormal,
};
-/* Definitions for ch395fTestTask */
-osThreadId_t ch395fTestTaskHandle;
-const osThreadAttr_t ch395fTestTask_attributes = {
- .name = "ch395fTestTask",
+/* Definitions for testTask */
+osThreadId_t testTaskHandle;
+const osThreadAttr_t testTask_attributes = {
+ .name = "testTask",
.stack_size = 4096 * 4,
.priority = (osPriority_t) osPriorityBelowNormal,
};
@@ -105,7 +105,7 @@ const osMessageQueueAttr_t netMsgQueue_attributes = {
void StartDefaultTask(void *argument);
void StartNetTask(void *argument);
void StartFtpTask(void *argument);
-void StartCh395fTestTask(void *argument);
+void start_test_task(void *argument);
void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */
@@ -148,11 +148,9 @@ void MX_FREERTOS_Init(void) {
/* creation of ftpTask */
ftpTaskHandle = osThreadNew(StartFtpTask, NULL, &ftpTask_attributes);
-
-
- /* creation of ch395fTestTask */
- ch395fTestTaskHandle = osThreadNew(StartCh395fTestTask, NULL, &ch395fTestTask_attributes);
+ /* creation of testTask */
+ testTaskHandle = osThreadNew(start_test_task, NULL, &testTask_attributes);
/* USER CODE BEGIN RTOS_THREADS */
#ifdef ENABLE_NET_LAYER_TESTS
@@ -243,22 +241,22 @@ __weak void StartFtpTask(void *argument)
/* USER CODE END StartFtpTask */
}
-/* USER CODE BEGIN Header_StartCh395fTestTask */
+/* USER CODE BEGIN Header_start_test_task */
/**
-* @brief Function implementing the ch395fTestTask thread.
+* @brief Function implementing the testTask thread.
* @param argument: Not used
* @retval None
*/
-/* USER CODE END Header_StartCh395fTestTask */
-__weak void StartCh395fTestTask(void *argument)
+/* USER CODE END Header_start_test_task */
+__weak void start_test_task(void *argument)
{
- /* USER CODE BEGIN StartCh395fTestTask */
+ /* USER CODE BEGIN start_test_task */
/* Infinite loop */
for(;;)
{
osDelay(1);
}
- /* USER CODE END StartCh395fTestTask */
+ /* USER CODE END start_test_task */
}
/* Private application code --------------------------------------------------*/
diff --git a/Src/main.c b/Src/main.c
index 7e40511f..033fcf51 100644
--- a/Src/main.c
+++ b/Src/main.c
@@ -59,10 +59,7 @@
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
-/* RS-485 通信实例(UART5: PC12=TX, PD2=RX, PD0=DE) */
-rs485_handle_t g_rs485;
-#define RS485_RX_BUF_SIZE 256
-uint8_t g_rs485_rx_buf[RS485_RX_BUF_SIZE];
+/* RS-485 实例与接收缓冲已收归 rs485.c(见 rs485.h 的 extern 导出) */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
@@ -117,71 +114,8 @@ int main(void)
/* USER CODE BEGIN 2 */
HAL_Delay(100);
- /*
- * 时间子系统初始化(尽早完成,后续所有日志带时间戳)
- * sd2506_init() 从 RTC 读取初始时间
- * sys_clock_init() 将 RTC 时间转为 Unix epoch,建立墙钟基准
- */
+ /* 应用层初始化(时间子系统优先,详见 app_main_init) */
app_main_init();
- sd2506_init();
- sys_clock_init();
-
- /* RS-485 初始化(UART5 + PD0 方向控制) */
- rs485_init(&g_rs485, &huart5, ST_DIR4_GPIO_Port, ST_DIR4_Pin);
- DBG_INFO("RS485 init: OK (UART5, DE=PD0)");
-
- /* 启动第一次 RS-485 接收 */
- rs485_receive_start(&g_rs485, g_rs485_rx_buf, RS485_RX_BUF_SIZE);
-
- /* GD5F 初始化测试 */
- int ret = gd5f2gq5ue_init();
- DBG_INFO("NAND init: %s",
- (ret == GD5F_OK) ? "OK" :
- (ret == GD5F_ERROR) ? "ERROR" :
- (ret == GD5F_BUSY_TIMEOUT) ? "BUSY TIMEOUT" :
- (ret == GD5F_ECC_ERROR) ? "ECC ERROR" :
- (ret == GD5F_PROGRAM_FAIL) ? "PROGRAM FAIL" :
- (ret == GD5F_ERASE_FAIL) ? "ERASE FAIL" :
- (ret == GD5F_ID_MISMATCH) ? "ID MISMATCH" : "UNKNOWN");
-
- /* TPAFE5160 ADC 测试 */
- int tp_ret = tpafe5160_init();
- DBG_INFO("ADC init: %s",
- (tp_ret == TPAFE5160_OK) ? "OK" : "TIMEOUT");
-
- /* SD2506 RTC 诊断信息 */
- {
- /* 读取 ID */
- uint8_t sd_id[8];
- sd2506_get_id(sd_id);
- DBG_INFO("SD2506 ID: %02X%02X%02X%02X%02X%02X%02X%02X",
- sd_id[0], sd_id[1], sd_id[2], sd_id[3],
- sd_id[4], sd_id[5], sd_id[6], sd_id[7]);
-
- /* 读取当前时间 */
- sd2506_time_t sd_time;
- sd2506_get_time(&sd_time);
- DBG_INFO("SD2506 time: %04d-%02d-%02d %02d:%02d:%02d (week %d)",
- sd_time.year, sd_time.month, sd_time.day,
- sd_time.hour, sd_time.minute, sd_time.second,
- sd_time.week);
-
- /* 读取温度 */
- int8_t sd_temp;
- sd2506_get_temperature(&sd_temp);
- DBG_INFO("SD2506 temp: %d C", sd_temp);
-
- /* 读取电池电压 */
- uint16_t sd_vbat;
- sd2506_get_battery_voltage(&sd_vbat);
- DBG_INFO("SD2506 battery: %d.%02d V",
- sd_vbat / 1000, (sd_vbat % 1000) / 10);
- }
-
- /* 启动第一次转换(CubeMX 已配置 BUSY EXTI 中断) */
- tpafe5160_start_conv_irq();
-
- /* CH395F 测试已全部移至 ch395fTestTask */
/* USER CODE END 2 */
/* Init scheduler */
@@ -252,37 +186,10 @@ void SystemClock_Config(void)
/* USER CODE BEGIN 4 */
/*
- * HAL UART 扩展接收回调(IDLE 空闲帧中断触发)
- * 当 RS-485 总线空闲超过 1 个字符时间后触发
+ * RS-485 的 HAL UART 回调(HAL_UARTEx_RxEventCallback / HAL_UART_ErrorCallback)
+ * 已收归 rs485.c 实现,本文件不再定义它们。如需处理接收到的 RS-485 数据,
+ * 请重定义 rs485.h 中的弱钩子 rs485_on_rx(uint8_t *buf, uint16_t size)。
*/
-void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
-{
- if (huart->Instance == UART5) {
- rs485_rx_set_size(&g_rs485, Size);
-
- /* TODO: 在此处处理接收到的 RS-485 数据
- * g_rs485_rx_buf[0 .. Size-1] 为有效数据
- * 例如:rs485_transmit(&g_rs485, g_rs485_rx_buf, Size, 0);
- */
-
- /* 重新开启接收 */
- rs485_receive_start(&g_rs485, g_rs485_rx_buf, RS485_RX_BUF_SIZE);
- }
-}
-
-/*
- * HAL UART 错误回调
- */
-void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
-{
- if (huart->Instance == UART5) {
- /* STM32F4 通过读 SR + DR 清除 ORE/NE/FE/PE 错误标志 */
- (void)huart->Instance->SR;
- (void)huart->Instance->DR;
- /* 重新开启接收 */
- rs485_receive_start(&g_rs485, g_rs485_rx_buf, RS485_RX_BUF_SIZE);
- }
-}
/* USER CODE END 4 */
/**
diff --git a/rx_100k.bin b/rx_100k.bin
deleted file mode 100644
index db15ba9928a4e1345de7b1a4ab7a23a3d6794720..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 102400
zcmeI*g}Rhg7zWUpQ>1i*w6wI8N+T)V-Q6IHNOwu6(%qfXpmaCVU1B1joO8Gjo^=WH
z%*_Aq{l&Wi!I03f@Q4^OW5tdW_k(!x6C_NOI7!lE$y20El{(FbY15_8kTFx{kFsRV
zmOV$#Tp#DolQ&=f0tG)QRJcgdVxNBYdGQh@OO-BDw%ixxD^#phxyqMSt5vU2vsUdo
zb?en{(6CYCCQX|)Z_%<<>o#rMweQfeQ|B&SyLIo;vsdr0`h5LO-*5YU*MGpkL4$`3
z9X5Q#$Wf!mj2$=r`w0^#O`bA!+VmMSXU+cM$2oK7&0nx^(c&dbmn~nha@FcJYuByc
zuyNDoEnBy3-?8(jUAy<}-M9b1!9zd)a`?#6W5-XNJazia*>mSFT)cGo%GGPXUcYhk
z*6llY@7;g!@X_NZPoF*i?ZxkZ{Q1}4|GfP7)$2F^{r@&HDmviv`~Lm@{r^4xJpaA_
zy#Jklod2AEo&Vi`-2dEv-T&1;)PK~!)c@2!)qmB$)&KQB^ndif^#Alf^?&uh_5aO3
znEx>UV*bbclld?6Z|48ZKbrqE|7!l%{ImIQ^Y7;W?LXN6u>WHJ$NrQ3FZ*xy|Li~7
z|Fr*V|JVMr{crp4_W$J{$bXQ3A^$`EiToG&H}ZevAIX1`ek&VT2>^WXXJ{CEC4|DFHNf9JpR-}&$Scm6y7o&U~%=fCsc`S1L9
z{yYDj|IUBszw_Vu@BDZEJO7>k&VT2>^WXXJ{CEC4|DFHNf9JpR-}&$Scm6y7o&U~%
z=fCsc`S1L9{yYDj|IUBszw_Vu@BDZEJO7>k&VT2>^WXXJ{CEC4|DFHNf9JpR-}&$S
zcm6y7o&U~%=fCsc`S1L9{yYDj|IUBszw_Vu@BDZEJO7>k&VT2>^WXXJ{CEC4|DFHN
zf9JpR-}&$Scm6y7o&U~%j}8PwLc{p){CEC4|DFHNf9JpR-}&$Scm6y7o&U~%=fCsc
z`S1L9{yYDj|IUBszw_Vu@BDZEJO7>k&VT2>^WXXJ{CEC4|DFHNf9JpR-}&$Scm6y7
zo&U~%=fCsc`S1L9{yYDj|IUBszw_Vu@BDZEJO7>k&VT2>^WXXJ{CEC4|DFHNf9JpR
z-}&$Scm6y7o&U~%=fCsc`S1L9{yYDj|IUBszw_Vu@BDZEJO7>k&VT2>^WXXJ{CEC4
z|DFHNf9JpR-}&$Scm6y7o&U~%=fCsc`S1L9{yYDj|IUBszw_Vu@BDZEJO7>k&VLVv
sgocGj@Zb6G{CEC4|DFHNf9JpR-}&$Scm6y7o&U~%=fCsc-&6nn9V+8PssI20
diff --git a/test/ch395f_test.h b/test/ch395f_test.h
index e1b789f8..90194433 100644
--- a/test/ch395f_test.h
+++ b/test/ch395f_test.h
@@ -38,8 +38,8 @@ extern "C" {
* 阶段使能开关(取消注释即启用对应阶段)
*
* 构建模式互斥(详见 docs/CH395F_Test_Guide.md「构建模式与执行顺序」):
- * 硬件层模式(阶段 1~5):netTask 不创建,ch395fTestTask 独占 CH395F
- * NET 层模式 (阶段 6~11):完整网络栈,netTask 轮询 + ch395fTestTask 监听循环
+ * 硬件层模式(阶段 1~5):netTask 不创建,testTask 独占 CH395F
+ * NET 层模式 (阶段 6~11):完整网络栈,netTask 轮询 + testTask 监听循环
*
* 注意:同层内可多选(Phase 6~10 共享 testTask 循环);
* Phase 4 (DHCP) 会改变 IP,必须独立运行;
diff --git a/test/ch395f_test_task.c b/test/ch395f_test_task.c
index 46e84203..212f0d20 100644
--- a/test/ch395f_test_task.c
+++ b/test/ch395f_test_task.c
@@ -1345,13 +1345,12 @@ static void phase5_run(void) {
TEST_REPORT("Phase 5");
}
#endif
-void StartCh395fTestTask(void *argument)
+/*
+ * CH395F 测试套件入口(由 test_config.c 的 start_test_task 统一调度)。
+ * 此处只负责 CH395F 自身的硬件层 / NET 层逻辑,套件选择已上移到 test_config.c。
+ */
+void ch395f_test_run(void)
{
- (void)argument;
-
- DBG_INFO("Test task started, suite=%s", TEST_SUITE_NAME);
-
-#if defined(TEST_SUITE_CH395F)
#if defined(ENABLE_HW_LAYER_TESTS)
/* 硬件层模式(阶段 1~5):netTask 未创建,由本任务完成一次性协议栈
* 初始化并独占 CH395F —— 无 net_poll() 并发读取中断,无 SPI 并发事务 */
@@ -1391,16 +1390,5 @@ void StartCh395fTestTask(void *argument)
#else
net_layer_test_main();
#endif
-#endif /* TEST_SUITE_CH395F */
-#elif defined(TEST_SUITE_STORAGE)
- storage_test_run();
-#elif defined(TEST_SUITE_GD5F)
- gd5f_test_run();
-#elif defined(TEST_SUITE_RTC)
- sd2506_test_run();
#endif
-
- for (;;) {
- osDelay(1000);
- }
}
diff --git a/test/ch395f_test_task.h b/test/ch395f_test_task.h
index 66bf1669..aa839584 100644
--- a/test/ch395f_test_task.h
+++ b/test/ch395f_test_task.h
@@ -5,7 +5,8 @@
extern "C" {
#endif
-void StartCh395fTestTask(void *argument);
+void start_test_task(void *argument);
+void ch395f_test_run(void);
#ifdef __cplusplus
}
diff --git a/test/net_test_task.c b/test/net_test_task.c
index 3017277b..43e8257c 100644
--- a/test/net_test_task.c
+++ b/test/net_test_task.c
@@ -1,6 +1,6 @@
/*****************************************************************************
* 模块名称:CH395F NET 层测试(Phase 6~11)
- * 模块功能:基于 BSD Socket API(单连接模式)的 NET 层测试,由 ch395fTestTask
+ * 模块功能:基于 BSD Socket API(单连接模式)的 NET 层测试,由 testTask
* 任务内的 net_layer_test_main() / net_layer_client_test_main() 事件
* 循环驱动,需 PC 配合。
* 包含 Phase 6(TCP Echo) / 7(TCP Client) / 8(大文件) / 9(Select) /
diff --git a/test/net_test_task.h b/test/net_test_task.h
index f5ed5c95..6a583a2a 100644
--- a/test/net_test_task.h
+++ b/test/net_test_task.h
@@ -6,7 +6,7 @@ extern "C" {
#endif
/* NET 层测试事件循环入口(定义于 net_test_task.c)。
- * 由 ch395fTestTask 在 NET 层构建模式下调用,内部创建单连接 TCP 监听
+ * 由 testTask 在 NET 层构建模式下调用,内部创建单连接 TCP 监听
* 并驱动 Phase 6/8~11 依次执行,连接断开后自动重新 listen。 */
void net_layer_test_main(void);
diff --git a/test/sd2506_test.h b/test/sd2506_test.h
index 153084af..9c494d0d 100644
--- a/test/sd2506_test.h
+++ b/test/sd2506_test.h
@@ -37,7 +37,7 @@ extern sd2506_test_stats_t g_sd2506_test_stats;
g_sd2506_test_stats.failed); \
} while (0)
-/* 测试入口:由 StartCh395fTestTask 经 TEST_SUITE_RTC 调度,顺序执行 Phase 1~3 */
+/* 测试入口:由 start_test_task 经 TEST_SUITE_RTC 调度,顺序执行 Phase 1~3 */
void sd2506_test_run(void);
#ifdef __cplusplus
diff --git a/test/sd2506_test_task.c b/test/sd2506_test_task.c
index 4212ab4e..583d5d1c 100644
--- a/test/sd2506_test_task.c
+++ b/test/sd2506_test_task.c
@@ -10,7 +10,7 @@
*
* 当前测试:Phase 1 基本读写(init / 读 / 写 / 回读一致 / 走时),
* 结束后自动恢复板子原始时间。
- * 入口:sd2506_test_run()(由 StartCh395fTestTask 经 TEST_SUITE_RTC 调度)
+ * 入口:sd2506_test_run()(由 start_test_task 经 TEST_SUITE_RTC 调度)
* 创建日期:2026-08-28
******************************************************************************/
diff --git a/test/storage_test.h b/test/storage_test.h
index e0d6b94d..e501960c 100644
--- a/test/storage_test.h
+++ b/test/storage_test.h
@@ -1,7 +1,7 @@
/*
* 模块名称:Storage Test Suite
* 模块功能:FTL + FatFS 存储测试的声明与断言宏。实现见 storage_test_task.c,
- * 统一由 ch395fTestTask 调度;正常产品构建下由 defaultTask 调用,
+ * 统一由 testTask 调度;正常产品构建下由 defaultTask 调用,
* 行为与原 defaultTask 内联逻辑一致。
* 适用平台:STM32F4 系列
* 作者:王建锋
diff --git a/test/storage_test_task.c b/test/storage_test_task.c
index 373a79cf..468e55a9 100644
--- a/test/storage_test_task.c
+++ b/test/storage_test_task.c
@@ -1,6 +1,6 @@
/******************************************************************************
* 模块名称:Storage Test Task
- * 模块功能:FTL + FatFS 存储测试,从 defaultTask 迁入,统一由 ch395fTestTask
+ * 模块功能:FTL + FatFS 存储测试,从 defaultTask 迁入,统一由 testTask
* 调度运行。正常产品构建下由 defaultTask 调用,行为与原内联逻辑一致。
* 作者:王建锋
* 创建日期:2026-08-26
diff --git a/test/test_config.c b/test/test_config.c
new file mode 100644
index 00000000..8adad1ee
--- /dev/null
+++ b/test/test_config.c
@@ -0,0 +1,50 @@
+/******************************************************************************
+ * 模块名称:统一测试任务调度器(Test Config)
+ * 模块功能:所有测试任务的唯一入口。FreeRTOS 的 testTask 仅调用本文件
+ * 的 start_test_task(),由本文件根据 test_config.h 中启用的
+ * TEST_SUITE_* 宏,将测试分发到对应套件的运行函数。
+ *
+ * 各套件运行函数(ch395f_test_run / storage_test_run / gd5f_test_run
+ * / sd2506_test_run)各自实现具体测试逻辑,本文件只负责"选哪个跑",
+ * 不掺杂任何具体测试细节,使测试入口界限清晰、易于维护。
+ * 作者:王建锋
+ * 创建日期:2026-08-29
+ ******************************************************************************/
+
+#define DBG_TAG "[TEST_CFG]"
+#include "cmsis_os.h"
+#include "dbg_log.h"
+
+#include "test_config.h"
+
+#include "ch395f_test_task.h"
+#include "storage_test.h"
+#include "gd5f_test.h"
+#include "sd2506_test.h"
+
+/*
+ * 测试任务统一入口(FreeRTOS start_test_task 线程函数)。
+ * 根据编译期选中的 TEST_SUITE_* 调度对应套件,其余时间挂起。
+ */
+void start_test_task(void *argument)
+{
+ (void)argument;
+
+ DBG_INFO("Test task started, suite=%s", TEST_SUITE_NAME);
+
+#if defined(TEST_SUITE_CH395F)
+ ch395f_test_run();
+#elif defined(TEST_SUITE_STORAGE)
+ storage_test_run();
+#elif defined(TEST_SUITE_GD5F)
+ gd5f_test_run();
+#elif defined(TEST_SUITE_RTC)
+ sd2506_test_run();
+#else
+ DBG_INFO("No test suite selected (normal product build)");
+#endif
+
+ for (;;) {
+ osDelay(1000);
+ }
+}
diff --git a/test/test_config.h b/test/test_config.h
index f16839e7..8bc5f89d 100644
--- a/test/test_config.h
+++ b/test/test_config.h
@@ -1,7 +1,7 @@
/*
* 模块名称:统一测试套件选择
* 模块功能:集中选择当前构建要运行的测试套件。所有测试统一由
- * ch395fTestTask(StartCh395fTestTask)调度,defaultTask 在测试
+ * testTask(start_test_task)调度,defaultTask 在测试
* 构建下空闲,正常产品构建下仅运行存储挂载/测试。
* 适用平台:STM32F4 系列
* 作者:王建锋