本节主要是通过我们的51单片机的gpio来模拟一线式总线通信,通过单总线(也就是一线是总线)和ds18b20温度传感器进行通信,读出环境中的温度,并通过数码管显示当前的温度。
1) 单总线通信协议
单总线协议主要分为初始化总线协议,读总线协议和写总线协议.
单总线初始化协议过程大致如下:先输出低电平,至少保持480us,一般在480us到960us之间,接着输出高电平,延时15~60us.这个时候51单片机就进入了接收模式,这个时候从机需要60到240us发送一个低电平脉冲,如果这时主机接收到的是低电平,就需要主机从开始进入接收模式到初始化结束至少延时480us,这才算是完成了单总线通信的初始化‘。
单总线写时序协议过程大致如下:但总线协议的写时序还主要分为写0和写1,本身它的传输也是低位传输。整个写时序的时间至少需要60us,并且两次写时序的时间间隙至少是1us.写1时序主要是先输出低电平,延时2us,然后写1,延时50us.写0时序,先写0,延时60us,然后写1,延时2us的时间.
单总线读时序协议过程大致如下:整个读时序的时间至少需要60us,而且两次读时序的时间间隙至少是1us.主机先输出低电平2us,然后延时12us,这个时候主机进入接收模式,读取当前的电平状态,然后再延时50us,读取数据也是从低位开始读取。
2) 硬件相关
关于ds18b20是介绍一些主要特性,ds18b20的内部存储器主要是由一个高速的暂存器RAM和我们一个E2PROM,后者就可以存放高温度和低温度TH,TL和配置寄存器。配置寄存器是配置不同的位数来确定温度和数字的转化,配置寄存器结构如下:
其中TM不需要我们关注,默认值就可以了。R1和R0用来配置分辨率,他们的分辨率分别对应9位,10位,11位,12位,所以他们对应的分辨率温度是:0.5℃,025℃,0.125℃,0.0625℃对应关系如下:
ds18b20默认使用12-bit的情况,我们的高速缓存器是由9个字节组成,他们的分配如下,
我们可以使用相应的命令来读取每一个寄存器的值,对应的命令可以查看芯片手册,而我们读取温度的命令是0x44H,读取时低位在前,高位在后,并且所有的温度都是以二进制补码的形式存在的,所以我们读取出来的数据都要经过转换才能得到最终的温度。
3)软件相关
软件代码如下,其中最主要的是时间的换算,我使用了调试器来计算时间,所以时间基本都是十分准确的。我们使用ds18b20来读取温度,使用数码管来显示当前的温度,控制ds18b20的代码如下:
#include "reg52.h"
#include <intrins.h>
#include "ds18b20.h"
sbit DSPORT = P3^7;
void delay_us(int n){
int i;
for(i = 0;i < n;i++);
}
void delay_12us(){
int a = 0;
}
unsigned char Ds18b20Init()
{
int n = 0;
DSPORT = 0;
delay_us(40);
DSPORT = 1;
delay_us(1);
do{
delay_us(10);
if(DSPORT == 1){
break;
}
n++;
}while(n < 3);
return 1;
}
unsigned char Ds18b20ReadByte(){
unsigned char val = 0;
int i;
unsigned char byte;
for(i = 0;i < 8;i++){
DSPORT = 0;
_nop_();
_nop_();
DSPORT = 1;
delay_12us();
byte = DSPORT;
val >>= 1;
val |= byte << 7;
delay_us(2);
}
return val;
}
void Ds18b20WriteByte(unsigned char dat){
int i;
unsigned char temp;
DSPORT = 0;
for(i = 0;i < 8;i++){
temp = dat & 0x01;
dat >>= 1;
if(temp == 1){
DSPORT = 0;
_nop_();
_nop_();
DSPORT = temp;
delay_us(2);
}
else{
DSPORT = temp;
delay_us(3);
DSPORT = 1;
}
}
}
int Ds18b20ReadTemp(){
int temp = 0;
char temh, teml;
Ds18b20Init();
delay_us(60);
Ds18b20WriteByte(0xcc);
Ds18b20WriteByte(0x44);
Ds18b20Init();
delay_us(60);
Ds18b20WriteByte(0xcc);
Ds18b20WriteByte(0xbe);
teml = Ds18b20ReadByte();
temh = Ds18b20ReadByte();
temp = temh;
temp <<= 8;
temp |= teml;
return temp;
}
我们的数码管的代码如下:
#include "reg52.h"
#include "key.h"
#include "ds18b20.h"
unsigned char smgduan[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//显示0~F的值
sbit LSA = P2^2;
sbit LSB = P2^3;
sbit LSC = P2^4;
char arr[5];
void key_scan(int temp){
int i;
float tp;
if(temp < 0){
arr[4] = 0x40;
temp = temp - 1;
temp = ~temp;
tp = temp;
temp = tp * 0.0625 * 100 + 0.5;
}
else{
arr[4] = 0x00;
tp = temp;
temp = tp * 0.0625 * 100 + 0.5;
}
for(i = 0;i < 4; i++){
arr[i] = smgduan[temp % 10];
temp /= 10;
if(i == 2){
arr[i] |= 0x80;
}
}
for(i = 0;i < 5; i++){
switch(i){
case 0:
LSA = 0; LSB = 0; LSC = 0;
break;
case 1:
LSA = 1; LSB = 0; LSC = 0;
break;
case 2:
LSA = 0; LSB = 1;LSC = 0;
break;
case 3:
LSA = 1; LSB = 1; LSC = 0;
break;
case 4:
LSA = 0; LSB = 0; LSC = 1;
break;
}
P0 = arr[i];
delay_us(4);
P0 = 0x00;
}
}
我们的主函数如下:
#include "reg52.h"
#include "key.h"
#include "ds18b20.h"
void main(){
int temp;
while(1){
temp = Ds18b20ReadTemp();
key_scan(temp);
}
}
这样我们的整个功能就完成啦。