这两天在给老师做课题,其中用到DS18B20,因为使用的是PIC16F887单片机,所以操作上要对端口方向做好处理,这里到还好,重要的是我的DS18B20是接的RE0端口,所以,麻烦的事来了,初学者一般不知道RE0默认是AN通道,这把我折腾了整整3天,我一直在调时序,白白浪费我3天时间啊....希望有缘的初学者能看到这篇文章,代码很清晰,比开发板例程易读不知多少倍!(但介于本人初学,对内存的考虑不是很周全,可能占用的RAM跟ROM比例程稍多,不过没什么关系,编译出来内存还空着啦!)
#include
__CONFIG(0x3F31);
#define DQ RE0
#define DQ_HIGH() TRISE0=1
#define DQ_LOW() TRISE0=0;DQ=0
#define E RB0
#define RW RB1
#define RS RB2
#define uchar unsigned char
#define uint unsigned int
#define delay_1us() asm("nop")
#define delay_10us()
asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop")
const uchar
nember[]={'0','1','2','3','4','5','6','7','8','9','-'};//显示采集温度数值数组
void delay(uint x)
{
uint a,b;
for(a=x;a>0;a--)
for(b=110;b>0;b--);
}
void write_com(uchar com)
{
RS=0;//命令选择端开
PORTD=com;
delay(2);//延时
E=1;//使能端送
delay(2);//延时
E=0;
}
void write_data(uchar date)
{
RS=1;//命令选择端开
PORTD=date;
delay(2);//延时
E=1;//使能端送高
delay(2);//延时
E=0;
}
void delay_100us()//9个delay_10us();与2个delay_1us();
{
delay_10us();delay_10us();delay_10us();
delay_10us();delay_10us();delay_10us();
delay_10us();delay_10us();delay_10us();
NOP(); NOP(); NOP(); NOP(); NOP();
}
void display_error(uchar
add) //显示没有检测到DS18B20
{
uchar i=0;
uchar Error[]="Error";
write_com(add);
while(Error[i]!='\0')
{
write_data(Error[i]);
i++;
delay(10);
}
}
void display_dot(uchar
add) //显示温度的小数点
{
write_com(add);
write_data('.');
}
void init_ds18b20()
{
uchar st=1;
DQ_HIGH();//选为输入时,有上拉电阻拉高
NOP();NOP();
while(st)
{
DQ_LOW();
delay_100us();delay_100us();delay_100us();delay_100us();//>=750us
delay_100us();delay_100us();delay_100us();delay_100us();
DQ_HIGH();
delay_10us();delay_10us();delay_10us();delay_10us();//70us
delay_10us();delay_10us();delay_10us();
st=DQ;
if(st==1)
{ display_error(0x80+11);
delay(2);
break; }
delay_100us();delay_100us();delay_100us();
delay_100us();delay_100us();//480us以上
}
}
void write_byte(uchar
date) //向DS18B20写入一个字节数据
{ uchar i,temp;
DQ_HIGH();
NOP();NOP();
for(i=8;i>0;i--)
{ temp=date&0x01; //0000
0001
DQ_LOW();
delay_10us();delay_10us(); //20us
if(temp==1)
DQ_HIGH();
//发“1”
delay_10us();delay_10us();
delay_10us();delay_10us();
NOP();NOP();NOP();NOP();NOP(); //45us以上
DQ_HIGH();
date=date>>1; //右移1位 }
}
uchar
read_byte() //从DS18B20读取一个字节数据
{ uchar i,date;
static bit j;
for(i=8;i>0;i--)
{
date=date>>1;
DQ_HIGH();
NOP();NOP();
DQ_LOW();
NOP();NOP();NOP();
NOP();NOP();NOP();
DQ_HIGH();
NOP();NOP();
NOP();NOP();
j=DQ;
if(j==1)
date=date|0x80; //1000 0000
delay_10us();delay_10us();delay_10us(); //30us }
return (date);
}
void init()
{
//端口方向初始化
TRISD=0;
TRISB=0;
TRISA=0;
PORTA=0;
TRISE=0;
PORTE=0;
ADCON1=0x8d;//RE全为I/O
//1602init
RS=0;
RW=0;
write_com(0x38);//打开显示模式
write_com(0x0c);//光标显示并闪烁
write_com(0x06);//读写指针、光标加,整屏不移动 write_com(0x01);//清屏
write_com(0x80);//数组指针显示
}
void main() //做好读温度的准备
{
float temp;
int temper1; int temp1,temp2;
uchar a1,a2,a3,a4,a5;
init();
while(1)
{
init_ds18b20(); //初始化DS18B20,读取应答信号
write_byte(0xcc); //
跳过读序列号的操作
write_byte(0x44); //
启动温度转换
delay(780); //750ms
delay_100us();
init_ds18b20();
write_byte(0xcc);
write_byte(0xbe); //读取温度寄存器,前两个分别是温度的低位和高位
temp1=read_byte();
temp2=read_byte();
delay(10);
temper1=(temp2<<8|temp1);
if((temper1&0xf000)==0) //高字节前5位为0表明为正温度
{
temper1*=62.5;
}
else //为负温度时,读回会数据需取反加1
{ temper1=(~temper1)+1;
temper1*=62.5;
write_com(0x80+8);
write_data(nember[10]);//显示负号
}
a1=temper1/10000;
a2=temper1000/1000;
a3=temper100/100;
a4=temper10/10;
a5=temper1;//
write_com(0x80+9);
write_data(nember[a1]);
write_data(nember[a2]);
display_dot(0x80+11);
write_com(0x80+12);
write_data(nember[a3]);
write_data(nember[a4]);
write_data(nember[a5]);
}
}