测试日期:2026-08-12
测试分支:
codex/plsr-2026-minimal测试代码基线:
bf314a2343002a3df78bce5049badb29d11eae2ePLSR 实现提交:
c7b851659ea26f9af8563e146bf5332c3c8d6dfd分支起点:
e35595aae18690a46a03445865bd6218a166bd89结论口径:只记录本次 2026 PLSR 最小重写实际执行过的测试
本次重写的 Host 回归、Python 离线自检、IAR 全量构建、固件烧录、真实 Modbus RTU 板测、Y0~Y3 逐路选择、X4/X5 实体回环、硬复位持久化和最终默认状态复核均通过。
| 类别 | 最终结果 | 数量或关键结果 |
|---|---|---|
| C Host 回归 | PASS | 44 个测试组,1,694,007 次断言,0 失败 |
| Python 产品测试脚本离线自检 | PASS | 未打开串口 |
| Python 控制面板离线自检 | PASS | 未打开 GUI 或串口 |
| IAR EWARM 全量构建 | PASS | 0 errors,0 warnings |
| 固件烧录和校验 | PASS | Verification...OK,SHA256 与板测固件一致 |
| Modbus RTU/运动板测 | PASS | 9 组 |
| X4/X5 实体回环板测 | PASS | 9 个用例 |
| 硬复位持久化 | PASS | 配置和位置恢复,运动不自动续跑 |
| 最终默认状态复核 | PASS | 位置、配置、输入和夹具输出均恢复 |
| 逻辑分析仪测试 | 未执行 | 不在本轮验收范围内 |
这里的“Y0~Y3 验证”是同一个逻辑 PLSR 依次选择 Y0、Y1、Y2、Y3 作为脉冲输出,不是四轴同时运动或四轴联动。
1,694,007 assertions 是逐脉冲检查、边界检查和随机算术比对产生的断言总数,不代表 169 万个独立测试场景。
0x1000~0x1197、0x2000~0x2006、0x3000。旧工程曾做过的四轴并发、双 AB、软限位、长稳、USB CDC 并发和逻辑分析仪波形测试属于另一套实现,不能作为本次重写的通过证据,因此没有合并到本报告。
| 项目 | 实际环境或结果 |
|---|---|
| 目标板 | 信捷 XDM-60T4-E,STM32F407IG |
| RTOS | uC/OS-II |
| 串口 | COM5,9600 bit/s,8E1,从站 1 |
| Host 编译器 | GCC 4.9.2 (tdm64-1) |
| Host 编译选项 | C99,-Wall -Wextra -Werror,PLSR_HOST_TEST |
| IAR | EWARM 8.3.4,配置 Modbus |
| IAR 工程 | EWARM/Modbus.ewp |
| HEX | EWARM/Modbus/Exe/Modbus.hex |
| HEX 文本文件大小 | 142,131 B |
| Intel HEX 有效数据 | 50,513 B |
| HEX 地址范围 | 0x08000000..0x0800C550 |
| SHA256 | 97956D9EF30A9B274140B9B82F412EDD77F9637F563FCC3F552BB4B140772343 |
IAR 链接结果:
49,004 bytes of readonly code memory
1,509 bytes of readonly data memory
18,660 bytes of readwrite data memory
Errors: none
Warnings: none
执行命令:
powershell.exe -NoProfile -ExecutionPolicy Bypass `
-File .\tests\plsr_host\run_tests.ps1
最终输出:
SUMMARY tests=44 assertions=1694007 failures=0
44 个测试组全部 PASS:
| # | 测试组 | 实际验证内容 |
|---|---|---|
| 1 | u64_by_u32_division |
用 8 个边界被除数、9 个边界除数和 250,000 组确定性伪随机数据,比对 64 位除 32 位算法与 C 原生除法的商和余数。 |
| 2 | protocol_boundaries |
覆盖区间外、跨区、地址溢出、零长度、空缓冲、只读区、保留字、32 位字段不完整写入、非法值和控制字宽度,并确认失败写入不破坏原值。 |
| 3 | wait_zero_one_ms |
验证 WAIT_TIME=0 按最小 1 ms 处理,先进入 WAITING,1 ms 后才进入下一段。 |
| 4 | act_zero_no_pulse |
验证 ACT_TIME=0 的段不产生脉冲并在下一次 1 ms 调度跳过,后续段正常执行。 |
| 5 | relative_absolute_zero |
验证相对位移 0、相对位移 +5,以及当前位置等于绝对目标时的零位移完成。 |
| 6 | self_jump_stop |
验证单段自跳转可循环运行,并能由 STOP 正常终止。 |
| 7 | stop_deceleration |
验证 STOP 从 1000 Hz 按 1000 ms 减速至 100 Hz 后停止;重复 STOP 不重启减速,最终位置 902。 |
| 8 | stop_pending_boundary |
验证最后脉冲已完成但 Poll 尚未处理时收到 STOP,最终为 STOPPED 而非 COMPLETED。 |
| 9 | direction_delay_one_ms |
验证 1 ms 方向建立延时结束前不输出脉冲,满 1 ms 后启动。 |
| 10 | ext_edge_at_natural_boundary |
验证 EXT 边沿和自然末脉冲同时发生时只处理一次,不重复切段或计数。 |
| 11 | ext_cut_natural_boundary_interleavings |
覆盖 EXT 切断与自然边界的两种临界交错顺序,均正确进入后续 2000 Hz 段。 |
| 12 | ext_edge_during_direction_delay |
验证方向延时期间的 EXT 边沿不会在首脉冲前错误切段。 |
| 13 | stopped_clears_pending_ext |
验证 STOP 清除方向延时期间挂起的 EXT 边沿,重新 START 不消费旧边沿。 |
| 14 | ext_edge_seamless_epoch |
验证上一段的 EXT 竞态不会误切无缝衔接后的下一段。 |
| 15 | ramp_seamless_epoch |
验证上一段遗留的调速更新不会重定向无缝衔接后的下一段。 |
| 16 | short_profile_boundary_matrix |
覆盖直线、平滑、正弦三曲线与 1、2、10、99、100、101 脉冲,检查频率范围、升降趋势、对称性、计数和理论时长。 |
| 17 | short_final_no_poll |
验证三种曲线的 10 脉冲最终短段在脉冲间无 1 ms Poll 时仍逐脉冲减速并准确完成。 |
| 18 | zero_to_maximum_ramp_duration |
验证三种曲线从 0 加速到 100 kHz、1000 脉冲、20 ms 配置的单调性、峰值和总时长。 |
| 19 | maximum_pulse_ramp_duration |
验证三种曲线在 65,535 脉冲和 1311 ms 加速条件下采用可达峰值并准确计数。 |
| 20 | maximum_single_ramp_no_poll |
验证三种曲线的 65,535 脉冲单段在无 Poll 时持续逐脉冲更新并准确完成。 |
| 21 | short_timer_failure |
注入短段频率更新失败,验证立即关闭输出并进入 ERROR/TIMER,位置保留为 1。 |
| 22 | stop_after_subsequent_handoff |
验证无缝切入第二段后立即 STOP 能从新段频率正常减速并停止。 |
| 23 | short_zero_speed_edges |
验证起始/停止速度均为 0 时,1 脉冲和 2 脉冲短段仍以有效非零硬件频率准确执行。 |
| 24 | short_dynamic_retarget |
验证 50 脉冲短段中把目标频率由 1000 改为 2000 Hz 后安全上升,随后 STOP 正常停止。 |
| 25 | short_ext_cut |
验证短段执行 3 脉冲后触发 EXT,在下一脉冲边界切断,最终位置 4。 |
| 26 | current_frequency_dynamic |
验证当前段动态频率按安全脉冲边界生效,状态反映实际输出,运行中改脉冲数不改变启动快照。 |
| 27 | poll_mailbox_failure |
注入动态调频失败,验证 Poll 仅提交更新,失败在脉冲边界转为 ERROR/TIMER。 |
| 28 | future_frequency_on_arrival |
普通逐段模式中修改未来第二段频率,当前段不受影响,到达第二段时采用新值。 |
| 29 | subsequent_future_frequency_handoff |
无缝模式中修改未来段频率,验证预装衔接重建并以新频率无缝进入第二段。 |
| 30 | future_frequency_last_pulse_race |
验证未来段调频与当前段最后脉冲竞态时,第二段仍以新频率启动并准确完成。 |
| 31 | latched_update_before_future_frequency_commit |
验证屏蔽中断期间锁存的物理脉冲先消费旧衔接,再提交新的未来频率预装。 |
| 32 | subsequent_no_poll_gap |
验证两段只靠 ISR 无缝衔接,中间不依赖 Poll,输出不停止,最终位置 7。 |
| 33 | three_single_pulse_no_poll |
验证三个各 1 脉冲、1000/2000/3000 Hz 的段在无 Poll 时连续衔接。 |
| 34 | handoff_queue_failures |
两种时序下注入下一段频率排队失败,均关闭输出并转为 ERROR/TIMER。 |
| 35 | command_state_restrictions |
验证 IDLE/RUNNING 状态下 START、STOP、CLEAR、静态/动态配置写入及非法组合的限制。 |
| 36 | command_mailbox_start_snapshot |
验证 START 延迟执行与配置快照,后续写入和重复 START 不覆盖首个快照。 |
| 37 | command_mailbox_stop_clear_deferred |
验证重复 STOP/CLEAR 可幂等入队,状态变化延迟到 Poll,冲突命令返回 BUSY。 |
| 38 | command_mailbox_validation |
验证 START 入队前检查起始段和绝对位置有效性,非法配置不启动。 |
| 39 | command_mailbox_start_failure |
注入底层启动失败,验证 Poll 后进入 ERROR/INVALID_RESOURCE,CLEAR 可恢复。 |
| 40 | wait_signal_positive |
验证段后进入 WAITING,选定输入有效后由下一次 Poll 结束等待。 |
| 41 | persistence_reinit |
在 Host 存储模拟中验证输出、方向、2345 Hz 配置和位置 4 跨 PlsrInit() 恢复,CLEAR 后位置保持 0。 |
| 42 | position_flash_save |
验证位置稳定约 1000 ms 后仅保存一次,CLEAR 后延时产生第二次保存。 |
| 43 | busy_reset_absolute_gate |
模拟运动中复位,保留位置但取消绝对位置可信标志;CLEAR 后恢复绝对运动。 |
| 44 | position_overflow |
验证 INT32_MAX 正向溢出时立即停脉冲并进入 ERROR/COUNT。 |
说明:Host 测试直接调用寄存器 API 和平台替身,不能代替真实 RTU 串口板测;真实 RTU 结果见第 7 节。
执行命令:
py -B HostComputer\plsr_modbus_product_test.py --self-test
结果:
Self-test passed; no serial port was opened
覆盖:
执行命令:
py -B HostComputer\plsr_control_panel.py --self-test
结果:
PLSR control panel self-test passed; no GUI or serial port was opened
覆盖 9600 波特率、X4/X5 选项、10 段地址步进、整数解析和越界拒绝、公共区 20 WORD 编码、段区 8 WORD 编码、负脉冲补码及跳转段范围校验。
执行命令:
& 'F:\IAR Systems\Embedded Workbench 8.3\common\bin\IarBuild.exe' `
'EWARM\Modbus.ewp' -build Modbus -log warnings
最终结果:
Total number of errors: 0
Total number of warnings: 0
EWARM/Modbus/List/Modbus.map 同时报告 Errors: none、Warnings: none,内存数据见第 3 节。
最终烧录使用 EWARM/Modbus/Exe/Modbus.hex,只擦除并写入应用占用的 Sector 0~3。ST-LINK 最终输出:
Verification...OK
开发过程中还用 STM32CubeProgrammer 执行过两次下载校验,用于确认当时固件可以下载和校验。它们属于中间版本的烧录链路记录,不是功能测试,也不作为最终交付固件的产物证据;最终结论以上述 ST-LINK 烧录、最终 SHA256 和后续完整板测为准。
烧录前后均读取并检查 Sector 10/11。应用 HEX 地址上限为 0x0800C550,不覆盖持久化区;烧录流程也没有擦除 Sector 10/11。该检查用于确认后续持久化板测不是由重新烧录清空后伪造出的初始状态。
板测烧录件和当前分支重新构建产物的 SHA256 均为:
97956D9EF30A9B274140B9B82F412EDD77F9637F563FCC3F552BB4B140772343
执行入口:
py -B HostComputer\plsr_modbus_product_test.py `
--port COM5 --run --phase all --allow-motion
最终 phase=all 的 9 组测试全部 PASS:
| # | 用例 | 实际验证内容 | 结果 |
|---|---|---|---|
| 1 | fixed_map_and_exceptions |
真实 FC03/06/10 访问固定配置、段、状态和控制区;检查保留字读零、控制字读零;验证不支持 FC01 返回 0x01,非法/跨区/只读地址返回 0x02,保留字非零、半个 DWORD 和组合控制命令返回 0x03。 | PASS |
| 2 | positive_negative_absolute_zero |
验证正向相对、负向相对、绝对定位及零位移,检查方向、终态和逻辑位置。 | PASS |
| 3 | all_outputs_100khz |
同一个 PLSR 依次选择 Y0、Y1、Y2、Y3,每路请求 100 kHz、执行 1000 个逻辑脉冲并检查完成位置。不是四轴并发。 | PASS |
| 4 | short_final_profiles |
对直线、平滑、正弦三种曲线执行最终 10 脉冲短段,检查终态和准确位置。 | PASS |
| 5 | wait_time |
执行 10 脉冲后进入 WAITING,配置等待 120 ms;检查不会异常提前完成,之后正常完成。 | PASS |
| 6 | act_time |
2000 脉冲目标配合 80 ms ACT,检查时间到后在 0~2000 之间切断并正常完成。 | PASS |
| 7 | dynamic_frequency_repeated_stop |
当前段从 500 Hz 动态改为 1200 Hz;真实状态读回 1199 Hz,在允许的 1 Hz 容差内;运行中静态输出选择写入返回 BUSY;重复 STOP 正常减速停止且未跑满 3000 脉冲。 | PASS |
| 8 | future_segment_frequency_on_arrival |
普通逐段模式运行第一段时修改未来第二段频率,确认当前段仍为 400 Hz,进入第二段后采用 900 Hz。 | PASS |
| 9 | subsequent_future_frequency_handoff |
SUBSEQUENT 模式运行第一段时修改未来段频率,确认当前段不变,第二段以 900 Hz 衔接并最终累计 400 脉冲。 | PASS |
脚本在退出时停止仍在运行的任务,并恢复进入测试前保存的公共配置和前两段配置。
正式烧录后还在完整 phase=all 之前单独执行过一次 --phase smoke,固定映射和异常码检查 PASS。第 9 节恢复默认值并再次硬复位后又执行了一次 smoke,结果仍为 PASS。
本轮保留的自动化回环接线:
Y4 -> X4
Y5 -> X5
脚本通过 ST-LINK 临时把 Y4/Y5 对应 GPIO 配置为输出,驱动 PLC 输入并直接读回 X4/X5 输入位;产品协议和正式固件没有为测试夹具新增私有寄存器。
开发期夹具最初尝试写 GPIO 的只写 BSRR 寄存器。旧版 ST-LINK_CLI 3.5.0 会在写后回读校验,因 BSRR 不可按普通寄存器回读而返回码 4。夹具随后改用 PI8/PE6 的 ODR 位带地址 0x424402A0、0x42420298 驱动输出。该失败是调试工具与只写寄存器的校验方式不兼容,不是接线、X4/X5 输入或 PLSR 功能故障。
执行入口:
py -B HostComputer\plsr_modbus_product_test.py `
--port COM5 --run --phase x45 --allow-motion
| # | 用例 | 实际验证内容 | 结果 |
|---|---|---|---|
| 1 | x45_electrical_scan |
Y4/Y5 均关闭时 X4/X5 均为低;分别打开 Y4、Y5 时对应输入为高、另一路保持低;关闭后恢复低,验证双路电气扫描和隔离。 | PASS |
| 2 | X4_wait_signal |
选择 X4;50 脉冲后进入 WAITING,X4 低时保持,Y4 拉有效后完成,位置仍为 50。 | PASS |
| 3 | X4_ext_signal |
选择 X4;5000 脉冲运动中由 Y4 触发 EXT_SIGNAL,提前切段并正常完成。 | PASS |
| 4 | X4_ext_or_complete_natural |
选择 X4;不触发输入,50 脉冲自然完成。 | PASS |
| 5 | X4_ext_or_complete_trigger |
选择 X4;5000 脉冲运动中由 Y4 外部触发,提前完成。 | PASS |
| 6 | X5_wait_signal |
选择 X5;50 脉冲后进入 WAITING,X5 低时保持,Y5 拉有效后完成,位置仍为 50。 | PASS |
| 7 | X5_ext_signal |
选择 X5;5000 脉冲运动中由 Y5 触发 EXT_SIGNAL,提前切段并正常完成。 | PASS |
| 8 | X5_ext_or_complete_natural |
选择 X5;不触发输入,50 脉冲自然完成。 | PASS |
| 9 | X5_ext_or_complete_trigger |
选择 X5;5000 脉冲运动中由 Y5 外部触发,提前完成。 | PASS |
ODR 位带夹具修正后先完成独立电气扫描,再执行完整九项用例。异常清理顺序修正后又执行了第二次完整九项回归,结果仍全部 PASS。
测试结束或异常退出均按以下顺序清理:先关闭 Y4/Y5,再停止仍在运行的 PLSR,恢复测试前配置,最后把 PI8/PE6 恢复为输入高阻并清除上下拉。最终复核结果:
持久化测试使用真实目标板和 100 ms 硬复位,不以 Host 内存模拟代替。
执行 persistence-prepare:
执行 100 ms 硬复位后运行 persistence-verify --cleanup,结果:
随后执行 CLEAR,把位置恢复为 0;写回默认配置并等待保存。
再次执行 100 ms 硬复位后运行 defaults-verify,结果:
最后又执行一次固定映射 smoke,确认默认配置恢复后 RTU 访问和异常码仍通过。
本节记录中间失败;这些结果不是最终失败,而是用于说明本轮调试实际发现并关闭了哪些问题。
| 阶段 | 中间现象 | 原因与处理 | 修复后结果 |
|---|---|---|---|
| 旧架构 PDU 试验 | length == 8U、TestStatusAndControl() == 0 失败 |
试验基于待废弃的旧 PDU 架构。需求复核后没有继续补旧架构,而是按 2026 固定产品接口重写;该试验路线整体废弃。 | 新固定接口由 Host protocol_boundaries 和真实 fixed_map_and_exceptions 覆盖并通过。 |
| 初期 Host | 12 tests / 262 assertions / 2 failures |
失败项是 wait_zero_rejected、act_zero_rejected。复核明确需求后确认正确语义为 WAIT=0 按 1 ms,ACT=0 零脉冲跳段,代码和测试按正确语义修正。 |
wait_zero_one_ms、act_zero_no_pulse 通过;最终 44/1,694,007/0。 |
| 扩充 EXT 测试 | Host 编译失败,PLSR_EXT_SIGNAL 未声明 |
测试扩充时枚举和测试引用未同步,补齐枚举/接口后重跑。 | Host 编译和完整回归通过。 |
| Modbus 裁剪中间态 | IAR 10 errors / 2 warnings |
头文件先精简,旧源文件与 main.c 尚未同步清理;不是运动算法失败。随后完成最小 RTU 源文件和入口同步。 |
IAR 全量构建 0 errors / 0 warnings。 |
| Host 脚本启动 | Windows 默认策略阻止直接运行 .ps1 |
改用 powershell.exe -NoProfile -ExecutionPolicy Bypass -File ...。 |
Host 脚本正常执行并通过。 |
| X4/X5 夹具初版 | ST-LINK_CLI 3.5.0 写 BSRR 返回码 4 |
BSRR 是只写寄存器,旧 CLI 的写后回读校验不适用。改用 PI8/PE6 的 ODR 位带地址驱动 Y4/Y5。 | 独立电气扫描和完整 X4/X5 九项通过;判定为夹具工具限制,不计为产品失败。 |
| X4/X5 夹具审查 | 异常退出清理顺序不够安全 | 调整为先关闭 Y4/Y5,再停止运动、恢复配置,最后释放 GPIO;保证清理期间不会继续保持输入触发。 | 完整 X4/X5 九项重新执行并通过,最终 GPIO/输入状态复核通过。 |
中间失败均保留在本报告中,但最终交付结论以当前基线重新执行的结果为准。
以下项目本轮未执行,不得从本报告的 PASS 推导为已验证:
测试结束后目标板处于以下状态:
97956D9EF30A9B274140B9B82F412EDD77F9637F563FCC3F552BB4B140772343 对应版本。defaults-verify 和固定映射 smoke 均 PASS。本报告没有引用旧实现的四轴、双 AB、长稳或逻辑分析仪结果作为本次重写证据。