前言
在嵌入式系统开发中,串口通信是一种常见的数据传输方式。为了保证数据的可靠传输,有时需要设计一种自定义的串口协议。本文将介绍如何利用STM32微控制器实现串口自定义协议,以确保数据的完整性和可靠性。
一、自定义协议概述:
自定义串口协议通常由起始字节、数据内容、校验和和结束字节组成。起始字节用于标识数据包的开始,结束字节表示数据包的结束。校验和用于验证数据的完整性,可以通过累加数据内容得到,接收端根据校验和检测数据是否损坏。
二、使用步骤
1. 发送主机实现细节:
基于STM32的串口发送函数,我们可以设计一个函数来发送自定义协议数据包。以下是一个示例函数的实现:
// 定义协议中使用的特殊字符
#define START_BYTE 0x11
#define END_BYTE 0x99
#define ESCAPE_BYTE 0x7D
#define ESCAPE_MASK 0x20
// 发送单个字节
void Usart_SendByte(usart_type *TX_usart, uint8_t data)
{
usart_data_transmit(TX_usart, data);
while (usart_flag_get(TX_usart, USART_TDC_FLAG) == RESET);
}
// 发送自定义协议数据包
void Usart_SendCustomPacket(usart_type *usart, uint8_t *data, uint8_t length) {
uint8_t checksum = 0;
// 发送起始字节
Usart_SendByte(usart, START_BYTE);
checksum += START_BYTE;
// 发送数据长度
Usart_SendByte(usart, length);
checksum += length;
// 循环发送数据字节
for (uint8_t i = 0; i < length; i++) {
if (data[i] == START_BYTE || data[i] == END_BYTE || data[i] == ESCAPE_BYTE) {
// 如果数据字节为特殊字符,进行字节转义处理
Usart_SendByte(usart, ESCAPE_BYTE);
Usart_SendByte(usart, data[i] ^ ESCAPE_MASK);
checksum += ESCAPE_BYTE;
checksum += data[i] ^ ESCAPE_MASK;
} else {
// 否则直接发送数据字节
Usart_SendByte(usart, data[i]);
checksum += data[i];
}
}
// 计算并发送校验和(不包括起始字节和结束字节)
Usart_SendByte(usart, checksum);
checksum += checksum;
// 发送结束字节
Usart_SendByte(usart, END_BYTE);
}
2.接收从机实现细节
我们设计了一个用于接收数据帧的函数receiveFrame,通过状态机的方式实现协议的解析和数据接收。
typedef enum {
WAIT_START_BYTE,
READ_LENGTH,
READ_DATA,
ESCAPE_NEXT_BYTE,
READ_CHECKSUM,
WAIT_END_BYTE
} ReceiveState;
ReceiveState state = WAIT_START_BYTE;
uint8_t receivedLength = 0;
uint8_t dataIndex = 0;
uint8_t checksum = 0;
void receiveFrame(usart_type *usart, uint8_t *data, uint8_t *length) {
uint8_t receivedByte;
receivedByte = usart_data_receive(usart);
switch (state) {
// 等待接收起始字节
case WAIT_START_BYTE:
if (receivedByte == START_BYTE) {
state = READ_LENGTH;
checksum = receivedByte;
}
break;
// 读取数据长度
case READ_LENGTH:
*length = receivedByte;
receivedLength = receivedByte;
checksum += receivedByte;
state = READ_DATA;
break;
// 读取数据内容
case READ_DATA:
if (receivedByte == ESCAPE_BYTE) {
state = ESCAPE_NEXT_BYTE;
} else {
data[dataIndex++] = receivedByte;
checksum += receivedByte;
if (dataIndex >= receivedLength) {
state = READ_CHECKSUM;
}
}
break;
// 处理转义字节
case ESCAPE_NEXT_BYTE:
data[dataIndex++] = receivedByte ^ ESCAPE_MASK;
checksum += receivedByte;
if (dataIndex >= receivedLength) {
state = READ_CHECKSUM;
} else {
state = READ_DATA;
}
break;
// 读取并验证校验和
case READ_CHECKSUM:
if (receivedByte == checksum ) {
state = WAIT_END_BYTE;
} else {
// 校验和错误,进行错误处理
dataIndex = 0;
checksum = 0;
state = WAIT_START_BYTE;
}
break;
// 等待接收结束字节
case WAIT_END_BYTE:
if (receivedByte == END_BYTE) {
// 接收完成
dataIndex = 0;
checksum = 0;
state = WAIT_START_BYTE;
return;
} else {
// 结束字节错误,重新开始接收
dataIndex = 0;
checksum = 0;
state = WAIT_START_BYTE;
}
break;
}
}
---
###
1. 在每个状态下,根据接收到的字节进行相应的处理,包括等待起始字节、读取数据长度、读取数据内容、处理转义字节、验证校验和和等待结束字节。
2. 如果数据接收完毕,并且数据校验正确,则返回接收到的数据和长度。
3. 如果在任何阶段出现错误(如校验和错误、结束字节错误),则重置状态机并丢弃当前接收的数据,以等待下一个起始字节的到来。
# 总结
通过以上实现,我们可以在STM32上轻松地实现串口自定义协议的接收功能。该协议确保了数据的完整性和可靠性,适用于各种嵌入式系统中需要进行串口通信的场景。同时,我们可以根据具体的应用需求对协议进行定制和优化,以满足系统的特定要求。