原创 单片机与组态王通信源程序

2009-8-21 13:35 2821 1 2 分类: MCU/ 嵌入式

/*
  单片机与组态王进行数据通讯(HEX模式)
         2005-11-18测试通过
               赵学军
*/


#include <reg51.h>
#define byte unsigned char
#define word unsigned int
#define ENQ 0x05  //询问
#define ACK 0x06  //确认
#define NAK 0x15  //否认
#define EOT 0x04  //发送结束
#define ETX 0x03  //应答结束
#define DB  0x01  //字节
#define DW  0x02  //字
#define DF  0x03  //浮点
#define BufMax 30
#define time0  -100000
bit RecvOk,CRCok; //接收成功标志位
bit LED; //指示灯
sbit LIT="P0"^0;//指示灯的连接口
byte idata Rbuf[BufMax],Tbuf[BufMax]; // 接收数组和发送数组
byte data Rptr,Tptr,Tnum; //  接收数据 发送数据    发送数据个数计数位
byte data MyAddr,DelayMs,last,ReadMe;


byte  idata DatB[8]; //比特数组用于发送时 存储比特数据 
word  idata DatW[8];  // 特数组用于发送时存储字符型数据
float idata DatF[8];  //特数组用于发送时存储浮点型数据


/******************************************************************************
校验函数的工作是根据组态王的通信协议中数据校验编写的。其规则为:
CRC: 为从第一个字节 至CRC前的所有字节的异或值,由以上程序“crc^=Rbuf”执行。


*********************************************************************************/


void CheckCRC()//校验函数
{
  byte i,n,crc;
  n="Rptr";
  crc="ENQ";
  for(i=0;i<n;i++) crc^=Rbuf;
  if(crc==0)
  CRCok="1";//校验正确标志位
  else CRCok="0";
}
/**********************************************************************************
串口中断的功能有两个:接收和发送信息。SBUF只能一次接受一个字节,因此对于组态王的
信息指令单片机每次只接受一个字节的内容存到数组的一个元素中。如果有的信息大于两字节
这分别存在两个数组元素里。例如在格式格式3、组态王读数据请求格式,DataAddr为数据偏
移地址2字节,低字节在前,高字节在后。此时单片机的程序会将它分开存,分别存在数组的
Rbuf[2],Rbuf[3]里。
***********************************************************************************/
void Uart() interrupt 4 using 1//串口中断
{
  byte m;
  if(RI) //判断串口接收寄存器SBUF是否满了,满则RI由硬件置一
  {
    RI="0";//软件清零
    m="SBUF";
 if(last==ENQ &&(m==MyAddr ||m=='R' || m=='W')) //判断接收信息的类型  
        Rptr="0";   
 if(Rptr<BufMax)
 {
   Rbuf[Rptr]=m; //将接受的数据存到数组里(除了ENQ)
          Rptr++;
 }
    if(last==EOT) //判断是否接收完毕 
    RecvOk="1"; //接收完毕标志位
 last=m;
  }
  else//发送程序
  {
    TI="0";//发送缓存器满
 if(Tnum>0)
 {
   SBUF="Tbuf"[Tptr];//将发送数组中的数据逐个发送到 SBUF
      if(++Tptr>=BufMax)
          Tptr="0";
   Tnum--;
 }
    else if(ReadMe==2)
 {
   SM2=1; //开多机通信
          ReadMe="0";//?
 }
  }
}
/**********************************************************************************
定时器中断 定时:
**********************************************************************************/
void Timer1() interrupt 1 using 2
{
  TH0=time0>>8;
  TL0=time0&0xff;
  if(DelayMs>0)
  DelayMs--;
  else SM2=1;//多机通信标志位
}
/*********************************************************************************
                                  主函数
*********************************************************************************/


void main()
{
  byte n,i,*p,crc,k;
  word ptr,num;
  IE="0x92";//开总中断EA串行中断ES定时器中断ET1,ET0外部中断EX1,EX0
  SCON="0xD0"; //串行工作方式3九位异步收发波特率可变(有定时器控制)REN允许串行接收
  SM2=1; //管理多机通信
  TB8=0;//在SM2=1的情况下表示发送内容为数据帧
  TMOD="0x21";//(定时器工作方式控制寄存器)设定T1为工作方式2T0为工作方式1 由TCON中的TRx控制
  PCON="0x80";//设定SMOD=1
/************************************************************************************
波特率的产生:因为SMOD=1 定时器工作在模式3 波特率=2eqrSMOD/64*晶振频率/12*[256-(TH1)]
************************************************************************************/
  TH1=-2;
  TL1=-2;
  TR1=1;//(计数运行控制位)允许T1计数
  TH0=time0>>8;
  TL0=time0&0xff;
  TR0=1;//允许T0计数
  Tptr="0"; Tnum="0";  Rptr="0";
  ReadMe="0"; RecvOk=0;MyAddr=0;
/*********************************************************************************
                             以下数组为待发送数组
*********************************************************************************/
  DatB[0]=123;DatB[1]=234;DatB[2]=111; DatB[3]=222;
  DatW[0]=1234;DatW[1]=5678;DatW[2]=7890;DatW[3]=17890;
  DatF[0]=12.34; DatF[1]=123.4; DatF[2]=1234.5;


 
while(1)
  {
    DatB[1]=P1;
 if(RecvOk)
 {
       RecvOk="0";
       CheckCRC();//调用校验函数
     if(Rbuf[0]==MyAddr && CRCok==1 && SM2==1)  //对PC的查询命令进行应答
    {
          LED=!LED; LIT="LED"; //开始应答时点灯
       Tbuf[0]=ACK; //格式二
              Tbuf[1]=MyAddr;
              Tbuf[2]=ETX;
              crc="0";
            for(i=0;i<3;i++)
                crc^=Tbuf;//发送前校验
    Tbuf[3]=crc; //将校验结果作为校验位
    Tptr="0"; //在发送程序里作为循环变量
                  Tnum="4"; //发送数据个数为4个
                  TI="1";  //发送缓存器空
    DelayMs="10";
                  ReadMe="1";
                  SM2=0;
    }
    else if(SM2==0 && CRCok==1)     //在应答后进行数据传送控制
    { 
          ReadMe="2";
       n="Rbuf"[4];//格式
    ptr="Rbuf"[3]<<8 | Rbuf[2]; //由于地址偏移量为两字节所以存在数组的两个元素中
               //这个操作将低八位在前高八位在后,转化为高前低后并合成一个十六位的地址偏移量
       if(Rbuf[0]=='R')//判断读
       {
       switch(Rbuf[1])//判断读和种类型的数据
       {
      case DB:num=n; //读字节
                    DatB[0]++;
              p=(byte*)DatB+ptr;//获取数组首地址并加上地址偏移量确定指针一次移多少位
              for(i=0;i<num;i++)
        {
          k="i";
          Tbuf[3+k]=p[k];//将指针数组指向的内容赋给发送数组
                    //之所以从3+K开始是因为发送数组的前三位是ACK和Datalong(两字节)
        }
              break;
      case DW:num=n/2;
              DatW[0]++;
              p=(byte*)DatW+ptr*2;
              for(i=0;i<num;i++)
        {
          k="i"*2;
          Tbuf[3+k]=p[k+1];
          Tbuf[4+k]=p[k];
        }
              break;
      case DF:num=n/4;
              DatF[0]+=0.01;
                       p=(byte*)DatF+ptr*4;
              for(i=0;i<num;i++)
        {
          k="4"*i;
          Tbuf[6+k]=p[k];
          Tbuf[5+k]=p[k+1];
          Tbuf[4+k]=p[k+2];
          Tbuf[3+k]=p[k+3];
        }
              break;
    }
                 Tbuf[0]=ACK; 
                        Tbuf[1]=n&0xff; //将Datalong低前高后存入数组
                        Tbuf[2]=n>>8;
   Tbuf[3+n]=ETX;
                         crc="0"; 
                       for(i=0;i<4+n;i++)
                         crc^=Tbuf;
    Tbuf[4+n]=crc;
    Tptr="0";
                         Tnum="n"+5;//发送数据个数
                         TI="1";//发送寄存器空
    }
    else if(Rbuf[0]=='W')//判断写 格式六
    {
                     n="Rptr-6";
       switch(Rbuf[1])
       {
      case DB:num=n;
              p=(byte*)DatB+ptr;
              for(i=0;i<num;i++)
        {
                k="i";
          p=Rbuf[k+4];
        }
              break;
      case DW:num=n/2;
              p=(byte*)DatW+ptr*2;
              for(i=0;i<num;i++)
        {
          k="i"*2;
          p[k+1]=Rbuf[k+4];
          p[k]=Rbuf[k+5];
        }
              break;
      case DF:num=n/4;
                       p=(byte*)DatF+ptr*4;
              for(i=0;i<num;i++)
        {
          k="4"*i;
          p[k+3]=Rbuf[k+4];
          p[k+2]=Rbuf[k+5];
          p[k+1]=Rbuf[k+6];
          p[k]=Rbuf[k+7];
        }
              break;
    }
          Tbuf[0]=ACK; Tbuf[1]=MyAddr; Tbuf[2]=ETX;
             crc="0";  for(i=0;i<3;i++) crc^=Tbuf;
    Tbuf[3]=crc;
    Tptr="0"; Tnum="4"; TI="1";
    }
    }
 }
  }
}


 


 

PARTNER CONTENT

文章评论1条评论)

登录后参与讨论

用户612414 2012-4-14 16:43

很有用!
相关推荐阅读
我要评论
1
1
关闭 站长推荐上一条 /1 下一条