MODBUS协议
2023-12-15 14:17:06
一、概念
????????modbus是一个公开免费的协议,广泛应用于工业控制领域(PLC和仪器,PLC和PLC,PLC和上位机,PLC和触摸屏等等,其中PLC是可控制逻辑单元)
? ? ? ? 他有两种物理接口(硬件协议),一个是串口(RS232,RS485,RS422),一个是以太网。串口主要用于modbus RTU或者是modbus ascii模式,而以太网主要用于modbus tcp协议。
? ? ? ? 一般的通信方式是:主机广播或者单播发送指令,从机分析请求,并且给主机应答(如果出错就返回异常功能码)。从机只能响应主机,不能主动发送数据
二、功能码0x01-读取线圈中的数据
主机发送,读线圈数据(注意:数据以16进制格式发送)
从机地址 | 功能码 | 寄存器起始地址高字节 | 寄存器起始地址低字节 | 寄存器数量高字节 | 寄存器数量低字节 | CRC校验高字节 | CRC校验低字节 |
从机响应?(注意:接收数据也是16进制)
从机地址 | 功能码 | 返回数据长度(一个字节,最多是255个数据) | 数据 | CRC校验高字节 | CRC校验低字节 |
举例:
主机:01 01 00 00 00 08 3D CC
从机:01 01 01 21 91 90
三、功能码0x03读保持寄存器的数据
typedef struct
{
//作为从机时使用
u8 myadd; //本设备从机地址
u8 rcbuf[100]; //modbus接受缓冲区
u8 timout; //modbus数据持续时间
u8 recount; //modbus端口接收到的数据个数
u8 timrun; //modbus定时器是否计时标志
u8 reflag; //modbus一帧数据接受完成标志位
u8 sendbuf[100]; //modbus接发送缓冲区
//作为主机添加部分
u8 Host_Txbuf[8]; //modbus发送数组
u8 slave_add; //要匹配的从机设备地址(做主机实验时使用)
u8 Host_send_flag;//主机设备发送数据完毕标志位
int Host_Sendtime;//发送完一帧数据后时间计数
u8 Host_time_flag;//发送时间到标志位,=1表示到发送数据时间了
u8 Host_End;//接收数据后处理完毕
}MODBUS;
//参数1从机地址,参数2起始地址,参数3寄存器个数
void Host_send03(uint8_t slave,uint16_t StartAddr,uint16_t num)
{
int j;
uint16_t crc;//计算的CRC校验位
modbus.slave_add=slave;//这是先把从机地址存储下来,后面接收数据处理时会用到
modbus.Host_Txbuf[0]=slave;//这是要匹配的从机地址
modbus.Host_Txbuf[1]=0x03;//功能码
modbus.Host_Txbuf[2]=StartAddr/256;//起始地址高位
modbus.Host_Txbuf[3]=StartAddr%256;//起始地址低位
modbus.Host_Txbuf[4]=num/256;//寄存器个数高位
modbus.Host_Txbuf[5]=num%256;//寄存器个数低位
crc=Modbus_CRC16(&modbus.Host_Txbuf[0],6); //获取CRC校验位
modbus.Host_Txbuf[6]=crc/256;//CRC校验高位
modbus.Host_Txbuf[7]=crc%256;//CRC校验低位
//开始发送数据
RS485_TX_ENABLE;//使能485控制端(启动发送)
for(j=0;j<i;j++)
{
Modbus_Send_Byte(modbus.sendbuf[j]);
}
RS485_RX_ENABLE;//失能485控制端(改为接收)
}
//主机接收从机的消息进行处理功能码0x03
void HOST_receive03()
{
u16 crc,rccrc;//计算crc和接收到的crc
if(modbus.reflag == 0) //如果接收未完成则返回空
{
return;
}
//(数组中除了最后两位CRC校验位其余全算)
crc = Modbus_CRC16(&modbus.rcbuf[0],modbus.recount-2); //获取CRC校验位
rccrc = modbus.rcbuf[modbus.recount-2]*256+modbus.rcbuf[modbus.recount-1];//计算读取的CRC校验位
if(crc == rccrc) //CRC检验成功 开始分析包
{
if(modbus.rcbuf[0] == modbus.slave_add) // 检查地址是是对应从机发过来的
{
if(modbus.rcbuf[1]==3)//功能码时03
{
int i;
int count=(int)modbus.rcbuf[2];//这是数据个数
printf("从机返回 %d 个寄存器数据:\r\n",count/2);
for(i=0;i<count;i=i+2)
{
printf("data%d= %d\r\n",i+1,(int)modbus.rcbuf[4+i]+((int)modbus.rcbuf[3+i])*256);
}
}
}
}
}
modbus.recount = 0;//接收计数清零
modbus.reflag = 0; //接收标志清零
}
?
四、功能码0x06向一个寄存器中写入数据
void Host_send06(uint8_t slave,uint16_t Addr,uint16_t data)
{
uint16_t crc,j;//计算的CRC校验位
modbus.slave_add=slave;//从机地址赋值一下,后期有用
modbus.Host_Txbuf[0]=slave;//这是要匹配的从机地址
modbus.Host_Txbuf[1]=0x06;//功能码
modbus.Host_Txbuf[2]=Addr/256;//写入寄存器地址高位
modbus.Host_Txbuf[3]=Addr%256;//写入寄存器低位
modbus.Host_Txbuf[4]=data/256;//写入数据高位
modbus.Host_Txbuf[5]=data%256;//写入数据低位
crc=Modbus_CRC16(&modbus.Host_Txbuf[0],6); //获取CRC校验位
modbus.Host_Txbuf[6]=crc/256;//CRC校验高位
modbus.Host_Txbuf[7]=crc%256;//CRC校验低位
//开始发送数据
RS485_TX_ENABLE;//使能485控制端(启动发送)
for(j=0;j<i;j++)
{
Modbus_Send_Byte(modbus.sendbuf[j]);
}
RS485_RX_ENABLE;//失能485控制端(改为接收)
}
//从机返回数据
void Host_receive06()
{
int crc,rccrc;
crc = Modbus_CRC16(&modbus.rcbuf[0],6); //获取CRC校验位
rccrc = modbus.rcbuf[6]*256+modbus.rcbuf[7];//计算读取的CRC校验位
if(crc == rccrc) //CRC检验成功 开始分析包
{
if(modbus.rcbuf[0] == modbus.slave_add) // 检查地址是是对应从机发过来的
{
if(modbus.rcbuf[1]==6)//功能码时06
{
printf("地址为 %d 的从机寄存器 %d 中写入数据 %d \r\n ",(int)modbus.rcbuf[0],(int)modbus.rcbuf[3]+((int)modbus.rcbuf[2])*256,(int)modbus.rcbuf[5]+((int)modbus.rcbuf[4])*256);
}
}
}
}
五、功能码0x10-多个寄存器写入数据?
?
? ? ? ? 理解加模仿,然后自己写功能吗0x10的代码
?
六、主机接收从机所有数据合并函数
void Modbus_receiveAll()
{
u16 crc,rccrc;//crc和接收到的crc
if(modbus.reflag == 0) //如果接收未完成则返回空
{
return;
}
crc = Modbus_CRC16(&modbus.rcbuf[0],modbus.recount-2); //获取CRC校验位
rccrc = modbus.rcbuf[modbus.recount-2]*256+modbus.rcbuf[modbus.recount-1];//计算读取的CRC校验位
if(crc == rccrc) //CRC检验成功 开始分析包
{
if(modbus.rcbuf[0] == modbus.myadd) // 检查地址是否时自己的地址
{
switch(modbus.rcbuf[1]) //分析modbus功能码
{
case 3: //处理读保存寄存器的数据的代码 break;
case 6: //对应代码 break;
case 16: //对应代码 break;
}
}
else if(modbus.rcbuf[0] == 0) //广播地址不予回应
{
}
}
modbus.recount = 0;//接收计数清零
modbus.reflag = 0; //接收标志清零
}
文章来源:https://blog.csdn.net/m0_61973119/article/details/134940067
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!