標(biāo)題: DS1302與STC12單片機(jī)的連接電路和驅(qū)動(dòng)實(shí)現(xiàn) [打印本頁]
作者: michaelchain 時(shí)間: 2021-8-27 11:07
標(biāo)題: DS1302與STC12單片機(jī)的連接電路和驅(qū)動(dòng)實(shí)現(xiàn)
DS1302是低功耗帶RAM的實(shí)時(shí)時(shí)鐘電路, 常見的SOP8封裝體積很小, 它可以對(duì)年月日周時(shí)分秒進(jìn)行計(jì)時(shí), 具有閏年補(bǔ)償功能, 工作電壓為2.0V-5.5V, 采用三線接口與CPU進(jìn)行同步通信, 并可采用突發(fā)方式一次傳送多個(gè)字節(jié)的時(shí)鐘信號(hào)或RAM數(shù)據(jù). DS1302內(nèi)部有一個(gè)31byte的用于臨時(shí)性存放數(shù)據(jù)的RAM寄存器. DS1302是DS1202的升級(jí)產(chǎn)品, 與DS1202兼容, 但增加了主電源/后備電源雙電源引腳, 同時(shí)提供了對(duì)后備電源進(jìn)行涓細(xì)電流充電的能力.
幾個(gè)需要知道的點(diǎn):
- 不帶溫度補(bǔ)償, 所以在溫度變化大的環(huán)境, 走時(shí)誤差會(huì)比較大, 需要有校對(duì)機(jī)制
- 三線接口機(jī)制與SPI相似, 但不是SPI, 因?yàn)樗谕桓上實(shí)現(xiàn)的雙向IO
- 自身不帶電源/電容, 掉電即重置, 需要自己管理好主電備電
- 備電的充電方式是可以通過寄存器控制的, 分三級(jí)控制, 第一級(jí)開關(guān), 第二級(jí)可選1級(jí)或2級(jí)二極管降壓, 第三級(jí)可選三種阻值
- 額外的31個(gè)byte的存儲(chǔ)可以自由讀寫
- burst read有兩處, 一處是8個(gè)字節(jié)的control+時(shí)間, 另一處是31字節(jié)的ram, 都是一次性全部讀取, 但是可以編程控制讀到第幾個(gè)字節(jié)就停止.
- 讀取的結(jié)果是BCD碼, 要轉(zhuǎn)換
Vcc1和Vcc2DS1302的引腳中Vcc2為主電源, Vcc1為后備電源. 在主電源關(guān)閉的情況下, 也能保持時(shí)鐘的連續(xù)運(yùn)行. DS1302由Vcc1或Vcc2兩者中的較大者供電, 當(dāng)Vcc2大于Vcc1+0.2V時(shí), Vcc2給DS1302供電, 當(dāng)Vcc2小于Vcc1時(shí), DS1302由Vcc1供電.
注意: 供電不要低于3.6V, 否則在秒進(jìn)位時(shí)容易出現(xiàn)時(shí)間清零. 如果是在VCC端串聯(lián)電阻限流, 電阻不要超過3K.
RST腳RST是復(fù)位/片選線, 通過把RST輸入驅(qū)動(dòng)置高電平來啟動(dòng)所有的數(shù)據(jù)傳送.
- 當(dāng)RST為高電平時(shí), 允許地址/命令序列送入移位寄存器, 所有的數(shù)據(jù)傳送被初始化, 允許對(duì)DS1302進(jìn)行操作
- 提供終止單字節(jié)或多字節(jié)數(shù)據(jù)傳送的方法, 如果在傳送過程中RST置為低電平,則會(huì)終止此次數(shù)據(jù)傳送,I/O引腳變?yōu)楦咦钁B(tài)
- 上電運(yùn)行時(shí), 在Vcc>2.0V之前RST必須保持低電平. 只有在SCLK為低電平時(shí), 才能將RST置為高電平.
寫入邏輯:- 將RST先拉低
- 將SCLK拉低(如果不拉低, 且本來是高電平, 則瞬間就發(fā)出了一個(gè)bit, 這時(shí)還沒放數(shù)據(jù))
- 將RST拉高, 形成一個(gè)上升沿
- 在IO口放入一個(gè)bit (字節(jié)發(fā)送順序:從低位到高位)
- 將SCLK拉高, 形成上升沿, 這時(shí)候DS1302就會(huì)將IO口的數(shù)據(jù)移入它的寄存器
- 拉低SCLK, 為下一個(gè)字節(jié)做準(zhǔn)備
- IO口再放一個(gè)bit, 再拉高, 直到發(fā)送完所有的16個(gè)字節(jié)
- 最后拉低RST, 完成寫入過程
寄存器地址與時(shí)間相關(guān)的寄存器從0x80到0x8F, 其中偶數(shù)為寫地址, 奇數(shù)為讀地址, 共控制8個(gè)字節(jié), 這8個(gè)字節(jié)可以通過burst方式一次性讀出
- #define DS1302_W_SECOND 0x80
- #define DS1302_W_MINUTE 0x82
- #define DS1302_W_HOUR 0x84
- #define DS1302_W_DAY 0x86
- #define DS1302_W_MONTH 0x88
- #define DS1302_W_WEEK 0x8A
- #define DS1302_W_YEAR 0x8C
- #define DS1302_W_PROTECT 0x8E
- #define DS1302_R_SECOND 0x81
- #define DS1302_R_MINUTE 0x83
- #define DS1302_R_HOUR 0x85
- #define DS1302_R_DAY 0x87
- #define DS1302_R_MONTH 0x89
- #define DS1302_R_WEEK 0x8B
- #define DS1302_R_YEAR 0x8D
- #define DS1302_R_PROTECT 0x8F
復(fù)制代碼
與控制相關(guān)的寄存器為如下幾個(gè), 也是偶數(shù)寫奇數(shù)讀, 這些只能單個(gè)讀寫
- #define DS1302_W_TK_CHARGER 0x90
- #define DS1302_W_CLK_BURST 0xBE
- #define DS1302_W_RAM_BURST 0xFE
- #define DS1302_R_TK_CHARGER 0x91
- #define DS1302_R_CLK_BURST 0xBF
- #define DS1302_R_RAM_BURST 0xFF
復(fù)制代碼片內(nèi)的31個(gè)字節(jié)RAM起始地址為0xC0, 這31個(gè)字節(jié)可以通過burst方式一次性讀出
- #define DS1302_RAM_SIZE 0x31 // Ram Size in bytes
- #define DS1302_RAM_START 0xC0 // First byte Address
復(fù)制代碼
使用STC12 MCU讀取DS1302接線圖, 測試中可以只接Vcc2或Vcc1, 可以串電阻或串二極管限流,
DS1302-sch3.png (171.57 KB, 下載次數(shù): 54)
下載附件
2021-8-27 11:05 上傳
實(shí)際的測試板, 接DS1302使用的是SOP8的測試座, 避免焊接, 中間的萬能板僅僅是為了用一個(gè)IC座外接晶振, 可以忽略其他元件
DS1302-test2.jpg (125.82 KB, 下載次數(shù): 67)
下載附件
2021-8-27 11:05 上傳
測試座里的DS1302
DS1302-test1.jpg (94.84 KB, 下載次數(shù): 49)
下載附件
2021-8-27 11:06 上傳
代碼例子, 包含單個(gè)讀, 單個(gè)寫, burst讀時(shí)間, burst讀RAM, 基于 HML_FwLib_STC12 封裝庫. 因?yàn)槭欠菢?biāo)準(zhǔn)SPI, 所以實(shí)際上只用到了串口打印, 直接把這個(gè)方法拎出來也行, 就不需要引入這個(gè)封裝庫了. - /*****************************************************************************/
- /**
- * 連接方式
- * | | |
- * | --------- | ------------ |
- * |1:Vcc2 VCC | 8:Vcc1 |
- * |2:X1 OCS | 7:SCLK P1_0 |
- * |3:X2 OCS | 6:I/O P1_1 |
- * |4:GND GND | 5:RST/CE P1_2 |
- *
- *****************************************************************************/
- #include "hml/hml.h"
- #include <stdio.h>
- #define DS1302_W_SECOND 0x80
- #define DS1302_W_MINUTE 0x82
- #define DS1302_W_HOUR 0x84
- #define DS1302_W_DAY 0x86
- #define DS1302_W_MONTH 0x88
- #define DS1302_W_WEEK 0x8A
- #define DS1302_W_YEAR 0x8C
- #define DS1302_W_PROTECT 0x8E
- #define DS1302_W_TK_CHARGER 0x90
- #define DS1302_W_CLK_BURST 0xBE
- #define DS1302_W_RAM_BURST 0xFE
- #define DS1302_R_SECOND 0x81
- #define DS1302_R_MINUTE 0x83
- #define DS1302_R_HOUR 0x85
- #define DS1302_R_DAY 0x87
- #define DS1302_R_MONTH 0x89
- #define DS1302_R_WEEK 0x8B
- #define DS1302_R_YEAR 0x8D
- #define DS1302_R_PROTECT 0x8F
- #define DS1302_R_TK_CHARGER 0x91
- #define DS1302_R_CLK_BURST 0xBF
- #define DS1302_R_RAM_BURST 0xFF
- #define DS1302_RAM_SIZE 0x31 // Ram Size in bytes
- #define DS1302_RAM_START 0xC0 // First byte Address
- #define HEX2BCD(v) ((v) % 10 + (v) / 10 * 16)
- #define BCD2HEX(v) ((v) % 16 + (v) / 16 * 10)
- #define DS1302_SCK P1_0
- #define DS1302_IO P1_1
- #define DS1302_RST P1_2
- const uint8_t READ_RTC_ADDR[7] = {0x81, 0x83, 0x85, 0x87, 0x89, 0x8b, 0x8d};
- const uint8_t WRITE_RTC_ADDR[7] = {0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c};
- uint8_t BUF[8] = { 0 };
- // Write one byte to DS1302
- void DS1302_WriteByte(uint8_t writeByte)
- {
- for(uint8_t i=0; i < 8; i++)
- {
- if(1 == (writeByte & 0x01)){
- DS1302_IO = 1;
- }else{
- DS1302_IO = 0;
- }
- DS1302_SCK = 1;
- DS1302_SCK = 0;
- writeByte >>= 1; // From low to high
- }
- }
-
- // Read one byte from DS1302
- uint8_t DS1302_ReadByte(void)
- {
- uint8_t dat, readByte = 0;
- for(uint8_t i = 0; i < 8; i++)
- {
- //dat = DS1302_IO;
- //readByte = (readByte>>1) | (dat << 7); // From low to high
- readByte >>= 1;
- if(DS1302_IO)
- {
- readByte |= 0x80;
- }
- DS1302_SCK = 1;
- DS1302_SCK = 0;
- }
- return readByte;
- }
- void DS1302_WriteReg(uint8_t addr, uint8_t value)
- {
- DS1302_RST = 0;
- DS1302_SCK = 0;
- DS1302_RST = 1;
- DS1302_WriteByte(addr);
- DS1302_WriteByte(value);
- DS1302_SCK = 1;
- DS1302_RST = 0;
- }
-
- uint8_t DS1302_ReadReg(uint8_t addr)
- {
- uint8_t readByte = 0;
- DS1302_RST = 0;
- DS1302_SCK = 0;
- DS1302_RST = 1;
- DS1302_WriteByte(addr);
- readByte = DS1302_ReadByte();
- DS1302_SCK = 1;
- DS1302_RST = 0;
- return readByte;
- }
- void DS1302_ReadBurst(uint8_t cmd, uint8_t len, uint8_t *buf)
- {
- uint8_t readByte = 0;
- DS1302_RST = 0;
- DS1302_SCK = 0;
- DS1302_RST = 1;
- DS1302_WriteByte(cmd);
- while(len--)
- *buf++ = DS1302_ReadByte();
- DS1302_SCK = 1;
- DS1302_RST = 0;
- }
- void DS1302_Init(void)
- {
- DS1302_WriteReg(DS1302_W_PROTECT, 0x00); // write unprotect
- DS1302_WriteReg(DS1302_W_TK_CHARGER, 0x01); // stop charger
- DS1302_WriteReg(0XC0, 0x00);
- DS1302_WriteReg(0XC2, 0x01);
- DS1302_WriteReg(0XC4, 0x02);
- DS1302_WriteReg(0XC6, 0x03);
- DS1302_WriteReg(DS1302_W_PROTECT, 0x80); // write protect
- }
- void DS1302_print(uint8_t dat)
- {
- printf_tiny("%x%x ", dat >> 4, dat & 0x0F);
- }
- void DS1302_printBuf(uint8_t len)
- {
- for(uint8_t i = 0; i < len; i++)
- {
- printf_tiny("%x%x ", BUF[i] >> 4, BUF[i] & 0x0F);
- }
- printf_tiny("\n");
- }
- void main(void)
- {
- UTIL_enablePrintf();
- DS1302_Init();
- while(1)
- {
- uint8_t dat = DS1302_ReadReg(DS1302_R_SECOND);
- DS1302_print(dat);
- dat = DS1302_ReadReg(DS1302_R_MINUTE);
- DS1302_print(dat);
- dat = DS1302_ReadReg(DS1302_R_HOUR);
- DS1302_print(dat);
- printf_tiny("\n");
- DS1302_ReadBurst(DS1302_R_CLK_BURST, 8, BUF);
- DS1302_printBuf(8);
- DS1302_ReadBurst(DS1302_R_RAM_BURST, 8, BUF);
- DS1302_printBuf(8);
- sleep(491);
- }
- }
復(fù)制代碼
我的8051筆記都在這個(gè)標(biāo)簽下: https://www.cnblogs.com/milton/tag/8051/
作者: 188610329 時(shí)間: 2021-8-28 16:39
糾正一下, DS1302, 是SPI, 是早期的半雙工SPI, 也叫雙線制SPI(不算CE,現(xiàn)在普遍的都是三線全雙工SPI),如果你手頭有CH551這類低端的單片機(jī),你會(huì)發(fā)現(xiàn)SPI設(shè)置成雙線半雙工模式后,可以直接用SPI驅(qū)動(dòng)DS1302。效率也高,代碼也簡單。
作者: michaelchain 時(shí)間: 2021-8-29 21:16
多謝指導(dǎo), 長知識(shí)了.
歡迎光臨 (http://www.raoushi.com/bbs/) |
Powered by Discuz! X3.1 |