51单片机DS18B20温度传感器和数字管显示温度
DS18B20及数码管显示温度介绍
-
- 51单片机DS18B20温度传感器和数字管显示温度
- DS18B20作用
-
- 怎么读取DS18B20的“1”和“0”
-
- 如何在单片机的数码管上显示数码管?
DS18B20作用
我们常用的DS18B20长什么样呢
它一共有3个角,分别是 GND(接地) DQ(数据总线,单片机IO口相连) Vdd(供电) 我用的单片机是清翔的V3.21 单片机使用芯片STC89C52
DS18N20电路原理图
所以我会用C语言 sbit DS = P2^2 声明这个IO口叫做DS(DS18B20数据总线),也就是我们可以写DS为 1或0 控制它是低电平还是高电平
传感器输出的信号不能是我们人类一眼就能看到的十进制数字。因此,我们需要DS18B20输出的数字信号转换为十进制数字,让我们人类理解
那我们该怎么读呢?DS18B当然,20号数字信号的前提是我们必须先发出指令DS18B知道我们想让它做什么
怎么读取DS18B1和020
DS18B20采用1-wire Bus(单总线时序),以前I2C和SPI(ADDA不同于中光敏电阻和热敏电阻,它只有一条线,所以我们在给它DS18B对我个人来说,在发出和接收信号时,任何时序都必须非常严格,以确保读取数据的准确性,SPI总线让我感觉很舒服,因为它只有三个函数(89C52),虽然1-wire只有三个函数,但它有非常严格的顺序要求,写作或阅读data需要多少微秒才能成功运行,等等,以后大家都会明白为什么。
我先给你看三个步骤,然后告诉你如何在C语言中实现这三个步骤
- 初始化DS18B20
- 写入ROM操作指令(我们通常在初中只使用ROM指令)
- 写入DS18B20功能指令(一般用温度准换指令,读取快速暂存器指令)
bit DSInit() {
bit i; DS = 1; _nop_(); DS = 0; delay_us(75);//拉低总线499.45us延时,在DQ总线上的DS18B20全部被复位 DS = 1; ///释放总线 delay_us(4); //37.95us i = DS; delay_us(20); //141.95us,等待18b回到低电平存在信号 DS = 1;///释放总线 _nop_(); return (i); } //us执行函数,执行一次us所需6.5us进入一次11.95us void delay_us/span>(uchar us) {
while(us--); }
图为初始化时序
初始化时序里面包含了复位DS18B20和接收DS218B20的存在信号
主机和DS18B20做任何通讯前都需要对其初始化,初始化期间,总线控制器拉低总线并保持480us以上,挂在总线上的器件将被复位,然后释放总线,等到15-60us,此时18B20将返回一个60-240us的低电平存在信号,我们需要等待60-240us,来接收完这个低电平存在信号
void DSWriteByte(uchar dat) //总线每次只能写一位进去
{
uchar i;
for(i = 0;i<8;i++)
{
DS = 0; //下拉总线
_nop_(); //产生一点时序
DS = dat & 0x01; //0x01 00000001
delay_us(10); //76.95us
DS = 1; //上拉总线
_nop_();
dat >>= 1; //dat右移一位
}
}
//读一个字节
uchar DSReadByte()
{
uchar i,dat,j;
for(i = 0;i<8;i++)
{
DS = 0;
_nop_(); //产生读时序
DS = 1; //释放总线
_nop_();
j = DS;
delay_us(10); //76.95us
DS = 1;
_nop_();
dat = ((j<<7)|(dat>>1) );
}
return (dat);
}
图为写时序和读时序
- 写时序分为写0时序和写1时序
- 总线控制器通过控制单总线高低电平持续时间从而把逻辑1或0写DS18B20中。
- 总线控制器要产生一个写时序,必须将总线拉低最少1us,产生写0时序时总线必须保持低电平60~120us之间,然后释放总线,产生写1时序时在总线产生写时序后的15us内允许把总线拉高。注意:2次写周期之间至少间隔1us(完成一个for循环大概就1us)
- 各位谨记上拉总线电压来释放总线
- 每一次 DS18B20 的操作都必须满足以上步骤,若是缺少步骤或是顺序混乱,器件 将不会返回值。例如这样的顺序:发起 ROM 搜索指令[F0h]和报警搜索指令[ECh] 之后,总线控制器必须返回步骤 1。
因为我本身也是一个初学者,所以我对SPI总线和I2C还有UART串口通信,还比较模糊,我也建议各位认真,并且重复揣摩芯片手册,或者能读懂芯片手册,按照芯片手册来写出我们想要的效果,这样,我觉得我们才能成为一名合格的单片机学习者
怎么让数码管显示在单片机的数码管上
这里就直接给大家上代码吧!
代码很长,但希望大家能够仔细对照着时序图阅读
/*DS18B20只在一根通信线上 1-Wire 只有唯一的64位序列码储存在板载ROM(方便识别DS18B20) 12位分辨率对应为0.0625度*/ #include<reg52.h> #include<intrins.h> #define uchar unsigned char #define uint unsigned int //共阴数码管段选表0-9 uchar code SMGduan[]= { 0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,}; //数码管位选码 uchar code SMGwei[] = { 0xfe, 0xfd, 0xfb, 0xf7}; sbit DS = P2^2; //DS18B20 DQ数据角 sbit DU= P2^6; sbit WE = P2^7; void delay_us(uchar); /*1.初始化 2.ROM操作指令 3.功能指令*/ /*if i == 0 那么18b20在总线上并且准备就绪*/ bit DSInit() { bit i; DS = 1; _nop_(); DS = 0; delay_us(75);//拉低总线499.45us延时,在DQ总线上的DS18B20全部被复位 DS = 1; //释放总线 delay_us(4); //37.95us i = DS; delay_us(20); //141.95us,等待18b返回低电平存在信号 DS = 1;//释放总线 _nop_(); return (i); } //写一个字节 void DSWriteByte(uchar dat) //总线每次只能写一位进去 { uchar i; for(i = 0;i<8;i++) { DS = 0; //下拉总线 _nop_(); //产生一点时序 DS = dat & 0x01; //0x01 00000001 delay_us(10); //76.95us DS = 1; //上拉总线 _nop_(); dat >>= 1; //dat右移一位 } } //读一个字节 uchar DSReadByte() { uchar i,dat,j; for(i = 0;i<8;i++) { DS = 0; _nop_(); //产生读时序 DS = 1; //释放总线 _nop_(); j = DS; delay_us(10); //76.95us DS = 1; _nop_(); dat = ((j<<7)|(dat>>1) ); } return (dat); } //us执行函数,执行一次us所需6.5us进入一次11.95us void delay_us(uchar us) { while(us--); } void display(uint i) { uchar b, s, g; static uchar wei; b = i / 100; s = i % 100 / 10; g = i % 10; P0 = 0XFF;//清除断码 WE = 1;//打开位选
锁存器 P0 = SMGwei[wei]; WE = 0;//锁存位选数据 P0 = 0XFF;//清除断码 switch(wei) { case 0: DU = 1; P0 = SMGduan[b]; DU = 0; break; case 1: DU = 1; P0 = SMGduan[s]|0x80; DU = 0; break; case 2: DU = 1; P0 = SMGduan[g]; DU = 0; break; } wei++; if(wei == 3) wei = 0; } void main() { uchar L,M; uint i; while(1) { DSInit(); //初始化 DSWriteByte(0xcc); //发送忽略ROM指令 DSWriteByte(0x44); //发送完指令后,DS18B20开始转换并且存储到高速寄存器 DSInit(); DSWriteByte(0xcc); //发送忽略ROM指令 DSWriteByte(0xbe); //读取DS18B20暂存器指令 L = DSReadByte(); //读取LS BYTE M = DSReadByte(); //读取MS BYTE i = M; i<<=8; i |= L; //效果为 假设M为00000111 左移八位变为 0000011100000000(<<自动补全0)然后或上L,相当于M+L i = i*0.0625*10 +0.5; //+0.5 为了四舍五入。因为if为int型,自动抛掉小数点后面 display(i); } }
大家有不懂的都可以私信博主!
代码成功运行图: 坐标苏州,室内温度20度左右