18B20 & LCD1602(PIC)

本文详细介绍了如何使用单总线协议与18B20温度传感器进行数据交互,并展示了如何将读取的温度数据显示在LCD1602上。文中还探讨了精确延时的重要性及其实现方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

对于18B20,它是单总线协议,我们对于它,只有读取温度数据才有意义。
对它的读写主要是两张时序图
18B20 <wbr>& <wbr>LCD1602(PIC)
对写的操作
void write_byte(uchar dat)
{
uchar i;
for(i=0;i<8;i++)
{
TRISA4=0;
bus=0;
nop;
nop;
bus=dat&0x01;
delayms(5,3);
TRISA4=1; //释放总线
nop;
nop;
dat>>=1;
}
}

18B20 <wbr>& <wbr>LCD1602(PIC)
对读的操作
uchar read_byte()
{
uchar i,dat=0;
for(i=0;i<8;i++)
{
dat>>=1;
TRISA4=0;
bus=0;
nop;
nop;
nop;
nop;
nop;
TRISA4=1;
nop;
nop;
nop;
nop;
nop;
if(bus==1)dat=dat|0x80;
else dat=dat|0x00;
delayms(2,7); //这里延时时间略长可以使读数比较稳定
}
return dat;
}

还有一个时序是关于复位信号的,或者说是给18B20说“要开始工作了”的信号
18B20 <wbr>& <wbr>LCD1602(PIC)
uchar reset()
{
TRISA4=0;
bus=0;
delayms(10,25);
TRISA4=1;
delayms(5,1);
if(bus==1)
{
delayms(2,60);
return 0;
}
else 
{
delayms(2,60);
return 1;
}
}


但是18B20的单总线协议对延时操作的要求貌似很高,我在粗糙的延时下都读不出数据,显示的全是0,后来发现是延时长短的缘故

我看到有个人写的挺不错的,但是不能转载,就直接贴过来了
cite:

PICC精确延时

延时函数深入了解一下就能设计出一个理想的框价出来。

一般的我们都用

for(x=100;--x;){;}

等价于
x=100;
while(--x){;};  或 for(x=0;x<100;x++){;}

来写一个延时函数。

在这里要特别注意:X=100,并不表示只运行100个指令时间就跳出循环。

可以看看编译后的汇编:

x=100;while(--x){;}

汇编后:
 movlw 100
 bcf 0x03,5
 bcf  0x03,6
 movwf _delay
loop decfsz _delay
 goto loop
 return

从代码可以看出总的指令是是303个,其公式是8+3*(X-1)。注意其中循环周期是X-1是99个。

这里总结的是x为char类型的循环体,当x为int时候,其中受X值的影响较大。

建议设计一个char类型的循环体,然后再用一个循环体来调用它,可以实现精确的长时间的延时。

下面给出一个能精确控制延时的函数,此函数的汇编代码是最简洁、最能精确控制指令时间的:

void delay(char x,char y){
 char z;
 do{
  z=y;
  do{;}
  while(--z);
 }
 while(--x);
}

其指令时间为:7+(3*(Y-1)+7)*(X-1)

如果再加上函数调用的call指令、页面设定、传递参数花掉的7个指令。

则是:14+(3*(Y-1)+7)*(X-1)。

如果要求不是特别严格的延时,可以用这个函数:

void delay(){
 unsigned int d=1000;
 while(--d){;}
}

此函数在4M晶体下产生10003us的延时,也就是10MS。

如果把D改成2000,则是20003us,以此类推。

有朋友不明白,为什么不用while(x--) 后减量,来控制设定X值是多少就循环多少周期呢?

现在看看编译它的汇编代码:

 bcf 3,5
 bcf 3,6  
 movlw 10
 movwf   _delay
loop
 decf     _delay
 incfsz    _delay,w
 goto loop
 return

可以看出循环体中多了一条指令,不简洁。所以在PICC中最好用 前减量 来控制循环体。

再谈谈这样的语句:

for(x=100;--x;){;}和for(x=0;x<100;x++){;}

从字面上看2者意思一样,但可以通过汇编查看代码。后者代码雍长,而前者就很好的汇编出了简洁的代码。

所以在PICC中最好用前者的形式来写循环体,好的C编译器会自动把增量循环化为减量循环。因为这是由处理器硬件特性决定的。

PICC并不是一个很智能的C编译器,所以还是人脑才是第一的,掌握一些经验对写出高效,简洁的代码是有好处的

endcite

那个延时公式还是很重要的,单位是us




之后就贴上代码

#include       //调用头文件,可以去PICC18软件下去查找PIC18FXX2.H

#include

__CONFIG(1,XT) ;         //晶振为外部4M

__CONFIG(2,WDTDIS) ;      //看门狗关闭

__CONFIG(4,LVPDIS) ;     //禁止低电压编程

#define uint unsigned int

#define uchar unsigned char

#define bus RA4

#define nop NOP()

#define en RB3

#define rw RB4

#define rs RB5

#define D PORTD


unsigned char  temp1;                        //采集到的温度高8位

unsigned char  temp2;                         //采集到的温度低8位

//转换后的温度值小数点部分查表

const unsigned char tablexiao[16]={0,0,1,2,2,3,4,4,5,6,6,7,8,8,9,9};

char p[]="#@";

char k[]="kaito";



void delay(uint x)

{

int i,j;

for(i=0;i<=110;i++)

for(j=0;j<=x;j++);

}


void write_com(uchar com)

{

rs=0;

rw=0;

delay(5);

en=1;

delay(5);

D=com;

en=0;

delay(5);

}


void write_dat(uchar dat)

{

rs=1;

rw=0;

delay(5);

en=1;

delay(5);

D=dat;

en=0;

delay(5);

}


void init()

{

write_com(0x38);

write_com(0x0c);

write_com(0x06);

write_com(0x01);

}


 

void delayms(unsigned char x,unsigned char y)

{

unsigned char z;

   do{

       z=y;

       do{

       ;

       }while(--z);           

    }while(--x);  

}


uchar reset()

{

TRISA4=0;

bus=0;

delayms(10,25);

TRISA4=1;

delayms(5,1);

if(bus==1)

{

delayms(2,60);

return 0;

}

else 

{

delayms(2,60);

return 1;

}

}


void write_byte(uchar dat)

{

uchar i;

for(i=0;i<8;i++)

{

TRISA4=0;

bus=0;

nop;

nop;

bus=dat&0x01;

delayms(5,3);

TRISA4=1; //释放总线

nop;

nop;

dat>>=1;

}

}


uchar read_byte()

{

uchar i,dat=0;

for(i=0;i<8;i++)

{

dat>>=1;

TRISA4=0;

bus=0;

nop;

nop;

nop;

nop;

nop;

TRISA4=1;

nop;

nop;

nop;

nop;

nop;

if(bus==1)dat=dat|0x80;

else dat=dat|0x00;

delayms(2,7); //这里延时时间略长可以使读数比较稳定

}

return dat;

}


void convert_T()

{

if(reset()==1)

{

       write_byte(0xcc);     // 跳过多器件识别 

write_byte(0x44);

}

}


void read_T(void) 

    unsigned char Lsb,Msb;            

    if(reset()==1) 

{  

        write_byte(0xcc);      // 跳过多器件识别 

        write_byte(0xbe);      // 读暂存器 

        Lsb=read_byte();       // 低字节 

        Msb=read_byte();      // 高字节 

temp2=Lsb&0x0f;        //LSB的低4位为小数部分

temp1=(Lsb>>4)|(Msb<<4);//LSB的高4位和MSB拼成整数部分

    }     


void main(void)

{

int i=0;

ADCON1=0X06;             //所有IO均为数字口,模拟量输入禁止

TRISB=0B11000111;    //RB3-5设置为输出

TRISD=0x00;

init();

while(1)

{

convert_T();            //启动温度转换

    delayms(5,20);          //延时

read_T();               //读温度数据

if(temp1>99) temp1=99; //这里我们只显示2位整数部分,所以限定在99度  

if(temp2>15) temp2=0;   //限定范围,以免查表溢出    

 

//下面就是用LCD1602显示

write_com(0x80+1);

write_dat(0x30+(temp1/10));

write_com(0x80+2);

write_dat(0x30+(temp1));

write_com(0x80+3);

write_dat('.');

write_com(0x80+4);

write_dat(0x30+tablexiao[temp2&0x0f]);

if(temp1>=23)

{

write_com(0x80+0x40+8);

write_dat('*');

write_dat('o');

write_dat('*');

write_dat('!');

}

else

{

write_com(0x80+0x40+8);

write_dat('=');

write_dat('v');

write_dat('=');

write_dat('?');

}

write_com(0x80+0x40+2);

for(i=1;i<=strlen(k);i++)

{

write_dat(*(k+i-1));

}

}

}

最后还有一个超过23度表情的变化,和我的logo

今天才发现,原来在PIC里对LCD1602也可以连续写入字符串
但是,在这里十分特殊,不支持对

指针进行++或者--操作,也不可以做例如p=p+1的操作,只能用for循环实现,如下
char k[]="kaito";
write_com(0x80+0x40+2);
for(i=1;i<=strlen(k);i++)
{
write_dat(*(k+i-1));
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值