feat(屏板): 帧 B 组帧 + 中断起发 TX DMA + 按键队列 + 离线判定(批 2)
批 2 五项全部落地(docs/任务规划/屏板链路.md §3): - 2-1 sb_link_frame_update():uiTask 渲染后按需组帧,不留 3200 B 影子 副本,直接拿活显存与帧内点阵段 memcmp;组完自校验,过了才置 ready。 静态 4208 B 帧缓冲实测落 RW_IRAM1(0x20006d98),不在 CCM。 - 2-2 空闲中断校完 6 B 召唤帧直接点 TX DMA(sb_tx_kick),不排队不攒批; busy 期间再来的召唤丢帧并计数,绝不叠发(屏板收帧一次 IDLE 一包)。 - 2-3 按键走环形队列(中断入、任务出),uiTask 按 §4.3 表映射 UI_KEY_* 后调 ui_port_queue_key;9/10(RESET/FACTORY)按决策四先丢弃并留痕。 - 2-4 HAL_UART_TxCpltCallback 在 rs485.c 加第三行转发(三个全局回调 落点收一处,防日后重复定义);busy 冻结帧缓冲,错误中断按 gState 收拾被打断的 TX,防残留 busy 把链路永久锁死。 - 2-5 连续 >=10 s 无有效召唤帧才判离线(不按 500 ms——屏板约 5000 s 自复位有 1 s 空窗),恢复即清标志;CRC 错帧静默丢弃并计数。 实测:台架 40 s(PC 顶替屏板,--key-fixed 2 持续改画面)rx=143 ok=143 keys=143 crc=0 / tx=143 done=143 drop=0 abor=0 fb=144 bad=0;脚本一停 10 s 准点出 OFFLINE。组帧耗时 509 µs 典型 / 709 µs 峰值——原 0.3 ms 判据系估算,已按实测在规划文档改口为 1 ms 并给出压降路径(未做)。 附带:rs485_on_rx 补 CODE-RULE-09 文档块(本轮改 rs485.c 触发整文件 重扫被拦);CRC 循环计数改 32 位(消掉 ARMCC 每轮 UXTH);bench 脚本 加 --reply/--key-fixed/--dump,并写明 CH340 收不了 700 kbaud 连续流、 --reply 收不全 4208 B 不是固件问题。 主机单测 146/146 PASSED(+65);Keil 0 错误 0 警告;ci_local 全绿 (SKIP=nested-comment,worktree 限制,主树脚本另查 0 新发)。 Code=238,936 B / RW_IRAM1 65.5 % / RW_CCM 87.9 %。
This commit is contained in:
@@ -34,6 +34,20 @@
|
||||
* TC-SBLINK-013 统计上报的时基回绕:now_ms 跨 0xFFFFFFFF 仍按差值法工作
|
||||
* TC-SBLINK-014 统计快照:空指针守卫、字段逐项对得上
|
||||
*
|
||||
* 覆盖点(对应 docs/任务规划/屏板链路.md §3 批 2 的五项):
|
||||
* TC-SBLINK-016 组帧入口守卫:未初始化 / 空显存指针
|
||||
* TC-SBLINK-017 帧 B 内容:点阵逐字节、info[0..3] 小端掩码、info[4..7] 非 LOGO、
|
||||
* 998 B 填充全 0、CRC 覆盖 [0, 4205](2-1 判据)
|
||||
* TC-SBLINK-018 变化检测:内容不变不重组;点阵改一字节要重组;只改掩码也要重组
|
||||
* TC-SBLINK-019 中断起发:未组帧不发;一召唤一帧 4208 B;TX 期间不叠发(2-2 判据)
|
||||
* TC-SBLINK-020 TX 占用期间帧缓冲逐字节冻结,TxCplt 后补组(2-4 的"无脏帧")
|
||||
* TC-SBLINK-021 起发失败要清 busy,否则链路此后永不发送
|
||||
* TC-SBLINK-022 错误中断里清残留 busy;但 TX 真的还在发时不得判死(2-4 的"无残留 busy")
|
||||
* TC-SBLINK-023 按键队列:单次交付、周期帧不带键、溢出丢新并计数、FIFO 顺序(2-3)
|
||||
* TC-SBLINK-024 离线判定:9.999 s 不判、10 s 判、不重复刷屏、一帧即恢复(2-5 判据)
|
||||
* TC-SBLINK-025 组帧耗时换算:周期数 → µs 的除数(SystemCoreClock/1e6)
|
||||
* TC-SBLINK-026 上报行:fb/tx 列可见;离线行排在周期行之前且不重复
|
||||
*
|
||||
* 通过:全部 TC PASS(main 返回 0)。
|
||||
* 入口:int main(void),返回 0=PASS / 1=FAIL(由 run 脚本判读)。
|
||||
*
|
||||
@@ -83,6 +97,7 @@ static bsp_err_t setup_link(void)
|
||||
s_uart.Init.Mode = 3U;
|
||||
s_uart.Init.OverSampling = 16U;
|
||||
s_uart.RxState = HAL_UART_STATE_READY;
|
||||
s_uart.gState = HAL_UART_STATE_READY;
|
||||
s_uart.ErrorCode = 0U;
|
||||
|
||||
s_other.Instance = &g_sb_uart;
|
||||
@@ -114,6 +129,30 @@ static void make_frame(uint8_t *p_frame, uint8_t addr, uint8_t func, uint8_t cmd
|
||||
p_frame[5] = (uint8_t)((crc >> 8) & 0xFFU);
|
||||
}
|
||||
|
||||
/* 假显存:组帧的输入(内容按 i 变化,方便"改一个字节"的用例) */
|
||||
static uint8_t s_bmp[SB_FRAME_B_BMP_LEN];
|
||||
|
||||
static void fill_bmp(uint8_t seed)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0U; i < SB_FRAME_B_BMP_LEN; i++) {
|
||||
s_bmp[i] = (uint8_t)(seed + (uint8_t)(i & 0xFFU));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 模拟"HAL 的 DMA 传完 → gState 打回 READY → 调 TxCplt 回调"。
|
||||
* 真机这两步在 HAL_DMA_IRQHandler 里连着做(UART_DMATransmitCplt 里先改 gState
|
||||
* 再回调),所以测试也必须按这个顺序——否则"错误中断里靠 gState 判残留 busy"
|
||||
* 那条用例的前提就不成立了。
|
||||
*/
|
||||
static void tx_complete(void)
|
||||
{
|
||||
s_uart.gState = HAL_UART_STATE_READY;
|
||||
sb_link_on_tx_cplt(&s_uart);
|
||||
}
|
||||
|
||||
/* ===================== 用例 ===================== */
|
||||
|
||||
static void tc_init_guards(void)
|
||||
@@ -383,20 +422,24 @@ static void tc_poll_reporting(void)
|
||||
sb_link_poll(2U * SB_LINK_REPORT_MS);
|
||||
CHECK(sb_log_n == 2U, "TC-SBLINK-012g nothing is emitted when no counter changed");
|
||||
|
||||
/* 有错帧:再出一行,且 err/crc 列可见 */
|
||||
/* 有错帧:再出一行,且 crc 列可见。
|
||||
* 同时喂一帧正常召唤:本用例的时基是每步 +5 s,单靠错帧会让"连续 10 s 无**有效**
|
||||
* 召唤帧"在第三步就成立(那是 2-5 的离线判定,由 TC-SBLINK-027 专测),
|
||||
* 那行 OFFLINE 会插进来把行号错开,让本用例悄悄变成在测别的东西。 */
|
||||
{
|
||||
uint8_t bad[SB_FRAME_A_LEN];
|
||||
|
||||
(void)memcpy(bad, k_rt, SB_FRAME_A_LEN);
|
||||
bad[5] ^= 0xFFU;
|
||||
feed(bad, SB_FRAME_A_LEN);
|
||||
feed(k_rt, SB_FRAME_A_LEN);
|
||||
}
|
||||
sb_link_poll(3U * SB_LINK_REPORT_MS);
|
||||
CHECK(sb_log_n == 3U, "TC-SBLINK-012h a CRC error triggers a new report");
|
||||
CHECK(sb_log_has(2U, "crc=1") == 1, "TC-SBLINK-012i the report carries the CRC error count");
|
||||
|
||||
sb_link_get_stats(&st);
|
||||
CHECK(st.rx_events == 3U, "TC-SBLINK-012j (sanity) three events were fed in total");
|
||||
CHECK(st.rx_events == 4U, "TC-SBLINK-012j (sanity) four events were fed in total");
|
||||
}
|
||||
|
||||
static void tc_poll_wraparound(void)
|
||||
@@ -438,6 +481,328 @@ static void tc_stats_snapshot(void)
|
||||
CHECK(st.rearm_fail == 0U, "TC-SBLINK-014e ... rearm_fail");
|
||||
}
|
||||
|
||||
/* ===================== 批 2 用例(帧 B 组帧 / 起发 / 收尾 / 按键 / 离线) ===================== */
|
||||
|
||||
static void tc_frame_update_guards(void)
|
||||
{
|
||||
fill_bmp(0x11U);
|
||||
|
||||
/* 未初始化:STATE。直接清 s_huart(本用例白盒:测试 TU 里 include 了 sb_link.c,
|
||||
* 静态量可见)——否则本用例能否通过就取决于"有没有人先跑过 setup_link",
|
||||
* 那是用例顺序依赖,加一条用例就可能把它弄红。 */
|
||||
s_huart = NULL;
|
||||
sb_fake_reset();
|
||||
sb_log_reset();
|
||||
CHECK(sb_link_frame_update(s_bmp, 0U) == BSP_ERR_STATE,
|
||||
"TC-SBLINK-016 a frame update before init is refused");
|
||||
|
||||
(void)setup_link();
|
||||
CHECK(sb_link_frame_update(NULL, 0U) == BSP_ERR_PARAM,
|
||||
"TC-SBLINK-016b a NULL framebuffer is refused");
|
||||
}
|
||||
|
||||
static void tc_frame_build_content(void)
|
||||
{
|
||||
uint32_t i;
|
||||
uint32_t pad_bad = 0U;
|
||||
sb_link_stats_t st;
|
||||
|
||||
(void)setup_link();
|
||||
fill_bmp(0x11U);
|
||||
|
||||
CHECK(sb_link_frame_update(s_bmp, 0xA5A55A5AU) == BSP_OK,
|
||||
"TC-SBLINK-017 the first frame is built");
|
||||
CHECK(memcmp(s_frame, s_bmp, SB_FRAME_B_BMP_LEN) == 0,
|
||||
"TC-SBLINK-017b ... the bitmap region is a verbatim copy of the live framebuffer");
|
||||
/* 0xA5A55A5A 小端 = 5A 5A A5 A5 */
|
||||
CHECK((s_frame[SB_FRAME_B_INFO_OFF + 0U] == 0x5AU) &&
|
||||
(s_frame[SB_FRAME_B_INFO_OFF + 1U] == 0x5AU) &&
|
||||
(s_frame[SB_FRAME_B_INFO_OFF + 2U] == 0xA5U) &&
|
||||
(s_frame[SB_FRAME_B_INFO_OFF + 3U] == 0xA5U),
|
||||
"TC-SBLINK-017c ... the LED mask lands in info[0..3] little-endian");
|
||||
CHECK((s_frame[SB_FRAME_B_INFO_OFF + 4U] == 0U) &&
|
||||
(s_frame[SB_FRAME_B_INFO_OFF + 5U] == 0U),
|
||||
"TC-SBLINK-017d ... info[4..5] is not the LOGO magic");
|
||||
CHECK((s_frame[SB_FRAME_B_INFO_OFF + 6U] == 0U) &&
|
||||
(s_frame[SB_FRAME_B_INFO_OFF + 7U] == 0U),
|
||||
"TC-SBLINK-017e ... info[6..7] reserved is zero");
|
||||
for (i = SB_FRAME_B_PAD_OFF; i < SB_FRAME_B_CRC_OFF; i++) {
|
||||
if (s_frame[i] != 0U) {
|
||||
pad_bad++;
|
||||
}
|
||||
}
|
||||
CHECK(pad_bad == 0U, "TC-SBLINK-017f ... all 998 padding bytes are zero");
|
||||
CHECK(sb_frame_b_verify(s_frame, (uint16_t)SB_LINK_FRAME_LEN) == BSP_OK,
|
||||
"TC-SBLINK-017g ... and the CRC covers [0, 4205]");
|
||||
|
||||
sb_link_get_stats(&st);
|
||||
CHECK(st.frame_build == 1U, "TC-SBLINK-017h ... counted exactly once");
|
||||
CHECK(st.frame_bad == 0U, "TC-SBLINK-017i ... self-check clean");
|
||||
}
|
||||
|
||||
static void tc_frame_unchanged(void)
|
||||
{
|
||||
sb_link_stats_t st;
|
||||
|
||||
(void)setup_link();
|
||||
fill_bmp(0x11U);
|
||||
(void)sb_link_frame_update(s_bmp, 0U);
|
||||
|
||||
/* 同样的内容再来一次:不重组。uiTask 每 10 ms 调一次,这条决定了常态成本只有 memcmp */
|
||||
CHECK(sb_link_frame_update(s_bmp, 0U) == BSP_OK,
|
||||
"TC-SBLINK-018 an unchanged framebuffer returns OK");
|
||||
sb_link_get_stats(&st);
|
||||
CHECK(st.frame_build == 1U, "TC-SBLINK-018b ... without rebuilding the frame");
|
||||
|
||||
/* 点阵改一个字节:必须重组 */
|
||||
s_bmp[1234] ^= 0xFFU;
|
||||
CHECK(sb_link_frame_update(s_bmp, 0U) == BSP_OK,
|
||||
"TC-SBLINK-018c a changed byte is picked up");
|
||||
sb_link_get_stats(&st);
|
||||
CHECK(st.frame_build == 2U, "TC-SBLINK-018d ... and triggers a rebuild");
|
||||
|
||||
/* 点阵没变、只有掩码变:也要重组(info[0..3] 在 CRC 覆盖区内) */
|
||||
CHECK(sb_link_frame_update(s_bmp, 0x00000001U) == BSP_OK,
|
||||
"TC-SBLINK-018e a changed LED mask is picked up");
|
||||
sb_link_get_stats(&st);
|
||||
CHECK(st.frame_build == 3U, "TC-SBLINK-018f ... and triggers a rebuild too");
|
||||
CHECK(s_frame[SB_FRAME_B_INFO_OFF] == 0x01U,
|
||||
"TC-SBLINK-018g ... with the new mask in info[0]");
|
||||
}
|
||||
|
||||
static void tc_tx_kick(void)
|
||||
{
|
||||
sb_link_stats_t st;
|
||||
|
||||
(void)setup_link();
|
||||
fill_bmp(0x22U);
|
||||
|
||||
/* 还没组帧:召唤来了也不发(发一帧全 0 的图等于让屏板白屏,而且它不报错) */
|
||||
feed(k_rt, SB_FRAME_A_LEN);
|
||||
sb_link_get_stats(&st);
|
||||
CHECK(st.tx_kick == 0U, "TC-SBLINK-019 no frame is sent before the first build");
|
||||
CHECK(st.tx_busy_drop == 1U, "TC-SBLINK-019b ... the summon is counted as dropped instead");
|
||||
|
||||
(void)sb_link_frame_update(s_bmp, 0U);
|
||||
|
||||
feed(k_rt, SB_FRAME_A_LEN);
|
||||
sb_link_get_stats(&st);
|
||||
CHECK(st.tx_kick == 1U, "TC-SBLINK-019c a valid summon kicks the transfer");
|
||||
CHECK(sb_tx_calls == 1U, "TC-SBLINK-019d ... through exactly one HAL call");
|
||||
CHECK(sb_tx_len == SB_LINK_FRAME_LEN, "TC-SBLINK-019e ... of 4208 bytes");
|
||||
CHECK(sb_tx_buf == s_frame,
|
||||
"TC-SBLINK-019f ... from the static frame buffer (the DMA-reachable one)");
|
||||
|
||||
/* 屏板会一直召唤:上一帧没发完就再来一次 → 绝不叠发 */
|
||||
feed(k_rt, SB_FRAME_A_LEN);
|
||||
sb_link_get_stats(&st);
|
||||
CHECK(sb_tx_calls == 1U,
|
||||
"TC-SBLINK-019g a second summon during TX does not stack another transfer");
|
||||
CHECK(st.tx_busy_drop == 2U, "TC-SBLINK-019h ... it is counted as dropped");
|
||||
|
||||
/* TxCplt 之后又能发了 */
|
||||
tx_complete();
|
||||
sb_link_get_stats(&st);
|
||||
CHECK(st.tx_done == 1U, "TC-SBLINK-019i TxCplt is counted");
|
||||
feed(k_rt, SB_FRAME_A_LEN);
|
||||
CHECK(sb_tx_calls == 2U, "TC-SBLINK-019j ... and the next summon goes out again");
|
||||
}
|
||||
|
||||
static void tc_tx_busy_freezes_frame(void)
|
||||
{
|
||||
uint8_t before[SB_FRAME_B_BMP_LEN];
|
||||
sb_link_stats_t st;
|
||||
|
||||
(void)setup_link();
|
||||
fill_bmp(0x33U);
|
||||
(void)sb_link_frame_update(s_bmp, 0U);
|
||||
feed(k_rt, SB_FRAME_A_LEN); /* 起发 → busy */
|
||||
|
||||
(void)memcpy(before, s_frame, sizeof(before));
|
||||
|
||||
fill_bmp(0x77U); /* 画面变了,但正在发 */
|
||||
CHECK(sb_link_frame_update(s_bmp, 0U) == BSP_ERR_BUSY,
|
||||
"TC-SBLINK-020 a frame update during TX is refused");
|
||||
CHECK(memcmp(s_frame, before, sizeof(before)) == 0,
|
||||
"TC-SBLINK-020b ... the buffer DMA is reading is left byte-for-byte untouched");
|
||||
|
||||
tx_complete();
|
||||
CHECK(sb_link_frame_update(s_bmp, 0U) == BSP_OK,
|
||||
"TC-SBLINK-020c ... and the pending change is picked up right after TxCplt");
|
||||
CHECK(memcmp(s_frame, s_bmp, sizeof(before)) == 0,
|
||||
"TC-SBLINK-020d ... so no stale frame survives the busy window");
|
||||
sb_link_get_stats(&st);
|
||||
CHECK(st.frame_build == 2U, "TC-SBLINK-020e ... exactly one rebuild, no double build");
|
||||
}
|
||||
|
||||
static void tc_tx_kick_failure(void)
|
||||
{
|
||||
sb_link_stats_t st;
|
||||
|
||||
(void)setup_link();
|
||||
fill_bmp(0x44U);
|
||||
(void)sb_link_frame_update(s_bmp, 0U);
|
||||
|
||||
sb_tx_force = SB_FAKE_TX_BUSY;
|
||||
feed(k_rt, SB_FRAME_A_LEN);
|
||||
sb_link_get_stats(&st);
|
||||
CHECK(st.tx_kick_fail == 1U, "TC-SBLINK-021 a failed HAL kick is counted");
|
||||
CHECK(st.tx_kick == 0U, "TC-SBLINK-021b ... and is not counted as a transfer");
|
||||
|
||||
/* 失败必须把 busy 清掉,否则此后每次召唤都被判"上一帧还在发",链路再也不发 */
|
||||
sb_tx_force = SB_FAKE_TX_OK;
|
||||
feed(k_rt, SB_FRAME_A_LEN);
|
||||
sb_link_get_stats(&st);
|
||||
CHECK(st.tx_kick == 1U, "TC-SBLINK-021c ... and the next summon retries successfully");
|
||||
}
|
||||
|
||||
static void tc_tx_abort_on_error(void)
|
||||
{
|
||||
sb_link_stats_t st;
|
||||
|
||||
(void)setup_link();
|
||||
fill_bmp(0x55U);
|
||||
(void)sb_link_frame_update(s_bmp, 0U);
|
||||
feed(k_rt, SB_FRAME_A_LEN); /* 起发,gState = BUSY_TX */
|
||||
|
||||
/* 真机:错误中断打断 TX 时 HAL 把 gState 打回 READY,但**不会**再调 TxCplt →
|
||||
* busy 会永久挂着。这正是"拔掉屏板后无残留 busy"要防的那条。 */
|
||||
sb_ore_latched = 1U;
|
||||
s_uart.RxState = HAL_UART_STATE_READY;
|
||||
s_uart.gState = HAL_UART_STATE_READY;
|
||||
s_uart.ErrorCode = HAL_UART_ERROR_ORE;
|
||||
sb_link_on_error(&s_uart);
|
||||
|
||||
sb_link_get_stats(&st);
|
||||
CHECK(st.tx_abort == 1U, "TC-SBLINK-022 a stuck TX busy is cleared by the error callback");
|
||||
|
||||
s_uart.gState = HAL_UART_STATE_READY;
|
||||
feed(k_rt, SB_FRAME_A_LEN);
|
||||
CHECK(sb_tx_calls == 2U, "TC-SBLINK-022b ... so the link keeps transmitting afterwards");
|
||||
|
||||
/* 反例:TX 真的还在发(gState 仍是 BUSY_TX)时不得判死 */
|
||||
sb_ore_latched = 0U;
|
||||
s_uart.RxState = HAL_UART_STATE_BUSY_RX; /* 非阻塞型错误:接收照跑 */
|
||||
s_uart.ErrorCode = HAL_UART_ERROR_NE;
|
||||
sb_link_on_error(&s_uart); /* gState 仍 BUSY_TX */
|
||||
sb_link_get_stats(&st);
|
||||
CHECK(st.tx_abort == 1U, "TC-SBLINK-022c ... but a still-running TX is not declared dead");
|
||||
CHECK(sb_tx_calls == 2U, "TC-SBLINK-022d ... and no extra transfer was started");
|
||||
}
|
||||
|
||||
static void tc_key_queue(void)
|
||||
{
|
||||
uint8_t i;
|
||||
sb_link_stats_t st;
|
||||
|
||||
(void)setup_link();
|
||||
|
||||
CHECK(sb_link_key_take() == 0U, "TC-SBLINK-023 an empty queue yields 0");
|
||||
|
||||
feed(k_key, SB_FRAME_A_LEN); /* 屏板实测向量:key = 9 */
|
||||
CHECK(sb_link_key_take() == 9U, "TC-SBLINK-023b the key byte reaches the consumer");
|
||||
CHECK(sb_link_key_take() == 0U, "TC-SBLINK-023c ... exactly once");
|
||||
|
||||
/* 周期召唤的 key 恒 0:不占队列(屏板自己的语义就是"没有按键事件") */
|
||||
feed(k_rt, SB_FRAME_A_LEN);
|
||||
CHECK(sb_link_key_take() == 0U, "TC-SBLINK-023d a periodic summon never queues a key");
|
||||
|
||||
/* 队列满:丢**新**的并计数(旧键已经在路上,新键还会有下一次召唤)。
|
||||
* 环形队列留一格判满 → 容量 = SB_LINK_KEY_QUEUE_LEN - 1 = 3,推 6 次丢 3 次。 */
|
||||
for (i = 0U; i < (uint8_t)(SB_LINK_KEY_QUEUE_LEN + 2U); i++) {
|
||||
feed(k_key, SB_FRAME_A_LEN);
|
||||
}
|
||||
sb_link_get_stats(&st);
|
||||
CHECK(st.key_drop == 3U, "TC-SBLINK-023e overflow drops the newest and counts it");
|
||||
|
||||
for (i = 0U; i < (uint8_t)(SB_LINK_KEY_QUEUE_LEN - 1U); i++) {
|
||||
CHECK(sb_link_key_take() == 9U, "TC-SBLINK-023f ... FIFO order is preserved");
|
||||
}
|
||||
CHECK(sb_link_key_take() == 0U, "TC-SBLINK-023g ... and the queue is empty again");
|
||||
}
|
||||
|
||||
static void tc_offline(void)
|
||||
{
|
||||
sb_link_stats_t st;
|
||||
|
||||
(void)setup_link();
|
||||
feed(k_rt, SB_FRAME_A_LEN);
|
||||
|
||||
sb_link_poll(0U);
|
||||
sb_link_get_stats(&st);
|
||||
CHECK(st.offline == 0U, "TC-SBLINK-024 a fresh link is online");
|
||||
|
||||
/* 9.999 s 无召唤:还不算离线(屏板自复位约 1 s 空窗,按 500 ms 判会反复误报) */
|
||||
sb_link_poll(SB_LINK_OFFLINE_MS - 1U);
|
||||
sb_link_get_stats(&st);
|
||||
CHECK(st.offline == 0U, "TC-SBLINK-024b just under 10 s of silence is still online");
|
||||
|
||||
/* 满 10 s:置离线,并且只打一行 */
|
||||
sb_log_reset();
|
||||
sb_link_poll(SB_LINK_OFFLINE_MS);
|
||||
sb_link_get_stats(&st);
|
||||
CHECK(st.offline == 1U, "TC-SBLINK-024c 10 s of silence marks the board offline");
|
||||
CHECK(sb_log_has(0U, "OFFLINE") == 1, "TC-SBLINK-024d ... with one dedicated line");
|
||||
|
||||
/* 再久也不重复刷屏 */
|
||||
sb_log_reset();
|
||||
sb_link_poll(SB_LINK_OFFLINE_MS + 1000U);
|
||||
CHECK(sb_log_n == 0U, "TC-SBLINK-024e the offline line is not repeated every poll");
|
||||
|
||||
/* 屏板回来:一帧就够,立刻清标志并报恢复 */
|
||||
sb_log_reset();
|
||||
feed(k_rt, SB_FRAME_A_LEN);
|
||||
sb_link_poll(SB_LINK_OFFLINE_MS + 1010U);
|
||||
sb_link_get_stats(&st);
|
||||
CHECK(st.offline == 0U, "TC-SBLINK-024f a single valid summon brings it back online");
|
||||
CHECK(sb_log_has(0U, "back online") == 1, "TC-SBLINK-024g ... and says so");
|
||||
}
|
||||
|
||||
static void tc_frame_timing(void)
|
||||
{
|
||||
sb_link_stats_t st;
|
||||
|
||||
(void)setup_link();
|
||||
fill_bmp(0x66U);
|
||||
|
||||
/* 每次读 CYCCNT 前进 16800 周期 = 100 µs @168 MHz;组帧读两次 → 差值正好 16800。
|
||||
* 这条钉的是"周期 → µs"的除数(SystemCoreClock/1e6),换算错了会在台架上
|
||||
* 把 0.17 ms 报成 17 ms 或 1.7 µs,两种都会被当成"组帧太慢/没测到"。 */
|
||||
sb_cyccnt_step = 16800U;
|
||||
(void)sb_link_frame_update(s_bmp, 0U);
|
||||
|
||||
sb_link_get_stats(&st);
|
||||
CHECK(st.frame_us == 100U, "TC-SBLINK-025 the cycle count is converted with SystemCoreClock");
|
||||
CHECK(st.frame_us_max == 100U, "TC-SBLINK-025b ... and the peak is tracked");
|
||||
|
||||
s_bmp[0] ^= 0xFFU;
|
||||
sb_cyccnt_step = 50400U; /* 300 µs */
|
||||
(void)sb_link_frame_update(s_bmp, 0U);
|
||||
sb_link_get_stats(&st);
|
||||
CHECK(st.frame_us == 300U, "TC-SBLINK-025c ... the last build duration");
|
||||
CHECK(st.frame_us_max == 300U, "TC-SBLINK-025d ... and the peak follows the slower build");
|
||||
}
|
||||
|
||||
static void tc_tx_report(void)
|
||||
{
|
||||
(void)setup_link();
|
||||
fill_bmp(0x88U);
|
||||
(void)sb_link_frame_update(s_bmp, 0U);
|
||||
|
||||
sb_log_reset();
|
||||
sb_link_poll(SB_LINK_REPORT_MS);
|
||||
CHECK(sb_log_n == 1U, "TC-SBLINK-026 a changed frame counter produces a report");
|
||||
CHECK(sb_log_has(0U, "fb=1") == 1, "TC-SBLINK-026b ... carrying the build count");
|
||||
CHECK(sb_log_has(0U, "tx=0") == 1, "TC-SBLINK-026c ... and the transfer counters");
|
||||
|
||||
/* 这一拍会先出一行 OFFLINE(10 s 无有效召唤,2-5),但不该再出 TX 行。
|
||||
* 断言行数而不是"没看见 tx=",是为了把"离线判定排在周期上报之前"这个顺序也钉住。 */
|
||||
sb_log_reset();
|
||||
sb_link_poll(2U * SB_LINK_REPORT_MS);
|
||||
CHECK(sb_log_n == 1U, "TC-SBLINK-026d only the offline line is emitted, no TX report");
|
||||
CHECK(sb_log_has(0U, "OFFLINE") == 1, "TC-SBLINK-026e ... and it is the offline one");
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
printf("=== ScreenLink Link Tests ===\n");
|
||||
@@ -476,6 +841,29 @@ int main(void)
|
||||
printf("-- stats snapshot --\n");
|
||||
tc_stats_snapshot();
|
||||
|
||||
printf("-- batch 2: frame update guards --\n");
|
||||
tc_frame_update_guards();
|
||||
printf("-- batch 2: frame B content --\n");
|
||||
tc_frame_build_content();
|
||||
printf("-- batch 2: change detection --\n");
|
||||
tc_frame_unchanged();
|
||||
printf("-- batch 2: TX kick from the RX callback --\n");
|
||||
tc_tx_kick();
|
||||
printf("-- batch 2: frame frozen while TX runs --\n");
|
||||
tc_tx_busy_freezes_frame();
|
||||
printf("-- batch 2: TX kick failure --\n");
|
||||
tc_tx_kick_failure();
|
||||
printf("-- batch 2: TX aborted by an error interrupt --\n");
|
||||
tc_tx_abort_on_error();
|
||||
printf("-- batch 2: key queue --\n");
|
||||
tc_key_queue();
|
||||
printf("-- batch 2: offline detection --\n");
|
||||
tc_offline();
|
||||
printf("-- batch 2: frame build timing --\n");
|
||||
tc_frame_timing();
|
||||
printf("-- batch 2: TX report line --\n");
|
||||
tc_tx_report();
|
||||
|
||||
printf("\n=== ScreenLink Link Tests: %d/%d PASSED (failed=%d) ===\n",
|
||||
s_passed, s_total, s_failed);
|
||||
return (s_failed == 0) ? 0 : 1;
|
||||
|
||||
Reference in New Issue
Block a user