第四章:手撕协议栈 —— 缓冲区与结构体数据的 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 的协议逻辑写得天衣无缝了。

相关推荐
FreakStudio7 天前
W55MH32L-EVB 上手测评:硬件 TCP/IP 加持的以太网单片机,MicroPython 零门槛开发
python·单片机·嵌入式·大学生·面向对象·并行计算·电子diy·电子计算机
✎ ﹏梦醒͜ღ҉繁华落℘12 天前
单片机基础知识---stm32单片机的优先级
stm32·单片机·mongodb
网络研究院12 天前
2026年网络安全
网络·安全·法律·法规·趋势·发展
酣大智12 天前
ARP代理--工作原理
运维·网络·arp·arp代理
treesforest12 天前
AI安全系统如何识别异常访问?IP风险识别正在成为关键能力
网络·人工智能·tcp/ip·安全·web安全
shushangyun_12 天前
2026年快消品B2B系统推荐:支持终端门店订货、促销政策自动化的工具?
java·运维·网络·数据库·人工智能·spring·自动化
u1521096484912 天前
S.S.Audio PRO A2音频隔离器
嵌入式硬件·音视频·实时音视频·视频编解码·视频
zd84510150012 天前
RS485 总线详解
单片机·嵌入式硬件
2601_9618451512 天前
粉笔行测题库|系统班|刷题
网络·百度·微信·微信公众平台·facebook·新浪微博
程序猿阿伟12 天前
《Chrome离线扩展安装的底层逻辑与场景落地指南》
服务器·网络·chrome