Modbus主机如何采集Modbus从机所测得的数据

2019-10-16 00:35发布

我现在是 Modbus从机可以准确的采集SHT10的温湿度,MQ-2气体,和光照指标 上位机用 Modbus Poll显示。
现在想 用另一个单片机  采集到从机的这些环境指标 发送至串口助手 怎么办?
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
23条回答
song15032
1楼-- · 2019-10-16 01:03
 精彩回答 2  元偷偷看……
song15032
2楼-- · 2019-10-16 06:14
  我做完从机,采集完环境的数据,可以用Modbus Poll作为主机来显示从机的数是多少。所以我就不用关心主机是怎么工作的,是吧。 现在不用Modbus Poll做主机了,用另外一个单片机做主机,主机怎么才能收到从机采集的数呢? 有没有Modbus主从能通上信的小例程。谢谢。
song15032
3楼-- · 2019-10-16 08:25
yklstudent 发表于 2016-7-19 13:19
你要做的就是做个MODBUS协议主机,周期读取MODBUS协议从机的内容;

是啊,可是主机的Modbus协议怎么读取呢。 这是我从机部分 Modbus内容:
[mw_shl_code=c,true]/*******************************串口发送函数 ********************************/
void Begin_send(void)
{
        DE_OUT;      //处于发送
        uartsends(sendBuf,sendCount);
        DE_IN;              //发送完后将485置于接收状态
  receCount = 0;   //清接收地址偏移寄存器
}


//fuction:01 读单个或多个线圈状态
void readCoils(void)
{
uint addr;
uint tempAddr;
uint byteCount;
uint  bitCount;
uint crcData;
uint  position;
uint  i,k;
uint tempData;
uchar  exit = 0;
addr = (receBuf[2]<<8) + receBuf[3];
tempAddr = addr;
bitCount = (receBuf[4]<<8) + receBuf[5]; //读取的位个数
byteCount = bitCount / 8;    //字节个数
if(bitCount%8 != 0)
  byteCount++;      
  for(k=0;k<byteCount;k++)//字节位置
  {
    position = k + 3;
    sendBuf[position] = 0;
    for(i=0;i<8;i++)
    {
      getCoilVal(tempAddr,&tempData);
      sendBuf[position] |= tempData << i;
      tempAddr++;
      if(tempAddr >= addr+bitCount)//读完
      {
        exit = 1;
        break;
      }
    }
    if(exit == 1)
    break;
  }
  sendBuf[0] = LocalAddr;
  sendBuf[1] = 0x01;
  sendBuf[2] = byteCount;
  byteCount += 3;
  crcData = crc16(sendBuf,byteCount);
  sendBuf[byteCount] = crcData & 0xff;
  byteCount++;
  sendBuf[byteCount] = crcData >> 8;
  sendCount = byteCount + 1;
  Begin_send();   
}

//fuction:02 读取线圈输入(只读寄存器)状态
void readInPutCoils(void)
{
uint addr;
uint tempAddr;
uint byteCount;
uint  bitCount;
uint crcData;
uint  position;
uint  i,k;
uint tempData;
uchar  exit = 0;
addr = (receBuf[2]<<8) + receBuf[3];
tempAddr = addr +10000;        //只读线圈寄存器偏移地址10000
bitCount = (receBuf[4]<<8) + receBuf[5]; //读取的位个数
byteCount = bitCount / 8;    //字节个数
if(bitCount%8 != 0)
  byteCount++;      
  for(k=0;k<byteCount;k++)//字节位置
  {
    position = k + 3;
    sendBuf[position] = 0;
    for(i=0;i<8;i++)
    {
      getCoilVal(tempAddr,&tempData);
      sendBuf[position] |= tempData << i;
      tempAddr++;
      if(tempAddr >= addr+10000+bitCount)//读完
      {
        exit = 1;
        break;
      }
    }
    if(exit == 1)
    break;
  }
  sendBuf[0] = LocalAddr;
  sendBuf[1] = 0x02;
  sendBuf[2] = byteCount;
  byteCount += 3;
  crcData = crc16(sendBuf,byteCount);
  sendBuf[byteCount] = crcData & 0xff;
  byteCount++;
    sendBuf[byteCount] = crcData >> 8;
  sendCount = byteCount + 1;
  Begin_send();   
}

/********function code : 03,读取多个寄存器值 ********/

void readRegisters(void)
{
        uint addr;
        uint tempAddr;
        uint crcData;
        uint readCount;
        uint byteCount;
        uint i;
        uint tempData = 0;
        addr = (receBuf[2]<<8) + receBuf[3];
        tempAddr = addr+40000;      //+40000,保持寄存器偏移地址
        readCount = (receBuf[4]<<8) + receBuf[5]; //要读的个数 ,整型
        byteCount = readCount * 2;                  //每个寄存器内容占高,低两个字节
        for(i=0;i<byteCount;i+=2,tempAddr++)
        {
              getRegisterVal(tempAddr,&tempData);   
              sendBuf[i+3] = tempData >> 8;        
              sendBuf[i+4] = tempData & 0xff;  
        }
        sendBuf[0] = LocalAddr;
        sendBuf[1] = 3;  //function code : 03
        sendBuf[2] = byteCount;
        byteCount += 3;             //加上前面的地址,功能码,地址 共3+byteCount个字节
        crcData = crc16(sendBuf,byteCount);
        sendBuf[byteCount] = crcData & 0xff;   // CRC代码低位在前
        byteCount++;
        sendBuf[byteCount] = crcData >> 8 ;           //高位在后
        sendCount = byteCount + 1;                        //例如byteCount=49,则sendBuf[]中实际上有49+1个元素待发
        Begin_send();
}
//fuction 04:读取输入寄存器
void readInPutRegisters(void)
{
        uint addr;
        uint tempAddr;
        uint crcData;
        uint readCount;
        uint byteCount;
        uint i;
        uint tempData = 0;
        addr = (receBuf[2]<<8) + receBuf[3];
        tempAddr = addr+30000;  //+输入寄存器偏移地址:30000
        readCount = (receBuf[4]<<8) + receBuf[5]; //要读的个数 ,整型
        byteCount = readCount * 2;                  //每个寄存器内容占高,低两个字节
        for(i=0;i<byteCount;i+=2,tempAddr++)
        {
              getRegisterVal(tempAddr,&tempData);   
              sendBuf[i+3] = tempData >> 8;        
              sendBuf[i+4] = tempData & 0xff;  
        }
        sendBuf[0] = LocalAddr;
        sendBuf[1] = 4;  //function code : 04
        sendBuf[2] = byteCount;
        byteCount += 3;             //加上前面的地址,功能码,地址 共3+byteCount个字节
        crcData = crc16(sendBuf,byteCount);
        sendBuf[byteCount] = crcData & 0xff;   // CRC代码低位在前
        byteCount++;
        sendBuf[byteCount] = crcData >> 8 ;           //高位在后
        sendCount = byteCount + 1;                        //例如byteCount=49,则sendBuf[]中实际上有49+1个元素待发
        Begin_send();
}
         
//fuction:05 ,强制单个线圈
void forceSingleCoil(void)
{
uint addr;
uint tempAddr;
uint tempData;
uint  onOff;
uchar i;
addr = (receBuf[2]<<8) + receBuf[3];
tempAddr = addr;
onOff = (receBuf[4]<<8) + receBuf[5];
if(onOff == 0xff00)
{
  tempData = 1;//设为ON
}
else if(onOff == 0x0000)//设为OFF
{
  tempData = 0;
}
setCoilVal(tempAddr,tempData);
for(i=0;i<receCount;i++)
{
  sendBuf = receBuf;
}
sendCount = receCount;
Begin_send();
}

/****************fuction:06设置单个寄存器ok**********************************************************/

//主机器发送  地址,功能码,寄存器高位,寄存器低位,数据数高位, 数数低位,CRC低位,CRC高位
//例如设置D0 发送,        01      06         00            3F       00                         01                   */
/*********************************************************************************/

void presetSingleRegister(void)
{
uint addr;
uint tempAddr;
uint tempData;
uint crcData;
addr = (receBuf[2]<<8) + receBuf[3];
tempAddr = (addr+40000);
tempData = (receBuf[4]<<8) + receBuf[5];
setRegisterVal(tempAddr,tempData);  
sendBuf[0] = LocalAddr;
sendBuf[1] = 6;    //function code : 16
sendBuf[2] = addr >> 8;  //寄存器地址高位
sendBuf[3] = addr & 0xff;//寄存器地址低位
sendBuf[4] =receBuf[4];
sendBuf[5] =receBuf[5];
crcData = crc16(sendBuf,6);//生成CRC校验码
sendBuf[6] = crcData & 0xff;  //CRC代码低位在前
sendBuf[7] = crcData >> 8;          //高位在后
sendCount = 8;
Begin_send();
}

/********从机响应主机问询函数,function code : 15,强置多线圈值 *********/
//////////////询问数据包格式:
///////////////////////// receBuf[0] receBuf[1]  receBuf[2]     receBuf[3]   receBuf[4]    receBuf[5]   receBuf[6]  receBuf[7]  receBuf[8] ... receBuf[9]   receBuf[10]
//询问数据格式:receBuf[]={从站地址, 功能码,  起始地址高位,起始地址低位,寄存器数高位,寄存器数低位, 字节计数,  数据高位,  数据低位,... 校验码低位, 校验码高位}
/****************************************************************************/
void forceMultipleCoils(void)
{
uint addr;
uint tempAddr;
uint byteCount;
uint  bitCount;
uint crcData;
uint tempData;
uint  i,k;
uchar  exit = 0;
addr = (receBuf[2]<<8) + receBuf[3];
tempAddr = addr;
bitCount = (receBuf[4]<<8) + receBuf[5];
byteCount = bitCount / 8;    //字节个数
if(bitCount%8 != 0)
  byteCount++;     
  for(k=0;k<byteCount;k++)//字节位置
  {
    for(i=0;i<8;i++)
    {
      tempData = (receBuf[k+3] >>i)&0x01;  
      setCoilVal(tempAddr,tempData);
      tempAddr++;
      if(tempAddr >= addr+bitCount)//读完
      {
        exit = 1;
        break;
      }
    }
  
    if(exit == 1)
    break;
  }
sendBuf[0] = LocalAddr;
sendBuf[1] = 15;    //function code : 16
sendBuf[2] = addr >> 8;  //寄存器地址高位
sendBuf[3] = addr & 0xff;//寄存器地址低位
sendBuf[4] = bitCount >> 8;//待设置寄存器数量高位
sendBuf[5] = bitCount & 0xff;//待设置寄存器数量低位
crcData = crc16(sendBuf,6);//生成CRC校验码
sendBuf[6] = crcData & 0xff;  //CRC代码低位在前
sendBuf[7] = crcData >> 8;          //高位在后
sendCount = 8;
Begin_send();
}

/********从机响应主机问询函数,function code : 16,设置多个寄存器值 *********/
//////////////询问数据包格式:
///////////////////////// receBuf[0] receBuf[1]  receBuf[2]     receBuf[3]   receBuf[4]    receBuf[5]   receBuf[6]  receBuf[7]  receBuf[8] ... receBuf[9]   receBuf[10]
//询问数据格式:receBuf[]={从站地址, 功能码,  起始地址高位,起始地址低位,寄存器数高位,寄存器数低位, 字节计数,  数据高位,  数据低位,... 校验码低位, 校验码高位}
/****************************************************************************/
void presetMultipleRegisters(void)
{
uint addr;
uint tempAddr;
uint setCount;
uint crcData;
uint tempData;
uchar i;
addr = (receBuf[2]<<8) + receBuf[3];
tempAddr = addr+40000;
setCount = (receBuf[4]<<8) + receBuf[5];
for(i=0;i<setCount;i++,tempAddr++)
{
  tempData = (receBuf[i*2+7]<<8) + receBuf[i*2+8];//待设置寄存器值
  setRegisterVal(tempAddr,tempData);  
}
sendBuf[0] = LocalAddr;
sendBuf[1] = 16;    //function code : 16
sendBuf[2] = addr >> 8;  //寄存器地址高位
sendBuf[3] = addr & 0xff;//寄存器地址低位
sendBuf[4] = setCount >> 8;//待设置寄存器数量高位
sendBuf[5] = setCount & 0xff;//待设置寄存器数量低位
crcData = crc16(sendBuf,6);//生成CRC校验码
sendBuf[6] = crcData & 0xff;  //CRC代码低位在前
sendBuf[7] = crcData >> 8;          //高位在后
sendCount = 8;
Begin_send();
}


/*************************查询uart接收的数据包内容函数 **************************/
////函数功能:丛机根据串口接收到的数据包receBuf[1]里面的内容,即function code执行相应的命令
/********************************************************************************/
void checkComm0Modbus(void)                   //10ms内必须响应接收数据
{
uint crcData;
uint tempData;
uint temp;
if(receCount > 4)                 //如果接收到数据
{         
  switch(receBuf[1])
  {
case 1:                                                                //读取寄存器(一个或多个)
   {         
      if(receCount >= 8)  //从询问数据包格式可知,receCount应该等于8        ,接收完成一组数据应该关闭接收中断
        {            
          if(receBuf[0]==LocalAddr)   //核对地址
            {         
              crcData = crc16(receBuf,6);                     //核对校验码
              temp=receBuf[7];
              temp=(temp<<8)+receBuf[6];
              if(crcData == temp)
              if(receBuf[1] == 1)
                {
                readCoils();                        //读取线圈输出状态(一个或多个)
                }
                receCount = 0;                                                                           
             }       
          
        }      
          break;
      }
case 2:                                                                //读取寄存器(一个或多个)
   {         
      if(receCount >= 8)  //从询问数据包格式可知,receCount应该等于8        ,接收完成一组数据应该关闭接收中断
        {            
          if(receBuf[0]==LocalAddr)   //核对地址
            {         
              crcData = crc16(receBuf,6);                     //核对校验码
              temp=receBuf[7];
              temp=(temp<<8)+receBuf[6];
              if(crcData == temp)
              if(receBuf[1] == 2)
                {
                readInPutCoils();                        //读取线圈输入状态(一个或多个)
                }
                                                        receCount = 0;                                                                           
             }       
          
        }      
          break;
      }   
   case 3:                                                                //读取寄存器(一个或多个)
   {         
      if(receCount >= 8)  //从询问数据包格式可知,receCount应该等于8        ,接收完成一组数据应该关闭接收中断
        {            
          if(receBuf[0]==LocalAddr)   //核对地址
            {         
              crcData = crc16(receBuf,6);                     //核对校验码
              temp=receBuf[7];
              temp=(temp<<8)+receBuf[6];
              if(crcData == temp)
              if(receBuf[1] == 3)
              {
                readRegisters();                        //读取保持寄存器(一个或多个)
               }
                                                        receCount = 0;                                                                           
            }                  
        }      
        break;
   }
case 4:                                                                //读取寄存器(一个或多个)
   {         
      if(receCount >= 8)  //从询问数据包格式可知,receCount应该等于8        ,接收完成一组数据应该关闭接收中断
      {            
          if(receBuf[0]==LocalAddr)   //核对地址
          {         
            crcData = crc16(receBuf,6);                     //核对校验码
            temp=receBuf[7];
            temp=(temp<<8)+receBuf[6];
            if(crcData == temp)
            if(receBuf[1] == 4)
              {
              readInPutRegisters();                        //读取输入寄存器(一个或多个)
              }
                                                receCount = 0;                                                                           
          }       
          
      }      
          break;
    }
   case 5:                                                                //读取寄存器(一个或多个)
   {         
      if(receCount >= 8)  //从询问数据包格式可知,receCount应该等于8        ,接收完成一组数据应该关闭接收中断
      {            
          if(receBuf[0]==LocalAddr)   //核对地址
          {         
            crcData = crc16(receBuf,6);                     //核对校验码
            temp=receBuf[7];
            temp=(temp<<8)+receBuf[6];
            if(crcData == temp)
            if(receBuf[1] == 5)
              {
              forceSingleCoil();                        //强置单个线圈 状态
              }
                                                receCount = 0;                                                                           
          }       
          
      }      
          break;
    }
case 6: if(receCount >= 8)
           {   
             if(receBuf[0]==LocalAddr)
                  {
                    crcData = crc16(receBuf,6);
                    temp=receBuf[7];
                    temp=(temp<<8)+receBuf[6];
                    if(crcData == temp)
                    if(receBuf[1] == 6)
                      {
                          presetSingleRegister();      //预置单个保持寄存器
                      }
                    receCount = 0;                                                                                                                                         
                 }         
          }
                 break;
case 15://设置多个线圈
      tempData = receBuf[6];
      tempData += 9; //数据个数
      if(receCount >= tempData)
      {
          if(receBuf[0]==LocalAddr )
            {
                crcData = crc16(receBuf,tempData-2);
              if(crcData == (receBuf[tempData-1]<<8)+ receBuf[tempData-2])//更改了??
                {
                  forceMultipleCoils();  
                }
            }
                  receCount = 0;
        }
      break;        
  case 16:                                                //设置多个寄存器
   {
        tempData = (receBuf[4]<<8) + receBuf[5];                 //设置寄存器个数
        tempData = tempData * 2;                                                 //数据个数=        寄存器*2
        tempData += 9;       //从询问数据包格式可知,receCount应该等于9+byteCount
        if(receCount >= tempData)
          {         
            if(receBuf[0]==LocalAddr )        //核对地址
                {         
                  crcData = crc16(receBuf,tempData-2);
                     temp=receBuf[tempData-1];
                  temp=(temp<<8)+receBuf[tempData-2];
                  if(crcData == temp)
                    {  
                    presetMultipleRegisters();  
                    }
                }
              receCount = 0;
            }
        break;
      }   
  default: break;  
  }
}
}

/********************以下为移植的数据修改区**************************************************/
//取线圈状态 返回0表示成功
uint getCoilVal(uint addr,uint *tempData)
{
uint tempAddr;
tempAddr = addr;//只取低8位地址
switch(tempAddr)
{
//  case RWSTATUES0:      *tempData = RWstatus0; break;
  default:  break;     
}
return  0;
}

uint setCoilVal(uint addr,uint tempData)//设定线圈状态 返回0表示成功
{
uint tempAddr;
tempAddr = addr;
switch(tempAddr)
{
//  case RWSTATUES0:   RWstatus0= tempData; break;

  default: break;  
}
return 0;
}
       

/*******************************读取寄存器内容函数 返回0表示成功**************************/
uint getRegisterVal(uint addr,uint *tempData)
{
switch(addr)                                                                 
{
case WEIDU: { *tempData =Temperature ; break; }
case SHIDU: { *tempData =Humidity ; break; }
case CO2: { *tempData =Co2 ; break; }
case RIZHAO: { *tempData =Rizhao ; break; }
default:  break;  
}
return 0;
}


/*******************************设置寄存器内容函数 *返回0表示成功*************************/

uint setRegisterVal(uint addr,uint tempData)
{
switch(addr)
{
case WEIDU: {Temperature=tempData ;break; }
case SHIDU: { Humidity=tempData ;break; }
case CO2: { Co2=tempData ;break; }
case RIZHAO: { Rizhao=tempData  ; break; }
}
return 0;
}[/mw_shl_code]
不吃鱼的老鼠
4楼-- · 2019-10-16 09:35
song15032 发表于 2016-7-19 13:32
是啊,可是主机的Modbus协议怎么读取呢。 这是我从机部分 Modbus内容:
[mw_shl_code=c,true]/********* ...

我给你一块钱,你给我一根冰棍,我不给你钱,你就不给我冰棍,这就是协议
止天
5楼-- · 2019-10-16 13:39
你要是只是想发给电脑  可以换一个modbus调试工具  

或者不用modbus协议

你要是想做主机,从机你都会了,主机有什么不会的
njjh1718
6楼-- · 2019-10-16 14:21
 精彩回答 2  元偷偷看……

一周热门 更多>