7708f12c10
按键召唤答的是按键前的旧帧:uiTask 还没消化键答案就发走了,新画面 只能等下一次周期召唤(280 ms/轮)才带得出——真机反馈 250-450 ms 的 主构成(根因账本 docs/子系统设计/屏板链路设计说明.md §7.1)。 改动(用户批准范围 A+B+C;屏板固件未动,决策三不变): - sb_link:按键召唤改为只入队不起发;uiTask 同拍重绘并组帧成功后交 凭据(sb_link_key_rendered),TIM3 1 kHz 的 sb_link_ms_tick 以 ≤1 ms 放行新帧。140 ms 截止(SB_KEY_RESP_MAX_MS,+59.6 ms 线上 <205 ms 屏板放弃线)保底发旧帧,"一召一答"契约不破;线忙时同旧丢单、 不留扣件。起发两中断源(USART1 IDLE/TIM3)共用 sb_tx_kick, "判忙→占位"在 PRIMASK 临界区原子完成。 - ui_task:排空注键挪到组帧之前,注键→即派即绘→组帧同拍闭环; 组帧返回 BSP_OK 才交重绘凭据(BUSY 拍帧还没反映按键)。 - ui_port:注键单槽改 8 深环(连按不再互相覆盖,满丢最旧+计数); 新增 ui_port_service_keys:同拍 lv_indev_read + lv_refr_now, 省掉 indev ≤20 ms + 刷新 ≤20 ms 两段等待。 - ui_menu_view:菜单移动选中只失效新旧两行带(黄金参考零像素变化), 滚动/异常回退整框失效。 - simulator:窗口循环 sleep 扣除本轮已耗时(原先无条件睡满 lv_timer_handler 返回值,叠加 Windows Sleep 15.6 ms 粒度放大观感)。 - 串口 TX 上报新增 dfok/dfold(凭据放行新帧数/超时保底旧帧数): dfold 连涨 = uiTask 被饿 ≥140 ms 的直接现场证据。 验证:test/host/sb_link 164/164(新增 18 条:不立刻起发/凭据放行/ 140 ms 截止保底/忙时不留扣件/陈旧凭据不放行等边界);菜单 8 状态 + 内容页 66 张黄金像素 PASS;Keil @build 0 错误 0 警告(两次); BOM 99 文件无漂移、代码规范、doc-links 本次文件零问题、cppcheck PASS。 文档:屏板规划文档已被主干收编进设计说明,延迟账本落 docs/子系统设计/屏板链路设计说明.md §7.1,并同步 §4 口径、§8 接口、 §9.1 单测台账 146→164、§10 坑账本补条。 RAM 台账(分支不跑 --write,并行分支提交约定):本笔盖戳行 RW_IRAM1 94,336 B (72.0%) / RW_CCM 57,376 B (87.9%),较上一台账行 +4,400 B——为距上次台账构建以来分支累计差(含批 3 状态灯等未盖戳 提交),非全部本笔引入;本次自身增量按对象计为 ui_port.o +12 B (8 深键环+丢计数+indev 指针)与 sb_link/o 数十字节级。 gate: only mem-budget/mem-numbers pending lead ledger backfill (合并落主干后 Lead 统一 --write 回填受管区)。
328 lines
13 KiB
C
328 lines
13 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 */
|
||
#include "sb_link.h" /* sb_link_ms_tick():TIM3 1 kHz 顺手做按键召唤延迟应答的毫秒时钟 */
|
||
/* 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 /
|
||
* HAL_UART_TxCpltCallback)已收归 rs485.c 实现,本文件不再定义它们。如需处理
|
||
* 接收到的 RS-485 数据,请重定义 rs485.h 中的弱钩子 rs485_on_rx(uint8_t *buf, uint16_t size)。
|
||
* 屏板链路(USART1)在同样的三个回调里转发给 sb_link_on_rx_event / sb_link_on_error /
|
||
* sb_link_on_tx_cplt——那些回调是全局单例,全工程只能有一份定义,**不要在本文件再写一份**。
|
||
*/
|
||
/* 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();
|
||
/* 按键召唤延迟应答的毫秒时钟(2026-10-30 按键延迟整治):只读写几个标志位 +
|
||
* 必要时点一次已经在 IDLE 分支验证过的 DMA 起发,µs 级、无内核 API;
|
||
* sb_link_init 之前 s_huart 为空直接返回——TIM3 比调度器早 ≈100 ms 就开跑。
|
||
* ⚠ 别把它挪去任务里:扣件的放行截止(140 ms)依赖 1 kHz 硬时钟,
|
||
* uiTask 自己被饿的时候恰恰最需要这个时钟还在走。 */
|
||
sb_link_ms_tick();
|
||
}
|
||
/* 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 */
|