找回密码
 立即注册
查看: 78|回复: 1

【15系列】SYK-0806-A2S1 工业自动化控制之【16-自定义协议通信】

[复制链接]
  • 打卡等级:偶尔看看I
  • 打卡总天数:15
  • 最近打卡:2025-04-21 13:00:02

19

主题

9

回帖

215

积分

中级会员

积分
215
发表于 2025-4-17 15:23:53 | 显示全部楼层 |阅读模式
大家好,我是『芯知识学堂』的SingleYork,前一篇文章给大家介绍了“SYK-0806-A2S1 工业自动化控制之【15-串口收发十六进制数】”,

这一篇中,笔者继续给大家介绍跟串口通信有关的“自定义协议通信”。

在一些工业应用的场合,我们经常需要用到串口通信,既然是要通信肯定是需要相关协议的支持,业内比较标准的协议当然要数MODBUS协议了。

然而MODBUS协议要完全弄懂,也并非易事,很多时候,可能我们只需要简单控制一些输出同时读取输入输出状态,以及设置一些参数等。

如果用标准的MODBUS协议肯定是没有问题的,但是并不是所有人都能在短时间内摸透MODBUS协议。

那么,或许有人会说,自己随便写个简单的协议不就好了!

没错,这样也是可以,只要通信设备双方都按照约定好的协议去执行相关动作即可,

这一讲中,笔者就要着重介绍这种自定义协议的通信了。

说到自定义协议,笔者第一次接触的时候,还是在用迪文DGUS屏的时候,

在接触了迪文DGUS屏的指令后,笔者才学会的使用自定义协议来做一些通信。

那么,笔者就以迪文DGUS屏的指令为例,跟大家详细一下自定义协议的相关知识吧。

迪文DGUS串口数据帧的架构是由以下几个部分组成:

  1. 帧头(2字节)+ 数据长度(1字节)+ 指令(1字节)+ 数据(N字节)+ CRC校验(2字节,可选)
复制代码

所有指令都是以十六进制数发送,以写控制寄存器指令(80)为例:

假如我们需要讲触摸屏的画面“从当前页面切换到第五幅图片”,那么我们只需要通过串口向屏发送如下指令即可:

  1. 5A A5 04 80 03 00 05(此处不带CRC校验)
复制代码

那么这些十六进制数都代表什么意思呢?其含义如下:

5A A5:帧头由两个字节组成,可以自定义
04:发送数据的长度(指从指令开始到最后的数据长度,此处从80指令开始共发送4个字节)
80:写控制寄存器指令
03:控制寄存器地址
00 05:图片地址

在这条命令中,省去了CRC校验,其实在很多场合也可以用和校验的方式替代CRC校验,比如,我们可以将上述指令改成如下方式:

  1. 5A A5 04 80 03 00 05 92
复制代码

最后一个数92即是一个校验和,从数据长度位开始到最后一个数据累加求余,即得到了校验和。

当然咯,这个校验和我们可以用1个字节,也可以用2个字节,看大家使用习惯了,

笔者经常都是用1个字节来做和校验位,既然是自定义,那么肯定是你想怎么用就怎么用了,

前提是,通信两边都用一样的协议,这样大家都能通信上了,哈哈。看到这里,相信大家对于自定义协议应该有一定了解了。

其实,自定义协议确实很简单,可以自己任意定义一串数字,只要双方都按照定义好的格式收发数据即可。

接下来,笔者就一个实际的案例,在这款工控板上演示一下自定义协议通信。

该工控板上有6个输出,分别是Y0-Y5;8个输入,分别是X00-X07,

用串口助手模拟上位机来发送指令,分别控制每个输出口的输出状态,

在工控板接收到串口助手发来的指令后,根据不同指令执行相关动作,并返回此时输入输出口的状态。

首先,笔者定义串口助手发送的通信帧格式如下:

  1. 帧头(2字节)+ 长度(1字节)+ 命令(1字节)+ 控制指令(2字节)
复制代码

控制逻辑如下:

串口助手发送(十六进制):5A A5 03 06 00 00,Y00输出ON
串口助手发送(十六进制):5A A5 03 06 00 01,Y00输出OFF

串口助手发送(十六进制):5A A5 03 06 00 01,Y01输出ON
串口助手发送(十六进制):5A A5 03 06 00 11,Y01输出OFF

串口助手发送(十六进制):5A A5 03 06 00 02,Y02输出ON
串口助手发送(十六进制):5A A5 03 06 00 12,Y02输出OFF

串口助手发送(十六进制):5A A5 03 06 00 03,Y03输出ON
串口助手发送(十六进制):5A A5 03 06 00 13,Y03输出OFF

串口助手发送(十六进制):5A A5 03 06 00 04,Y04输出ON
串口助手发送(十六进制):5A A5 03 06 00 14,Y04输出OFF

串口助手发送(十六进制):5A A5 03 06 00 05,Y05输出ON
串口助手发送(十六进制):5A A5 03 06 00 15,Y05输出OFF

其中:

5A A5 – 即为帧头
03 ------ 为数据长度(从该为后面一位起数据总字节数)
06 ------ 为命令字
00 00 – 为控制指令

本例中笔者偷懒了,也省去了和校验/CRC校验,不过笔者相信,看到这里了,

大家对应该是有能力自己增加上和校验的,要是实在不知道,可以私聊笔者。

那么,发送问题是解决了,但是,要怎么接收这一串指令呢?

还是参考迪文DGUS接收数据帧的方式,首先校验帧头:5A A5,

然后再判断长度位,根据长度位来确定需要接收数据的长度,比如,此处长度位为03,

那么,我们只需要在接收到长度位之后,继续再接收完3个数据即可认为数据接收完成,具体代码实现如下:

  1. /********************* UART1中断函数************************/
  2. void UART1_int (void) interrupt UART1_VECTOR
  3. {
  4.         static bit RX_5A_OK = 0;
  5.     static bit RX_A5_OK = 0;
  6.     static u8  UART1_DataTemp = 0;
  7.    
  8.     if(RI)
  9.         {
  10.                 RI = 0;
  11.         
  12.         UART1_DataTemp = SBUF;
  13.         
  14.         if(RX_5A_OK)
  15.                 {
  16.                         if(RX_A5_OK)
  17.                         {
  18.                                 RX1_Buffer[COM1.RX_Cnt++] = UART1_DataTemp;      //将接收到的数组暂存到RX1_Buffer数组
  19.                                 
  20.                                 if(COM1.RX_Cnt == RX1_Buffer[0] + 1)                         //接收完成
  21.                                 {
  22.                                         Uart1_RX_Finish = 1;                                     //数据接收完成,将标志位置1
  23.                                         RX_5A_OK                 = 0;
  24.                                         RX_A5_OK                 = 0;
  25.                                 }
  26.                         }
  27.                         else
  28.                         {
  29.                                 if(UART1_DataTemp == 0xA5)
  30.                                 {
  31.                                         RX_A5_OK                 = 1;
  32.                                         COM1.RX_Cnt         = 0;
  33.                                 }
  34.                         }
  35.                 }
  36.                 else
  37.                 {
  38.                         if(UART1_DataTemp == 0x5A)
  39.                         {
  40.                                 RX_5A_OK = 1;
  41.                         }
  42.                 }
  43.         
  44.         if(COM1.RX_Cnt >= COM_RX1_Lenth)        COM1.RX_Cnt = 0;
  45.         }
  46.         if(TI)
  47.         {
  48.                 TI = 0;
  49.         COM1.B_TX_busy = 0;
  50.         }
  51. }
复制代码

当然,这里其实我们也可以使用结束符来实现,比如回车换行符,也很简单,只要找到回车换行对应的ASCII码的十六进制数就好了,

要是笔者没记错的话,应该是:0x0D、0x0A,那么我们可以在串口接收到0x0D、0x0A两个数据之后认为是接收完成,

当然,代码部分笔者就不再贴出来了,留给读者去完成了。

串口中断接收完数据后,会产生一个Uart1_RX_Finish接收完成标志,然后就可以开始解析数据了:

  1. /*********************     APP运行    ***********************/
  2. void app_run(void)
  3. {
  4.     static u8 cnt = 0;
  5.    
  6.     if(Uart1_RX_Finish)
  7.     {
  8.         if(!Uart1_TX_EN)
  9.         {
  10.             //根据收到的指令执行相应的动作
  11.             if((RX1_Buffer[0] == 0x03)&&(RX1_Buffer[1] == 0x06)&&(RX1_Buffer[2] == 0x00)&&(RX1_Buffer[3] == 0x00))
  12.             {
  13.                 //接收到指令(十六进制):5A A5 03 06 00 00,Y00输出ON
  14.                 Y00 = OutputT_ON;
  15.             }
  16.             if((RX1_Buffer[0] == 0x03)&&(RX1_Buffer[1] == 0x06)&&(RX1_Buffer[2] == 0x00)&&(RX1_Buffer[3] == 0x10))
  17.             {
  18.                 //接收到指令(十六进制):5A A5 03 06 00 01,Y00输出OFF
  19.                 Y00 = OutputT_OFF;
  20.             }
  21.             
  22.             if((RX1_Buffer[0] == 0x03)&&(RX1_Buffer[1] == 0x06)&&(RX1_Buffer[2] == 0x00)&&(RX1_Buffer[3] == 0x01))
  23.             {
  24.                 //接收到指令(十六进制):5A A5 03 06 00 01,Y01输出ON
  25.                 Y01 = OutputT_ON;
  26.             }
  27.             if((RX1_Buffer[0] == 0x03)&&(RX1_Buffer[1] == 0x06)&&(RX1_Buffer[2] == 0x00)&&(RX1_Buffer[3] == 0x11))
  28.             {
  29.                 //接收到指令(十六进制):5A A5 03 06 00 11,Y01输出OFF
  30.                 Y01 = OutputT_OFF;
  31.             }
  32.             
  33.             if((RX1_Buffer[0] == 0x03)&&(RX1_Buffer[1] == 0x06)&&(RX1_Buffer[2] == 0x00)&&(RX1_Buffer[3] == 0x02))
  34.             {
  35.                 //接收到指令(十六进制):5A A5 03 06 00 02,Y02输出ON
  36.                 Y02 = OutputT_ON;
  37.             }
  38.             if((RX1_Buffer[0] == 0x03)&&(RX1_Buffer[1] == 0x06)&&(RX1_Buffer[2] == 0x00)&&(RX1_Buffer[3] == 0x12))
  39.             {
  40.                 //接收到指令(十六进制):5A A5 03 06 00 12,Y02输出OFF
  41.                 Y02 = OutputT_OFF;
  42.             }
  43.             
  44.             if((RX1_Buffer[0] == 0x03)&&(RX1_Buffer[1] == 0x06)&&(RX1_Buffer[2] == 0x00)&&(RX1_Buffer[3] == 0x03))
  45.             {
  46.                 //接收到指令(十六进制):5A A5 03 06 00 03,Y03输出ON
  47.                 Y03 = OutputT_ON;
  48.             }
  49.             if((RX1_Buffer[0] == 0x03)&&(RX1_Buffer[1] == 0x06)&&(RX1_Buffer[2] == 0x00)&&(RX1_Buffer[3] == 0x13))
  50.             {
  51.                 //接收到指令(十六进制):5A A5 03 06 00 13,Y03输出OFF
  52.                 Y03 = OutputT_OFF;
  53.             }
  54.             
  55.             if((RX1_Buffer[0] == 0x03)&&(RX1_Buffer[1] == 0x06)&&(RX1_Buffer[2] == 0x00)&&(RX1_Buffer[3] == 0x04))
  56.             {
  57.                 //接收到指令(十六进制):5A A5 03 06 00 04,Y04输出ON
  58.                 Y04 = OutputT_ON;
  59.             }
  60.             if((RX1_Buffer[0] == 0x03)&&(RX1_Buffer[1] == 0x06)&&(RX1_Buffer[2] == 0x00)&&(RX1_Buffer[3] == 0x14))
  61.             {
  62.                 //接收到指令(十六进制):5A A5 03 06 00 14,Y04输出OFF
  63.                 Y04 = OutputT_OFF;
  64.             }
  65.             
  66.             if((RX1_Buffer[0] == 0x03)&&(RX1_Buffer[1] == 0x06)&&(RX1_Buffer[2] == 0x00)&&(RX1_Buffer[3] == 0x05))
  67.             {
  68.                 //接收到指令(十六进制):5A A5 03 06 00 05,Y05输出ON
  69.                 Y05 = OutputT_ON;
  70.             }
  71.             if((RX1_Buffer[0] == 0x03)&&(RX1_Buffer[1] == 0x06)&&(RX1_Buffer[2] == 0x00)&&(RX1_Buffer[3] == 0x15))
  72.             {
  73.                 //接收到指令(十六进制):5A A5 03 06 00 15,Y05输出OFF
  74.                 Y05 = OutputT_OFF;
  75.             }
  76.             
  77.             TX1_Buffer[0]  = 0x5A;    //帧头
  78.             TX1_Buffer[1]  = 0xA5;    //帧头
  79.             TX1_Buffer[2]  = 0x10;    //长度:从长度后一位开始到最后一个数据总字节数
  80.             TX1_Buffer[3]  = 0x06;    //命令0x06
  81.             
  82.             TX1_Buffer[4]  = !X00;    //X00输入状态
  83.             TX1_Buffer[5]  = !X01;    //X01输入状态
  84.             TX1_Buffer[6]  = !X02;    //X02输入状态
  85.             TX1_Buffer[7]  = !X03;    //X03输入状态
  86.             TX1_Buffer[8]  = !X04;    //X04输入状态
  87.             TX1_Buffer[9]  = !X05;    //X05输入状态
  88.             TX1_Buffer[10] = !X06;    //X06输入状态
  89.             TX1_Buffer[11] = !X07;    //X07输入状态
  90.             
  91.             TX1_Buffer[12] =  Y00;    //Y00输出状态
  92.             TX1_Buffer[13] =  Y01;    //Y01输出状态
  93.             TX1_Buffer[14] =  Y02;    //Y02输出状态
  94.             TX1_Buffer[15] =  Y03;    //Y03输出状态
  95.             TX1_Buffer[16] =  Y04;    //Y04输出状态
  96.             TX1_Buffer[17] =  Y05;    //Y05输出状态
  97.             
  98.             TX1_Buffer[18] = 0x00;    //和校验:从长度位开始(包括长度位)到校验前一位数累加和取低8位
  99.             
  100.             for(cnt=2;cnt<18;cnt++)
  101.             {
  102.                TX1_Buffer[18] += TX1_Buffer[cnt];
  103.             }
  104.             COM1.TX_write  = 0;
  105.             Uart1_TX_EN    = 1;//uart1发送数据使能置“1”,准备开始发送数据
  106.         }
  107.         else
  108.         {         
  109.             if(COM1.TX_write<19)
  110.             {
  111.                 if(COM1.B_TX_busy == 0)
  112.                 {
  113.                     COM1.B_TX_busy = 1;
  114.                     SBUF = TX1_Buffer[COM1.TX_write++];        //发送接收到的字符
  115.                 }
  116.             }
  117.             else
  118.             {
  119.                 COM1.RX_Cnt     = 0;
  120.                 Uart1_RX_Finish = 0;
  121.                 Uart1_TX_EN     = 0;//数据发送完成,uart1发送数据使能清“0”
  122.                
  123.                 memset(RX1_Buffer,NULL,sizeof(RX1_Buffer));//清空RX1_Buffer数组
  124.             }
  125.         }
  126.     }
  127. }
复制代码

这部分代码主要有两个功能,第一部分主要就是对串口发来的数据的解析,根据不同的指令,执行相关动作:

  1. //根据收到的指令执行相应的动作
  2. if((RX1_Buffer[0] == 0x03)&&(RX1_Buffer[1] == 0x06)&&(RX1_Buffer[2] == 0x00)&&(RX1_Buffer[3] == 0x00))
  3. {
  4.     //接收到指令(十六进制):5A A5 03 06 00 00,Y00输出ON
  5.     Y00 = OutputT_ON;
  6. }
  7. if((RX1_Buffer[0] == 0x03)&&(RX1_Buffer[1] == 0x06)&&(RX1_Buffer[2] == 0x00)&&(RX1_Buffer[3] == 0x10))
  8. {
  9.     //接收到指令(十六进制):5A A5 03 06 00 01,Y00输出OFF
  10.     Y00 = OutputT_OFF;
  11. }
  12. if((RX1_Buffer[0] == 0x03)&&(RX1_Buffer[1] == 0x06)&&(RX1_Buffer[2] == 0x00)&&(RX1_Buffer[3] == 0x01))
  13. {
  14.     //接收到指令(十六进制):5A A5 03 06 00 01,Y01输出ON
  15.     Y01 = OutputT_ON;
  16. }
  17. if((RX1_Buffer[0] == 0x03)&&(RX1_Buffer[1] == 0x06)&&(RX1_Buffer[2] == 0x00)&&(RX1_Buffer[3] == 0x11))
  18. {
  19.     //接收到指令(十六进制):5A A5 03 06 00 11,Y01输出OFF
  20.     Y01 = OutputT_OFF;
  21. }
  22. if((RX1_Buffer[0] == 0x03)&&(RX1_Buffer[1] == 0x06)&&(RX1_Buffer[2] == 0x00)&&(RX1_Buffer[3] == 0x02))
  23. {
  24.     //接收到指令(十六进制):5A A5 03 06 00 02,Y02输出ON
  25.     Y02 = OutputT_ON;
  26. }
  27. if((RX1_Buffer[0] == 0x03)&&(RX1_Buffer[1] == 0x06)&&(RX1_Buffer[2] == 0x00)&&(RX1_Buffer[3] == 0x12))
  28. {
  29.     //接收到指令(十六进制):5A A5 03 06 00 12,Y02输出OFF
  30.     Y02 = OutputT_OFF;
  31. }
  32. if((RX1_Buffer[0] == 0x03)&&(RX1_Buffer[1] == 0x06)&&(RX1_Buffer[2] == 0x00)&&(RX1_Buffer[3] == 0x03))
  33. {
  34.     //接收到指令(十六进制):5A A5 03 06 00 03,Y03输出ON
  35.     Y03 = OutputT_ON;
  36. }
  37. if((RX1_Buffer[0] == 0x03)&&(RX1_Buffer[1] == 0x06)&&(RX1_Buffer[2] == 0x00)&&(RX1_Buffer[3] == 0x13))
  38. {
  39.     //接收到指令(十六进制):5A A5 03 06 00 13,Y03输出OFF
  40.     Y03 = OutputT_OFF;
  41. }
  42. if((RX1_Buffer[0] == 0x03)&&(RX1_Buffer[1] == 0x06)&&(RX1_Buffer[2] == 0x00)&&(RX1_Buffer[3] == 0x04))
  43. {
  44.     //接收到指令(十六进制):5A A5 03 06 00 04,Y04输出ON
  45.     Y04 = OutputT_ON;
  46. }
  47. if((RX1_Buffer[0] == 0x03)&&(RX1_Buffer[1] == 0x06)&&(RX1_Buffer[2] == 0x00)&&(RX1_Buffer[3] == 0x14))
  48. {
  49.     //接收到指令(十六进制):5A A5 03 06 00 14,Y04输出OFF
  50.     Y04 = OutputT_OFF;
  51. }
  52. if((RX1_Buffer[0] == 0x03)&&(RX1_Buffer[1] == 0x06)&&(RX1_Buffer[2] == 0x00)&&(RX1_Buffer[3] == 0x05))
  53. {
  54.     //接收到指令(十六进制):5A A5 03 06 00 05,Y05输出ON
  55.     Y05 = OutputT_ON;
  56. }
  57. if((RX1_Buffer[0] == 0x03)&&(RX1_Buffer[1] == 0x06)&&(RX1_Buffer[2] == 0x00)&&(RX1_Buffer[3] == 0x15))
  58. {
  59.     //接收到指令(十六进制):5A A5 03 06 00 15,Y05输出OFF
  60.     Y05 = OutputT_OFF;
  61. }
复制代码

处理完相关指令后,变重新装载要发送的数据(控制板返回给串口助手的数据),

返回的数据帧格式为:

帧头(2字节)+ 长度(1字节)+ 命令(1字节)+ X00状态(1字节)+ X01状态(1字节)+ X02状态(1字节)+ X03状态(1字节)+ X04状态(1字节)+ X05状态(1字节)+ X06状态(1字节)+ X07状态(1字节)+ Y0状态(1字节)+ Y1状态(1字节)+ Y2状态(1字节)+ Y3状态(1字节)+ Y4状态(1字节)+ Y5状态(1字节),

代码如下图:

  1. TX1_Buffer[0]  = 0x5A;    //帧头
  2. TX1_Buffer[1]  = 0xA5;    //帧头
  3. TX1_Buffer[2]  = 0x10;    //长度:从长度后一位开始到最后一个数据总字节数
  4. TX1_Buffer[3]  = 0x06;    //命令0x06
  5. TX1_Buffer[4]  = !X00;    //X00输入状态
  6. TX1_Buffer[5]  = !X01;    //X01输入状态
  7. TX1_Buffer[6]  = !X02;    //X02输入状态
  8. TX1_Buffer[7]  = !X03;    //X03输入状态
  9. TX1_Buffer[8]  = !X04;    //X04输入状态
  10. TX1_Buffer[9]  = !X05;    //X05输入状态
  11. TX1_Buffer[10] = !X06;    //X06输入状态
  12. TX1_Buffer[11] = !X07;    //X07输入状态
  13. TX1_Buffer[12] =  Y00;    //Y00输出状态
  14. TX1_Buffer[13] =  Y01;    //Y01输出状态
  15. TX1_Buffer[14] =  Y02;    //Y02输出状态
  16. TX1_Buffer[15] =  Y03;    //Y03输出状态
  17. TX1_Buffer[16] =  Y04;    //Y04输出状态
  18. TX1_Buffer[17] =  Y05;    //Y05输出状态
  19. TX1_Buffer[18] = 0x00;    //和校验:从长度位开始(包括长度位)到校验前一位数累加和取低8位
  20. for(cnt=2;cnt<18;cnt++)
  21. {
  22.    TX1_Buffer[18] += TX1_Buffer[cnt];
  23. }
复制代码

当然,这里笔者写的有点繁琐,其实8个输入状态完全可以用一个字节来实现,6个输出状态也可以完全用一个字节来实现,

这里笔者只是为了让大家更好的理解这个协议,所以写的繁琐了一点。

数据装载完成,就可以将数据一个个发送出去了,这里便是第二部分的功能:

  1. if(COM1.TX_write<19)
  2. {
  3.     if(COM1.B_TX_busy == 0)
  4.     {
  5.         COM1.B_TX_busy = 1;
  6.         SBUF = TX1_Buffer[COM1.TX_write++];        //发送接收到的字符
  7.     }
  8. }
  9. else
  10. {
  11.     COM1.RX_Cnt     = 0;
  12.     Uart1_RX_Finish = 0;
  13.     Uart1_TX_EN     = 0;//数据发送完成,uart1发送数据使能清“0”
  14.    
  15.     memset(RX1_Buffer,NULL,sizeof(RX1_Buffer));//清空RX1_Buffer数组
  16. }
复制代码

发送完成之后,将接收数组中的数据清零:

  1. memset(RX1_Buffer,NULL,sizeof(RX1_Buffer));//清空RX1_Buffer数组
复制代码

接下来,我们只需要将程序下载到控制板中,再用串口助手来发送对应的指令,就能看到效果了。

好了,有关自定义协议的知识就简单介绍到这里了,有疑问的小伙伴们可以给笔者留言或者直接参与评论,

下一节笔者将给大家介绍“单片机EEPROM的应用”,详见“SYK-0806-A2S1 工业自动化控制之【17-EEPROM实现数据掉电保存】”,感谢大家的支持!

本章附件:

【STC15系列】SYK-0806-A2S1- 16-自定义协议通信.rar (72.47 KB, 下载次数: 3)




1 喜欢他/她就送朵鲜花吧,赠人玫瑰,手有余香!

本帖被以下淘专辑推荐:

回复

使用道具 举报 送花

3

主题

1160

回帖

1031

积分

等待验证会员

积分
1031
发表于 2025-4-17 15:28:01 | 显示全部楼层
SYK-0806-A2S1 工业自动化控制之【16-自定义协议通信】

大家好,我是『芯知识学堂』的SingleYork。在上一篇文章中,我们探讨了“SYK-0806-A2S1 工业自动化控制之【15-串口收发十六进制数】”,今天我们将继续深入讨论与串口通信相关的“自定义协议通信”。

在工业自动化领域,串口通信是一种常见的数据交换方式。为了实现设备间的有效通信,通常需要依赖特定的通信协议。MODBUS协议作为业内广泛采用的标准协议,虽然功能强大,但其复杂性也使得许多开发者在短时间内难以完全掌握。因此,在某些应用场景下,自定义协议成为了一种更为灵活和高效的选择。

自定义协议的必要性

MODBUS协议虽然功能全面,但在一些简单的控制任务中,如控制输出、读取输入输出状态以及设置参数等,其复杂性可能显得冗余。对于这些场景,自定义协议能够提供更为简洁和直接的解决方案。自定义协议的核心在于通信双方按照预先约定的规则进行数据交换,只要双方遵循相同的协议,即可实现有效的通信。

自定义协议的基本结构

自定义协议的设计通常包括以下几个关键部分:

1. 帧头(Header):用于标识数据帧的开始,通常为固定的字节序列。
2. 数据长度(Length):指示数据部分的字节数,便于接收方正确解析。
3. 指令(Command):定义具体的操作类型,如读取、写入等。
4. 数据(Data):包含实际的操作数据,长度可变。
5. 校验(CRC):用于验证数据的完整性,确保传输过程中没有发生错误。

以迪文DGUS屏的指令为例,其串口数据帧的架构如下:
  1. 帧头(2字节)+ 数据长度(1字节)+ 指令(1字节)+ 数据(N字节)+ CRC校验(2字节,可选)
复制代码

所有指令均以十六进制数发送。例如,若需要将触摸屏的画面从当前页面切换到第五幅图片,可以通过串口发送如下指令:
  1. 5A A5 04 80 03 00 05(此处不带CRC校验)
复制代码

自定义协议的设计与实现

在设计自定义协议时,需考虑以下几个关键因素:

1. 帧头设计:帧头应具有唯一性,以避免与其他数据混淆。通常选择不易与其他数据冲突的字节序列。
2. 数据长度字段:明确指示数据部分的长度,便于接收方正确解析。
3. 指令定义:根据具体应用需求,定义不同的指令类型,如读取、写入、状态查询等。
4. 数据格式:根据指令类型,定义数据部分的格式,确保双方能够正确解析。
5. 校验机制:引入CRC校验等机制,确保数据传输的可靠性。

实例分析

以迪文DGUS屏的指令为例,我们详细分析其自定义协议的设计与实现。

1. 帧头:5A A5,用于标识数据帧的开始。
2. 数据长度:04,表示数据部分有4个字节。
3. 指令:80,表示写控制寄存器指令。
4. 数据:03 00 05,表示将画面切换到第五幅图片。
5. CRC校验:可选,用于验证数据的完整性。

通过上述指令,我们可以实现触摸屏画面的切换操作。这种自定义协议的设计简洁明了,易于理解和实现,适用于各种工业自动化控制场景。

总结

自定义协议在工业自动化控制中具有广泛的应用前景。通过合理设计协议结构,开发者可以快速实现设备间的有效通信,满足各种控制需求。与标准协议相比,自定义协议具有更高的灵活性和可定制性,能够更好地适应特定应用场景的需求。

在未来的文章中,我们将继续探讨更多与工业自动化控制相关的技术话题,敬请期待。

感谢大家的阅读,我是SingleYork,我们下期再见!

注:本文所述内容仅供参考,具体实现需根据实际应用场景进行调整和优化。

[本文内容由国芯人工智能辅助生成,仅供参考]
回复 支持 反对

使用道具 举报 送花

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

本版积分规则

QQ|手机版|小黑屋|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2025-5-2 04:16 , Processed in 0.121777 second(s), 59 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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