硬件: 51板
(1)单线ds18b20接 P2.2
(2)使用外部电源给ds18b20供电,没有使用寄生电源奥
软件:
Kei uVision 2
刚开始对时序把握不好,可是在论坛里没找到比较详细的解释,所以俺倒塌了这个东东,就把俺的经验贴上来,供大家参考,呵呵……
如有错误请指正
#include "reg52.h"
#include "intrins.h"
#define uchar unsigned char
#define uint unsigned int
sbit ds=P2^2;
sbit dula=P2^6;
sbit wela=P2^7;
uchar flag ;
uint temp; //参数temp一定要声明为 int 型
uchar code table[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,
0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71}; //不带小数点数字编码
uchar code table1[]={0xbf,0x86,0xdb,0xcf,0xe6,0xed,0xfd,
0x87,0xff,0xef}; //带小数点数字编码
/*延时函数*/
void TempDelay (uchar us)
{
while(us--);
}
void delay(uint count) //延时子函数
{
uint i;
while(count)
{
i=200;
while(i>0)
i--;
count--;
}
}
/*串口初始化,波特率9600,方式1 */
void init_com()
{
TMOD=0x20; //设置定时器1为模式2
TH1=0xfd; //装初值设定波特率
TL1=0xfd;
TR1=1; //启动定时器
SM0=0; //串口通信模式设置
SM1=1;
// REN=1; //串口允许接收数据
PCON=0; //波特率不倍频
// SMOD=0; //波特率不倍频
// EA=1; //开总中断
//ES=1; //开串行中断
}
/*数码管的显示 */
void display(uint temp)
{
uchar bai,shi,ge;
bai=temp/100;
shi=temp%100/10;
ge=temp%100%10;
dula=0;
P0=table[bai]; //显示百位
dula=1; //从0到1,有个上升沿,解除锁存,显示相应段
dula=0; //从1到0再次锁存
wela=0;
P0=0xfe;
wela=1;
wela=0;
delay(1); //延时约2ms
P0=table1[shi]; //显示十位
dula=1;
dula=0;
P0=0xfd;
wela=1;
wela=0;
delay(1);
P0=table[ge]; //显示个位
dula=1;
dula=0;
P0=0xfb;
wela=1;
wela=0;
delay(1);
}
/*****************************************
时序:初始化时序、读时序、写时序。
所有时序都是将主机(单片机)作为主设备,单总
线器件作为从设备。而每一次命令和数据的传输
都是从主机主动启动写时序开始,如果要求单总
线器件回送数据,在进行写命令后,主机需启动
读时序完成数据接收。数据和命令的传输都是低
位在先。
初始化时序:复位脉冲 存在脉冲
读;1 或 0时序
写;1 或 0时序
只有存在脉冲信号是从18b20(从机)发出的,其
它信号都是由主机发出的。
存在脉冲:让主机(总线)知道从机(18b20)已
经做好了准备。
******************************************/
/*--------------------------------------------------------------------------------------------------------------------
初始化:检测总线控制器发出的复位脉冲
和ds18b20的任何通讯都要从初始化开始
初始化序列包括一个由总线控制器发出的复位脉冲
和跟在其后由从机发出的存在脉冲。
初始化:复位脉冲+存在脉冲
具体操作:
总线控制器发出(TX)一个复位脉冲 (一个最少保持480μs 的低电平信号),然后释放总线,
进入接收状态(RX)。单线总线由5K 上拉电阻拉到高电平。探测到I/O 引脚上的上升沿后
DS1820 等待15~60μs,然后发出存在脉冲(一个60~240μs 的低电平信号)。
具体看"倒塌 18b20"文档里的 " 单线复位脉冲时序和1-wire presence detect "的时序图
-------------------------------------------------------------------------------------------------------------------*/
void ds_reset(void)
{
ds=1;
_nop_(); //1us
ds=0;
TempDelay(80); //当总线停留在低电平超过480us,总线上所以器件都将被复位,这里//延时约530us总线停留在低电平超过480μs,总线上的所有器件都
//将被复位。
_nop_();
ds=1; //产生复位脉冲后,微处理器释放总线,让总线处于空闲状态,原因查//18b20中文资料
TempDelay(5); //释放总线后,以便从机18b20通过拉低总线来指示其是否在线,
//存在检测高电平时间:15~60us, 所以延时44us,进行 1-wire presence //detect(单线存在检测)
_nop_();
_nop_();
_nop_();
if(ds==0)
flag=1; //detect 18b20 success
else
flag=0; //detect 18b20 fail
TempDelay(20); //存在检测低电平时间:60~240us,所以延时约140us
_nop_();
_nop_();
ds=1; //再次拉高总线,让总线处于空闲状态
/**/
}
/*----------------------------------------
读/写时间隙:
DS1820 的数据读写是通过时间隙处理
位和命令字来确认信息交换。
------------------------------------------*/
bit ds_read_bit(void) //读一位
{
bit dat;
ds=0; //单片机(微处理器)将总线拉低
_nop_(); //读时隙起始于微处理器将总线拉低至少1us
ds=1; //拉低总线后接着释放总线,让从机18b20能够接管总线,输出有效数据
_nop_();
_nop_(); //小延时一下,读取18b20上的数据 ,因为从ds18b20上输出的数据
//在读"时间隙"下降沿出现15us内有效
dat=ds; //主机读从机18b20输出的数据,这些数据在读时隙的下降沿出现//15us内有效
TempDelay(10); //所有读"时间隙"必须60~120us,这里77us
return(dat); //返回有效数据
}
uchar ds_read_byte(void ) //读一字节
{
uchar value,i,j;
value=0; //一定别忘了给初值
for(i=0;i<8;i++)
{
j=ds_read_bit();
value=(j<<7)|(value>>1); //这一步的说明在一个word文档里面
}
return(value); //返回一个字节的数据
}
void ds_write_byte(uchar dat) //写一个字节
{
uchar i;
bit onebit; //一定不要忘了,onebit是一位
for(i=1;i<=8;i++)
{
onebit=dat&0x01;
dat=dat>>1;
if(onebit) //写 1
{
ds=0;
_nop_();
_nop_(); //看时序图,至少延时1us,才产生写"时间隙"
ds=1; //写时间隙开始后的15μs内允许数据线拉到高电平
TempDelay(5); //所有写时间隙必须最少持续60us
}
else //写 0
{
ds=0;
TempDelay(8); //主机要生成一个写0 时间隙,必须把数据线拉到低电平并保持至少60μs,这里64us
ds=1;
_nop_();
_nop_();
}
}
}
/*****************************************
主机(单片机)控制18B20完成温度转换要经过三个步骤:
每一次读写之前都要18B20进行复位操作,复位成功后发送
一条ROM指令,最后发送RAM指令,这样才能对DS18b20进行
预定的操作。
复位要求主CPU将数据线下拉500us,然后释放,当ds18B20
受到信号后等待16~60us,后发出60~240us的存在低脉冲,
主CPU收到此信号表示复位成功
******************************************/
/*----------------------------------------
进行温度转换:
先初始化
然后跳过ROM:跳过64位ROM地址,直接向ds18B20发温度转换命令,适合单片工作
发送温度转换命令
------------------------------------------*/
void tem_change()
{
ds_reset();
delay(1); //约2ms
ds_write_byte(0xcc);
ds_write_byte(0x44);
}
/*----------------------------------------
获得温度:
------------------------------------------*/
uint get_temperature()
{
float wendu;
uchar a,b;
ds_reset();
delay(1); //约2ms
ds_write_byte(0xcc);
ds_write_byte(0xbe);
a=ds_read_byte();
b=ds_read_byte();
temp=b;
temp<<=8;
temp=temp|a;
wendu=temp*0.0625; //温度读取的解释我记录在 "倒塌 18B20"里面
temp=wendu*10+0.5;
return temp;
}
/*----------------------------------------
读ROM
------------------------------------------*/
/*
void ds_read_rom() //这里没有用到
{
uchar a,b;
ds_reset();
delay(30);
ds_write_byte(0x33);
a=ds_read_byte();
b=ds_read_byte();
}
*/
void main()
{
uint a;
init_com();
while(1)
{
tem_change(); //12位转换时间最大为750ms
for(a=10;a>0;a--)
{
display( get_temperature());
}
}
}
回复 楼主 的帖子
虽然一直在做ARM,可是俺也喜欢51,希望尽快把ARM和51都倒塌掉,呵呵
回复 沙发 的帖子
DS18B20 测温+LCD1602显示
上个程序俺用数码管显示温度,这次就贴个用LCD1602显示的吧,呵呵……
lcd1602硬件连接:
/*----------------------------------------------------------------------------------------------------
TC1602A(16*2)模拟口线接线方式
连接线图:
---------------------------------------------------
|LCM-----51 | LCM-----51 | LCM------51 |
-------------------------------------------------------------------
|D0-----P0.0 | D4-----P0.4 | RW------接地 (只能写) |
|D1-----P0.1 | D5-----P0.5 | RS-------P3.5 |
|D2-----P0.2 | D6-----P0.6 | E---------P3.4 |
|D3-----P0.3 | D7-----P0.7 | VLCD接1K电阻到GND|
--------------------------------------------------- ----------------
[注:AT89c52使用11.0592M晶振]
---------------------------------------------------------------------------------------------------------*/
DS18B20的硬件连接:
这个没有变,和上一个程序一样
程序如下:
#include "reg52.h"
#include "intrins.h"
#define uchar unsigned char
#define uint unsigned int
sbit DS=P2^2;
sbit dula=P2^6;
sbit wela=P2^7;
sbit LCD1602_E=P3^4;
sbit LCD1602_RS=P3^5;
uchar flag;
uint temp;
uchar tflag; //温度正负标志位
uint tvalue; //呵呵 刚开始又犯了那个错误,就是tvalue的类型要声明为int型
uchar data disdata[5];
uchar table1[]="temperature:"; //lcd第一行要显示的字符
uchar table2[]=" "; //lcd第二行初始化时使用
/***********************************************
LCD1602 的操作
***********************************************/
void delay(uint x) //延时子函数
{
uint a,b;
for(a=x;a>0;a--)
for(b=10;b>0;b--);
}
/*---------------------------------------------
写指令,看着时序图编写就可以了
------------------------------------------------ */
void lcd_write_cmd(uchar cmd)
{
P0=cmd;
LCD1602_RS=0;
LCD1602_E=0;
delay(10);
LCD1602_E=1;
delay(10);
LCD1602_E=0;
}
/*-----------------------------------
读指令,同样要看着时序图写
-------------------------------------*/
void lcd_write_data(uchar data1)
{
P0=data1;
LCD1602_RS=1;
LCD1602_E=0;
delay(10);
LCD1602_E=1;
delay(10);
LCD1602_E=0;
}
/*-----------------------------------
lcd1602 的初始化设置
-------------------------------------*/
void init_lcd1602(void)
{
dula=0;
wela=0;
lcd_write_cmd(0x38);
delay(20);
lcd_write_cmd(0x0e);
delay(20);
lcd_write_cmd(0x06);
delay(20);
lcd_write_cmd(0x01);
delay(20);
}
/*-----------------------------------
初始化lcd1602的显示时用到的显示函数
-------------------------------------*/
void display(unsigned char *p)
{
while(*p!='\0')
{
lcd_write_data(*p);
p++;
delay(20);
}
}
/*-----------------------------------
初始化lcd1602的显示
-------------------------------------*/
void init_lcd_display(void)
{
init_lcd1602();
lcd_write_cmd(0x80); //设定第一行显示初始地址
delay(20);
display(table1);
lcd_write_cmd(0xc0); //设定第而行显示的初地址
delay(20);
display(table2);
}
/***********************************************
DS18B20 的操作
***********************************************/
/*-----------------------------------
延时函数
-------------------------------------*/
void TempDelay (uchar us)
{
while(us--);
}
/*ds18b20 的初始化*/
void ds_reset(void)
{
DS=1;
_nop_(); //1us
DS=0;
TempDelay(80); //当总线停留在低电平超过480us,总线上所以器件都将被复位,这里延时约530us
//总线停留在低电平超过480μs,总线上的所有器件都将被复位。
_nop_();
DS=1; //产生复位脉冲后,微处理器释放总线,让总线处于空闲状态,原因查18b20中文资料
TempDelay(5); //释放总线后,以便从机18b20通过拉低总线来指示其是否在线,
//存在检测高电平时间:15~60us, 所以延时44us,进行1-wire presence detect(单线存在检测)
_nop_();
_nop_();
_nop_();
if(DS==0)
flag=1; //detect 18b20 success
else
flag=0; //detect 18b20 fail
TempDelay(20); //存在检测低电平时间:60~240us,所以延时约140us
_nop_();
_nop_();
DS=1; //再次拉高总线,让总线处于空闲状态
/**/
}
/*-----------------------------------------------------------
读/写时间隙:
DS1820 的数据读写是通过时间隙处理
位和命令字来确认信息交换。
-------------------------------------------------------------*/
bit ds_read_bit(void) //读一位
{
bit dat;
DS=0; //单片机(微处理器)将总线拉低
_nop_(); //读时隙起始于微处理器将总线拉低至少1us
DS=1; //拉低总线后接着释放总线,让从机18b20能够接管总线,输出有效数据
_nop_();
_nop_(); //小延时一下,读取18b20上的数据 ,因为从ds18b20上输出的数据在读"时间隙"下降沿出现15us内有效
dat=DS; //主机读从机18b20输出的数据,这些数据在读时隙的下降沿出现15us内有效
TempDelay(10); //所有读"时间隙"必须60~120us,这里77us
return(dat); //返回有效数据
}
uchar ds_read_byte(void ) //读一字节
{
uchar value,i,j;
value=0; //一定别忘了给初值
for(i=0;i<8;i++)
{
j=ds_read_bit();
value=(j<<7)|(value>>1); //这一步的说明在一个word文档里面
}
return(value); //返回一个字节的数据
}
void ds_write_byte(uchar dat) //写一个字节
{
uchar i;
bit onebit; //一定不要忘了,onebit是一位
for(i=1;i<=8;i++)
{
onebit=dat&0x01;
dat=dat>>1;
if(onebit) //写 1
{
DS=0;
_nop_();
_nop_(); //看时序图,至少延时1us,才产生写"时间隙"
DS=1; //写时间隙开始后的15μs内允许数据线拉到高电平
TempDelay(5); //所有写时间隙必须最少持续60us
}
else //写 0
{
DS=0;
TempDelay(8); //主机要生成一个写0 时间隙,必须把数据线拉到低电平并保持至少60μs,这里64us
DS=1;
_nop_();
_nop_();
}
}
}
/*--------------------------------------------------------------------------------------
进行温度转换:
先初始化
然后跳过ROM:跳过64位ROM地址,直接向ds18B20发温度转换命令,适合单片工作
发送温度转换命令
----------------------------------------------------------------------------------------*/
void tem_change()
{
ds_reset();
delay(1); //约2ms
ds_write_byte(0xcc);
ds_write_byte(0x44);
}
/*----------------------------------------
获得温度:
------------------------------------------*/
uint get_temperature()
{
float wendu;
uchar a,b;
ds_reset();
delay(1); //约2ms
ds_write_byte(0xcc);
ds_write_byte(0xbe);
a=ds_read_byte();
b=ds_read_byte();
temp=b;
temp<<=8;
temp=temp|a;
if(temp<0x0fff)
tflag=0; //正值
else
{
tflag=1;
temp=~temp+1;
}
wendu=temp*0.0625; //温度读取的解释我记录在 "倒塌 18B20"里面
temp=wendu*10+0.5;
return temp;
}
/*-----------------------------------
显示读取的温度
------------------------------------------*/
void display_18b20(uint tvalue) // 参数tvalue 的类型一定要声明为int型,如果声明为char型,数值会局限在0-255,超过255就会自动清零
{
uint flagdata;
disdata[0]=tvalue/1000+0x30; //0x30在字符库里对应显示字符"0", 这里是显示温度的百位数
disdata[1]=tvalue%1000/100+0x30; //十位数
disdata[2]=tvalue%100/10+0x30; //个位
disdata[3]=tvalue%10+0x30; //小数位
if(tflag==0)
flagdata=0x20; //0x20在字符库里为空
else
flagdata=0x2d; //0x2d在字符库里对应负号"-"
if(disdata[0]==0x30)
{
disdata[0]=0x20;
if(disdata[1]==0x30)
{
disdata[1]=0x20;
}
}
lcd_write_cmd(0xc0);
lcd_write_data(flagdata);//显示符号位
lcd_write_cmd(0xc1);
lcd_write_data(disdata[0]);//显示百位
lcd_write_cmd(0xc2);
lcd_write_data(disdata[1]);//显示十位
lcd_write_cmd(0xc3);
lcd_write_data(disdata[2]);//显示个位
lcd_write_cmd(0xc4);
lcd_write_data(0x2e);//显示小数点
lcd_write_cmd(0xc5);
lcd_write_data(disdata[3]);//显示小数位
}
void main()
{
init_lcd_display(); //lcd1602的初始化显示
while(1)
{
tem_change();
tvalue=get_temperature();
display_18b20(tvalue);
}
}
大家多交流啊,希望能通过一起努力,把DS18B20彻底的倒塌掉,呵呵……
大家快跟帖啊……有经验一起分享,进步更快奥!
回复 板凳 的帖子
看DS18B20的手册,似乎寄生电源特别简单,跟MCU连一根DQ,连一根地线就OK了
真这么简单?有人实践过吗?
另外如果用寄生电源,和用外接电源的程序应该一样吧,不用什么改动吧
回复 4楼 的帖子
这个程序的最大问题在于延时,
其实 1 总线的每一位读写时序要求比较高,所以在读写每一位的时候,不能被其他程序中断。
楼主的两个程序都没有使用中断,而按照论坛高手 农讲所所长 的说法,没有中断的单片机不是一个完全的单片机。
即使这两个程序在线路板上运行的如何好。如程序要增加中断,所有函数都要重新测试一番,
回复 5楼 的帖子
首先,要谢谢大家,这样一起交流才有意思啊,大家才会有进步!更要谢谢14楼的兄弟!
14楼: 最大问题
这个程序的最大问题在于延时,
其实 1 总线的每一位读写时序要求比较高,所以在读写每一位的时候,不能被其他程序中断。
楼主的两个程序都没有使用中断,而按照论坛高手 农讲所所长 的说法,没有中断的单片机不是一个完全的单片机。
即使这两个程序在线路板上运行的如何好。如程序要增加中断,所有函数都要重新测试一番,
说的很好,下次改进,呵呵……
希望大家继续批判,也希望大家能共享好的资料啊!
Re: [分享] DS18B20 测温程序完全解读
好经验,急需,感谢分享,
Re: [分享] DS18B20 测温程序完全解读
没有汇编的吗?:'( :'( :'(
Re: [分享] DS18B20 测温程序完全解读
学习学习,最近可能要用到
Re: [分享] DS18B20 测温程序完全解读
太感谢了!!!!!!!!!!!!
回复 4楼 dz851210 的帖子
资料上有写,18B20在寄生电源模式对总线的占用时间较长,且在100摄氏度以上时漏电流较大,在一条总线上挂多个传感器时好像不太推荐.
关注 学习
------------------------------------