news 2026/6/9 23:36:55

14.正点原子阿波罗H7之utest

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
14.正点原子阿波罗H7之utest

1.嵌入式测试方法论

一个产品如果完整测试流程:

以 RT-Thread 的 PWM 驱动为例:

  1. 单元测试(白盒):用 Utest + Mock 测试rt_pwm_set/rt_pwm_enable函数的参数校验、返回值逻辑;
  2. 集成测试(灰盒):调用 PWM 驱动 + 应用层代码,验证能输出预期占空比的 PWM 波;
  3. 冒烟测试:快速验证 PWM 基础调光功能是否可用;
  4. 性能测试:测 PWM 频率切换的响应时间、最大输出频率;
  5. 稳定性测试:连续运行 72 小时 PWM 调光,监控内存 / CPU 无泄漏;
  6. 可靠性测试:模拟 PWM 引脚短路,测系统是否能检测并保护硬件;
  7. 系统测试:结合产品需求,测试 PWM 调光的范围、精度、兼容性;
  8. 回归测试:修改 PWM 驱动代码后,重新执行所有单元 / 集成用例,避免回归 Bug。

快速开发可以使用冒烟测试,验证 “核心功能能跑起来”。

冒烟测试举例:

/* * Copyright (c) 2006-2018, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2020-12-08 wanghaijing first version. */ /* * 程序清单:这是一个 PWM 设备使用例程 * 例程导出了 pwm_sample 命令到控制终端 * 命令调用格式:pwm_sample * 程序功能:通过逻辑分析仪能看到 PH.10 引脚的电平变化。 */ #include <rtthread.h> #include <rtdevice.h> #define PWM_DEV_NAME "pwm5" /* PWM设备名称 */ #define PWM_DEV_CHANNEL 1 /* PWM通道 */ struct rt_device_pwm *pwm_dev; /* PWM设备句柄 */ static int pwm_sample(int argc, char *argv[]) { rt_uint32_t period, pulse, dir; period = 500000; /* 周期为0.5ms,单位为纳秒ns */ dir = 1; /* PWM脉冲宽度值的增减方向 */ pulse = 0; /* PWM脉冲宽度值,单位为纳秒ns */ /* 查找设备 */ pwm_dev = (struct rt_device_pwm *)rt_device_find(PWM_DEV_NAME); if (pwm_dev == RT_NULL) { rt_kprintf("pwm sample run failed! can't find %s device!\n", PWM_DEV_NAME); return RT_ERROR; } /* 设置PWM周期和脉冲宽度默认值 */ rt_pwm_set(pwm_dev, PWM_DEV_CHANNEL, period, pulse); /* 使能设备 */ rt_pwm_enable(pwm_dev, PWM_DEV_CHANNEL); while (1) { rt_thread_mdelay(50); if (dir) { pulse += 5000; /* 从0值开始每次增加5000ns */ } else { pulse -= 5000; /* 从最大值开始每次减少5000ns */ } if (pulse >= period) { dir = 0; } if (0 == pulse) { dir = 1; } /* 设置PWM周期和脉冲宽度 */ rt_pwm_set(pwm_dev, PWM_DEV_CHANNEL, period, pulse); } } /* 导出到 msh 命令列表中 */ MSH_CMD_EXPORT(pwm_sample, pwm sample);

2.utest介绍

如果我们要细致的测试代码,建议将utest用起来。

测试流程:

3.utest举例

用我们的代码举例:

1.增加utest组件

2.配置

3.编译

4.编写代码

打开代码后,发现utest和ulog组件已经加入进来了。

新建一个测试文件:

将该代码添加到工程中:

test.c中:

#include "utest.h" #include <rtthread.h> #include <rtdevice.h> #include <board.h> int math_add(int a, int b) { return a + b; } int math_sub(int a, int b) { return a - b; } rt_bool_t math_is_even(int num) { if (num % 2 == 0) { return RT_TRUE; } return RT_FALSE; } #define utest_log_e(...) rt_kprintf("[UTEST ERROR] " __VA_ARGS__) #define utest_log_i(...) rt_kprintf("[UTEST INFO] " __VA_ARGS__) // 通用断言(条件为假则失败) #define utest_assert(condition, msg) \ do { \ if (!(condition)) \ { \ utest_log_e("Assert failed: %s, file: %s, line: %d, msg: %s", \ #condition, __FILE__, __LINE__, msg); \ utest_handle_get()->error = UTEST_FAILED; \ utest_handle_get()->failed_num++; \ return; \ } \ } while (0) // 整数相等断言 #define utest_assert_int(expected, actual, msg) utest_assert((expected) == (actual), msg) /************************ 单个单元测试函数 ************************/ // 测试加法 static void test_unit_add(void) { // 底层断言宏:utest_assert_int(预期值, 实际值, 失败提示) utest_assert_int(5, math_add(2, 3), "2+3 calculation error"); utest_assert_int(0, math_add(-1, 1), "-1+1 calculation error"); utest_assert_int(0, math_add(0, 0), "0+0 calculation error"); } // 测试减法 static void test_unit_sub(void) { utest_assert_int(2, math_sub(5, 3), "5-3 calculation error"); utest_assert_int(-2, math_sub(3, 5), "3-5 calculation error"); utest_assert_int(0, math_sub(0, 0), "0-0 calculation error"); } // 测试偶数判断 static void test_unit_is_even(void) { // 布尔值断言:先转成整数对比(RT_TRUE=1, RT_FALSE=0) utest_assert_int(RT_TRUE, math_is_even(4), "4 should be even"); utest_assert_int(RT_FALSE, math_is_even(5), "5 should be odd"); utest_assert_int(RT_TRUE, math_is_even(0), "0 should be even"); } /************************ 测试用例入口函数 ************************/ // 总测试用例函数(会被 UTEST_TC_EXPORT 注册) static void test_case_math_utils(void) { // 执行单个单元测试(失败则立即返回,不再执行后续) UTEST_UNIT_RUN(test_unit_add); UTEST_UNIT_RUN(test_unit_sub); UTEST_UNIT_RUN(test_unit_is_even); rt_kprintf("All math utility test cases executed successfully!\n"); } /************************ 初始化/清理函数 ************************/ // 测试用例执行前初始化(可选) static rt_err_t test_case_init(void) { rt_kprintf("===== Math utility test case initialization =====\n"); return RT_EOK; } // 测试用例执行后清理(可选) static rt_err_t test_case_cleanup(void) { rt_kprintf("===== Math utility test case cleanup =====\n"); return RT_EOK; } /************************ 注册测试用例 ************************/ // 格式:UTEST_TC_EXPORT(测试用例函数, 用例名, 初始化函数, 清理函数, 超时时间(秒)) UTEST_TC_EXPORT(test_case_math_utils, "math_utils_test", test_case_init, test_case_cleanup, 10);

5.测试

串口助手中运行测试用例:

6.总结

本案例为简化演示,把测试代码和被测试的业务代码写在了一起;但实际工作中建议严格分离:单元测试用例要独立抽离出来,不混入主代码。正式量产的产品固件里,还能通过编译配置跳过测试代码的编译,这样既不影响产品代码的轻量化,也能在开发阶段完整验证功能

4.单元测试用例总结

一个高质量的嵌入式单元测试用例,需全面覆盖以下核心维度,确保代码在各类场景下的可靠性与鲁棒性

测试维度

测试目标

示例场景

功能正确性

验证核心逻辑是否符合预期(最基础)

加法函数输入 1+2 应返回 3;串口发送字符应能正确出队

边界条件

验证参数 / 输入的极值场景

数组操作测试下标 0 / 最大值 / 最大值 + 1;缓冲区测试满 / 空 / 刚好满的情况

异常场景

验证错误输入 / 硬件异常时的容错性

传 NULL 指针、参数越界、硬件超时、资源申请失败(如内存分配失败)

性能 / 耗时

验证函数执行效率(嵌入式关注实时性)

算法执行耗时是否低于阈值;中断响应时间是否符合要求

资源泄漏

验证内存 / 句柄 / 外设资源是否释放

动态申请内存后是否释放;打开的串口 / 定时器是否关闭

并发 / 重入

验证多线程 / 中断下的逻辑正确性

多线程读写同一缓冲区;中断中调用的函数是否可重入

兼容性

验证不同配置 / 硬件版本下的适配性

不同编译选项(如 DEBUG 开启 / 关闭);不同芯片型号(如 STM32F1/F4)

接口契约

验证函数入参 / 返回值是否符合约定

返回值是否按规范(0 = 成功,负数 = 错误码);入参检查是否生效

优秀的嵌入式工程师,核心能力之一便是通过持续打磨单元测试用例、覆盖全维度场景,不断迭代优化代码,从根源上规避嵌入式系统中因边界、异常、并发等问题引发的硬件故障、系统崩溃等风险

最后再啰嗦一句:一份高质量的嵌入式单元测试用例,从来不是一蹴而就的,而是要在反复校验、补全场景、规避坑点中慢慢打磨;一名优秀的嵌入式工程师,也绝非短期速成,而是靠无数个项目的沉淀、一次次问题的复盘、长期的深耕细作才得以成长。所谓 “35 岁退休” 的说法,放在嵌入式这个重经验、重沉淀、重实战的领域里,简直可笑至极。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/6 16:37:01

医疗器械厂商需要CRM吗?医疗CRM的必要性及主流推荐

为何医疗器械厂商需要CRM&#xff1f;与快消品等行业不同&#xff0c;医疗器械行业的销售与服务链条长、客户决策复杂、合规要求极高。专业的CRM系统能系统地解决以下核心痛点&#xff1a;解决客户信息分散&#xff1a;将医院、科室、专家、经销商等各方信息集中管理&#xff0…

作者头像 李华
网站建设 2026/6/9 7:29:33

32、实用编程工具:拼写检查器与索引生成器详解

实用编程工具:拼写检查器与索引生成器详解 在编程和文档处理的领域中,有两个非常实用的工具值得我们深入探讨,它们分别是拼写检查器 spellcheck.awk 和索引生成器 masterindex 。这两个工具在不同的场景下都能发挥巨大的作用,帮助我们提高工作效率和文档质量。 拼写检…

作者头像 李华
网站建设 2026/6/8 8:41:43

绵阳口腔医院服务商

绵阳口腔医院的技术探索与科雅口腔的专业实践绵阳口腔医院在发展过程中面临着一些技术挑战&#xff0c;如治疗精度不够、患者体验有待提升等。绵阳科雅口腔门诊部针对这些问题提供了专业解决方案。科雅口腔采用先进的数字化口腔技术&#xff0c;通过口腔扫描仪获取患者口腔的精…

作者头像 李华
网站建设 2026/6/9 18:34:02

Kotaemon如何防止误导性总结?关键细节保留策略

Kotaemon如何防止误导性总结&#xff1f;关键细节保留策略 在智能客服、法律咨询和医疗辅助等高风险场景中&#xff0c;一个看似合理的错误回答可能带来严重后果。比如将“合同有效期至2025年12月31日”概括为“长期有效”&#xff0c;或把“利率下调0.5个百分点”描述成“大幅…

作者头像 李华
网站建设 2026/6/7 23:04:57

Kotaemon支持Docker部署吗?一键启动脚本已开源

Kotaemon支持Docker部署吗&#xff1f;一键启动脚本已开源 在AI应用快速落地的今天&#xff0c;一个棘手的问题始终困扰着开发者&#xff1a;为什么同一个模型代码&#xff0c;在开发机上跑得好好的&#xff0c;一到测试或生产环境就“水土不服”&#xff1f;依赖版本冲突、系统…

作者头像 李华
网站建设 2026/6/8 4:33:04

Win空格预览工具!图片 视频 PSD 秒看设计必备神器

宝子们&#xff01;Windows 党终于不用羡慕 Mac 的空格预览了&#xff5e; 断更近两年的神器 QuickLook 4.0 强势回归&#xff0c;修复 bug 后更流畅&#xff0c;核心功能依旧能打&#xff01;软件下载地址 操作超简单&#xff1a;单击选中文件按空格&#xff0c;图片、音频、…

作者头像 李华