大家好,我是『芯知识学堂』的SingleYork,前一篇文章给大家介绍了“SYK-0806-A2S1 工业自动化控制之【14-串口收发字符串】”,这一篇中,笔者继续给大家介绍“串口收发字十六进制数”。
有做工控项目的小伙伴们或许会发现,在很多应用场合,实际上都是用十六进制数来进行数据传递,包括MODBUS协议也是一些十六进制的数。所以,笔者觉得有必要跟大家一起分享一下如何使用串口来收发十六进制数。
有了前面的基础,相信大家对串口收发数据并不陌生了,本例笔者以单个字节的十六进制数的收发为例,进行简单介绍。
例如,笔者现在要实现这么一个功能:
上位机发送0x00,控制板Y00输出ON ,并返回接收到的数据;
上位机发送0x10,控制板Y00输出OFF,并返回接收到的数据;
上位机发送0x01,控制板Y01输出ON ,并返回接收到的数据;
上位机发送0x11,控制板Y01输出OFF,并返回接收到的数据;
上位机发送0x02,控制板Y02输出ON ,并返回接收到的数据;
上位机发送0x12,控制板Y02输出OFF,并返回接收到的数据;
上位机发送0x03,控制板Y03输出ON ,并返回接收到的数据;
上位机发送0x13,控制板Y03输出OFF,并返回接收到的数据;
上位机发送0x04,控制板Y04输出ON ,并返回接收到的数据;
上位机发送0x14,控制板Y04输出OFF,并返回接收到的数据;
上位机发送0x05,控制板Y05输出ON ,并返回接收到的数据;
上位机发送0x15,控制板Y05输出OFF,并返回接收到的数据;
看上去似乎很简单,可是,对于新手来说也未必做得写的出来。那么笔者就简单介绍一下,想要实现上述功能,该怎么实现吧。
其实代码部分很简单,我们还是在之前的SYK-0806-A2S1 工业自动化控制之【13-串口收发单个字符】代码基础上做修改,主要代码也还是在app.c文件中实现。
首先,还是一样需要对uart和gpio进行初始化:
void app_init(void)
{
UART_config(); //UART初始化
GPIO_Config(); //GPIO初始化
EA = 1; //开启总中断
}
接下来,由于逻辑也比较简单,所以笔者就偷懒直接将所有代码放到了uart1中断函数里面了:
/********************* UART1中断函数************************/
void UART1_int (void) interrupt UART1_VECTOR
{
u8 RX_Data;
if(RI)
{
RI = 0;
RX_Data = SBUF;
switch(RX_Data)
{
case 0x00:Y00 = OutputT_ON ;break;//接收到十六进制数0x00,Y00输出ON
case 0x10:Y00 = OutputT_OFF;break;//接收到十六进制数0x10,Y00输出OFF
case 0x01:Y01 = OutputT_ON ;break;//接收到十六进制数0x01,Y01输出ON
case 0x11:Y01 = OutputT_OFF;break;//接收到十六进制数0x11,Y01输出OFF
case 0x02:Y02 = OutputT_ON ;break;//接收到十六进制数0x02,Y02输出ON
case 0x12:Y02 = OutputT_OFF;break;//接收到十六进制数0x12,Y02输出OFF
case 0x03:Y03 = OutputT_ON ;break;//接收到十六进制数0x03,Y03输出ON
case 0x13:Y03 = OutputT_OFF;break;//接收到十六进制数0x13,Y03输出OFF
case 0x04:Y04 = OutputT_ON ;break;//接收到十六进制数0x04,Y04输出ON
case 0x14:Y04 = OutputT_OFF;break;//接收到十六进制数0x14,Y04输出OFF
case 0x05:Y05 = OutputT_ON ;break;//接收到十六进制数0x05,Y05输出ON
case 0x15:Y05 = OutputT_OFF;break;//接收到十六进制数0x15,Y05输出OFF
default:break;
}
SBUF = RX_Data; //发送接收到的数据
}
if(TI)
{
TI = 0;
}
}
由于是单个十六进制数的收发,因此也不需要担心收发忙的问题,关于动作的执行也很简单,笔者就用一个swtich()语句来实现了。
程序编译OK后,直接下载到板子上,通过串口助手一次发送对应的十六进制数,即可看到对应的输出ON或者OFF了。
好了,关于使用本节内容笔者就介绍到这里了,有疑问的小伙伴们可以给笔者留言或者直接参与评论,下一节笔者将继续给大家介绍“串口的基本使用”,详见“SYK-0806-A2S1 工业自动化控制之【16-自定义协议通信】”,感谢大家的支持!
本例程源码下载地址:【STC15系列】SYK-0806-A2S1 工业自动化控制之【15-串口收发十六进制数】
版权声明:本文为CSDN博主「SingleYork」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u012535488/article/details/122241558
大家好,我是『芯知识学堂』的SingleYork,前一篇文章给大家介绍了“SYK-0806-A2S1 工业自动化控制之【15-串口收发十六进制数】”,这一篇中,笔者继续给大家介绍跟串口通信有关的“自定义协议通信”。
在一些工业应用的场合,我们经常需要用到串口通信,既然是要通信肯定是需要相关协议的支持,业内比较标准的协议当然要数MODBUS协议了。
然而MODBUS协议要完全弄懂,也并非易事,很多时候,可能我们只需要简单控制一些输出同时读取输入输出状态,以及设置一些参数等。如果用标准的MODBUS协议肯定是没有问题的,但是并不是所有人都能在短时间内摸透MODBUS协议。
那么,或许有人会说,自己随便写个简单的协议不就好了!没错,这样也是可以,只要通信设备双方都按照约定好的协议去执行相关动作即可,这一讲中,笔者就要着重介绍这种自定义协议的通信了。
说到自定义协议,笔者第一次接触的时候,还是在用迪文DGUS屏的时候,在接触了迪文DGUS屏的指令后,笔者才学会的使用自定义协议来做一些通信。那么,笔者就以迪文DGUS屏的指令为例,跟大家详细一下自定义协议的相关知识吧。
迪文DGUS串口数据帧的架构是由以下几个部分组成:
帧头(2字节)+ 数据长度(1字节)+ 指令(1字节)+ 数据(N字节)+ CRC校验(2字节,可选)
所有指令都是以十六进制数发送,以写控制寄存器指令(80)为例:
假如我们需要讲触摸屏的画面“从当前页面切换到第五幅图片”,那么我们只需要通过串口向屏发送如下指令即可:
5A A5 04 80 03 00 05(此处不带CRC校验)
那么这些十六进制数都代表什么意思呢?其含义如下:
5A A5:帧头由两个字节组成,可以自定义
04:发送数据的长度(指从指令开始到最后的数据长度,此处从80指令开始共发送4个字节)
80:写控制寄存器指令
03:控制寄存器地址
00 05:图片地址
在这条命令中,省去了CRC校验,其实在很多场合也可以用和校验的方式替代CRC校验,比如,我们可以将上述指令改成如下方式:
5A A5 04 80 03 00 05 92
最后一个数92即是一个校验和,从数据长度位开始到最后一个数据累加求余,即得到了校验和。
当然咯,这个校验和我们可以用1个字节,也可以用2个字节,看大家使用习惯了,笔者经常都是用1个字节来做和校验位,既然是自定义,那么肯定是你想怎么用就怎么用了,前提是,通信两边都用一样的协议,这样大家都能通信上了,哈哈。看到这里,相信大家对于自定义协议应该有一定了解了。
其实,自定义协议确实很简单,可以自己任意定义一串数字,只要双方都按照定义好的格式收发数据即可。
接下来,笔者就一个实际的案例,在这款工控板上演示一下自定义协议通信。该工控板上有6个输出,分别是Y0-Y5;8个输入,分别是X00-X07,用串口助手模拟上位机来发送指令,分别控制每个输出口的输出状态,在工控板接收到串口助手发来的指令后,根据不同指令执行相关动作,并返回此时输入输出口的状态。
首先,笔者定义串口助手发送的通信帧格式如下:
帧头(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个数据即可认为数据接收完成,具体代码实现如下:
/********************* UART1中断函数************************/
void UART1_int (void) interrupt UART1_VECTOR
{
static bit RX_5A_OK = 0;
static bit RX_A5_OK = 0;
static u8 UART1_DataTemp = 0;
if(RI)
{
RI = 0;
UART1_DataTemp = SBUF;
if(RX_5A_OK)
{
if(RX_A5_OK)
{
RX1_Buffer[COM1.RX_Cnt++] = UART1_DataTemp; //将接收到的数组暂存到RX1_Buffer数组
if(COM1.RX_Cnt == RX1_Buffer[0] + 1) //接收完成
{
Uart1_RX_Finish = 1; //数据接收完成,将标志位置1
RX_5A_OK = 0;
RX_A5_OK = 0;
}
}
else
{
if(UART1_DataTemp == 0xA5)
{
RX_A5_OK = 1;
COM1.RX_Cnt = 0;
}
}
}
else
{
if(UART1_DataTemp == 0x5A)
{
RX_5A_OK = 1;
}
}
if(COM1.RX_Cnt >= COM_RX1_Lenth) COM1.RX_Cnt = 0;
}
if(TI)
{
TI = 0;
COM1.B_TX_busy = 0;
}
}
当然,这里其实我们也可以使用结束符来实现,比如回车换行符,也很简单,只要找到回车换行对应的ASCII码的十六进制数就好了,要是笔者没记错的话,应该是:0x0D、0x0A,那么我们可以在串口接收到0x0D、0x0A两个数据之后认为是接收完成,当然,代码部分笔者就不再贴出来了,留给读者去完成了。
串口中断接收完数据后,会产生一个Uart1_RX_Finish接收完成标志,然后就可以开始解析数据了:
/********************* APP运行 ***********************/
void app_run(void)
{
static u8 cnt = 0;
if(Uart1_RX_Finish)
{
if(!Uart1_TX_EN)
{
//根据收到的指令执行相应的动作
if((RX1_Buffer[0] == 0x03)&&(RX1_Buffer[1] == 0x06)&&(RX1_Buffer[2] == 0x00)&&(RX1_Buffer[3] == 0x00))
{
//接收到指令(十六进制):5A A5 03 06 00 00,Y00输出ON
Y00 = OutputT_ON;
}
if((RX1_Buffer[0] == 0x03)&&(RX1_Buffer[1] == 0x06)&&(RX1_Buffer[2] == 0x00)&&(RX1_Buffer[3] == 0x10))
{
//接收到指令(十六进制):5A A5 03 06 00 01,Y00输出OFF
Y00 = OutputT_OFF;
}
if((RX1_Buffer[0] == 0x03)&&(RX1_Buffer[1] == 0x06)&&(RX1_Buffer[2] == 0x00)&&(RX1_Buffer[3] == 0x01))
{
//接收到指令(十六进制):5A A5 03 06 00 01,Y01输出ON
Y01 = OutputT_ON;
}
if((RX1_Buffer[0] == 0x03)&&(RX1_Buffer[1] == 0x06)&&(RX1_Buffer[2] == 0x00)&&(RX1_Buffer[3] == 0x11))
{
//接收到指令(十六进制):5A A5 03 06 00 11,Y01输出OFF
Y01 = OutputT_OFF;
}
if((RX1_Buffer[0] == 0x03)&&(RX1_Buffer[1] == 0x06)&&(RX1_Buffer[2] == 0x00)&&(RX1_Buffer[3] == 0x02))
{
//接收到指令(十六进制):5A A5 03 06 00 02,Y02输出ON
Y02 = OutputT_ON;
}
if((RX1_Buffer[0] == 0x03)&&(RX1_Buffer[1] == 0x06)&&(RX1_Buffer[2] == 0x00)&&(RX1_Buffer[3] == 0x12))
{
//接收到指令(十六进制):5A A5 03 06 00 12,Y02输出OFF
Y02 = OutputT_OFF;
}
if((RX1_Buffer[0] == 0x03)&&(RX1_Buffer[1] == 0x06)&&(RX1_Buffer[2] == 0x00)&&(RX1_Buffer[3] == 0x03))
{
//接收到指令(十六进制):5A A5 03 06 00 03,Y03输出ON
Y03 = OutputT_ON;
}
if((RX1_Buffer[0] == 0x03)&&(RX1_Buffer[1] == 0x06)&&(RX1_Buffer[2] == 0x00)&&(RX1_Buffer[3] == 0x13))
{
//接收到指令(十六进制):5A A5 03 06 00 13,Y03输出OFF
Y03 = OutputT_OFF;
}
if((RX1_Buffer[0] == 0x03)&&(RX1_Buffer[1] == 0x06)&&(RX1_Buffer[2] == 0x00)&&(RX1_Buffer[3] == 0x04))
{
//接收到指令(十六进制):5A A5 03 06 00 04,Y04输出ON
Y04 = OutputT_ON;
}
if((RX1_Buffer[0] == 0x03)&&(RX1_Buffer[1] == 0x06)&&(RX1_Buffer[2] == 0x00)&&(RX1_Buffer[3] == 0x14))
{
//接收到指令(十六进制):5A A5 03 06 00 14,Y04输出OFF
Y04 = OutputT_OFF;
}
if((RX1_Buffer[0] == 0x03)&&(RX1_Buffer[1] == 0x06)&&(RX1_Buffer[2] == 0x00)&&(RX1_Buffer[3] == 0x05))
{
//接收到指令(十六进制):5A A5 03 06 00 05,Y05输出ON
Y05 = OutputT_ON;
}
if((RX1_Buffer[0] == 0x03)&&(RX1_Buffer[1] == 0x06)&&(RX1_Buffer[2] == 0x00)&&(RX1_Buffer[3] == 0x15))
{
//接收到指令(十六进制):5A A5 03 06 00 15,Y05输出OFF
Y05 = OutputT_OFF;
}
TX1_Buffer[0] = 0x5A; //帧头
TX1_Buffer[1] = 0xA5; //帧头
TX1_Buffer[2] = 0x10; //长度:从长度后一位开始到最后一个数据总字节数
TX1_Buffer[3] = 0x06; //命令0x06
TX1_Buffer[4] = !X00; //X00输入状态
TX1_Buffer[5] = !X01; //X01输入状态
TX1_Buffer[6] = !X02; //X02输入状态
TX1_Buffer[7] = !X03; //X03输入状态
TX1_Buffer[8] = !X04; //X04输入状态
TX1_Buffer[9] = !X05; //X05输入状态
TX1_Buffer[10] = !X06; //X06输入状态
TX1_Buffer[11] = !X07; //X07输入状态
TX1_Buffer[12] = Y00; //Y00输出状态
TX1_Buffer[13] = Y01; //Y01输出状态
TX1_Buffer[14] = Y02; //Y02输出状态
TX1_Buffer[15] = Y03; //Y03输出状态
TX1_Buffer[16] = Y04; //Y04输出状态
TX1_Buffer[17] = Y05; //Y05输出状态
TX1_Buffer[18] = 0x00; //和校验:从长度位开始(包括长度位)到校验前一位数累加和取低8位
for(cnt=2;cnt<18;cnt++)
{
TX1_Buffer[18] += TX1_Buffer[cnt];
}
COM1.TX_write = 0;
Uart1_TX_EN = 1;//uart1发送数据使能置“1”,准备开始发送数据
}
else
{
if(COM1.TX_write<19)
{
if(COM1.B_TX_busy == 0)
{
COM1.B_TX_busy = 1;
SBUF = TX1_Buffer[COM1.TX_write++]; //发送接收到的字符
}
}
else
{
COM1.RX_Cnt = 0;
Uart1_RX_Finish = 0;
Uart1_TX_EN = 0;//数据发送完成,uart1发送数据使能清“0”
memset(RX1_Buffer,NULL,sizeof(RX1_Buffer));//清空RX1_Buffer数组
}
}
}
}
这部分代码主要有两个功能,第一部分主要就是对串口发来的数据的解析,根据不同的指令,执行相关动作:
//根据收到的指令执行相应的动作
if((RX1_Buffer[0] == 0x03)&&(RX1_Buffer[1] == 0x06)&&(RX1_Buffer[2] == 0x00)&&(RX1_Buffer[3] == 0x00))
{
//接收到指令(十六进制):5A A5 03 06 00 00,Y00输出ON
Y00 = OutputT_ON;
}
if((RX1_Buffer[0] == 0x03)&&(RX1_Buffer[1] == 0x06)&&(RX1_Buffer[2] == 0x00)&&(RX1_Buffer[3] == 0x10))
{
//接收到指令(十六进制):5A A5 03 06 00 01,Y00输出OFF
Y00 = OutputT_OFF;
}
if((RX1_Buffer[0] == 0x03)&&(RX1_Buffer[1] == 0x06)&&(RX1_Buffer[2] == 0x00)&&(RX1_Buffer[3] == 0x01))
{
//接收到指令(十六进制):5A A5 03 06 00 01,Y01输出ON
Y01 = OutputT_ON;
}
if((RX1_Buffer[0] == 0x03)&&(RX1_Buffer[1] == 0x06)&&(RX1_Buffer[2] == 0x00)&&(RX1_Buffer[3] == 0x11))
{
//接收到指令(十六进制):5A A5 03 06 00 11,Y01输出OFF
Y01 = OutputT_OFF;
}
if((RX1_Buffer[0] == 0x03)&&(RX1_Buffer[1] == 0x06)&&(RX1_Buffer[2] == 0x00)&&(RX1_Buffer[3] == 0x02))
{
//接收到指令(十六进制):5A A5 03 06 00 02,Y02输出ON
Y02 = OutputT_ON;
}
if((RX1_Buffer[0] == 0x03)&&(RX1_Buffer[1] == 0x06)&&(RX1_Buffer[2] == 0x00)&&(RX1_Buffer[3] == 0x12))
{
//接收到指令(十六进制):5A A5 03 06 00 12,Y02输出OFF
Y02 = OutputT_OFF;
}
if((RX1_Buffer[0] == 0x03)&&(RX1_Buffer[1] == 0x06)&&(RX1_Buffer[2] == 0x00)&&(RX1_Buffer[3] == 0x03))
{
//接收到指令(十六进制):5A A5 03 06 00 03,Y03输出ON
Y03 = OutputT_ON;
}
if((RX1_Buffer[0] == 0x03)&&(RX1_Buffer[1] == 0x06)&&(RX1_Buffer[2] == 0x00)&&(RX1_Buffer[3] == 0x13))
{
//接收到指令(十六进制):5A A5 03 06 00 13,Y03输出OFF
Y03 = OutputT_OFF;
}
if((RX1_Buffer[0] == 0x03)&&(RX1_Buffer[1] == 0x06)&&(RX1_Buffer[2] == 0x00)&&(RX1_Buffer[3] == 0x04))
{
//接收到指令(十六进制):5A A5 03 06 00 04,Y04输出ON
Y04 = OutputT_ON;
}
if((RX1_Buffer[0] == 0x03)&&(RX1_Buffer[1] == 0x06)&&(RX1_Buffer[2] == 0x00)&&(RX1_Buffer[3] == 0x14))
{
//接收到指令(十六进制):5A A5 03 06 00 14,Y04输出OFF
Y04 = OutputT_OFF;
}
if((RX1_Buffer[0] == 0x03)&&(RX1_Buffer[1] == 0x06)&&(RX1_Buffer[2] == 0x00)&&(RX1_Buffer[3] == 0x05))
{
//接收到指令(十六进制):5A A5 03 06 00 05,Y05输出ON
Y05 = OutputT_ON;
}
if((RX1_Buffer[0] == 0x03)&&(RX1_Buffer[1] == 0x06)&&(RX1_Buffer[2] == 0x00)&&(RX1_Buffer[3] == 0x15))
{
//接收到指令(十六进制):5A A5 03 06 00 15,Y05输出OFF
Y05 = OutputT_OFF;
}
处理完相关指令后,变重新装载要发送的数据(控制板返回给串口助手的数据),返回的数据帧格式为:帧头(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字节),代码如下图:
TX1_Buffer[0] = 0x5A; //帧头
TX1_Buffer[1] = 0xA5; //帧头
TX1_Buffer[2] = 0x10; //长度:从长度后一位开始到最后一个数据总字节数
TX1_Buffer[3] = 0x06; //命令0x06
TX1_Buffer[4] = !X00; //X00输入状态
TX1_Buffer[5] = !X01; //X01输入状态
TX1_Buffer[6] = !X02; //X02输入状态
TX1_Buffer[7] = !X03; //X03输入状态
TX1_Buffer[8] = !X04; //X04输入状态
TX1_Buffer[9] = !X05; //X05输入状态
TX1_Buffer[10] = !X06; //X06输入状态
TX1_Buffer[11] = !X07; //X07输入状态
TX1_Buffer[12] = Y00; //Y00输出状态
TX1_Buffer[13] = Y01; //Y01输出状态
TX1_Buffer[14] = Y02; //Y02输出状态
TX1_Buffer[15] = Y03; //Y03输出状态
TX1_Buffer[16] = Y04; //Y04输出状态
TX1_Buffer[17] = Y05; //Y05输出状态
TX1_Buffer[18] = 0x00; //和校验:从长度位开始(包括长度位)到校验前一位数累加和取低8位
for(cnt=2;cnt<18;cnt++)
{
TX1_Buffer[18] += TX1_Buffer[cnt];
}
当然,这里笔者写的有点繁琐,其实8个输入状态完全可以用一个字节来实现,6个输出状态也可以完全用一个字节来实现,这里笔者只是为了让大家更好的理解这个协议,所以写的繁琐了一点。数据装载完成,就可以将数据一个个发送出去了,这里便是第二部分的功能:
if(COM1.TX_write<19)
{
if(COM1.B_TX_busy == 0)
{
COM1.B_TX_busy = 1;
SBUF = TX1_Buffer[COM1.TX_write++]; //发送接收到的字符
}
}
else
{
COM1.RX_Cnt = 0;
Uart1_RX_Finish = 0;
Uart1_TX_EN = 0;//数据发送完成,uart1发送数据使能清“0”
memset(RX1_Buffer,NULL,sizeof(RX1_Buffer));//清空RX1_Buffer数组
}
发送完成之后,将接收数组中的数据清零:
memset(RX1_Buffer,NULL,sizeof(RX1_Buffer));//清空RX1_Buffer数组
接下来,我们只需要将程序下载到控制板中,再用串口助手来发送对应的指令,就能看到效果了。好了,有关自定义协议的知识就简单介绍到这里了,有疑问的小伙伴们可以给笔者留言或者直接参与评论,下一节笔者将给大家介绍“单片机EEPROM的应用”,详见“SYK-0806-A2S1 工业自动化控制之【17-EEPROM实现数据掉电保存】”,感谢大家的支持!
本例程源码下载地址:【STC15系列】SYK-0806-A2S1 工业自动化控制之【16-自定义协议通信】
版权声明:本文为CSDN博主「SingleYork」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u012535488/article/details/122245666
暂无评论