0f7b5a87fb
登记簿、文档与代码注释里的存量字母加数字编号(N-x / S-x / T-x / E-x / A-x / 卡号) 一律改写为语义短语,净删 2,028 处引用;删除基线与待确认、并行分道方案、排期详述、 采集保护收尾 4 篇(内容已并入 docs/任务规划/README 与登记簿)。 门禁机器与三张账同批重打:check_ids.py 新增变更记录行豁免与第二根护栏 undecidable_total_ceiling;check_test_traceability.py 只加 stdout/stderr UTF-8 reconfigure(TC_RE 未动);check_cubemx_survival.py 的 --write 固定 newline=LF; id_registry / id_aliases(+19 条 retired-to-phrase)/ id_known_backlog 重打。 test/ 56 个文件同步 TC 标签语义化,其中 18 个 TU 改了 CHECK / printf 里的标签文字, 是本批唯一改变构建产物的文件集合。 为什么合成一笔、不拆四笔(实测撞闸,非偏好):pre-commit 跑钩子前会 stash 未 staged 部分,拆开后中间提交必然是「新规则 + 旧语料」——实跑可判定 85.15% < 基线 86.00%、 不可判定 1,453 > 上限 1,166,编号门禁直接拦下。编号识别面与它描述的语料只能同码移动。 实跑基线(工作区全量):754 文件 / 8,230 处编号字面量 / 可判定 7,079 = 86.01% (基线 86.0%,容差 0.1pp)/ 不可判定绝对数 1,151(上限 1,166)/ 校验 1-6 错误级 0 / 告警 = 悬空 2(App/algo/freq.c 的 S2,属 §4.4 同号不同义)+ 孤儿 50。 基线从 84.0 抬到 86.0 的理由见 docs/门禁说明/id-check.md §7.6:本批真删了 2,028 处 引用,可判定面随真实内容上升,不是把分母做小。同批新增排除 docs/硬件平台/ 语料,使 识别面少 264 处——芯片手册位号与网表料号不属编号空间(registry 有 excluded_paths_note)。 ⚠ 余量只剩 0.11pp(约 10 处)与绝对数 15 处:把「可判定的引用」改成散文会立刻撞第二 根护栏。另遗留一项:id_aliases 里 retired 的 N / S / T / A 别名没有 scope(U1~U5 有), 今后有人新写一个 A-4,校验 3 不会响,只能靠人,建议下批补 scope。 固件不变证据:115 个 .c/.h 剥注释并归一字符串后与 HEAD 逐字节全等,残留代码差异 0; App / Src / Drivers 的字符串字面量零改动。@build 0 错误 0 警告;40/40 主机套件、2,889 断言全通过;覆盖率 line 89.2% / func 92.6%;pre-commit 48 钩子 --all-files 全绿; check_doc_links --all 通过(176 文档,章节引用无法判定 0);逐字节卫生核过:14 个带 BOM 文件与 HEAD 一致、0 个 CR、0 个 U+FFFD,字符串字面量纯 ASCII。 .cubemx_survival_baseline.json 的 watchdog-module 哈希 56bf27 变 2bb051 属合法: Src/freertos.c 的 USER CODE 区内只有注释文字随编号改写,代码未变。 同批带上体积同码凭证与 RAM 台账行(@build 之后盖戳):RW_IRAM1 79,008 B / 60.3%, RW_CCM 57,376 B / 87.9%,阈值 92%。
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():第 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 */
|
||
/* Error_Handler 统一降级(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 */
|
||
/* 第 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 */
|
||
/* 第 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 */
|
||
/* Error_Handler 统一降级(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 */
|