Files
STM32F4-Base/Src/freertos.c
T

446 lines
16 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* 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 "test_config.h" /* 套件选择:STORAGE 套件下 defaultTask 让出存储所有权 */
#include <string.h>
#include "net_task.h"
#include "ch395f.h"
#include "dbg_log.h"
#include "net_socket.h"
#include "lftpd.h"
#include "task_watchdog.h"
#include "../test/board/ch395f_test_task.h"
#include "../test/board/ch395f_test.h" /* ENABLE_NET_LAYER_TESTS:netTask 条件创建 */
#include "time_sync.h"
#include "sys_events.h"
#include "default_task.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/board/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",
/* 512→1024: 该任务扛 mount+mkfs+FAT探针+cfg加载+syslog写+配置引擎
* (全深层 FatFS I/O),2KB 实测峰值溢出——开启
* configCHECK_FOR_STACK_OVERFLOW=2 后被钩子抓到(2026-09-21)。
* ⚠ 注释位于 .ioc 生成区之外,CubeMX 重生成会丢(值本身在 FREERTOS.Tasks01 里,不丢)。 */
.stack_size = 1024 * 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",
/* 1024→2048: 存储套件 deep call chain + 局部缓冲,4KB 实测溢出(2026-09-20)。
* ⚠ 注释位于 .ioc 生成区之外,CubeMX 重生成会丢(值本身在 FREERTOS.Tasks01 里,不丢)。 */
.stack_size = 2048 * 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);
/* 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 */
/* 任务体已迁出至 App/task/default_task.c(2026-09-25):
* 本文件只保留 CubeMX 接线(任务创建)与崩溃钩子 */
default_task_func(argument);
/* 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 */
task_wd_bind(TASK_WD_FTP); /* FTL 心跳路由:登记本任务句柄(入口一次) */
/* 等网络 + 存储都真正就绪后再 listen,取代拍脑袋的 7s 魔法延时 */
/*
sys_evtHandle:等待哪个事件组
SYS_EVT_NET_READY | SYS_EVT_STORAGE_READY:要等哪几位(1u<<0 网络、1u<<1 存储)
osFlagsWaitAll:请求的位全部置位才唤醒(即"网络且存储都就绪")
100u:单次等待分片时长——2026-10-02 台架修复由 osWaitForever 改为 100ms
分片循环:IWDG 必需掩码含 FTP 位,屏障等待态必须能喂心跳(见下方注释)
*/
if (sys_evtHandle != NULL) {
/* osFlagsNoClear 必须带:就绪位是**状态**不是命令,消费型等待会把位清掉,
* 导致后面 WaitAll(两位) 的等待者永远等不到(2026-09-21 实测:netTask 先
* 消费 STORAGE_READY、uiTask/timeSync 再消费 NET_READY → 本任务永不唤醒、
* FTP 21 端口从未监听)。
* 2026-10-02 台架修复:等待改 100ms 分片 + 每片喂心跳(与 timeSync/uiTask/
* netTask 入口同模式)——IWDG 必需掩码含 FTP 位,原 osWaitForever 无心跳,
* IWDG 启动(存储就绪)时网络未必就绪 → 掩码 800ms 不可达 → 停喂 → 复位循环 */
for (;;) {
uint32_t r = osEventFlagsWait(sys_evtHandle,
SYS_EVT_NET_READY | SYS_EVT_STORAGE_READY,
osFlagsWaitAll | osFlagsNoClear, 100u);
if ((r & osFlagsError) == 0u) {
break; /* 两位均就绪(状态位,未被消费) */
}
lftpd_task_wd_beat(); /* 看门狗:分片喂心跳(屏障等待态) */
}
}
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 */
/*
* 名称指针合法性校验(栈溢出/越界可能已破坏 TCB,指针不可信):
* 只接受落在 Flash / 主 SRAM / CCM 映射范围内的指针;不校验就直接交给 UART,
* 钩子自身也会以同样方式崩(2026-09-24 实测 HardFault:BFAR=0x10010000 恰为
* CCM 末尾+1,PC 落在 HAL_UART_Transmit 的缓冲读取处)。
*/
static int hook_ptr_ok(const void *p)
{
uint32_t a = (uint32_t)p;
uint32_t win = 32u; /* 将要读取的最大字节数(任务名 ≤ 32) */
if ((a >= 0x08000000u) && (a <= (0x08100000u - win))) {
return 1; /* Flash(RO 区) */
}
if ((a >= 0x20000000u) && (a <= (0x20020000u - win))) {
return 1; /* SRAM1 + SRAM2 */
}
if ((a >= 0x10000000u) && (a <= (0x10010000u - win))) {
return 1; /* CCM(FreeRTOS 堆:TCB/栈所在) */
}
return 0;
}
/*
* 栈溢出钩子(configCHECK_FOR_STACK_OVERFLOW=2,2026-09-20 存储套件死机事故后加):
* 溢出 = 内核状态已不可信,禁止走 printf/内核对象/控制台锁,裸发固定文本后复位。
* 2026-09-24 加固:任务名先经范围校验并拷入本地缓冲(不再把 TCB 指针交给 UART),
* 指针不可信时打印占位文本——避免"上报崩溃的代码自己再崩一次"。
*/
void vApplicationStackOverflowHook(void *xTask, signed char *pcTaskName)
{
static const char msg_head[] = "\r\n[ERR] STACK OVERFLOW task=";
static const char msg_bad[] = "(name unavailable)";
uint16_t len = 0u;
(void)xTask;
dbg_console_write_raw(msg_head, (unsigned)(sizeof(msg_head) - 1u));
if ((pcTaskName != NULL) && (hook_ptr_ok(pcTaskName) != 0)) {
while ((len < 32u) && (pcTaskName[len] != '\0')) {
len++;
}
}
if (len == 0u) {
dbg_console_write_raw(msg_bad, (unsigned)(sizeof(msg_bad) - 1u));
} else {
char name[33]; /* 拷入本地缓冲:UART 永不接触 TCB 指针 */
memcpy(name, pcTaskName, (size_t)len);
name[len] = '\0';
dbg_console_write_raw(name, (unsigned)len);
}
dbg_console_write_raw("\r\n", 2u);
HAL_Delay(200);
NVIC_SystemReset();
}
/*
* 堆分配失败钩子:只告警不复位(NULL 由各调用点自行处理并上报自检)。
* 同样走裸 UART 有界发送——钩子可能在临界区内被调,printf/信号量不可用。
*/
void vApplicationMallocFailedHook(void)
{
static const char msg[] = "\r\n[ERR] MALLOC FAILED (heap exhausted)\r\n";
dbg_console_write_raw(msg, (unsigned)(sizeof(msg) - 1u));
}
/*
* 断言失败钩子(2026-09-29,配合 IWDG;见 Inc/FreeRTOSConfig.h 的 configASSERT)。
* 与栈溢出钩子同为"内核状态已不可信"路径:裸 UART 有界输出 + 主动复位,
* 不走 printf / 内核对象 / 控制台锁。
* 设计取舍(docs/子系统设计/看门狗设计说明.md §4.6):
* - 上报 file/line 是断言唯一有价值的现场信息,故必须打印(早于 TCB 名之类);
* - 打印后**主动 NVIC_SystemReset()** 而非空转:断言点未必能让 IWDG 抓到
* (若恰在启动段,IWDG 尚未启动),主动复位保证任何断言都能自恢复;
* 代价是 RCC_CSR 记 SFTRSTF 而非 IWDGRSTF——故上报串里写死 ASSERT 字样,
* 使"重启原因"可由日志区分(软件复位 + 上一条是 ASSERT = 断言导致)。
*/
void vApplicationAssertFailed(const char *file, long line)
{
static const char msg_head[] = "\r\n[ERR] ASSERT FAILED ";
const char *p = file;
char num[16];
int i;
/* 文件名只取末段(全路径太长且无信息量) */
if (p != NULL) {
const char *q = p;
while (*q != '\0') {
if ((*q == '/') || (*q == '\\')) {
p = q + 1;
}
q++;
}
/* 有界拷贝:上限 48 字符 */
{
uint16_t len = 0u;
char name[49];
while ((len < 48u) && (p[len] != '\0')) {
name[len] = p[len];
len++;
}
name[len] = '\0';
dbg_console_write_raw(msg_head, (unsigned)(sizeof(msg_head) - 1u));
dbg_console_write_raw(name, (unsigned)len);
}
} else {
dbg_console_write_raw(msg_head, (unsigned)(sizeof(msg_head) - 1u));
dbg_console_write_raw("(no file)", 9u);
}
dbg_console_write_raw(":", 1u);
/* 行号转十进制(不用 snprintf:钩子可能在临界区,且避免库依赖) */
i = 0;
if (line <= 0) {
num[i++] = '0';
} else {
char rev[16];
int n = 0;
while ((line > 0) && (n < 15)) {
rev[n++] = (char)('0' + (line % 10));
line /= 10;
}
while (n > 0) {
num[i++] = rev[--n];
}
}
dbg_console_write_raw(num, (unsigned)i);
dbg_console_write_raw(" (resetting)\r\n", 15u);
HAL_Delay(200); /* 等 UART 移位寄存器排空 */
NVIC_SystemReset();
}
/* USER CODE END Application */