在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ 數(shù)據(jù)庫(kù)/ 16.4 溫度傳感器 DS18B20
18. RS485 通信與 Modbus 協(xié)議
17.5 A/D 差分輸入信號(hào)
15.8 C 語(yǔ)言復(fù)合數(shù)據(jù)類型(結(jié)構(gòu)體,共用體,枚舉類型)
16.3 NEC 協(xié)議紅外遙控器
13.1 單片機(jī)通信時(shí)序解析
14.4 單片機(jī) EEPROM 單字節(jié)讀寫操作時(shí)序
13.3 多個(gè) .c 文件的初步認(rèn)識(shí)
18.2 Modbus 通信協(xié)議介紹
15.1 BCD 碼介紹
18.3 單片機(jī) Modbus 多機(jī)通信程序設(shè)計(jì)
18.1 單片機(jī) RS485 通信接口、控制線、原理圖及程序?qū)嵗?/span>
15. 實(shí)時(shí)時(shí)鐘 DS1302
14.7 單片機(jī) I2C 和 EEPROM 的綜合編程
17. 模數(shù)轉(zhuǎn)換與數(shù)模轉(zhuǎn)換
16.2 紅外遙控通信原理
13.2 1602 液晶整屏移動(dòng)程序
17.6 D/A 輸出
17.7 單片機(jī)信號(hào)發(fā)生器程序
16.4 溫度傳感器 DS18B20
14.6 單片機(jī)EEPROM的頁(yè)寫入
13.4 單片機(jī)計(jì)算器程序設(shè)計(jì)[詳細(xì)]
17.2 A/D(模數(shù)轉(zhuǎn)換)的主要指標(biāo)
17.4 PCF8591 應(yīng)用程序
17.1 A/D 和 D/A 的基本概念
17.3 PCF8591硬件接口(電路圖引腳圖)
14.3 單片機(jī) EEPROM 簡(jiǎn)介
13.5 單片機(jī)串口通信原理和控制程序
15.5 DS1302 寄存器介紹
15.2 單片機(jī) SPI 通信接口
15.6 DS1302 通信時(shí)序介紹
14.5 單片機(jī) EEPROM 多字節(jié)讀寫操作時(shí)序
16. 紅外通信與 DS18B20 溫度傳感器
14.1 單片機(jī) I2C 時(shí)序介紹
15.3 實(shí)時(shí)時(shí)鐘芯片 DS1302 介紹
15.9 單片機(jī)電子時(shí)鐘程序設(shè)計(jì)
16.1 紅外光的基本原理
15.4 DS1302 的硬件信息
15.7 DS1302 的 BURST 模式
14.2 單片機(jī) I2C 尋址模式
14. 單片機(jī) I2C 總線與 EEPROM
13. 單片機(jī) 1602 液晶與串口的應(yīng)用實(shí)例

16.4 溫度傳感器 DS18B20

DS18B20 是美信公司的一款溫度傳感器,單片機(jī)可以通過(guò) 1-Wire 協(xié)議與 DS18B20 進(jìn)行通信,最終將溫度讀出。1-Wire 總線的硬件接口很簡(jiǎn)單,只需要把 DS18B20 的數(shù)據(jù)引腳和單片機(jī)的一個(gè) IO 口接上就可以了。硬件的簡(jiǎn)單,隨之而來(lái)的,就是軟件時(shí)序的復(fù)雜。1-Wire總線的時(shí)序比較復(fù)雜,很多同學(xué)在這里獨(dú)立看時(shí)序圖都看不明白,所以這里還要帶著大家來(lái)研究 DS18B20 的時(shí)序圖。我們先來(lái)看一下 DS18B20 的硬件原理圖,如圖16-12所示。

http://wiki.jikexueyuan.com/project/mcu-tutorial-three/images/35.png" alt="" />

圖16-12 DS18B20 電路原理圖

DS18B20 通過(guò)編程,可以實(shí)現(xiàn)最高12位的溫度存儲(chǔ)值,在寄存器中,以補(bǔ)碼的格式存儲(chǔ),如圖16-13所示。

http://wiki.jikexueyuan.com/project/mcu-tutorial-three/images/36.png" alt="" />

圖16-13 DS18B20 溫度數(shù)據(jù)格式

一共2個(gè)字節(jié),LSB 是低字節(jié),MSB 是高字節(jié),其中 MSb 是字節(jié)的高位,LSb 是字節(jié)的低位。大家可以看出來(lái),二進(jìn)制數(shù)字,每一位代表的溫度的含義,都表示出來(lái)了。其中 S表示的是符號(hào)位,低11位都是2的冪,用來(lái)表示最終的溫度。DS18B20 的溫度測(cè)量范圍是從-55度到+125度,而溫度數(shù)據(jù)的表現(xiàn)形式,有正負(fù)溫度,寄存器中每個(gè)數(shù)字如同卡尺的刻度一樣分布,如圖16-14所示。

http://wiki.jikexueyuan.com/project/mcu-tutorial-three/images/37.png" alt="" />

圖16-14 DS18B20 溫度值

二進(jìn)制數(shù)字最低位變化1,代表溫度變化0.0625度的映射關(guān)系。當(dāng)0度的時(shí)候,那就是 0x0000,當(dāng)溫度125度的時(shí)候,對(duì)應(yīng)十六進(jìn)制是 0x07D0,當(dāng)溫度是零下55度的時(shí)候,對(duì)應(yīng)的數(shù)字是 0xFC90。反過(guò)來(lái)說(shuō),當(dāng)數(shù)字是 0x0001 的時(shí)候,那溫度就是0.0625度了。

首先,我先根據(jù)手冊(cè)上 DS18B20 工作協(xié)議過(guò)程大概講解一下。

1)初始化 和 I2C 的尋址類似,1-Wire 總線開始也需要檢測(cè)這條總線上是否存在 DS18B20 這個(gè)器件。如果這條總線上存在 DS18B20,總線會(huì)根據(jù)時(shí)序要求返回一個(gè)低電平脈沖,如果不存在的話,也就不會(huì)返回脈沖,即總線保持為高電平,所以習(xí)慣上稱之為檢測(cè)存在脈沖。此外,獲取存在脈沖不僅僅是檢測(cè)是否存在 DS18B20,還要通過(guò)這個(gè)脈沖過(guò)程通知 DS18B20 準(zhǔn)備好,單片機(jī)要對(duì)它進(jìn)行操作了,如圖16-15所示。

http://wiki.jikexueyuan.com/project/mcu-tutorial-three/images/38.png" alt="" />

圖16-15 檢測(cè)存在脈沖

大家注意看圖,實(shí)粗線是我們的單片機(jī) IO 口拉低這個(gè)引腳,虛粗線是 DS18B20 拉低這個(gè)引腳,細(xì)線是單片機(jī)和 DS18B20 釋放總線后,依靠上拉電阻的作用把 IO 口引腳拉上去。這個(gè)我們前邊提到過(guò)了,51單片機(jī)釋放總線就是給高電平。

存在脈沖檢測(cè)過(guò)程,首先單片機(jī)要拉低這個(gè)引腳,持續(xù)大概 480 us 到 960 us 之間的時(shí)間即可,我們的程序中持續(xù)了 500 us。然后,單片機(jī)釋放總線,就是給高電平,DS18B20 等待大概15到 60 us 后,會(huì)主動(dòng)拉低這個(gè)引腳大概是60到 240 us,而后 DS18B20 會(huì)主動(dòng)釋放總線,這樣 IO 口會(huì)被上拉電阻自動(dòng)拉高。

有的同學(xué)還是不能夠徹底理解,程序列出來(lái)逐句解釋。首先,由于 DS18B20 時(shí)序要求非常嚴(yán)格,所以在操作時(shí)序的時(shí)候,為了防止中斷干擾總線時(shí)序,先關(guān)閉總中斷。然后第一步,拉低 DS18B20 這個(gè)引腳,持續(xù) 500 us;第二步,延時(shí) 60 us;第三步,讀取存在脈沖,并且等待存在脈沖結(jié)束。

bit Get18B20Ack(){
    bit ack;
    EA = 0; //禁止總中斷
    IO_18B20 = 0; //產(chǎn)生 500us 復(fù)位脈沖
    DelayX10us(50);
    IO_18B20 = 1;
    DelayX10us(6); //延時(shí) 60us
    ack = IO_18B20; //讀取存在脈沖
    while(!IO_18B20); //等待存在脈沖結(jié)束
    EA = 1; //重新使能總中斷
    return ack;
}

很多同學(xué)對(duì)第二步不理解,時(shí)序圖上明明是 DS18B20 等待 15 us 到 60 us,為什么要延時(shí)60 us 呢?舉個(gè)例子,媽媽在做飯,告訴你大概5分鐘到10分鐘飯就可以吃了,那么我們什么時(shí)候去吃,能夠絕對(duì)保證吃上飯呢?很明顯,10分鐘以后去吃肯定可以吃上飯。同樣的道理,DS18B20 等待大概是 15 us 到 60 us,我們要保證讀到這個(gè)存在脈沖,那么 60 us 以后去讀肯定可以讀到。當(dāng)然,不能延時(shí)太久,太久,超過(guò) 75 us,就可能讀不到了,為什么是 75 us,大家自己思考一下。

2)ROM 操作指令 我們學(xué) I2C 總線的時(shí)候就了解到,總線上可以掛多個(gè)器件,通過(guò)不同的器件地址來(lái)訪問(wèn)不同的器件。同樣,1-Wire 總線也可以掛多個(gè)器件,但是它只有一條線,如何區(qū)分不同的器件呢?

在每個(gè) DS18B20 內(nèi)部都有一個(gè)唯一的64位長(zhǎng)的序列號(hào),這個(gè)序列號(hào)值就存在 DS18B20 內(nèi)部的 ROM 中。開始的8位是產(chǎn)品類型編碼(DS18B20 是 0x10),接著的48位是每個(gè)器件唯一的序號(hào),最后的8位是 CRC 校驗(yàn)碼。DS18B20 可以引出去很長(zhǎng)的線,最長(zhǎng)可以到幾十米,測(cè)不同位置的溫度。單片機(jī)可以通過(guò)和 DS18B20 之間的通信,獲取每個(gè)傳感器所采集到的溫度信息,也可以同時(shí)給所有的 DS18B20 發(fā)送一些指令。這些指令相對(duì)來(lái)說(shuō)比較復(fù)雜,而且應(yīng)用很少,所以這里大家有興趣的話就自己去查手冊(cè)完成吧,我們這里只講一條總線上只接一個(gè)器件的指令和程序。

Skip ROM(跳過(guò) ROM):0xCC。當(dāng)總線上只有一個(gè)器件的時(shí)候,可以跳過(guò) ROM,不進(jìn)行 ROM 檢測(cè)。

3)RAM 存儲(chǔ)器操作指令 RAM 讀取指令,只講2條,其它的大家有需要可以隨時(shí)去查資料。 Read Scratchpad(讀暫存寄存器):0xBE

這里要注意的是,DS18B20 的溫度數(shù)據(jù)是2個(gè)字節(jié),我們讀取數(shù)據(jù)的時(shí)候,先讀取到的是低字節(jié)的低位,讀完了第一個(gè)字節(jié)后,再讀高字節(jié)的低位,直到兩個(gè)字節(jié)全部讀取完畢。

Convert Temperature(啟動(dòng)溫度轉(zhuǎn)換):0x44

當(dāng)我們發(fā)送一個(gè)啟動(dòng)溫度轉(zhuǎn)換的指令后,DS18B20 開始進(jìn)行轉(zhuǎn)換。從轉(zhuǎn)換開始到獲取溫度,DS18B20 是需要時(shí)間的,而這個(gè)時(shí)間長(zhǎng)短取決于 DS18B20 的精度。前邊說(shuō) DS18B20 最高可以用12位來(lái)存儲(chǔ)溫度,但是也可以用11位,10位和9位一共四種格式。位數(shù)越高,精度越高,9位模式最低位變化1個(gè)數(shù)字溫度變化0.5度,同時(shí)轉(zhuǎn)換速度也要快一些,如圖16-16所示。

http://wiki.jikexueyuan.com/project/mcu-tutorial-three/images/39.png" alt="" />

圖16-16 DS18B20 溫度轉(zhuǎn)換時(shí)間

其中寄存器 R1 和 R0 決定了轉(zhuǎn)換的位數(shù),出廠默認(rèn)值就11,也就是12位表示溫度,最大的轉(zhuǎn)換時(shí)間是 750 ms。當(dāng)啟動(dòng)轉(zhuǎn)換后,至少要再等 750 ms 之后才能讀取溫度,否則讀到的溫度有可能是錯(cuò)誤的值。這就是為什么很多同學(xué)讀 DS18B20 的時(shí)候,第一次讀出來(lái)的是85度,這個(gè)值要么是沒有啟動(dòng)轉(zhuǎn)換,要么是啟動(dòng)轉(zhuǎn)換了,但還沒有等待一次轉(zhuǎn)換徹底完成,讀到的是一個(gè)錯(cuò)誤的數(shù)據(jù)。

4)DS18B20 的位讀寫時(shí)序 DS18B20 的時(shí)序圖不是很好理解,大家對(duì)照時(shí)序圖,結(jié)合我的解釋,一定要把它學(xué)明白。寫時(shí)序圖如圖16-17所示。

http://wiki.jikexueyuan.com/project/mcu-tutorial-three/images/40.png" alt="" />

圖16-17 DS18B20 位寫入時(shí)序

當(dāng)要給 DS18B20 寫入0的時(shí)候,單片機(jī)直接將引腳拉低,持續(xù)時(shí)間大于 60 us 小于 120 us就可以了。圖上顯示的意思是,單片機(jī)先拉低 15 us 之后,DS18B20 會(huì)在從 15 us 到 60 us 之間的時(shí)間來(lái)讀取這一位,DS18B20 最早會(huì)在 15 us 的時(shí)刻讀取,典型值是在 30 us 的時(shí)刻讀取,最多不會(huì)超過(guò) 60us,DS18B20 必然讀取完畢,所以持續(xù)時(shí)間超過(guò) 60 us 即可。

當(dāng)要給 DS18B20 寫入1的時(shí)候,單片機(jī)先將這個(gè)引腳拉低,拉低時(shí)間大于 1 us,然后馬上釋放總線,即拉高引腳,并且持續(xù)時(shí)間也要大于 60 us。和寫0類似的是,DS18B20 會(huì)在 15 us 到 60 us 之間來(lái)讀取這個(gè)1。

可以看出來(lái),DS18B20 的時(shí)序比較嚴(yán)格,寫的過(guò)程中最好不要有中斷打斷,但是在兩個(gè)“位”之間的間隔,是大于1小于無(wú)窮的,那在這個(gè)時(shí)間段,我們是可以開中斷來(lái)處理其它程序的。發(fā)送即寫入一個(gè)字節(jié)的數(shù)據(jù)程序如下。

void Write18B20(unsigned char dat){
    unsigned char mask;

    EA = 0; //禁止總中斷
    for (mask=0x01; mask!=0; mask<<=1){ //低位在先,依次移出 8 個(gè) bit
        IO_18B20 = 0; //產(chǎn)生 2us 低電平脈沖
        _nop_();
        _nop_();
        if ((mask&dat) == 0){ //輸出該 bit 值
            IO_18B20 = 0;
        }else{
            IO_18B20 = 1;
        }
        DelayX10us(6); //延時(shí) 60us
        IO_18B20 = 1; //拉高通信引腳
    }
    EA = 1; //重新使能總中斷
}

讀時(shí)序圖如圖16-18所示。

http://wiki.jikexueyuan.com/project/mcu-tutorial-three/images/41.png" alt="" />

圖16-18 DS18B20 位讀取時(shí)序

當(dāng)要讀取 DS18B20 的數(shù)據(jù)的時(shí)候,我們的單片機(jī)首先要拉低這個(gè)引腳,并且至少保持 1 us 的時(shí)間,然后釋放引腳,釋放完畢后要盡快讀取。從拉低這個(gè)引腳到讀取引腳狀態(tài),不能超過(guò) 15 us。大家從圖16-18可以看出來(lái),主機(jī)采樣時(shí)間,也就是 MASTER SAMPLES,是在 15 us 之內(nèi)必須完成的,讀取一個(gè)字節(jié)數(shù)據(jù)的程序如下。

unsigned char Read18B20({
    unsigned char dat;
    unsigned char mask;

    EA = 0; //禁止總中斷
    for (mask=0x01; mask!=0; mask<<=1){ //低位在先,依次采集 8 個(gè) bit
        IO_18B20 = 0; //產(chǎn)生 2us 低電平脈沖
        _nop_();
        _nop_();
        IO_18B20 = 1; //結(jié)束低電平脈沖,等待 18B20 輸出數(shù)據(jù)
        _nop_(); //延時(shí) 2us
        _nop_();
        if (!IO_18B20){ //讀取通信引腳上的值
            dat &= ~mask;
        }else{
            dat |= mask;
        }
        DelayX10us(6); //再延時(shí) 60us
    }
    EA = 1; //重新使能總中斷
    return dat;
}

DS18B20 所表示的溫度值中,有小數(shù)和整數(shù)兩部分。常用的帶小數(shù)的數(shù)據(jù)處理方法有兩種,一種是定義成浮點(diǎn)型直接處理,第二種是定義成整型,然后把小數(shù)和整數(shù)部分分離出來(lái),在合適的位置點(diǎn)上小數(shù)點(diǎn)即可。我們?cè)诔绦蛑惺褂玫氖堑诙N方法,下面我們就寫一個(gè)程序,將讀到的溫度值顯示在 1602 液晶上,并且保留一位小數(shù)位。

/***************************DS18B20.c 文件程序源代碼****************************/
#include <reg52.h>
#include <intrins.h>
sbit IO_18B20 = P3^2; //DS18B20 通信引腳
/* 軟件延時(shí)函數(shù),延時(shí)時(shí)間(t*10)us */
void DelayX10us(unsigned char t){
    do {
        _nop_();
        _nop_();
        _nop_();
        _nop_();
        _nop_();
        _nop_();
        _nop_();
        _nop_();
    } while (--t);
}
/* 復(fù)位總線,獲取存在脈沖,以啟動(dòng)一次讀寫操作 */
bit Get18B20Ack(){
    bit ack;
    EA = 0; //禁止總中斷
    IO_18B20 = 0; //產(chǎn)生 500us 復(fù)位脈沖
    DelayX10us(50);
    IO_18B20 = 1;
    DelayX10us(6); //延時(shí) 60us
    ack = IO_18B20; //讀取存在脈沖
    while(!IO_18B20); //等待存在脈沖結(jié)束
    EA = 1; //重新使能總中斷
    return ack;
}
/* 向 DS18B20 寫入一個(gè)字節(jié),dat-待寫入字節(jié) */
void Write18B20(unsigned char dat){
    unsigned char mask;
    EA = 0; //禁止總中斷
    for (mask=0x01; mask!=0; mask<<=1){ //低位在先,依次移出 8 個(gè) bit
        IO_18B20 = 0; //產(chǎn)生 2us 低電平脈沖
        _nop_();
        _nop_();
        if ((mask&dat) == 0){ //輸出該 bit 值
            IO_18B20 = 0;
        }else{
            IO_18B20 = 1;
        }
    }
    DelayX10us(6); //延時(shí) 60us
    IO_18B20 = 1; //拉高通信引腳
    EA = 1; //重新使能總中斷
}
/* 從 DS18B20 讀取一個(gè)字節(jié),返回值-讀到的字節(jié) */
unsigned char Read18B20(){
    unsigned char dat;
    unsigned char mask;
    EA = 0; //禁止總中斷
    for (mask=0x01; mask!=0; mask<<=1){ //低位在先,依次采集 8 個(gè) bit
        IO_18B20 = 0; //產(chǎn)生 2us 低電平脈沖
        _nop_();
        _nop_();
        IO_18B20 = 1; //結(jié)束低電平脈沖,等待 18B20 輸出數(shù)據(jù)
        _nop_(); //延時(shí) 2us
        _nop_();
        if (!IO_18B20){ //讀取通信引腳上的值
            dat &= ~mask;
        }else{
            dat |= mask;
        }
        DelayX10us(6); //再延時(shí) 60us
    }
    EA = 1; //重新使能總中斷
    return dat;
}
/* 啟動(dòng)一次 18B20 溫度轉(zhuǎn)換,返回值-表示是否啟動(dòng)成功 */
bit Start18B20(){
    bit ack;
    ack = Get18B20Ack(); //執(zhí)行總線復(fù)位,并獲取 18B20 應(yīng)答
    if (ack == 0){ //如 18B20 正確應(yīng)答,則啟動(dòng)一次轉(zhuǎn)換
        Write18B20(0xCC); //跳過(guò) ROM 操作
        Write18B20(0x44); //啟動(dòng)一次溫度轉(zhuǎn)換
    }
    return ~ack; //ack==0 表示操作成功,所以返回值對(duì)其取反
}
/* 讀取 DS18B20 轉(zhuǎn)換的溫度值,返回值-表示是否讀取成功 */
bit Get18B20Temp(int *temp){
    bit ack;
    unsigned char LSB, MSB; //16bit 溫度值的低字節(jié)和高字節(jié)
    ack = Get18B20Ack(); //執(zhí)行總線復(fù)位,并獲取 18B20 應(yīng)答
    if (ack == 0){ //如 18B20 正確應(yīng)答,則讀取溫度值
        Write18B20(0xCC); //跳過(guò) ROM 操作
        Write18B20(0xBE); //發(fā)送讀命令
        LSB = Read18B20(); //讀溫度值的低字節(jié)
        MSB = Read18B20(); //讀溫度值的高字節(jié)
        *temp = ((int)MSB << 8) + LSB; //合成為 16bit 整型數(shù)
    }
    return ~ack; //ack==0 表示操作應(yīng)答,所以返回值為其取反值
}

/*Lcd1602.c 文件程序源代碼***/ (此處省略,可參考之前章節(jié)的代碼)

/*****************************main.c 文件程序源代碼******************************/
#include <reg52.h>
bit flag1s = 0; //1s 定時(shí)標(biāo)志
unsigned char T0RH = 0; //T0 重載值的高字節(jié)
unsigned char T0RL = 0; //T0 重載值的低字節(jié)

void ConfigTimer0(unsigned int ms);
unsigned char IntToString(unsigned char *str, int dat);
extern bit Start18B20();
extern bit Get18B20Temp(int *temp);
extern void InitLcd1602();
extern void LcdShowStr(unsigned char x, unsigned char y, unsigned char *str);

void main(){
    bit res;
    int temp; //讀取到的當(dāng)前溫度值
    int intT, decT; //溫度值的整數(shù)和小數(shù)部分
    unsigned char len;
    unsigned char str[12];

    EA = 1; //開總中斷
    ConfigTimer0(10); //T0 定時(shí) 10ms
    Start18B20(); //啟動(dòng) DS18B20
    InitLcd1602(); //初始化液晶

    while (1){
        if (flag1s){ //每秒更新一次溫度
            flag1s = 0;
            res = Get18B20Temp(&temp); //讀取當(dāng)前溫度
            if (res){ //讀取成功時(shí),刷新當(dāng)前溫度顯示
                intT = temp >> 4; //分離出溫度值整數(shù)部分
                decT = temp & 0xF; //分離出溫度值小數(shù)部分
                len = IntToString(str, intT); //整數(shù)部分轉(zhuǎn)換為字符串
                str[len++] = '.'; //添加小數(shù)點(diǎn)
                decT = (decT*10) / 16; //二進(jìn)制的小數(shù)部分轉(zhuǎn)換為 1 位十進(jìn)制位
                str[len++] = decT + '0'; //十進(jìn)制小數(shù)位再轉(zhuǎn)換為 ASCII 字符
                while (len < 6){ //用空格補(bǔ)齊到 6 個(gè)字符長(zhǎng)度
                    str[len++] = ' ';
                }
                str[len] = '\0'; //添加字符串結(jié)束符
                LcdShowStr(0, 0, str); //顯示到液晶屏上
            }else{ //讀取失敗時(shí),提示錯(cuò)誤信息
                LcdShowStr(0, 0, "error!");
            }
            Start18B20(); //重新啟動(dòng)下一次轉(zhuǎn)換
        }
    }
}
/* 整型數(shù)轉(zhuǎn)換為字符串,str-字符串指針,dat-待轉(zhuǎn)換數(shù),返回值-字符串長(zhǎng)度 */
unsigned char IntToString(unsigned char *str, int dat){
    signed char i = 0;
    unsigned char len = 0;
    unsigned char buf[6];

    if (dat < 0){ //如果為負(fù)數(shù),首先取絕對(duì)值,并在指針上添加負(fù)號(hào)
        dat = -dat;
        *str++ = '-';
        len++;
    }
    do { //先轉(zhuǎn)換為低位在前的十進(jìn)制數(shù)組
        buf[i++] = dat % 10;
        dat /= 10;
    } while (dat > 0);
    len += i; //i 最后的值就是有效字符的個(gè)數(shù)
    while (i-- > 0){ //將數(shù)組值轉(zhuǎn)換為 ASCII 碼反向拷貝到接收指針上
        *str++ = buf[i] + '0';
    }
    *str = '\0'; //添加字符串結(jié)束符
    return len; //返回字符串長(zhǎng)度
}
/* 配置并啟動(dòng) T0,ms-T0 定時(shí)時(shí)間 */
void ConfigTimer0(unsigned int ms){
    unsigned long tmp; //臨時(shí)變量
    tmp = 11059200 / 12; //定時(shí)器計(jì)數(shù)頻率
    tmp = (tmp * ms) / 1000; //計(jì)算所需的計(jì)數(shù)值
    tmp = 65536 - tmp; //計(jì)算定時(shí)器重載值
    tmp = tmp + 12; //補(bǔ)償中斷響應(yīng)延時(shí)造成的誤差
    T0RH = (unsigned char)(tmp>>8); //定時(shí)器重載值拆分為高低字節(jié)
    T0RL = (unsigned char)tmp;
    TMOD &= 0xF0; //清零 T0 的控制位
    TMOD |= 0x01; //配置 T0 為模式 1
    TH0 = T0RH; //加載 T0 重載值
    TL0 = T0RL;
    ET0 = 1; //使能 T0 中斷
    TR0 = 1; //啟動(dòng) T0
}
/* T0 中斷服務(wù)函數(shù),完成 1 秒定時(shí) */
void InterruptTimer0() interrupt 1{
    static unsigned char tmr1s = 0;
    TH0 = T0RH; //重新加載重載值
    TL0 = T0RL;
    tmr1s++;
    if (tmr1s >= 100){ //定時(shí) 1s
        tmr1s = 0;
        flag1s = 1;
    }
}