PSM: Protocol State Machine, un componente de análisis de protocolo de datos para transmisión

PSM: Protocol State Machine, un componente de análisis de protocolo de datos para transmisión


introducir

PSM: Máquina de estado de protocolo, máquina de estado de protocolo.

Un componente de análisis de protocolo de datos para transmisión, que puede resolver de manera efectiva los problemas de tinción de paquetes y ruptura de cuadros.

PSM lanza el paquete de datos de protocolo completo a la capa de software de la aplicación en forma de una función de devolución de llamada para garantizar que los datos recibidos por la capa de software de la aplicación sean un marco de datos completo y válido.

El código está escrito en C puro y se puede aplicar sin problemas en Windows, Linux, microcontrolador integrado y RTOS integrado.

Descarga de código: https://gitee.com/jhembedded/psm


Escenas a utilizar

  • Use protocolos de transmisión como tcp y udp en Windows y Linux
  • Al desarrollar linux incorporado y microcomputadora de un solo chip, el puerto serie, SPI, IIC, USB, red, etc. se utilizan para enviar y recibir datos de longitud variable

La formulación del protocolo también es relativamente regular. La estructura de un paquete de datos de protocolo completo incluye: el encabezado del paquete, la longitud del campo de datos, la cola del paquete y otra información, o solo la forma del encabezado del protocolo seguido de los datos. Basado en esta característica, Nació PSM. .


Introducción

PSM es administrado por la estructura psm_t, que es la siguiente:

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;                                    // 可用于携带附加参数
};

La migración de PSM también es muy simple, la API es la siguiente:

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: función de recepción de datos, que almacena los datos recibidos en PSM
  • psm_recv_byte: función de recepción de datos de un solo byte, almacena los datos recibidos en PSM
  • psm_reset: Restablecer PSM
  • psm_unpack: analizar los datos recibidos

Al trasplantar, coloque psm_recv o psm_recv_byte en la posición de recepción de datos real para la recepción de datos, y use psm_unpack para analizar los datos recibidos. Después de que psm_unpack se analice correctamente, se llama automáticamente a la función de devolución de llamada para enviar el paquete de protocolo completo a la capa superior.

Tome el puerto serie de un solo chip como ejemplo, coloque psm_recv_byte o psm_recv en el puerto serie que recibe la interrupción para la recepción de datos y entrene la función psm_unpack en un bucle sin fin en la función principal.

Ejemplo:


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);  // 解析
	}
}

Al mismo tiempo, psm_unpack también se puede colocar directamente en la interrupción de recepción del puerto serie. Dado que los datos se analizan en la interrupción del puerto serie en este momento, se llamará a la función de devolución de llamada después de que el análisis sea exitoso, por lo que no debería haber tiempo -operaciones que consumen en la función de devolución de llamada.

Ejemplo:

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

Casos de uso

Ejemplo 1: una cadena de caracteres que termina con un retorno de carro y un avance de línea


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,     // 结束符大小
};

Ejemplo 2: formulario de encabezado de protocolo

La estructura del protocolo es generalmente:

Baotou código de comando Longitud de datos CDN N bytes de datos

Entre ellos: Baotou + código de comando + longitud de datos + CRC se combinan para formar el encabezado del protocolo, seguido de los datos reales.

Si el encabezado del paquete, el código de comando, la longitud de los datos y el CRC son tipos cortos sin firmar y el encabezado del paquete es 0XA1B0, la configuración de PSM es la siguiente:

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,  // 主体数据的位置偏移
};

Ejemplo 3: formulario de encabezado y cola de paquete

La estructura del protocolo es generalmente:

Baotou datos envolver la cola

Suponiendo que el encabezado del paquete y el tráiler del paquete son de tipo corto sin firmar, y que el encabezado del paquete es 0XA5A5 y el tráiler del paquete es 0X5A5A, la configuración de PSM es la siguiente:

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,     // 包尾的大小
};


Supongo que te gusta

Origin blog.csdn.net/qq153471503/article/details/131675950
Recomendado
Clasificación