18B20 & LCD1602(PIC)

2019-04-15 14:30发布

对于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)); }