一、DS1302簡介
1. 功能特性
DS1302是一款由Maxim Integrated公司生産的實時時鐘(RTC)晶片。
RTC晶片用于計算機和其他電子裝置中的時間和日期的內建電路,通常被用于電子時鐘、計時器、溫度記錄器等應用中。
DS1302部分功能特性:
- 實時時鐘功能:提供年、月、日、時、分、秒等時間資料。
- 串行接口:通過串行接口(如SPI)與微控制器進行通信。
- 低功耗設計:具有低功耗特性,在電池供電情況下能夠長時間穩定運作。
- 内置晶體振蕩器:內建了晶體振蕩器,不需要外部晶振。
- 電池備份:支援電池備份以保持時鐘運作,即使主電源斷電時也能保持時間資料。
- 溫度補償:具有溫度補償功能,可以提高時鐘的準确性。
DS1302通常與微控制器或單片機配合使用,通過串行接口進行通信,并通過讀寫寄存器來配置和讀取時間資料。
DS1302工作電壓2.0~5.5V。
2. 涓流充電
涓流充電是一種低功率充電方式,它通過限制充電電流的大小來防止電池過度充電和損壞。
DS1302内部內建了涓流充電電路,它會在主電源供電時通過合适的電阻限制充電電流,以适當的速率充電晶片内部的備用電池。一旦電池被充滿,充電電路會自動停止充電,以防止過度充電。
當主電源斷電時,DS1302會自動從備用電池供電,保持時鐘和日期功能正常運作。備用電池通常是一個較小的锂電池,它能夠提供足夠的電力維持DS1302的基本功能長達數年。
3. 接口介紹
DS1302實時時鐘晶片具有串行接口,通常使用3線或4線SPI(Serial Peripheral Interface)進行通信。以下是DS1302的主要接口:
時鐘資料和控制線:
- CE(Chip Enable):晶片使能線,用于啟用DS1302的通信。
- IO(Data I/O):資料輸入/輸出線,用于與微控制器進行資料交換。
- SCLK(Serial Clock):串行時鐘線,用于同步資料傳輸。
電源線:
- VCC:晶片供電正極。
- GND:晶片地線。
備用電池連接配接:
- VBAT:備用電池正極,用于備份時鐘資料。
- GND(BAT):備用電池地線。
二、寄存器介紹
1. 控制寄存器
- BIT7 MSB,1時定稿,0時禁止對DS1302寫入
- BIT6 0:時鐘/月曆;1RAM資料
- BIT5~BIT1:輸入輸出的寄存器
- BIT0 LSB,0時寫操作,1時讀操作。
2. 時間寄存器
寄存器 | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
1 | RAM/CK | A4 | A3 | A2 | A1 | A0 | R/W | |
秒 | 1 | 0/1 | ||||||
分 | 1 | 1 | 0/1 | |||||
小時 | 1 | 1 | 0/1 | |||||
日 | 1 | 1 | 1 | 0/1 | ||||
月 | 1 | 1 | 0/1 | |||||
星期 | 1 | 1 | 1 | 0/1 | ||||
年 | 1 | 1 | 1 | 0/1 |
3. 月曆/時鐘寄存器
BCD碼格式存入。
寄存器名稱 | 取值範圍 | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
秒寄存器 | 00-59 | CH | 秒的十位 | 秒的個位 | |||||
分寄存器 | 00-59 | 分的十位 | 分的個位 | ||||||
小時寄存器 | 01-12或00-23 | 12/24 | a/p | HR | 小時的個位 | ||||
日寄存器 | 01-31 | 日的十位 | 日的個位 | ||||||
月寄存器 | 01-12 | 1/0 | 月的個位 | ||||||
星期寄存器 | 01-07 | 星期幾 | |||||||
年寄存器 | 01-99 | 年的十位 | 年的個位 | ||||||
寫保護寄存器 | WP=1保護 | ||||||||
慢充電寄存器 | TCS | TCS | TCS | TCS | DS | DS | RS | RS | |
時鐘突發寄存器 |
三、BCD碼介紹
BCD(Binary-Coded Decimal)碼是一種數字編碼方式,用于表示十進制數字的二進制形式。在BCD編碼中,每個十進制數字(0到9)都用4位二進制數表示。這種編碼方式可以使計算機更友善地處理十進制數字。
BCD碼的基本原理是将一個十進制數的每一位分别用二進制數表示,例如将十進制數 45 分别用BCD編碼表示:
- 數字 4 的BCD編碼為 0100
- 數字 5 的BCD編碼為 0101
是以,數字 45 的BCD編碼為 0100 0101。
BCD碼有幾種常見的表示方法:
- 8421碼:每個十進制數字用4位二進制數表示,範圍從 0000 到 1001。這種編碼方式的名稱來自每個位上的權重,分别是8、4、2和1。例如,十進制數 7 的BCD編碼為 0111。
- 2421碼:類似于8421碼,但在前四個數字中,9表示為 1001,而不是 1000。這種編碼方式的目的是為了簡化BCD加法的實作。
- Excess-3碼:在8421碼的基礎上,每個數字都加上了3。例如,數字 0 的8421碼是 0000,加上3後變為 0011。
DS1302實時時鐘晶片使用的是8421碼(也稱為8421BCD碼)
四、DS1302時序
1. 讀時序
單位元組讀:
- CE:高電平使能
- SCLK:時鐘
- IO:資料讀
- I/O設定為輸入
- 在時鐘上升沿寫入,從低向高寫入;
- 第一個位元組是寫入指令
- I/O設定為輸出
- 後一個位元組是讀操作
- 寫入完成後,在下一個時鐘下降沿讀;
2. 寫時序
單位元組寫:
五、實作
1. 硬體原理圖
下面代碼在數位管顯示時間。
2. ds1302.h
//
// 時鐘晶片
//
#ifndef LESSON11_DS1302_H
#define LESSON11_DS1302_H
#include <reg52.h>
#include "types.h"
sbit DS1302_CE = P3^5;
// 時鐘口
sbit DS1302_CLK = P3^6;
// IO 口
sbit DS1302_IO = P3^4;
/**
* 寫入一個位元組
*/
void ds1302_write_byte(u8 addr, u8 dat);
/**
* 讀取一個位元組
*/
u8 ds1302_read_byte(u8 addr);
//變量聲明
extern u8 gDS1302_TIME[7];//存儲時間
void ds1302_init(void);
void ds1302_read_time(void);
#endif //LESSON11_DS1302_H
3. ds1302.c
//
// 時鐘晶片
//
#include "ds1302.h"
#include "intrins.h"
// DS1302寫入和讀取的位址指令
u8 gREAD_RTC_ADDR[7] = {
0x81, // 二進制 1000 0001 , 表示: 1000 0000 讀取秒
0x83, // 1000 0011 讀取分
0x85, // 1000 0101 讀取時
0x87, // 1000 0111 讀取日
0x89, // 1000 1001 讀取月
0x8b, // 1000 1011 讀取星期
0x8d // 1000 1101 讀取年
};
u8 gWRITE_RTC_ADDR[7] = {0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c};
// DS1302 初始化要寫入的時間
u8 gDS1302_TIME[7] = {
0x47, // 秒
0x56, // 分
0x15, // 時
0x20, // 日
0x03, // 月
0x05, // 星期
0x24 // 年
};
/**
* 寫入一個位元組
*/
void ds1302_write_byte(u8 addr, u8 dat){
u8 i;
// 使能腳複位
DS1302_CE = 0;
_nop_();
// 時鐘腳複位
DS1302_CLK = 0;
_nop_();
// 使能腳置高
DS1302_CE = 1;
_nop_();
// 從低位開始寫入
for(i=0; i<8; i++){
// 先發送位址
DS1302_IO = addr & 0x01;
// 右移一位
addr >>= 1;
// SLK 上升沿寫入
DS1302_CLK = 1;
_nop_();
DS1302_CLK = 0;
_nop_();
}
// 寫入資料
for(i=0; i<8; i++){
// 先發送資料
DS1302_IO = dat & 0x01;
// 右移一位
dat >>= 1;
// SLK 上升沿寫入
DS1302_CLK = 1;
_nop_();
DS1302_CLK = 0;
_nop_();
}
// 複位
DS1302_CE = 0;
_nop_();
}
/**
* 讀取一個位元組
*/
u8 ds1302_read_byte(u8 addr){
u8 i;
u8 temp = 0;
u8 value = 0;
// 使能腳複位
DS1302_CE = 0;
_nop_();
// 時鐘腳複位
DS1302_CLK = 0;
_nop_();
// 使能腳置高
DS1302_CE = 1;
_nop_();
// 從低位開始寫入
for(i=0; i<8; i++){
// 先發送位址
DS1302_IO = addr & 0x01;
// 右移一位
addr >>= 1;
// SLK 上升沿寫入
DS1302_CLK = 1;
_nop_();
DS1302_CLK = 0;
_nop_();
}
// 讀取資料
for(i=0; i<8; i++){
temp = DS1302_IO;
value = (temp << 7) | (value >> 1);
// SLK 下降沿讀取
DS1302_CLK = 1;
_nop_();
DS1302_CLK = 0;
_nop_();
}
// 複位
DS1302_CE = 0;
_nop_();
// 釋放時鐘
DS1302_CLK = 1;
_nop_();
DS1302_IO = 0;
_nop_();
DS1302_IO = 1;
_nop_();
return value;
}
/**
* ds1302初始化
*/
void ds1302_init(void){
u8 i = 0;
// 寫入禁止寫保護
ds1302_write_byte(0x8e, 0x00);
// 寫資料
for(i=0; i<7; i++){
ds1302_write_byte(gWRITE_RTC_ADDR[i], gDS1302_TIME[i]);
}
// 寫入啟用寫保護
ds1302_write_byte(0x8e, 0x80);
}
/**
* 寫入時間
*/
void ds1302_read_time(void){
u8 i=0;
for(i=0;i<7;i++)
{
gDS1302_TIME[i]=ds1302_read_byte(gREAD_RTC_ADDR[i]);
}
}
4. main.c
#include <reg52.h>
#include "led_utils.h"
#include "common_utils.h"
#include "timer_utils.h"
#include "uart_utils.h"
#include "key_utils.h"
#include "eeprom_utils.h"
#include "segment_display_utils.h"
#include "ds1302.h"
/**
* @brief 主函數
*/
main()
{
u8 time_buf[8];
// 關閉所有led
led_all_off();
uart_init(0xFA);
ds1302_init();
while(1)
{
ds1302_read_time();
uart_send(u8_to_hex(gDS1302_TIME[2]));
time_buf[0]=gDS1302_TIME[2]/16;
time_buf[1]=gDS1302_TIME[2]&0x0f;
time_buf[2]=0x10;
time_buf[3]=gDS1302_TIME[1]/16;
time_buf[4]=gDS1302_TIME[1]&0x0f;
time_buf[5]=0x10;
time_buf[6]=gDS1302_TIME[0]/16;
time_buf[7]=gDS1302_TIME[0]&0x0f;
segment_show_u8_array(time_buf);
}
}
本文代碼開源在
https://gitee.com/xundh/learn51