/* * 模块名称:UI 数据快照存储(ui_data) * 模块功能:三通道快照(实时量 rt / 参数 par / 装置信息 info)的存储、发布盖章与 * 只读访问——本模块是纯内存叶子:不取数、不格式化、不判断业务含义、 * 不碰 LVGL、不 include 任何 App 头(职责边界与依赖方向 = * Drivers/BSP/UI/ui_data.h 文件头与 * docs/UI设计/UI_数据供给设计说明.md §3.2) * 并发模型:单写者同上下文——唯一发布者 App/app/ui_data_feed.c 与全部页面钩子都在 * uiTask 内串行执行,故无锁、无双缓冲、无 seqlock;将来真出现第二个 * 发布者,保护收在本模块内部(双槽 + 每槽 seq + 单字节活动下标,照 * App/algo/phasor.c 双区先例),页面代码不受影响 * (判据 docs/UI设计/UI_数据供给设计说明.md §5.1) * 适用平台:STM32F407ZGTx / PC 模拟器(两端共用) * 作者:王建锋 * 创建日期:2026-10-25 * 修改记录: * v1.0 2026-10-25 王建锋 创建(道E task-14) */ /* 头文件包含区 */ #include #include "ui_data.h" /* 静态变量定义区 */ /* 三通道存储(上电全零 = "尚无首帧",与 ui_data_init() 后状态一致) */ static ui_data_rt_t s_rt; static ui_data_par_t s_par; static ui_data_info_t s_info; /* * 发布计数:seq 由本层盖章,与入帧携带的 seq 无关——入帧的该字段被覆盖。 * 读侧"有无首帧"的判据是计数为 0,不是帧内 valid:valid 是"这一帧有没有有效 * 数据"(写侧可能发布一帧全无效的真值),两者语义不同,混用会让"首帧之前" * 与"首帧但无数据"在页面上显示成同一种态。 */ static uint32_t s_rt_cnt; static uint32_t s_par_cnt; static uint32_t s_info_cnt; /* 函数定义区 */ /* * 函数功能:快照层初始化——三通道清零(valid=0、seq=0),页面即按"无首帧"渲染 * 入口参数:无 * 返回值:无 * 限定条件:内核启动前可调(纯内存操作,零 OS 依赖);可重复调用(幂等) * 函数说明:静态存储上电本已清零,本函数的意义是运行期复位(例如"恢复出厂后 * 重开 UI"这类后续路径),以及让宿主侧测试能随时造出"首帧之前"的状态 */ void ui_data_init(void) { (void)memset(&s_rt, 0, sizeof(s_rt)); (void)memset(&s_par, 0, sizeof(s_par)); (void)memset(&s_info, 0, sizeof(s_info)); s_rt_cnt = 0u; s_par_cnt = 0u; s_info_cnt = 0u; } /* * 函数功能:发布一帧实时量快照(覆盖存量并给 seq 盖章 +1) * 入口参数:a_snap - 源帧(本层整帧拷入自有存储,调用方可复用其栈/静态空间) * const ui_data_rt_t * * 返回值:0 - 已发布;-1 - 参数无效(NULL,存量不动) int * 限定条件:唯一发布者 = App/app/ui_data_feed.c,uiTask 上下文 * 函数说明:seq 由本层盖章(入帧 seq 被忽略);其余字段原样入存,本层不做任何 * 业务判断与格式化 */ int ui_data_publish_rt(const ui_data_rt_t *a_snap) { if (a_snap == NULL) { return -1; } (void)memcpy(&s_rt, a_snap, sizeof(s_rt)); s_rt_cnt += 1u; s_rt.seq = s_rt_cnt; return 0; } /* * 函数功能:发布一帧参数快照(覆盖存量并给 seq 盖章 +1) * 入口参数:a_snap - 源帧 const ui_data_par_t * * 返回值:0 - 已发布;-1 - 参数无效(NULL,存量不动) int * 限定条件:同上;发布节拍 = 定值校验和变化才发 * (docs/UI设计/UI_数据供给设计说明.md §5.2) */ int ui_data_publish_par(const ui_data_par_t *a_snap) { if (a_snap == NULL) { return -1; } (void)memcpy(&s_par, a_snap, sizeof(s_par)); s_par_cnt += 1u; s_par.seq = s_par_cnt; return 0; } /* * 函数功能:发布一帧装置信息快照(覆盖存量并给 seq 盖章 +1) * 入口参数:a_snap - 源帧 const ui_data_info_t * * 返回值:0 - 已发布;-1 - 参数无效(NULL,存量不动) int * 限定条件:同上 */ int ui_data_publish_info(const ui_data_info_t *a_snap) { if (a_snap == NULL) { return -1; } (void)memcpy(&s_info, a_snap, sizeof(s_info)); s_info_cnt += 1u; s_info.seq = s_info_cnt; return 0; } /* * 函数功能:读最新一帧实时量快照(整帧拷给页面私有缓存) * 入口参数:a_out - 输出缓冲(非 NULL) ui_data_rt_t * * 返回值:0 - 已拷出;-1 - 尚无首帧(输出不动,页面显示 "--"); * -2 - 参数无效(NULL) int * 限定条件:页面钩子上下文(uiTask);勿在逐行 refresh 里无条件整帧拷贝, * 先比 ui_data_rt_seq()(用法见 Drivers/BSP/UI/ui_data.h 文件头) */ int ui_data_rt(ui_data_rt_t *a_out) { if (a_out == NULL) { return -2; } if (s_rt_cnt == 0u) { return -1; } (void)memcpy(a_out, &s_rt, sizeof(s_rt)); return 0; } /* * 函数功能:读最新一帧参数快照 * 入口参数:a_out - 输出缓冲(非 NULL) ui_data_par_t * * 返回值:0 - 已拷出;-1 - 尚无首帧(输出不动);-2 - 参数无效 int * 限定条件:同上;定值页同样用 ui_data_par_seq() 门控页面私有缓存 */ int ui_data_par(ui_data_par_t *a_out) { if (a_out == NULL) { return -2; } if (s_par_cnt == 0u) { return -1; } (void)memcpy(a_out, &s_par, sizeof(s_par)); return 0; } /* * 函数功能:读最新一帧装置信息快照 * 入口参数:a_out - 输出缓冲(非 NULL) ui_data_info_t * * 返回值:0 - 已拷出;-1 - 尚无首帧(输出不动);-2 - 参数无效 int * 限定条件:同上 */ int ui_data_info(ui_data_info_t *a_out) { if (a_out == NULL) { return -2; } if (s_info_cnt == 0u) { return -1; } (void)memcpy(a_out, &s_info, sizeof(s_info)); return 0; } /* * 函数功能:查实时帧发布序号(页面缓存失效判据) * 入口参数:无 * 返回值:当前 seq;从未发布 = 0 uint32_t * 限定条件:无(只读单字,任意上下文安全) * 函数说明:轻量查询——只读一个 uint32,可逐行调用;序号只在发布时 +1,页面 * 据此把"整帧拷贝"从每帧每行降到只在换帧时一次 */ uint32_t ui_data_rt_seq(void) { return s_rt.seq; } /* * 函数功能:查参数帧发布序号 * 入口参数:无 * 返回值:当前 seq;从未发布 = 0 uint32_t * 限定条件:无 */ uint32_t ui_data_par_seq(void) { return s_par.seq; } /* * 函数功能:查装置信息帧发布序号 * 入口参数:无 * 返回值:当前 seq;从未发布 = 0 uint32_t * 限定条件:无 */ uint32_t ui_data_info_seq(void) { return s_info.seq; }