一、通讯协议:
1、通讯传送方式:
通讯传送分为独立的信息头,和发送的编码数据。以下的通讯传送方式定义也与MODBUS RTU通讯规约相兼容:
编 码 8位二进制
起始位 1位
数据位 8位
奇偶校验位 1位(偶校验位)
停止位 1位
错误校检 CRC(冗余循环码)
初始结构 = ≥4字节的时间
地址码 = 1 字节
功能码 = 1 字节
数据区 = N 字节
错误校检 = 16位CRC码
结束结构 = ≥4字节的时间
地址码:地址码为通讯传送的第一个字节。这个字节表明由用户设定地址码的从机将接收由主机发送来的信息。并且每个从机都有具有唯一的地址码,并且响应回送均以各自的地址码开始。主机发送的地址码表明将发送到的从机地址,而从机发送的地址码表明回送的从机地址。
功能码:通讯传送的第二个字节。ModBus通讯规约定义功能号为1到127。本仪表只利用其中的一部分功能码。作为主机请求发送,通过功能码告诉从机执行什么动作。作为从机响应,从机发送的功能码与从主机发送来的功能码一样,并表明从机已响应主机进行操作。如果从机发送的功能码的最高位为1(比如功能码大与此同时127),则表明从机没有响应操作或发送出错。
数据区:数据区是根据不同的功能码而不同。数据区可以是实际数值、设置点、主机发送给从机或从机发送给主机的地址
-
-
-
wj321 发表于 2008/10/24 16:13:52
据N
CRC H
CRC L
Addr1
3 H
返回数据的字节数N
Data (1~N)
CRC高位
CRC低位
帧 长 度:5+N 个字节
设备地址:1~247
功 能 码:3H
数 据 量:实际的读取数据数量
数 据:返回数据的意义
a=HoldStart
n= DataNum-1
VW a (VB a)
VWa(VB a+1)
…
VW a+n(VB a+n)
VWa+n(VB a+n+1)
Data(1)
Data(2)
…
Data(N-1)
Data(N)
校 验 码:CRC16校验
命令有误:
1) 没有任何返回
2) 返回异议帧
设备地址
功能码
错误信息
CRC H
CRC L
Addr1
83 H
一个字节的错误信息
CRC高位
CRC低位
引用 wj321 2008/10/24 16:13:52 发表于2楼的内容
-
-
-
-
wj321 发表于 2008/10/24 16:17:09
uint8 testCoil; //用于测试 位地址1
uint16 testRegister; //用于测试 字地址0uint8 localAddr = 1; //地址
uint8 sendCount; //发送字节个数
uint8 receCount; //接收字节个数
uint8 sendPosi; //发送位置
uint8 sendBuf[32],receBuf[32]; //发送,接收缓冲区
uint8 checkoutError; //校验结果
uint8 receTimeOut; //接收超时//CRC较验计算
uint16 crc16(uint8 *puchMsg, uint16 usDataLen)
{
uint8 uchCRCHi = 0xFF ; /* 高CRC字节初始化 */
uint8 uchCRCLo = 0xFF ; /* 低CRC 字节初始化 */
uint32 uIndex ; /* CRC循环中的索引 */
while (usDataLen--) /* 传输消息缓冲区 */
{
uIndex = uchCRCHi ^ *puchMsg++ ; /* 计算CRC */
uchCRCHi = uchCRCLo ^ auchCRCHi[uIndex] ;
uchCRCLo = auchCRCLo[uIndex] ;
}
return (uchCRCHi << 8 | uchCRCLo) ;
}//uint16 crc16(uint8 *puchMsg, uint16 usDataLen)
//MODBUS状态机,在主程序中调用即可
void checkComm0Modbus(void)
{
uint16 crcData;
uint16 tempData;
if(receCount > 4)
{
switch(receBuf[1])
{// 其它的功能码可以参照协议自行编写
case 1://读取线圈状态(读取点 16位以内)
case 3://读取保持寄存器(一个或多个)
case 5://强制单个线圈
case 6://设置单个寄存器
if(receCount >= 8)
{//接收完成一组数据
//应该关闭接收中断
UCSRB &= ~BIT(7);
if(receBuf[0]==localAddr && checkoutError==0)
{
crcData = crc16(receBuf,6);
if(crcData == receBuf[7]+(receBuf[6]<<8))
{//校验正确
if(receBuf[1] == 1)
{//读取线圈状态(读取点 16位以内)
readCoil();
}
else if(receBuf[1] == 3)
{//读取保持寄存器(一个或多个)
readRegisters();
}
else if(receBuf[1] == 5)
{//强制单个线圈
forceSingleCoil();
}
else if(receBuf[1] == 6)
{
//presetSingleRegister();
}}
}
receCount = 0;
checkoutError = 0;
UCSRB |= BIT(7);
}
break;
case 15://设置多个线圈
tempData = receBuf[6];
tempData += 9; //数据个数
if(receCount >= tempData)
{//应该关闭接收中断
UCSRB &= ~BIT(7);
if(receBuf[0]==localAddr && checkoutError==0)
{
crcData = crc16(receBuf,tempData-2);
if(crcData == (receBuf[tempData-2]<<8)+ receBuf[tempData-1])
{
//forceMultipleCoils();
}
}
receCount = 0;
checkoutError = 0;
UCSRB |= BIT(7);
}
break;
case 16://设置多个寄存器
tempData = (receBuf[4]<<8) + receBuf[5];
tempData = tempData * 2; //数据个数
tempData += 9;
if(receCount >= tempData)
{//应该关闭接收中断
UCSRB &= ~BIT(7);
if(receBuf[0]==localAddr && checkoutError==0)
{
crcData = crc16(receBuf,tempData-2);
if(crcData == (receBuf[tempData-2]<<8)+ receBuf[tempData-1])
{
presetMultipleRegisters();
}
}
receCount = 0;
checkoutError = 0;
UCSRB |= BIT(7);
}
break;
default:
break;
}
}
}
引用 wj321 2008/10/24 16:17:09 发表于3楼的内容
-