OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 10136|回复: 13

串口通信中如何自己定义协议

[复制链接]

1

主题

882

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
3071
金钱
3071
注册时间
2018-2-7
在线时间
285 小时
发表于 2019-9-3 17:14:51 | 显示全部楼层 |阅读模式
本帖最后由 HXYDJ 于 2020-3-18 08:53 编辑

在单片机刚开始学习的时候,串口通信是经常要用到的,但是实际产品中串口通信是需要通信协议的。好多人不明白为什么要用通信协议,如何定义通信协议,带通信协议的程序要怎么写。今天就来说一下如何串口通信协议是如何定义出来的。
先看一段最简单的串口程序。

  1. void Uart1_Init( unsigned int baudrate )
  2. {
  3.     unsigned int baud;
  4.     baud = 16000000 / baudrate;
  5.     Uart1_IO_Init();                                //IO口初始化
  6.     UART1_CR1 = 0;
  7.     UART1_CR2 = 0;
  8.     UART1_CR3 = 0;
  9.     UART1_BRR2 = ( unsigned char )( ( baud & 0xf000 ) >> 8 ) | ( ( unsigned char )( baud & 0x000f ) );
  10.     UART1_BRR1 = ( ( unsigned char )( ( baud & 0x0ff0 ) >> 4 ) );

  11.     UART1_CR2_bit.REN = 1;        //接收使能
  12.     UART1_CR2_bit.TEN = 1;        //发送使能
  13.     UART1_CR2_bit.RIEN = 1;       //接收中断使能
  14. }
  15. //阻塞式发送函数
  16. void SendChar( unsigned char dat )
  17. {
  18.     while( ( UART1_SR & 0x80 ) == 0x00 ); //发送数据寄存器空
  19.     UART1_DR = dat;
  20. }
  21. //接收中断函数
  22. #pragma vector = 20            
  23. __interrupt void UART1_Handle( void )
  24. {
  25.     unsigned char res;
  26.     res = UART1_DR;  
  27.     return;
  28. }
复制代码

主要函数有三个,一个初始化函数,一个发送函数,一个接收中断。先定义一个简单的协议,比如:接收到1点亮LED灯,接收到0熄灭LED灯。那么接收中断函数就可以修改为:
  1. #pragma vector = 20            
  2. __interrupt void UART1_Handle( void )
  3. {
  4.     unsigned char res;
  5.     res = UART1_DR;
  6.     if( res == 0x01 )
  7.                         LED = 1;        
  8.         else if( res == 0 )
  9.                         LED = 0;
  10.     return;
  11. }
复制代码

直接判断接收到的数据值,根据数据值来控制LED灯的状态。
如果需要控制两个LED灯怎么办呢?需要发送两个数据来控制LED灯的状态。
下来将协议改复杂点,第一位数据控制LED1,第二个数据控制LED2,同样1是点亮LED,0是熄灭LED。如果是这样的话接收数据的时候就不能像上面那样,接收一个数据就去控制LED的状态,因为这次发送的数据有两位,必须区分开第一个数据和第二个数据,于是可以考虑用数组接收数据。修改程序如下:
  1. #pragma vector = 20            
  2. unsigned char res[2];
  3. unsigned char cnt=0;
  4. __interrupt void UART1_Handle( void )
  5. {
  6.     res[ cnt++ ] = UART1_DR;
  7.     if( res[ 0 ] == 0x01 )
  8.                         LED1 = 1;        
  9.         else if( res[ 0 ] == 0 )
  10.                         LED1 = 0;
  11.          if( res[ 1 ] == 0x01 )
  12.                         LED2 = 1;        
  13.         else if( res[ 1 ] == 0 )
  14.                         LED2 = 0;
  15.     return;
  16. }
复制代码

这样通过数组将接收的数据存起来,然后用下标来判断第几个数据,再去控制LED灯的状态。
这时如要需要控制三个LED的话,发送的数据就要在增加一位,加上第三个LED灯,可以用同样的方式来接收数据。修改程序如下:

  1. #pragma vector = 20            
  2. unsigned char res[3];
  3. unsigned char cnt=0;
  4. __interrupt void UART1_Handle( void )
  5. {
  6.     res[ cnt++ ] = UART1_DR;
  7.     if( res[ 0 ] == 0x01 )
  8.                         LED1 = 1;        
  9.         else if( res[ 0 ] == 0 )
  10.                         LED1 = 0;
  11.          if( res[ 1 ] == 0x01 )
  12.                         LED2 = 1;        
  13.         else if( res[ 1 ] == 0 )
  14.                         LED2 = 0;
  15.          if( res[ 2 ] == 0x01 )
  16.                         LED3 = 1;        
  17.         else if( res[ 2 ] == 0 )
  18.                         LED3 = 0;
  19.     return;
  20. }
复制代码


这样的话就算在多加几个LED控制,通信起来也一样适用。看起来这样的协议就可以满足使用了。但是仔细想想,这种协议看起来没什么问题,唯一的缺点就是他会将每个接收到数据都作为有效命令对待。如果说上位机没发送点亮LED的命令,但是串口线上出现了干扰,如果干扰信号刚好是0或者1,那么程序就有可能误动作。就需要在接收数据的时候用一个标志来判断当前发送的数据是真正的命令还是干扰。用一个特殊的值来做为接收指令的开始,这里选用0XA5做为数据的开始,为什么选0xA5呢,因为0xA5的 二进制数位 1010 0101 刚好是一个1一个0,间隔开,用这个数字做为开始可以很大程度上的避免干扰信号,因为干扰信号一般不会是这种高低高低很有规律的信号。于是协议就改为 0xA5 为第一个数据,做为上位机发送命令的标志,然后后面用0和1来代表LED的状态,0为LED熄灭,1为LED点亮。假如我们需要点亮3个LED,那么上位机发送的指令就是 0xA5 0x01 0x01 0x01 一个起始标志,后面跟着三个控制信号。程序修改为:

  1. #pragma vector = 20            
  2. unsigned char res[4];
  3. unsigned char cnt=0;
  4. __interrupt void UART1_Handle( void )
  5. {
  6.     res[ cnt++ ] = UART1_DR;
  7.     if( res[ 0 ] == 0xA5   )
  8.     {
  9.                     if( res[ 1 ] == 0x01 )
  10.                                         LED1 = 1;        
  11.                         else if( res[ 1 ] == 0 )
  12.                                         LED1 = 0;
  13.                          if( res[ 2 ] == 0x01 )
  14.                                         LED2 = 1;        
  15.                         else if( res[ 2 ] == 0 )
  16.                                         LED2 = 0;
  17.                          if( res[ 3 ] == 0x01 )
  18.                                         LED3 = 1;        
  19.                         else if( res[ 3 ] == 0 )
  20.                                         LED3 = 0;
  21.         }
  22.     return;
  23. }
复制代码

这样接收到第一个数据的时候先判断是不是0xA5,如果是0xA5说明是发送的指令,就执行后面的命令,如果第一个数据不是0xA5,就说明是干扰信号,就不执行命令。这样就可以避免干扰信号,导致程序误动作。这样是不是就可以了,仔细分析分析,如果干扰信号没有发生在数据开始位置,而是发生在了数据结束位置,比如我现在只需要控制两个LED灯,发生的指令为0xA5 0x01 0x01,但是接收完前面几个数据后发生了干扰,数据多了一个0x01,那么单片机接收到的数据就成了0xA5 0x01 0x01 0x01 导致第三个灯被误打开,为了避免这种干扰情况,可以再增加一个结束标志,代表发送数据结束,用0x5A作为结尾,刚好和开始标志相反。那么此时如果要控制两个灯的话,发送的数据就变为 0xA5 0x01 0x01 0x5A 。代码修改为:

  1. #pragma vector = 20            
  2. unsigned char res[4];
  3. unsigned char cnt=0;
  4. __interrupt void UART1_Handle( void )
  5. {
  6.     res[ cnt++ ] = UART1_DR;
  7.     if( ( res[ 0 ] == 0xA5   )&&( res[ 3 ] == 0xA5 ) )
  8.     {
  9.                     if( res[ 1 ] == 0x01 )
  10.                                         LED1 = 1;        
  11.                         else if( res[ 1 ] == 0 )
  12.                                         LED1 = 0;
  13.                          if( res[ 2 ] == 0x01 )
  14.                                         LED2 = 1;        
  15.                         else if( res[ 2 ] == 0 )
  16.                                         LED2 = 0;
  17.         }
  18.     return;
  19. }
复制代码

这样通过同时判断发送数据的开始标志和结束标志,确保接收到的数据是真正的命令,避免了干扰数据。但是仔细观察后又发现了一个新的问题。结束标志的数据位置下标是固定的,也就是说每次发送数据只能发送4个字节,也就是每次只能控制两个LED灯,如果要增加控制LED灯的数量就要修改程序,这样在实际操作中很不方便,能不能可以动态的识别发送了几个数据?于是想到,在发送指令的时候,告诉单片机我要控制LED的数量,单片机根据数量值,自动去判断当前需要点亮几个LED灯,于是协议修改为在开始标志后面再添加一位,代表要控制的LED灯数量,后面是点亮或者熄灭命令,最后为结束标志。假如现在要点亮2个LED灯,发送的数据为:0xA5 0x02 0x01 0x01 0x5A,开始标志后面的0x02就代表要控制两个LED灯。如果要点亮3个灯发送的数据就为0xA5 0x03 0x01 0x01 0x01 0x5A。那么如何确定结束标志在哪个位置呢?通过观察上面两组数据可以发现控制2个LED灯的话结束标志在第4位,控制3个LED灯的话结束标志在第5位,结束标志的位置刚好比控制lED灯数量多2,于是程序修改为:

  1. #pragma vector = 20            
  2. unsigned char res[5];
  3. unsigned char cnt=0;
  4. unsigned char num=0;
  5. __interrupt void UART1_Handle( void )
  6. {
  7.     res[ cnt++ ] = UART1_DR;
  8.     if(  res[ 0 ] == 0xA5   )
  9.     {
  10.                     num = res[ 1 ];
  11.                     if( res [ num + 2] == 0x5A )
  12.                     {
  13.                         if( num == 3 )
  14.                         {
  15.                                     if( res[ 2 ] == 0x01 )
  16.                                                         LED1 = 1;        
  17.                                         else if( res[ 2 ] == 0 )
  18.                                                         LED1 = 0;
  19.                                          if( res[ 3 ] == 0x01 )
  20.                                                         LED2 = 1;        
  21.                                         else if( res[ 3 ] == 0 )
  22.                                                         LED2 = 0;
  23.                                          if( res[ 4 ] == 0x01 )
  24.                                                         LED2 = 1;        
  25.                                         else if( res[ 4 ] == 0 )
  26.                                                         LED2 = 0;
  27.                                 }
  28.                         
  29.                           if(  num == 2 )
  30.                           {
  31.                                     if( res[ 2 ] == 0x01 )
  32.                                                         LED1 = 1;        
  33.                                         else if( res[ 2 ] == 0 )
  34.                                                         LED1 = 0;
  35.                                          if( res[ 3 ] == 0x01 )
  36.                                                         LED2 = 1;        
  37.                                         else if( res[ 3 ] == 0 )
  38.                                                         LED2 = 0;               
  39.                                 }                        
  40.                         }
  41.         }
  42.     return;
  43. }
复制代码

这样通过在协议中增加一个数量判断,程序就可以动态的设置LED灯的状态了。但是感觉串口中断中的代码太多了,数据接收和数据处理都放在一个函数中了,这样程序读起来比较费劲。能不能把接收数据和处理数据分开呢?那么就可以在串口中断函数中只接收数据。数据接收完成之后设置标志位,然后在主函数中去处理接收到的数据。于是修改程序为:

  1. #pragma vector = 20            
  2. unsigned char res[5];
  3. unsigned char cnt = 0;
  4. unsigned char num = 0;
  5. unsigned char receive_ok = 0;
  6. __interrupt void UART1_Handle( void )
  7. {
  8.     res[ cnt++ ] = UART1_DR;
  9.     if(  res[ 0 ] == 0xA5   )
  10.     {
  11.                     num = res[ 1 ];
  12.                     if( res [ num + 2] == 0x5A )
  13.                     {
  14.                          receive_ok = 1;                                        //接收完成
  15.                          cnt = 0;
  16.                         }
  17.         }
  18.     return;
  19. }
  20. void LED_Show( void )
  21. {
  22.                 if( receive_ok )
  23.                 {
  24.                      receive_ok = 0;
  25.                                   if( res[ 1 ] == 3 )
  26.                          {
  27.                                     if( res[ 2 ] == 0x01 )
  28.                                                         LED1 = 1;        
  29.                                         else if( res[ 2 ] == 0 )
  30.                                                         LED1 = 0;
  31.                                          if( res[ 3 ] == 0x01 )
  32.                                                         LED2 = 1;        
  33.                                         else if( res[ 3 ] == 0 )
  34.                                                         LED2 = 0;
  35.                                          if( res[ 4 ] == 0x01 )
  36.                                                         LED2 = 1;        
  37.                                         else if( res[ 4 ] == 0 )
  38.                                                         LED2 = 0;
  39.                                 }                        
  40.                           if( res[ 1 ]  == 2 )
  41.                           {
  42.                                     if( res[ 2 ] == 0x01 )
  43.                                                         LED1 = 1;        
  44.                                         else if( res[ 2 ] == 0 )
  45.                                                         LED1 = 0;
  46.                                          if( res[ 3 ] == 0x01 )
  47.                                                         LED2 = 1;        
  48.                                         else if( res[ 3 ] == 0 )
  49.                                                         LED2 = 0;               
  50.                                 }                        
  51.                 }
  52. }

  53. void main ( void )
  54. {
  55.                 while( 1 )
  56.                 {
  57.                                 LED_Show();
  58.                 }
  59. }
复制代码

这样通过一个标志位,将串口接收代码和数据处理代码分开。在接收数据过程中不会因为处理数据导致串口接收异常。程序看起来也比较简洁明了。
在实际项目应用中有时候为了确保接收数据的正确性还需要增加校验位。校验位一般在结束标志的前一位,我们在这里也增加一个校验位,校验位在结束标志前面,校验方式为前面所有数据的累加和。比如 要发送的数据为 0xA5 0x02 0x01 0x01 校验 0x5A
校验 = 0xA5 + 0x02 + 0x01+ 0x01 校验 = 0xA9 所以发送的数据就为 :0xA5 0x02 0x01 0x01 0xA9 0x5A 。串口接收到数据后,也将校验位前面的所有数据累加,然后累加的结果和校验位的数据对比,如果计算的校验结果和校验位的数值相等,说明接收的数据是正确的。否则说明接收的数据错误。修改串口接收部分代码为:

  1. #pragma vector = 20            
  2. unsigned char res[6];
  3. unsigned char cnt = 0;
  4. unsigned char num = 0;
  5. unsigned char receive_ok = 0;
  6. __interrupt void UART1_Handle( void )
  7. {
  8. unsigned char  i=0;
  9. unsigned char  check=0;
  10.     res[ cnt++ ] = UART1_DR;
  11.     if(  res[ 0 ] == 0xA5   )                                                                        //接收到开始标志
  12.     {
  13.                     num = res[ 1 ];                                                                        //存储命令个数
  14.                     if( res [ num + 3] == 0x5A )                                //接收到结束标志
  15.                     {
  16.                             for( i = 0; i <  num+2; i++)
  17.                             {
  18.                                        check  += res[ i ];                                                // 计算校验位前面所有数据累加和
  19.                        }
  20.                         if( check == res [ num + 2] )                        //如果计算的校验位和接收到的校验位想等            
  21.                         {                              
  22.                            receive_ok = 1;                                                        //接收完成
  23.                          }
  24.                          else
  25.                          {
  26.                             receive_ok = 0;                                                        //接收失败
  27.                             cnt = 0;
  28.                                 }
  29.                                  
  30.                         }
  31.         }
  32.     return;
  33. }
复制代码


通过增加校验位来判断接收数据的正确性,这样通过增加开始标志、结束标志、命令数量、校验位这些措施来保证数据传输的可靠性和完整性。如果对数据正确性要求更高的话,可以使用更复杂的校验方法,或者使用更复杂的开始标志或者结束标志。比如开始标志使用两位 如0xA5 0x5A 结束标志也使用两位 0x0D 0x0A ,校验方式使用CRC校验。这样数据的可靠性就会更高。在项目使用中根据不同情况自己定义协议的复杂性,如果要求不高就可以使用简单点的协议,如果对数据要求高,自己根据实际情况设计复杂一点的协议。
通过上面的例子可以看出来,协议主要是用来保证通信过程中数据的安全性和可靠性。协议可以很简单,也可以很复杂,主要决定于应用场合和环境。搞清楚了为什么要定义协议,为什么有的协议很简单,有的协议却很复杂的原因之后,以后在项目中如果再遇到串口通信时,就再也不会发慌了。


STM8S_UART_3.zip

163.38 KB, 下载次数: 89

参考代码

正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

8

主题

37

帖子

0

精华

初级会员

Rank: 2

积分
81
金钱
81
注册时间
2020-3-6
在线时间
26 小时
发表于 2020-3-25 18:13:49 | 显示全部楼层
楼主 你好 我单片机使用你上面的协议设置与XCOM调试助手调试出来的结果与您的结果一样
我想让上位机也可以有调试助手一样的结果 然后我就在上位机中定义该协议头和尾
但我在上位机上给它发送0xA5 0x5A xx xx xx 0x55 0xAA格式的数据包时
串口传回数据为空
不知道是不是上位机发送或接收是否有什么问题 冒昧打扰 如果楼主有时间的话还想请楼主指教一下  麻烦了 谢谢

下面是我的发送代码
public static String ReadHead = "0xA5 0x5A",ReadTail="0x55 0xAA"; 定义的头和尾
String data = ReadHead + strLFIds + strRFIds + strRRIds + strLRIds + vin + ReadTail ;          头+数据+尾的数据包
            mycom.MySerialPortSend(data);//发送数据至串口端

            String datas = mycom.MySerialPortReceives();//接受串口数据
            tlf.Text = datas.Substring(3, 8); 将截取的数据防至文本框中

  public bool MySerialPortSend(string str)//串口数据发送
        {
            try
            {
                byte[] byteArray = System.Text.Encoding.Default.GetBytes(str);
                com.BaseStream.BeginWrite(byteArray, 0, byteArray.Length, new AsyncCallback(WriteSerialPortData), com);
            }
            catch (System.Exception)
            {
                return false;
            }
            return true;
        }

private void WriteSerialPortData(IAsyncResult ar)
        {
            try
            {
                SerialPort com1 = (SerialPort)ar.AsyncState;
                com1.BaseStream.EndWrite(ar);
            }
            catch (System.Exception)
            {
            }
        }
串口数据读取
  private void ReadSerialPortData(IAsyncResult ar)
        {
            try
            {
                SerialPort com1 = (SerialPort)ar.AsyncState;
                int len = com1.BaseStream.EndRead(ar);
                ReceiveStrP = Encoding.Default.GetString(readbyte).Remove(len);
                ReceiveStr += ReceiveStrP;
            }
            catch (System.Exception ex)
            {
                if (ex.ToString().Contains("由于线程退出"))
                {
                    SerialPortLinkPortFail(this, "err0");

                }
            }
            finally
            {
                try
                {
                    if (StopCom != true)
                    {
                         com.BaseStream.BeginRead(readbyte, 0, readbyte.Length, new AsyncCallback(ReadSerialPortData), com);
                    }
                    else
                    {
                        com.Close();
                        System.Threading.Thread.CurrentThread.Abort();
                    }


                }
                catch (System.Exception)
                {

                }

            }

        }
public string  MySerialPortReceives()//串口数据接收
        {
           
            com.BaseStream.BeginRead(readbyte, 0, readbyte.Length, new AsyncCallback(ReadSerialPortData), com);

            string str1 =Encoding.ASCII.GetString(readbyte);//十六进制转换
              return str;
}

回复 支持 1 反对 0

使用道具 举报

6

主题

412

帖子

0

精华

资深版主

Rank: 8Rank: 8

积分
2660
金钱
2660
注册时间
2019-8-14
在线时间
411 小时
发表于 2019-9-3 20:10:52 | 显示全部楼层
好东西,帮顶
回复 支持 反对

使用道具 举报

0

主题

1

帖子

0

精华

新手入门

积分
17
金钱
17
注册时间
2019-8-4
在线时间
3 小时
发表于 2019-10-16 21:14:19 | 显示全部楼层
好帖子!!!!顶顶顶
回复 支持 反对

使用道具 举报

1

主题

2

帖子

0

精华

初级会员

Rank: 2

积分
104
金钱
104
注册时间
2019-5-17
在线时间
59 小时
发表于 2020-1-15 17:25:28 | 显示全部楼层
顶顶顶
回复 支持 反对

使用道具 举报

1

主题

882

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
3071
金钱
3071
注册时间
2018-2-7
在线时间
285 小时
 楼主| 发表于 2020-3-26 20:25:32 | 显示全部楼层
城南一偶 发表于 2020-3-25 18:13
楼主 你好 我单片机使用你上面的协议设置与XCOM调试助手调试出来的结果与您的结果一样
我想让上位机也可以 ...

估计是上位机发送的是字符A5  5A而不是十六进制的A5 5A,如果你有USB转串口助手的话,把两个USB转串口都接到电脑上,然后将两个串口发送和接收连起来,用上位机通过一个串口发送数据,然后用串口调试助手打开另一个串口接收数据,看看接收的数据是否正确。
回复 支持 反对

使用道具 举报

8

主题

37

帖子

0

精华

初级会员

Rank: 2

积分
81
金钱
81
注册时间
2020-3-6
在线时间
26 小时
发表于 2020-3-27 09:50:50 | 显示全部楼层
HXYDJ 发表于 2020-3-26 20:25
估计是上位机发送的是字符A5  5A而不是十六进制的A5 5A,如果你有USB转串口助手的话,把两个USB转串口都 ...

嗯 好的 谢谢楼主
昨天已用串口监控工具查看发送与串口发回来的数据 检查是否数据发送出现了错误
发送与接受的数据一致 收到的数据格式出现了问题 我再去看看是否转换的时候出现了问题
回复 支持 反对

使用道具 举报

8

主题

37

帖子

0

精华

初级会员

Rank: 2

积分
81
金钱
81
注册时间
2020-3-6
在线时间
26 小时
发表于 2020-3-27 15:56:41 | 显示全部楼层
HXYDJ 发表于 2020-3-26 20:25
估计是上位机发送的是字符A5  5A而不是十六进制的A5 5A,如果你有USB转串口助手的话,把两个USB转串口都 ...

楼主 我想请教一下
我觉得我可能还没理解清楚你的这段代码
我如果在上位机发成 413353541303135354141  (A55A0155AA的十六进制)给单片机 单片机就没有响应      
回复 支持 反对

使用道具 举报

1

主题

882

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
3071
金钱
3071
注册时间
2018-2-7
在线时间
285 小时
 楼主| 发表于 2020-3-27 17:07:50 | 显示全部楼层
城南一偶 发表于 2020-3-27 15:56
楼主 我想请教一下
我觉得我可能还没理解清楚你的这段代码
我如果在上位机发成 413353541303135354141 ...

你把上位机的16进制好好研究研究,等你啥时候上位机发出来的数据在串口助手上用16进制显示正常的时候,说明上位机的程序就好了。
回复 支持 反对

使用道具 举报

8

主题

37

帖子

0

精华

初级会员

Rank: 2

积分
81
金钱
81
注册时间
2020-3-6
在线时间
26 小时
发表于 2020-3-27 18:25:00 | 显示全部楼层
HXYDJ 发表于 2020-3-27 17:07
你把上位机的16进制好好研究研究,等你啥时候上位机发出来的数据在串口助手上用16进制显示正常的时候,说 ...

好的 如果想要上位机可以通过获得下位机发送的0x30 这些是不是想要在单片机中进行十六进制转换啥的
回复 支持 反对

使用道具 举报

20

主题

71

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
431
金钱
431
注册时间
2020-3-24
在线时间
159 小时
发表于 2020-4-11 18:52:54 | 显示全部楼层
楼主,你这个接受完成后cnt要清零,还有错误或者接受完成缓存区值要清掉,不过楼主这个应该只是举个例子,可能没注意到
回复 支持 反对

使用道具 举报

1

主题

882

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
3071
金钱
3071
注册时间
2018-2-7
在线时间
285 小时
 楼主| 发表于 2020-4-13 08:41:17 | 显示全部楼层
SunShine9527 发表于 2020-4-11 18:52
楼主,你这个接受完成后cnt要清零,还有错误或者接受完成缓存区值要清掉,不过楼主这个应该只是举个例子, ...

谢谢指出,这个确实只是简单的写了一个例子,还有好多细节问题需要修改。主要的还是说明通信协议制定的方法和实现思维。
回复 支持 反对

使用道具 举报

1

主题

38

帖子

0

精华

高级会员

Rank: 4

积分
650
金钱
650
注册时间
2020-5-17
在线时间
121 小时
发表于 2020-7-15 09:35:34 | 显示全部楼层
很好的帖子
回复 支持 反对

使用道具 举报

3

主题

40

帖子

0

精华

初级会员

Rank: 2

积分
166
金钱
166
注册时间
2021-1-11
在线时间
30 小时
发表于 2021-7-25 17:01:23 | 显示全部楼层
好东西,谢谢楼主
向我开炮
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2024-11-24 11:22

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表