阅读本文之前请先找到DS18B20的中文手册
DS18B20数据手册-中文版 - 知乎 (zhihu.com)
下面先对DS18B简单介绍一下:
常见的学校课程设计DS18B引脚一般为3脚封装,如下图左侧所示:
可以看到,DS18B20的引脚为GND,DQ,VDD。其中GND与VDD引脚分别接地线和电源,DQ线是主要的数据传输口,可以连接P1-P在6端口的引脚上,由于数据传输主要依靠拉高或拉低电平,然后由单片机判断,因此可以连接任何端口。
DS18B测量温度后,数据将存储在温度寄存器中,存储格式如下:
可见高五位是S,即SIGN,用于存储数据的符号位较低,DS18B当20的精度逐渐降低时,从BIT0到高位会逐渐不定义(即BIT0不定义时,精度会下降。如果精度再次下降,那就是BIT1未定义,因为BIT0-BIT3.记录小数点后的数据)。这里需要注意的是,温度寄存器的数据也里DS18B20的中,关于接下来我会详细解释一下是什么。
DS18B20存储温度数据时,分两个字节存储。在两个字节的16位数据中,高五位是符号位,低十一位是数据位。因此,读取DS18B20数据需要连续两次读一字节,先读低八位,再读高八位。在发送读ScratchPad说明结束后,传感器将从低到高传输共9字节的数据,我们只需读取两字节即可读取数据。ScratchPad默认字节顺序如下:
(此处可以看到ScratchPad事实上,它是一个存储数据的寄存器。我们需要的数据在于它BYTE0与BYTE1那里)
而访问DS18B20事件序列如下:
每次对DS18B20访问必须遵循这些步骤。如果这些步骤中的任何一个丢失或未执行,则必须遵循此步骤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(); //转换 dely_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修改的版本。
此版本代码是确认无误的,如果有问题,那反正不是我的问题