news 2026/4/23 18:46:11

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/23 9:55:11

Kotaemon与Elasticsearch集成:混合检索方案实现

Kotaemon与Elasticsearch集成&#xff1a;混合检索方案实现 在企业级智能问答系统日益普及的今天&#xff0c;一个核心挑战始终存在&#xff1a;如何让大模型既“懂行”又“靠谱”&#xff1f;我们见过太多生成流畅但张冠李戴的回答——这正是“幻觉”的代价。尤其在金融、医疗…

作者头像 李华
网站建设 2026/4/23 11:38:42

传感器学习(day13):STM8微控制器打造高可靠电磁炉触摸方案

每日更新教程&#xff0c;评论区答疑解惑&#xff0c;小白也能变大神&#xff01;" 目录 基于意法半导体STM8微控制器的电磁炉电容触摸按键解决方案深度解析 摘要 第一章&#xff1a;引言 1.1 电磁炉人机交互需求的演进 1.2 电容触摸按键的优势与挑战 1.3 意法半导体…

作者头像 李华
网站建设 2026/4/23 11:38:23

Kotaemon如何帮助开发者规避大模型合规风险?

Kotaemon如何帮助开发者规避大模型合规风险&#xff1f; 在金融、医疗和政务等高敏感领域&#xff0c;AI系统一旦“说错话”&#xff0c;轻则引发用户质疑&#xff0c;重则导致法律追责。想象这样一个场景&#xff1a;某银行的智能客服告诉客户“根据最新政策&#xff0c;您可申…

作者头像 李华
网站建设 2026/4/23 12:45:43

Kotaemon ERP数据查询:SAP/Oracle桥接方案

Kotaemon ERP数据查询&#xff1a;SAP/Oracle桥接方案 在一家跨国制造企业的月度经营分析会上&#xff0c;财务总监突然发问&#xff1a;“上季度华东区的高毛利产品线库存周转率有没有改善&#xff1f;”会议室瞬间安静——没人能立刻回答。有人跑去导SAP报表&#xff0c;有人…

作者头像 李华
网站建设 2026/4/23 11:27:09

大文件预览实现

Vue大文件预览与虚拟滚动实现学习笔记 一、需求背景 实现 XML 文件预览功能&#xff0c;支持超过 10000 行的大文件预览&#xff0c;具备高性能和良好用户体验。 二、核心挑战 性能问题&#xff1a;大文件一次性渲染会导致页面卡顿甚至崩溃内存占用&#xff1a;大量 DOM 节…

作者头像 李华
网站建设 2026/4/23 12:54:16

Kotaemon健身计划生成:个性化运动处方

Kotaemon健身计划生成&#xff1a;个性化运动处方 在智能健康设备普及的今天&#xff0c;用户早已不再满足于“每天走一万步”或“每周练三次胸”的通用建议。越来越多的人开始追问&#xff1a;“我膝盖受过伤&#xff0c;还能做深蹲吗&#xff1f;”“我想减脂但只有晚上能锻炼…

作者头像 李华