硬件:STC89C52开发板、1602数显、DHT温湿度传感器(利用其I2C数据线)
软件:Keil v4 C语言编译平台,stc-ipc-15xx-v6.85烧录程序
对DHT温度数据的解析有点问题,室温25度左右,温度的高、低位的最高位均为1,这与硬件说明文档不符,我就都屏蔽掉了最高位,得出的温度数据还比较靠谱,也不知怎么回事,各位读者如果知道的话不吝赐教!!!
DHT12的SDA、SCL分别接P2.0、P2.1,连接好电源、接地后采集结果如下图:
串口传出的数据分别通过P3.0 、P3.1传送给arm开发板
数据格式是每个数据前后各加一个空格,如:“空格78.5空格空格25.6空格……”,51单片机开发板的miniUSB是一个串口转USB的设备,通过它插入PC,就可以在PC端查看串口传出来的数据了,PC上通过串口助手看到单片机输出的结果,如下图:
代码:包括了利用I2C时序采集DHT的温湿度数据、1602显示采集到数据、利用串口中断将数据发送出去三大部分
#include
#include
#define AddWr 0xb8 //自己买的温湿度传感器DHT12器件地址
sbit sda=P2^0;
sbit scl=P2^1;
sbit rw = P1^1;
sbit RS = P1^0;
sbit LCDEN = P2^5;
unsigned char bai,shi,ge;
unsigned char table1[]="wendu:";
unsigned char table2[]="shidu:";
unsigned char table3[]="0123456789";
unsigned char table4[]="CRC Error !!!";
unsigned char table5[]="Sensor Error !!";
int Temprature,Humi;
void delayUs()
{
_nop_();
}
void delayMs(unsigned int a)
{
unsigned int i, j;
for(i = a; i > 0; i--)
for(j = 100; j > 0; j--);
}
void delay(unsigned char dat)
{
unsigned char i;
for(i=dat;i>0;i--)_nop_();
}
/*******************************************************************
总线初始化
函数原型: void I2c_Init();
功能: 启动I2C总线,即发送I2C起始条件.
********************************************************************/
void I2c_Init()
{
sda=1;
_nop_(); /*起始条件建立时间大于4.7us,延时*/
_nop_();
_nop_();
_nop_();
_nop_();
scl=1;
_nop_(); /*起始条件建立时间大于4.7us,延时*/
_nop_();
_nop_();
_nop_();
_nop_();
}
/*******************************************************************
起动总线函数
函数原型: void IIC_Start();
功能: 启动I2C总线,即发送I2C起始条件.
********************************************************************/
void IIC_Start()
{
sda=1; /*发送起始条件的数据信号*/
_nop_();
scl=1;
_nop_(); /*起始条件建立时间大于4.7us,延时*/
_nop_();
_nop_();
_nop_();
_nop_();
sda=0; /*发送起始信号*/
_nop_(); /* 起始条件锁定时间大于4μs*/
_nop_();
_nop_();
_nop_();
_nop_();
scl=0; /*钳住I2C总线,准备发送或接收数据 */
_nop_();
_nop_();
}
/*******************************************************************
结束总线函数
函数原型: void IIC_Stop();
功能: 结束I2C总线,即发送I2C结束条件.
********************************************************************/
void IIC_Stop()
{
sda=0; /*发送结束条件的数据信号*/
_nop_(); /*发送结束条件的时钟信号*/
scl=1; /*结束条件建立时间大于4μs*/
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
sda=1; /*发送I2C总线结束信号*/
_nop_();
_nop_();
_nop_();
_nop_();
}
/*******************************************************************
字节数据发送函数
函数原型: void IIC_Send_Byte(UCHAR c);
功能: 将数据c发送出去,可以是地址,也可以是数据,发完后等待应答,并对
此状态位进行操作.(不应答或非应答都使ack=0)
发送数据正常,ack=1; ack=0表示被控器无应答或损坏。
********************************************************************/
void IIC_Send_Byte(unsigned char c)
{
unsigned char i;
for(i=0;i<8;i++) /*要传送的数据长度为8位*/
{
if((c<250)
{
IIC_Stop();
return 1;
}
delay(1);
}
scl=0;//时钟输出0
delay(50); //这个等待很重要,校验错误的几率小多了
return 0;
}
/*****************************1602*******************************/
void writeComm(unsigned char comm)
{
RS = 0;
P0 = comm;
LCDEN = 1;
delayUs();
LCDEN = 0;
delayMs(1);
}
void init1602()
{
rw = 0;
writeComm(0x38);
writeComm(0x0c);
writeComm(0x06);
writeComm(0x01);
}
//写数据:RS=1, RW=0;
void writeData(unsigned char dat)
{
RS = 1;
P0 = dat;
LCDEN = 1;
delayUs();
LCDEN = 0;
delayMs(1);
}
void writeString(unsigned char * str, unsigned char length)
{
unsigned char i;
for(i = 0; i < length; i++)
{
writeData(str[i]);
}
}
/*------------------------------------------------
把uint 类型的温度、值转化为ascii码字符,
通过串口输出温度采集值
------------------------------------------------*/
void serialOutput(int num)
{
SBUF=' ';
delay(50);
SBUF=num/100+'0';
delay(50);
SBUF=num/10%10+'0';
delay(50);
SBUF='.';
delay(50);
SBUF=num%10+'0';
delay(50);
SBUF=' ';
delay(50);
}
/*------------------------------------------------
串口输出初始化函数
------------------------------------------------*/
void serial_init()
{
SCON=0x40;//初始化串口方式1,REN=0不允许接受
PCON=0x80; //SMOD=1 波特率倍增
TMOD=0x20; //T1初始化为方式2,自动填充
TL1=0xF4; //波特率4800b/s
TH1=0xF4;
ES=1; //不打开串口中断,TI=1时依然可以串口输出,需要在上次输出后加点延时
EA=1; //打开中断总开关
TR1=1; //T1开始运转
}
/*------------------------------------------------
串口输出中断函数
------------------------------------------------*/
void SISR() interrupt 4
{
T1=0;
}
/*********************************************************************************************************
功能:DHT12读取温湿度函数
变量:Humi_H:湿度高位;Humi_L:湿度低位;Temp_H:温度高位;Temp_L:温度低位;Temp_CAL:校验位
数据格式为:湿度高位(湿度整数)+湿度低位(湿度小数)+温度高位(温度整数)+温度低位(温度小数)+ 校验位
校验:校验位=湿度高位+湿度低位+温度高位+温度低位
*********************************************************************************************************/
void sensor_read(void)
{
int i;
unsigned char Humi_H,Humi_L,Temp_H,Temp_L,Temp_CAL,temp;
IIC_Start(); //主机发送起始信号
IIC_Send_Byte(0Xb8); //发送IIC地址
if(!IIC_Wait_Ack()) //等待从机应答信号(如无应答:考虑IIC通讯频率是否太快,或者传感器接线错误)
{
i=0;
IIC_Send_Byte(0);
while(IIC_Wait_Ack())//等待从机应答信号
{
if(++i>=500)
{
break;
}
}
i=0;
IIC_Start(); //主机发送起始信号
IIC_Send_Byte(0Xb9); //发送读指令
while(IIC_Wait_Ack()) //等待从机应答信号
{
if(++i>=500)
{
break;
}
}
Humi_H=IIC_Read_Byte(); //读取湿度高位
Ack_I2c(0);
Humi_L=IIC_Read_Byte(); //读取湿度低位
Ack_I2c(0);
Temp_H=IIC_Read_Byte(); //读取温度高位
Ack_I2c(0);
Temp_L=IIC_Read_Byte(); //读取温度低位
Ack_I2c(0);
Temp_CAL=IIC_Read_Byte(); //读取校验位
Ack_I2c(1);
IIC_Stop(); //发送停止信号
temp = (unsigned char)(Humi_H+Humi_L+Temp_H+Temp_L);//只取低8位
if(Temp_CAL==temp)//如果校验成功,往下运行
{
Humi=Humi_H*10+Humi_L; //湿度
//实践得出,在室温约27°时,温度高位Temp_H、温度低位Temp_L的最高位都是1,按照DHT12的技术说明,温度低位Temp_L的最高位是1的话表示是负值,明显不对
//温度高位Temp_H的数值是10011011(155)也不对啊,那有这高的温度,将最高位去掉为0时,温度高位为27度,这还差不多。
//温度高位Temp_H的数值是128-137,如果去掉最高位为零的话,那么是0-9,这是对的
//Temp_H、Temp_L都去掉最高位后合体的温度约为27.5度,且可以环境温度上下浮动变化,感觉这样就对了。
//那么我的这个传感器的负温度是怎么表示的呢?这个得在温度小于零的环境中试试Temp_H、Temp_L读到的都是什么值就可以了
Temprature =(Temp_H&0x7F)*10+(Temp_L&0x7F);
/*if(Temp_L&0X80) //为负温度
{
//Temprature =0-(Temp_H*10+((Temp_L&0x7F)));
// Temprature=Temp_L;
//Temprature =(int)((char)0-(Temp_L&0x7F));
//Temprature=Temp_H&0x7F;
Temprature =(int)((char)0-((Temp_H&0x7F)*10+(Temp_L&0x7F)));
}
else //为正温度
{
//Temprature=(Temp_H*10+(Temp_L&0x7F));//为正温度
Temprature=Temp_H;
}*/
//判断温湿度是否超过测量范围(温度:-20℃~60摄氏度;湿度:20%RH~95%RH)
if(Humi>950)
{
Humi=950;
}
if(Humi<200)
{
Humi =200;
}
if(Temprature>1000)
{
Temprature=1000;
}
if(Temprature<-200)
{
Temprature = -200;
}
i=Humi;
bai=i/100;shi=i%100/10;ge=i%100%10;
writeComm(0x80);
writeString(table2,6);
writeData(table3[bai]);
writeData(table3[shi]);
writeData('.');
writeData(table3[ge]);
writeData('%');
writeData('R');
writeData('H');
i=Temprature;
if(i<0)
{
i=-i;
bai=i/100;shi=i%100/10;ge=i%100%10;
writeComm(0x80+0x40);
writeString(table1,6);
writeData('-');
writeData(table3[bai]);
writeData(table3[shi]);
writeData('.');
writeData(table3[ge]);
writeData(0xdf);
writeData('C');
}else{
bai=i/100;shi=i%100/10;ge=i%100%10;
writeComm(0x80+0x40);
writeString(table1,6);
writeData('+');
writeData(table3[bai]);
writeData(table3[shi]);
writeData('.');
writeData(table3[ge]);
writeData(0xdf); //"℃中的左上角圆圈的ascii码"
writeData('C');
}
serialOutput(Humi);
serialOutput(Temprature);
}
else //校验失败
{
writeComm(0x80);
writeString(table4,12);
}
}else
{
writeComm(0x80+0x40);
writeString(table5,15);
}
}
//******************main()主函数********************************
void main()
{
void I2c_Init();
init1602();
serial_init();
while(1)
{
sensor_read();
delayMs(2000);
}
}