目录
前言
上次学习了EC11开关编码器的使用,这次来学习DS18B20基础的使用方法,虽然过程有点波折,但是成功显示温度的那一刻,那种感受却是无比喜悦的。
一、DS18B20简介
这东西用来干嘛的就不用多说了吧,反正不是三极管。说下器件本身的几个特点:
1、两种供电模式:
A寄生电源供电。
B外部电源供电。
2、两种连接模式:
A单个独立工作。
B多个并联在一条总线上工作。
3、通讯协议:1wire总线。
1、复位(检测存在脉冲)
2、写间隙(生成下降沿后写)
3、读间隙(生成上升沿后读)
4、读写顺序:低位在前
4 、本次学:外部电源、单个独立工作。
原理图就不讲了:数据脚给个2-10k的上拉电阻,正负脚不接反就行。
二、工作过程:
简单来说分三步(发命令前均需初始化):
1、采集温度(自动)。
2、转换温度(发命令)。
3、输出温度(发命令读取数据)。
详细点说分十步:
1、初始化(检测通讯是否正常)。
2、发送跳过编码命令(0xCC)。
3、发送温度转换命令(0x44)。
4、等待时间95ms-750ms,转换完成。
5、初始化(检测通讯),每次都必须做。
6、发送跳过编码命令(0xCC)。
7、发送温度读取命令(0xBE)。
8、读取温度寄存器低字节(低位先读)。
9、读取温度寄存器高字节(低位先读)。
10、将两字节数据合成16位整型数据。
本次只用到了三个命令:
Skip ROM: 0xCCH(跳过编码)
Convert_T: 0x44H(温度转换)
Read Scratchpad: 0xBEH(读暂存器即存温度数据的地方)
三、更详细的操作流程一张图解释:
四、 怎么初始化(复位/检测脉冲):
操作流程:
MCU将数据线拉低-
等待500us(480-960)-
MCU拉高-等待50us(15-60) -
读取数据线的值给ACK(低电平正常) -
等待高电平来到结束检测-
返回 ACK的值判断初始化(复位)结果。
下面是初始化函数:
/************************************************************************
功能描述:18B20初始化
入口参数:
返回数据:ACK 即复位结果
备注: TEMP 为总线IO口
************************************************************************/
bit Init_18b20() //初始化
{
bit ACK; //初始化结果变量声明
EA = 0; //关中断
TEMP = 0; //总线拉低
Delay500us(); //延时500
TEMP = 1; //拉高
Delay100us(); //延时100
ACK = TEMP; //检测存在脉冲
while(!TEMP); //等待存在脉冲结束
EA = 1; //关中断
return ACK; //返回存在脉冲结果
}
/************************************************************************
----------------------------秋末的蒲公英-------------------------------
************************************************************************/
上面是初始化部分处理
五、怎么发命令?怎么读数据?看手册:
这个就真不好用文字表达了,毕竟图片的信息量更大更清楚。配合代码可能会更好理解。不过有一点特别值得注意,就是在读取前的那一刻,必须保证你没有从MCU输出低电平,我可是被这个读时序图给坑惨了,按我的理解这个读取的时序图是不那么恰当的,新手(就是我)容易被误导,准备读取前数据前数据总线必须是高电平才能正常工作。这个读时序图展示的显然不是这样的。有其他意见的欢迎指正哈。
下面是写一个字节的函数:
/************************************************************************
功能描述:向18B20写一个字节
入口参数:cmd 即要输入的命令
返回数据:
备注: TEMP 为总线IO口
************************************************************************/
void Write_18b20(uint8 cmd)
{
unsigned char mask;
EA = 0; //关中断
for(mask=0x01;mask!=0;mask<<=1)
{
TEMP = 0; //拉低总线
Delay10us(); //延时10
if((mask&cmd)==0)
TEMP = 0; //写低电平
else
TEMP = 1; //写高电平
Delay50us(); //延时50
TEMP = 1; //释放总线
}
EA = 1; //关中断
}
/************************************************************************
----------------------------秋末的蒲公英-------------------------------
************************************************************************/
下来是读取一个字节函数:
/************************************************************************
功能描述:向18B20读一个字节
入口参数:
返回数据:
备注: TEMP 为总线IO口
************************************************************************/
unsigned char Read_18b20()
{
uint8 i = 8;
uint8 dat;
EA = 0; //关中断
while(i--)
{
TEMP = 0; //拉低总线
_nop_(); //延时2
TEMP = 1; //释放总线
Delay10us(); //延时10us
if(TEMP) //接收1
{
dat >>= 1;
dat |= 0x80;
}
else //接收0
{
dat >>= 1;
}
Delay50us(); //延时60
}
EA = 1; //关中断
return dat; //返回数据
}
/************************************************************************
----------------------------秋末的蒲公英-------------------------------
************************************************************************/
再下来是启动温度转换部分代码:
/************************************************************************
功能描述:启动温度转换并返回存在结果
入口参数:
返回数据:
备注:
************************************************************************/
bit Start_18b20()
{
bit ACK;
ACK = Init_18b20(); //初始化读存在脉冲
if(ACK == 0) //收到存在脉冲进行下一步
{
Write_18b20(0xCC); //Skin_ROM//只能但从机使用该指令
Write_18b20(0x44); //Convert_T启动温度转换命令
}
return ~ACK;
}
/************************************************************************
----------------------------秋末的蒲公英-------------------------------
************************************************************************/
接着获取温度寄存器中的数据:
/************************************************************************
功能描述:获取温度寄存器中的值
入口参数:
返回数据:
备注:
************************************************************************/
bit Get18b20temp(uint16 *temp) //获取温度
{
bit ACK;
uint8 LSB; //温度接收缓存低字节
uint8 MSB; //温度接收缓存高字节
ACK = Init_18b20();
if(ACK == 0)
{
Write_18b20(0xCC); //Skin_ROM
Write_18b20(0xBE); //写读暂存寄存器命令
LSB = Read_18b20(); //读暂存LSB中的温度数据
MSB = Read_18b20(); //读暂存MSB中的温度数据
}
*temp = ((int)MSB <<8 ) + LSB; //合成温度数据
return ACK;
}
/************************************************************************
----------------------------秋末的蒲公英-------------------------------
************************************************************************/
六、怎么转换温度?先看数据手册
数据手册真的超级重要,第一时间看数据手册
看上图就可以知道,数据的长读一共是16位,其中最高的5位(我思维导图说4位其实无所谓哈不影响最终结果最主要是懒得改了)任意一位都可以用来判断温度的正负,最低四位是表示温度的小数的转换过程上面说过了哈,或者可以参考下后面的转换代码。
举个负数的例子吧:例如 -10.125度,负温度要先将数据转换成补码再计算
FF5EH 的补码 = FF5EH 的反码 +1 = 00A2H
所以:
温度整数 = 00A0H *精度 = 160*0.0625 = 10
或者直接右移四位的值就是温度整数:
温度整数 = (00A2H>>4) = 00AH = 10
温度小数 = 0002H *精度 = 2*0.0625 = 0.125
下面是转换代码:
/************************************************************************
功能描述:获取温度并将温度转换成十进制送到数码管缓冲区或转换成字符串
入口参数:
返回数据:
备注:
************************************************************************/
void Temp_translate()
{
uint8 temp_buf; //整数温度缓存
uint8 symbol; //符号缓存
uint8 th = 0; //数据高四位
uint8 tm = 0; //整数部分源码
uint16 tl = 0; //小数温度缓存
uint16 temp; //温度源码缓存
Get18b20temp(&temp); //获取温度
th = (uint8)(temp>>12); //取数据高四位
tm = (uint8)((temp&0x0FF0)>>4); //取温度整数值
tl = temp&0x000F; //取温度小数值
tl = tl*625/1000; //只留一位并放大十倍
if(th) //高四位大于0说明是负数
{
symbol = '-'; //标记符号字符
temp_buf = 256-tm; //取整数温度
}
else
{
symbol = ' '; //正数不显示
temp_buf = tm; //取整数温度
}
//共阳数码管显示部分
smg_buf[0] = Smg_Char[12]; //温度符号
smg_buf[1] = Smg_Char[tl]; //温度小数
smg_buf[2] = Smg_Char[temp_buf%10]; //温度个位
smg_buf[2] &= 0x7F; //小数点
smg_buf[3] = Smg_Char[temp_buf/10%10]; //温度十位
//LCD1602显示部分
lcd_buf[0] = symbol;
lcd_buf[1] = (temp_buf/10%10) + '0'; //
lcd_buf[2] = (temp_buf%10) + '0'; //
lcd_buf[3] = '.'; //
lcd_buf[4] = tl + '0'; //
lcd_buf[5] = 'c'; //
lcd_buf[6] = '\0'; //
}
/************************************************************************
----------------------------秋末的蒲公英-------------------------------
************************************************************************/
以上仅是DS18B20驱动代码
七、结尾来看实验结果
四位数码管显示
LCD1602液晶显示