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

相关推荐
voipmaker5 小时前
GB28181 对接踩坑实录:注册不上、目录不全、流卡顿,一次说清(附 2022 版变更清单)
物联网·音视频·实时音视频·信息与通信·iot·gb28181·国标gb28181
吠品6 小时前
智谱开源 ZCode 之后,真正的考试才开始
单片机·嵌入式硬件·1024程序员节
Chery11407 小时前
高速ADC与精密DAC怎么选?DP9230D与DP8532D应用解析
嵌入式硬件·物联网·射频工程
长不大的杨瑞杰8 小时前
OpenCode v2 会话可靠性实战:Snapshots、Compaction、Instructions 与 Formatters
人工智能·安全·云计算·软件工程·devops
小李不想当小白10 小时前
SPI通信外设与W25Q64 (硬件SPI)(STM32标准库学习笔记)
笔记·stm32·单片机·嵌入式硬件·学习·嵌入式
LCG米10 小时前
STM32驱动W5500以太网模块实测:TCP吞吐瓶颈定位、TX指针回绕陷阱与断线重连保活落地
stm32·单片机·嵌入式硬件·tcp/ip
尼喃12 小时前
662K/65Z5三脚稳压芯片规格书—PW6566B33/B30引脚功能
嵌入式硬件
创界工坊工作室12 小时前
63-基于STC89C52霍尔传感器的智能化电能表设计
stm32·单片机·嵌入式硬件·51单片机·proteus
智购科技自动售货机厂家13 小时前
让AI经营自动售货机:从Project Vend看工具权限与业务规则~YH
大数据·服务器·单片机·嵌入式硬件·深度学习
Zwawa14 小时前
从 printf 到曲线:把数据送上 VOFA+
c语言·单片机·嵌入式