说明
本人使用的是清翔的51单片机开发板,如果型号相同最方便,但是如果型号不同也可以参考,因为芯片都是一样的,只是外设不同而已,使用时只需要对照自己的开发板原理图稍微修改下引脚即可。
|
本次笔记对应视频教程的第40,,41集 数字温度传感器DS18B20(理论+实践)
如果笔记之中有任何错误,请在评论区指出,谢谢
目录
一、DS18B20
1.1 总览
注意中文数据手册有些许错误,看的时候需要留心。
1.2 两种供电方式
DS18B20可以不接VCC而从数据引脚吸取电源,这样最少只需要数据脚和接地两根线就行了。
1.3 温度寄存器的格式
S为0表示正的温度,S为1表示负的温度
注:上电默认温度85℃
1.4 内部存储器结构
设置完数据之后可以再发送拷贝命令把数据复制到EEPROM中,上电后会自动把EEPROM的数据复制到TH,TL,和配置寄存器中,实现掉电不丢失数据。
配置报警
位0和位1是测得的温度,位2位3是用户设置的高低温度报警数据,位4是配置寄存器
配置精度
存储器位4是配置精度的
1.5 访问DS18B20的顺序
通过单线总线端口访问 DS18B20 的协议如下:
- 步骤1. 初始化
- 步骤2. ROM 操作指令必须先发出一条ROM指令表明了顺序很重要
- 步骤3. DS18B20 功能指令
每一次 DS18B20 的操作都必须满足以上步骤,若是缺少步骤或是顺序混乱,器件将不会返回值。
1.6 ROM指令
Search ROM [F0h] ( 搜索 ROM 指令)
READ ROM [33h] ( 读取 ROM 指令 )
MATH ROM [55h] ( 匹配 ROM 指令 )
SKIP ROM [CCh] ( 忽略 ROM 指令)
ALARM SEARCH [ECH] 报警 搜索指令
1.7 功能指令
1.8 功能指令总结表
二、 单总线信号
2.1 初始化时序
主机先拉低总线480us以上,构成复位脉冲,然后释放总线,DS18B20会在探测到上升沿后,等待15到60us,再拉低总线60到240us构成存在脉冲。
2.2 写时序
要产生写时序,先把总线拉低至少1us,如果写0,总线必须拉低60到120us,然后释放总线,如果写1,在写时序之后15us内把总线拉高,总时长要大于60us。注意两次写周期之间至少要间隔1us
2.3 读时序
要产生读时序,必须把总线拉低至少1us,然后释放总线,在读信号开始后15us内总线控制器采样总线数据,读一位数据至少保持在60us以上。两次读周期之间至少间隔1us
2.4 操作举例
三、 开发板原理图上的DS18B20
由于开发板上只挂载了1个DS18B20,所以可以直接发送忽略ROM指令,不需要发送64位地址,节省时间。
四、编程
4.1 创建工程
复制上一份工程文件夹,修改名称为“14.数字温度传感器DS18B20”,进入项目文件夹,打开工程文件,删除main.c函数的内容。
4.2 main.c
void main()
{
int TEM;
uchar TEML;
uchar TEMH;
while(1)
{
DSinit(); //初始化DS18B20
DSWriteByte(0xCC); //发送忽略ROM指令
DSWriteByte(0x44); //发送温度转换功能指令
//上面3个步骤已经完成,再次操作需要再次进行3步
DSinit(); //初始化DS18B20
DSWriteByte(0xCC); //发送忽略ROM指令
DSWriteByte(0xBE); //读取暂存器功能指令
TEML = DSReadByte(); //读取1个字节(温度低位)
TEMH = DSReadByte(); //读取1个字节(温度高位)
DSinit(); //停止读取数据
TEM = TEMH;
TEM <<= 8;
TEM |= TEML;
TEM = TEM * 0.0625*10 + 0.5; //扩大十倍并四舍五入
SEG_DIS3(TEM);
SEG_DIS(2,22);
}
}
/**********************************************************************************/
/************************* DS18B20 *********************************************/
/**********************************************************************************/
//DS18B20初始化
bit DSinit()
{
bit i;
DS = 1;
_nop_();
DS = 0;
delay_us(73); //11.95 + 6.5*x = 480,得x ≈ 73
DS = 1; //释放总线
delay_us(8); //等待60us, 11.95 + 6.5*x = 60,得x=8
i = DS;
delay_us(29); //等待DS发送完存在信号,200us, 11.95+6.5*x=200,得x=29
DS = 1;
return (i);
}
//DS18B20写时序,写一个字节
void DSWriteByte(uchar DAT)
{
uchar i;
DS = 1;
for (i = 0; i < 8; i++)
{
DS = 0;
_nop_();
DS = DAT & 0x01;
DAT >>= 1;
delay_us(11); //写0和写1都要求时长超过60us,11.95+6.5*x=80,->x=11
DS = 1; //释放总线,等待下一次传输
_nop_();
}
DS = 1;
}
//DS18B20读一个字节
uchar DSReadByte()
{
uchar i;
uchar j;
uchar dat;
DS = 1;
for (i = 0; i < 8; i++)
{
DS = 0;
_nop_(); //产生读时序
DS = 1; //释放总线
delay5us(); //延时一会儿
j = DS;
delay_us(10); //延时等待这次数据DS18B20发送完全
DS = 1;
_nop_();
dat = (j<<7)|(dat>>1); //接收到的数据是从低位到高位,顺序调换一下
}
return (dat);
}
void delay_us(uchar us)
{
while(us--); //进入需要11.95us,执行一次us--需要6.5us
}
delay_us的时间可以用debug功能查看 。本代码并未考虑温度为负的情况。根据温度寄存器的格式,高字节前5位都是符号位,所以可以判断高字节前5位的任何1位是否为1,为1则表示温度为负。再处理一下数码管显示部分,就可以实现负温度显示了。
注意负数是以补码的形式存储在内存中的,所以还要处理一下,还原温度值。关于负数如何以补码的形式存储,请自行查阅资料。为了验证自己的负数代码是否正确,可以注释掉从DS18B20读取数据的代码,然后自己手动给对应的变量赋值,编译下载看数码管是显示正常。
对于显示位数,也可以通过处理显示函数来实现,温度整数部分最大是3位,小数部分最大是4位,可以先判断温度是否为负,来决定是否显示负号,再判断百位,十位,个位是不是0,如果是0则不显示,位置可以用一个变量来判断,某个位有数字显示(非0),就显示完之后让这个变量自加,如果某个位是0,就不运行这个位的代码,从而能实现自动向左靠齐。个位判断完后,在个位显示代码里面要加上小数点,然后再处理剩下的小数部分。小数部分最多4位,如果想实现不显示小数最后的0,可以判断最后1位是否为0,为0则不显示,如果最后一位为0,再判断倒数第2位是否为0,如此连续判断4位小数,就可以实现去掉小数末尾的0
在显示全部整数和小数中,就没必要进行四舍五入了,直接显示原转换精度,把转换后的TEM乘以0.0625再乘以10000,这样就能去掉全部的小数,把小数转换成了一个整数,再送入数码管显示处理。
注意,由于DS18B20测温范围最大125℃,日常测温在10到40度之间,但是第二位的小数点是手动打上去的,实际变量的值要乘以10倍,也就是100到400,超过了uchar类型的范围,因此我修改了一下SEG_DIS3为
void SEG_DIS3(uint i)
{
SEG_DIS(1, i / 100);
SEG_DIS(2, i % 100 / 10);
SEG_DIS(3, i % 10);
}
只需要修改一下参数由uchar编程uint就行了。
如果想要修改成其他精度,可以参考
- 1.5 访问DS18B20的顺序
- 1.6 ROM指令
- 1.7 功能指令
这三个小节的内容。
DSinit();//初始化DS18B20
DSWriteByte(0xcc);//发送跳跃ROM指令
DSWriteByte(0x4e);//写暂存器指令
DSWriteByte(0x7f);
DSWriteByte(0xf7);
DSWriteByte(0x1f);//配置工作在9位模式下
DSinit();//初始化DS18B20
DSWriteByte(0xcc);//发送跳跃ROM指令
DSWriteByte(0x48);//把温度上下限和精度设置存储到EEPROM中,下次上电自动读取
这些内容放在while循环的前面就行了,因为这些内容只需要执行一次,不需要一直在while循环里面重复执行,而且EEPROM有写入次数限制
4.3 main.h
写入函数声明和变量定义
/************** DS18B20函数 *************************/
bit DSinit(); //DS18B20初始化
void DSWriteByte(uchar DAT); //DS18B20写时序,写一个字节
uchar DSReadByte(); //DS18B20读一个字节
sbit DS = P2^2;
4.4 修改后的代码
void SEG_DIS(uchar position, uchar number)
{
//P0 = 0xFF;
DU = 0;
P0 = we[position - 1];
WE = 1;
WE = 0;
//P0 = 0x00;
P0 = du[number];
DU = 1;
DU = 0;
}
bit s; //标志温度是否为负的变量
void main()
{
//这些内容只需要执行一次即可,执行完可以注释掉
unsigned long TEM;
uchar TEML;
uchar TEMH;
uchar i;
//
// DSinit(); //初始化DS18B20
// DSWriteByte(0xCC); //发送忽略ROM指令
// DSWriteByte(0x4e); //写暂存器指令
// DSWriteByte(0x1e); //设置报警温度上限:30
// DSWriteByte(0x1d); //设置报警温度下限:29
// DSWriteByte(0x7f); //设置12位精度
//
// DSinit(); //初始化DS18B20
// DSWriteByte(0xCC); //发送忽略ROM指令
// DSWriteByte(0x48); //拷贝暂存器数据到EEPROM实现掉电不丢失
//循环部分
while(1)
{
DSinit(); //初始化DS18B20
DSWriteByte(0xCC); //发送忽略ROM指令
DSWriteByte(0x44); //发送温度转换功能指令
//上面3个步骤已经完成,再次操作需要再次进行3步
DSinit(); //初始化DS18B20
DSWriteByte(0xCC); //发送忽略ROM指令
DSWriteByte(0xBE); //读取暂存器功能指令
TEML = DSReadByte(); //读取1个字节(温度低位)
TEMH = DSReadByte(); //读取1个字节(温度高位)
DSinit(); //停止读取数据
TEM = TEMH;
TEM <<= 8;
TEM |= TEML;
if (TEMH & 0x80) //符号位为1,说明温度小于0,需要根据补码求原码,显示负号
{
TEM = ~TEM + 1;
s = 1;
}
else
s = 0; //温度为正数,不显示负号,标志位为0
TEM = TEM * 0.0625*10000; //扩大10000倍,4位小数全部变成整数
i = 0;
//符号位
if (s) SEG_DIS(++i, 20);
//百位
if (TEM < 1000000);
else SEG_DIS(++i,TEM/1000000%10);
//十位
if (TEM < 100000);
else SEG_DIS(++i,TEM/100000%10);
//个位
SEG_DIS(++i,TEM/10000%10);
delay(1); //延时是因为个位显示比较暗
SEG_DIS(i,22);
//第一位小数
SEG_DIS(++i,TEM/1000%10);
//倒数第3位小数
if (TEM % 1000) SEG_DIS(++i,TEM/100%10);
//倒数第2位小数
if (TEM % 100) SEG_DIS(++i,TEM/10%10);
//最后1位小数
if (TEM%10) SEG_DIS(++i,TEM%10);
}
}
/**********************************************************************************/
/************************* DS18B20 *********************************************/
/**********************************************************************************/
//DS18B20初始化
bit DSinit()
{
bit i;
DS = 1;
_nop_();
DS = 0;
delay_us(73); //11.95 + 6.5*x = 480,得x ≈ 73
DS = 1; //释放总线
delay_us(8); //等待60us, 11.95 + 6.5*x = 60,得x=8
i = DS;
delay_us(29); //等待DS发送完存在信号,200us, 11.95+6.5*x=200,得x=29
DS = 1;
return (i);
}
//DS18B20写时序,写一个字节
void DSWriteByte(uchar DAT)
{
uchar i;
DS = 1;
for (i = 0; i < 8; i++)
{
DS = 0;
_nop_();
DS = DAT & 0x01;
DAT >>= 1;
delay_us(11); //写0和写1都要求时长超过60us,11.95+6.5*x=80,->x=11
DS = 1; //释放总线,等待下一次传输
_nop_();
}
DS = 1;
}
//DS18B20读一个字节
uchar DSReadByte()
{
uchar i;
uchar j;
uchar dat;
DS = 1;
for (i = 0; i < 8; i++)
{
DS = 0;
_nop_(); //产生读时序
DS = 1; //释放总线
delay5us(); //延时一会儿
j = DS;
delay_us(10); //延时等待这次数据DS18B20发送完全
DS = 1;
_nop_();
dat = (j<<7)|(dat>>1);
}
return (dat); //接收到的数据是从低位到高位,顺序调换一下
}
4.5 现象
位数会随温度变化而变化,要测试负数,可以自己赋值s=1
本次笔记对应视频教程的第40,,41集 数字温度传感器DS18B20(理论+实践),到此结束。
|
下次笔记将对应视频教程的第42,43集 红外通讯(理论+实践)
|
如果笔记之中有任何错误,请在评论区指出,谢谢