寫的不知道好不好,有什麼不對的地方還請指出,謝了。
1、本節基于DS18B20 1總線通訊。DS18B20溫度轉換很慢,初始化複位也慢,讀取它的溫度不需要那麼頻繁,可按照需要修改。
2、驅動DS18B20,代碼3個:初始化DS18B20、寫1位元組、讀1位元組。
3、在《51單片機——DS18B20溫度傳感器-1總線通訊1.0》章節上做代碼優化了,主要解決了小數轉換及顯示問題。
4、溫度讀取出來後用數位管來顯示小數,上章節數位管顯示小數有精度問題,本次直接優化OK,隻是12位精度顯示的時候最後面那位老是0應該為0或者5的。
5、在調試的時候發現有時候會出現-0.0000,不知道是什麼問題,可能是通訊時序上的問題吧,我在通訊時序中修改了延遲代碼,故以下程式好像不會出現-0.0000
6、我覺的對于DS18B20主要是溫度讀取出來後進行不失真的算法轉換,然後就是數位管對于正負小數溫度的顯示。本節已完美達到要求。
7、感覺不錯的,清點贊或關注我,我會不定期更新優化代碼。
main.c
#include "dt.h"
#include "ds18b20.h"
#include "time.h"
#include <reg52.h>
void main(void)
{
RES_dt_dispaly();
Timer0_16bit(20);
while(1)//大概2秒轉換一下溫度
{
convert_temp_DS18B20(12);//800毫秒了
delay_ms(1000);//軟體延遲1秒
TR0=0;
read_temp_DS18B20(12);//大概3毫秒
dt_convert_DS18B20();//大概1毫秒
TR0=1;
}
}
ds18b20.h
#ifndef __DS18B20_H__
#define __DS18B20_H__
#include <reg52.h>
#include "delay.h"
#include <intrins.h>
sbit wire = P2^2;//GPIO P2.2
//DS18B20溫度存放在全局變量
extern unsigned int DS18B20_float_bit;//小數位
extern unsigned char DS18B20_int_bit;//整數位
extern unsigned char DS18B20_Accuracy_bit;//精度位
extern bit DS18B20_flag_bit;//符号位
void convert_temp_DS18B20(unsigned char); //6、轉換溫度,x為選擇使用的精度9位 10位 11位 12位,預設12位
void read_temp_DS18B20(unsigned char Accuracy);//10、讀取DS18B20的溫度,輸入選擇的精度9位 10位 11位 12位 9 10 11 12,預設12位
#endif
ds18b20.c
#include "ds18b20.h"
//與DS18B20通訊傳輸資料的時候均是先傳送低位最後傳送高位
//DS18B20溫度存放在全局變量
unsigned int DS18B20_float_bit;//小數位
unsigned char DS18B20_int_bit;//整數位
unsigned char DS18B20_Accuracy_bit;//精度位
bit DS18B20_flag_bit;//符号位
//1、複位初始化DS18B20
static void DS18B20_RST(void)//大概1毫秒 //後期加入逾時等待故障代碼
{
wire = 0;
delay_us(75); //拉低480us複位DS18B20
wire = 1; //釋放資料線
while(wire);// 逾時60us判斷是否有應答信号,逾時代碼後期在加入
/*if(wire) //沒有應答的話
{
delay_us(26);//延遲180us二次判斷DS18B20回複存在信号,即應答ACK信号
if (wire)//還沒有應答的話
{
//DS18B20初始化失敗,添加處理代碼
}
else//有應答信号了
{
delay_us(26);//延遲180us判斷是否器件釋放總線
if (wire)//釋放總線的話
{
delay_us(26);//再次延長180us
}
else
{
//DS18B20初始化失敗,添加處理代碼
}
}
}*/
//else//有應答信号了
{
while(!wire);//等待器件釋放總線//延長240us
/*if (wire == 1)//如果器件釋放總線的話
{
delay_us(38);//再次延長240us
}
else
{
//DS18B20初始化失敗,添加處理代碼
}*/
}
}
//2、向DS18B20寫1位元組
static void write_1_byte_DS18B20(unsigned char x)
{
unsigned char i;
for (i=0;i < 8;i++)
{
wire = 0;
_nop_(); //延遲大于1us
wire = x & 0x01;
delay_us(8);//延遲60us讓DS18B20好采樣
wire = 1;//釋放總線
x = x >> 1;
}
}
//3、讀DS18B20 1位元組
static unsigned char read_1_byte_DS18B20(void)
{
unsigned char i;
unsigned char dat=0;
for ( i=0 ; i < 8; i ++)
{
dat = dat >> 1;//放在for循環後面有問題,會向右多移動一位,怪不得之前的資料讀取出來有問題
wire = 0;
_nop_();
wire = 1;//釋放總線,等待DS18B20發送資料到總線上
_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();//延遲個10us時間在采集資料
_nop_();_nop_();_nop_();_nop_();
if(wire)//這個if裡面的數不建議寫成if(wire);推薦寫成if(wire==1);
{
dat += 0x80;
}
wire = 1;//釋放總線
delay_us(4);//延遲45us
}
return dat;
}
//6、轉換溫度,x為選擇使用的精度9位 10位 11位 12位,預設12位
void convert_temp_DS18B20(unsigned char x)
{
DS18B20_RST(); //初始化函數
write_1_byte_DS18B20(0xcc); //跳過64位ROM碼檢測
write_1_byte_DS18B20(0x44); //溫度轉換指令-功能指令
//x=0001 1111 0x1f 9位精度,轉換時間93.75ms
//x=0011 1111 0x3f 10位精度,轉換時間187.5ms
//x=0101 1111 0x5f 11位精度,轉換時間375ms
//x=0111 1111 0x7f 12位精度,轉換時間750ms
switch (x)
{
case 9: delay_ms(95); break;
case 10:delay_ms(190);break;
case 11:delay_ms(380);break;
default:delay_ms(750);break;//預設按照12位精度轉換時間
}
}
//10、讀取DS18B20的溫度,輸入選擇的精度9位 10位 11位 12位 9 10 11 12,預設12位
void read_temp_DS18B20(unsigned char Accuracy)
{
unsigned int TLTL=0;//小數部分
unsigned char TL = 0;//輔助處理小數部分
unsigned char TH = 0;//處理整數部分
bit flag=0;//存放符号
DS18B20_RST();//複位器件
write_1_byte_DS18B20(0xcc);//跳過ROM檢測指令
write_1_byte_DS18B20(0xbe);//讀取暫存寄存器使用的指令
TL = read_1_byte_DS18B20();//将讀取的低溫8位存起來
TH = read_1_byte_DS18B20();//将讀取的高溫8位存起來
DS18B20_RST();//強制複位,後面的資料不在讀取了
//開始使用算法計算溫度
TH = (TH << 4) + (TL >> 4);//得到整數
TL = TL & 0x0f;//得到小數部分
if (TH < 128)//先判斷是正溫度還是負溫度
{//為正數
switch (Accuracy)//比對精度
{
case 9: TL >>= 3; TLTL = TL * 5; break;//比對9位精度
case 10: TL >>= 2; TLTL = TL * 25; break;//比對10位精度
case 11: TL >>= 1; TLTL = TL * 125;break;//比對11位精度
case 12: TL >>= 0; TLTL = TL * 625;break;//比對12位精度
default:TL >>= 0; TLTL = TL * 625;break;//預設比對12位精度
}
}
else//為負數
{
flag = 1;
switch (Accuracy)//比對精度
{
case 9: TL >>= 3; if (TL)
{
TLTL = TL * 5;
TH = ~TH;
}
else
{
TLTL = 0;
TH = ~TH + 1;
} break;//比對9位精度 0.5
case 10: TL >>= 2; if (TL)
{
TLTL = (~TL & 0x03 + 1) * 25;
TH = ~TH;
}
else
{
TLTL = 0;
TH = ~TH + 1;
} break;//比對10位精度0.75
case 11: TL >>= 1; if (TL)
{
TLTL = (~TL & 0x07 + 1) * 125;
TH = ~TH;
}
else
{
TLTL = 0;
TH = ~TH + 1;
} break;//比對11位精度0.875
default: TL >>= 0; if (TL)
{
TLTL = (~TL & 0x0f + 1) * 625;
TH = ~TH;
}
else
{
TLTL = 0;
TH = ~TH + 1;
} break;//比對12位精度0.9375
}
}
DS18B20_flag_bit = flag;//存放符号位
DS18B20_int_bit= TH;//存放正整數部分
DS18B20_float_bit = TLTL;//存放小數部分
DS18B20_Accuracy_bit = Accuracy;//存放精度
}
dt.h
#ifndef __DT_H__
#define __DT_H__
#include "delay.h"
#include "ds18b20.h"
void RES_dt_dispaly(void);//複位初始化數位管,并顯示8個0~8個9
//0≤x≤99999999 unsigned int 無符号整型
//将資料x轉換到數組裡面
void convert_long_int(unsigned long int x);
//小數轉換後存放到數組裡面
void dt_convert_DS18B20(void);
//循環顯示數組裡的元素到數位管
void dt_display(void);
#endif
dt.c
//數位管dt 使用共陰極數位管即位選是陰極時數位管才選中
//使用P0-8個GPIO口
//P2.7位選腳(控制哪個數位管亮)、P2.6段選腳(送數位管值) 控制2個74鎖存器
#include <reg52.h>
#include "dt.h"
#define dt_dt P0
sbit wei = P2^7; //鎖存器位選
sbit duan = P2^6; //鎖存器段選
static unsigned char datax[8];//定義局部變量//用來存放小數及正整數,然後發給數位管進行顯示低位到高位存放
static code unsigned char dt_duan[25] = //數位管段選真值表 dt_duan[i] + 0x80 數位管顯示資料并且此資料的右下角有小數點
{
0x3F, //"0"
0x06, //"1"
0x5B, //"2"
0x4F, //"3"
0x66, //"4"
0x6D, //"5"
0x7D, //"6"
0x07, //"7"
0x7F, //"8"
0x6F, //"9"
0x80, //"."
0x40, //"-"
0x77, //"A"
0x7C, //"B"
0x39, //"C"
0x5E, //"D"
0x79, //"E"
0x71, //"F"
0x76, //"H"
0x38, //"L"
0x37, //"n"
0x3E, //"u"
0x73, //"P"
0x5C, //"o"
0x00 //熄滅
};
static code unsigned char dt_wei[9]={0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe,0xff};//1位-8位(0x7f表示最右邊哪個數位管)0xff數位管就不亮了
//重新整理數位管進行顯示
void dt_display(void)//循環顯示數組裡的元素到數位管
{
static unsigned char a=0;
for (a = 0; a < 8; a++)//a亮——a值——a值滅;a+1亮——a+1值——a+1值滅;開始循環(亮,真值,假值)
{
dt_dt = dt_wei[a];//燈亮
wei = 1; //打開位選
wei = 0; //鎖存位選
dt_dt = datax[a];//數字
duan = 1; //打開段選
duan = 0; //鎖存段選
delay_ms(1);//這個時間乘以8不能大于定時器時間
dt_dt = dt_duan[24];//清楚段碼數字熄滅,假值,防止下位數位管顯示上次的值
duan = 1; //打開段選
duan = 0; //鎖存段選
}
dt_dt = dt_wei[8];//清除位碼,關閉所有數位管
}
//将要顯示的正整數臨時存儲在數組裡面,然後循環顯示數組裡的元素即可
//x傳遞的資料10進制(x不是8位則高位不會亮)
void convert_long_int(unsigned long int x)
{
static unsigned char i = 0; //數組标号也是段選真值
static unsigned char a = 0; //流水号,用來表示數位管1~8個
for (a = 0;a < 8;a++) //将x沒位的值存放到數組裡面
{
i = x % 10;//擷取最後一位數字
if ((a!=0)&&(x==0))//防止x=0時,導緻數位管不顯示0
{
i = 24;//即0x00,數位管不會亮
}
x = x / 10;//每次舍去最低位
datax[a] = dt_duan[i];
}
}
//對于DS18B20溫度的
void dt_convert_DS18B20(void)//小數轉換後存放到數組裡面
{
static unsigned int value; //計算結果值
static unsigned char a; //流水号,用來表示數位管1~8個
static unsigned char b;//流水号
switch(DS18B20_Accuracy_bit)//判斷精度
{
case 9:b=1;break;
case 10:b=2;break;
case 11:b=3;break;
default:b=4;break;
}
for (a = 0;a < b;a++)//Accuracy_bit的值存放到數組裡面
{
value = DS18B20_float_bit % 10;//擷取最後一位數字
DS18B20_float_bit = DS18B20_float_bit / 10;//每次舍去最低位
datax[a] = dt_duan[value];
}
for(;a<8;a++)
{
value=DS18B20_int_bit % 10;//擷取最後一位數字
if(a==b)//要加入小數學
{
datax[a] = dt_duan[value]+dt_duan[10];
}
else if(DS18B20_int_bit==0)
{
if(DS18B20_flag_bit)
{
DS18B20_flag_bit=0;
datax[a]=dt_duan[11];//顯示-号
}
else
datax[a]=dt_duan[24];//不顯示0
}
else
{
datax[a] = dt_duan[value];
}
DS18B20_int_bit = DS18B20_int_bit / 10;//每次舍去最低位
}
}
void RES_dt_dispaly(void)//清除數位管,關閉數位管,初始化數位管
{
unsigned char i;
unsigned long int y=11111111;
unsigned long int z = 63;//大概1秒
dt_dt = dt_wei[8];//清除位碼,關閉所有數位管
wei = 1;
wei = 0;
duan = 1;
duan = 0;//恢複單片機IO口預設高電平狀态
for (i = 0; i < 8; i++)
{
convert_long_int(y);
while (z--!=0)
{
dt_display();
}
y += 11111111;
z = 20;//大概很快
}
dt_dt = dt_wei[8];//清除位碼,關閉所有數位管
wei = 1;
wei = 0;
duan = 1;
duan = 0;//恢複單片機IO口預設高電平狀态
}
time.h
#ifndef __TIME_H__
#define __TIME_H__
//定時器0初始化,并設定ms毫秒且工作方式1,16位模式
extern void Timer0_16bit(unsigned char time0_16_ms);
#endif // !__TIME_H__
time.c
#include"REG52.h"
#include "dt.h"
sfr T2MOD = 0xC9;
static unsigned char H0 = 0, L0 = 0;
//定時器0初始化,設定ms毫秒且工作方式1,16位模式
void Timer0_16bit(unsigned char time0_16_ms)
{
TMOD |= 0x01;
TH0 =(65536 - (unsigned int)(time0_16_ms * 921.6)) / 256;//需将計算的小數轉換整形
TL0 =(65536 - (unsigned int)(time0_16_ms * 921.6)) % 256;//需将計算的小數轉換整形
H0 = TH0;
L0 = TL0;
EA = 1;//開啟總中斷
ET0 = 1;//允許定時器0溢出進入中斷
TR0 = 1;//啟動定時器
}
void Timer0Interrupt(void) interrupt 1
{
TH0 = H0;
TL0 = L0;
dt_display();//重新整理一次10毫秒
}
delay.h
//這是一個延遲函數為毫秒級//
#ifndef __DELAY_H__
#define __DELAY_H__
void delay_us(unsigned char us);//us級别延遲,最大輸入255,us--1次用6.5us,進入1次函數需要11.95us
void delay_ms(unsigned int ms);//最大輸入65535,ms=1,最小1ms時間
void delay_5us(void);//延遲5us時間
#endif // !__DELAY_H__
delay.c
//軟體延遲ms級别、us級别、5us
#include <intrins.h>
void delay_us(unsigned char us) //us級别延遲,最大輸入255,us--1次用6.5us,進入1次函數需要11.95us
{
while (us--);
}
void delay_ms(unsigned int ms) //設定毫秒級别延遲函數,z最大輸入65535
{
unsigned char x;
for (ms; ms > 0; ms--)
for (x = 114; x > 0; x--);
}
void delay_5us(void)//一進一出,延遲5us時間
{
_nop_();
}