#include <reg52.h>
#include <intrins.h>
#define LCDClear() LCDWriteCmd(0x01)
#define CRC8Check(dat, size) (CRC8(dat, size) == dat[size])
//#define TEMPTEST
sbit RS = P2^0;
sbit RW = P2^1;
sbit E = P2^2;
sbit BF = P0^7;
sbit DQ = P3^3;
sbit K1 = P1^4;
unsigned char serial[8]; // 64位序列号
void delay(unsigned int n)
{
unsigned char i;
while (n--)
for (i = 0; i < 115; i++);
}
void LCDBusyTest(void)
{
E = 0;
RS = 0;
RW = 1;
E = 1;
BF = 1;
while (BF);
E = 0;
}
void LCDWriteCmd(unsigned char cmd)
{
LCDBusyTest();
RS = 0;
RW = 0;
E = 1;
P0 = cmd;
E = 0;
}
void LCDWriteData(unsigned char dat)
{
LCDBusyTest();
RS = 1;
RW = 0;
E = 1;
P0 = dat;
E = 0;
}
void LCDWriteString(char *s)
{
while (*s)
LCDWriteData(*s++);
}
void LCDWriteHex(unsigned char num)
{
char *list = "0123456789ABCDEF";
LCDWriteData(list[num >> 4]);
LCDWriteData(list[num & 0x0f]);
}
void LCDWriteNumber(char num)
{
if (num < 0)
{
LCDWriteData('-');
num = -num;
}
else if (num == 0)
LCDWriteData(' ');
else
LCDWriteData('+');
LCDWriteData(num / 100 + '0');
LCDWriteData(num % 100 / 10 + '0');
LCDWriteData(num % 10 + '0');
}
void LCDInit(void)
{
delay(40);
LCDWriteCmd(0x30);
delay(10);
LCDWriteCmd(0x30);
delay(10);
LCDWriteCmd(0x0c);
delay(1);
LCDWriteCmd(0x01);
}
void wait(unsigned int p)
{
TMOD = 0x01;
TL0 = p & 0xff;
TH0 = p >> 8;
TF0 = 0;
TR0 = 1;
while (!TF0);
TR0 = 0;
}
bit DQInit(void)
{
bit flag; // DS18B20是否存在的标志
DQ = 1; // 先将数据线拉高
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
DQ = 0;
wait(0xfd68); // 720us
DQ = 1;
wait(0xffc8); // 60us
flag = !DQ;
wait(0xfe33); // 500us
return flag;
}
bit DQReadBit(void)
{
bit result;
DQ = 1;
_nop_();
DQ = 0; // 将总线拉低, 启动读时序
_nop_();
DQ = 1; // 释放总线, 准备读, 必须在15us(包括拉低总线的时间)的时间内读完端口
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
result = DQ;
// 15us过后, 数据线已经被拉高, 此时已无法读数据
wait(0xffd3); // 48us
return result;
}
unsigned char DQRead(void)
{
unsigned char dat = 0;
unsigned char i;
for (i = 0; i < 8; i++)
{
dat >>= 1;
if (DQReadBit())
dat |= 0x80;
}
return dat;
}
void DQWriteBit(bit dat)
{
DQ = 1; // 先将数据线拉高
_nop_();
DQ = 0; // 将数据线从高拉低时即启动写时序
_nop_();
_nop_();
DQ = dat;
wait(0xffc8); // 60us
DQ = 1;
_nop_();
_nop_();
}
void DQWrite(unsigned char dat)
{
unsigned char i;
for (i = 0; i < 8; i++)
{
DQWriteBit(dat & 1);
dat >>= 1;
}
}
unsigned char CRC8(unsigned char dat[], unsigned char size)
{
unsigned char buf;
unsigned char crc = 0;
unsigned char i, j;
for (i = 0; i < size; i++)
{
buf = dat[i];
for (j = 0; j < 8; j++)
{
if (((crc ^ buf) & 1) == 0)
crc >>= 1;
else
{
crc ^= 0x18;
crc >>= 1;
crc |= 0x80;
}
buf >>= 1;
}
}
return crc;
}
bit DQReadSerial(void)
{
unsigned char i;
DQInit();
DQWrite(0x33);
for (i = 0; i < 8; i++)
serial[i] = DQRead();
return CRC8Check(serial, 7);
}
void DQWriteSerial(bit skip)
{
unsigned char i;
if (!skip)
{
DQWrite(0x55);
for (i = 0; i < 8; i++)
DQWrite(serial[i]);
}
else
DQWrite(0xcc);
}
// 读64位序列号(总线上只有1个DS18B20时才能使用这个命令)
void ShowSerial(void)
{
bit flag = DQReadSerial();
unsigned char i;
LCDClear();
LCDWriteString("序列号:");
LCDWriteCmd(0x90);
for (i = 0; i < 8; i++)
LCDWriteHex(serial[i]);
LCDWriteCmd(0x88);
if (flag)
LCDWriteString("已通\xb9\xfd校验"); // 因为编码原因, '过'字不能直接在源文件中使用
else
LCDWriteString("未通\xb9\xfd校验");
LCDWriteCmd(0x98);
LCDWriteString("电源模式: ");
DQInit();
DQWrite(0xb4);
if (DQReadBit())
LCDWriteString("外部");
else
LCDWriteString("寄生");
}
// 按下K1键后继续
void pause(void)
{
while (1)
{
if (!K1)
{
delay(15);
if (!K1)
{
while (!K1);
break;
}
}
}
}
void ShowTemp(bit skip_rom)
{
unsigned char dat[9];
unsigned int i;
#ifdef TEMPTEST
unsigned char list[] = {0xff, 0xff, 0xfd, 0xfe, 0xfd, 0xff, 0xfe, 0x00, 0xfe, 0x01, 0xfe, 0x02, 0x07, 0xd0, 0x01, 0x91, 0x00, 0xa2, 0x00, 0x08, 0x00, 0x00, 0xff, 0xf8, 0xff, 0x5e, 0xfe, 0x6f, 0xfc, 0x90};
static char p = 0; // 温度列表测试
#endif
DQInit();
DQWriteSerial(skip_rom);
DQWrite(0x44); // 开始转换
delay(1000);
DQInit();
DQWriteSerial(skip_rom);
DQWrite(0xbe);
for (i = 0; i < 9; i++)
dat[i] = DQRead();
#ifdef TEMPTEST
dat[0] = list[p + 1];
dat[1] = list[p];
p += 2;
if (p == sizeof(list))
p = 0;
#endif
LCDWriteCmd(0x80);
#ifndef TEMPTEST
if (CRC8Check(dat, 8))
#endif
{
//LCDWriteString("\xca\xfd据\xd5\xfd确");
LCDWriteString("温度:");
if (dat[1] & 0xf8)
{
// 只要高5位有1位是1, 就是负温度
dat[0] = ~dat[0];
dat[1] = ~dat[1];
dat[1] &= 0x7ff; // 去掉高5位
dat[0]++; // 低位加1
if (dat[0] == 0)
dat[1]++; // 进位
LCDWriteData('-');
}
else
{
if (dat[0] == 0 && dat[1] == 0)
LCDWriteData(' ');
else
LCDWriteData('+');
}
i = (dat[1] & 0x07) << 4;
i |= (dat[0] & 0xf0) >> 4;
LCDWriteData('0' + i / 100);
LCDWriteData('0' + i % 100 / 10);
LCDWriteData('0' + i % 10);
LCDWriteData('.');
i = (dat[0] & 0x0f) * 625;
LCDWriteData('0' + i / 1000);
LCDWriteData('0' + i % 1000 / 100);
LCDWriteData('0' + i % 100 / 10);
LCDWriteData('0' + i % 10);
LCDWriteString("℃");
}
#ifndef TEMPTEST
else
LCDWriteString("\xca\xfd据错误");
#endif
LCDWriteCmd(0x90);
LCDWriteString("范围:");
LCDWriteNumber(dat[3]);
LCDWriteData('~');
LCDWriteNumber(dat[2]);
LCDWriteString("℃");
LCDWriteCmd(0x88);
LCDWriteString("分辨率:");
i = (dat[4] >> 5 & 0x03) + 9;
if (i >= 10)
{
LCDWriteData(' ');
LCDWriteData('0' + i / 10);
}
LCDWriteData('0' + i % 10);
LCDWriteString("位");
if (i < 10)
LCDWriteString(" ");
#ifdef TEMPTEST
pause();
#endif
}
void config(void)
{
DQInit();
DQWriteSerial(0);
DQWrite(0x4e);
DQWrite(30); // 温度上限
DQWrite(-13); // 温度下限
DQWrite(0x7f); // 12位分辨率
DQInit();
DQWriteSerial(0);
DQWrite(0x48); // 把温度报警值存入EEPROM中(关闭电源再打开会自动读取)
delay(10);
// 重设RAM中的报警值, 但不保存到EEPROM
/*
DQInit();
DQWriteSerial(0);
DQWrite(0x4e);
DQWrite(50); // 温度上限
DQWrite(10); // 温度下限
DQWrite(0x7f); // 12位分辨率
DQInit();
DQWriteSerial(0); // 从EEPROM中读取温度报警值
DQWrite(0xb8);
delay(10);
*/
}
int main(void)
{
LCDInit();
if (!DQInit())
{
LCDWriteString("无温度传感器");
while (1);
}
ShowSerial();
pause();
config();
LCDClear();
while (1)
{
ShowTemp(0);
}
}