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

相关推荐
贞明电子5 小时前
工业三色报警灯怎么接入项目系统?开关量与RS485控制应用指南
运维·网络·微信公众平台·贞明电子
HideF6 小时前
Linux
linux·服务器·网络
万联WANFLOW6 小时前
海外网络故障怎么排查?Cloudflare 三季度断网报告解读与 mtr 实战
运维·网络·故障分析
sbjdhjd6 小时前
网工进阶笔记:H3CSE 园区网全栈技术与协议汇总
网络·笔记·云计算·ssh·网络工程·vlan·aaa
吠品6 小时前
智谱开源 ZCode 之后,真正的考试才开始
单片机·嵌入式硬件·1024程序员节
Chery11408 小时前
高速ADC与精密DAC怎么选?DP9230D与DP8532D应用解析
嵌入式硬件·物联网·射频工程
taktok_ai8 小时前
用 cc-switch 连接 Taktok
网络
长不大的杨瑞杰8 小时前
OpenCode v2 会话可靠性实战:Snapshots、Compaction、Instructions 与 Formatters
人工智能·安全·云计算·软件工程·devops
小嘉丶学长10 小时前
Day14 · 字典与集合
java·开发语言·网络·python·学习·1024程序员节
小李不想当小白11 小时前
SPI通信外设与W25Q64 (硬件SPI)(STM32标准库学习笔记)
笔记·stm32·单片机·嵌入式硬件·学习·嵌入式