news 2026/4/18 12:26:12

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/4/18 0:02:32

研究生必备:8款AI论文写作神器,轻松搞定毕业论文,科研无忧!

如果你是那个正在宿舍、图书馆或出租屋里&#xff0c;对着空白的Word文档抓耳挠腮&#xff0c;看着日历上日益逼近的提交Deadline而彻夜难眠的研究生&#xff1b;如果你是那个被导师的“进度怎么样了&#xff1f;”问得头皮发麻&#xff0c;为高昂的知网查重费用而心疼&#xf…

作者头像 李华
网站建设 2026/4/18 3:46:17

毕业/期刊/职称论文必备:9款AI工具一键极速生成超高效

如果你是正在熬夜赶Deadline的毕业生、被导师连环催稿的研究生、或是预算紧张却要拼职称的科研人…… 深夜两点&#xff0c;宿舍只剩键盘敲击声。你盯着屏幕上“延毕预警”邮件&#xff0c;心里一遍遍回放导师那句“内容逻辑不行&#xff0c;参考文献也不规范”。查重一次要花…

作者头像 李华
网站建设 2026/4/18 7:40:51

大学生必备:7款AI写毕业论文神器,轻松搞定论文+真实参考文献!

如果你是正在熬夜赶Deadline的毕业生&#xff0c;对着空白的文档发呆&#xff0c;心里盘算着“如果明天交不了初稿&#xff0c;导师会不会把我拉黑”&#xff1b;如果你是那个月初就花光了生活费&#xff0c;根本负担不起动辄几百块的查重费用&#xff0c;还在为文献综述绞尽脑…

作者头像 李华
网站建设 2026/4/17 19:43:13

“免费+秒回”还吊打旗舰?Gemini 3 Flash 杀疯了,OpenAI 却还在挤牙膏

前3秒结论&#xff1a;瑞士LogicStar的代码智能体把平均修Bug时间砍掉95%&#xff0c;但真正的赢家不是AI&#xff0c;而是会“指挥”AI的人。01 当AI开始“自己打补丁”&#xff0c;开发流程被彻底改写LogicStar 2025年12月发布的自主修复智能体&#xff0c;用深度语义分析大模…

作者头像 李华
网站建设 2026/4/18 8:53:08

传感器学习(day14):脑电技术全解析:从原理到应用

每日更新教程&#xff0c;评论区答疑解惑&#xff0c;小白也能变大神&#xff01;" 目录 脑电传感技术&#xff1a;从基础原理到前沿应用的深度解析 摘要 第一章&#xff1a;脑电信号的物理本质与检测挑战 1.1 脑电信号的基本原理 1.2 脑电检测的核心挑战 第二章&am…

作者头像 李华
网站建设 2026/4/18 8:51:49

如何下载别人发布的短视频(官方方式和第三方方式)

下载别人发布的短视频&#xff0c;一般分为官方方式和第三方方式&#xff0c;具体取决于平台是否允许保存、对清晰度/水印的要求&#xff0c;以及是否用于个人学习收藏。 一、最安全&#xff1a;平台官方保存&#xff08;推荐&#xff09; 适合抖音 / 快手 / TikTok&#xff0…

作者头像 李华