极客工坊

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 25696|回复: 8

arduino uno 串口接收55个16进制数组

[复制链接]
发表于 2014-12-26 10:28:37 | 显示全部楼层 |阅读模式
学习编程一段时间了,想做一个接收数组的程序。
用一块板定时发送一组16时进制数组
程序如下:

#define LED_PIN 8
unsigned char hexdata[55]= {0x7E, 0x01,0xA1,0x1C,0x00,0x00,0x00,0x00,0x00,0x00,
                            0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
                            0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
                            0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
                            0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
                            0x00, 0x00,0x00,0x00,0x0B};
void setup()
{
  Serial.begin(9600);
  pinMode(LED_PIN,OUTPUT);
}

void loop()
{
  digitalWrite(LED_PIN,HIGH);   
  Serial.write(hexdata,55);
  digitalWrite(LED_PIN,LOW);
     delay(5000);
}

用另一块板的模拟串口读取,然后再用串口发送出去

#include <SoftwareSerial.h>
#include <avr/wdt.h>
#include <MsTimer2.h>

int mark;
unsigned char comdata[]={};
unsigned char com;

unsigned char hexdata[55]= {0x7E, 0x01,0xA1,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
                            0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
                            0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
                            0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
                            0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
                            0x00, 0x00,0x00,0x00,0xA2};
                           
SoftwareSerial mySerial(8,7); // RX, TX                           
                           
void setup()
{
      Serial.begin(9600);
      mySerial.begin(9600);
      wdt_enable(WDTO_4S);  //看门狗设成4S
}

void loop()
{
  while ( mySerial.available() > 0)
  {

     for(int i=0; i< 55;i++) comdata[i] = char (mySerial.read()) ;  

  }

       for(int k=0;k<55;k++) Serial.write(comdata[k]);

delay(4000);



   //    Serial.write(hexdata,55);
    wdt_reset();  //喂狗

}

发送的数据用串口助手查看是正确的,但用模拟串口接收下的数据再发送出来的数,与发送的数对应不上。
请高手指教。
回复

使用道具 举报

 楼主| 发表于 2014-12-26 10:45:13 | 显示全部楼层
刚才看到延时和喂狗时间有问题,

#include <SoftwareSerial.h>
#include <avr/wdt.h>
#include <MsTimer2.h>

int mark;
unsigned char comdata[]={};
unsigned char com;

unsigned char hexdata[55]= {0x7E, 0x01,0xA1,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
                            0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
                            0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
                            0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
                            0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
                            0x00, 0x00,0x00,0x00,0xA2};
                           
SoftwareSerial mySerial(8,7); // RX, TX                           
                           
void setup()
{
      Serial.begin(9600);
      mySerial.begin(9600);
      wdt_enable(WDTO_8S);  //看门狗设成8S
}

void loop()
{
  while ( mySerial.available() > 0)
  {

     for(int i=0; i< 55;i++) comdata[i] = char (mySerial.read()) ;  

  }

   

delay(2000);
   for(int k=0;k<55;k++) Serial.write(comdata[k]);


   //    Serial.write(hexdata,55);
    wdt_reset();  //喂狗

}

接收到的数据是7E 01 A1 1C 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
前段数正常,后段不正常。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2014-12-26 10:51:16 | 显示全部楼层
刚才把unsigned char comdata[]={};
这个定义改成unsigned char comdata[55]={};
后段数据也正常了。
回复 支持 反对

使用道具 举报

发表于 2014-12-27 02:07:42 | 显示全部楼层
邏輯上存在很大的問題:

這裡只檢測到 mySerial 有最少一個之資料, 就盲目地讀取 55 個數據.
  1. while ( mySerial.available() > 0)
  2.   {

  3.      for(int i=0; i< 55;i++) comdata[i] = char (mySerial.read()) ;  

  4.   }
复制代码
要知道, 發送數據, 是需要時間的.  你用的 buadrate 只是 9600, 在沒有 overhead 的情況下, 發送一個 Byte 也要接近 1 ms 了.
沒問題只是好運氣吧了.
回复 支持 反对

使用道具 举报

 楼主| 发表于 2014-12-28 20:08:22 | 显示全部楼层
Super169 发表于 2014-12-27 02:07
邏輯上存在很大的問題:

這裡只檢測到 mySerial 有最少一個之資料, 就盲目地讀取 55 個數據.要知道, 發送 ...

谢谢提醒,我慢慢学,后面把头判断加上
回复 支持 反对

使用道具 举报

发表于 2014-12-30 13:45:40 | 显示全部楼层
luckly 发表于 2014-12-28 20:08
谢谢提醒,我慢慢学,后面把头判断加上

希望能分享代码~
回复 支持 反对

使用道具 举报

 楼主| 发表于 2014-12-30 14:50:10 | 显示全部楼层
maxims 发表于 2014-12-30 13:45
希望能分享代码~


#include <math.h>
#include <MsTimer2.h>
#include <stdio.h>
#include <avr/wdt.h>
#include <string.h>
#include <SoftwareSerial.h>
/*------------------------------------------------------------------------------------------------
MODBUS_RTU 从站协议
@版本:V1.0
@最后更新时间:2013/05/21 17:00
@作者:Frame.Kuang
@版权申明:免费,开源
说明:
本程序处于测试阶段,支持modbus功能码03,06,16
---------------------------------------------------------------------------------------------------*/
//基本参数
#define baudrate 9600  //定义通讯波特率
#define slaveID 1  //定义modbus RTU从站站号
#define modbusDataSize 16 //定义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端口初始化函数


int  led=13;
unsigned char comdata[55]={  };
unsigned int  DC_V,DC_i,AC_V,AC_i,Temperature,Inverter_State,Grid_Hz,count,Today_Energy,Yesterday_Energy,TM_Energy,YM_Energy,Total_EnergyL,Total_EnergyH,mark;



unsigned char hexdata[55]= {0x7E, 0x01,0xA1,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
                            0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
                            0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
                            0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
                            0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
                            0x00, 0x00,0x00,0x00,0xA2};
                           
SoftwareSerial mySerial(8,7); // RX, TX                           

void flash()
{
     count++;
     
     if(count==10)
   {
         mySerial.write( hexdata,55);
         digitalWrite(led,LOW);
         
   }  
   if(count ==50)
   {
     count = 0;
   }

}


                           
void setup()
{
      Serial.begin(9600);
      mySerial.begin(9600);
      pinMode(led,OUTPUT);
      for(int i=0; i< 55;i++) comdata=0;
        modbusRTU_INI(&Serial);  //定义modbus通讯端口 端口0:&Serial 端口1:&Serial1 端口2:&Serial2
        MsTimer2::set (100,flash); //中断设置函数,每隔1MS进一次中断。
        MsTimer2::start();
      wdt_enable(WDTO_8S);  //看门狗设成8S
      
}



void loop()
{

          while ( mySerial.available() > 0)
          {
             if( 0x7E == char ( mySerial.read() ))
                  
             {
               for(int i=1; i< 55;i++)
               comdata = char ( mySerial.read() );  
              delay(5);
             }
            
             else{
            
             Serial.flush();
             }
            
          }
         
          Serial.flush();
        

         if(  comdata[1]== 0x01 &&  comdata[2]== 0xA1 &&  comdata[3]== 0x1C  )
         {
           digitalWrite(led,HIGH);
           
           DC_V = comdata[5];
           DC_V = DC_V<<8;
           DC_V += comdata[4];
            modbusData[0]= DC_V;
            
           DC_i = comdata[7];
           DC_i = DC_i<<8;
           DC_i += comdata[6];
            modbusData[1]= DC_i;
            
           AC_V = comdata[9];
           AC_V = AC_V<<8;
           AC_V +=comdata[8];
            modbusData[2]= AC_V;
           
           AC_i = comdata[11];
           AC_i = AC_i << 8;
           AC_i += comdata[10];
           modbusData[3]= AC_i;
           
           Temperature = comdata[13];
           Temperature = Temperature<< 8;
           Temperature += comdata[12];
           modbusData[4] = Temperature;
           

           
           Total_EnergyH = comdata[17];
           Total_EnergyH = Total_EnergyH << 8;
           Total_EnergyH +=comdata[16];
           modbusData[5] = Total_EnergyH;
         
           Total_EnergyL  = comdata[15];
           Total_EnergyL  = Total_EnergyL<<8;
           Total_EnergyL += comdata[14];
           modbusData[6] = Total_EnergyL;
           
           Inverter_State = comdata[19];
           Inverter_State = Inverter_State << 8 ;
           Inverter_State += comdata[18];
           modbusData[7] = Inverter_State;
           
           Grid_Hz = comdata[25];
           Grid_Hz = Grid_Hz << 8;
           Grid_Hz += comdata[24];
           modbusData[8] = Grid_Hz;
           
           Today_Energy = comdata[38];
           Today_Energy = Today_Energy<<8;
           Today_Energy += comdata[37];
           modbusData[9] = Today_Energy;
           
           Yesterday_Energy = comdata[40];
           Yesterday_Energy = Yesterday_Energy<<8;
           Yesterday_Energy += comdata[39];
           modbusData[10] =Yesterday_Energy;
           
           TM_Energy = comdata[34];
           TM_Energy = TM_Energy<<8;
           TM_Energy += comdata[33];
           modbusData[11] = TM_Energy;
           
           YM_Energy = comdata[36];
           YM_Energy = YM_Energy<<8;
           YM_Energy += comdata[35];
           modbusData[12] = YM_Energy;
     
         
         }
         
       else
       {
         for( int k= 0; k<55; k++) comdata[k] = 0;  
        
        digitalWrite(led,LOW);
       }
      
     for( int k= 0; k<55; k++) comdata[k] = 0;  
     delay(1000);
     modbusRTU_slave();  //执行modbus函数
    wdt_reset();  //喂狗



}



//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<bufferSize)  //接收的数据量应小于一帧数据的最大字节数量
    {
      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<endData;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<endData;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;
}
回复 支持 反对

使用道具 举报

 楼主| 发表于 2014-12-30 14:51:05 | 显示全部楼层
这是全部代码?边学边练
回复 支持 反对

使用道具 举报

发表于 2014-12-30 16:44:28 | 显示全部楼层
luckly 发表于 2014-12-30 14:51
这是全部代码?边学边练



这个玩意不知道有没有用

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

Archiver|联系我们|极客工坊

GMT+8, 2026-6-18 02:59 , Processed in 0.055632 second(s), 20 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表