第二章:隔离硬件 —— 利用 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 写入等一切硬件行为。

相关推荐
刘延林.2 小时前
esp32 s3+micpython快速验证ML307R 是否能正常连接4G
单片机·嵌入式硬件
不做无法实现的梦~8 小时前
86步进电机和DM860H驱动器的使用方法和记录
单片机·嵌入式硬件
Aaron15888 小时前
RFSOC+VU13P/VU9P+GPU多通道同步一体化解决方案
人工智能·嵌入式硬件·算法·matlab·fpga开发·硬件架构·基带工程
所见即所得111118 小时前
stm32烧录过程中串口问题(串口被占用无法使用)
stm32·单片机·嵌入式硬件
星恒讯工业路由器9 小时前
SDN:让网络变得更智能、更灵活、更可编程
网络·物联网·信息与通信·sdn
Freak嵌入式9 小时前
WIZnet-EVB-Pico2开始,用MicroPython玩转以太网开发
arm开发·人工智能·python·嵌入式硬件·机器人·嵌入式·micropython
Ligocious9 小时前
stm32---1.两种开发方式点亮LED
stm32·单片机
黑白园9 小时前
STM32F103C8TC使用ST-Link下载
stm32·单片机·嵌入式硬件
时空自由民.9 小时前
嵌入式MCU的中断系统工作流程及其原理
单片机·嵌入式硬件