串口协议包的接收及解析处理

串口协议包的接收及解析处理

串口是单片机应用中应用最多的外设之一。很多电子硬件都会提供串口,如蓝牙模块、WIFI模块、串口屏等。如果我们想开发基于串口通信的产品或使用基于串口通信的电子硬件时,都避不开通信协议

通信协议

通信协议听起来很抽象,实际他就是两个通信设备之间交流的纽带。例如我们两个人互相对话,这就是一个通信的过程,为什么我们可以听懂对方的话,因为我们说的都是汉语,汉语就是我们之间的通信协议。

通信协议一般分为物理层协议层。对于物理层我这里就不介绍了,大家可以根据自己的情况翻阅其他资料,这里主要介绍基于串口的应用层协议。

串口应用层协议

串口应用层协议以下简称为串口协议。在大家购买的各类串口通信的产品时,官方都会提供一份通信协议。总的来说这些协议五花八门,下面我列举几个我见到的通信协议:

DWIN DGUS串口屏 读取内存地址值的指令:

        | Example 5A A5 06 83 20 01 01 78 01 ……
        |          / /  |  |   \ /   |  \     \
        |        Header |  |    |    |   \_____\_ DATA (Words!)
        |     DatagramLen  /  VPAdr  |
        |           Command          DataLen (in Words) 

某指纹模块使其进入休眠模式的指令:

        | Example F5 2C 00 00 00 00 01 F5
        |         /  |  |  |  |   |  |  \     
        |   Header  /   |  |  |   |   \  Tailer
        |     Command   P1 P2 P3  |    CheckSum
        |                         Alawys 0

3D打印机的G代码指令:

        |                (X,Y,Z Command)
    	|                / | \   
    	|              /   |   \
        |	         |     |     |
    	| Example G1 X10.0 Y10.0 Z10.0 ;注释
        |         /      \    |    /   |      
        |   Command        \  |  /     Comment
        |                 (Distance)                        

某平衡小车控制指令:

	| Example $0,0,0,0,1,1,AP23.54,AD85.45,VP10.78,VI0.26#
        |        / | \ \ | / /  |  |    |  |    |  |    | |   \     
        |  Header /    \\|//    |  data |  data |  data | data Tailer
        |  move cmd  other cmd  angel P angle D speed P speed I 

以上通信协议前两种属于十六进制格式的通信协议,后面两种属于字符串格式的通信协议。就实际应用而言,十六进制的通信协议格式应用更加广泛。字符串格式的通信协议更倾向于对于字符串的格式有明确标准的场合,如gcode代码。

实际上无论是十六进制格式的通信协议还是字符串格式的通信协议,只是使用过程中给人的直观感受不同而已(勾选16进制发送,不勾选十六进制发送),对于串口硬件传输而言都是一样的:数据一个字节一个字节的传输,这一个字节的数据我们既可以当做十六进制数据处理也可以当做字符数据处理。

对于通信协议我们一般都会包含一些包头、包尾、包长、校验码等必要的信息,以便于解析协议。没有这些标识信息我们怎么知道发过来的数据到底对还是不对,发过来的数据到底是什么含义。

串口数据的接收及协议解析处理

对于串口接收问题前面之前有文章介绍过串口缓存机制的应用。当然这里不应用缓存机制也是完全可行的。这里我们讲解基于不带串口缓存机制的处理。对于串口接收我们最常用的方式就是在串口中断中接收数据。

利用串口接收数据包信息大致分为下面三种情况:

  1. 接收一帧数据,对帧数据进行处理(可以利用串口接收非空中断和串口空闲中断实现)
  2. 中断中边接收边处理存储,并将有效数据存储起来,再对有效数据进行解析。
  3. 将接收到的数据全部存入缓存,从缓存中提取数据并做处理。

1. 接收帧数据

利用串口和ESP8266WIFI模块通信(可参考野火例程)

2. 边接收边处理存储

以对上面平衡小车协议解析为例:

  • 串口数据接收
/* 变量说明 */
#define BUFFER_SIZE 4    //最多缓存4包数据
#define MAX_CMD_SIZE 80  //每包最多包含80个字节的数据

bool g_bStartBitFlag = 0; //开始接收数据包的标志位
char cmdBuffer[BUFFER_SIZE][MAX_CMD_SIZE]; 缓存数组
 int bufindr = 0; //数据包的读取索引
 int bufindw = 0; //数据包的写入索引
 int buflen  = 0; //缓存中数据包的个数
int serial_count = 0; //每包数据中的计数变量

/**
 * @brief  USART3串口中断服务函数
 * @param  none
 * @retval none
 */ 
void USART3_IRQHandler(void)
{
	uint8_t rec;
	if(USART_GetITStatus(USART3, USART_IT_RXNE) != RESET)  //接收中断
	{
		rec = USART_ReceiveData(USART3);//(USART1->DR);	//读取接收到的数据
  		if(rec == '$')  //判断接收到的数据是否为包头信息
   		{
     		g_bStartBitFlag = 1; //数据包开始标志位
     		serial_count = 0;    //计数变量
   		}
        if(g_bStartBitFlag == 1)
        {
          cmdBuffer[bufindw][serial_count++] = rec; //命令缓存
        }
        if(g_bStartBitFlag == 1 && rec == '#') //接收到包尾数据,接收完成
        {
          g_bStartBitFlag = 0;
          bufindw = (bufindw + 1) & (BUFFER_SIZE - 1);
          buflen += 1;
        }
        if(serial_count >= 80) //接收长度溢出,重新接收
        {
          g_bStartBitFlag = 0;
          serial_count = 0;
        }
 	} 
} 
  • 数据包解析:
void protocol_process(void)
{
 if(buflen)
 {
   switch(cmdBuffer[bufindr][1])
   {
     case '0':
       //...
       break;
     case '1':
       //...
       break;
   }
     
   /* 协议中其他数据的处理 */
     
   buflen -= 1; //该包数据已分析完缓存中数据包数减1
   bufindr = (bufindr + 1) & (BUFFER_SIZE - 1); //移到下一读取索引位置
 }
}

3. 先缓存后处理

以上面串口屏的协议为例:(该段代码来自于marlin固件)

/* 定义包头及指令信息 */
#define HEADER1 0x5A
#define HEADER2 0xA5
#define CMD_READVAR  0x83
#define CMD_WRITEVAR 0x82

/* 枚举读取数据报文的状态 */
typedef enum
{
    IDLE,
    SEEN_HEADER1,
    SEEN_HEADER2,
    DATA
}rx_datagram_state_t;

/* 定义数据包接收状态的变量,并初始化为空闲状态 */
rx_datagram_state_t rx_datagram_state = IDLE;

/* 协议数据处理函数 */
void processRx()
{
    uint8_t receivedbyte;
    while(serial_available())  //缓存中有数据
    {
        switch(rx_datagram_state)
        {
            case IDLE:
                receivedbyte = serial_read();
                if(HEADER1 == receivebyte)
                {
                    rx_datagram_state = SEEN_HEADER1;
                }
                break;
            case SEEN_HEADER1:
                receivedbyte = serial_read();
                rx_datagram_state = (HEADER2 == receivebyte) ? IDLE : SEEN_HEADER2
                break;
            case SEEN_HEADER2:
                rx_dategram_len = serial_read(); //数据包的长度
                rx_datagram_state = DATA;
                break;
            case DATA:
				if(serial_available() < rx_datagram_len) return;
                uint8_t command = serial_read();
                uint8_t readlen = rx_datagram_len - 1; //command is part of len
                uint8_t tmp[rx_datagram_len - 1];
                unsigned char *ptmp = tmp;
                while(readlen--)
                {
                    receivedbyte = serial_read();
                    *ptmp++ = receivedbyte;
                }
                // mostly we'll get this: 5A A5 03 82 4F 4B -- ACK on 0x82,so discard it.
                if(command == CMD_WRITEVAR && 'O' == tmp[0] && 'K' == tmp[1])
                {
                    rx_datagram_state = IDLE;
                    break;
                }
                /* AutoUpload, (and answer to) Command 0x83 :
                |      tmp[0  1  2  3  4 ... ]
                | Example 5A A5 06 83 20 01 01 78 01 ……
                |          / /  |  |   \ /   |  \     \
                |        Header |  |    |    |   \_____\_ DATA (Words!)
                |     DatagramLen  /  VPAdr  |
                |           Command          DataLen (in Words) */
                if(command == CMD_READVAR)
                {
                    const uint16_t vp = tmp[0] << 8 | tmp[1];
                    const uint8_t dlen = tmp[2] << 1; //Convert to Bytes.(Display works with words)
                    
                    /* 数据处理 */
                    
                    rx_datagram_state = IDLE;
                    break;
                }
            //discard anything else
            rx_datagram_state = IDLE;
        }
    }
}
  • 30
    点赞
  • 218
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
### 回答1: Verilog 串口接收协议解析是指使用 Verilog 语言实现对串口接收数据进行解析的过程。串口通信是一种常见的通信方式,用于在两个设备之间传输数据。在串口接收协议解析中,我们需要对接收到的串口数据进行处理解析,以提取出有效的信息。 首先,在 Verilog 中,我们需要定义一个串口接收模块,该模块将从串口接收端口接收数据。接收模块需要根据串口通信协议处理数据。常见的串口通信协议括 UART (Universal Asynchronous Receiver/Transmitter) 和 SPI (Serial Peripheral Interface) 等。 在 UART 协议中,我们需要解析串口接收到的数据帧。数据帧括起始位、数据位、校验位和停止位等。我们可以使用 Verilog 中的有限状态机来实现对数据帧的解析。首先,我们需要等待起始位的出现,然后开始接收数据位。接收完所有数据位后,我们会根据校验位的值来判断数据的正确性。最后,我们需要等待停止位的出现,确认一帧数据的接收完成。 在 SPI 协议中,我们需要解析串口发送过来的数据包。SPI 协议使用主从模式,我们可以在 Verilog 中实现 SPI 主机或从机的接收模块。解析数据包的过程括读取命令字节、读取数据字节和确定数据的正确性。 总结来说,Verilog 串口接收协议解析是指使用 Verilog 语言实现对串口接收到的数据进行解析的过程。这个过程涉及到根据串口通信协议解析起始位、数据位、校验位和停止位等信息,以及解析命令字节和数据字节等内容。通过使用有限状态机等技术,我们可以实现对串口接收数据的准确解析和理解。 ### 回答2: Verilog是一种硬件描述语言,用于设计数字逻辑电路和系统。在Verilog中,可以通过使用特定的模块来实现串口接收协议解析串口接收协议一般是指通过串行通信接收数据时的约定规则,其中括波特率、数据位数、校验位以及停止位等信息。在Verilog中,可以使用FIFO(First-In-First-Out)缓冲区来接收串口数据,并对数据进行解析。 首先,需要定义一个用于接收串口数据的FIFO缓冲区,该缓冲区按照FIFO的规则进行数据写入和读取。当接收到数据时,将数据写入到FIFO缓冲区的末尾位置。同时,需要定义一个指针来指示当前读取到的位置。 然后,需要解析接收到的数据。根据串口协议的要求,可根据波特率和数据位数以及校验位的约定来进行解析。可以使用一个状态机来处理不同的解析情况。当接收到数据时,根据当前状态判断该数据是有效数据还是校验位,并进行相应的处理。 最后,根据具体的应用需求,可以进行进一步的数据处理,如将接收到的数据写入到存储器中或进行其他操作。 总结起来,Verilog可以通过定义FIFO缓冲区和使用状态机来实现串口接收协议解析。该方法可以适用于不同的串口接收协议,并可以根据具体的应用需求进行进一步的数据处理。 ### 回答3: Verilog串口接收协议解析是指使用Verilog语言来实现对串口接收数据进行解析的过程。串口是一种常见的通信接口,广泛应用于各种电子设备中。而串口接收协议则是定义了串口数据的格式和传输规则,用于确保数据的可靠传输。 Verilog语言是一种硬件描述语言,可以用于设计和描述数字电路的行为和结构。通过在Verilog中实现串口接收协议解析,可以将其应用于数字电路设计中,以便在硬件层面上实现对串口数据的解析处理。 在Verilog中实现串口接收协议解析通常需要以下步骤: 1. 接收数据:通过串口接收接收串口发送的数据。接收的数据会被存储在一个输入寄存器中。 2. 解析数据:通过对输入寄存器中的数据进行解析,可以提取出各个数据位所代表的含义。例如,可以将串口数据中的起始位、数据位、校验位和停止位等进行解析,以获取有效的数据。 3. 进行校验:对解析后的数据进行校验,以确保数据的完整性和正确性。常见的校验方法括奇偶校验和循环冗余校验(CRC)等。 4. 进行处理:对解析和校验后的数据进行进一步的处理。根据具体的应用需求,可以进行数据的存储、计算、显示或者其他操作。 通过Verilog语言实现串口接收协议解析,可以将硬件和软件结合起来,实现对串口数据的高效处理。这种设计方式可以提高系统的响应速度和数据处理能力,并且能够在硬件层面上实现对串口数据的实时解析,大大提高了系统的效率和可靠性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值