第四章:手撕协议栈 —— 缓冲区与结构体数据的 Mock 技巧

这一章我们要攻克嵌入式开发中最常见的"重头戏":通信协议栈

不管是 UART、SPI 还是J1939/NMEA 2000等应用层协议,核心难点都在于:如何处理连续的数据流、缓冲区溢出以及复杂的结构体解析

4.1 协议栈 TDD 的难点

在测试串口(UART)时,我们不仅要 Mock 硬件发送函数,还要模拟"对方发过来的数据"。

  • 发送测试:验证逻辑是否将正确的字节序写入了驱动。

  • 接收测试:向缓冲区注入模拟数据,验证解析算法(如帧头、校验位、帧尾)是否正确。

4.2 接口抽象:面向报文的驱动

src/Driver_UART.h 中,我们不直接操作寄存器,而是定义发送字节的接口:

#ifndef DRIVER_UART_H

#define DRIVER_UART_H

#include <stdint.h>

// 发送一个字节

void UART_SendByte(uint8_t data);

// 检查是否有数据收到

bool UART_IsDataAvailable(void);

// 获取收到的字节

uint8_t UART_GetByte(void);

#endif

4.3 实战:测试一个简单的命令解析器

需求: 协议格式为 [0xAA, CMD, DATA, 0x55]。 我们要实现一个 Protocol_Parse() 函数,当收到 0xAA 0x01 0x01 0x55 时,触发"点灯"动作。

第一步:编写测试 (test_Protocol.c) 这里我们要用到 Expect 的连贯调用,模拟一串字节的到来。

#include "unity.h"

#include "mock_Driver_UART.h"

#include "mock_Hardware_Interface.h"

#include "Protocol_Service.h"

void test_Should_ExecuteCommand_WhenValidFrameReceived(void) {

// 模拟收到一帧完整数据包:0xAA, 0x01, 0x01, 0x55

// 第一次调用:帧头

UART_IsDataAvailable_ExpectAndReturn(true);

UART_GetByte_ExpectAndReturn(0xAA);

// 第二次调用:命令码 0x01

UART_IsDataAvailable_ExpectAndReturn(true);

UART_GetByte_ExpectAndReturn(0x01);

// 第三次调用:数据位 0x01

UART_IsDataAvailable_ExpectAndReturn(true);

UART_GetByte_ExpectAndReturn(0x01);

// 第四次调用:帧尾 0x55

UART_IsDataAvailable_ExpectAndReturn(true);

UART_GetByte_ExpectAndReturn(0x55);

// 关键:当解析到完整有效帧后,预期硬件接口会执行点灯

HW_GPIO_SetLed_Expect(true);

// 执行解析函数(模拟在 while(1) 或中断中调用多次)

for(int i=0; i<4; i++) {

Protocol_Parse();

}

}

4.4 进阶技巧:如何测试发送大块结构体?

如果你要发送一个复杂的 J1939 报文结构体,可以使用 CMock 的 ExpectWithArray 功能。

源码接口: void UART_WriteBuffer(uint8_t* data, uint16_t len);

测试代码:

void test_Should_SendCorrectPacket(void) {

uint8_t expected_data\[\] = {0x18, 0xFE, 0xF1, 0x00, 0xAA, 0xBB, 0xCC, 0xDD};

// 告诉 CMock:我预期 UART_WriteBuffer 被调用

// 并且第一个参数(指针)指向的内容应该匹配 expected_data

UART_WriteBuffer_ExpectWithArray(expected_data, 8, 8);

J1939_SendWheelSpeed(0xAABBCCDD);

}

4.5 本章核心心法:协议栈的"状态机测试"

在专栏中,你可以重点给读者分享以下观点:

  1. 分层治之:驱动层(UART_Send)用 Mock 模拟;解析层(CRC校验、状态机转换)在 PC 上纯逻辑测试。

  2. 错误注入 :在测试里故意返回一个错误的校验码 0x99,验证解析器是否能正确丢弃非法包并复位状态机。

  3. 压力测试:在测试里模拟缓冲区瞬间塞满 1024 字节,观察逻辑是否会死锁。

本章小结

这一章我们攻克了"流式数据"的 TDD 难关。你现在已经有能力在不连串口线的情况下,把 J1939、NMEA 2000 或者 Modbus 的协议逻辑写得天衣无缝了。

相关推荐
开发笔记-阿牛3 小时前
解析一款有意思的产品||CK6159A语音芯片设计的儿童专注力训练机-舒尔特玩具
stm32·单片机·嵌入式硬件·音频
数据知道4 小时前
邮件安全实战:SPF/DKIM/DMARC 配置与钓鱼绕过
网络·安全·web安全·网络安全·邮件安全
王莎莎-MinerU4 小时前
MCP 解决的是工具接入,科研 Agent 还缺的是科学证据接口标准化
开发语言·网络·人工智能·深度学习·pdf·c#·php
残影飞雪5 小时前
ESP32-C5下载烧录固件失败报错的原因
单片机·嵌入式硬件
Chloeis Syntax5 小时前
JAVAEE初阶 --- 构造HTTP请求
网络·网络协议·http·postman
砚凝霜5 小时前
软考网络工程师|第 1 章 计算机网络基础 完整备考笔记
网络·笔记·计算机网络
rcms152702692186 小时前
NIKON 4S019-394 接口模块
网络
搞全栈小苏6 小时前
SIMetrix_simplis8.4安装教程——附安装包
stm32·单片机·嵌入式硬件·电力电子
某不知名網友6 小时前
Linux Socket 编程完整讲解
网络
ICT系统集成阿祥6 小时前
不用复杂命令,快速定位二层 / 三层网络故障
网络·网络排查·二层故障·三层故障