36a3c3449c
推荐清单第 3 步(结构债 S-2 剩余面),用户批准"按你推荐做"。 新建 `App/task/task_handles.h`——**只有声明、没有 `.c`**:头文件不需要双端注册, 且 `../App/task` 本来就在 uvprojx 的 `<IncludePath>` 里,所以这一步零构建面。 - `App/task/default_task.c:52-69` 的 18 行局部 extern(9 个句柄 + 9 个 attributes) 删除,改 include;栈水位观测用的任务清单表本体仍留在该文件 - `App/task/adc_task.c:125` 的 1 行同法收口 - `Src/main.c` 函数内的 `extern volatile uint32_t g_rcc_csr_latched;` 移进 `App/app/app_main.h`(`main.c` 本来就 include 它),USER CODE 区内只留"读 RCC_CSR → 写 RMVF 清除"两句,顺序约束的原注释保留 为什么值得做:原先每加一个任务都得记得往 `default_task.c` 再抄两行 extern,抄 漏一行 = 读别人的栈(静默,无报错)。现在只有一个改动点;而且 CubeMX 若把句柄 改名,报错会落在链接期而不是静默通过。 **镜像逐项体积不变**:`Code=213,188 RO-data=18,580 RW-data=1,184 ZI-data=131,504`,`@build` 0 错 0 警告 → 行为中性。 `check_cubemx_survival.py` 对这次有意的 USER CODE 区改动报"漂移",已按其文档流程 `--write` 重建基线(`.cubemx_survival_baseline.json` 仅 1 个指纹变化;顺手把该脚本 写文件带来的 CRLF 归一回 LF,符合 `* text=auto eol=lf`),`--check` 复跑通过、易失 清单 29 项全部到位。 报告 §0.4/§9 的 S-2 口径同时改成**精确分类**:该修的手写跨模块裸 extern **24 处, 今天已消 22 处,只剩 2 处**(`ch395f.c:177` 的 `g_spi1_dma_done` 与 `net_socket.c:77` 的 `netMsgQueueHandle`);`stm32f4xx_it.c:241-256` 那 16 条是 CubeMX「External variables」生成块(删了会被重生成写回来,**不算债**),`:183/195/207/220` 那 4 条 是手写在 `__asm` Handler 里引用汇编标签(ARMCC `__asm` 取不到 C 数据符号, **合法例外**)。上一版写的"25 处"把这三类混在一起了,一并更正。另更正 §0.4 的 S-1 一格:不是"运行时属主纪律被破"(UI 只投递 `net_cfg_save_request()`,写盘在 defaultTask),也不是"进不了主机侧单测"(模拟器已跑整条链),要修的只是静态分层 方向。 门禁:`@build` 0/0;`cubemx-survival --check` 通过;`check_doc_links.py --all --sections-strict` 146 文档 0 问题;`check_ids.py --strict` 增量拦截通过、可判定 82.83%;`mem-numbers` 0。字节卫生:新增头文件与各改动文件 BOM 无 / CR 0 / U+FFFD 0。未推送。
319 lines
12 KiB
C
319 lines
12 KiB
C
/* USER CODE BEGIN Header */
|
||
/**
|
||
******************************************************************************
|
||
* @file : main.c
|
||
* @brief : Main program body
|
||
******************************************************************************
|
||
* @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 "main.h"
|
||
#include "cmsis_os.h"
|
||
#include "adc.h"
|
||
#include "dma.h"
|
||
#include "i2c.h"
|
||
#include "iwdg.h"
|
||
#include "spi.h"
|
||
#include "tim.h"
|
||
#include "usart.h"
|
||
#include "gpio.h"
|
||
|
||
/* Private includes ----------------------------------------------------------*/
|
||
/* USER CODE BEGIN Includes */
|
||
#include <string.h>
|
||
#include <stdio.h>
|
||
#include "ch395f.h"
|
||
#include "gd5f2gq5ue.h"
|
||
#include "tpafe5160.h"
|
||
#include "sd2506.h"
|
||
#include "rs485.h"
|
||
#include "net_socket.h"
|
||
#include "net_select.h"
|
||
#include "app_main.h"
|
||
#include "sys_clock.h"
|
||
#include "task_watchdog.h" /* task_wd_tick_probe():round 8b 时间源存活性取证 */
|
||
#include "adc_task.h" /* adc_tick_irq_notify():TIM3 采样节拍 → 唤醒 adcTask */
|
||
/* 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 */
|
||
/* N-20(2026-10-12):本文件唯一的 DBG_* 调用点是 Error_Handler;其帧经
|
||
* MX_*_Init 调用链叠在 MSP 上,而 MSP 仅 1KB(startup Stack_Size 0x400)
|
||
* → 行缓冲收紧到 128 B(本文件最长行 = Error_Handler 行 ≈78 B < 128,不截断)。
|
||
* 日志成本口径见 dbg_log.h 的 DBG_LINE_BUF_MAX 注释。 */
|
||
#define DBG_LINE_BUF_MAX 128U
|
||
#define DBG_TAG "[MAIN]"
|
||
#include "dbg_log.h"
|
||
/* USER CODE END PM */
|
||
|
||
/* Private variables ---------------------------------------------------------*/
|
||
|
||
/* USER CODE BEGIN PV */
|
||
/* RS-485 实例与接收缓冲已收归 rs485.c(见 rs485.h 的 extern 导出) */
|
||
/* USER CODE END PV */
|
||
|
||
/* Private function prototypes -----------------------------------------------*/
|
||
void SystemClock_Config(void);
|
||
void MX_FREERTOS_Init(void);
|
||
/* USER CODE BEGIN PFP */
|
||
|
||
/* USER CODE END PFP */
|
||
|
||
/* Private user code ---------------------------------------------------------*/
|
||
/* USER CODE BEGIN 0 */
|
||
|
||
/* USER CODE END 0 */
|
||
|
||
/**
|
||
* @brief The application entry point.
|
||
* @retval int
|
||
*/
|
||
int main(void)
|
||
{
|
||
|
||
/* USER CODE BEGIN 1 */
|
||
/* round 8b 取证:**必须在 HAL_Init() 之前**把上一轮的"时间源存活性"终值
|
||
* 快照到 .noinit 的 prev 槽——TIM7 在 HAL_Init 里就 HAL_TIM_Base_Start_IT
|
||
* 开始 1ms 中断,而开机上报要到 app_main_init(≈117ms)才跑;不先搬走,
|
||
* 上一轮终值会被本轮开机瞬间的 tick 覆盖(见 docs/子系统设计/看门狗设计说明.md 2026-10-02 变更记录)。 */
|
||
task_wd_tick_snapshot();
|
||
/* USER CODE END 1 */
|
||
|
||
/* MCU Configuration--------------------------------------------------------*/
|
||
|
||
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
|
||
HAL_Init();
|
||
|
||
/* USER CODE BEGIN Init */
|
||
/* 复位原因锁存(2026-09-29,配合 IWDG):RCC_CSR 的复位标志只由上电复位(POR)
|
||
* 清除,其余复位(看门狗/软件/引脚/欠压)会跨复位保留——若不主动清除,
|
||
* 上一次的 IWDGRSTF 会一直挂着,导致每次启动都被误记为"看门狗复位"。
|
||
* 故在 HAL_Init()(此时 RCC 可用、串口尚未就绪)之后**立即读取并清除**,
|
||
* 锁存到 RCC_CSR 影子变量,待日志子系统起来后由 app_main_init 打印。
|
||
* 注意顺序:读 → 写 RMVF 清除,两步之间不可插入其它可能复位的代码。 */
|
||
{ /* 符号声明见 App/app/app_main.h */
|
||
g_rcc_csr_latched = RCC->CSR; /* 锁存:含 IWDGRSTF/SFTRSTF/PORRSTF 等 */
|
||
RCC->CSR |= RCC_CSR_RMVF; /* 写 1 清全部复位标志(只清标志,不影响时钟) */
|
||
}
|
||
/* USER CODE END Init */
|
||
|
||
/* Configure the system clock */
|
||
SystemClock_Config();
|
||
|
||
/* USER CODE BEGIN SysInit */
|
||
|
||
/* USER CODE END SysInit */
|
||
|
||
/* Initialize all configured peripherals */
|
||
MX_GPIO_Init();
|
||
MX_DMA_Init();
|
||
MX_USART1_UART_Init();
|
||
MX_SPI2_Init();
|
||
MX_SPI1_Init();
|
||
MX_I2C1_Init();
|
||
MX_UART4_Init();
|
||
MX_UART5_Init();
|
||
MX_USART2_UART_Init();
|
||
MX_USART3_UART_Init();
|
||
MX_IWDG_Init();
|
||
MX_ADC1_Init();
|
||
MX_TIM3_Init();
|
||
/* USER CODE BEGIN 2 */
|
||
HAL_Delay(100);
|
||
|
||
/* 应用层初始化(时间子系统优先,详见 app_main_init) */
|
||
app_main_init();
|
||
/* USER CODE END 2 */
|
||
|
||
/* Init scheduler */
|
||
osKernelInitialize(); /* Call init function for freertos objects (in cmsis_os2.c) */
|
||
MX_FREERTOS_Init();
|
||
|
||
/* Start scheduler */
|
||
osKernelStart();
|
||
|
||
/* We should never get here as control is now taken by the scheduler */
|
||
|
||
/* Infinite loop */
|
||
/* USER CODE BEGIN WHILE */
|
||
while (1)
|
||
{
|
||
/* USER CODE END WHILE */
|
||
|
||
/* USER CODE BEGIN 3 */
|
||
}
|
||
/* USER CODE END 3 */
|
||
}
|
||
|
||
/**
|
||
* @brief System Clock Configuration
|
||
* @retval None
|
||
*/
|
||
void SystemClock_Config(void)
|
||
{
|
||
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
|
||
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
|
||
|
||
/** Configure the main internal regulator output voltage
|
||
*/
|
||
__HAL_RCC_PWR_CLK_ENABLE();
|
||
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
|
||
|
||
/** Initializes the RCC Oscillators according to the specified parameters
|
||
* in the RCC_OscInitTypeDef structure.
|
||
*/
|
||
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSI|RCC_OSCILLATORTYPE_HSE;
|
||
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
|
||
RCC_OscInitStruct.LSIState = RCC_LSI_ON;
|
||
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
|
||
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
|
||
RCC_OscInitStruct.PLL.PLLM = 4;
|
||
RCC_OscInitStruct.PLL.PLLN = 168;
|
||
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
|
||
RCC_OscInitStruct.PLL.PLLQ = 4;
|
||
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
|
||
{
|
||
Error_Handler();
|
||
}
|
||
|
||
/** Initializes the CPU, AHB and APB buses clocks
|
||
*/
|
||
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|
||
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
|
||
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
|
||
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
|
||
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
|
||
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
|
||
|
||
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
|
||
{
|
||
Error_Handler();
|
||
}
|
||
}
|
||
|
||
/* USER CODE BEGIN 4 */
|
||
|
||
/*
|
||
* 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)。
|
||
*/
|
||
/* USER CODE END 4 */
|
||
|
||
/**
|
||
* @brief Period elapsed callback in non blocking mode
|
||
* @note This function is called when TIM7 interrupt took place, inside
|
||
* HAL_TIM_IRQHandler(). It makes a direct call to HAL_IncTick() to increment
|
||
* a global variable "uwTick" used as application time base.
|
||
* @param htim : TIM handle
|
||
* @retval None
|
||
*/
|
||
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
|
||
{
|
||
/* USER CODE BEGIN Callback 0 */
|
||
|
||
/* USER CODE END Callback 0 */
|
||
if (htim->Instance == TIM7)
|
||
{
|
||
HAL_IncTick();
|
||
}
|
||
/* USER CODE BEGIN Callback 1 */
|
||
/* round 8b 取证:TIM7 每 1ms 把"时间源存活性"写进复位不丢的 .noinit 槽
|
||
* (uwTick + 中断计数)。复位后由 task_wd_seg_report() 打 TICKRUN 行,
|
||
* 与"进段打点"时刻对比,判定 TIM7(HAL 时基)是否在冻结同刻停摆
|
||
* (见 docs/子系统设计/看门狗设计说明.md 2026-10-02 变更记录)。
|
||
* ⚠ task_wd_tick_probe() 内不得调用任何 FreeRTOS API:TIM7 在 HAL_Init
|
||
* 阶段即开始中断,远早于调度器启动,此时 ISR 版内核 API 会触发 port.c 的
|
||
* 优先级断言并把系统卡死在本 ISR 内(2026-10-02 台架实测)。
|
||
* 放 USER CODE 区(L2):CubeMX 重生成不丢。 */
|
||
if (htim->Instance == TIM7)
|
||
{
|
||
task_wd_tick_probe();
|
||
}
|
||
|
||
/* 1 kHz 采样节拍:TIM3 更新事件内脉冲 CONVST(仅两次 GPIO 写,亚 µs),
|
||
* 再唤醒 adcTask 消费这一拍(ISR 只发唤醒,计算全留在任务)。
|
||
* 同一个更新事件还经 TRGO 硬件触发 STM32 ADC1 一轮 9 通道转换(DMA 自动搬运)
|
||
* → 两片转换器由同一硬件事件驱动,采样时刻对齐。
|
||
* TIM3 与 BUSY EXTI(EXTI9_5_IRQn) 同为抢占优先级 6 → 互不抢占,
|
||
* 读 RD 时序不会被下一次 CONVST 脉冲打断。详见
|
||
* docs/子系统设计/ADC采样节拍设计说明.md §3.3。放 USER CODE 区(L2)。
|
||
* ⚠ 节拍源必须能触发 ADC1:F4 上 ADC1 的外部触发清单里没有 TIM6/TIM7 的 TRGO
|
||
* (只接 DAC),判据见 docs/子系统设计/ADC采样节拍设计说明.md §3.2。
|
||
* ⚠ 唤醒必须走 adc_tick_irq_notify(),**不要**在这里直接 osThreadFlagsSet:
|
||
* TIM3 在 app_main_init() 就启动,远早于建任务与 osKernelStart,
|
||
* "消费者就绪"门在 adc_task.c 里(同本文件 TIM7 分支 2026-10-02 的实测坑)。 */
|
||
if (htim->Instance == TIM3)
|
||
{
|
||
tpafe5160_start_conv_irq();
|
||
adc_tick_irq_notify();
|
||
}
|
||
/* USER CODE END Callback 1 */
|
||
}
|
||
|
||
/**
|
||
* @brief This function is executed in case of error occurrence.
|
||
* @retval None
|
||
*/
|
||
void Error_Handler(void)
|
||
{
|
||
/* USER CODE BEGIN Error_Handler_Debug */
|
||
/* N-20 统一降级(2026-10-12):旧形态 __disable_irq()+while(1) = 单处外设初始化
|
||
* 失败即整机挂死,只剩 IWDG 兜底且无任何自检日志。现:打 1 条自检日志 +
|
||
* NVIC_SystemReset()(与 4 个 Fault 处理同款自恢复;IWDG 是最后兜底而非唯一
|
||
* 出路;复位归因 SFTRSTF → 开机日志 "software (NVIC_SystemReset)")。
|
||
* ⚠ 不得 __disable_irq():日志必须能走串口。
|
||
* 调用方识别(为何不打地址):本函数无参数、调用点全在 CubeMX 生成区(不可加标签);
|
||
* ⚠ __builtin_return_address(0) **不可用**——AC5 前端接受但编成库函数调用,
|
||
* MicroLIB 无该符号 → 链接错 L6218E(2026-10-12 台架实证,勿再尝试);
|
||
* ARMCC __asm 只有整函数形态且取不到 C 符号、带不动 C 日志(看门狗设计说明 §10.4)。
|
||
* → 子系统定位靠启动日志锚点:失败点在"最后一条成功日志"与 main() 的
|
||
* MX_*_Init 顺序(GPIO→DMA→USART1→SPI2→SPI1→I2C1→UART4→UART5→USART2→
|
||
* USART3→IWDG→ADC1→TIM3→app_main_init)之间。
|
||
* 日志路径内核前安全:调用点全部在 osKernelStart 之前,控制台写者未注册
|
||
* → con_append 走 dbg_console_write_raw(DWT/计数有界轮询,huart1 未就绪
|
||
* 即返回),sys_clock_get_str 未初始化时打占位串——全程无内核 API。
|
||
* 日志成本:仅这 1 条(≈90 B Flash;行缓冲已在本文件 PM 区收到 128 B)。
|
||
* 重生成存活:函数体在 USER CODE Error_Handler_Debug 区(L2,KeepUserCode
|
||
* 逐字保留);另登记 check_cubemx_survival.py 审计项兜底(L3)。 */
|
||
DBG_ERROR("boot init failure, resetting (locate MX_*_Init via boot order)");
|
||
NVIC_SystemReset();
|
||
/* USER CODE END Error_Handler_Debug */
|
||
}
|
||
#ifdef USE_FULL_ASSERT
|
||
/**
|
||
* @brief Reports the name of the source file and the source line number
|
||
* where the assert_param error has occurred.
|
||
* @param file: pointer to the source file name
|
||
* @param line: assert_param error line source number
|
||
* @retval None
|
||
*/
|
||
void assert_failed(uint8_t *file, uint32_t line)
|
||
{
|
||
/* USER CODE BEGIN 6 */
|
||
/* User can add his own implementation to report the file name and line number,
|
||
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
|
||
/* USER CODE END 6 */
|
||
}
|
||
#endif /* USE_FULL_ASSERT */
|