阅读本文之前请先找到DS18B20的中文手册
DS18B20数据手册-中文版 - 知乎 (zhihu.com)
下面先对DS18B20简单进行一些介绍:
学校课程设计中常见的DS18B20的引脚一般为3脚封装,如下图左侧所示:
可以看到,DS18B20的引脚为GND,DQ,VDD。其中GND与VDD的引脚分别接地线和电源就好了,而DQ线是主要的数据传输口,它可以接在P1-P6端口的引脚上,因为数据传输主要靠拉高或者拉低电平,再由单片机判断高低电平,所以接哪个口都可以。
DS18B20测量完温度后会将数据存在温度寄存器中,储存的格式如下:
可以看到,高五位是S,即SIGN,符号位,而低11位用来储存数据,DS18B20的精度逐渐调低时,由BIT0到高位会逐渐未定义(即BIT0未定义时,精度会下降,精度再下降的话,就是BIT1未定义,因为BIT0-BIT3负责记录小数点后的数据)。这里需要注意的是,温度寄存器的数据同样在DS18B20的ScratchPad中,关于ScratchPad是什么,接下来我会详细讲解。
DS18B20的具体操作:
DS18B20存储温度数据时是分两位字节存储的,两字节16位数据中,高五位为符号位,低十一位为数据位。因此,读取DS18B20的数据需要连续进行两次读一字节的操作,先读低八位,后读高八位。在发送读ScratchPad指令后,传感器会由低到高传送共9字节的数据,我们只需要读取两字节即可读出数据。ScratchPad中默认的字节顺序如下:
(此处可以看到ScratchPad其实就是一个存数据的寄存器,我们需要的数据就在它的BYTE0与BYTE1那里)
而访问DS18B20的事件序列如下所示:
第一步:初始化
第二步:ROM命令(紧跟任何数据交换请求)
第三步:DS18B20功能命令(紧跟任何数据交换请求)
每次对DS18B20的访问都必须遵循这样的步骤来进行,如果这些步骤中的任何一个丢失或者没有执行,则DS18B20将不会响应。因此,实现温度转换的具体步骤即为:初始化、发送跳过ROM命令、发送温度转换命令、再次初始化、再次发送跳过ROM命令、读取ScratchPad中头两个字节的数据。(此处可以跳过ROM是因为只有一个传感器,无需通过DS18B20本身的编码来确定是哪个传感器,如果有多个传感器连接在单片机上,则跳过命令会使传回的数据发生错误。)
通过以上步骤,即可实现对DS18B20的初始化以及开始温度转换,并最终返回存储在ScratchPad中的温度转换后的数据。而此数据在经过函数转化后,便会被存在数组中,之后在LCD上进行输出。
代码部分如下:
//MAKER: JK2001 ZHZ
#include <MSP430.h>
#include<LCD1602.h>
#define uchar unsigned char
#define uint unsigned int
uchar tem[16]={"The temp is"};
//此部分为时钟的初始化,DS18B20需要极为精确的微秒操作
#define CPU_F ((double)1000000)
#define delay_us(x) __delay_cycles((long)(CPU_F*(double)x/1000000.0))
#define delay_ms(x) __delay_cycles((long)(CPU_F*(double)x/1000.0))
#define DQ0 P2OUT &=~BIT6 //此处定义了DS18B20的端口
#define DQ1 P2OUT |= BIT6
unsigned int value;
uint Init_18B20(){ //18B20初始化
P2DIR |= BIT6;
uint i = 9;
DQ1; //先上拉电平打断初始化之前的操作
DQ0; //拉低电平等待响应
delay_us(500);
DQ1;
P2DIR &= ~BIT6;
delay_us(80);
if(P2IN & BIT6){i=1;P2DIR|=BIT6;}
else{i=0;P2DIR|=BIT6;} //如果是低电平,则初始化成功
delay_us(300);
return i;
}
void send_byte(uchar cmd){ //发送一个字节的命令
uint num=0;
for(num=0;num<8;num++){ //进行循环以发送每一位的数据
P2DIR |= BIT6;
DQ0;
delay_us(8);
if(cmd & 0x01){DQ1;}
delay_us(60);
DQ1;
cmd >>= 1;
}
}
uchar read_byte(void){ //读取一个字节
uint i;
uchar data=0;
for(i=0;i<8;i++){
data>>=1; //右移,读取下一位
DQ0;
delay_us(5);
DQ1;
delay_us(8);
P2DIR &= ~BIT6;
_NOP();
if(P2IN & BIT6){data|=0x80;}
delay_us(50);
P2DIR |= BIT6;
DQ1;
delay_us(10);
}
return data;
}
uint read_temp(void){ //读取操作进行两次并将结果合并
uint low=0;
uint high=0;
uint temp = 0;
low = read_byte();
high= read_byte();
temp = (high*256) | low;
return temp;
}
void skip(void){ //向18B20发送跳过ROM命令
send_byte(0xcc);}
void zh(void){
send_byte(0x44); //温度转换命令
}
void read_sp(void){ //读取ScratchPad命令
send_byte(0xbe);}
uint do1zh(void){ //做一次转换
uchar i=0;
do{i = Init_18B20();} //初始化
while(i);
skip(); //跳过ROM
zh(); //转换
delay_ms(800); //转换时延时,12位精度最少需要750ms
do{i = Init_18B20();} //初始化
while(i);
skip(); //跳过ROM命令
read_sp(); //读取数据
return read_byte(); //返回数据
}
uchar dN[6]={"000000"}; //初始化dN数组
void Disp_Numb(uint temper){ //通过温度转换函数转换数据
uchar i;
for(i = 0;i < 6;i++) dN[i] = 0; //初始化显示变量
if(temper & BIT0)
{dN[0] = 5;dN[1] = 2;dN[2] = 6;} //0.0625
if(temper&BIT1)
{dN[1] += 5;dN[2] += 2;dN[3] += 1;} //0.125
if(temper & BIT2) //0.25
{
dN[2] += 5;dN[3] += 2;
if(dN[2] >= 10)
{dN[2] -= 10;dN[3] += 1;}
}
if(temper & BIT3) //0.5
{dN[3] += 5;}
if(temper & BIT4)
{dN[4] += 1;}
if(temper & BIT5)
{dN[4] += 2;}
if(temper & BIT6)
{dN[4] += 4;}
if(temper & BIT7)
{dN[4] += 8;
if(dN[4] >= 10)
{dN[4] -= 10;dN[5] += 1;}
}
if(temper& BIT8)
{dN[4] += 6;dN[5] += 1;
if(dN[4] >= 10)
{dN[4] -= 10;dN[5] += 1;}
}
if(temper & ~BIT9)
{dN[4] += 2;dN[5] += 3;
if(dN[4] >= 10)
{dN[4] -= 10;dN[5] += 1;}
}
if(temper & ~BITA)
{dN[4] += 4;dN[5] += 6;
if(dN[4] >= 10)
{dN[4] -= 10;dN[5] += 1;}
if(dN[5] >= 10)
{dN[5] -= 10;}
}
}
//此处的主函数实现的主要功能是将测试温度显示在LCD上
int main (void)
{
WDTCTL = WDTPW + WDTHOLD;
P2DIR |= BIT6;
P2OUT |= BIT6;
Port_init();
LCD_init();
LCD_Desk();
LCD_Write_str(0,0,tem); //输出temp is,此处和下处调用了头文件
while (1){
Disp_Numb(do1zh());
LCD_Write_char(0x09,1,dN[5]+0x30); //0X30是ASCII码转换
LCD_Write_char(0x0a,1,dN[4]+0x30);
LCD_Write_char(0x0b,1,0x2e); //0x2e是小数点的ASCII码值
LCD_Write_char(0x0c,1,dN[3]+0x30);
LCD_Write_char(0x0d,1,dN[2]+0x30);
LCD_Write_char(0x0e,1,dN[1]+0x30);
LCD_Write_char(0x0f,1,dN[0]+0x30);
}
}
以上代码除了DS18B20本身的代码外,其余部分使用了LCD1602的头文件,这个并不难,所以不再赘述,有必要说明的是,DS18B20的各种操作可以参照51单片机关于DS18B20的操作,这里给出的是我自己基于MSP430修改的版本。
此版本代码是确认无误的,如果有问题,那反正不是我的问题