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

相关推荐
VALENIAN瓦伦尼安教学设备3 小时前
激光对中仪采购要点
数据库·嵌入式硬件·算法
芯岭技术3 小时前
OM6625A芯片使用PY32 link烧录程序流程操作介绍
嵌入式硬件·物联网
凌云拓界3 小时前
NodeVerdict | .ndv 二进制格式:为 WASM 解码器设计的紧凑布局
安全·架构·开源·node.js·编辑器·软件工程·wasm
梁辰兴4 小时前
软件工程:模块独立性
软件工程·设计原则·梁辰兴·模块独立性定义·度量标准·提高方法·设计目标
zlinear数据采集卡5 小时前
ZLinear DABM-D223 通信协议与软件开发全解析:从 USB CDC 到 DDS 波形输出
arm开发·嵌入式硬件·fpga开发·开源
小比特-combat5 小时前
多模式调光台灯
单片机·嵌入式硬件
2CM_Embed6 小时前
Altium Designer 中为什么一颗芯片会分成多个 Part?——以 C2000 MCU 为例
单片机·嵌入式硬件·altium designer·c2000
姓刘的哦6 小时前
STM32内置Flash
stm32
大爱编程♡7 小时前
STM32-USART串口
stm32·单片机·嵌入式硬件
hongmai6668888 小时前
耐高温小钢炮:ESP32-C3-MINI-1U-H4模组选型分享
笔记·单片机·嵌入式硬件·物联网·risc-v