Files
STM32F4-Base/App/algo/sb_led_map.h
T
wandering1 0b84c43d2e feat(屏板): 状态灯位映射 sb_led_map + 104 RUN 观测口(批 3);附 4-1 并发压测脚本
批 3(LOGO 按用户裁定出局,不实现、sb_proto 的 LOGO 组帧挂待删标注):
- 新增 App/algo/sb_led_map.c/h:业务状态 → 屏板 LED 位掩码的唯一映射表
  (纯逻辑,主机单测可跑)。ID1 运行心跳 500ms 半周期闪(与主板 LED 同拍)、
  ID2 保护动作、ID3 104 链路 RUN、ID4 采样电平故障(不选 sticky——灯要能灭)、
  ID5 保护启动 picked;bit0 与 ID 6~19 恒 0。这张表参考源码里没有,属自定,
  出处 docs/产品需求/产品需求设计文档.md §5.16.3 ①。
- iec104_serv 新增 iec104_serv_link_run()(透传 link 状态机,不另存镜像量)。
- ui_task.c 帧组装块每拍现采四路状态源(均为任意上下文可读观测口)折算后
  传 sb_link_frame_update——替换批 2 的恒 0 占位;掩码变了帧就重组,
  复用 busy/dirty 机制,无新通路。
- 主机单测 test/host/sb_led_map 17/17(位语义/恒零位/闪烁相位/tick 回绕/
  NULL 契约);注册豁免走 SIM_EXEMPT(模拟器无屏板串口)。

批 4 的 4-1 双通道并发回归(真屏板在线实测):
- 新增 test/host/remolcd_pull_test.py:RemoDispBus 拉帧压测客户端
  (INIT→LCDMEM 10 Hz、逐帧 XOR+长度校验、往返延迟统计、--dump PBM)。
- 实测:网络侧两轮 2200 拉帧零错零超时(min 35/avg 63/max 131 ms);
  屏板侧 60 s 隔离窗口 rx +233 全收、drop 零新增、bad=0、off=0、无
  OFFLINE——两侧互不拖。观察项 len=7(断帧尺寸≠6,累计 7 次)出现窗口
  无法归因压测、隔离窗口零新增、有效帧零损失,已挂设计说明 §5.7 待查。

附带:sb_link.h frame_update 注释回填实测耗时(509/688 µs 取代旧估算)。
Keil 0 错误 0 警告;ci_local 全绿(SKIP=nested-comment,主树脚本另查 0 新发);
Code=239,064 B / RW_IRAM1 65.5 % / RW_CCM 87.9 %;覆盖率 90.1 % / 44 套件。
2026-10-30 00:39:57 +08:00

102 lines
5.9 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.
/*
* 模块名称:屏板状态灯位映射(业务状态 → LED 位掩码)
* 模块功能:把装置的业务状态(保护动作 / 保护启动 / 采样故障 / 104 主站在线 /
* 运行心跳)折算成屏板帧 B `info[0..3]` 的 32 位 LED 掩码。
* ⚠ 本层**纯逻辑**:不碰 HAL、不读全局、不打日志——输入是调用方
* (uiTask)采好的状态快照,输出是一个 uint32_t → 主机侧可纯逻辑单测
* (test/host/sb_led_map/)。与 Drivers/BSP/ScreenLink/sb_proto 同一分层理由。
* 适用平台:平台无关(纯标准 C)
* 作者:王建锋
* 创建日期:2026-10-29
* 修改记录:
* 2026-10-29 王建锋 创建初始版本(批 3 的 3-1:映射表落纸,docs/任务规划/屏板链路.md §3)
*
* 这张表为什么必须"自定":屏板源码(DTU-LCD LED_Printf)只规定了"位 → 灯号"的
* 物理口径(bit1~bit19 → ID 1~19,ID 0 电源灯硬件常亮、不看位,bit20~31 未使用),
* **"哪路状态占哪一位"在参考主机源码里不存在**(参考的 LCD_LED_Update 实现在
* 未开源的屏板 BSP 库里)。出处 docs/产品需求/产品需求设计文档.md §5.16.3 ①。
*/
#ifndef SB_LED_MAP_H
#define SB_LED_MAP_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
/* ============================================================================
* 一、位定义(屏板物理口径:bit i = 灯 ID i;bit0 被屏板忽略,别指望用它)
* ==========================================================================*/
/* ID 1 = 运行灯:装置活着的心跳,500 ms 半周期闪烁(与主板 LED1~3 心跳节奏同源,
* docs/产品需求/产品需求设计文档.md §5.8)。它**不取决于任何输入状态**——只要这段
* 代码还在被调度,灯就在闪;灯不闪 = uiTask 停了,这正是操作员要看的信号。 */
#define SB_LED_BIT_RUN (1UL << 1)
/* ID 2 = 告警灯:任一保护元件处于**动作**态(prot_elm_active_mask != 0)。
* ⚠ 产品构建处于「未标定闭锁」(App/task/protect_task.c 的 VALID 门禁),
* 标定前恒灭——与屏上"保护动作 N"同一已知口径,不是本灯的缺陷。 */
#define SB_LED_BIT_ALARM (1UL << 2)
/* ID 3 = 通信灯:IEC 60870-5-104 链路处于 RUN(STARTDT 已确认 = 主站真的在线在
* 轮询)。选 RUN 而不选"TCP 已建连":连上不发 STARTDT 的扫描器不该点亮它。 */
#define SB_LED_BIT_COMM (1UL << 3)
/* ID 4 = 采样故障灯:ADC 采样有效性**电平**故障位非零(adc_health_flags,
* 静默/倒退等,当前活跃、可自愈熄灭)。选电平不选 sticky:这盏灯要能熄灭,
* "曾经故障过"的账在自检记录里,不该用一盏长明灯喊给操作员。 */
#define SB_LED_BIT_ADC_FAULT (1UL << 4)
/* ID 5 = 保护启动灯:任一保护元件处于**启动**(picked)态(prot_elm_pick_mask)。
* picked 早于 asserted,"越限启动了但没到动作点"这段过程只在这盏灯上可见
* (与 104「保护启动」记录同一动机,App/algo/prot_elm.h 的 pick_mask 注释)。
* 同样受未标定闭锁口径约束。 */
#define SB_LED_BIT_PICKUP (1UL << 5)
/* 已赋义位的并集(判据用:掩码里不得出现本表之外的置位)。
* ID 6~19 保留未用,恒 0;将来扩展从 ID 6 顺序取,跳号留白不回收语义。 */
#define SB_LED_BITS_DEFINED (SB_LED_BIT_RUN | SB_LED_BIT_ALARM | SB_LED_BIT_COMM | \
SB_LED_BIT_ADC_FAULT | SB_LED_BIT_PICKUP)
/* 运行灯闪烁半周期(ms):0 ~ SB_LED_BLINK_HALF_MS-1 亮,下一个 500 ms 灭,交替。
* 取 500 是为与主板 LED 心跳(defaultTask 500 ms 翻转)同拍,两套灯闪烁同相。 */
#define SB_LED_BLINK_HALF_MS (500U)
/* ============================================================================
* 二、状态输入快照(调用方每拍现采,本模块不缓存、不加锁)
* ==========================================================================*/
typedef struct
{
uint32_t prot_active; /* prot_elm_active_mask():bit i = 元件 i 动作 */
uint32_t prot_pick; /* prot_elm_pick_mask():bit i = 元件 i 启动 */
uint32_t adc_flags; /* adc_health_flags():ADC_HF_* 电平故障位 */
uint8_t iec104_run; /* 1 = IEC 104 链路 RUN(iec104_serv_link_run()) */
uint8_t pad[3]; /* 显式补齐:结构体大小固定 16 B,主机/目标端一致 */
} sb_led_in_t;
/* ============================================================================
* 三、求值入口
* ==========================================================================*/
/*
* 函数功能:把状态快照折算成屏板 LED 32 位掩码(纯函数,映射表唯一出处见本头 §一)
* 入口参数:p_in - 状态快照;**允许 NULL**:等价于全零输入(只跑运行灯心跳——
* 心跳证明的是"这段代码活着",与别的状态无关,不该被空输入连坐)
* now_ms - 当前毫秒时基(运行灯相位唯一依赖;osKernelGetTickCount 同域)
* 返回值:32 位 LED 掩码(bit i = 灯 ID i;bit0 与 ID 6~19 恒 0,直接进帧 B info[0..3])
* 限定条件:任意上下文(纯计算:无分支副作用、不读写任何静态量、时基回绕安全——
* 回绕瞬间至多把一个半周期截短或延长 1 拍,500 ms 的心跳灯看不出来)
* 函数说明:闪烁相位 = (now_ms / 500) 的奇偶,偶 = 亮。上电 first 500 ms 为亮,
* "刚上电灯就在闪"是最直观的自检信号。各状态位相互独立,任一非零即置位,
* 无优先级、无互斥(告警与通信可以同时亮,那是两件同时在真发生的事)。
*/
uint32_t sb_led_map_eval(const sb_led_in_t *p_in, uint32_t now_ms);
#ifdef __cplusplus
}
#endif
#endif /* SB_LED_MAP_H */