/*
单片机与组态王进行数据通讯(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";
}
}
}
}
}
用户612414 2012-4-14 16:43