1634088bd7
屏板链路设计说明.md -> 屏板链路设计.md(去档名「说明」,与 ADC 节拍 / 运行与自检记录两篇口径一致);控制台单写者日志设计说明.md 同改。 两档并存期间旧名引用 0 残留。 - 屏板链路 §3.2 台架日志口(RS232#1 / CN1 端子 / COM3)移入控制台档 §3.4:日志口的接线属 UART5 控制台通道,不属屏板链路;屏板档只留 「别去 CN1 找 USART1 端子」这句接线防错提示。 - 屏板链路 §7.1 由「根因账本 + 修复前后对比」改写为现状描述:延迟 应答机制表 + 耗时分解表 + 两个计数口径 + 已知边界,40 行压到 24 行。 顺带修正 §8 的错引:原指 `网络自愈架构设计.md` §8(实为「延后项」, 不含 uiTask 内容)改为 §4.1 `net_reconcile_tick`。 - §9.1 增补长稳行(串口日志 2026-11-13 18:31 -> 2026-11-15 21:43, 单次上电 51.2 h:rx=658262 ok=658262、crc/len/fmt/err 全 0、 drop=7 仅启动期、kerr/abor/bad=0、全程 off=0);§9.2 销项三条 (仪器项、2 h 长稳、len=7 观察项),新增「按键通路」一条——51.2 h 日志 keys=0 kq=0 dfok=0 dfold=0,延迟应答在真机上从未执行过。 - 删除 sb_frame_b_build_logo() 及 SB_LOGO_MAGIC_* / SB_SCREEN_W/H 与对应单测(TC-SBPROTO-022/023 全组、024c/024d),组帧入口只剩 sb_frame_b_build_image()。sb_proto 单测 59 -> 45 条,45/45 通过。 - cfg_file.c 出厂模板三条中文字面量改 \uXXXX 转义:消除全量重编时的 4 条 #870-D invalid multibyte character sequence,运行期字节不变, 源码回到纯 ASCII(符合「字符串字面量必须纯 ASCII」)。 验证:keil-build 0 Error 0 Warning;sb_proto 45/45、sb_link 164/164; pre-commit 48 钩子全 Passed;check_doc_links --sections-strict 通过。
444 lines
18 KiB
C
444 lines
18 KiB
C
/*
|
||
* 模块名称:屏板链路协议层(sb_proto)主机侧单元测试
|
||
* 模块功能:在 PC 上对 Drivers/BSP/ScreenLink/sb_proto.c 做离线验证。
|
||
*
|
||
* 被测对象:真实源文件(以 #include 原样纳入,保证测的是生产代码而非副本):
|
||
* ../../../Drivers/BSP/ScreenLink/sb_proto.c
|
||
* 本层为**纯逻辑**(只依赖 stdint.h 与 string.h),故**无需任何桩**。
|
||
*
|
||
* 为什么要主机侧单测(而不是只用台架):
|
||
* 1. 协议层只有两种结局:屏板认 / 屏板不认。屏板不认时它的超时判定只看「收没收到一整包」、
|
||
* 不看 CRC,现场只表现为**画面不刷新**——在台架上这条现象与波特率错、时序错、线接反
|
||
* 完全同形,无法归因。主机侧把 CRC 与字节序钉死,才谈得上"发出去的就是对的"。
|
||
* 2. 帧 A 的坏帧分支(长度不对 / 地址不对 / 功能码不对 / CRC 错)在台架上要造噪声才可能出现,
|
||
* 且一出现就被复位冲掉;主机侧是几行断言。
|
||
* 3. 帧 B 4208 B 的边界(填充段参与 CRC、整帧重写)不占 MCU 的 RAM,毫秒级。
|
||
*
|
||
* 覆盖点(对应 docs/产品需求/产品需求设计文档.md §5.16.2 帧结构与任务规划 1-1 判据):
|
||
* TC-SBPROTO-001 CRC-16/MODBUS 教科书校验值:"123456789" -> 0x4B37
|
||
* TC-SBPROTO-002 CRC 与 Modbus RTU 教科书帧一致:01 03 00 00 00 0A -> 0xCDC5(线上 C5 CD)
|
||
* TC-SBPROTO-003 查表法 == 逐位法(300 组伪随机长度/内容,钉住表被改错)
|
||
* TC-SBPROTO-004 查表法边界:len 个字节与逐字节累积等价(前 1 字节 / 全 0 / 全 FF)
|
||
* TC-SBPROTO-010 帧 A 实测向量:屏板发的周期召唤 01 03 01 00 F0 48 -> RT_CMD / key 0
|
||
* TC-SBPROTO-011 帧 A 实测向量:按键召唤 01 03 00 09 31 DE -> KEY_CMD / key 9
|
||
* TC-SBPROTO-012 帧 A 的 key:RT 命令下非 0 也按 0 收(不为此丢一帧)
|
||
* TC-SBPROTO-013 帧 A 坏帧:CRC 错 / 长度 5 与 7 / 地址 0x02 / 功能码 0x04 / 命令类型 0x02
|
||
* TC-SBPROTO-014 帧 A 参数守卫:空指针两路;失败时不动 *p_out
|
||
* TC-SBPROTO-020 帧 B 普通图:点阵逐字节、掩码小端、info[4..7] 为 0、填充段全 0、CRC 自校验
|
||
* TC-SBPROTO-021 帧 B 普通图:整帧重写(预填 0xAA 的缓冲被清干净)、重复组帧结果一致
|
||
* TC-SBPROTO-024 帧 B 参数守卫:空指针两处
|
||
* TC-SBPROTO-030 帧 B 自校验:改点阵 / 改填充段 / 改 CRC 自身 / 长度不对
|
||
* TC-SBPROTO-040 屏板侧视角:用屏板 Check_CRC 的口径(高低互换后比对)复核本模块组的两种帧
|
||
*
|
||
* 通过:全部 TC PASS(main 返回 0)。
|
||
* 入口:int main(void),返回 0=PASS / 1=FAIL(由 run 脚本判读)。
|
||
*
|
||
* 创建日期:2026-10-29
|
||
*/
|
||
|
||
#include <stdint.h>
|
||
#include <stdio.h>
|
||
#include <string.h>
|
||
|
||
/* ===================== 断言 ===================== */
|
||
static int s_total = 0, s_passed = 0, s_failed = 0;
|
||
|
||
#define CHECK(cond, name) do { \
|
||
s_total++; \
|
||
if (cond) { s_passed++; printf(" [PASS] %s\n", name); } \
|
||
else { s_failed++; printf(" [FAIL] %s (line %d)\n", name, __LINE__); } \
|
||
} while (0)
|
||
|
||
/* ===================== 被测代码 ===================== */
|
||
/* 原样纳入生产源文件(纯逻辑,无需桩) */
|
||
#include "sb_proto.c"
|
||
|
||
/* ===================== 工具 ===================== */
|
||
|
||
/* 伪随机(自持实现,不依赖 rand() 的实现差异,换编译器也复现同一组向量) */
|
||
static uint32_t s_rng = 0x20261029U;
|
||
|
||
static uint32_t rng_next(void)
|
||
{
|
||
s_rng = (s_rng * 1103515245U) + 12345U;
|
||
return (s_rng >> 8) & 0x00FFFFFFU;
|
||
}
|
||
|
||
/* 逐位法 CRC-16/MODBUS:与被测查表法互为独立实现,用来抓表项被改错 */
|
||
static uint16_t crc16_bitwise(const uint8_t *p, uint16_t len)
|
||
{
|
||
uint16_t crc = 0xFFFFU;
|
||
uint16_t i;
|
||
uint8_t b;
|
||
|
||
for (i = 0U; i < len; i++)
|
||
{
|
||
crc ^= (uint16_t)p[i];
|
||
for (b = 0U; b < 8U; b++)
|
||
{
|
||
if ((crc & 1U) != 0U)
|
||
{
|
||
crc = (uint16_t)((crc >> 1) ^ 0xA001U);
|
||
}
|
||
else
|
||
{
|
||
crc = (uint16_t)(crc >> 1);
|
||
}
|
||
}
|
||
}
|
||
return crc;
|
||
}
|
||
|
||
/*
|
||
* 屏板 Check_CRC() 的等价物(照屏板固件 Middlewares/Modbus/CRC16.c 的口径):
|
||
* 屏板的 Calculate_CRC() 返回的是高低字节互换后的「线上字」,所以它把帧尾两字节按
|
||
* 「前一个是高」拼起来再比。本函数只用 sb_crc16 的标准值复刻这一口径,
|
||
* 用来回答"屏板会不会认这一帧"。
|
||
*/
|
||
static int screen_board_accepts(const uint8_t *buf, uint16_t len)
|
||
{
|
||
uint16_t wire;
|
||
uint16_t crc;
|
||
|
||
if (len < 2U)
|
||
{
|
||
return 0;
|
||
}
|
||
wire = (uint16_t)(((uint16_t)buf[len - 2U] << 8) | (uint16_t)buf[len - 1U]);
|
||
crc = sb_crc16(buf, (uint16_t)(len - 2U));
|
||
/* 标准值高低互换 == 屏板 Calculate_CRC() 的返回值 */
|
||
return ((uint16_t)((crc << 8) | (crc >> 8)) == wire) ? 1 : 0;
|
||
}
|
||
|
||
/* 按给定 4 字节组一帧帧 A(用生产代码自己的 CRC,再按线上顺序摆) */
|
||
static void make_frame_a(uint8_t *p_out, uint8_t addr, uint8_t func, uint8_t cmd, uint8_t key)
|
||
{
|
||
uint16_t crc;
|
||
|
||
p_out[0] = addr;
|
||
p_out[1] = func;
|
||
p_out[2] = cmd;
|
||
p_out[3] = key;
|
||
crc = sb_crc16(p_out, (uint16_t)SB_FRAME_A_CRC_OFF);
|
||
p_out[4] = (uint8_t)(crc & 0xFFU);
|
||
p_out[5] = (uint8_t)((crc >> 8) & 0xFFU);
|
||
}
|
||
|
||
static uint8_t s_frame[SB_FRAME_B_LEN]; /* 4208 B,帧 B 输出缓冲 */
|
||
static uint8_t s_bmp[SB_FRAME_B_BMP_LEN]; /* 3200 B,点阵源 */
|
||
|
||
static void fill_bmp_pattern(void)
|
||
{
|
||
uint16_t i;
|
||
|
||
for (i = 0U; i < SB_FRAME_B_BMP_LEN; i++)
|
||
{
|
||
s_bmp[i] = (uint8_t)((i * 37U) & 0xFFU);
|
||
}
|
||
}
|
||
|
||
/* ===================== 用例 ===================== */
|
||
|
||
static void tc_crc_vectors(void)
|
||
{
|
||
static const uint8_t k_check[] = { '1', '2', '3', '4', '5', '6', '7', '8', '9' };
|
||
static const uint8_t k_modbus[] = { 0x01, 0x03, 0x00, 0x00, 0x00, 0x0A };
|
||
uint16_t crc;
|
||
|
||
crc = sb_crc16(k_check, (uint16_t)sizeof(k_check));
|
||
CHECK(crc == 0x4B37U, "TC-SBPROTO-001 CRC-16/MODBUS check value of '123456789' is 0x4B37");
|
||
|
||
crc = sb_crc16(k_modbus, (uint16_t)sizeof(k_modbus));
|
||
CHECK(crc == 0xCDC5U,
|
||
"TC-SBPROTO-002 the textbook Modbus RTU frame yields 0xCDC5, so the wire bytes are C5 CD (low first)");
|
||
}
|
||
|
||
static void tc_crc_table(void)
|
||
{
|
||
uint8_t buf[64];
|
||
uint16_t n;
|
||
uint16_t i;
|
||
int mismatch = 0;
|
||
|
||
for (n = 0U; n < 300U; n++)
|
||
{
|
||
uint16_t len = (uint16_t)(rng_next() % 65U);
|
||
|
||
for (i = 0U; i < len; i++)
|
||
{
|
||
buf[i] = (uint8_t)(rng_next() & 0xFFU);
|
||
}
|
||
if (sb_crc16(buf, len) != crc16_bitwise(buf, len))
|
||
{
|
||
mismatch++;
|
||
}
|
||
}
|
||
CHECK(mismatch == 0,
|
||
"TC-SBPROTO-003 the table walk agrees with the bitwise reference on 300 pseudo-random buffers");
|
||
|
||
buf[0] = 0x00U;
|
||
CHECK(sb_crc16(buf, 1U) == crc16_bitwise(buf, 1U),
|
||
"TC-SBPROTO-004 a single 0x00 byte: table and bitwise agree (initial value is really 0xFFFF)");
|
||
|
||
for (i = 0U; i < 32U; i++)
|
||
{
|
||
buf[i] = 0xFFU;
|
||
}
|
||
CHECK(sb_crc16(buf, 32U) == crc16_bitwise(buf, 32U),
|
||
"TC-SBPROTO-004b 32 bytes of 0xFF: table and bitwise agree");
|
||
}
|
||
|
||
static void tc_frame_a_vectors(void)
|
||
{
|
||
/* 屏板 RS485_RefreshScreenCMD() 在 700000 8N1 上真实发出去的字节:
|
||
* 周期召唤(type=RT_CMD=1, key=0)与按键召唤(type=KEY_CMD=0, key=9)。
|
||
* 这两条是拿屏板自己的算法算出来的线上字节,**不是**本模块算的。 */
|
||
static const uint8_t k_rt[] = { 0x01, 0x03, 0x01, 0x00, 0xF0, 0x48 };
|
||
static const uint8_t k_key[] = { 0x01, 0x03, 0x00, 0x09, 0x31, 0xDE };
|
||
sb_frame_a_t out;
|
||
|
||
memset(&out, 0, sizeof(out));
|
||
CHECK(sb_frame_a_parse(k_rt, (uint16_t)sizeof(k_rt), &out) == BSP_OK,
|
||
"TC-SBPROTO-010 the screen board periodic summon 01 03 01 00 F0 48 parses");
|
||
CHECK((out.cmd == SB_CMD_RT) && (out.key == 0U),
|
||
"TC-SBPROTO-010b it reports RT_CMD with key 0");
|
||
|
||
memset(&out, 0, sizeof(out));
|
||
CHECK(sb_frame_a_parse(k_key, (uint16_t)sizeof(k_key), &out) == BSP_OK,
|
||
"TC-SBPROTO-011 the screen board key summon 01 03 00 09 31 DE parses");
|
||
CHECK((out.cmd == SB_CMD_KEY) && (out.key == 9U),
|
||
"TC-SBPROTO-011b it reports KEY_CMD with key 9");
|
||
|
||
/* 屏板会认本模块自己组出来的这两帧吗(同一口径反查) */
|
||
CHECK(screen_board_accepts(k_rt, (uint16_t)sizeof(k_rt)) == 1,
|
||
"TC-SBPROTO-011c the board side accepts the periodic summon frame");
|
||
CHECK(screen_board_accepts(k_key, (uint16_t)sizeof(k_key)) == 1,
|
||
"TC-SBPROTO-011d the board side accepts the key summon frame");
|
||
}
|
||
|
||
static void tc_frame_a_lenient_key(void)
|
||
{
|
||
uint8_t buf[SB_FRAME_A_LEN];
|
||
sb_frame_a_t out;
|
||
|
||
/* 屏板周期召唤本应带 key = 0;真带了非 0 也收下(丢一帧就等于丢一次刷新) */
|
||
make_frame_a(buf, SB_ADDR, SB_FUNC_READ_HOLDING, SB_CMD_RT, 0x2AU);
|
||
memset(&out, 0, sizeof(out));
|
||
CHECK(sb_frame_a_parse(buf, (uint16_t)SB_FRAME_A_LEN, &out) == BSP_OK,
|
||
"TC-SBPROTO-012 an RT frame carrying a non-zero key is still accepted");
|
||
CHECK(out.key == 0U, "TC-SBPROTO-012b its key is normalised to 0 (the key field only means something for KEY_CMD)");
|
||
|
||
/* KEY_CMD 的 key 原样透出,包括 0 与 0xFF */
|
||
make_frame_a(buf, SB_ADDR, SB_FUNC_READ_HOLDING, SB_CMD_KEY, 0x00U);
|
||
CHECK(sb_frame_a_parse(buf, (uint16_t)SB_FRAME_A_LEN, &out) == BSP_OK && out.key == 0x00U,
|
||
"TC-SBPROTO-012c a KEY_CMD with key 0 is accepted and keeps 0");
|
||
make_frame_a(buf, SB_ADDR, SB_FUNC_READ_HOLDING, SB_CMD_KEY, 0xFFU);
|
||
CHECK(sb_frame_a_parse(buf, (uint16_t)SB_FRAME_A_LEN, &out) == BSP_OK && out.key == 0xFFU,
|
||
"TC-SBPROTO-012d a KEY_CMD with key 0xFF is accepted and keeps 0xFF");
|
||
}
|
||
|
||
static void tc_frame_a_reject(void)
|
||
{
|
||
uint8_t buf[SB_FRAME_A_LEN];
|
||
sb_frame_a_t out;
|
||
sb_frame_a_t sentinel;
|
||
|
||
/* CRC 错:改 CRC 低字节 / 改数据字节 */
|
||
make_frame_a(buf, SB_ADDR, SB_FUNC_READ_HOLDING, SB_CMD_RT, 0U);
|
||
buf[4] ^= 0x01U;
|
||
CHECK(sb_frame_a_parse(buf, (uint16_t)SB_FRAME_A_LEN, &out) == BSP_ERR_CRC,
|
||
"TC-SBPROTO-013 a flipped CRC byte is reported as CRC error");
|
||
make_frame_a(buf, SB_ADDR, SB_FUNC_READ_HOLDING, SB_CMD_RT, 0U);
|
||
buf[2] ^= 0x02U;
|
||
CHECK(sb_frame_a_parse(buf, (uint16_t)SB_FRAME_A_LEN, &out) == BSP_ERR_CRC,
|
||
"TC-SBPROTO-013b a flipped payload byte is reported as CRC error");
|
||
|
||
/* 长度不对 */
|
||
make_frame_a(buf, SB_ADDR, SB_FUNC_READ_HOLDING, SB_CMD_RT, 0U);
|
||
CHECK(sb_frame_a_parse(buf, (uint16_t)(SB_FRAME_A_LEN - 1U), &out) == BSP_ERR_PARAM,
|
||
"TC-SBPROTO-013c a 5-byte frame is rejected");
|
||
CHECK(sb_frame_a_parse(buf, (uint16_t)(SB_FRAME_A_LEN + 1U), &out) == BSP_ERR_PARAM,
|
||
"TC-SBPROTO-013d a 7-byte frame is rejected (the idle interrupt may hand over a longer burst)");
|
||
|
||
/* 地址 / 功能码 / 命令类型不符(CRC 都是对的,纯粹是内容不对) */
|
||
make_frame_a(buf, 0x02U, SB_FUNC_READ_HOLDING, SB_CMD_RT, 0U);
|
||
CHECK(sb_frame_a_parse(buf, (uint16_t)SB_FRAME_A_LEN, &out) == BSP_ERR_PARAM,
|
||
"TC-SBPROTO-013e slave address 0x02 is rejected even with a good CRC");
|
||
make_frame_a(buf, SB_ADDR, 0x04U, SB_CMD_RT, 0U);
|
||
CHECK(sb_frame_a_parse(buf, (uint16_t)SB_FRAME_A_LEN, &out) == BSP_ERR_PARAM,
|
||
"TC-SBPROTO-013f function code 0x04 is rejected even with a good CRC");
|
||
make_frame_a(buf, SB_ADDR, SB_FUNC_READ_HOLDING, 0x02U, 0U);
|
||
CHECK(sb_frame_a_parse(buf, (uint16_t)SB_FRAME_A_LEN, &out) == BSP_ERR_PARAM,
|
||
"TC-SBPROTO-013g command type 0x02 is rejected even with a good CRC");
|
||
|
||
/* 失败时不动 *p_out */
|
||
sentinel.cmd = 0x5AU;
|
||
sentinel.key = 0xA5U;
|
||
out = sentinel;
|
||
make_frame_a(buf, 0x02U, SB_FUNC_READ_HOLDING, SB_CMD_RT, 0U);
|
||
(void)sb_frame_a_parse(buf, (uint16_t)SB_FRAME_A_LEN, &out);
|
||
CHECK((out.cmd == 0x5AU) && (out.key == 0xA5U),
|
||
"TC-SBPROTO-014 a failed parse leaves the caller result struct untouched");
|
||
}
|
||
|
||
static void tc_frame_a_guard(void)
|
||
{
|
||
uint8_t buf[SB_FRAME_A_LEN];
|
||
sb_frame_a_t out;
|
||
|
||
make_frame_a(buf, SB_ADDR, SB_FUNC_READ_HOLDING, SB_CMD_RT, 0U);
|
||
CHECK(sb_frame_a_parse(0, (uint16_t)SB_FRAME_A_LEN, &out) == BSP_ERR_PARAM,
|
||
"TC-SBPROTO-014b a null buffer is rejected");
|
||
CHECK(sb_frame_a_parse(buf, (uint16_t)SB_FRAME_A_LEN, 0) == BSP_ERR_PARAM,
|
||
"TC-SBPROTO-014c a null result pointer is rejected");
|
||
}
|
||
|
||
static void tc_frame_b_image(void)
|
||
{
|
||
uint16_t i;
|
||
uint16_t crc;
|
||
int bmp_ok = 1;
|
||
int pad_ok = 1;
|
||
|
||
fill_bmp_pattern();
|
||
CHECK(sb_frame_b_build_image(s_frame, s_bmp, 0xDEADBEEFU) == BSP_OK,
|
||
"TC-SBPROTO-020 building an image frame succeeds");
|
||
|
||
for (i = 0U; i < SB_FRAME_B_BMP_LEN; i++)
|
||
{
|
||
if (s_frame[i] != s_bmp[i])
|
||
{
|
||
bmp_ok = 0;
|
||
}
|
||
}
|
||
CHECK(bmp_ok == 1, "TC-SBPROTO-020b the 3200-byte bitmap lands at offset 0 unchanged");
|
||
|
||
CHECK((s_frame[SB_FRAME_B_INFO_OFF + 0U] == 0xEFU) &&
|
||
(s_frame[SB_FRAME_B_INFO_OFF + 1U] == 0xBEU) &&
|
||
(s_frame[SB_FRAME_B_INFO_OFF + 2U] == 0xADU) &&
|
||
(s_frame[SB_FRAME_B_INFO_OFF + 3U] == 0xDEU),
|
||
"TC-SBPROTO-020c the LED mask goes into info[0..3] little-endian (info[0] = bit0..7)");
|
||
|
||
CHECK((s_frame[SB_FRAME_B_INFO_OFF + 4U] == 0U) && (s_frame[SB_FRAME_B_INFO_OFF + 5U] == 0U) &&
|
||
(s_frame[SB_FRAME_B_INFO_OFF + 6U] == 0U) && (s_frame[SB_FRAME_B_INFO_OFF + 7U] == 0U),
|
||
"TC-SBPROTO-020d info[4..7] stay 0 for a normal picture (reserved bytes)");
|
||
|
||
for (i = SB_FRAME_B_PAD_OFF; i < SB_FRAME_B_CRC_OFF; i++)
|
||
{
|
||
if (s_frame[i] != 0U)
|
||
{
|
||
pad_ok = 0;
|
||
}
|
||
}
|
||
CHECK(pad_ok == 1, "TC-SBPROTO-020e the 998-byte padding block is all zero");
|
||
|
||
crc = sb_crc16(s_frame, (uint16_t)SB_FRAME_B_CRC_OFF);
|
||
CHECK((s_frame[SB_FRAME_B_CRC_OFF] == (uint8_t)(crc & 0xFFU)) &&
|
||
(s_frame[SB_FRAME_B_CRC_OFF + 1U] == (uint8_t)((crc >> 8) & 0xFFU)),
|
||
"TC-SBPROTO-020f the CRC sits at 4206/4207 low byte first");
|
||
CHECK(sb_frame_b_verify(s_frame, (uint16_t)SB_FRAME_B_LEN) == BSP_OK,
|
||
"TC-SBPROTO-020g the built frame verifies");
|
||
CHECK(screen_board_accepts(s_frame, (uint16_t)SB_FRAME_B_LEN) == 1,
|
||
"TC-SBPROTO-040 the board side Check_CRC accepts the image frame");
|
||
}
|
||
|
||
static void tc_frame_b_rewrite(void)
|
||
{
|
||
static uint8_t first[SB_FRAME_B_LEN];
|
||
uint16_t i;
|
||
int pad_ok = 1;
|
||
|
||
/* 预填脏数据:整帧重写必须把填充段与 info 一起清干净 */
|
||
memset(s_frame, 0xAA, sizeof(s_frame));
|
||
fill_bmp_pattern();
|
||
CHECK(sb_frame_b_build_image(s_frame, s_bmp, 0x00000001U) == BSP_OK,
|
||
"TC-SBPROTO-021 building over a dirty buffer succeeds");
|
||
for (i = SB_FRAME_B_PAD_OFF; i < SB_FRAME_B_CRC_OFF; i++)
|
||
{
|
||
if (s_frame[i] != 0U)
|
||
{
|
||
pad_ok = 0;
|
||
}
|
||
}
|
||
CHECK(pad_ok == 1, "TC-SBPROTO-021b a 0xAA-pre-filled buffer is fully rewritten (padding zeroed)");
|
||
|
||
memcpy(first, s_frame, sizeof(first));
|
||
CHECK(sb_frame_b_build_image(s_frame, s_bmp, 0x00000001U) == BSP_OK,
|
||
"TC-SBPROTO-021c building the same content twice succeeds");
|
||
CHECK(memcmp(first, s_frame, sizeof(first)) == 0,
|
||
"TC-SBPROTO-021d the same inputs give a byte-identical frame (idempotent)");
|
||
}
|
||
|
||
|
||
|
||
static void tc_frame_b_guard(void)
|
||
{
|
||
fill_bmp_pattern();
|
||
|
||
CHECK(sb_frame_b_build_image(0, s_bmp, 0U) == BSP_ERR_PARAM,
|
||
"TC-SBPROTO-024 a null frame buffer is rejected (image)");
|
||
CHECK(sb_frame_b_build_image(s_frame, 0, 0U) == BSP_ERR_PARAM,
|
||
"TC-SBPROTO-024b a null bitmap is rejected (image)");
|
||
CHECK(sb_frame_b_verify(0, (uint16_t)SB_FRAME_B_LEN) == BSP_ERR_PARAM,
|
||
"TC-SBPROTO-024e a null frame is rejected by verify");
|
||
}
|
||
|
||
static void tc_frame_b_verify(void)
|
||
{
|
||
fill_bmp_pattern();
|
||
(void)sb_frame_b_build_image(s_frame, s_bmp, 0x00000000U);
|
||
|
||
s_frame[100] ^= 0x01U;
|
||
CHECK(sb_frame_b_verify(s_frame, (uint16_t)SB_FRAME_B_LEN) == BSP_ERR_CRC,
|
||
"TC-SBPROTO-030 a flipped bitmap byte is caught");
|
||
s_frame[100] ^= 0x01U;
|
||
CHECK(sb_frame_b_verify(s_frame, (uint16_t)SB_FRAME_B_LEN) == BSP_OK,
|
||
"TC-SBPROTO-030b flipping it back restores the verdict");
|
||
|
||
/* 填充段屏板不读,但它参与 CRC:改了必须报错,否则"填充随便填"会悄悄破坏帧 */
|
||
s_frame[SB_FRAME_B_PAD_OFF + 500U] = 0x01U;
|
||
CHECK(sb_frame_b_verify(s_frame, (uint16_t)SB_FRAME_B_LEN) == BSP_ERR_CRC,
|
||
"TC-SBPROTO-030c a modified padding byte is caught (the padding really is inside the CRC range)");
|
||
s_frame[SB_FRAME_B_PAD_OFF + 500U] = 0x00U;
|
||
|
||
s_frame[SB_FRAME_B_CRC_OFF] ^= 0xFFU;
|
||
CHECK(sb_frame_b_verify(s_frame, (uint16_t)SB_FRAME_B_LEN) == BSP_ERR_CRC,
|
||
"TC-SBPROTO-030d a corrupted CRC byte is caught");
|
||
|
||
(void)sb_frame_b_build_image(s_frame, s_bmp, 0U);
|
||
CHECK(sb_frame_b_verify(s_frame, (uint16_t)(SB_FRAME_B_LEN - 1U)) == BSP_ERR_PARAM,
|
||
"TC-SBPROTO-030e a length other than 4208 is rejected");
|
||
}
|
||
|
||
int main(void)
|
||
{
|
||
printf("=== ScreenLink Proto Tests ===\n");
|
||
|
||
printf("-- CRC-16/MODBUS check values --\n");
|
||
tc_crc_vectors();
|
||
printf("-- CRC table vs bitwise reference --\n");
|
||
tc_crc_table();
|
||
|
||
printf("-- frame A: the board's real summon bytes --\n");
|
||
tc_frame_a_vectors();
|
||
printf("-- frame A: key handling --\n");
|
||
tc_frame_a_lenient_key();
|
||
printf("-- frame A: rejection paths --\n");
|
||
tc_frame_a_reject();
|
||
printf("-- frame A: argument guards --\n");
|
||
tc_frame_a_guard();
|
||
|
||
printf("-- frame B: image (bitmap + LED mask) --\n");
|
||
tc_frame_b_image();
|
||
printf("-- frame B: full-frame rewrite / idempotence --\n");
|
||
tc_frame_b_rewrite();
|
||
printf("-- frame B: argument guards --\n");
|
||
tc_frame_b_guard();
|
||
printf("-- frame B: self-verification --\n");
|
||
tc_frame_b_verify();
|
||
|
||
printf("\n=== ScreenLink Proto Tests: %d/%d PASSED (failed=%d) ===\n",
|
||
s_passed, s_total, s_failed);
|
||
return (s_failed == 0) ? 0 : 1;
|
||
}
|