/8051单片机TLC1549数据收集程序源代码
//单片机:At89S52
//衔接类型:232 |485 接口
//校验方法:checksum
//开发环境:keil C
#i nclude
#i nclude
#i nclude “iic.h”
#define uchar unsigned char
#define uint unsigned int
uint Timer_Pro_Flag=0;//0为显现处理,1为时序收集处理
sbit TEST_CAP_VOL_CRLT=P1^0;//用于操控外部继电器
sbit PWM_Pin=P3^7;//PWM输出
sbit IrDA_in_Pin=P1^0;//红外脉冲输入检测脚
bit PWMFlag=0;
uchar OLD_TH0,OLD_TL0;
#define MAXCMD_LENGTH 7
#define AD_VOL_PER 1.04058 //正常收集时的电压校准值
#define AD_Loop_PickVol_PER 1.04058 //CD4051循环收集时的电压校准值
sbit WDTRST=0xA6;//At89S5x看门狗寄存器
sbit ADCLK=P2^0;
sbit ADOUT=P2^1;
sbit ADCS=P2^2;
//————————–
/************CD4051—8选1模仿开关芯片*****************/
/*
|——————————————————|
| 4 2 1 |
|———————————————–|——|
|端口 | INH C B A | | |
|—— 7 6 5 4 3 2 1 0 | 正值 | 取反 |
| 通道 |——————————–|——-|——|
|——|……………|8 4 2 1 | | |
|——|—————|—————-|——-|——|
| 0 |0 0 0 0 0 0 0 0 | 0x00 | 0xff |
| 1 |0 0 0 0 0 0 1 0 | 0x02 | 0xf7 |
| 2 |0 0 0 0 0 1 0 0 | 0x04 | 0xfb |
| 3 |0 0 0 0 0 1 1 0 | 0x06 | 0xf3 |
| 4 |0 0 0 0 1 0 0 0 | 0x08 | 0xfd |
| 5 |0 0 0 0 1 0 1 0 | 0x0A | 0xf8 |
| 6 |0 0 0 0 1 1 0 0 | 0x0C | 0xf9 |
| 7 |0 0 0 0 1 1 1 0 | 0x0e | 0xf1 |
|—————————————|——-|——|
*/
uchar CD4051_NUM[]={0x00,0x02,0x04,0x06,0x08,0x0A,0x0C,0x0e}; //0~7编码
sbit CD4051_A=P1^1;
sbit CD4051_B=P1^2;
sbit CD4051_C=P1^3;
sbit CD4051_INH=P1^4; //CD4051_INH为1时,,一切通导都不导通
bit CD4051_Vol_Conver_Flag=0; //0时为正常收集,1为CD4051循环收集
//————————–
//LED显现
//————————–
uchar LedCount=0;
uchar LED_BIT[5]; //用于显现的每一位数据码的内容
uchar LED_NUM[]={0x00,0x10,0x20,0x40,0x80};//挑选显现位
//——————————————————–
void SendByte(unsigned char word)
{
TI=0;
SBUF=word;
while(TI==0);
TI=0;
}
uchar ComBuf[MAXCMD_LENGTH];//用于保存串口的数据
/*—少数延时—*/
void delay(uint t)
{
uint i=0;
for(;i<=t;i++);
}
void Pluckdelay(uint t)
{
uint i=0,j;
for(;i<=t;i++)
for(j=1;j<=1000;j++);
}
/*=================================
ComBuf[5]参加CheckSUM校验码
算法:0x01+not(字节1+字节2+…+字节N)
=================================*/
void SetCheckSUM()
{
ComBuf[5]=0x01+~(ComBuf[0]+ComBuf[1]+ComBuf[2]+ComBuf[3]+ComBuf[4]);
}
/*=============================================================
检测上位机发送来的ComBuf[5]checksum校验码和核算后的是否持平
正确则回来:1
过错则回来:0
==============================================================*/
bit ISCheckSUM()
{
uchar crc;
crc=0x01+~(ComBuf[0]+ComBuf[1]+ComBuf[2]+ComBuf[3]+ComBuf[4]);
if(ComBuf[5]==crc)
return 1;
else
{
ComBuf[1]=crc;//假如过错,则回来核算后得到的CHECKSUM校验码
ComBuf[2]=ComBuf[5];//回来本来上位机发送来的CHECKSUM校验码
return 0;
}
}
/*———————————–
* *
* 等候接纳上位机发来的指令 *
* *
————————————-*/
void WaitComm()
{
uchar n=0;
RI=0;
while (1)
{
while(!RI);//等接纳数据
//—————–
ComBuf[n]=SBUF;
RI=0;
// SBUF=ComBuf[n];
if (ComBuf[n]==0x7e) break;//接纳到结束符则退出
if (n>=MAXCMD_LENGTH)
n=0;//接纳10个字节,假如还没有接纳到0x7e结束符,就从头记载
else
n++;
}
}
/*——————————-
* *
* 发送combuf数据至上位机 *
* *
——————————–*/
void SendByteArray()
{
unsigned i;
SetCheckSUM();//参加ComBuf[5]的checkSUM校验码
ComBuf[4]=P0;//取读本模块地址
ComBuf[6]=0x7e;//结束符
for(i=0;i<=MAXCMD_LENGTH-1;i++)
{
SendByte(ComBuf[i]);
}
}
/*——————————-
* *
* 送端口状况至上位机 *
* *
——————————–*/
void GetP0()
{
switch(ComBuf[2])
{
case 0x00: ComBuf[3]=P0_0; break;
case 0x01: ComBuf[3]=P0_1; break;
case 0x02: ComBuf[3]=P0_2; break;
case 0x03: ComBuf[3]=P0_3; break;
case 0x04: ComBuf[3]=P0_4; break;
case 0x05: ComBuf[3]=P0_5; break;
case 0x06: ComBuf[3]=P0_6; break;
case 0x07: ComBuf[3]=P0_7; break;
default:
ComBuf[3]=P0;//为8时…则以上传整个port状况
}
}
//——————–
void GetP1()
{
switch(ComBuf[2])
{
case 0x00: ComBuf[3]=P1_0; break;
case 0x01: ComBuf[3]=P1_1; break;
case 0x02: ComBuf[3]=P1_2; break;
case 0x03: ComBuf[3]=P1_3; break;
case 0x04: ComBuf[3]=P1_4; break;
case 0x05: ComBuf[3]=P1_5; break;
case 0x06: ComBuf[3]=P1_6; break;
case 0x07: ComBuf[3]=P1_7; break;
default:
ComBuf[3]=P1;//为8时…则以上传整个port状况
}
}
//—————
void GetP2()
{
switch(ComBuf[2])
{
case 0x00: ComBuf[3]=P2_0; break;
case 0x01: ComBuf[3]=P2_1; break;
case 0x02: ComBuf[3]=P2_2; break;
case 0x03: ComBuf[3]=P2_3; break;
case 0x04: ComBuf[3]=P2_4; break;
case 0x05: ComBuf[3]=P2_5; break;
case 0x06: ComBuf[3]=P2_6; break;
case 0x07: ComBuf[3]=P2_7; break;
default:
ComBuf[3]=P2;//为8时…则以上传整个port状况
}
}
//————–
void GetP3()
{
switch(ComBuf[2])
{
case 0x00: ComBuf[3]=P3_0; break;
case 0x01: ComBuf[3]=P3_1; break;
case 0x02: ComBuf[3]=P3_2; break;
case 0x03: ComBuf[3]=P3_3; break;
case 0x04: ComBuf[3]=P3_4; break;
case 0x05: ComBuf[3]=P3_5; break;
case 0x06: ComBuf[3]=P3_6; break;
case 0x07: ComBuf[3]=P3_7; break;
default:
ComBuf[3]=P3;//为8时…则以上传整个port状况
}
}
//——-发送AT89S5x的指定端口状况到上位机—–
void SendPortData()
{
switch (ComBuf[1])/*Port号*/
{
case 0x00: GetP0(); break;
case 0x01: GetP1(); break;
case 0x02: GetP2(); break;
case 0x03: GetP3(); break;
}
/*——————–*/
SendByteArray();//发送数据
}
/*——————————-
* *
* 各个端口的状况设定 *
* *
——————————–*/
void SetP0()
{
switch(ComBuf[2])
{
case 0x00: P0_0=ComBuf[3]; break;
case 0x01: P0_1=ComBuf[3]; break;
case 0x02: P0_2=ComBuf[3]; break;
case 0x03: P0_3=ComBuf[3]; break;
case 0x04: P0_4=ComBuf[3]; break;
case 0x05: P0_5=ComBuf[3]; break;
case 0x06: P0_6=ComBuf[3]; break;
case 0x07: P0_7=ComBuf[3]; break;
default:
P0=ComBuf[3];//为8时…则设置整个port状况
}
}
//=================================
void SetP1()
{
switch(ComBuf[2])
{
case 0x00: P1_0=ComBuf[3]; break;
case 0x01: P1_1=ComBuf[3]; break;
case 0x02: P1_2=ComBuf[3]; break;
case 0x03: P1_3=ComBuf[3]; break;
case 0x04: P1_4=ComBuf[3]; break;
case 0x05: P1_5=ComBuf[3]; break;
case 0x06: P1_6=ComBuf[3]; break;
case 0x07: P1_7=ComBuf[3]; break;
default:
P1=ComBuf[3];//为8时…则设置整个port状况
}
}
//=================================
void SetP2()
{
switch(ComBuf[2])
{
case 0x00: P2_0=ComBuf[3]; break;
case 0x01: P2_1=ComBuf[3]; break;
case 0x02: P2_2=ComBuf[3]; break;
case 0x03: P2_3=ComBuf[3]; break;
case 0x04: P2_4=ComBuf[3]; break;
case 0x05: P2_5=ComBuf[3]; break;
case 0x06: P2_6=ComBuf[3]; break;
case 0x07: P2_7=ComBuf[3]; break;
default:
P2=ComBuf[3];//为8时…则设置整个port状况
}
}
//=================================
void SetP3()
{
switch(ComBuf[2])
{
case 0x00: P3_0=ComBuf[3]; break;
case 0x01: P3_1=ComBuf[3]; break;
case 0x02: P3_2=ComBuf[3]; break;
case 0x03: P3_3=ComBuf[3]; break;
case 0x04: P3_4=ComBuf[3]; break;
case 0x05: P3_5=ComBuf[3]; break;
case 0x06: P3_6=ComBuf[3]; break;
case 0x07: P3_7=ComBuf[3]; break;
default:
P3=ComBuf[3];//为8时…则设置整个port状况
}
}
/*=================================
WritePortData()
按上位机传来的格局进行端口的设置
=================================*/
void WritePortData()
{
switch (ComBuf[1])/*Port号*/
{
case 0x00: SetP0(); break;
case 0x01: SetP1(); break;
case 0x02: SetP2(); break;
case 0x03: SetP3(); break;
}
}
/*———————————-
* SetEA() *
* *
* 中止答应设定,(EA寄存器) *
* ComBuf[1]==>0x00为EA设定 *
* 0x01为读取EA值 *
————————————*/
void SetEA()
{
if (ComBuf[1]==0x00)
EA=ComBuf[3];
else
{
ComBuf[3]=EA;
SendByteArray();//发送数据
}
}
/*—————————————————————
* *
* float型转为2位char型,并发送至串行 *
* void Convert_AD_VOL_ValueToChar() *
* *
—————————————————————*/
void Convert_AD_VOL_ValueToChar(uint vol)
{
float temp_float_vol;
unsigned int temp;
uchar AD_Hight,AD_Low;
temp_float_vol=vol*0.0048*AD_VOL_PER;
temp=temp_float_vol*100;
AD_Hight=temp /100;//取个位数
AD_Low=temp-AD_Hight*100;//取2位小数
ComBuf[2]=AD_Hight;
ComBuf[3]=AD_Low;
SendByteArray();//发送数据
}
/*——————————–
* *
* 预先收集一次AD数据 *
* *
*——————————–*/
void Befor_Once_AD()
{
uchar i;
ADCLK=ADOUT=0;
//———-
ADCS=0; //敞开操控电路,使能DATA OUT和I/O CLOCK
for(i=1;i<=10;i++)
{
ADCLK=1;
ADCLK=0;
}
ADCS=1;
delay(25);//两次转化距离大于21us
}
/*—————————————————————
* GetAD() TLC1549数据收集 *
* sbit ADCLK=P2^0; *
* sbit ADOUT=P2^1; *
* sbit ADCS=P2^2; *
—————————————————————–*/
void GetAD()
{
uchar i=1,w,PickCount;
uint vol;
Befor_Once_AD();//预先收集一次AD数据
//—————
if (ComBuf[1]==0)ComBuf[1]=0x01;
PickCount=ComBuf[1];
for(w=1;w<=PickCount;w++)
{
ADCLK=ADOUT=0;
vol=0;
ADCS=0; //敞开操控电路,使能DATA OUT和I/O CLOCK
for(i=1;i<=10;i++)
{
//给一个脉冲
ADCLK=1;
vol<<=1;
if(ADOUT)vol|=0x01;
ADCLK=0;
}
ADCS=1;
delay(21);//两次转化距离大于21us
//—————
ComBuf[1]=w;//发送第几次收集的序号
Convert_AD_VOL_ValueToChar(vol);//对float转为2位char型,并发送至串行口
P2=0xff;//p2口置初始状况
}
}
/*—————————————————————
* *
* TLC1549数据软件滤波收集 *
* *
—————————————————————–*/
void GetAD_With_VOL_Filter()
{
uchar i,w,j,k,PickCount,AD_Hight=0,AD_Low=0;
uint Vol=0,VolArray[10],temp;
float SumVol=0;
Befor_Once_AD();//预先收集一次AD数据
//—————
//—————
PickCount=11;
for(w=0;w<=PickCount;w++)
{
ADCLK=ADOUT=0;
Vol=0;
ADCS=0; //敞开操控电路,使能DATA OUT和I/O CLOCK
for(i=1;i<=10;i++)
{
//给一个脉冲
ADCLK=1;
Vol<<=1;
if(ADOUT)Vol|=0x01;
ADCLK=0;
}
ADCS=1;
delay(21);//两次转化距离大于21us
VolArray[w]=Vol;//保存收集来的数据
//—————
P2=0xff;//p2口置初始状况
}
//——-按从小到大排序——–
//挑选排序法..
for(i=0;i<=PickCount-1;i++)
{
k=i;
for(j=PickCount+1;j{
if(VolArray[j]>VolArray[k])k=j;
if(k!=i)
{
temp=VolArray[k];
VolArray[k]=VolArray[i];
VolArray[i]=temp;
}
}
}
//———-累加核算安静均值————
//乎略最小和最大值
for(i=1;i<=PickCount-1;i++)
{
SumVol=SumVol+VolArray[i];//累加成果
}
SumVol=SumVol/(PickCount-1)*0.0048;//电压值=平均值*介数
/*——————————
0时为正常收集,1为CD4051循环收集
由于CD4051通道存在电压耗费,
所以和正常的直接纳集的校准值不一样
——————————-*/
/*电压校准比*/
if(CD4051_Vol_Conver_Flag)
SumVol*=AD_Loop_PickVol_PER;//选用CD4051时的电压校准值
else
SumVol*=AD_VOL_PER;//直接输入时的电压校准值
//—————-
temp=SumVol*100;//保存2位小位
AD_Hight=temp /100;//取个位数
AD_Low=temp-AD_Hight*100;//取2位小数
//ComBuf[1]=w;//发送第几次收集的序号
ComBuf[2]=AD_Hight;
ComBuf[3]=AD_Low;
SendByteArray();//发送数据
}
/*————————————————————————-*
* *
* CD4051_PickVol…8路选通TLC1549收集 *
* *
*————————————————————————-*/
void CD4051_PickVol()
{
CD4051_Vol_Conver_Flag=1;
P1=CD4051_NUM[ComBuf[1]];//CD4051通道选通
// delay(2300);//通道切换时刻距离,防止电路的残余电
GetAD_With_VOL_Filter();
CD4051_Vol_Conver_Flag=0;
}
/*————————————————————————-*
* *
* CD4051_LoopPickVol()…8路巡检TLC1549收集 *
* *
*————————————————————————-*/
void CD4051_LoopPickVol()
{
uchar i=0,w;
w=ComBuf[1]-1;//通导号等于。。。通道数-1
for(;i<=w;i++)
{
ComBuf[1]=i;//通道号
CD4051_PickVol();
}
P1=0xff;//封闭通道
}
/*————————————————————————-*
* *
* 电容放电计数测验 *
* TestCapCount() *
* *
*————————————————————————-*/
void TestCapCount()
{
uint Vol,TempVol=0xff,Count=0,temp1,temp2;
uchar i,CAPDELAYTIME;
float TEST_CAP_OUT_value=”/0.05;”
P1=CD4051_NUM[1];//CD4051通道选通,1号通道
TEST_CAP_VOL_CRLT=0;//翻开电源
if (ComBuf[1]==0x00)//为0x00时为电容丈量方法,0x01为电压丈量方法
{
for(i=0;i<=ComBuf[2];i++)delay(60000);//等候电容充电
TEST_CAP_VOL_CRLT=1;//断开电源
TEST_CAP_OUT_value=”/0.01;//当为电容丈量时。。下限电压”
}
CAPDELAYTIME=ComBuf[3];//延时常量
//————–
P1_1=0;//翻开LED状况指示
while(TempVol*0.0048*AD_VOL_PER>=TEST_CAP_OUT_VALUE)//当放电到0V时退出
{
ADCLK=ADOUT=0;
Vol=0;
ADCS=0; //敞开操控电路,使能DATA OUT和I/O CLOCK
for(i=1;i<=10;i++)
{
//给一个脉冲
ADCLK=1;
Vol<<=1;
if(ADOUT)Vol|=0x01;
ADCLK=0;
}
ADCS=1;
delay(21);//两次转化距离大于21us
//—————
P2=0xff;//p2口置初始状况
Count++;//计数
ComBuf[0]=0x05;//使用软件滤波的处理进程显现
if(Count>2)TempVol=Vol;//第一次的取值有可能是1,,去掉不要
Convert_AD_VOL_ValueToChar(Vol);//转化并发送本次数据
Pluckdelay(CAPDELAYTIME);//收集距离时刻,为ComBuf[4]*1000的时刻常数
}
//从高到低取
P1=0xff;//初始P1口
ComBuf[0]=0x0b;
temp1=Count/1000;//取前1-2位
ComBuf[1]=temp1;
temp2=Count/10-temp1*100;//得到3-4位
ComBuf[2]=temp2;
ComBuf[3]=Count-(temp1*1000+temp2*10);
SendByteArray();//发送数据
}
//————————————————————————–//
//写一个字节到AT24C04EEPROM
void WriteAT24C04()
{
uchar address,RomData;
address=ComBuf[1];
RomData=ComBuf[2];
WriteByte_24c04(RomData,address);
}
//读取AT24C04EEPROM一个字节
void ReadAT24C04()
{
ComBuf[2]=ReadByte_24c04(ComBuf[1]);
SendByteArray();//发送数据
}
//=================================
// 看门狗设置
//=================================
//void watchdog()
//{
//WDTRST=0x1E;
//WDTRST=0xE1;//喂狗指令
//}
void SetLedData()
{
uchar ShowData,ShowBit;
Timer_Pro_Flag=0;//0为显现处理,1为时序收集处理
ShowBit=ComBuf[1];
ShowData=LED_NUM[ShowBit];//挑选位
ShowData|=ComBuf[2];//显现内容
LED_BIT[ShowBit]=0x00;
LED_BIT[ShowBit]=ShowData;
TH0=(65536-4000)>>8;
TL0=(65536-4000)&0xff;
TR0=ComBuf[3];
if(ComBuf[3]) P0=0x00;//封闭显现
}
/*=========================================
PluckPulse—-时序收集
===========================================*/
void PluckPulse()
{
Timer_Pro_Flag=1;//0为显现处理,1为时序收集处理
OLD_TH0=ComBuf[2];
OLD_TL0=ComBuf[3];
TH0=OLD_TH0;
TL0=OLD_TL0;
TR0=ComBuf[1];//封闭或发动计时器
}
//————
/*=========================================
PWM—-时序收集
===========================================*/
void PWM()
{
Timer_Pro_Flag=2;//0为显现处理,1为时序收集处理
OLD_TH0=ComBuf[2];
OLD_TL0=ComBuf[3];
TH0=OLD_TH0;
TL0=OLD_TL0;
TR0=ComBuf[1];//封闭或发动计时器
}
//————
void timer0(void) interrupt 1 using 1
{
//——————-
switch(Timer_Pro_Flag)
{
case 0:
//LED显现处理
TH0=(0xffff-4000)>>8;
TL0=(0xffff-4000)&0xff;
if (LedCount>4) LedCount=0;
P0=0x00;
P0=LED_BIT[LedCount++];
break;
case 1:
// 时序收集
TH0=OLD_TH0;
TL0=OLD_TL0;
SendByte(IrDA_in_Pin);//发送P1^0引脚状况
break;
case 2://模仿PWM输出
if(!PWMFlag)
{
TH0=OLD_TH0;
TL0=OLD_TL0;
TR0=1;
PWMFlag=1;
PWM_Pin=0;
}
else
{
PWM_Pin=1;
TR0=0;
TH0=OLD_TH0;
TL0=OLD_TL0;
TR0=1;
PWMFlag=0;
}
break;
}
}
/*===================================================================
主程序开端处
===================================================================*/
void main()
{
//晶振:11.0592,波特率:19200
TMOD=0x21;
TL1=0xfd;
TH1=0xfd;
SCON=0xd8;
PCON=0x80;//高位为0时不倍频:9600pbf,1时倍频:19200bpf
TR1=1;
//——————
// TMOD=0x01;//作业在定时器方法1,16位计数器
TH0=(65536-4000)/256;
TL0=(65536-4000)%256;
ET0=1;
EA=1;//中止答应
//————-
while(1)
{
WaitComm();//等候接纳数据
//校正checksum校验码是否正确,如正确则进行相关的操作
if(ISCheckSUM())
{
switch (ComBuf[0])
{
case 0x01:WritePortData(); break; //呼应上位机发送的写操作
case 0x02:SendPortData(); break; //呼应上位机发送的读操作
case 0x03:SetEA();break; //中止答应设定
case 0x04:GetAD();break; //TLC1549数据收集
case 0x05:GetAD_With_VOL_Filter();break;//采软件滤软件的TLC1549数据收集
case 0x06:CD4051_PickVol();break; //CD4051–8选1TLC1549收集
case 0x07:CD4051_LoopPickVol();break; //8路巡检TLC1549收集
case 0x08:ReadAT24C04();break; //读取AT24C04EEPROM一个字节
case 0x09:WriteAT24C04();break; //写一个字节到AT24C04EEPROM
case 0x0a:SetLedData();break; //设定显现的数据
case 0x0b:TestCapCount();break; //电容放电时刻计数测验
case 0x0c:PluckPulse();break; //时序收集
case 0x0d:PWM();break; //操控P2_7模仿输出PWM
}
}
else//如查验过错则回来上位机过错信息
{
ComBuf[0]=0xFF;
SendByteArray();//回来过错信息
}
}
}