PSM: Protocol State Machine, a data protocol analysis component for streaming

PSM: Protocol State Machine, a data protocol analysis component for streaming


introduce

PSM: Protocol State Machine, protocol state machine.

A data protocol analysis component for streaming, which can effectively solve the problems of packet staining and frame breaking.

PSM throws the complete protocol data packet to the application software layer in the form of a callback function to ensure that the data received by the application software layer is a complete and valid data frame.

The code is written in pure C, and can be seamlessly applied under windows, linux, embedded microcontroller, and embedded RTOS.

Code download: https://gitee.com/jhembedded/psm


scenes to be used

  • Use streaming protocols such as tcp and udp under windows and linux
  • When developing embedded linux and single-chip microcomputer, serial port, SPI, IIC, USB, network, etc. are used to send and receive variable-length data

The formulation of the protocol is also relatively regular. The structure of a complete protocol data packet includes: packet header, length of data field, packet tail and other information, or only the form of protocol header followed by data. Based on this feature, PSM was born. .


Introduction

PSM is managed by the psm_t structure, which is as follows:

typedef struct psm psm_t;
struct psm
{
    
    
	uint8_t* buf;                     // 指向数据缓存
	uint32_t bufsize;                 // 数据缓存大小

	uint32_t wpos;                    // 写指针
	uint32_t rpos;                    // 读指针

	uint32_t match_start;             // 数据匹配索引

	psm_state_t state;                // 当前协议状态机的状态

	int (*recv_callback)(psm_t* psm, const void* buf, uint32_t len);  // 数据回调函数

	uint8_t  delimiter[4];             // 协议尾分隔符
	uint32_t delimiter_bytes;          // 协议尾分隔符长度
	uint32_t delimiter_index;          // 协议尾分隔符匹配索引

	uint8_t  identifier[4];            // 协议头标识符
	uint32_t identifier_bytes;         // 协议头标识符长度
	uint32_t identifier_index;         // 协议头标识符匹配索引

	uint32_t body_offset;              // 主体数据偏移地址
	uint32_t body_length;              // 主体数据长度

	uint32_t length_field_offset;                 // 长度字段偏移地址
	uint32_t length_field_bytes;                  // 长度字段所占字节数
	psm_unpack_coding_t length_field_coding;      // 长度字节编码方式

	void* arg;                                    // 可用于携带附加参数
};

The porting of PSM is also very simple, the API is as follows:

int psm_recv(psm_t* psm, const void* buf, uint32_t len);
int psm_recv_byte(psm_t* psm, uint8_t c);
void psm_reset(psm_t* psm);
int psm_unpack(psm_t* psm);
  • psm_recv: Data receiving function, which stores the received data in PSM
  • psm_recv_byte: data single byte receiving function, store the received data in PSM
  • psm_reset: Reset PSM
  • psm_unpack: parse the received data

When transplanting, place psm_recv or psm_recv_byte in the actual data receiving position for data receiving, and use psm_unpack to analyze the received data. After psm_unpack is successfully parsed, the callback function is automatically called to throw the complete protocol packet to the upper layer.

Take the single-chip serial port as an example, put psm_recv_byte or psm_recv in the serial port receiving interrupt for data reception, and train the psm_unpack function in an endless loop in the main function.

Example:


psm_t psm_usart =
{
    
    
	...
	...
	...
	...
};

void UART1_IRQHandler(void)
{
    
    
	static uint8_t RxData;
	...
	psm_recv_byte(&psm_usart, RxData);  // 接收一个字节
	...
}

int main()
{
    
    
	...
	...
	...

	while (1)
	{
    
    
		psm_unpack(&psm_usart);  // 解析
	}
}

At the same time, psm_unpack can also be directly placed in the serial port receiving interrupt. Since the data is analyzed in the serial port interrupt at this time, the callback function will be called after the parsing is successful, so there should be no time-consuming operations in the callback function.

Example:

void UART1_IRQHandler(void)
{
    
    
	static uint8_t RxData;
	...
	psm_recv_byte(&psm_usart, RxData);  // 接收一个字节
	psm_unpack(&psm_usart);  // 解析
	...
}

Use Cases

Example 1: A character string ending with a carriage return and line feed


int recv_callback(psm_t * psm, const void* buf, uint32_t len)
{
    
    
	// 在此函数中对完整的数据包进行使用
	return 0;
}

uint8_t buffer[512];

psm_t psm =
{
    
    
	.buf                = buffer,
	.bufsize            = sizeof(buffer),

	.recv_callback      = recv_callback,

	.delimiter[0]       = '\r',  // 结束符第一个字节
	.delimiter[1]       = '\n',  // 结束符第二个字节
	.delimiter_bytes    = 2,     // 结束符大小
};

Example 2: Protocol header form

The protocol structure is generally:

Baotou command code Data length CRC N bytes of data

Among them: Baotou + command code + data length + CRC are combined to form the protocol header, followed by the actual data.

If the packet header, command code, data length, and CRC are all unsigned short types, and the packet header is 0XA1B0, the PSM settings are as follows:

psm_t psm =
{
    
    
	.buf              = buffer,
	.bufsize          = sizeof(buffer),

	.recv_callback    = recv_callback,

	.identifier[0]    = 0XB0,  // 包头低字节
	.identifier[1]    = 0XA1,  // 包头高字节
	.identifier_bytes = 2,     // 包头的大小

	.length_field_bytes  = 2,  // 协议头中数据长度字段的大小
	.length_field_offset = 4,  // 协议头中数据长度字段的位置偏移
	.length_field_coding = PSM_UNPACK_ENCODE_BY_LITTEL_ENDIAN,   // 编码方式:小端

	.body_offset = 8,  // 主体数据的位置偏移
};

Example 3: Packet header and tail form

The protocol structure is generally:

Baotou data wrap tail

Assuming that the packet header and packet trailer are unsigned short type, and the packet header is 0XA5A5, and the packet trailer is 0X5A5A, the PSM settings are as follows:

psm_t psm =
{
    
    
	.buf              = buffer,
	.bufsize          = sizeof(buffer),

	.recv_callback    = recv_callback,

	.identifier[0]    = 0XA5,  // 包头低字节
	.identifier[1]    = 0XA5,  // 包头高字节
	.identifier_bytes = 2,     // 包头的大小

	.delimiter[0]     = 0X5A,  // 包尾低字节
	.delimiter[1]     = 0X5A,  // 包尾高字节
	.delimiter_bytes  = 2,     // 包尾的大小
};


Guess you like

Origin blog.csdn.net/qq153471503/article/details/131675950