51单片机——DS18B20温度传感器-1总线通讯1.1
程序员文章站
2022-07-12 10:54:43
...
写的不知道好不好,有什么不对的地方还请指出,谢了。
1、本节基于DS18B20 1总线通讯。DS18B20温度转换很慢,初始化复位也慢,读取它的温度不需要那么频繁,可按照需要修改。
2、驱动DS18B20,代码3个:初始化DS18B20、写1字节、读1字节。
3、在《51单片机——DS18B20温度传感器-1总线通讯1.0》章节上做代码优化了,主要解决了小数转换及显示问题。
4、温度读取出来后用数码管来显示小数,上章节数码管显示小数有精度问题,本次直接优化OK,只是12位精度显示的时候最后面那位老是0应该为0或者5的。
5、在调试的时候发现有时候会出现-0.0000,不知道是什么问题,可能是通讯时序上的问题吧,我在通讯时序中修改了延迟代码,故以下程序好像不会出现-0.0000
6、我觉的对于DS18B20主要是温度读取出来后进行不失真的算法转换,然后就是数码管对于正负小数温度的显示。本节已完美达到要求。
7、感觉不错的,清点赞或关注我,我会不定期更新优化代码。
main.c
#include "dt.h"
#include "ds18b20.h"
#include "time.h"
#include <reg52.h>
void main(void)
{
RES_dt_dispaly();
Timer0_16bit(20);
while(1)//大概2秒转换一下温度
{
convert_temp_DS18B20(12);//800毫秒了
delay_ms(1000);//软件延迟1秒
TR0=0;
read_temp_DS18B20(12);//大概3毫秒
dt_convert_DS18B20();//大概1毫秒
TR0=1;
}
}
ds18b20.h
#ifndef __DS18B20_H__
#define __DS18B20_H__
#include <reg52.h>
#include "delay.h"
#include <intrins.h>
sbit wire = P2^2;//GPIO P2.2
//DS18B20温度存放在全局变量
extern unsigned int DS18B20_float_bit;//小数位
extern unsigned char DS18B20_int_bit;//整数位
extern unsigned char DS18B20_Accuracy_bit;//精度位
extern bit DS18B20_flag_bit;//符号位
void convert_temp_DS18B20(unsigned char); //6、转换温度,x为选择使用的精度9位 10位 11位 12位,默认12位
void read_temp_DS18B20(unsigned char Accuracy);//10、读取DS18B20的温度,输入选择的精度9位 10位 11位 12位 9 10 11 12,默认12位
#endif
ds18b20.c
#include "ds18b20.h"
//与DS18B20通讯传输数据的时候均是先传送低位最后传送高位
//DS18B20温度存放在全局变量
unsigned int DS18B20_float_bit;//小数位
unsigned char DS18B20_int_bit;//整数位
unsigned char DS18B20_Accuracy_bit;//精度位
bit DS18B20_flag_bit;//符号位
//1、复位初始化DS18B20
static void DS18B20_RST(void)//大概1毫秒 //后期加入超时等待故障代码
{
wire = 0;
delay_us(75); //拉低480us复位DS18B20
wire = 1; //释放数据线
while(wire);// 超时60us判断是否有应答信号,超时代码后期在加入
/*if(wire) //没有应答的话
{
delay_us(26);//延迟180us二次判断DS18B20回复存在信号,即应答ACK信号
if (wire)//还没有应答的话
{
//DS18B20初始化失败,添加处理代码
}
else//有应答信号了
{
delay_us(26);//延迟180us判断是否器件释放总线
if (wire)//释放总线的话
{
delay_us(26);//再次延长180us
}
else
{
//DS18B20初始化失败,添加处理代码
}
}
}*/
//else//有应答信号了
{
while(!wire);//等待器件释放总线//延长240us
/*if (wire == 1)//如果器件释放总线的话
{
delay_us(38);//再次延长240us
}
else
{
//DS18B20初始化失败,添加处理代码
}*/
}
}
//2、向DS18B20写1字节
static void write_1_byte_DS18B20(unsigned char x)
{
unsigned char i;
for (i=0;i < 8;i++)
{
wire = 0;
_nop_(); //延迟大于1us
wire = x & 0x01;
delay_us(8);//延迟60us让DS18B20好采样
wire = 1;//释放总线
x = x >> 1;
}
}
//3、读DS18B20 1字节
static unsigned char read_1_byte_DS18B20(void)
{
unsigned char i;
unsigned char dat=0;
for ( i=0 ; i < 8; i ++)
{
dat = dat >> 1;//放在for循环后面有问题,会向右多移动一位,怪不得之前的数据读取出来有问题
wire = 0;
_nop_();
wire = 1;//释放总线,等待DS18B20发送数据到总线上
_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();//延迟个10us时间在采集数据
_nop_();_nop_();_nop_();_nop_();
if(wire)//这个if里面的数不建议写成if(wire);推荐写成if(wire==1);
{
dat += 0x80;
}
wire = 1;//释放总线
delay_us(4);//延迟45us
}
return dat;
}
//6、转换温度,x为选择使用的精度9位 10位 11位 12位,默认12位
void convert_temp_DS18B20(unsigned char x)
{
DS18B20_RST(); //初始化函数
write_1_byte_DS18B20(0xcc); //跳过64位ROM码检测
write_1_byte_DS18B20(0x44); //温度转换指令-功能命令
//x=0001 1111 0x1f 9位精度,转换时间93.75ms
//x=0011 1111 0x3f 10位精度,转换时间187.5ms
//x=0101 1111 0x5f 11位精度,转换时间375ms
//x=0111 1111 0x7f 12位精度,转换时间750ms
switch (x)
{
case 9: delay_ms(95); break;
case 10:delay_ms(190);break;
case 11:delay_ms(380);break;
default:delay_ms(750);break;//默认按照12位精度转换时间
}
}
//10、读取DS18B20的温度,输入选择的精度9位 10位 11位 12位 9 10 11 12,默认12位
void read_temp_DS18B20(unsigned char Accuracy)
{
unsigned int TLTL=0;//小数部分
unsigned char TL = 0;//辅助处理小数部分
unsigned char TH = 0;//处理整数部分
bit flag=0;//存放符号
DS18B20_RST();//复位器件
write_1_byte_DS18B20(0xcc);//跳过ROM检测指令
write_1_byte_DS18B20(0xbe);//读取暂存寄存器使用的指令
TL = read_1_byte_DS18B20();//将读取的低温8位存起来
TH = read_1_byte_DS18B20();//将读取的高温8位存起来
DS18B20_RST();//强制复位,后面的数据不在读取了
//开始使用算法计算温度
TH = (TH << 4) + (TL >> 4);//得到整数
TL = TL & 0x0f;//得到小数部分
if (TH < 128)//先判断是正温度还是负温度
{//为正数
switch (Accuracy)//匹配精度
{
case 9: TL >>= 3; TLTL = TL * 5; break;//匹配9位精度
case 10: TL >>= 2; TLTL = TL * 25; break;//匹配10位精度
case 11: TL >>= 1; TLTL = TL * 125;break;//匹配11位精度
case 12: TL >>= 0; TLTL = TL * 625;break;//匹配12位精度
default:TL >>= 0; TLTL = TL * 625;break;//默认匹配12位精度
}
}
else//为负数
{
flag = 1;
switch (Accuracy)//匹配精度
{
case 9: TL >>= 3; if (TL)
{
TLTL = TL * 5;
TH = ~TH;
}
else
{
TLTL = 0;
TH = ~TH + 1;
} break;//匹配9位精度 0.5
case 10: TL >>= 2; if (TL)
{
TLTL = (~TL & 0x03 + 1) * 25;
TH = ~TH;
}
else
{
TLTL = 0;
TH = ~TH + 1;
} break;//匹配10位精度0.75
case 11: TL >>= 1; if (TL)
{
TLTL = (~TL & 0x07 + 1) * 125;
TH = ~TH;
}
else
{
TLTL = 0;
TH = ~TH + 1;
} break;//匹配11位精度0.875
default: TL >>= 0; if (TL)
{
TLTL = (~TL & 0x0f + 1) * 625;
TH = ~TH;
}
else
{
TLTL = 0;
TH = ~TH + 1;
} break;//匹配12位精度0.9375
}
}
DS18B20_flag_bit = flag;//存放符号位
DS18B20_int_bit= TH;//存放正整数部分
DS18B20_float_bit = TLTL;//存放小数部分
DS18B20_Accuracy_bit = Accuracy;//存放精度
}
dt.h
#ifndef __DT_H__
#define __DT_H__
#include "delay.h"
#include "ds18b20.h"
void RES_dt_dispaly(void);//复位初始化数码管,并显示8个0~8个9
//0≤x≤99999999 unsigned int 无符号整型
//将数据x转换到数组里面
void convert_long_int(unsigned long int x);
//小数转换后存放到数组里面
void dt_convert_DS18B20(void);
//循环显示数组里的元素到数码管
void dt_display(void);
#endif
dt.c
//数码管dt 使用共阴极数码管即位选是阴极时数码管才选中
//使用P0-8个GPIO口
//P2.7位选脚(控制哪个数码管亮)、P2.6段选脚(送数码管值) 控制2个74锁存器
#include <reg52.h>
#include "dt.h"
#define dt_dt P0
sbit wei = P2^7; //锁存器位选
sbit duan = P2^6; //锁存器段选
static unsigned char datax[8];//定义局部变量//用来存放小数及正整数,然后发给数码管进行显示低位到高位存放
static code unsigned char dt_duan[25] = //数码管段选真值表 dt_duan[i] + 0x80 数码管显示数据并且此数据的右下角有小数点
{
0x3F, //"0"
0x06, //"1"
0x5B, //"2"
0x4F, //"3"
0x66, //"4"
0x6D, //"5"
0x7D, //"6"
0x07, //"7"
0x7F, //"8"
0x6F, //"9"
0x80, //"."
0x40, //"-"
0x77, //"A"
0x7C, //"B"
0x39, //"C"
0x5E, //"D"
0x79, //"E"
0x71, //"F"
0x76, //"H"
0x38, //"L"
0x37, //"n"
0x3E, //"u"
0x73, //"P"
0x5C, //"o"
0x00 //熄灭
};
static code unsigned char dt_wei[9]={0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe,0xff};//1位-8位(0x7f表示最右边哪个数码管)0xff数码管就不亮了
//刷新数码管进行显示
void dt_display(void)//循环显示数组里的元素到数码管
{
static unsigned char a=0;
for (a = 0; a < 8; a++)//a亮——a值——a值灭;a+1亮——a+1值——a+1值灭;开始循环(亮,真值,假值)
{
dt_dt = dt_wei[a];//灯亮
wei = 1; //打开位选
wei = 0; //锁存位选
dt_dt = datax[a];//数字
duan = 1; //打开段选
duan = 0; //锁存段选
delay_ms(1);//这个时间乘以8不能大于定时器时间
dt_dt = dt_duan[24];//清楚段码数字熄灭,假值,防止下位数码管显示上次的值
duan = 1; //打开段选
duan = 0; //锁存段选
}
dt_dt = dt_wei[8];//清除位码,关闭所有数码管
}
//将要显示的正整数临时存储在数组里面,然后循环显示数组里的元素即可
//x传递的数据10进制(x不是8位则高位不会亮)
void convert_long_int(unsigned long int x)
{
static unsigned char i = 0; //数组标号也是段选真值
static unsigned char a = 0; //流水号,用来表示数码管1~8个
for (a = 0;a < 8;a++) //将x没位的值存放到数组里面
{
i = x % 10;//获取最后一位数字
if ((a!=0)&&(x==0))//防止x=0时,导致数码管不显示0
{
i = 24;//即0x00,数码管不会亮
}
x = x / 10;//每次舍去最低位
datax[a] = dt_duan[i];
}
}
//对于DS18B20温度的
void dt_convert_DS18B20(void)//小数转换后存放到数组里面
{
static unsigned int value; //计算结果值
static unsigned char a; //流水号,用来表示数码管1~8个
static unsigned char b;//流水号
switch(DS18B20_Accuracy_bit)//判断精度
{
case 9:b=1;break;
case 10:b=2;break;
case 11:b=3;break;
default:b=4;break;
}
for (a = 0;a < b;a++)//Accuracy_bit的值存放到数组里面
{
value = DS18B20_float_bit % 10;//获取最后一位数字
DS18B20_float_bit = DS18B20_float_bit / 10;//每次舍去最低位
datax[a] = dt_duan[value];
}
for(;a<8;a++)
{
value=DS18B20_int_bit % 10;//获取最后一位数字
if(a==b)//要加入小数学
{
datax[a] = dt_duan[value]+dt_duan[10];
}
else if(DS18B20_int_bit==0)
{
if(DS18B20_flag_bit)
{
DS18B20_flag_bit=0;
datax[a]=dt_duan[11];//显示-号
}
else
datax[a]=dt_duan[24];//不显示0
}
else
{
datax[a] = dt_duan[value];
}
DS18B20_int_bit = DS18B20_int_bit / 10;//每次舍去最低位
}
}
void RES_dt_dispaly(void)//清除数码管,关闭数码管,初始化数码管
{
unsigned char i;
unsigned long int y=11111111;
unsigned long int z = 63;//大概1秒
dt_dt = dt_wei[8];//清除位码,关闭所有数码管
wei = 1;
wei = 0;
duan = 1;
duan = 0;//恢复单片机IO口默认高电平状态
for (i = 0; i < 8; i++)
{
convert_long_int(y);
while (z--!=0)
{
dt_display();
}
y += 11111111;
z = 20;//大概很快
}
dt_dt = dt_wei[8];//清除位码,关闭所有数码管
wei = 1;
wei = 0;
duan = 1;
duan = 0;//恢复单片机IO口默认高电平状态
}
time.h
#ifndef __TIME_H__
#define __TIME_H__
//定时器0初始化,并设定ms毫秒且工作方式1,16位模式
extern void Timer0_16bit(unsigned char time0_16_ms);
#endif // !__TIME_H__
time.c
#include"REG52.h"
#include "dt.h"
sfr T2MOD = 0xC9;
static unsigned char H0 = 0, L0 = 0;
//定时器0初始化,设定ms毫秒且工作方式1,16位模式
void Timer0_16bit(unsigned char time0_16_ms)
{
TMOD |= 0x01;
TH0 =(65536 - (unsigned int)(time0_16_ms * 921.6)) / 256;//需将计算的小数转换整形
TL0 =(65536 - (unsigned int)(time0_16_ms * 921.6)) % 256;//需将计算的小数转换整形
H0 = TH0;
L0 = TL0;
EA = 1;//开启总中断
ET0 = 1;//允许定时器0溢出进入中断
TR0 = 1;//启动定时器
}
void Timer0Interrupt(void) interrupt 1
{
TH0 = H0;
TL0 = L0;
dt_display();//刷新一次10毫秒
}
delay.h
//这是一个延迟函数为毫秒级//
#ifndef __DELAY_H__
#define __DELAY_H__
void delay_us(unsigned char us);//us级别延迟,最大输入255,us--1次用6.5us,进入1次函数需要11.95us
void delay_ms(unsigned int ms);//最大输入65535,ms=1,最小1ms时间
void delay_5us(void);//延迟5us时间
#endif // !__DELAY_H__
delay.c
//软件延迟ms级别、us级别、5us
#include <intrins.h>
void delay_us(unsigned char us) //us级别延迟,最大输入255,us--1次用6.5us,进入1次函数需要11.95us
{
while (us--);
}
void delay_ms(unsigned int ms) //设置毫秒级别延迟函数,z最大输入65535
{
unsigned char x;
for (ms; ms > 0; ms--)
for (x = 114; x > 0; x--);
}
void delay_5us(void)//一进一出,延迟5us时间
{
_nop_();
}