451 lines
17 KiB
C
451 lines
17 KiB
C
/* USER CODE BEGIN Header */
|
||
/**
|
||
******************************************************************************
|
||
* File Name : freertos.c
|
||
* Description : Code for freertos applications
|
||
******************************************************************************
|
||
* @attention
|
||
*
|
||
* Copyright (c) 2026 STMicroelectronics.
|
||
* All rights reserved.
|
||
*
|
||
* This software is licensed under terms that can be found in the LICENSE file
|
||
* in the root directory of this software component.
|
||
* If no LICENSE file comes with this software, it is provided AS-IS.
|
||
*
|
||
******************************************************************************
|
||
*/
|
||
/* USER CODE END Header */
|
||
|
||
/* Includes ------------------------------------------------------------------*/
|
||
#include "FreeRTOS.h"
|
||
#include "task.h"
|
||
#include "main.h"
|
||
#include "cmsis_os.h"
|
||
|
||
/* Private includes ----------------------------------------------------------*/
|
||
/* USER CODE BEGIN Includes */
|
||
#include <string.h>
|
||
#include "net_task.h"
|
||
#include "ch395f.h"
|
||
#include "dbg_log.h"
|
||
#include "net_socket.h"
|
||
#include "usart.h"
|
||
#include "ff.h"
|
||
#include "gd5f2gq5ue.h"
|
||
#include "nand_ftl.h"
|
||
#include "lftpd.h"
|
||
#include "../test/ch395f_test_task.h"
|
||
#include "../test/ch395f_test.h" /* ENABLE_NET_LAYER_TESTS:netTask 条件创建 */
|
||
#include "time_sync.h"
|
||
#include "sys_events.h"
|
||
#include "sys_log.h"
|
||
#include "net_cfg.h"
|
||
#include "cfg_file.h"
|
||
/* USER CODE END Includes */
|
||
|
||
/* Private typedef -----------------------------------------------------------*/
|
||
/* USER CODE BEGIN PTD */
|
||
|
||
/* USER CODE END PTD */
|
||
|
||
/* Private define ------------------------------------------------------------*/
|
||
/* USER CODE BEGIN PD */
|
||
/* USER CODE END PD */
|
||
|
||
/* Private macro -------------------------------------------------------------*/
|
||
/* USER CODE BEGIN PM */
|
||
|
||
/* USER CODE END PM */
|
||
|
||
/* Private variables ---------------------------------------------------------*/
|
||
/* USER CODE BEGIN Variables */
|
||
/* 存储测试变量(g_storage_stats / s_perf_buf)已迁至 test/storage_test_task.c */
|
||
osThreadId_t timeSyncTaskHandle;
|
||
const osThreadAttr_t timeSyncTask_attributes = {
|
||
.name = "timeSyncTask",
|
||
.stack_size = 512 * 4,
|
||
.priority = (osPriority_t) osPriorityNormal,
|
||
};
|
||
/* USER CODE END Variables */
|
||
/* Definitions for defaultTask */
|
||
osThreadId_t defaultTaskHandle;
|
||
const osThreadAttr_t defaultTask_attributes = {
|
||
.name = "defaultTask",
|
||
.stack_size = 512 * 4,
|
||
.priority = (osPriority_t) osPriorityNormal,
|
||
};
|
||
/* Definitions for netTask */
|
||
osThreadId_t netTaskHandle;
|
||
const osThreadAttr_t netTask_attributes = {
|
||
.name = "netTask",
|
||
.stack_size = 1024 * 4,
|
||
.priority = (osPriority_t) osPriorityNormal,
|
||
};
|
||
/* Definitions for ftpTask */
|
||
osThreadId_t ftpTaskHandle;
|
||
const osThreadAttr_t ftpTask_attributes = {
|
||
.name = "ftpTask",
|
||
.stack_size = 1024 * 4,
|
||
.priority = (osPriority_t) osPriorityNormal,
|
||
};
|
||
/* Definitions for testTask */
|
||
osThreadId_t testTaskHandle;
|
||
const osThreadAttr_t testTask_attributes = {
|
||
.name = "testTask",
|
||
.stack_size = 1024 * 4,
|
||
.priority = (osPriority_t) osPriorityBelowNormal,
|
||
};
|
||
/* Definitions for uiTask */
|
||
osThreadId_t uiTaskHandle;
|
||
const osThreadAttr_t uiTask_attributes = {
|
||
.name = "uiTask",
|
||
.stack_size = 4096 * 4,
|
||
.priority = (osPriority_t) osPriorityBelowNormal,
|
||
};
|
||
/* Definitions for netMsgQueue */
|
||
osMessageQueueId_t netMsgQueueHandle;
|
||
const osMessageQueueAttr_t netMsgQueue_attributes = {
|
||
.name = "netMsgQueue"
|
||
};
|
||
/* Definitions for sys_evt */
|
||
osEventFlagsId_t sys_evtHandle;
|
||
const osEventFlagsAttr_t sys_evt_attributes = {
|
||
.name = "sys_evt"
|
||
};
|
||
|
||
/* Private function prototypes -----------------------------------------------*/
|
||
/* USER CODE BEGIN FunctionPrototypes */
|
||
void ui_task_func(void *argument);
|
||
static void default_task_net_cfg_save_notify(void);
|
||
/* USER CODE END FunctionPrototypes */
|
||
|
||
void StartDefaultTask(void *argument);
|
||
void StartNetTask(void *argument);
|
||
void StartFtpTask(void *argument);
|
||
void start_test_task(void *argument);
|
||
void StartUiTask(void *argument);
|
||
|
||
void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */
|
||
|
||
/**
|
||
* @brief FreeRTOS initialization
|
||
* @param None
|
||
* @retval None
|
||
*/
|
||
void MX_FREERTOS_Init(void) {
|
||
/* USER CODE BEGIN Init */
|
||
|
||
/* USER CODE END Init */
|
||
|
||
/* USER CODE BEGIN RTOS_MUTEX */
|
||
/* add mutexes, ... */
|
||
/* USER CODE END RTOS_MUTEX */
|
||
|
||
/* USER CODE BEGIN RTOS_SEMAPHORES */
|
||
/* add semaphores, ... */
|
||
/* USER CODE END RTOS_SEMAPHORES */
|
||
|
||
/* USER CODE BEGIN RTOS_TIMERS */
|
||
/* start timers, add new ones, ... */
|
||
/* USER CODE END RTOS_TIMERS */
|
||
|
||
/* Create the queue(s) */
|
||
/* creation of netMsgQueue */
|
||
netMsgQueueHandle = osMessageQueueNew (8, sizeof(net_msg_t), &netMsgQueue_attributes);
|
||
|
||
/* USER CODE BEGIN RTOS_QUEUES */
|
||
/* add queues, ... */
|
||
/* USER CODE END RTOS_QUEUES */
|
||
|
||
/* Create the thread(s) */
|
||
/* creation of defaultTask */
|
||
defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);
|
||
|
||
/* creation of netTask */
|
||
netTaskHandle = osThreadNew(StartNetTask, NULL, &netTask_attributes);
|
||
|
||
/* creation of ftpTask */
|
||
ftpTaskHandle = osThreadNew(StartFtpTask, NULL, &ftpTask_attributes);
|
||
|
||
/* creation of testTask */
|
||
testTaskHandle = osThreadNew(start_test_task, NULL, &testTask_attributes);
|
||
|
||
/* creation of uiTask */
|
||
uiTaskHandle = osThreadNew(StartUiTask, NULL, &uiTask_attributes);
|
||
|
||
/* USER CODE BEGIN RTOS_THREADS */
|
||
/* netTask = 基础线程:上方生成区的实例是全构建唯一实例(P1-6:旧
|
||
* #ifdef ENABLE_NET_LAYER_TESTS 的重复创建已删除)。
|
||
* - 产品 / NET 层测试模式:正常运行 bring_up + net_poll
|
||
* - CH395F 硬件层测试模式:net_task_func 入口自挂起(testTask 独占 CH395F) */
|
||
|
||
/* 本地时间同步任务(PC 推送 TCP):依赖 netTask 处理消息队列 */
|
||
timeSyncTaskHandle = osThreadNew(time_sync_task, NULL, &timeSyncTask_attributes);
|
||
/* USER CODE END RTOS_THREADS */
|
||
|
||
/* Create the event(s) */
|
||
/* creation of sys_evt */
|
||
sys_evtHandle = osEventFlagsNew(&sys_evt_attributes);
|
||
|
||
/* USER CODE BEGIN RTOS_EVENTS */
|
||
/* add events, ... */
|
||
/* USER CODE END RTOS_EVENTS */
|
||
|
||
}
|
||
|
||
/* USER CODE BEGIN Header_StartDefaultTask */
|
||
/**
|
||
* @brief Function implementing the defaultTask thread.
|
||
* @param argument: Not used
|
||
* @retval None
|
||
*/
|
||
/* USER CODE END Header_StartDefaultTask */
|
||
void StartDefaultTask(void *argument)
|
||
{
|
||
/* USER CODE BEGIN StartDefaultTask */
|
||
(void)argument;
|
||
|
||
/* 系统启动期挂载 NAND 上的 FatFS 卷,供 FTP 及其它模块访问。
|
||
* f_mount(forced=1) 会触发 disk_initialize() → nand_ftl_init()
|
||
* (dhara_map_resume)。此前默认构建从未挂载,导致 FTP 的
|
||
* LIST/STOR/RETR 全部返回 550(f_opendir 失败)。
|
||
* 若卷为空(FR_NO_FILESYSTEM,如恢复/坏块重建后清空),则首次自动
|
||
* 格式化建卷(MKFS_PARM 与存储测试一致:FM_FAT32 / 无分区表)。
|
||
*
|
||
* 卷健康探针 + 自愈(2026-09-20,台架实测 /log 每次开机写失败 ×5 的
|
||
* 根因处置):真根因 = s_append 打开模式缺 FA_WRITE 位——本 FatFs 移植
|
||
* FA_OPEN_APPEND(0x30) = FA_OPEN_ALWAYS|FA_SEEKEND,不含 FA_WRITE(0x02),
|
||
* 未带写位打开的文件 f_write 恒返回 FR_DENIED(7) → 打开模式已改
|
||
* FA_WRITE|FA_OPEN_APPEND(见 sys_log_mcu.c)。残余风险 = f_mount 仅校验
|
||
* 首扇区 BPB,FAT 区陈旧/擦除(旧镜像时代遗留、换芯片、格式化中途掉电)
|
||
* 时挂载仍"成功",文件操作才报 FR_DENIED/FR_DISK_ERR/FR_INT_ERR 等结构
|
||
* 性错误(注意本移植 f_write 簇分配失败返回 FR_OK+短写,探针须校验写回
|
||
* 字节数,短写按 FR_DENIED 归一化)。挂载后做一次小写探针(两次确认,
|
||
* 防 FTL 瞬态抖动误触发);结构性错误 → 自动重格式化自愈。代价:卷被清空
|
||
* (/config.ini 丢失 → 配置引擎首启路径回落工厂默认并自动重建模板)——
|
||
* 仅当卷已不可用时触发,可用设备 > 一份配置文件。 */
|
||
static FATFS default_fatfs;
|
||
nand_ftl_set_tree_probe(1); /* 诊断窗口:挂载+探针的读路径全记录 */
|
||
FRESULT fr = f_mount(&default_fatfs, "", 1);
|
||
if (fr == FR_NO_FILESYSTEM) {
|
||
nand_ftl_set_tree_probe(0); /* mkfs 路径不追踪(I/O 洪泛) */
|
||
DBG_INFO("[FS] no FAT volume, formatting NAND...");
|
||
static uint8_t fs_work[FF_MAX_SS];
|
||
MKFS_PARM opts = { FM_FAT32, 0, 0, 0, 0 };
|
||
/* 格式化前清空 FTL map 树(2026-09-20 台架实证:不清则 mkfs 经旧树
|
||
* 写新卷 → on-disk 树残留旧节点/旧绑定 → 复位后 resume 混用新旧 →
|
||
* FAT 探针失败 → 无限重格式化) */
|
||
(void)nand_ftl_format();
|
||
fr = f_mkfs("", &opts, fs_work, sizeof(fs_work));
|
||
if (fr == FR_OK) {
|
||
fr = f_mount(&default_fatfs, "", 1);
|
||
} else {
|
||
DBG_ERROR("[FS] f_mkfs failed (fr=%d)", (int)fr);
|
||
}
|
||
}
|
||
if (fr == FR_OK) {
|
||
/* 探针:/.fschk(8.3 合规)小写一次,验证 FAT 链真实可用。本移植
|
||
* f_write 簇分配失败返回 FR_OK+短写 → 必须校验写回字节数;短写按
|
||
* FR_DENIED 归一化(落入下方结构性集合触发重格式化) */
|
||
#define FS_VOL_PROBE(h) do { \
|
||
FIL __chk; \
|
||
static const uint8_t __chkbuf[8] = { 0 }; \
|
||
UINT __bw = 0u; \
|
||
FRESULT __wr; \
|
||
(h) = f_open(&__chk, "/.fschk", FA_WRITE | FA_OPEN_ALWAYS); \
|
||
if ((h) == FR_OK) { \
|
||
__wr = f_write(&__chk, __chkbuf, (UINT)sizeof(__chkbuf), &__bw); \
|
||
(void)f_close(&__chk); \
|
||
if (__wr == FR_OK && __bw == (UINT)sizeof(__chkbuf)) { \
|
||
(h) = FR_OK; \
|
||
(void)f_unlink("/.fschk"); \
|
||
} else { \
|
||
(h) = (__wr != FR_OK) ? __wr : FR_DENIED; \
|
||
} \
|
||
} \
|
||
} while (0)
|
||
FRESULT hfr;
|
||
nand_ftl_set_tree_probe(1);
|
||
FS_VOL_PROBE(hfr);
|
||
if (hfr != FR_OK) {
|
||
FS_VOL_PROBE(hfr); /* 两击确认 */
|
||
}
|
||
nand_ftl_set_tree_probe(0);
|
||
if (hfr != FR_OK) {
|
||
nand_ftl_scan_checks(); /* 诊断:环上 checkpoint 的 epoch 分布 */
|
||
}
|
||
if (hfr != FR_OK) {
|
||
if (hfr == FR_DENIED || hfr == FR_INVALID_OBJECT ||
|
||
hfr == FR_INT_ERR || hfr == FR_DISK_ERR ||
|
||
hfr == FR_NO_FILE || hfr == FR_NO_PATH ||
|
||
hfr == FR_INVALID_NAME || hfr == FR_NO_FILESYSTEM) {
|
||
DBG_INFO("[FS] volume FAT invalid (fr=%d), reformatting...",
|
||
(int)hfr);
|
||
f_mount(NULL, "", 0);
|
||
/* 格式化前清空 FTL map 树(同首启路径:防新旧树混合) */
|
||
(void)nand_ftl_format();
|
||
{
|
||
static uint8_t fs_work2[FF_MAX_SS];
|
||
MKFS_PARM opts2 = { FM_FAT32, 0, 0, 0, 0 };
|
||
fr = f_mkfs("", &opts2, fs_work2, sizeof(fs_work2));
|
||
if (fr == FR_OK) {
|
||
fr = f_mount(&default_fatfs, "", 1);
|
||
}
|
||
}
|
||
nand_ftl_log_state("[after-mkfs]");
|
||
nand_ftl_log_state("[after-mkfs]");
|
||
if (fr == FR_OK) {
|
||
DBG_INFO("[FS] volume reformatted OK");
|
||
} else {
|
||
DBG_ERROR("[FS] volume repair failed (fr=%d)", (int)fr);
|
||
syslog_post(SYSLOG_SELFTEST, "storage fault");
|
||
}
|
||
} else {
|
||
/* 其余错误(写保护/卷锁/参数类)不重格式化——只上报,
|
||
* 防对写锁卷整卷擦除(FTL 磨损最大操作) */
|
||
DBG_ERROR("[FS] volume probe failed (fr=%d)", (int)hfr);
|
||
syslog_post(SYSLOG_SELFTEST, "storage fault");
|
||
fr = FR_INT_ERR;
|
||
}
|
||
}
|
||
#undef FS_VOL_PROBE
|
||
if (fr == FR_OK) {
|
||
DBG_INFO("[FS] FATFS mounted");
|
||
nand_ftl_log_state("[probe-ok]");
|
||
/* 日志引擎挂载后处理:建 /log 目录 + 写"上电/重启"分类记录(唯一写者) */
|
||
syslog_mcu_mounted();
|
||
/* 加载网络配置进 RAM 副本(配置引擎属主 = 本任务;首启含旧档迁移;
|
||
* 文件缺失/损坏 = 工厂默认,不阻塞组网)。须在置位 STORAGE_READY 前,
|
||
* 使"存储就绪"屏障蕴含"配置已就绪",netTask 组网时配置恒有效 */
|
||
(void)net_cfg_load();
|
||
/* 广播"存储就绪"(= 卷挂载 + 配置加载完成),唤醒依赖文件系统的任务 */
|
||
if (sys_evtHandle != NULL) {
|
||
osEventFlagsSet(sys_evtHandle, SYS_EVT_STORAGE_READY);
|
||
}
|
||
}
|
||
} else {
|
||
DBG_ERROR("[FS] f_mount failed (fr=%d)", (int)fr);
|
||
/* 存储不可写:自检故障仅能滞留待发队列(串口已告警) */
|
||
syslog_post(SYSLOG_SELFTEST, "filesystem fault");
|
||
}
|
||
|
||
/* 注册"保存网络配置"请求通知:UI(uiTask)投递 → 置事件 → 本任务立即执行写盘
|
||
* (配置引擎属主 = 本任务;UI 保存不再直接触碰配置文件引擎,P1-3) */
|
||
net_cfg_set_save_req_cb(default_task_net_cfg_save_notify);
|
||
|
||
/* 配置 poll 周期计数(每 4 次循环 = 2s 轮询一次,与引擎文档/模拟器一致) */
|
||
uint32_t cfg_poll_cnt = 0u;
|
||
|
||
for (;;) {
|
||
/* 等待"保存网络配置"请求:有请求立即唤醒;无请求 500ms 后返回
|
||
* (取代原 osDelay(500):循环节奏不变,保存响应零空转) */
|
||
if (sys_evtHandle != NULL) {
|
||
(void)osEventFlagsWait(sys_evtHandle, SYS_EVT_CFG_SAVE, osFlagsWaitAny, 500u);
|
||
}
|
||
/* 执行待处理保存(配置引擎属主:写盘 + 成功后立即应用);无请求 = 空操作 */
|
||
net_cfg_save_run();
|
||
/* 配置文件变更轮询(2s 周期;沉降去抖在引擎内) */
|
||
if ((cfg_poll_cnt++ & 3u) == 0u) {
|
||
cfg_file_poll();
|
||
}
|
||
HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);
|
||
HAL_GPIO_TogglePin(LED2_GPIO_Port, LED2_Pin);
|
||
HAL_GPIO_TogglePin(LED3_GPIO_Port, LED3_Pin);
|
||
/* 排空运行期日志(上电/重启分类与自检故障落盘) */
|
||
syslog_drain();
|
||
}
|
||
/* USER CODE END StartDefaultTask */
|
||
}
|
||
|
||
/* USER CODE BEGIN Header_StartNetTask */
|
||
/**
|
||
* @brief Function implementing the netTask thread.
|
||
* @param argument: Not used
|
||
* @retval None
|
||
*/
|
||
/* USER CODE END Header_StartNetTask */
|
||
void StartNetTask(void *argument)
|
||
{
|
||
/* USER CODE BEGIN StartNetTask */
|
||
net_task_func(argument);
|
||
/* USER CODE END StartNetTask */
|
||
}
|
||
|
||
/* USER CODE BEGIN Header_StartFtpTask */
|
||
/**
|
||
* @brief Function implementing the ftpTask thread.
|
||
* @param argument: Not used
|
||
* @retval None
|
||
*/
|
||
/* USER CODE END Header_StartFtpTask */
|
||
__weak void StartFtpTask(void *argument)
|
||
{
|
||
/* USER CODE BEGIN StartFtpTask */
|
||
/* 等网络 + 存储都真正就绪后再 listen,取代拍脑袋的 7s 魔法延时 */
|
||
/*
|
||
sys_evtHandle:等待哪个事件组
|
||
SYS_EVT_NET_READY | SYS_EVT_STORAGE_READY:要等哪几位(1u<<0 网络、1u<<1 存储)
|
||
osFlagsWaitAll:请求的位全部置位才唤醒(即"网络且存储都就绪")
|
||
osWaitForever:无限期等待,不设超时——任务会挂起(让出 CPU,零占用),直到生产者置位对应位才被唤醒
|
||
*/
|
||
if (sys_evtHandle != NULL) {
|
||
(void)osEventFlagsWait(sys_evtHandle,
|
||
SYS_EVT_NET_READY | SYS_EVT_STORAGE_READY,
|
||
osFlagsWaitAll, osWaitForever);
|
||
}
|
||
lftpd_t ftp;
|
||
lftpd_start("/", 21, &ftp);
|
||
/* 广播"FTP 服务就绪"(预留给依赖 FTP 的后续功能) */
|
||
if (sys_evtHandle != NULL) {
|
||
osEventFlagsSet(sys_evtHandle, SYS_EVT_FTP_READY);
|
||
}
|
||
/* USER CODE END StartFtpTask */
|
||
}
|
||
|
||
/* USER CODE BEGIN Header_start_test_task */
|
||
/**
|
||
* @brief Function implementing the testTask thread.
|
||
* @param argument: Not used
|
||
* @retval None
|
||
*/
|
||
/* USER CODE END Header_start_test_task */
|
||
__weak void start_test_task(void *argument)
|
||
{
|
||
/* USER CODE BEGIN start_test_task */
|
||
/* Infinite loop */
|
||
for(;;)
|
||
{
|
||
osDelay(1);
|
||
}
|
||
/* USER CODE END start_test_task */
|
||
}
|
||
|
||
/* USER CODE BEGIN Header_StartUiTask */
|
||
/**
|
||
* @brief Function implementing the uiTask thread.
|
||
* @param argument: Not used
|
||
* @retval None
|
||
*/
|
||
/* USER CODE END Header_StartUiTask */
|
||
void StartUiTask(void *argument)
|
||
{
|
||
/* USER CODE BEGIN StartUiTask */
|
||
ui_task_func(argument);
|
||
/* USER CODE END StartUiTask */
|
||
}
|
||
|
||
/* Private application code --------------------------------------------------*/
|
||
/* USER CODE BEGIN Application */
|
||
/*
|
||
* "保存网络配置"请求通知(注册给 net_cfg 的保存交接):
|
||
* uiTask 投递保存请求 → 置 SYS_EVT_CFG_SAVE → defaultTask 循环立即唤醒执行写盘
|
||
*/
|
||
static void default_task_net_cfg_save_notify(void)
|
||
{
|
||
if (sys_evtHandle != NULL) {
|
||
(void)osEventFlagsSet(sys_evtHandle, SYS_EVT_CFG_SAVE);
|
||
}
|
||
}
|
||
/* USER CODE END Application */
|