文章目录
一、DS18B20介绍
1.DS18B20的温度测量范围从-55℃到+125℃
2.分辨率在9位到12位之间,可以由用户自己进行设置。
3.在-10℃到+85℃范围内,其精确度可以达到±0.5℃。
4.DS18B20可以通过单个数据线(以及地线)进行通信,也就是one wire。
二、DS18B20的温度转换与读取流程
[1] DS18B20复位。
[2] 写入字节0xCC,跳过ROM指令。
[3] 写入字节0x44,开始温度转换。
[4] 因为温度转换需要时间,所以延时700~900ms。
[5] DS18B20复位。
[6] 写入字节0xCC,跳过ROM指令。
[7] 写入字节0xBE,读取高速暂存器。
[8] 读取暂存器的第0字节,即温度数据的LSB。
[9] 读取暂存器的第1字节,即温度数据的MSB。
[10] DS18B20复位。表示读取数据结束。
[11] 将LSB和MSB整合成为一个16位 数据。
[12] 判断读取结果的符号,进行正负温度的数据处理。
看到这是不是开始害怕了,不用怕,我教大家如何利用蓝桥杯官方提供的资料来完成上面的这些内容。大家需要理解的只有[11]和[12]这两个怎么实现就行。
三、利用手册完成DS18B20的读写
3.1 官方给的底层驱动代码
先看一下官方提供给我们的底层驱动代码中onewire中有哪些函数?
void Write_DS18B20(unsigned char dat); // 写ds18b20
unsigned char Read_DS18B20(void); // 读取ds18b20
bit init_ds18b20(void); // 初始化ds18b20,也就是复位ds18b20
根据这三个函数,再结合官方给的DS18B20手册
我们就可以实现代码的编写。
3.2 手册指定的数据流
第一步:初始化,直接调用函数
init_ds18b20()
就可以了
第二步:ROM命令,直接跳过ROM指令,也就是发送0x44
第三步:发送要DS18B20干什么,比如让它温度转换或是读取温度。记住每次命令让它干什么之前都要重复前面两步,这是DS18B20的基本流程!!!!!!!!
3.3 结合ROM 命令流程图和功能命令流程图确认发送什么指令
3.3.1 实现第一个功能(开始温度转换)
记得3.2中所说的固定数据流吗?
初始化、ROM指令、功能指令
根据ROM流程图和Function流程图,我们完成了第一个功能,开始温度转换
init_ds18b20(); // 初始化
Write_DS18B20(0xcc); // ROM指令(跳过ROM指令)
Write_DS18B20(0x44); // 功能指令(开始温度转换)
记住,开始温度转换后一定要延时700ms到900ms
3.3.2 第二个功能(读取温度)
init_ds18b20(); // 初始化
Write_DS18B20(0xcc); // 跳过ROM
Write_DS18B20(0xbe); // 读取温度
LSB=Read_DS18B20(); // 读取第一个字节,也就是温度数据的低8位
MSB=Read_DS18B20(); // 读取第二个字节,也就是温度数据的高8位
// 后面还有7个字节的数据,我们不需要,复位DS18B20(调用初始化函数就是复位DS18B20)
// init_ds18b20(); // 2024.4.12更新说明:小蜜蜂老师的这行代码是不必要的,甚至会导致你的ds18b20数据不稳定,跳动。
不同温度对应的不同的数字输出,可以看到高5位用来表示温度的正负!
3.3.3 数据处理(截取b站小蜜蜂老师的课件)
3.4 整体代码
float DS18B20_Get_Data()
{
char LSB,MSB;
int data;
float temperature;
init_ds18b20();
Write_DS18B20(0xcc);
Write_DS18B20(0x44);
//Delay800ms(); // 可以去掉,也建议去掉,去掉后要确保不能太快的去读取ds18b20,会导致读取的数据不稳定
init_ds18b20();
Write_DS18B20(0xcc);
Write_DS18B20(0xbe);
LSB=Read_DS18B20();
MSB=Read_DS18B20();
// init_ds18b20(); // 2024.4.12更新说明:小蜜蜂老师的这行代码是不必要的,甚至会导致你的ds18b20数据不稳定,跳动。
data=MSB; // 保存高8位数据到低8位
data=(data<<8)|LSB; // 将高8位的数据向左移8位,再将低8位放到data的低8位中
if(data>0xf800) // 温度小于0,因为data前5位表示温度的正负
//所以我们就可以利用前5位是0还是1来判断读取的温度是负的还是正的
//一般都是正的,所以我就不讲了。
{
}
else // 温度大于0
{
temperature=data*0.0625;
}
return temperature;
}
四、DS18B20温度读取异常
4.1 上电出现异常
这是由于DS18B20在上电复位时,温度寄存器中的值位0x0550,即85摄氏度。如果你读取温度有延迟800ms就不会出现这样的问题。
//在main函数初始化部分添加
while((unsigned char)DS18B20_Get_Data() == 85); // 如果读取温度为85,那么一直卡在这里,除非读取到正常温度
4.2 读取温度没有延时
DS18B20的温度读取需要时间,这个时间是由于AD转换需要时间,所以开启温度转换后一定要延时一段时间。
2024.4.12更新说明:可以不要延时,但是不能读取频率过高。可以配合定时器中断,实现50ms读取一次,但不要放在定时器中断里面啊!
4.3 中断影响了onewire的时序
onewire对时序要求非常严格,所以当中断发生过于频繁或者中断中做的事情太多或者中断用了延时函数,这些都会导致ds18b20的数据异常!其实不止是中断,你代码编写的逻辑不好也会导致ds18b20读取数据异常…
解决办法:因为DS18B20的读取耗时较长,所以我们不能放在中断中解决中断对DS18B20的影响。最好的办法就是放在主函数中运行,然后利用中值滤波滤除异常数据,实践证明效果非常棒,几乎不可能出现数据异常!
五、示例代码
注意代码是通过串口查看读取的数据,请打开串口助手观测读取的数据。
onewire.h
#ifndef __ONEWIRE_H__
#define __ONEWIRE_H__
#include "stc15f2k60s2.h"
sbit DQ=P1^4;
bit init_ds18b20(void);
unsigned char Read_DS18B20(void);
void Write_DS18B20(unsigned char dat);
#endif
onewire.c
/* # 单总线代码片段说明
1. 本文件夹中提供的驱动代码供参赛选手完成程序设计参考。
2. 参赛选手可以自行编写相关代码或以该代码为基础,根据所选单片机类型、运行速度和试题
中对单片机时钟频率的要求,进行代码调试和修改。
*/
//
#include "onewire.h"
void Delay_OneWire(unsigned int t)
{
unsigned char i;
while(t--){
for(i=0;i<12;i++);
}
}
//
void Write_DS18B20(unsigned char dat)
{
unsigned char i;
for(i=0;i<8;i++)
{
DQ = 0;
DQ = dat&0x01;
Delay_OneWire(5);
DQ = 1;
dat >>= 1;
}
Delay_OneWire(5);
}
//
unsigned char Read_DS18B20(void)
{
unsigned char i;
unsigned char dat;
for(i=0;i<8;i++)
{
DQ = 0;
dat >>= 1;
DQ = 1;
if(DQ)
{
dat |= 0x80;
}
Delay_OneWire(5);
}
return dat;
}
//
bit init_ds18b20(void)
{
bit initflag = 0;
DQ = 1; // 初始化总线为高电平
Delay_OneWire(12);
DQ = 0; // 总线拉低总线480-960us产生复位脉冲
Delay_OneWire(80);
DQ = 1; // 主机释放总线,模拟生成一个上升沿,15-60us
Delay_OneWire(10);
initflag = DQ; // ds18b20检测到上升沿后,产生一个60-240us的低电平信号,如果为低电平,说明设备准备就绪
Delay_OneWire(5);
return initflag;
}
main.c
#include "stc15f2k60s2.h"
#include "onewire.h"
#include "intrins.h"
#include <stdio.h>
typedef unsigned char uint8;
typedef unsigned int u16;
/**********矩阵键盘IO定义***********/
sbit R1=P3^3;
sbit R2=P3^2;
sbit R3=P3^1;
sbit R4=P3^0;
sbit C1=P4^4;
sbit C2=P4^2;
sbit C3=P3^5;
sbit C4=P3^4;
/**********矩阵键盘IO定义***********/
uint8 code t_display[]={ //标准字库
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71,
//black - H J K L N o P U t G Q r M y
0x00,0x40,0x76,0x1E,0x70,0x38,0x37,0x5C,0x73,0x3E,0x78,0x3d,0x67,0x50,0x37,0x6e,
0xBF,0x86,0xDB,0xCF,0xE6,0xED,0xFD,0x87,0xFF,0xEF,0x46}; //0. 1. 2. 3. 4. 5. 6. 7. 8. 9. -1
uint8 display_buffer[]={16,16,16,16,16,16,16,16};
uint8 num=0;
unsigned int timerCount=0;
uint8 ledStatus=0;
void selectHC573(uint8 channel);
void initSystem();
void showBitSeg(uint8 pos,uint8 dat);
void Delay1ms(void); //@12.000MHz
void scanSeg();
void scanKeyboard();
bit NorP=1;
void Delay700ms(void) //@12.000MHz
{
unsigned char data i, j, k;
_nop_();
_nop_();
i = 40;
j = 236;
k = 16;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
float DS18B20_Get_Data()
{
uint8 LSB=0x00,HSB=0x00;
unsigned int buffer;
float temperature=0;
init_ds18b20();
Write_DS18B20(0xcc);
Write_DS18B20(0x44);
// Delay700ms(); // 延时等待温度转换完成
init_ds18b20();
Write_DS18B20(0xcc);
Write_DS18B20(0xbe);
LSB=Read_DS18B20();
HSB=Read_DS18B20();
buffer=(HSB<<8)|LSB;
temperature=buffer*0.0625;
printf("%f\n",temperature);
return temperature;
}
char putchar(char c)
{
SBUF = c;
while(!TI);
TI = 0;
return c;
}
void UartInit(void) //9600bps@12.000MHz
{
SCON = 0x50; //8位数据,可变波特率
AUXR |= 0x01; //串口1选择定时器2为波特率发生器
AUXR &= 0xFB; //定时器时钟12T模式
T2L = 0xE6; //设置定时初始值
T2H = 0xFF; //设置定时初始值
AUXR |= 0x10; //定时器2开始计时
}
int main()
{
uint8 t=0;
float temp=0;
initSystem();
UartInit();
while((unsigned char)DS18B20_Get_Data()==85)
while(1)
{
DS18B20_Get_Data();
Delay700ms();
}
}
void selectHC573(uint8 channel)
{
P2&=0x1f; //清空P2
switch(channel)
{
case 4:{P2|=(0x04<<5); break;} // LED
case 5:{P2|=(0x05<<5); break;} // Beep、Relay、Motor
case 6:{P2|=(0x06<<5); break;} // 数码管位码
case 7:{P2|=(0x07<<5); break;} // 数码管段码
default:{break;} // 关闭所有通道
}
}
void initSystem()
{
selectHC573(5);
P0=0x0f;
selectHC573(0);
P0=0xff; //因为下一步要操作LED,在开启LED的573前,应该把P0的数据清零
selectHC573(4);
P0=~0x00; // 设置LED
}
void showBitSeg(uint8 pos,uint8 dat)
{
selectHC573(7);
P0=0xff;
selectHC573(6);
P0=(0x80>>pos);
selectHC573(7);
P0=~dat;
}
void Delay1ms(void) //@12.000MHz
{
unsigned char data i, j;
i = 12;
j = 169;
do
{
while (--j);
} while (--i);
}
void scanSeg()
{
uint8 i=0;
for(i=0;i<8;i++)
{
showBitSeg(i,t_display[ display_buffer[i] ]);
Delay1ms();
}
}
void Delay20ms(void) //@12.000MHz
{
unsigned char data i, j;
i = 234;
j = 115;
do
{
while (--j);
} while (--i);
}
void scanKeyboard()
{
R1=R2=R3=R4=1;
C1=0; C2=C3=C4=1;
if(R1==0)
{
Delay20ms(); // 软件消抖
if(R1==0)
{
// while(R1==0)
{
num=0;
}
}
}
R1=R2=R3=R4=1;
C3=0; C2=C1=C4=1;
if(R2==0)
{
Delay20ms(); // 软件消抖
if(R2==0)
{
// while(R1==0)
{
num=13;
}
}
}
}