18B20 & LCD1602(PIC)
2019-04-15 14:30发布
生成海报
对于18B20,它是单总线协议,我们对于它,只有读取温度数据才有意义。
对它的读写主要是两张时序图
![18B20 <wbr>& <wbr>LCD1602(PIC) 18B20 <wbr>& <wbr>LCD1602(PIC)](http://simg.sinajs.cn/blog7style/images/common/sg_trans.gif)
对写的操作
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) 18B20 <wbr>& <wbr>LCD1602(PIC)](http://simg.sinajs.cn/blog7style/images/common/sg_trans.gif)
对读的操作
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) 18B20 <wbr>& <wbr>LCD1602(PIC)](http://simg.sinajs.cn/blog7style/images/common/sg_trans.gif)
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));
}
打开微信“扫一扫”,打开网页后点击屏幕右上角分享按钮