<acronym id="xonnx"></acronym>
      <td id="xonnx"></td>
    1. <pre id="xonnx"></pre>

      1. 專注電子技術學習與研究
        當前位置:單片機教程網 >> Arduino >> 瀏覽文章

        Arduino Modbus_RTU 從站程序

        作者:Frame   來源:會員整理上傳   點擊數:  更新時間:2014年05月22日   【字體:

         //基本參數

        #define baudrate 115200  //定義通訊波特率
        #define slaveID 1  //定義modbus RTU從站站號
        #define modbusDataSize 100  //定義modbus數據庫空間大小,可根據實際情況自行修改大小
        unsigned int modbusData[modbusDataSize]={};   //建立modbus數據庫
         
        //系統參數
        #define bufferSize 255  //一幀數據的最大字節數量
        unsigned char frame[bufferSize];  //用于保存接收或發送的數據
        HardwareSerial* ModbusPort;
         
        //函數聲明
        unsigned int calculateCRC(unsigned char* _regs,unsigned char arraySize);  //聲明CRC校驗函數
        void modbusRTU_slave();  //聲明modbus RTU從站函數
        void responseError(unsigned char ID,unsigned char function,unsigned char wrongNumber);  //聲明錯誤信息返回函數
        void modbusRTU_INI(HardwareSerial *SerialPort);  //聲明modbus RTU端口初始化函數
         
         
         
        //初始化函數
        void setup() 
        {
          delay(100);
          modbusRTU_INI(&Serial);  //定義modbus通訊端口 端口0:&Serial 端口1:&Serial1 端口2:&Serial2
        }
         
         
        //主循環
        void loop() 
        {
          modbusRTU_slave();  //執行modbus函數
        }
         
         
        //modbus RTU端口初始化函數
        //參數:端口號
        void modbusRTU_INI(HardwareSerial *SerialPort)
        {
          ModbusPort = SerialPort;
          (*ModbusPort).begin(baudrate);
          (*ModbusPort).flush(); 
        }
         
         
        //modbus RTU從站函數
        //支持功能碼03,06,16
        void modbusRTU_slave()
        {
          unsigned int characterTime; //字符時間
          unsigned char errorFlag=0;  //錯誤標志
          unsigned int crc16;  //校驗位
         
          unsigned char address=0;
         
          if (baudrate > 19200)  //波特率大于19200時進入條件
          {
            characterTime = 750; 
          }
          else
          {
            characterTime = 15000000/baudrate;  //1.5字符時間
          }
          while((*ModbusPort).available()>0)  //如果串口緩沖區數據量大于0進入條件
          {
            
            if(address
            {
              frame[address]=(*ModbusPort).read();
              address++;
            }
            else  //條件不滿足時直接清空緩沖區
            {
               (*ModbusPort).read();
            }
            delayMicroseconds(characterTime);  //等待1.5個字符時間
            if((*ModbusPort).available()==0)  //1.5個字符時間后緩沖區仍然沒有收到數據,認為一幀數據已經接收完成,進入條件
            {
              unsigned char function=frame[1];  //讀取功能碼     
              if(frame[0]==slaveID||frame[0]==0)  //站號匹配或者消息為廣播形式,進入條件
              {
                crc16 = ((frame[address - 2] << 8) | frame[address - 1]);
                if(calculateCRC(&frame[0],address - 2)==crc16)  //數據校驗通過,進入條件
                {
                  if (frame[0]!=0 && (function == 3))  //功能碼03不支持廣播消息
                  {
                    unsigned int startData=((frame[2] << 8) | frame[3]);  //讀取modbus數據庫起始地址          
                    unsigned int dataSize=((frame[4] << 8) | frame[5]);  //需要讀取的modbus數據庫數據長度
                    unsigned int endData=startData+dataSize;    //需要讀取的modbus數據庫數據的結束地址
                    unsigned char responseSize=5+dataSize*2;  //計算應答的數據長度
                    unsigned int temp1,temp2,temp3;
                    
                    if(dataSize>125 || endData>=modbusDataSize)  //讀取數據的結束地址超過了modbus數據庫的范圍或單次讀取的數據數量大于125
                    {
                      errorFlag=0x02;  //數據超過范圍
                      responseError(slaveID,function,errorFlag);  //返回錯誤消息
                    }
                    else
                    {
                      frame[0]=slaveID;  //設定站號
                      frame[1]=function;  //設定功能碼
                      frame[2]=dataSize*2;  //設定數據長度
                      temp3=3;
                      for(temp1=startData;temp1
                      {
                        temp2=modbusData[temp1];  //取出modbus數據庫中的數據
                        frame[temp3]=temp2>>8;
                        temp3++;
                        frame[temp3]=temp2 & 0xFF;
                        temp3++;
                      }
                      crc16 = calculateCRC(&frame[0],responseSize-2);
                      frame[responseSize-2] = crc16 >> 8;  //填寫校驗位
                      frame[responseSize-1] = crc16 & 0xFF;
                      (*ModbusPort).write(&frame[0],responseSize);  //返回功能碼03的消息
                    }
                  }
                  else if(function == 6)  //功能碼為06時進入條件
                  {
                    unsigned int startData=((frame[2] << 8) | frame[3]);  //寫入modbus數據庫的地址          
                    unsigned int setData=((frame[4] << 8) | frame[5]);  //寫入modbus數據庫的數值
                    if(startData>=modbusDataSize)
                    {
                      errorFlag=0x02;  //數據超過范圍
                      responseError(slaveID,function,errorFlag);  //返回錯誤消息
                    }
                    else
                    {
                      modbusData[startData]=setData;  //寫入數據到modbus數據庫             
                      frame[0]=slaveID;  //設定站號
                      frame[1]=function;  //設定功能碼
                      frame[2] = startData >> 8;  //填寫數據庫地址
                      frame[3] = startData & 0xFF;             
                      frame[4] = modbusData[startData] >> 8;  //填寫數據庫數值
                      frame[5] = modbusData[startData] & 0xFF;
                      crc16 = calculateCRC(&frame[0],6);  //計算校驗值
                      frame[6] = crc16 >> 8;  //填寫校驗位
                      frame[7] = crc16 & 0xFF;
                      (*ModbusPort).write(&frame[0],8);  //返回功能碼06的消息             
                    }
                  }
                  else if(function == 16)  //功能碼為16時進入條件
                  {
                    if(frame[6]!=address-9)  //校驗數據長度
                    {
                      errorFlag=0x03;  //數據長度不符
                      responseError(slaveID,function,errorFlag);  //返回錯誤消息
                    }
                    else  //校驗數據長度正確
                    {
                      unsigned int startData=((frame[2] << 8) | frame[3]);  //寫入modbus數據庫起始地址          
                      unsigned int dataSize=((frame[4] << 8) | frame[5]);  //需要寫入的modbus數據庫數據長度
                      unsigned int endData=startData+dataSize;    //需要寫入的modbus數據庫數據的結束地址
                      if(dataSize>125 || endData>=modbusDataSize)  //讀取數據的結束地址超過了modbus數據庫的范圍或單次讀取的數據數量大于125
                      {
                        errorFlag=0x02;  //數據超過范圍
                        responseError(slaveID,function,errorFlag);  //返回錯誤消息
                      }
                      else
                      {              
                        unsigned int temp1,temp2;
                        temp2 = 7;  //從數據貞的第8個數據開始讀取           
                        for(temp1=startData;temp1
                        {
                          modbusData[temp1]=(frame[temp2]<<8|frame[temp2+1]);  //將數據寫入modbus數據庫中
                          temp2+=2;
                        }
                        frame[0]=slaveID;  //填寫站號,frame[1]到frame[5]不變
                        crc16 = calculateCRC(&frame[0],6);  //計算CRC校驗
                        frame[6] = crc16 >> 8;  //填寫校驗位
                        frame[7] = crc16 & 0xFF;
                        (*ModbusPort).write(&frame[0],8);  //發送功能碼16的應答數據   
                      }    
                    }
                  } 
                  else  //其他功能碼
                  {
                    errorFlag = 0x01;  //不支持收到的功能碼
                    responseError(slaveID,function,errorFlag);  //返回錯誤消息
                  }      
                }
                else //數據校驗錯誤
                {
                  errorFlag = 0x03;
                  responseError(slaveID,function,errorFlag);  //返回錯誤消息
                }
              }
            }
          }
        }
         
         
         
        void responseError(unsigned char ID,unsigned char function,unsigned char wrongNumber)  //錯誤信息返回函數
        {
          unsigned int crc16;  //校驗位 
          frame[0] = ID;  //設定站號
          frame[1] = function+0x80;
          frame[2] = wrongNumber;  //填寫錯誤代碼
          crc16 = calculateCRC(&frame[0],3);  //計算校驗值
          frame[3] = crc16 >> 8;  //填寫校驗位
          frame[4] = crc16 & 0xFF;
          (*ModbusPort).write(&frame[0],5);  //返回錯誤代碼        
        }
         
         
        //CRC校驗函數
        //參數1:待校驗數組的起始地址
        //參數2:待校驗數組的長度
        //返回值CRC校驗結果,16位,低字節在前
        unsigned int calculateCRC(unsigned char* _regs,unsigned char arraySize)
        {
          unsigned int temp, temp2, flag;
          temp = 0xFFFF;
          for (unsigned char i = 0; i < arraySize; i++)
          {
            temp = temp ^ *(_regs+i);
            for (unsigned char j = 1; j <= 8; j++)
            {
              flag = temp & 0x0001;
              temp >>= 1;
              if (flag)
                temp ^= 0xA001;
            }
          }
          temp2 = temp >> 8;
          temp = (temp << 8) | temp2;
          temp &= 0xFFFF; 
          return temp;
        }
        關閉窗口

        相關文章

        欧美性色欧美精品视频,99热这里只有精品mp4,日韩高清亚洲日韩精品一区二区,2020国自产拍精品高潮