專注電子技術(shù)學習與研究
當前位置:單片機教程網(wǎng) >> MCU設(shè)計實例 >> 瀏覽文章

實時時鐘+紅外+溫度傳感器+LCD1602顯示

作者:寒竹子   來源:本站原創(chuàng)   點擊數(shù):  更新時間:2014年05月02日   【字體:

 此程序是在51hei單片機開發(fā)板上面做的,如需要移植到自己的電路上,修改相應的端口即可,開發(fā)板完整的電路圖下載:  點這里 (注意:只需要看相關(guān)部分的連線如液晶屏 等和單片機的連接方式,其他部分可以忽略)

/**
  ****************************************************************************
  * @file      main.c
  * @author    xr
  * @date      2014年4月10日22:21:56
  * @version   V1.2.3
  * @brief     在LCD1602上顯示時鐘,當前溫度值,和NEC協(xié)議的紅外解碼值
  * @note      單片機STC89C52RC MCU  晶振 11.0592MHZ
  ****************************************************************************
  */

#include <reg52.h>
#include "mytype.h"
bit fack = 0;//接收到溫度數(shù)據(jù)標志位
bit flag1s = 0;//1s時間標志位
bit flag200ms = 0;//200ms標志
uint8 thr0, tlr0;
uint8 counter = 0, i = 0;
//數(shù)碼管編碼
uint8 code LedTable[] = { 
    0xC0,  //"0"
                0xF9,  //"1"
                0xA4,  //"2"
                0xB0,  //"3"
                0x99,  //"4"
                0x92,  //"5"
                0x82,  //"6"
                0xF8,  //"7"
                0x80,  //"8"
                0x90,  //"9"
                0x88,  //"A"
                0x83,  //"B"
                0xC6,  //"C"
                0xA1,  //"D"
                0x86,  //"E"
                0x8E   //"F"
   };
extern void InitLcd1602();
extern void LcdShowStr(uint8 x, uint8 y, uint8 * str);
extern bit StartDs18B20();
extern bit ReadDs18B20Temp(int * temp);
extern void InitDS1302();
extern void DS1302BurstRead(uint8 * time);
extern void InitInfrared();
//外部變量只是聲明,不可以重新賦值
extern bit Irflag;//紅外遙控器解碼數(shù)據(jù)接收完成標志
extern uint8 Ircode[4];//存放遙控器解碼值(用戶碼, 用戶碼反碼,鍵碼,鍵碼反碼)

void ConfigTimer0(uint16 xms);
uint8 IntToString(uint8 * str, int dat);
void main(void)
{
 uint8 buf[4];
 uint8 len = 0;
 int temp;//溫度
 int intT, decT;//整數(shù)部分,小數(shù)部分
 uint8 str[20];
 uint8 time[20];//保存時間(BCD碼值)
 uint8 psec = 0xFF;//sec最大是59,所以一定能刷新顯示
 
 InitLcd1602();
 InitDS1302();
 ConfigTimer0(10);//定時10ms
 StartDs18B20();
 InitInfrared();
 
 while (1)
 {
  if (Irflag) //紅外解碼
  {
   Irflag = 0;
   
   buf[0] = Ircode[2] / 10 % 10;//按鍵碼的十位數(shù)
   buf[1] = Ircode[2] % 10;//按鍵碼的個位數(shù)
   str[0] = buf[0] + '0';
   str[1] = buf[1] + '0';
   str[2] = '\0';
   
   LcdShowStr(14, 1, str);
  }
  
  if (flag200ms) //200ms刷新時鐘顯示
  {
   flag200ms = 0;
   
   DS1302BurstRead(time);
   if (psec != time[0]) //time[0]保存的是sec寄存器的BCD碼值
   {
    //刷新顯示
    //日期
    str[0] = '2';
    str[1] = '0';
    str[2] = (time[6] >> 4) + '0';//取年十位
    str[3] = (time[6] & 0x0F) + '0';//取年個位轉(zhuǎn)字符
    str[4] = '-';
    str[5] = (time[4] >> 4) + '0'; //月十位
    str[6] = (time[4] & 0x0F) + '0'; //月個位
    str[7] = '-';
    str[8] = (time[3] >> 4) + '0'; //日十位
    str[9] = (time[3] & 0x0F) + '0'; //日個位
    str[10] = '\0';
    
    LcdShowStr(0, 0, str);
    
    //時間
    str[0] = (time[2] >> 4) + '0';//時十位
    str[1] = (time[2] & 0x0F) + '0';//時個位
    str[2] = ':';
    str[3] = (time[1] >> 4) + '0';
    str[4] = (time[1] & 0x0F) + '0';
    str[5] = ':';
    str[6] = (time[0] >> 4) + '0';
    str[7] = (time[0] & 0x0F) + '0';
    str[8] = '\0';
    
    LcdShowStr(0, 1, str);
    
    //星期
    str[0] = (time[5] & 0x0F) + '0';//星期只有一位數(shù)字
    str[1] = '\0';
    
    LcdShowStr(11, 0, "Week");
    LcdShowStr(15, 0, str);
    
    psec = time[0];
   }
  }
  
  if (flag1s) //1s刷新溫度顯示
  {
   flag1s = 0;
   
   fack = ReadDs18B20Temp(&temp);//讀取溫度
   if (fack) //讀取成功
   {
    intT = (temp >> 4);//整數(shù)部分,將小數(shù)部分移出
    decT = (temp & 0x000F);//小數(shù)部分
    len = IntToString(str, intT);//將intT整數(shù)部分轉(zhuǎn)換成字符存入str中,并返回有效字符個數(shù)
    str[len++] = '.';//小數(shù)點
    decT = decT * 10 / 16 % 10;//小數(shù)部分轉(zhuǎn)換
    str[len++] = decT + '0';
    str[len] = '\0';
    
    LcdShowStr(8, 1, "T:");
    LcdShowStr(10, 1, str);    
   }
   else
   {
    LcdShowStr(0, 0, "error!");
   }
  }
  StartDs18B20();//重新啟動溫度轉(zhuǎn)換
 }
}
//定時器T0配置
void ConfigTimer0(uint16 xms)
{
 uint16 tmp;
 tmp = 65536-xms*11059200/12/1000;
 thr0 = (uint8)(tmp >> 8);//取高字節(jié)
 tlr0 = (uint8)(tmp & 0x00FF);//取低字節(jié)
 TMOD &= 0xF0;//清零T0控制位
 TMOD |= 0x01;//T0方式1
 TH0 = thr0;
 TL0 = tlr0;//裝入定時初值
 TR0 = 1;//啟動T0定時器
 EA  = 1;//開總中斷
 ET0 = 1;//開定時器T0中斷
 
 //PT0 = 1;//設(shè)置T0中斷優(yōu)先級為最高級
}
//整數(shù)轉(zhuǎn)換成str
unsigned char IntToString(unsigned char * str, signed int dat)
{
 unsigned char len = 0;//統(tǒng)計有效字符的個數(shù)
 signed char i = 0;//計數(shù)器
 unsigned char buff[6];//數(shù)據(jù)分解緩沖區(qū)
 
 if (dat < 0) //負數(shù)
 {
  dat = -dat;//取絕對值
  *str++ = '-';//前面加上-
  len++;//長度++
 }
 
 //分解整數(shù)dat到buff中
 do
 {
  buff[i++] = dat % 10;
  dat /= 10;
 } while (dat > 0);//分解到dat==0為止
 len += i;//長度+i,有效字符個數(shù)
 while (i-- > 0) //拷貝轉(zhuǎn)換后的ASIIC碼字符到str接收指針中
 {
  *str++ = buff[i] + '0';//轉(zhuǎn)換成ASCII字符
 }
 *str = '\0';//加上串結(jié)束符
 
 return len;//返回有效字符個數(shù)
}
//定時器T0中斷服務
void timer0_ISP() interrupt 1

 TH0 = thr0;
 TL0 = tlr0;
 counter++;
 if (counter >= 20)
 {
  counter = 0;
  flag200ms = 1;
  i++;
  if (i >= 5)
  {
   i = 0;
   flag1s = 1;
  }
 }
}


 /**********自定義頭文件************/
#ifndef _MYTYPE_H_H
#define _MYTYPE_H_H
typedef unsigned int uint16;
typedef unsigned char uint8;
typedef unsigned long uint32;
#endif //_MYTYPE_H_H

 /***************LCD1602.c*********************/
#include <reg52.h>
#include "mytype.h"
//LCD1602
sbit LCD1602_RS = P1^0;
sbit LCD1602_RW = P1^1;
sbit LCD1602_EN = P1^5;
#define LCD1602_DB P0
//液晶忙碌等待
void WaitLcd1602()
{
 uint8 sta;
 
 LCD1602_DB = 0xFF;//拉高P0口
 
 LCD1602_RS = 0;
 LCD1602_RW = 1;
 do
 {
  LCD1602_EN = 1;
  sta = LCD1602_DB;
  LCD1602_EN = 0;//關(guān)閉液晶的數(shù)據(jù)輸出
 } while (sta & 0x80);
}
//寫命令
void WriteLcd1602Cmd(uint8 cmd)
{
 WaitLcd1602();
 
 LCD1602_RS = 0;
 LCD1602_RW = 0;
 LCD1602_DB = cmd;
 LCD1602_EN = 1;//高脈沖
 LCD1602_EN = 0;
}
//寫數(shù)據(jù)
void WriteLcd1602Data(uint8 dat)
{
 WaitLcd1602();
 
 LCD1602_RS = 1;
 LCD1602_RW = 0;
 LCD1602_DB = dat;
 LCD1602_EN = 1;//高脈沖
 LCD1602_EN = 0;
}
//液晶初始化
void InitLcd1602()
{
 WriteLcd1602Cmd(0x38);//設(shè)置16*2顯示 5*7點陣 8位數(shù)據(jù)口
 WriteLcd1602Cmd(0x0C);//開顯示不顯示光標
 WriteLcd1602Cmd(0x06);//寫入一個字符時字符指針++且地址++
 WriteLcd1602Cmd(0x01);//清屏
}
//寫str到LCD1602
void LcdShowStr(uint8 x, uint8 y, uint8 * str)
{
 uint8 addr;
 
 if (y == 0)
 { //第一行
  addr = 0x00 + x;
 }
 else
 { //第二行
  addr = 0x40 + x;
 }
 
 WriteLcd1602Cmd(0x80 | addr);
 while (*str != '\0')
 {
  WriteLcd1602Data(*str++);
 }
}

/**
  *********************************************************************
  * @file     infrared.c
  * @author   xr
  * @date     2014年4月11日08:25:04
  * @version  V1.2.3
  * @brief    紅外通信-NEC協(xié)議紅外遙控器解碼驅(qū)動
  * @note     單片機STC89C52RC MCU 晶振 11.0592MHZ
  *********************************************************************
  */
 
#include <reg52.h>
#include "mytype.h"
//紅外通信接口位聲明
sbit IRD = P3^3;
bit Irflag = 0;//紅外遙控器解碼數(shù)據(jù)接收完成標志
uint8 Ircode[4];//存放遙控器解碼值(用戶碼, 用戶碼反碼,鍵碼,鍵碼反碼)
extern void LcdShowStr(uint8 x, uint8 y, uint8 * str);
/**
  * @brief     紅外通信配置
  * @param     無
  * @retval    無
  */
void InitInfrared()
{
 //使用定時器T1作為計數(shù),不定時也不進入T1中斷
 TMOD &= 0x0F;//清零T1控制位
 TMOD |= 0x10;//設(shè)置T1方式1
 ET1 = 0;//禁止T1中斷
 TR1 = 0;//在外部中斷引腳即紅外通信引腳沒有載波(低電平)和空閑(高電平)時,先關(guān)閉T1計數(shù)
 TH1 = 0;
 TL1 = 0;//清零T1計數(shù)值
 
 //使能外部中斷1
 IT1 = 1;//設(shè)置INT1中斷觸發(fā)方式為負邊沿觸發(fā)
 EX1 = 1;//開啟外部中斷1
 EA  = 1;//開啟總中斷
 PX1 = 1;
}
/**
  * @brief     獲取紅外通信引腳載波(低電平)計數(shù)值
  * @param     無
  * @retval    載波計數(shù)值(即T1計數(shù)值)
  */
uint16 GetLowCounter() //計數(shù)值*(12/11059200)即是載波時間,計數(shù)值=TH1*256+TL1
{
 IRD = 1;//在檢測IRD引腳電平時要將此引腳拉高釋放
 TH1 = 0;
 TL1 = 0;//清零T1計數(shù)值
 TR1 = 1;//開啟T1計數(shù)
 
 while (!IRD) //若IRD=0,T1開始計數(shù)
 {
  //超時判斷,因為引導碼的載波時間是9ms,所以不能等待太長時間
  if (TH1 > 0x40) //超過20ms就強制退出 (TH1*256*(12/11059200)s)
  {
   break;
  }
 }
 TR1 = 0;//關(guān)閉T1計數(shù)
 
 return (TH1*256 + TL1);//返回計數(shù)值(TL1加滿到256向TH1進1)
}
/**
  * @brief     獲取空閑時間計數(shù)值(高電平)
  * @param     無
  * @retval    空閑計數(shù)值
  */
uint16 GetHeighCounter()
{
 IRD = 1;//拉高釋放引腳,以檢測IRD引腳的電平
 TH1 = 0;
 TL1 = 0;//清零T1計數(shù)值
 TR1 = 1;//開啟計數(shù)
 
 while (IRD)
 {
  if (TH1 > 0x40) //等待超出20ms,則認為是錯誤,強制退出
  {
   break;
  }
 }
 TR1 = 0;//關(guān)閉T1計數(shù)
 
 return (TH1*256 + TL1);//返回T1計數(shù)值
}
/**
  * @brief      外部中斷1服務程序(判斷解碼)
  * @param      無
  * @retval     無
  */
void EXINT1_ISP() interrupt 2 //INT1中斷標號:2
{
 //NEC協(xié)議:引導碼(9ms載波+4.5ms的空閑) 用戶碼-用戶反碼-鍵碼-鍵碼反碼-停止位
 //   比特值0:(560us的載波+560us的空閑) 比特值1:(560us的載波+1.68ms的空閑)
 uint8 i, j;
 uint8 byte;//接收字節(jié)
 uint16 time;//獲取時間
 
 //首先判斷引導碼
 time = GetLowCounter();//time*12/11059200*1000ms
 if ((time < 7834) || (time > 8755)) //如果不在8.5ms-9.5ms的范圍就清零外部中斷1標志位,退出中斷
 {
  IE1 = 0;//清零中斷標志
  return;//退出中斷函數(shù)
 }
 time = GetHeighCounter();//空閑
 if ((time < 3686) || (time > 4608)) //如果不在4ms-5ms的范圍就退出中斷
 {
  IE1 = 0;//中斷標志硬件置位,軟件清零
  return;
 }
 //引導碼正確,開始接收解碼數(shù)據(jù)
 
 for (i = 0; i < 4; i++) //接收4個字節(jié)數(shù)據(jù)
 {
  for (j = 0; j < 8; j++) //每個字節(jié)8bit
  {
   time = GetLowCounter();//載波時間計數(shù)
   if ((time < 424) || (time > 608)) //如果載波時間不在460-660us的范圍則認為是誤碼,退出中斷
   {
    IE1 = 0;
    return;
   }
   //檢測到560us的載波,開始判斷是560us的空閑還是1.68ms的空閑
   time = GetHeighCounter();//空閑
   if ((time > 424) && (time < 608)) //560us的空閑(即比特0)
   {
    //低位在先,逐位右移
    byte >>= 1;//右移一位0
   }
   else if ((time > 1198) && (time < 1659)) //1.68ms的空閑 1.3-1.8ms
   {
    byte >>= 1;//低位在先,先右移出一位
    byte |= 0x80;//再將這一位置1
   }
   else
   {
    //誤碼
    IE1 = 0;
    return;
   }
  }
  Ircode[i] = byte;//循環(huán)接收解碼字節(jié)數(shù)據(jù)
  
 }
 //4個字節(jié)全部接收完成
 Irflag = 1;
 IE1 = 0;
}

 /**
  *******************************************************************
  * @file     ds1302.c
  * @author   xr
  * @date     2014年4月10日17:31:07
  * @version  V1.2.3
  * @brief    DS1302底層驅(qū)動   單片機STC89C52RC MCU  晶振11.0592MHZ
  *******************************************************************
  */
 
#include <reg52.h>
#include "mytype.h"
//DS1302時鐘線,數(shù)據(jù)線,和使能引腳位聲明
sbit DS1302_SCK = P3^5;
sbit DS1302_SIO = P3^4;
sbit DS1302_CE  = P1^7;
/**
  * @brief    DS1302寫一個字節(jié)數(shù)據(jù)
  * @param    待寫入的字節(jié)
  * @retval   無
  */
void DS1302WriteByte(uint8 byte)
{
 uint8 mask = 0x01;//發(fā)送掩碼(低位在前,逐位發(fā)送)
 DS1302_SCK = 0;
 
 for (mask = 0x01; mask != 0; mask <<= 1) ////發(fā)送掩碼(低位在前,逐位發(fā)送)
 {
  if ((mask & byte) == 0)
  {
   DS1302_SIO = 0;
  }
  else
  {
   DS1302_SIO = 1;
  } //將數(shù)據(jù)準備好
  DS1302_SCK = 1;//先來一個上升沿,從機DS1302進行數(shù)據(jù)的采樣鎖存
  DS1302_SCK = 0;//再來一個下降沿,主機進行數(shù)據(jù)的輸出
 }
 //當一個字節(jié)的數(shù)據(jù)發(fā)送完成后,主機(即單片機)釋放SIO數(shù)據(jù)總線,由DS1302進行數(shù)據(jù)的發(fā)送
 DS1302_SIO = 1;
}
/**
  * @brief     DS1302讀一個字節(jié)的數(shù)據(jù)
  * @param     無
  * @retval    返回讀取到的數(shù)據(jù)
  */
uint8 DS1302ReadByte()
{
 uint8 byte = 0;//存放待讀取的字節(jié)
 uint8 mask = 0x01;//接收掩碼(低位在先,逐位接收)
 DS1302_SCK = 0;
 
 for (mask = 0x01; mask != 0; mask <<= 1) //(低位在先,逐位接收)
 {
  if (DS1302_SIO == 1)
  {
   byte |= mask;//字節(jié)byte的相應位置1
  }
  else
  {
   byte &= ~mask;//字節(jié)byte的相應位清零
  }
  DS1302_SCK = 1;//先來一個上升沿,主機進行數(shù)據(jù)采樣接收
  DS1302_SCK = 0;//再來一個下降沿,從機DS1302進行數(shù)據(jù)輸出
 }
 
 return (byte);
}
/**
  * @brief     向DS1302寄存器中寫入數(shù)據(jù)
  * @param     待寫入數(shù)據(jù)的寄存器地址reg和待寫入的數(shù)據(jù)dat
  * @retval    無
*/
void WriteDS1302(uint8 reg, uint8 dat)
{
 DS1302_CE = 1;//使能DS1302
 DS1302WriteByte((reg << 1) | 0x80);//寄存器的最高位固定位1第二位為RAM/CLK選擇位,A5-A1為寄存器地址位
          //且有效位為A3-A1,A0位為讀寫方向位
 DS1302WriteByte(dat);//寫入數(shù)據(jù)
 DS1302_CE = 0;//關(guān)閉片選
 
 DS1302_SIO = 0;//由于本版本的開發(fā)板上的DS1302的SIO口沒有加上拉電阻,在釋放SIO后SIO處于
     //非0非1的不穩(wěn)定狀態(tài),由于IO口的內(nèi)部輸出通過反相器輸出,給SIO=0則SIO口
     //會輸出一個高電平1,從而保持SIO數(shù)據(jù)口的穩(wěn)定!
}
/**
  * @brief     讀DS1302寄存器
  * @param     待讀取的寄存器地址reg
  * @retval    讀取到的數(shù)據(jù)dat
  */
uint8 ReadDS1302(uint8 reg)
{
 uint8 dat = 0;
 
 DS1302_CE = 1;//使能片選
 DS1302WriteByte((reg << 1) | 0x81);//左移出一位存放讀寫位,這里選擇讀
 dat = DS1302ReadByte();
 DS1302_CE = 0;
 
 DS1302_SIO = 0;
 
 return (dat);
}
/**
  * @brief      DS1302 Burst觸發(fā)模式連續(xù)寫八個寄存器
  * @param      待寫入數(shù)據(jù)指針*dat
  * @retval     無
  */
void DS1302BurstWrite(uint8 * dat)
{
 uint8 i = 0;
 
 DS1302_CE = 1;//使能片選
 DS1302WriteByte(0xBE);//A5-A1寫入1,觸發(fā)DS1302的突發(fā)模式連續(xù)讀寫八次
 for (i = 0; i < 8; i++)
 {
  DS1302WriteByte(dat[i]);//寫Sec-WP八個寄存器
 }
 DS1302_CE = 0;//關(guān)閉片選
 
 DS1302_SIO = 0;//通過反相器SIO口輸出1,保持穩(wěn)定
}
/**
  * @brief     DS1302 Burst模式連續(xù)讀取八個寄存器
  * @param     待接收讀取的數(shù)據(jù)的指針*time
  * @retval    無
  */
void DS1302BurstRead(uint8 * time)
{
 uint8 i = 0;
 
 DS1302_CE = 1;//使能片選
 DS1302WriteByte(0xBF);//觸發(fā)突發(fā)模式讀操作
 for (i = 0; i < 8; i++)
 {
  time[i] = DS1302ReadByte();//讀取Sec-WP八個寄存器
 }
 DS1302_CE = 0;//關(guān)閉片選
 
 DS1302_SIO = 0;//使SIO口處于穩(wěn)定
}
/**
  * @brief      DS1302初始化
  * @param      無
  * @retval     無
  */
void InitDS1302()
{
 uint8 ch = 0;//用于檢測DS1302停止狀態(tài)
 uint8 InitTime[] = {0x56, 0x59, 0x23, 0x10, 0x04, 0x04, 0x14, 0x00};//2014年4月10日23:59:56 WP=0
 
 //讀取秒寄存器
 ch = ReadDS1302(0);
 if ((ch & 0x80) != 0) //檢測秒寄存器的最高位CH,CH為1標志DS1302已經(jīng)停止運行,要去除寫保護,寫入初始時間
 {
  DS1302BurstWrite(InitTime);//突發(fā)模式寫
 }
}

/**
  ******************************************************************
  * @file     ds18b20.c
  * @author   xr
  * @date     2014年4月1日21:03:47
  * @version  V1.2.3
  * @brief    溫度傳感器DS18B20檢測溫度值程序
  * @note     單片機STC89C52RC MCU   晶振 11.0592MHZ
  ******************************************************************
  */
 
#include <reg52.h>
#include <intrins.h>
//溫度傳感器引腳
sbit IO_DS18B20 = P3^2;
/**
  * @brief    軟件延時xus
  * @param    無
  * @retval   無
  */
void delayx10us(unsigned int xus)

 do
 {
  _nop_();
  _nop_();
  _nop_();
  _nop_();
  _nop_();
  _nop_();
  _nop_();
  _nop_();
 } while (xus--); //8個_nop_()和函數(shù)的進棧出棧時間大約10us
}
/**
  * @brief    DS18B20溫度傳感器初始化檢測存在脈沖
  * @param    無
  * @retval   無
  */
bit ReadDs18B20Ack()
{
 bit ack; //存放檢測到的DS18B20的存在脈沖
 
 EA = 0;//由于DS18B20的時序時間的要求很高,進出中斷需要十幾個us會影響時序時間
 IO_DS18B20 = 0;//復位脈沖
 delayx10us(60);//持續(xù)600us
 IO_DS18B20 = 1;//釋放總線,來檢測DS18B20的存在脈沖
 delayx10us(6);//延時60us后來讀取DS18B20的存在脈沖,這個時候肯定可以讀取到
 ack = IO_DS18B20;
 while (!IO_DS18B20);//等待存在脈沖的結(jié)束
 EA = 1;//重新開啟中斷
 
 return ack;//返回存在脈沖
}
/**
  * @brief    DS18B20寫一個字節(jié)數(shù)據(jù)
  * @param    待寫入數(shù)據(jù) dat
  * @retval   無
  */
void WriteDs18B20(unsigned char dat)
{
 unsigned char mask = 0x01;//低位在前,逐位發(fā)送
 
 EA = 0;//在發(fā)送每一位時必須將總中斷關(guān)閉,避免進出中斷時間影響DS18B20的讀寫
 
 for (mask = 0x01; mask != 0; mask <<= 1)
 {
  IO_DS18B20 = 0;//主機拉低
  _nop_();
  _nop_();//延時2us,寫1或0時延時>1us
  if ((mask & dat) == 0)
  {
   IO_DS18B20 = 0;
  }
  else
  {
   IO_DS18B20 = 1;
  }
  delayx10us(6); //延時60-120us等待DS18B20來讀取數(shù)據(jù)
  IO_DS18B20 = 1;//釋放總線,這時DS18B20會來采樣數(shù)據(jù)進行寫入
 }
 EA = 1;//寫入一個字節(jié)數(shù)據(jù)完畢,打開總中斷
}
/**
  * @brief    讀DS18B20一個字節(jié)
  * @param    無
  * @retval   讀取的數(shù)據(jù)
  */
unsigned char ReadDs18B20()
{
 unsigned char mask = 0x01;//低位在前,逐位接收
 unsigned char dat = 0;
 
 EA = 0;//DS18B20在讀取數(shù)據(jù)前要關(guān)閉中斷,避免進出中斷時間,影響讀取
 for (mask = 0x01; mask != 0; mask <<= 1)
 {
  IO_DS18B20 = 0;//讀0或讀1時主機要先拉低>1us再拉高釋放>1us時間,然后主機對DS18B20進行數(shù)據(jù)的采樣讀取
  _nop_();
  _nop_();
  IO_DS18B20 = 1;
  _nop_();
  _nop_();
  if (IO_DS18B20 == 0)
  {
   dat &= ~mask;//相應位置1
  }
  else
  {
   dat |= mask;//相應位清零
  }
  delayx10us(6);//延時60-120us,等待主機數(shù)據(jù)的采樣
 }
 EA = 1;//開總中斷
 
 return dat;
}
/**
  * @brief    啟動DS18B20溫度轉(zhuǎn)換,啟動成功則返回應答ack
  * @param    無
  * @retval   返回復位存在脈沖
  */
bit StartDs18B20()
{
 bit ack;
 
 ack = ReadDs18B20Ack();//進行DS18B20復位,并讀取DS18B20的存在脈沖
 if (ack == 0)
 {
  WriteDs18B20(0xCC);//跳過ROM的器件地址尋址,因為只有一個DS18B20
  WriteDs18B20(0x44);//啟動溫度轉(zhuǎn)換
 }
 
 return ~ack;//ack==0則轉(zhuǎn)換成功
}
/**
  * @brief    讀取DS18B20的溫度值
  * @param    無
  * @retval   返回復位存在脈沖的取反值
  */
bit ReadDs18B20Temp(int * temp) //溫度值保存到temp指針指向的變量中
{
 bit ack;//保存復位的存在脈沖
 unsigned char lsb, msb;//溫度值的低字節(jié)和高字節(jié)
 
 ack = ReadDs18B20Ack();//DS18B20復位,并讀取DS18B20的存在脈沖
 if (ack == 0) //復位成功
 {
  WriteDs18B20(0xCC);//跳過ROM多個DS18B20的尋址
  WriteDs18B20(0xBE);//讀暫存寄存器中的數(shù)據(jù)
  lsb = ReadDs18B20();//因為讀取是從低位向高位讀取的,所以先讀取的是低字節(jié)數(shù)據(jù)
  msb = ReadDs18B20();//讀取高字節(jié)數(shù)據(jù)
  //將溫度值的低字節(jié)和高字節(jié)數(shù)據(jù)進行合并成16bit數(shù)據(jù),易于計算
  *temp = ((int)(msb) << 8) + lsb;//先將高字節(jié)強制轉(zhuǎn)換成16為數(shù)據(jù),將高字節(jié)移到高八位,再將低字節(jié)放到低八位即可
 }
 
 return ~ack;//ack == 0表示讀取成功
關(guān)閉窗口

相關(guān)文章