上一篇我说了我们做的是两个模块,但是介于DS18B20的资料太多,我就不再阐述,直接给最终的代码。
该代码是将温度传感器与YL69湿度传感器获取的数据在51单片机LCD上显示出来
#include <reg52.H>
#include <intrins.H>
#include <math.H>
#define uchar unsigned char
#define uint unsigned int
#define AT24C02_ADDR 0xa0 //AT24C02地址
#define DAC_EN 0x40
#define ADC_AutoINC 0x04
#define PCF8591_ADDR 0x90 //PCF8591地址
sbit SDA = P2^0;
sbit SCL = P2^1;
sbit LCDEN=P3^4;
sbit LCD_RS = P3^5; //LCD写数据或命令控制IO
sbit LCD_RW = P3^6; //LCD读写控制IO
sbit dula = P2^6;
sbit wela = P2^7;
sbit IN1=P1^0;
sbit IN2=P1^1;
sbit ENA=P1^2;
sbit beep = P2^3;
uint strHEX[4];INT8UtostrHEX函数转换的字符串显示10进制
void delayUs()
{
_nop_();
}
void delayMs(uint a)
{
uint i, j;
for(i = a; i > 0; i--)
for(j = 100; j > 0; j--);
}
/*I2C初始化*/
void I2C_init()
{
SDA = 1;
_nop_();
SCL = 1;
_nop_();
}
/*I2C起始信号*/
void I2C_Start()
{
SCL = 1;
_nop_();
SDA = 1;
delayUs();
SDA = 0;
delayUs();
}
/*I2C终止信号*/
void I2C_Stop()
{
SDA = 0;
_nop_();
SCL = 1;
delayUs();
SDA = 1;
delayUs();
}
/*主机发送应答*/
void Master_ACK(bit i)
{
SCL = 0; // 拉低时钟总线允许SDA数据总线上的数据变化
_nop_(); // 让总线稳定
if (i) //如果i = 1 那么拉低数据总线 表示主机应答
{
SDA = 0;
}
else
{
SDA = 1; //发送非应答
}
_nop_();//让总线稳定
SCL = 1;//拉高时钟总线 让从机从SDA线上读走 主机的应答信号
delayUs();
SCL = 0;//拉低时钟总线, 占用总线继续通信
_nop_();
SDA = 1;//释放SDA数据总线。
_nop_();
}
/*检测从机应答*/
bit Test_ACK()
{
SCL = 1;
delayUs();
if (SDA)
{
SCL = 0;
_nop_();
I2C_Stop();
return(0);
}
else
{
SCL = 0;
_nop_();
return(1);
}
}
/*发送一个字节*/
void I2C_send_byte(uchar byte)
{
uchar i;
for(i = 0 ; i < 8 ; i++)
{
SCL = 0;
_nop_();
if (byte & 0x80)
{
SDA = 1;
_nop_();
}
else
{
SDA = 0;
_nop_();
}
SCL = 1;
_nop_();
byte <<= 1; // 0101 0100B
}
SCL = 0;
_nop_();
SDA = 1;
_nop_();
}
/*I2C 读一字节*/
uchar I2C_read_byte()
{
uchar dat,i;
SCL = 0;
_nop_();
SDA = 1;
_nop_();
for(i = 0 ; i < 8 ; i++)
{
SCL = 1;
_nop_();
if (SDA)
{
dat |= 0x01; //
}
else
{
dat &= 0xfe; //1111 1110
}
_nop_();
SCL = 0 ;
_nop_();
if(i < 7)
{
dat = dat << 1;
}
}
return(dat);
}
uint *INT8UtostrHEX(uint num) //将一个字节的数据转换为字符串
{
uint i = 0;
switch(num/100)
{
case 0: strHEX[i] = '0'; i++; break;
case 1: strHEX[i] = '1'; i++; break;
case 2: strHEX[i] = '2'; i++; break;
case 3: strHEX[i] = '3'; i++; break;
case 4: strHEX[i] = '4'; i++; break;
case 5: strHEX[i] = '5'; i++; break;
case 6: strHEX[i] = '6'; i++; break;
case 7: strHEX[i] = '7'; i++; break;
case 8: strHEX[i] = '8'; i++; break;
case 9: strHEX[i] = '9'; i++; break;
}
switch(num%100/10)
{
case 0: strHEX[i] = '0'; i++; break;
case 1: strHEX[i] = '1'; i++; break;
case 2: strHEX[i] = '2'; i++; break;
case 3: strHEX[i] = '3'; i++; break;
case 4: strHEX[i] = '4'; i++; break;
case 5: strHEX[i] = '5'; i++; break;
case 6: strHEX[i] = '6'; i++; break;
case 7: strHEX[i] = '7'; i++; break;
case 8: strHEX[i] = '8'; i++; break;
case 9: strHEX[i] = '9'; i++; break;
}
switch(num%100%10)
{
case 0: strHEX[i] = '0'; i++; break;
case 1: strHEX[i] = '1'; i++; break;
case 2: strHEX[i] = '2'; i++; break;
case 3: strHEX[i] = '3'; i++; break;
case 4: strHEX[i] = '4'; i++; break;
case 5: strHEX[i] = '5'; i++; break;
case 6: strHEX[i] = '6'; i++; break;
case 7: strHEX[i] = '7'; i++; break;
case 8: strHEX[i] = '8'; i++; break;
case 9: strHEX[i] = '9'; i++; break;
}
strHEX[3] = '\0';
return (strHEX);
}
//写命令
void writeComm(uchar comm)
{
LCD_RS = 0;
P0 = comm;
LCDEN = 1;
delayUs();
LCDEN = 0;
delayMs(1);
}
//写数据
void writeData(uchar dat)
{
LCD_RS = 1;
P0 = dat;
LCDEN = 1;
delayUs();
LCDEN = 0;
delayMs(1);
}
//写字符串
void writeString(uchar * str, uchar length)
{
uchar i;
for(i = 0; i < length; i++)
{
writeData(str[i]);
}
}
//写字符串
void LCD1602_Write_String(uint x,uint y,uint *s) //LCD1602写字符串
{
if (y == 0)
{
writeComm(0x80 + x); //表示第一行
}
else
{
writeComm(0xC0 + x); //表示第二行
}
while (*s != '\0')
{
writeData(*s++);
}
}
//初始化显示屏
void init()
{
LCD_RW = 0;
dula = wela = 0;
writeComm(0x38);// 16*2显示
writeComm(0x0c); //打开显示
writeComm(0x06);//显示指针加1
writeComm(0x01); //清屏
}
/*ad读数据*/
bit I2C_ADC_ReadData(uchar ADDR, uint *ADC_Value)
{
I2C_Start() ;
I2C_send_byte(PCF8591_ADDR + 0);
if (!Test_ACK())
{
return(0);
}
I2C_send_byte(ADDR);
Master_ACK(0);
I2C_Start();
I2C_send_byte(PCF8591_ADDR + 1);
if (!Test_ACK())
{
return(0);
}
*ADC_Value = I2C_read_byte();
Master_ACK(0);
I2C_Stop();
return(1);
}
/*DS18B02温度*/
sbit ds = P2^2;
void dsInit()
{
unsigned int i;
ds = 0;
i = 100;
while(i>0) i--;
ds = 1;
i = 4;
while(i>0) i--;
}
void dsWait()
{
unsigned int i;
while(ds);
while(~ds);
i = 4;
while(i > 0) i--;
}
bit readBit() //读时序
{
unsigned int i;
bit b;
ds = 0;
i++;
ds = 1;
i++; i++;
b = ds;
i = 8;
while(i>0) i--;
return b;
}
unsigned char readByte()
{
unsigned int i;
unsigned char j, dat;
dat = 0;
for(i=0; i<8; i++)
{
j = readBit();
dat = (j << 7) | (dat >> 1);
}
return dat;
}
void writeByte(unsigned char dat) //用于写命令使用
{
unsigned int i;
unsigned char j;
bit b;
for(j = 0; j < 8; j++)
{
b = dat & 0x01;
dat >>= 1;
if(b)
{
ds = 0; i++; i++;
ds = 1;
i = 8; while(i>0) i--;
}
else
{
ds = 0;
i = 8; while(i>0) i--;
ds = 1;
i++; i++;
}
}
}
void sendChangeCmd()
{
dsInit();
dsWait();
delayMs(1);
writeByte(0xcc);//发送跳跃ROM指令
writeByte(0x44);// 发送温度转换指令
}
void sendReadCmd()
{
dsInit();
dsWait();
delayMs(1);
writeByte(0xcc); //发送跳跃指令
writeByte(0xbe); //读取DS18B20暂存器的值
}
int getTmpValue()
{
unsigned int tmpvalue;
int value;
float t;
unsigned char low, high;
sendReadCmd();
low = readByte();//只读取前温度的值
high = readByte();
tmpvalue = high;//将两个字节温度的值储存在tempvalue中
tmpvalue <<= 8;
tmpvalue |= low;
value = tmpvalue;
t = value * 0.0625;//将二进制的值转换为十进制
value = t * 100 + (value > 0 ? 0.5 : -0.5); //大于0加0.5, 小于0减0.5
return value;
}
void display(int v) //LCD显示温度的值
{
unsigned char count;
unsigned char datas[] = {0, 0, 0, 0, 0};
unsigned int tmp = abs(v);
datas[0] = tmp / 10000;
datas[1] = tmp % 10000 / 1000;
datas[2] = tmp % 1000 / 100;
datas[3] = tmp % 100 / 10;
datas[4] = tmp % 10;
writeComm(0x80|0x07);
if(datas[0] != 0)
{
writeData('0'+datas[0]);
}
for(count = 1; count != 5; count++)
{
writeData('0'+datas[count]);
if(count == 2)
{
writeData('.');
}
}
}
void main()
{
int a;
uint ADC_Value;
uchar table1[] = "wendu: ";
uchar table2[] = "shidu: ";
sendChangeCmd();
init();
I2C_init();
writeComm(0x80);
writeString(table1, 6);
writeComm(0x80 | 0x40);
writeString(table2, 6);
while(1)
{
delayMs(1000); //温度转换时间需要750ms以上
sendChangeCmd();
writeComm(0xc0);
do{
a=getTmpValue();
}while(a>=8500) ;
display(a);
if(a>=3000)
{
ENA=1;
IN1=0;
IN2=1;
beep=0;
}
else
{
ENA=0;
beep=1;
}
I2C_ADC_ReadData(3, &ADC_Value);
LCD1602_Write_String(7,1,INT8UtostrHEX(ADC_Value));
delayMs(400);
}
}
其实一个用的I2C,一个用的单总线,所以将两个分别写出来之后合在一个程序里面即可。