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 回填受管区)。
326 lines
20 KiB
C
326 lines
20 KiB
C
/*
|
||
* 模块名称:屏板链路传输层(USART1 中断收发)
|
||
* 模块功能:把主机与屏板之间那条 700 kbaud 链路完整地管起来:
|
||
* - 收:IDLE 空闲中断断帧、6 B 帧 A 校验、错误中断里清 ORE 并重挂接收;
|
||
* - 发:任务侧组一帧 B(4208 B)进静态缓冲,中断侧收到召唤帧**直接点 TX DMA**,
|
||
* TxCplt 里收尾(不排队、不攒批、不在任务里逐字节轮询);
|
||
* 例外(2026-10-30 按键延迟整治):**按键召唤延迟应答**——key≠0 的
|
||
* 召唤到达时当前帧必然还是"按键前"的内容(键还在中断队列里没被画),
|
||
* 立刻发出去等于让屏板白等一轮;改为扣住这一答,等新帧组好(任务侧
|
||
* 同拍完成)再由 1 kHz 节拍钩子在中断里放行,超时则发旧帧保底
|
||
* - 按键上行:中断只入队,注键由任务侧取走(**注键不能在中断里做**);
|
||
* - 链路监视:连续 >=10 s 无召唤帧才判离线;CRC 错帧静默丢弃并计数。
|
||
* 协议字节级编解码(CRC / 帧 A 解析 / 帧 B 组帧)在 sb_proto,本层只做搬运与时序。
|
||
* 适用平台:STM32F4 系列(依赖 HAL UART 的 ReceiveToIdle_IT 与 Transmit_DMA)
|
||
* 作者:王建锋
|
||
* 创建日期:2026-10-29
|
||
* 修改记录:
|
||
* 2026-10-29 王建锋 创建初始版本(700 kbaud + 收中断)
|
||
* 2026-10-29 王建锋 增加:帧 B 组帧入口、中断起发、TxCplt 收尾、按键队列、离线判定
|
||
* 2026-10-30 王建锋 按键延迟整治:按键召唤**延迟应答**——收到
|
||
* key≠0 的帧不再立刻把"按键前的旧帧"发走,而是扣住等
|
||
* 任务侧把按键画进新帧(上限 SB_KEY_RESP_MAX_MS),
|
||
* 一召唤一答与"起发必须在 IDLE 中断"两条约束都不变;
|
||
* sb_link_ms_tick() 挂在 1 kHz 采样节拍上驱动扣件超时
|
||
*
|
||
* 为什么单独一层、不并进 sb_proto:
|
||
* sb_proto 是纯逻辑(CRC / 组帧 / 解析),能在 test/host 里原样 include 跑单测;
|
||
* 本文件依赖 HAL 句柄与中断上下文,属 MCU 专用。两者分开,前者才留得住"零桩单测"。
|
||
*
|
||
* 全局回调的落点(改之前先读):
|
||
* HAL 的 HAL_UARTEx_RxEventCallback / HAL_UART_ErrorCallback / HAL_UART_TxCpltCallback
|
||
* 是**全局单例**,本工程唯一实现处是 Drivers/BSP/RS485/rs485.c
|
||
* (USE_HAL_UART_REGISTER_CALLBACKS = 0,没法按句柄注册回调)。那里只做一行转发,
|
||
* **按句柄过滤在本文件里做**——于是 rs485.c 不必知道屏板链路用的是哪个 huart,
|
||
* 新增通道也不用改它的判据。
|
||
*/
|
||
|
||
#ifndef SB_LINK_H
|
||
#define SB_LINK_H
|
||
|
||
#ifdef __cplusplus
|
||
extern "C" {
|
||
#endif
|
||
|
||
#include <stdint.h>
|
||
|
||
#include "main.h" /* UART_HandleTypeDef / USART_TypeDef / HAL_StatusTypeDef */
|
||
#include "bsp_err.h" /* bsp_err_t / BSP_OK / BSP_ERR_*:对外只报统一错误码 */
|
||
#include "sb_proto.h" /* SB_FRAME_A_LEN / sb_frame_a_t / sb_frame_a_parse */
|
||
|
||
/* ============================================================================
|
||
* 一、链路参数
|
||
* ==========================================================================*/
|
||
|
||
/*
|
||
* 串口参数:700000 8N1、16 倍过采样。
|
||
* 实际写入发生在 Src/usart.c 的 `USER CODE BEGIN USART1_Init 2`(CubeMX 的 .ioc
|
||
* **不保存任何 UART 的波特率**,GUI 里改也不落盘,所以只能在 USER CODE 区覆盖)。
|
||
* 理论值:APB2 84 MHz / (16 x 700 kHz) = 7.5 → BRR = 0x0078,误差 0 %。
|
||
*/
|
||
#define SB_LINK_BAUD (700000U)
|
||
|
||
/*
|
||
* 接收缓冲:16 B 足够。
|
||
* 屏板只发 6 B 的帧 A(周期召唤 / 按键召唤),16 B 留出一次噪声冲击的余量。
|
||
* ⚠ 不要照抄屏板侧的 4208 B——那是帧 B 的长度,方向相反(主机发、屏板收),
|
||
* 放进接收侧白占 SRAM(判据见 docs/子系统设计/屏板链路设计说明.md §2)。
|
||
*/
|
||
#define SB_LINK_RX_BUF_SIZE (16U)
|
||
|
||
/*
|
||
* 统计上报周期(ms)。只在计数有变化时才打一行,链路空闲不刷屏;
|
||
* 台架验收靠的就是这一行里的累计值(判据见 docs/子系统设计/屏板链路设计说明.md §9)。
|
||
*/
|
||
#define SB_LINK_REPORT_MS (5000U)
|
||
|
||
/*
|
||
* 判「屏板离线」的静默时长(ms)。**不能按 500 ms 判**:屏板固件约 5000 s 会自复位一次,
|
||
* 复位期间约 1 s 不召唤;再叠上主机的重挂/噪声恢复,短窗口会把"屏板活着但歇了口气"
|
||
* 误判成离线,然后在下一次召唤时又"恢复"——现场看到的是离线标志反复跳。
|
||
* 10 s 的取法:比 1 s 自复位空窗大一个数量级,又远小于"人发现屏板黑屏"的时间。
|
||
* 出处 docs/子系统设计/屏板链路设计说明.md §2 链路监视。
|
||
*/
|
||
#define SB_LINK_OFFLINE_MS (10000U)
|
||
|
||
/*
|
||
* 按键队列深度(帧 → 注键的交接)。**实际可存 LEN-1 = 3 个**(环形队列留一格判满)。
|
||
* 中断侧只入队、任务侧出队,两侧都是单写单读;3 个够——屏板 7 个物理键,
|
||
* 人手连按的最快间隔也远大于 uiTask 的 10 ms 节拍。满了丢最**新**的一帧并计数
|
||
* (丢新的比丢旧的好:旧键已经在路上,新键按一下还会有下一次召唤)。
|
||
*/
|
||
#define SB_LINK_KEY_QUEUE_LEN (4U)
|
||
|
||
/* 帧 B 的线上长度(= SB_FRAME_B_LEN)。单独起个名字是为了让调用方按"链路帧"理解,
|
||
* 不必知道协议层的命名;两者恒等由下面一行保证。 */
|
||
#define SB_LINK_FRAME_LEN SB_FRAME_B_LEN
|
||
|
||
/*
|
||
* 按键召唤延迟应答的上限(ms,2026-10-30 按键延迟整治的核心参数)。
|
||
*
|
||
* 为什么要有这个窗:按键召唤到达的那一刻,键还躺在中断队列里没被画——立刻应答
|
||
* 发出去的是**按键前**的旧帧,屏板白等一轮,新画面要等下一次召唤(平均 ~140 ms、
|
||
* 最坏 280 ms)才带得出来。现在把按键召唤扣住:任务侧同拍完成"出键→即派即绘→
|
||
* 组帧"后交回凭据(sb_link_key_rendered),1 kHz 节拍钩子 ≤1 ms 内放行新帧。
|
||
* 实测路径预算:召唤落点→下一拍 ≤10 + 渲染地板 ≤62 + 组帧 ≤1 ≈ **73 ms**,
|
||
* 留了将近一倍余量给"召唤恰好落在数据拍/重绘中段"这类不利相位。
|
||
*
|
||
* 为什么是 140 而不是更大:扣住的窗从召唤时刻起算,到点必须发(旧帧)保"一召唤
|
||
* 一答";140 + 线上 59.6 ≈ 199.6 < **205**(屏板放弃计时——它带键重问的最早时刻),
|
||
* 超时答案也赶在下一次召唤之前发完,不与任何重问对撞。再大就要撞上 205 线。
|
||
* 出处:docs/子系统设计/屏板链路设计说明.md §7.1(205/280 两线)与本函数说明。
|
||
*/
|
||
#define SB_KEY_RESP_MAX_MS (140U)
|
||
|
||
/* ============================================================================
|
||
* 二、链路统计
|
||
* ==========================================================================*/
|
||
|
||
/*
|
||
* 全部字段都是**累计值**,由中断侧自增、任务侧只读(读出来的是一份快照)。
|
||
* 分门别类记而不只记"错了多少次",是因为台架上这几类错指向完全不同的原因:
|
||
* · rx_crc_err —— 波特率/接线问题(帧收到了但字节错位),最常见的真故障;
|
||
* · rx_len_err —— 断帧判据或噪声冲击(收到的不是 6 B),也可能是一次噪声把两帧粘在一起;
|
||
* · rx_fmt_err —— 地址/功能码/命令字不符(帧完整但内容不是给本机的);
|
||
* · err_irq —— ORE/NE/FE/PE 触发过错误中断,即线上真的有噪声或溢出。
|
||
*/
|
||
typedef struct
|
||
{
|
||
uint32_t rx_events; /* IDLE 事件总数(每来一包 +1,含错帧) */
|
||
uint32_t rx_ok; /* 帧 A 校验通过(含 cmd = 按键帧) */
|
||
uint32_t rx_crc_err; /* CRC 不符 */
|
||
uint32_t rx_len_err; /* 长度不是 SB_FRAME_A_LEN */
|
||
uint32_t rx_fmt_err; /* 地址/功能码/命令字不符 */
|
||
uint32_t key_events; /* 其中 cmd == SB_CMD_KEY 的帧数(按键召唤,按键队列的消费源) */
|
||
uint32_t err_irq; /* 错误中断次数(ORE/NE/FE/PE) */
|
||
uint32_t err_bits; /* 错误中断里见过哪些位(HAL_UART_ERROR_* 的并集,见下) */
|
||
uint32_t rearm_fail; /* 重挂接收失败次数(正常应恒 0,非 0 说明链路已静默) */
|
||
uint16_t last_size; /* 最近一次 IDLE 事件的字节数 */
|
||
uint8_t last_cmd; /* 最近一帧有效帧的命令类型(SB_CMD_*) */
|
||
uint8_t last_key; /* 最近一帧有效帧的键值(无键为 0) */
|
||
|
||
/* ---- 发送侧(帧 B 组帧 + TX DMA 起发 + 收尾) ---- */
|
||
uint32_t tx_kick; /* 起发成功次数(每次有效召唤帧 +1) */
|
||
uint32_t tx_done; /* TxCplt 次数(链路正常时应恒等于 tx_kick) */
|
||
uint32_t tx_busy_drop; /* 召唤到了但上一帧还在发 → 本次不答(屏板 280 ms 后会再问) */
|
||
uint32_t tx_kick_fail; /* HAL_UART_Transmit_DMA 返回非 OK */
|
||
uint32_t tx_abort; /* 错误中断发现 TX 已不在发送态 → 主动清 busy(防残留) */
|
||
uint32_t frame_build; /* 组帧次数(只有内容/掩码变化才 +1) */
|
||
uint32_t frame_bad; /* 组完自校验失败次数(正常恒 0) */
|
||
uint32_t frame_us; /* 最近一次组帧耗时(µs,DWT 实测) */
|
||
uint32_t frame_us_max; /* 组帧耗时峰值(µs),预算 ≤1 ms(docs/子系统设计/屏板链路设计说明.md §7) */
|
||
|
||
/* ---- 按键上行与链路监视 ---- */
|
||
uint32_t key_drop; /* 按键队列满导致的丢弃次数 */
|
||
/* ---- 2026-10-30 按键延迟整治:延迟应答的两类放行 ---- */
|
||
uint32_t defer_fresh; /* 等到新画面后放行(理想路径;正常按键应几乎全落这里) */
|
||
uint32_t defer_old; /* 扣到 SB_KEY_RESP_MAX_MS 仍未见新画面 → 发旧帧保底
|
||
* (uiTask 被饿住的直接证据;持续非 0 要先查任务卡点) */
|
||
uint8_t offline; /* 1 = 连续 >= SB_LINK_OFFLINE_MS 无有效召唤帧 */
|
||
} sb_link_stats_t;
|
||
|
||
/*
|
||
* err_bits 的位定义 = HAL 的 HAL_UART_ERROR_* 原值(PE 0x01 / NE 0x02 / FE 0x04 / ORE 0x08)。
|
||
* 直接照抄 HAL 的值而不是自己另编一套:这一列的唯一用途是**判断物理层哪里不对**,
|
||
* 与 HAL 手册对照着看最省事。经验判读:
|
||
* · 只有 ORE(0x08) → 处理不过来(本链路 6 B/280 ms 不该出现,出现了就是重挂漏了);
|
||
* · NE(0x02)为主 → 线上有毛刺(线太长、无屏蔽、共地不良);
|
||
* · FE(0x04)为主 → 波特率对不上(对端不是 700000,或分频误差过大);
|
||
* · 全是 NE/FE 但 rx_ok 照样涨 → 数据没坏、只是采样点被判噪声,属"能用的告警",别当故障。
|
||
*/
|
||
|
||
/* ============================================================================
|
||
* 三、公开接口
|
||
* ==========================================================================*/
|
||
|
||
/*
|
||
* 函数功能:初始化屏板链路并挂上第一轮接收
|
||
* 入口参数:p_huart - 已由 CubeMX 初始化完成的 USART1 句柄(非空)
|
||
* 返回值:BSP_OK 已挂上接收,后续每包都会进 sb_link_on_rx_event
|
||
* BSP_ERR_PARAM 句柄为空
|
||
* BSP_ERR_STATE 句柄的波特率不是 SB_LINK_BAUD(Src/usart.c 的 USER CODE 覆盖丢了)
|
||
* BSP_ERR_BUSY 首次挂接收失败(HAL 侧忙,通常是上一轮没收完)
|
||
* BSP_ERR_IO 首次挂接收失败(HAL 侧状态错)
|
||
* 限定条件:须在 osKernelStart 之前或任一任务上下文调用;本函数不清任何外设。
|
||
* 函数说明:波特率**不由本函数设置**——那一步在 Src/usart.c 的 USER CODE 区
|
||
* (CubeMX 重生成只保护那里)。这里只做一次交叉核对:波特率对不上就报
|
||
* BSP_ERR_STATE 并打一行 ERR,免得"115200 收 700 kbaud"这种静默错配
|
||
* 被误判成接线或协议问题。
|
||
*/
|
||
bsp_err_t sb_link_init(UART_HandleTypeDef *p_huart);
|
||
|
||
/*
|
||
* 函数功能:重新挂上一轮 IDLE 中断接收
|
||
* 入口参数:无
|
||
* 返回值:BSP_OK 挂上了
|
||
* BSP_ERR_STATE 尚未 sb_link_init
|
||
* BSP_ERR_BUSY HAL 侧忙(上一轮接收未结束)
|
||
* BSP_ERR_IO HAL 侧状态错
|
||
* 限定条件:可在中断上下文调用(rx / error 两个回调都靠它续命);不可阻塞。
|
||
* 函数说明:HAL 的 ReceiveToIdle_IT 是**一次性**的:一次 IDLE 事件处理完就把
|
||
* RxState 打回 READY 并关掉 RXNEIE/IDLEIE。所以每个回调末尾都必须重挂,
|
||
* 漏一次链路就永久静默(表象与"屏板没上电"一模一样)。
|
||
*/
|
||
bsp_err_t sb_link_receive_start(void);
|
||
|
||
/*
|
||
* 函数功能:IDLE 事件入口(由 HAL_UARTEx_RxEventCallback 转发进来)
|
||
* 入口参数:p_huart - 触发事件的句柄(非本链路的会被直接忽略)
|
||
* size - 本次断出来的字节数
|
||
* 返回值:无
|
||
* 限定条件:**中断上下文**,只做 CRC 与计数(4 B 校验约 1 µs),不得打日志、不得阻塞。
|
||
* 函数说明:长度不是 6 就直接记 rx_len_err 并丢弃;是 6 才解析。周期召唤解析通过后
|
||
* **直接点 TX DMA** 把已经组好的帧 B 发出去(起发必须在中断里,
|
||
* 不能在任务里轮询——屏板按"总线空闲 1 个字符"断帧,一次几十 µs 的任务
|
||
* 抢占就能把帧切断,而屏板不报超时)。**按键召唤(cmd=KEY 且 key≠0)
|
||
* 例外:只入队、扣住不应答**(延迟应答,见 SB_KEY_RESP_MAX_MS 的账),
|
||
* 由 sb_link_ms_tick() 在新帧就绪或超时时放行——这一答必须走同一个
|
||
* 中断起发通道,绝不因为"等画面"而退化成任务态发送。
|
||
* 业务动作仍然不做:按键只入队,注键由任务侧 sb_link_key_take() 取走后做。
|
||
* 末尾无条件重挂接收。
|
||
*/
|
||
void sb_link_on_rx_event(UART_HandleTypeDef *p_huart, uint16_t size);
|
||
|
||
/*
|
||
* 函数功能:错误事件入口(由 HAL_UART_ErrorCallback 转发进来)
|
||
* 入口参数:p_huart - 触发事件的句柄(非本链路的会被直接忽略)
|
||
* 返回值:无
|
||
* 限定条件:**中断上下文**;不得打日志、不得阻塞。
|
||
* 函数说明:先读 SR 再读 DR 清掉 ORE/NE/FE/PE(RM0090 §30.6.1 的清除序列),再重挂接收。
|
||
* ⚠ 这一步不是可选的:HAL 在阻塞型错误里已经把 RxState 打回 READY 并关了
|
||
* 接收中断,**而它自己不清 ORE**。ORE 一旦锁存,接收就再也进不来 →
|
||
* 一次噪声就把链路永久打死,而现象与"屏板没上电"完全一样。
|
||
*/
|
||
void sb_link_on_error(UART_HandleTypeDef *p_huart);
|
||
|
||
/*
|
||
* 函数功能:链路统计上报(任务侧周期调用)
|
||
* 入口参数:now_ms - 当前毫秒时基(调用方给,便于主机单测;MCU 侧传 HAL_GetTick())
|
||
* 返回值:无
|
||
* 限定条件:任务上下文调用,**不可在中断里调**(内部会打日志)。
|
||
* 函数说明:每 SB_LINK_REPORT_MS 检查一次,只有计数有变化才打一行累计值。
|
||
* 第一帧成功收到时立刻打一行(不等周期),台架刚上电就能看到链路通了。
|
||
* 同时维护"屏板离线"标志:连续 >= SB_LINK_OFFLINE_MS 没有有效召唤帧才置 1,
|
||
* 恢复时清 0 并打一行。判定只在这里做(任务侧),中断侧只自增计数。
|
||
*/
|
||
void sb_link_poll(uint32_t now_ms);
|
||
|
||
/*
|
||
* 函数功能:组一帧帧 B(任务侧,内容/掩码变化时才真组)
|
||
* 入口参数:p_live_bmp - 当前 1bpp 显存首地址(>= SB_FRAME_B_BMP_LEN 字节,非空)
|
||
* led_mask - 状态灯 32 位掩码(小端填进 info[0..3];位的语义见
|
||
* App/algo/sb_led_map.h——位变了帧就重组,灯随下一轮召唤出去)
|
||
* 返回值:BSP_OK 帧已就绪(本次没变,或已重新组好)
|
||
* BSP_ERR_PARAM p_live_bmp 为空
|
||
* BSP_ERR_STATE 尚未 sb_link_init
|
||
* BSP_ERR_BUSY TX DMA 正在发这一帧,本拍跳过(TxCplt 之后的下一拍会补上)
|
||
* BSP_ERR_CRC 组完自校验失败(已计数 frame_bad,帧不会发出去)
|
||
* 限定条件:**任务上下文**,且必须与 ui_port_step() 同一个任务(显存是单写者)。
|
||
* 变化帧实测 ≈0.5 ms(典型)/ 0.8 ms(峰值),没变只付 memcmp(≈20 µs);
|
||
* 判据口径见 docs/子系统设计/屏板链路设计说明.md §7。
|
||
* 函数说明:1. **不持有显存副本**——直接拿 p_live_bmp 与帧缓冲里的点阵段 memcmp,
|
||
* 没变就立刻返回(约 20 µs)。省掉 3200 B 影子缓冲,也省掉每拍重算 CRC。
|
||
* 2. **TX 占用期间绝不写帧缓冲**(那会让 DMA 发出半新半旧的"脏帧"):
|
||
* 忙就直接返回 BSP_ERR_BUSY,下一拍再来——屏板 280 ms 一轮,丢一帧无害。
|
||
* 3. 组帧后立刻 sb_frame_b_verify() 自校验;不通过就不置 ready,帧不会被发出去。
|
||
*/
|
||
bsp_err_t sb_link_frame_update(const uint8_t *p_live_bmp, uint32_t led_mask);
|
||
|
||
/*
|
||
* 函数功能:取走一个待注的屏板按键(任务侧)
|
||
* 入口参数:无
|
||
* 返回值:屏板 key 字节(1~10,语义见 docs/子系统设计/屏板链路设计说明.md §5);
|
||
* 0 = 队列空(屏板自己的 0 就是"无键",两者同义,不必再分)
|
||
* 限定条件:任务上下文;中断侧在入队,本函数出队。单生产者单消费者,不需要关中断。
|
||
* 函数说明:只吐**屏板原始键号**,不映射 UI_KEY_*——键号是协议语义,UI 键码是 UI 语义,
|
||
* 两者在 uiTask 里汇合(那一层才知道当前是哪个页面在接键)。
|
||
* key == 9/10(RESET/FACTORY)照原样吐出,丢不丢由调用方定(当前口径见
|
||
* docs/子系统设计/屏板链路设计说明.md §5)。
|
||
*/
|
||
uint8_t sb_link_key_take(void);
|
||
|
||
/*
|
||
* 函数功能:交"按键已画进画面、帧与活显存一致"的放行凭据(延迟应答用)
|
||
* 入口参数:无
|
||
* 返回值:无
|
||
* 限定条件:任务上下文(uiTask);本拍 sb_link_frame_update() 返回 BSP_OK 之后才准调
|
||
* 函数说明:按键召唤不再立刻应答(见 sb_link_on_rx_event),TIM3 的 sb_link_ms_tick()
|
||
* 看到本凭据才把"带新画面的那一答"放上线。BUSY 拍次不许交(画面还没进帧)。
|
||
*/
|
||
void sb_link_key_rendered(void);
|
||
|
||
/*
|
||
* 函数功能:按键召唤延迟应答的毫秒时钟(1 kHz 调用)
|
||
* 入口参数:无
|
||
* 返回值:无
|
||
* 限定条件:**中断上下文**(Src/main.c TIM3 分支),不得调内核 API / 打日志
|
||
* 函数说明:有扣件时——凭据已到就起发新帧(dfok+1),扣满 SB_KEY_RESP_MAX_MS
|
||
* 则发当前帧保底(dfold+1)。初始化前调用安全(直接返回)。
|
||
*/
|
||
void sb_link_ms_tick(void);
|
||
|
||
/*
|
||
* 函数功能:TX 完成入口(由 HAL_UART_TxCpltCallback 转发进来)
|
||
* 入口参数:p_huart - 触发回调的句柄(非本链路的会被直接忽略)
|
||
* 返回值:无
|
||
* 限定条件:**中断上下文**(DMA2_Stream7 的 TC 中断里),不得打日志、不得阻塞。
|
||
* 函数说明:只清 busy 标志并计数。**不做组帧**——组帧要 0.17 ms,放进中断会拖累
|
||
* 整个系统的中断响应;有变化就让任务侧下一拍补(frame_update 会看到差异)。
|
||
*/
|
||
void sb_link_on_tx_cplt(UART_HandleTypeDef *p_huart);
|
||
|
||
/*
|
||
* 函数功能:取一份链路统计快照
|
||
* 入口参数:p_out - 输出缓冲(非空)
|
||
* 返回值:无(p_out 为空时直接返回,不改任何东西)
|
||
* 限定条件:任一上下文可调;中断侧在自增,快照不保证同一时刻一致(只用于观测与计数)。
|
||
* 函数说明:逐字段拷出,避免把 volatile 的内部结构整体赋给调用方。
|
||
*/
|
||
void sb_link_get_stats(sb_link_stats_t *p_out);
|
||
|
||
#ifdef __cplusplus
|
||
}
|
||
#endif
|
||
|
||
#endif /* SB_LINK_H */
|