第二章:隔离硬件 —— 利用 CMock 伪造 GPIO 与定时器

第一章我们跑通了环境,但这只是"纯软件"的逻辑。在嵌入式开发中,最让人头疼的是代码里随处可见的 HAL_GPIO_WritePin__HAL_TIM_GET_COUNTER 等硬件依赖。

如果直接把这些带进测试,编译器会因为找不到 STM32 的寄存器定义而报错。这一章,我们要学习 TDD 的核心技术------隔离(Isolation)与伪装(Mocking)

2.1 依赖倒置:不要直接调用 HAL 库

很多人的代码是这么写的:

// 坏习惯:业务逻辑和 HAL 库强耦合

void Toggle_Led_If_Expired(void) {

if (HAL_GetTick() > 1000) {

HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);

}

}

这段代码在 PC 上无法通过编译 ,因为 PC 没有 GPIOA

TDD 的思维: 我们不直接调用底层,而是定义一套"硬件接口层"。 在 src/ 下创建 Hardware_Interface.h

#ifndef HARDWARE_INTERFACE_H

#define HARDWARE_INTERFACE_H

#include <stdint.h>

#include <stdbool.h>

// 抽象后的接口,PC 和 STM32 都能识别

void HW_GPIO_SetLed(bool on);

uint32_t HW_GetSystemTick(void);

#endif

2.2 CMock 的魔法:mock_ 前缀

这是 Ceedling 最强大的功能。只要你在测试文件中 #include "mock_Hardware_Interface.h",Ceedling 就会:

  1. 扫描 Hardware_Interface.h

  2. 自动生成 一个 mock_Hardware_Interface.c 文件。

  3. 伪造HW_GPIO_SetLed_Expect 这种函数。

2.3 实战:编写一个延时点灯逻辑

我们要实现一个功能:调用 App_LightControl() 时,如果系统时间大于 500ms,就熄灭 LED。

第一步:编写测试 (test_App_Logic.c)test/ 目录下创建文件。注意,我们先写测试,此时 App_LightControl 甚至还没被定义。

#include "unity.h"

#include "mock_Hardware_Interface.h" // 自动生成的 Mock 接口

#include "App_Logic.h"

void test_ShouldTurnOffLed_WhenTimeExceeds500ms(void) {

// 模拟上帝视角:

// 1. 我们预期逻辑会询问当前时间,我们让它返回 501

HW_GetSystemTick_ExpectAndReturn(501);

// 2. 因为 501 > 500,我们预期逻辑会调用 LED 设置函数,参数为 false (关灯)

HW_GPIO_SetLed_Expect(false);

// 3. 执行被测动作

App_LightControl();

}

void test_ShouldKeepLedOn_WhenTimeIsBelow500ms(void) {

// 1. 模拟时间还没到 500ms,返回 100

HW_GetSystemTick_ExpectAndReturn(100);

// 2. 这里不写 HW_GPIO_SetLed_Expect

// 如果代码在这个时候调用了该函数,测试会立即报错(Unexpected Call)

App_LightControl();

}

2.4 第二步:实现逻辑使测试通过

src/ 中创建 App_Logic.c

#include "App_Logic.h"

#include "Hardware_Interface.h"

void App_LightControl(void) {

if (HW_GetSystemTick() > 500) {

HW_GPIO_SetLed(false);

}

}

2.5 深度解析:为什么这很重要?

  1. 确定性测试:在真实 STM32 上,你需要等 500ms 才能看到结果。在 Mock 环境下,时间是由你注入的。

  2. 硬件无关性:这段代码不需要连接任何开发板,在地铁上用笔记本就能完成开发。

  3. 接口契约 :这强迫你思考"我的逻辑到底需要哪些硬件资源",并把它们提炼成接口,从而实现了高内聚、低耦合

本章小结

我们学会了如何使用 Expect(预期调用)和 ExpectAndReturn(预期调用并注入返回值)。这不仅能 Mock GPIO,还能 Mock 传感器读取、Flash 写入等一切硬件行为。

相关推荐
老郑聊AI业财智造7 小时前
从“思考-行动”到“知行合一”:ReActAgent的架构原理与工程实践全景剖析
人工智能·架构·系统架构·软件工程·软件构建
゛凌乱的记忆づ7 小时前
50元从零到成品:一套AI辅助的嵌入式实战入门教程——中断篇
单片机·嵌入式硬件
影寂ldy8 小时前
C# UDP+串口Modbus转发网关
单片机·网络协议·udp
HUI-4749 小时前
EG1188|SOP‑8 高压非隔离降压 PWM 控制器,小功率辅助电源高性价比方案
嵌入式硬件·硬件工程
fox081519 小时前
栈空间带来的大问题
单片机·嵌入式硬件
wuyk55520 小时前
从零吃透Modbus通信|第3章:STM32 Modbus RTU主机
服务器·开发语言·网络·数据结构·stm32·单片机·嵌入式硬件
恒锐丰小瑞21 小时前
率能SS6285L 18V/5A/单通道直流电机驱动IC,超低导通电阻(58mΩ)与多重保护,用于电子锁/玩具/机器人
单片机·嵌入式硬件·机器人
天空'之城1 天前
单片机基础核心知识点汇总(二十)
单片机·嵌入式硬件
笨笨饿1 天前
#121_图传中的H.364编码与MP4的联系
linux·运维·前端·单片机·嵌入式硬件·面试·职场和发展
优化Henry1 天前
5G站点BBU功能模块集成化与偶发案例
运维·网络·学习·5g·tdd