树莓派基础实验32:DS1302实时时钟模块实验
一、介紹
??現在有很多流行的串行時鐘芯片,如DS1302,DS1307,PCF8485等,由于簡單的接口,低成本和易用性,他們被廣泛應用于電話、傳真、便攜式儀器等產品領域。在本實驗中,我們將使用DS1302實時時鐘(RTC)模塊獲取當前日期和時間。
??DS1302可以用于數據記錄,特別是對某些具有特殊意義的數據點的記錄,能實現數據與出現該數據的時間同時記錄。這種記錄對長時間的連續測控系統結果的分析,及對異常數據出現的原因的查找具有重要意義。
??傳統的數據記錄方式是隔時采樣或定時采樣,沒有具體的時間記錄,因此,只能記錄數據而無法準確記錄其出現的時間;若采用單片機計時,一方面需要采用計數器,占用硬件資源,另一方面需要設置中斷、查詢等,同樣耗費單片機的資源,而且,某些測控系統可能不允許。但是,如果在系統中采用時鐘芯片DS1302,則能很好地解決這個問題。
二、組件
★Raspberry Pi 3主板*1
★樹莓派電源*1
★40P軟排線*1
★DS1302實時時鐘模塊*1
★面包板*1
★跳線若干
三、實驗原理
1. DS1302的特點
??DS1302是DALLAS(達拉斯)公司出的一款涓流充電時鐘芯片,2001年DALLAS被MAXIM(美信)收購。
??DS1302實時時鐘芯片廣泛應用于電話、傳真、便攜式儀器等產品領域,他的主要性能指標如下:
??1、DS1302是一個實時時鐘芯片,可以提供秒、分、小時、日期、月、年等信息,并且還有軟年自動調整的能力,可以通過配置AM/PM來決定采用24小時格式還是12小時格式。
??2、擁有31字節數據存儲RAM。
??3、串行I/O通信方式,相對并行來說比較節省IO口的使用。
??4、DS1302的工作電壓比較寬,大概是2.0V~5.5V都可以正常工作。
??5、DS1302這種時鐘芯片功耗一般都很低,它在工作電壓2.0V的時候,工作電流小于300nA。
??6、DS1302共有8個引腳,有兩種封裝形式,一種是DIP-8封裝,芯片寬度(不含引腳)是300mil,一種是SOP-8封裝,有兩 種寬度,一種是150mil,一種是208mil。我們看一下DS1302的引腳封裝圖:
??7、當供電電壓是5V的時候,兼容標準的TTL電平標準,這里的意思是,可以完美的和單片機進行通信。
?? 8、由于DS1302是DS1202的升級版本,所以所有的功能都兼容DS1202。此外DS1302有兩個電源輸入,一個是主電源, 另外一個是備用電源,比如可以用電池或者大電容,這樣是為了保證系統掉電的情況下,我們的時鐘還會繼續走。如果使用的是充電電池,還可以在正常工作時,設置充電功能,給我們的備用電池進行充電。
??DS1302的特點第二條“擁有31字節數據存儲RAM”,這是DS1302額外存在的資源。這31字節的RAM相當于一個存儲器一樣,我們編寫單片機程序的時候,可以把我們想存儲的數據存儲在DS1302里邊,需要的時候讀出來,這塊功能和EEPROM有點類似,相當于一個掉電丟失數據的“EEPROM”,如果我們的時鐘電路加上備用電池,那么這31個字節的RAM就可以替代EEPROM的功能了。
??DS1302一共有8個引腳,下邊要根據引腳分布圖和典型電路圖來介紹一下每個引腳的功能:
??DS1302的電路一個重點就是時鐘電路,它所使用的晶振是一個32.768k的晶振,晶振外部也不需要額外添加其他的電容或者電阻電路了。時鐘的精度,首先取決于晶振的精度以及晶振的引腳負載電容。如果晶振不準或者負載電容過大過小,都會導致時鐘誤差過大。在這一切都搞定后,最終一個考慮因素是晶振的溫漂。隨著溫度的變化,晶振往往精度會發生變化,因此,在實際的系統中,其中一種方法就是經常校對。比如我們所用的電腦的時鐘,通常我們會設置一個選項“將計算機設置于internet時間同步”。選中這個選項后,一般可以過一段時間,我們的計算機就會和internet時間校準同步一次。
2. DS1302寄存器
??對DS1302的操作就是對其內部寄存器的操作,DS1302內部共有12個寄存器,其中有7個寄存器與日歷、時鐘相關,存放的數據位為BCD碼形式。此外,DS1302還有年份寄存器、控制寄存器、充電寄存器、時鐘突發寄存器及與RAM相關的寄存器等。時鐘突發寄存器可一次性順序讀/寫除充電寄存器以外的寄存器。
??DS1302的一條指令一個字節8位,其中第7位(即最高位)是固定1,這一位如果是0的話,那寫進去是無效的。第6位是選擇RAM還是CLOCK的,這里主要講CLOCK時鐘的使用,它的RAM功能我們不用,所以如果選擇CLOCK功能,第6位是0,如果要用RAM,那第6位就是1。從第5到第1位,決定了寄存器的5位地址,而第0位是讀寫位,如果要寫,這一位就是0,如果要讀,這一位就是1。
??DS1302時鐘的寄存器,其中8個和時鐘有關的,5位地址分別是00000一直到00111這8個地址,還有一個寄存器的地址是01000,這是涓流充電所用的寄存器,我們這里不講。在DS1302的數據手冊里的地址,直接把第7位、第6位和第0位值給出來了,所以指令就成了80H、81H那些了,最低位是1,那么表示讀,最低位是0表示寫。
??寄存器一:最高位CH是一個時鐘停止標志位。如果我們的時鐘電路有備用電源部分,上電后,我們要先檢測一下這一位,如果這一位是0,那說明我們的時鐘在系統掉電后,由于備用電源的供給,時鐘是持續正常運行的;如果這一位是1,那么說明我們的時鐘在系統掉電后,時鐘部分不工作了。若我們的Vcc1懸空或者是電池沒電了,當我們下次重新上電時,讀取這一位,那這一位就是1,我們可以通過這一位判斷時鐘在單片機系統掉電后是否持續運行。剩下的7位高3位是秒的十位,低4位是秒的個位,這里注意再提一次,DS1302內部是BCD碼,而秒的十位最大是5,所以3個二進制位就夠了。
??寄存器二:bit7沒意義,剩下的7位高3位是分鐘的十位,低4位是分鐘的個位。
??寄存器三:bit7是1的話代表是12小時制,是0的話代表是24小時制,bit6固定是0,bit5在12小時制下0代表的是上午,1代表的是下午,在24小時制下和bit4一起代表了小時的十位,低4位代表的是小時的個位。
??寄存器四:高2位固定是0,bit5和bit4是日期的十位,低4位是日期的個位。
??寄存器五:高3位固定是0,bit4是月的十位,低4位是月的個位。
??寄存器六:高5位固定是0,低3位代表了星期。
??寄存器七:高4位代表了年的十位,低4位代表了年的個位。這里特別注意,這里的00到99年指的是2000年到2099年。
??寄存器八:bit7是一個保護位,如果這一位是1,那么是禁止給任何其他的寄存器或者那31個字節的RAM寫數據的。因此在寫數據之前,這一位必須先寫成0。
3. DS1302的時序
?? 物理上,DS1302的通信接口由3個口線組成,即RST,SCLK,I/O。其中RST從低電平變成高電平啟動一次數據傳輸過程,SCLK是時鐘線,I/O是數據線。這個DS1302的通信線定義和SPI很像,事實上,DS1302的通信是SPI的變異種類,它用了SPI的通信時序,但是通信的時候沒有完全按照SPI的規則來,下面我們介紹DS1302的變異SPI通信方式。
??請注意數據是對時鐘信號敏感的,而且一般數據是在下降沿寫入,上升沿讀出。平時SCLK保持低電平,當需要寫命令或者寫數據時,在時鐘輸出變為高電平之前先輸出數據;當需要讀數據時,在時鐘輸出變為高電平之前采樣讀取數據。
四、實驗步驟
??第1步: 連接電路。
| GPIO4 | G23 | SCL(CLK) |
| GPIO5 | G24 | SDA(DAT) |
| GPIO6 | G25 | RST |
| 5V | 5V | VCC |
| GND | GND | GND |
??第2步: DS1302的Python程序比較復雜,我們先編寫一個模塊ds1302.py,在里面創建一個類DS1302(),在里面編寫讀取時鐘信息等方法。
''' RTC_DS1302 ''' ''' ------------------------------------------------------------------------ ''' ''' 控制處理實時時鐘DS1302的類。 '''import time import RPi.GPIO from datetime import datetimeclass DS1302:CLK_PERIOD = 0.00001DOW = [ "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" ]def __init__(self, scl=23, rst=25, io=24):self.scl = sclself.rst = rstself.io = io# 關閉GPIO警告。RPi.GPIO.setwarnings(False)# 配置樹莓派GPIO接口。RPi.GPIO.setmode(RPi.GPIO.BCM)# 初始化 DS1302 通信。self.init_ds1302()# 確保寫保護已關閉。self.write_byte(int("10001110", 2))self.write_byte(int("00000000", 2))# 確保涓流充電模式被關閉。self.write_byte(int("10010000", 2))self.write_byte(int("00000000", 2))# 結束 DS1302 通信。self.end_ds1302()self.datetime = {}def CloseGPIO(self):'''在結束前關閉 Raspberry Pi GPIO 。'''RPi.GPIO.cleanup()def init_ds1302(self):'''使用DS1302 RTC啟動一個事務。'''RPi.GPIO.setup(self.scl, RPi.GPIO.OUT, initial=0)RPi.GPIO.setup(self.rst, RPi.GPIO.OUT, initial=0)RPi.GPIO.setup(self.io, RPi.GPIO.OUT, initial=0)RPi.GPIO.output(self.scl, 0)RPi.GPIO.output(self.io, 0)time.sleep(self.CLK_PERIOD)RPi.GPIO.output(self.rst, 1)def end_ds1302(self):'''使用DS1302 RTC結束一個事務。'''RPi.GPIO.setup(self.scl, RPi.GPIO.OUT, initial=0)RPi.GPIO.setup(self.rst, RPi.GPIO.OUT, initial=0)RPi.GPIO.setup(self.io, RPi.GPIO.OUT, initial=0)RPi.GPIO.output(self.scl, 0)RPi.GPIO.output(self.io, 0)time.sleep(self.CLK_PERIOD)RPi.GPIO.output(self.rst, 0)def write_byte(self, Byte):'''將一個字節的數據寫入DS1302 RTC。'''for Count in range(8):time.sleep(self.CLK_PERIOD)RPi.GPIO.output(self.scl, 0)Bit = Byte % 2Byte = int(Byte / 2)time.sleep(self.CLK_PERIOD)RPi.GPIO.output(self.io, Bit)time.sleep(self.CLK_PERIOD)RPi.GPIO.output(self.scl, 1)def read_byte(self):'''將一個字節的數據讀入DS1302 RTC。'''RPi.GPIO.setup(self.io, RPi.GPIO.IN, pull_up_down=RPi.GPIO.PUD_DOWN)Byte = 0for Count in range(8):time.sleep(self.CLK_PERIOD)RPi.GPIO.output(self.scl, 1)time.sleep(self.CLK_PERIOD)RPi.GPIO.output(self.scl, 0)time.sleep(self.CLK_PERIOD)Bit = RPi.GPIO.input(self.io)Byte |= ((2 ** Count) * Bit)return Bytedef write_ram(self, Data):'''向RTC RAM寫一條消息。'''# Initiate DS1302 communication.self.init_ds1302()# Write address byte.self.write_byte(int("11111110", 2))# Write data bytes.for Count in range(len(Data)):self.write_byte(ord(Data[Count:Count + 1]))for Count in range(31 - len(Data)):self.write_byte(ord(" "))# End DS1302 communication.self.end_ds1302()def read_ram(self):'''向RTC RAM讀一條消息。'''# Initiate DS1302 communication.self.init_ds1302()# Write address byte.self.write_byte(int("11111111", 2))# Read data bytes.Data = ""for Count in range(31):Byte = self.read_byte()Data += chr(Byte)# End DS1302 communication.self.end_ds1302()return Datadef set_datetime(self, year, month, day, hour, minute, second, dayOfWeek=0):'''寫日期和時間給RTC,這里我放棄了星期幾的設置,傳遞的默認值。'''if not self.check_sanity():return False# Initiate DS1302 communication.self.init_ds1302()# Write address byte.self.write_byte(int("10111110", 2))# Write seconds data.self.write_byte((second % 10) | int(second / 10) * 16)# Write minute data.self.write_byte((minute % 10) | int(minute / 10) * 16)# Write hour data.self.write_byte((hour % 10) | int(hour / 10) * 16)# Write day data.self.write_byte((day % 10) | int(day / 10) * 16)# Write month data.self.write_byte((month % 10) | int(month / 10) * 16)# Write day of week data.self.write_byte((dayOfWeek % 10) | int(dayOfWeek / 10) * 16)# Write year data.self.write_byte((year % 100 % 10) | int(year % 100 / 10) * 16)# Make sure write protect is turned off.self.write_byte(int("00000000", 2))# Make sure trickle charge mode is turned off.self.write_byte(int("00000000", 2))# End DS1302 communication.self.end_ds1302()def get_datetime(self):'''從RTC中讀取日期和時間。'''# Initiate DS1302 communication.self.init_ds1302()# Write address byte.self.write_byte(int("10111111", 2))# Read date and time data.Data = ""Byte = self.read_byte()second = (Byte % 16) + int(Byte / 16) * 10Byte = self.read_byte()minute = (Byte % 16) + int(Byte / 16) * 10Byte = self.read_byte()hour = (Byte % 16) + int(Byte / 16) * 10Byte = self.read_byte()day = (Byte % 16) + int(Byte / 16) * 10Byte = self.read_byte()month = (Byte % 16) + int(Byte / 16) * 10Byte = self.read_byte()day_of_week = ((Byte % 16) + int(Byte / 16) * 10) - 1Byte = self.read_byte()year = (Byte % 16) + int(Byte / 16) * 10 + 2000# End DS1302 communication.self.end_ds1302()return datetime(year, month, day, hour, minute, second)def check_sanity(self):"檢查時鐘是否正常。如果時鐘正常則返回True,否則返回False"dt = self.get_datetime()if dt.year == 2000 or dt.month == 0 or dt.day == 0:return Falseif dt.second == 80:return Falsereturn Truedef format_time(dt):if dt is None:return ""fmt = "%m/%d/%Y %H:%M"return dt.strftime(fmt)def parse_time(s):fmt = "%m/%d/%Y %H:%M"return datetime.strptime(s, fmt)??第3步: 編寫實際控制程序,導入上面的模塊ds1302。運行本文件,不斷循環讀取并打印時鐘信息。
#!/usr/bin/env python from datetime import datetime import timeimport ds1302 #導入模塊ds1302rtc = ds1302.DS1302() #通過模塊ds1302中的類DS1302()創建一個實例rtcdef setup():''' 寫入初始時間 '''print ''print ''print rtc.get_datetime()print ''print ''a = raw_input( "Do you want to setup date and time?(y/n) ")if a == 'y' or a == 'Y':date = raw_input("Input date:(YYYY MM DD) ")time = raw_input("Input time:(HH MM SS) ")date = date.split()time = time.split()print ''print ''rtc.set_datetime(int(date[0]),int(date[1]), int(date[2]),\int(time[0]), int(time[1]), int(time[2]))dt = rtc.get_datetime()print "You set the date and time to:", dtdef loop():''' 顯示實時時間 '''while True:a = rtc.get_datetime()print atime.sleep(1)def destory():GPIO.cleanup() # Release resourceif __name__ == '__main__': # Program start from heresetup()try:loop()except KeyboardInterrupt: destory()??實驗結果示例:
總結
以上是生活随笔為你收集整理的树莓派基础实验32:DS1302实时时钟模块实验的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: codeforces 1550B. Ma
- 下一篇: iOS篇—Demo6—抽奖转盘