晨輝教你輕松學51--------外圍芯片之ds1302和ds18b20
對于市面上的大多數(shù)51單片機開發(fā)板來說。ds1302和ds18b20應該是比較常見的兩種外圍芯片。ds1302是具有SPI總線接口的時鐘芯片。ds18b20則是具有單總線接口的數(shù)字溫度傳感器。下面讓我們分別來認識并學會應用這兩種芯片。
首先依舊是看DS1302的datasheet中的相關介紹。 ![]() 上面是它的一些基本的應用介紹。 下面是它的引腳的描述。 ![]() 下面是DS1302的時鐘寄存器。我們要讀取的時間數(shù)據(jù)就是從下面這些數(shù)據(jù)寄存器中讀取出來的。當我們要想調整時間時,可以把時間數(shù)據(jù)寫入到相應的寄存器中就可以了。 ![]() 這是DS1302內部的31個RAM寄存器。在某些應用場合我們可以應用到。如我們想要做一個帶定時功能的鬧鐘。則可以把鬧鐘的時間寫入到31個RAM寄存器中的任意幾個。當單片機掉電時,只要我們的DS1302的備用電池還能工作,那么保存在其中的鬧鐘數(shù)據(jù)就不會丟失~~ ![]() 由于對于這些器件的操作基本上按照數(shù)據(jù)手冊上面提供的時序圖和相關命令字來進行操作就可以了。因此在我們應用這些器件的時候一定要對照著手冊上面的要求來進行操作。如果覺得還不夠放心的話。可以到網(wǎng)上下載一些參考程序。對著手冊看別人的程序,看別人的思路是怎么樣的。 DS1302和單片機的連接很簡單。只需一根復位線,一根時鐘線,一根數(shù)據(jù)線即可。同時它本身還需要接一個32.768KHz的晶振來提供時鐘源。對于晶振的兩端可以分別接一個6PF左右的電容以提高晶振的精確度。同時可以在第8腳接上一個3.6V的可充電的電池。當系統(tǒng)正常工作時可以對電池進行涓流充電。當系統(tǒng)掉電時,DS1302由這個電池提供的能量繼續(xù)工作。 下面讓我們來驅動它。 sbit io_DS1302_RST = P2^0 ; sbit io_DS1302_IO = P2^1 ; sbit io_DS1302_SCLK = P2^2 ; //-------------------------------------常數(shù)宏---------------------------------// #define DS1302_SECOND_WRITE 0x80 //寫時鐘芯片的寄存器位置 #define DS1302_MINUTE_WRITE 0x82 #define DS1302_HOUR_WRITE 0x84 #define DS1302_WEEK_WRITE 0x8A #define DS1302_DAY_WRITE 0x86 #define DS1302_MONTH_WRITE 0x88 #define DS1302_YEAR_WRITE 0x8C #define DS1302_SECOND_READ 0x81 //讀時鐘芯片的寄存器位置 #define DS1302_MINUTE_READ 0x83 #define DS1302_HOUR_READ 0x85 #define DS1302_WEEK_READ 0x8B #define DS1302_DAY_READ 0x87 #define DS1302_MONTH_READ 0x89 #define DS1302_YEAR_READ 0x8D //-----------------------------------操作宏----------------------------------// #define DS1302_SCLK_HIGH io_DS1302_SCLK = 1 ; #define DS1302_SCLK_LOW io_DS1302_SCLK = 0 ; #define DS1302_IO_HIGH io_DS1302_IO = 1 ; #define DS1302_IO_LOW io_DS1302_IO = 0 ; #define DS1302_IO_READ io_DS1302_IO #define DS1302_RST_HIGH io_DS1302_RST = 1 ; #define DS1302_RST_LOW io_DS1302_RST = 0 ; /****************************************************** * 保存時間數(shù)據(jù)的結構體 * ******************************************************/ struct { uint8 Second ; uint8 Minute ; uint8 Hour ; uint8 Day ; uint8 Week ; uint8 Month ; uint8 Year ; }CurrentTime ; /****************************************************************************** * Function: static void v_DS1302Write_f( uint8 Content ) * * Description:向DS1302寫一個字節(jié)的內容 * * Parameter:uint8 Content : 要寫的字節(jié) * * * ******************************************************************************/ static void v_DS1302Write_f( uint8 Content ) { uint8 i ; for( i = 8 ; i > 0 ; i-- ) { if( Content & 0x01 ) { DS1302_IO_HIGH } else { DS1302_IO_LOW } Content >>= 1 ; DS1302_SCLK_HIGH DS1302_SCLK_LOW } } /****************************************************************************** * Function: static uint8 v_DS1302Read_f( void ) * * Description: 從DS1302當前設定的地址讀取一個字節(jié)的內容 * * Parameter: * * Return: 返回讀出來的值(uint8) * ******************************************************************************/ static uint8 v_DS1302Read_f( void ) { uint8 i, ReadValue ; DS1302_IO_HIGH for( i = 8 ; i > 0 ; i-- ) { ReadValue >>= 1 ; if( DS1302_IO_READ ) { ReadValue |= 0x80 ; } else { ReadValue &= 0x7f ; } DS1302_SCLK_HIGH DS1302_SCLK_LOW } return ReadValue ; } /****************************************************************************** * Function: void v_DS1302WriteByte_f( uint8 Address, uint8 Content ) * * Description: 從DS1302指定的地址寫入一個字節(jié)的內容 * * Parameter: Address: 要寫入數(shù)據(jù)的地址 * * Content: 寫入數(shù)據(jù)的具體值 * * Return: * ******************************************************************************/ void v_DS1302WriteByte_f( uint8 Address, uint8 Content ) { DS1302_RST_LOW DS1302_SCLK_LOW DS1302_RST_HIGH v_DS1302Write_f( Address ) ; v_DS1302Write_f( Content ) ; DS1302_RST_LOW DS1302_SCLK_HIGH } /****************************************************************************** * Function: uint8 v_DS1302ReadByte_f( uint8 Address ) * * Description:從DS1302指定的地址讀出一個字節(jié)的內容 * * Parameter:Address: 要讀出數(shù)據(jù)的地址 * * * * Return: 指定地址讀出的值(uint8) * ******************************************************************************/ uint8 v_DS1302ReadByte_f( uint8 Address ) { uint8 ReadValue ; DS1302_RST_LOW DS1302_SCLK_LOW DS1302_RST_HIGH v_DS1302Write_f( Address ) ; ReadValue = v_DS1302Read_f() ; DS1302_RST_LOW DS1302_SCLK_HIGH return ReadValue ; } /****************************************************************************** * Function: void v_ClockInit_f( void ) * * Description:初始化寫入DS1302時鐘寄存器的值(主程序中只需調用一次即可) * * Parameter: * * * * Return: * ******************************************************************************/ void v_ClockInit_f( void ) { if( v_DS1302ReadByte_f( 0xc1) != 0xf0 ) { v_DS1302WriteByte_f( 0x8e, 0x00 ) ; //允許寫操作 v_DS1302WriteByte_f( DS1302_YEAR_WRITE, 0x08 ) ; //年 v_DS1302WriteByte_f( DS1302_WEEK_WRITE, 0x04 ) ; //星期 v_DS1302WriteByte_f( DS1302_MONTH_WRITE, 0x12 ) ; //月 v_DS1302WriteByte_f( DS1302_DAY_WRITE, 0x11 ) ; //日 v_DS1302WriteByte_f( DS1302_HOUR_WRITE, 0x13 ) ; //小時 v_DS1302WriteByte_f( DS1302_MINUTE_WRITE, 0x06 ) ; //分鐘 v_DS1302WriteByte_f( DS1302_SECOND_WRITE, 0x40 ) ; //秒 v_DS1302WriteByte_f( 0x90, 0xa5 ) ; //充電 v_DS1302WriteByte_f( 0xc0, 0xf0 ) ; //判斷是否初始化一次標識寫入 v_DS1302WriteByte_f( 0x8e, 0x80 ) ; //禁止寫操作 } } /****************************************************************************** * Function: void v_ClockUpdata_f( void ) * * Description:讀取時間數(shù)據(jù),并保存在結構體CurrentTime中 * * Parameter: * * * * Return: * ******************************************************************************/ void v_ClockUpdata_f( void ) { CurrentTime.Second = v_DS1302ReadByte_f( DS1302_SECOND_READ ) ; CurrentTime.Minute = v_DS1302ReadByte_f( DS1302_MINUTE_READ ) ; CurrentTime.Hour = v_DS1302ReadByte_f( DS1302_HOUR_READ ) ; CurrentTime.Day = v_DS1302ReadByte_f( DS1302_DAY_READ ) ; CurrentTime.Month = v_DS1302ReadByte_f( DS1302_MONTH_READ ) ; CurrentTime.Week = v_DS1302ReadByte_f( DS1302_WEEK_READ ) ; CurrentTime.Year = v_DS1302ReadByte_f( DS1302_YEAR_READ ) ; } 有了上面的這些函數(shù)我們就可以對DS1302進行操作了。當我們想要獲取當前時間時,只需要調用v_ClockUpdata_f( void )這個函數(shù)即可。讀取到的時間數(shù)據(jù)保存在CurrentTime這個結構體中。至于如何把時間數(shù)據(jù)在數(shù)碼管或者是液晶屏上顯示出來我相信大家應該都會了吧^_^. 看看顯示效果如何~~ ![]() 下面再讓我們看看DS18B20吧。 DS18B20是單總線的數(shù)字溫度傳感器。其與單片機的接口只需要一根數(shù)據(jù)線即可。當然連線簡單意味著軟件處理上可能要麻煩一點。下面來看看它的優(yōu)點: ![]() 看看它的靚照。外形和我們常用的三極管沒有什么兩樣哦。 ![]() DS18B20的內部存儲器分為以下幾部分 ROM:存放該器件的編碼。前8位為單線系列的編碼(DS18B20的編碼是19H)后面48位為芯片的唯一序列號。在出場的時候就已經設置好,用戶無法更改。最后8位是以上56位的CRC碼。 RAM:DS18B20的內部暫存器共9個字節(jié)。其中第一個和第二個字節(jié)存放轉換后的溫度值。第二個和第三個字節(jié)分別存放高溫和低溫告警值。(可以用RAM指令將其拷貝到EEPROM中)第四個字節(jié)為配置寄存器。第5~7個字節(jié)保留。第9個字節(jié)為前8個字節(jié)的CRC碼。 ![]() DS18B20的溫度存放如上圖所示。其中S位符號位。當溫度值為負值時,S = 1 ,反之則S = 0 。我們把得到的溫度數(shù)據(jù)乘上對應的分辨率即可以得到轉換后的溫度值。 DS18B20的通訊協(xié)議: 在對DS18B20進行讀寫編程時,必須嚴格保證讀寫的時序。否則將無法讀取測溫結果。根據(jù)DS18B20的通訊協(xié)議,主機控制DS18B20完成溫度轉換必須經過3個步驟:每一次讀寫之前都要對DS18B20進行復位,復位成功后發(fā)送一條ROM指令,最后發(fā)送RAM指令。這樣才能對DS18B20進行預定的操作。 復位要求主機將數(shù)據(jù)線下拉500us,然后釋放,DS18B20收到信號后等待16~160us然后發(fā)出60~240us的存在低脈沖,主機收到此信號表示復位成功。 ![]() 上圖即DS18B20的復位時序圖。 下面是讀操作的時序圖 ![]() 這是寫操作的時序圖 ![]() 下面讓我們來看看它的驅動程序如何寫吧。 sbit io_DS18B20_DQ = P2^3 ; #define DS18B20_DQ_HIGH io_DS18B20_DQ = 1 ; #define DS18B20_DQ_LOW io_DS18B20_DQ = 0 ; #define DS18B20_DQ_READ io_DS18B20_DQ /******************************************************************* * 保存溫度值的數(shù)組.依次存放正負標志,溫度值十位,個位,和小數(shù)位 * *******************************************************************/ uint8 Temperature[ 4 ] ; void v_Delay10Us_f( uint16 Count ) { while( --Count ) { _nop_(); } } /************************************************************************** * Function: uint8 v_Ds18b20Init_f( void ) * * Description: 初始化DS18B20 * * Parameter: * * * * Return: 返回初始化的結果(0:復位成功 1:復位失敗) * **************************************************************************/ uint8 v_Ds18b20Init_f( void ) { uint8 Flag ; DS18B20_DQ_HIGH //稍作延時 v_Delay10Us_f( 3 ) ; DS18B20_DQ_LOW //總線拉低 v_Delay10Us_f( 80 ) ; //延時大于480us DS18B20_DQ_HIGH //總線釋放 v_Delay10Us_f( 15 ) ; Flag = DS18B20_DQ_READ ; //如果Flag為0,則復位成功,否則復位失敗 return Flag ; } /****************************************************************************** * Function: void v_Ds18b20Write_f( uint8 Cmd ) * * Description: 向DS18B20寫命令 * * Parameter: Cmd: 所要寫的命令 * * * * Return: * ******************************************************************************/ void v_Ds18b20Write_f( uint8 Cmd ) { uint8 i ; for( i = 8 ; i > 0 ; i-- ) { DS18B20_DQ_LOW //拉低總線,開始寫時序 DS18B20_DQ_READ = Cmd & 0x01 ; //控制字的最低位先送到總線 v_Delay10Us_f( 5 ) ; //稍作延時,讓DS18B20讀取總線上的數(shù)據(jù) DS18B20_DQ_HIGH //拉高總線,1bit寫周期結束 Cmd >>= 1 ; } } /****************************************************************************** * Function: uint8 v_Ds18b20Read_f( void ) * * Description: 向DS18B20讀取一個字節(jié)的內容 * * Parameter: * * * * Return: 讀取到的數(shù)據(jù) * ******************************************************************************/ uint8 v_Ds18b20Read_f( void ) { uint8 ReadValue, i ; for( i = 8 ; i > 0 ; i-- ) { DS18B20_DQ_LOW ReadValue >>= 1 ; DS18B20_DQ_HIGH if( DS18B20_DQ_READ == 1 ) ReadValue |= 0x80 ; v_Delay10Us_f( 3 ) ; } return ReadValue ; } /****************************************************************************** * Function: uint16 v_Ds18b20ReadTemp_f( void ) * * Description: 讀取當前的溫度數(shù)據(jù)(只保留了一位小數(shù)) * * Parameter: * * * * Return: 讀取到的溫度值 * ******************************************************************************/ uint16 v_Ds18b20ReadTemp_f( void ) { uint8 TempH, TempL ; uint16 ReturnTemp ; /* if( v_Ds18b20Init_() ) return ; //復位失敗,在這里添加錯誤處理的代碼 */ v_Ds18b20Init_f() ; /復位DS18B20 v_Ds18b20Write_f( 0xcc ) ; //跳過ROM v_Ds18b20Write_f( 0x44 ) ; //啟動溫度轉換 v_Ds18b20Init_f() ; v_Ds18b20Write_f( 0xcc ) ; //跳過ROM v_Ds18b20Write_f( 0xbe ) ; //讀取DS18B20內部的寄存器內容 TempL = v_Ds18b20Read_f() ; //讀溫度值低位(內部RAM的第0個字節(jié)) TempH = v_Ds18b20Read_f() ; //讀溫度值高位(內部RAM的第1個字節(jié)) ReturnTemp = TempH ; ReturnTemp <<= 8 ; ReturnTemp |= TempL ; //溫度值放在變量ReturnTemp中 return ReturnTemp ; } /****************************************************************************** * Function: void v_TemperatureUpdate_f( void ) * * Description:讀取當前的溫度數(shù)據(jù)并轉化存放在數(shù)組Temperature(只保留了一位小數(shù)) * * Parameter: * * * * Return: * ******************************************************************************/ void v_TemperatureUpdate_f( void ) { uint8 Tflag = 0 ; uint16 TempDat ; float Temp ; TempDat = v_Ds18b20ReadTemp_f() ; if( TempDat & 0xf000 ) { Tflag = 1 ; TempDat = ~TempDat + 1 ; } Temp = TempDat * 0.0625 ; TempDat = Temp * 10 ; Temperature[ 0 ] = Tflag ; //溫度正負標志 Temperature[ 1 ] = TempDat / 100 + '0' ; //溫度十位值 Temperature[ 2 ] = TempDat % 100 / 10 + '0' ; //溫度個位值 Temperature[ 3 ] = TempDat % 10 + '0' ;//溫度小數(shù)位 } 如果想獲取當前的溫度數(shù)據(jù),在主函數(shù)中調用v_TemperatureUpdate_f( void )就可以了。溫度數(shù)據(jù)就保存到Temperature中去了。至于如何顯示,就不用多說了吧~@_@~ ![]() 時間和溫度一起顯示出來看看 ![]() OK,至此ds18b20和ds1302的應用告一段落。如果有不懂的,記得多看datasheet,多交流。 |
|