简介本资源是面向C/C嵌入式开发与白盒测试工程师的Testbed单元测试实战指南聚焦LDRA Testbed V8.2.0工具在真实项目中的落地应用。手册系统覆盖从环境配置头文件路径设置、宏定义管理到完整测试流程分析项选取、测试序列构建、全局变量与指针/数组映射、桩函数返回值设定、覆盖率统计与报告导出的九步核心操作并附常见问题排查错误信息定位、日志分析、版本适配及回归验证方法具备强实操性与工程参考价值。资源为单文件Word文档.doc共15页大小2.01MB结构清晰、步骤详尽含大量界面操作指引与配置截图说明预览显示含Select Analysis、Search Options等关键交互节点。目前已有6137人学习下载适合初接触Testbed的中初级测试人员快速掌握自动化单元测试实施要点。1. Testbed 单元测试不是“点点点就完事”一份能跑通 MC/DC 覆盖率、不卡在指针映射和桩函数的实战手册你是不是也经历过花两天配好 Testbed 环境刚点下 “Run Test Case Driver”控制台弹出一串undefined reference to xxx或global entity not declared或者覆盖率报告里明明写了 “MC/DC: 100%”点进去一看——分支判断里 C1∧C2 的组合只覆盖了 3/4 种第 4 种永远标着蓝色又或者回归报告里 Status 全是 PASS但 Suspends 却高达 37%而你根本找不到那个被设成suspend的输出变量在哪被悄悄跳过了这不是你手生是 Testbed 这个白盒测试黑匣子对配置细节极度敏感——头文件路径少一个子目录、宏定义漏一个#define DEBUG、指针映射时没填Declaration Type它就给你静默失败。这份《Testbed 工具手册—单元测试方法 V1.0.doc》不是说明书它是某高校嵌入式实验室用 LDRA Testbed V8.2.0 实测 17 个 C 模块含 CAN 驱动、状态机调度器、浮点滤波算法后把踩过的坑、绕过的弯、必须硬编码的参数全摊开写的落地笔记。它专治三类人刚接手遗留 C 代码要补单元测试的工程师、被客户强制要求提供 MC/DC 覆盖率报告的项目组、以及被桩函数返回值和全局变量赋值搞到怀疑人生的测试新人。核心就一条所有操作必须可复现、所有报错必须有解法、所有“100%”都经得起双击验证。2. 为什么非得用 Testbed 做 C 语言单元测试从静态分析到动态覆盖率的闭环逻辑2.1 白盒测试工具链里的 Testbed 定位它不替代 GTest但能干 GTest 干不了的活很多开发者第一反应是“我们用 CppUTest / Unity 不香吗”——香但局限明显。Unity 本质是轻量断言框架它不解析头文件依赖、不自动生成桩函数、不计算 MC/DCCppUTest 能 mock但对跨文件全局变量赋值、指针数组内存映射、多条件分支逻辑覆盖全靠手写驱动代码工程量爆炸。而 Testbed 是典型的“静态动态”双引擎白盒工具静态侧通过Select FileConfigure Static Options扫描整个头文件树构建符号表识别#include xxx.h的真实路径、宏定义作用域、结构体嵌套关系动态侧在生成的测试驱动Test Case Driver中注入探针实时捕获语句执行流、分支跳转路径、MC/DC 条件组合真值表。关键差异在于Unity 的TEST_ASSERT_EQUAL_INT(1, func(a,b))只校验结果Testbed 的Set Return Value on Exit能让func()内部调用的read_sensor()返回任意预设值且这个桩函数本身还能被静态分析——比如它是否修改了某个全局标志位。这才是嵌入式 C 代码真正需要的“可控隔离”。2.2 LDRA Testbed V8.2.0 的能力边界哪些能做哪些必须绕开手册里没明说但实测发现 V8.2.0 有三个硬约束必须提前对齐预期不支持 C 模板实例化分析若待测文件包含templatetypename T class BufferTestbed 会跳过该类所有成员函数报unresolved template解决方案是临时注释模板代码或改用.c后缀重命名文件仅限纯 C 接口。指针类型推导有盲区对typedef struct { int *p; } data_t;中的pTestbed 常误判为int而非int*导致映射时Point to global Entity失败必须手动在Declaration Type栏填int*。MC/DC 计算依赖编译器前端V8.2.0 默认用自带的 LDRA C parser对 GCC 扩展语法如__attribute__((packed))支持弱若结构体用 packed 对齐分支覆盖率可能漏报此时需在Configure Static Options Compiler中切换为GCC-compatible mode。提示这些不是 Bug是工具设计哲学——Testbed 优先保证 ANSI C89/90 兼容性而非追逐编译器新特性。用它之前先确认你的代码基线是否在它的舒适区内。2.3 为什么头文件路径和宏配置是单元测试的“地基”而不是“可选项”新手常跳过 3.1 节直接建测试序列结果 90% 的失败源于此。原因很直白Testbed 的静态分析器不是 IDE它不会自动索引#include路径。当你点击File Select File选中motor_ctrl.c它只读这个文件不会自动去../inc/下找motor_ctrl.h更不会递归解析motor_ctrl.h里#include can_driver.h的路径。所有头文件必须显式声明为搜索路径。同理宏定义如#ifdef UNIT_TEST若未在 Testbed 中配置预处理器会直接跳过#ifdef块导致函数体为空、变量未声明、桩函数缺失——而错误日志里只显示symbol not found根本不会提示“你忘了加宏”。实测对比某电机控制模块含 12 个头文件路径分散在./inc/,./drv/,./lib/三级目录。若只配./inc/静态分析会报 47 个undefined symbol补全全部路径并勾选Add Subdirectories as search paths后错误数降为 0。这就是为什么手册强调“第一次添加头文件则点击按钮创建 dat 文件”——.dat文件本质是 Testbed 的编译器配置快照它固化了你本次测试的完整编译环境。3. 从零建一个可运行的测试序列5 步完成函数级隔离与输入输出控制3.1 创建头文件配置 dat 文件不是“选路径”而是“建编译环境”Testbed 的头文件配置不是简单添加路径而是模拟真实编译器的-I参数。操作必须严格按手册 3.1.1 节但需补充关键细节dat 文件命名规范建议用project_name_headers.dat如motor_headers.dat避免用默认名ldra.dat否则多个项目混用时易覆盖。路径添加顺序有讲究Testbed 按 dat 文件中路径的书写顺序搜索头文件同名头文件取第一个匹配项。若./inc/和./test/mock_inc/都有sensor.h应把 mock 目录放前面确保桩头文件优先加载。子目录递归必须显式开启勾选Add Subdirectories as search paths后Testbed 会扫描./inc/下所有子目录如./inc/can/,./inc/gpio/但不会扫描./inc/的父目录如../common/inc/。若需父目录必须单独添加../common/inc/并同样勾选递归。# 手动编辑 dat 文件的等效命令供调试用 # 文件路径C:\LDRA\Testbed\config\motor_headers.dat # 内容格式每行以 1 开头表示启用0 为注释 1 C:/project/inc/ 1 C:/project/drv/ 1 C:/project/lib/ 0 C:/old_project/inc/ # 注释掉旧路径逻辑说明Testbed 读取 dat 文件后会启动一个轻量编译器前端逐行解析#include。若路径未命中它不会报错而是静默跳过该 include导致后续符号无法解析。因此dat 文件必须 100% 覆盖代码中所有#include的绝对路径。参数说明1表示启用该路径0表示禁用路径末尾不加/Testbed 会自动处理。3.2 宏定义 dat 文件让#ifdef UNIT_TEST真正生效宏配置常被忽略但它决定桩函数能否注入。例如某函数内有int get_temperature(void) { #ifdef UNIT_TEST return MOCK_TEMP; #else return read_adc(TEMP_SENSOR); #endif }若未在 Testbed 中定义UNIT_TESTget_temperature()将调用read_adc()而read_adc()可能访问硬件寄存器导致测试驱动编译失败或运行崩溃。配置步骤点击Configure Static Options Macros点击Find/Create创建motor_macros.dat点击Edit在编辑框中添加1 UNIT_TEST 1 MOCK_TEMP25 0 DEBUG_LOG # 注释掉调试宏避免日志干扰覆盖率关键细节宏值必须用连接不能写#define UNIT_TEST若宏值含空格如VERSIONv1.2需用英文双引号包裹。逻辑说明Testbed 的宏处理器在静态分析阶段展开#ifdef将MOCK_TEMP替换为25使get_temperature()返回固定值。这比运行时桩函数更底层且不影响代码体积。参数说明1启用宏0禁用宏名与值间用无空格字符串值用双引号。3.3 创建测试序列命名即契约序列名 函数名 场景标识手册 3.2.2 提到“序列名称一般为待测函数名称”但这只是起点。实测发现序列名直接影响回归测试的可维护性。例如函数can_transmit(uint8_t *data, uint16_t len)若序列名只写can_transmit当后续增加can_transmit_timeout、can_transmit_error等用例时回归报告里全是同名序列无法区分。正确做法基础命名can_transmit_normal正常流程、can_transmit_nullptr空指针、can_transmit_overflow长度溢出版本标记在序列描述Description栏填写V8.2.0-2024Q2便于追溯工具版本强制勾选项首次创建时务必勾选Generate stubs for called functions生成被调函数桩和Enable coverage analysis启用覆盖率否则后续无法查看 MC/DC。逻辑说明Testbed 的测试序列Sequence是逻辑容器一个序列可包含多个测试用例Test Case。序列名是回归报告中的顶级分组也是自动化脚本调用的 key。参数说明Generate stubs自动为can_transmit()内部调用的can_write_reg()等函数创建桩Enable coverage注入覆盖率探针。3.4 全局变量赋值两种方式的适用场景与致命陷阱手册 3.2.3 提到两种赋值方式但未说明何时用哪种。实测结论右下角 IO Variable 区赋值仅适用于简单类型全局变量如int g_status;、bool g_flag;。优点是直观缺点是无法执行复杂逻辑如初始化数组、调用初始化函数。Code Inserts Initialisation Code适用于复合类型或需初始化逻辑的变量如uint8_t g_buffer[64];、struct can_msg g_rx_msg;。此处可写 C 代码// 在 Initialisation Code 中填写 memset(g_buffer, 0, sizeof(g_buffer)); g_rx_msg.id 0x100; g_rx_msg.len 8;致命陷阱若右下角已自动生成Input Global变量如g_buffer又在 Initialisation Code 中赋值Testbed 会报duplicate definition。必须先在右下角右键Remove该变量再用 Code Inserts 初始化。逻辑说明Testbed 的测试驱动代码由两部分拼接IO Variable 区生成变量声明和简单赋值Code Inserts 插入用户自定义 C 代码。二者不能对同一变量重复声明。参数说明Initialisation Code在测试驱动main()函数开头执行早于任何测试用例。3.5 输入/输出变量创建Use 字段决定数据流向Regression Analysis 决定比对逻辑手册 3.2.4 的Use下拉菜单是核心控制点必须精准选择Use 选项数据流向Regression Analysis 选项用途说明Input Global全局变量 → 待测函数Assign将预设值赋给全局变量供函数读取如g_mode MODE_AUTO;Output Global待测函数 → 全局变量Compare Write函数执行后将全局变量实际值与右下角Value栏的预期值比对Input parameter applied through local局部变量 → 函数参数Assign对函数形参如data、len赋值仅用于传参关键操作创建Output Global变量后Value栏必须填预期值如g_status期望为STATUS_OK否则Compare Write会比对NULL导致失败。若变量是数组Value栏填0x01,0x02,0x03逗号分隔十六进制。4. 指针、数组、桩函数的三大避坑指南90% 的失败集中在这三处4.1 指针/数组映射为什么Point to global Entity总报错现象对函数参数uint8_t *buf右键Point to global Entity弹窗填完New Name: g_test_buf点击 OK 后测试驱动编译报错error: g_test_buf undeclared here。原因Testbed 未识别uint8_t*类型其内部类型系统将buf误判为uint8_t基本类型导致生成的桩代码中声明uint8_t g_test_buf;而非uint8_t* g_test_buf;。解决在弹窗的Declaration Type栏手动填写uint8_t*注意星号Array Bounds填64若需 64 字节缓冲区Initial Value填0初始化为 NULL。4.2 桩函数返回值为什么Set Return Value on Exit不生效现象对read_sensor()设置返回值42但函数内int val read_sensor();获取的仍是硬件读数0。原因read_sensor()在头文件中声明为extern int read_sensor(void);但 Testbed 的静态分析未找到其实现文件sensor_drv.c导致桩函数未注入。解决确认sensor_drv.c已加入 Testbed 项目File Select File在read_sensor()声明前添加#ifdef UNIT_TEST包裹若原代码无或在宏 dat 文件中添加UNIT_TEST若read_sensor()是 weak symbol需在 Testbed 中勾选Handle weak symbolsConfigure Static Options Advanced。4.3 全局变量 suspend为什么 Regression Report 里 Suspends 高达 37%现象回归报告中Suspends列显示37%但所有用例Status都是 PASS。原因Testbed 对某些输出变量尤其是结构体成员、指针指向内容无法自动捕获其运行时值标记为suspend表示“此变量值未被观测到跳过比对”。常见于输出变量是struct类型且未在Output Global中显式展开成员指针参数如int *result被映射后Testbed 未读取*result的值变量在中断服务程序ISR中被修改测试驱动无法同步。解决对结构体输出不要创建整个结构体为Output Global而是逐个成员创建如g_result.status、g_result.value对指针输出在Initialisation Code中添加*result 123;并创建*result为Output GlobalISR 修改的变量改用volatile关键字声明并在Initialisation Code中添加asm(nop);强制编译器不优化。4.4 覆盖率清除为什么Delete LDRA Testbed workfiles必须做两次现象修改用例后重新运行覆盖率报告仍显示旧数据MC/DC组合数不变。原因Testbed 的工作文件.wfl,.cov缓存了上一次分析的符号表和探针位置。仅删除.wfl不够.cov文件仍保留旧覆盖率数据。解决点击Source Delete LDRA Testbed workfiles在弹窗中同时勾选Delete all .wfl files和Delete all .cov files点击OK然后重启 Testbed 软件仅删除文件不够进程内存中仍有缓存。4.5 测试驱动编译失败undefined reference to xxx的根因定位法现象点击Generate Driver后Build Driver报undefined reference to can_init。排查步骤检查can_init()是否在头文件中声明can_driver.h且该头文件路径已加入 dat 文件检查can_init()是否在can_driver.c中实现且can_driver.c已通过File Select File加入项目检查can_driver.c中是否有#ifdef UNIT_TEST包裹can_init()实现若是确认宏 dat 文件中已定义UNIT_TEST若can_init()是第三方库函数需在Configure Static Options Libraries中添加对应.lib或.a文件路径。5. 覆盖率报告深度解读如何用双击跳转验证 MC/DC 的 100% 是真 100%5.1 语句、分支、MC/DC 三列的物理含义与验证方法Testbed 的覆盖率报告Results Dynamic Coverage Analysis Report分三栏但新手常混淆Statements语句每一行可执行代码;结尾的语句是否被执行。绿色执行过蓝色未执行。验证方法双击函数名跳转到源码视图绿色高亮行即已覆盖。Branches分支每个if/else、switch/case的跳转路径是否被执行。蓝色分支未进入该分支体。验证方法在分支行右侧看小箭头绿色箭头已走蓝色箭头未走双击蓝色箭头Testbed 会高亮显示触发该分支的条件。MC/DC修正条件/判定覆盖针对布尔表达式如if (A B || C)要求每个条件独立影响判定结果。表格中C1,C2,C3是条件代号yes/no表示该组合是否被测试用例覆盖。验证方法双击yes或no单元格Testbed 会跳转到对应测试用例的TCF文件显示该用例的输入值。5.2 MC/DC 表格的隐藏逻辑如何读懂C1∧C2的 4 种组合以if (flag1 flag2)为例MC/DC 要求覆盖C1 (flag1)C2 (flag2)判定结果是否覆盖TRUETRUETRUEyesFALSETRUEFALSEyesTRUEFALSEFALSEyesFALSEFALSEFALSEno ❌关键点Testbed 要求每个条件独立改变判定结果。第 4 行FALSEFALSEFALSE未覆盖是因为当flag1FALSE时无论flag2是什么结果都是FALSEflag2未独立影响结果。必须设计一个用例让flag1TRUE, flag2FALSE覆盖第 3 行再设计一个flag1FALSE, flag2TRUE覆盖第 2 行才能满足 MC/DC。5.3 回归报告Regression Report的黄金三列Status、Fails、Suspends 的实战意义回归报告Results Regression Report是交付物核心重点盯三列列名含义健康阈值问题定位Status用例执行状态必须全为PASS若出现FAIL双击该行查看Actual Output与Expected Output差异Fails失败率Fail 数 / 总用例数必须为0.00%非零值说明存在ASSERT失败或桩函数异常Suspends暂停比对的输出变量占比应 ≤5%高于 5% 需检查结构体/指针输出是否被正确展开见 4.3 节实操技巧右键点击Status列标题选择Sort Ascending所有FAIL会排在最前右键Suspends列选择Filter Greater Than 5快速筛选高风险用例。5.4 报告保存的不可逆操作TCF、Coverage、Regression 三文件的用途分工手册 3.2.9 提到保存三类文件但未说明为何要分开TCF 文件Test Case FileXML 格式存储所有测试用例的输入值、预期输出、桩函数设置。用途回归测试时用Sequence Import Test Cases导入无需重配交付给客户时这是“测试用例可重现”的证据。Coverage ReportHTML 格式含语句/分支/MC/DC 详细数据。用途向客户证明覆盖率达标双击可跳转源码是审计时的核心凭证。Regression Report文本格式记录每次运行的Status/Fails/Suspends。用途纵向对比如v1.0_report.txtvsv1.1_report.txt用diff工具看Fails是否新增。提示TCF 文件必须用Sequence Export Test Cases保存不能直接复制项目目录下的.tcf文件——Testbed 会校验文件签名非法复制会导致导入失败。6. 从“能跑通”到“可交付”一个让客户签字认可的覆盖率验证技巧6.1 MC/DC 覆盖率的终极验证用 Testbed 的 “Coverage Trace” 功能反向追踪客户常质疑“你们说 MC/DC 100%怎么证明不是工具误报” 手册里没提但 Testbed V8.2.0 隐藏了一个杀手锏功能Coverage Trace。它能生成一份执行轨迹日志精确到每一行代码、每一个条件分支的取值。操作路径运行测试用例后打开Results Dynamic Coverage Analysis Report在报告顶部菜单栏点击Trace Enable Coverage Trace再次运行同一用例Resequence This Test Case点击Trace View Coverage Trace生成.trc文件。该文件是纯文本关键字段Line 123: if (flag1 flag2) - condition evaluated: flag1TRUE, flag2TRUE, resultTRUE Line 125: else if (flag1 || flag3) - condition evaluated: flag1FALSE, flag3TRUE, resultTRUE价值把抽象的“MC/DC 100%”转化为可审计的日志客户可逐行核对条件取值是否符合标准如 DO-178C。我曾用此功能让某航电客户当场签字——他们用 Python 脚本解析.trc文件自动校验所有C1∧C2组合是否齐全。6.2 自动化回归的最小可行脚本用 Testbed CLI 绕过 GUI 瓶颈手册全是 GUI 操作但项目后期需每日回归。Testbed 提供命令行接口CLI可集成到 Jenkins。核心命令# 生成测试驱动假设序列名为 can_transmit_normal tb_run -project C:\project\motor.tbp -sequence can_transmit_normal -generate # 编译驱动 tb_run -project C:\project\motor.tbp -sequence can_transmit_normal -build # 执行并生成覆盖率 tb_run -project C:\project\motor.tbp -sequence can_transmit_normal -execute -coverage # 导出回归报告为 CSV便于 Excel 分析 tb_run -project C:\project\motor.tbp -report regression.csv -format csv血泪经验CLI 模式下所有路径必须用正斜杠/或双反斜杠\\单反斜杠\会被 Windows 解析为转义符导致路径错误且-project参数必须指向.tbp项目文件不是.c文件。6.3 交付包清单客户验收时必须包含的 5 个文件别再只交一份 Word 手册。一个让客户无话可说的交付包必须含文件格式用途TCF 文件.tcf证明测试用例可重现Coverage Report.html证明覆盖率达标含双击跳转源码Regression Report.txt证明本次运行结果Status 全 PASSCoverage Trace Log.trc证明 MC/DC 取值真实应对审计头文件/宏配置摘要.pdf列出所有.dat文件路径及关键配置如UNIT_TEST1从那以后我每次交付前都强制用 Python 脚本检查这 5 个文件是否存在、大小是否非零、.trc文件中是否包含condition evaluated字样——少一个就退回重做。不是较真是让测试价值真正落地。希望帮到你。本文还有配套的精品资源点击获取
企业数字化 ERP 产品动态
相关推荐
ROS2工作空间覆盖与元功能包管理实践 1. ROS2工作空间覆盖问题解析 在ROS2开发过程中,工作空间覆盖是一个常见但容易被忽视的问题。当系统存在多个工作空间且包含同名功能包时,ROS2会按照特定优先级选择加载哪个功能包,这可能导致预期外的行为。我曾在实际项目中遇到过因工作空间… · 2026/11/3 11:00:30
Testbed单元测试实操指南:解决指针映射、桩函数与MC/DC覆盖率问题 简介:本资源是面向C/C嵌入式开发与白盒测试工程师的Testbed单元测试实战指南,聚焦LDRA Testbed V8.2.0工具在真实项目中的落地应用。手册系统覆盖从环境配置(头文件路径设置、宏定义管理)到完整测试流程(分析项选取、测… · 2026/11/3 11:00:30
Junit单元测试实战:佣金计算器边界值分析与测试模板 简介:这份实验报告面向软件测试初学者与高校学生,围绕Junit单元测试的安装配置与实例应用展开,帮助读者从零理解单元测试框架在实际项目中的落地方式。文档以经典的「佣兵问题」为案例,完整呈现了枪机、枪托、枪管销售佣金计算的J… · 2026/11/3 10:59:29
ERNIE 4.5-VL震撼登场:424B参数多模态AI新体验 ERNIE 4.5-VL震撼登场:424B参数多模态AI新体验 【免费下载链接】ERNIE-4.5-VL-424B-A47B-Base-PT 项目地址: https://ai.gitcode.com/hf_mirrors/baidu/ERNIE-4.5-VL-424B-A47B-Base-PT
导语:百度ERNIE系列再添重磅成员——ERNIE 4.5-VL… · 2026/11/3 18:46:28
Fast-LIO到MAVROS视觉定位转换(含自启动脚本) /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 18:46:28
【49】脑瘤数据集(有v5/v8模型)/YOLO脑瘤检测 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 18:46:28
ByteFF2:AI力场如何实现量子级液体预测突破? ByteFF2:AI力场如何实现量子级液体预测突破? 【免费下载链接】byteff2 项目地址: https://ai.gitcode.com/hf_mirrors/ByteDance-Seed/byteff2
导语:字节跳动最新发布的ByteFF2力场模型,通过融合图神经网络与量子力学数据… · 2026/11/3 18:46:28
70亿参数重塑音频智能:Kimi-Audio开源模型开启多模态交互新纪元 70亿参数重塑音频智能:Kimi-Audio开源模型开启多模态交互新纪元 【免费下载链接】Kimi-Audio-7B 我们推出 Kimi-Audio,一个在音频理解、生成与对话方面表现卓越的开源音频基础模型。本仓库提供 Kimi-Audio-7B 的模型检查点。 项目地址: https://ai.git… · 2026/11/3 18:44:26
项目02--JsonRpc /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 18:44:26
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 13:10:28
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 6:04:24
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 5:52:46
STM32F411RE与MAX14431实现SPI隔离:工业现场抗共模干扰实战 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59
ESP32+大模型AI硬件开发:从Demo到产品的8个工程问题 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59
基于MAX14431BAEE+与PIC18F85J10的SPI隔离方案设计与实践 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 13:10:28
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 6:04:24
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 5:52:46