天天看點

51單片機學習筆記12 SPI接口 使用1302時鐘

作者:程式設計圈子

一、DS1302簡介

1. 功能特性

DS1302是一款由Maxim Integrated公司生産的實時時鐘(RTC)晶片。

RTC晶片用于計算機和其他電子裝置中的時間和日期的內建電路,通常被用于電子時鐘、計時器、溫度記錄器等應用中。

DS1302部分功能特性:

  1. 實時時鐘功能:提供年、月、日、時、分、秒等時間資料。
  2. 串行接口:通過串行接口(如SPI)與微控制器進行通信。
  3. 低功耗設計:具有低功耗特性,在電池供電情況下能夠長時間穩定運作。
  4. 内置晶體振蕩器:內建了晶體振蕩器,不需要外部晶振。
  5. 電池備份:支援電池備份以保持時鐘運作,即使主電源斷電時也能保持時間資料。
  6. 溫度補償:具有溫度補償功能,可以提高時鐘的準确性。

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):備用電池地線。
51單片機學習筆記12 SPI接口 使用1302時鐘

二、寄存器介紹

1. 控制寄存器

51單片機學習筆記12 SPI接口 使用1302時鐘
  • 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碼有幾種常見的表示方法:

  1. 8421碼:每個十進制數字用4位二進制數表示,範圍從 0000 到 1001。這種編碼方式的名稱來自每個位上的權重,分别是8、4、2和1。例如,十進制數 7 的BCD編碼為 0111。
  2. 2421碼:類似于8421碼,但在前四個數字中,9表示為 1001,而不是 1000。這種編碼方式的目的是為了簡化BCD加法的實作。
  3. Excess-3碼:在8421碼的基礎上,每個數字都加上了3。例如,數字 0 的8421碼是 0000,加上3後變為 0011。

DS1302實時時鐘晶片使用的是8421碼(也稱為8421BCD碼)

四、DS1302時序

1. 讀時序

單位元組讀:

51單片機學習筆記12 SPI接口 使用1302時鐘
  • CE:高電平使能
  • SCLK:時鐘
  • IO:資料讀
  • I/O設定為輸入
  • 在時鐘上升沿寫入,從低向高寫入;
  • 第一個位元組是寫入指令
  • I/O設定為輸出
  • 後一個位元組是讀操作
  • 寫入完成後,在下一個時鐘下降沿讀;

2. 寫時序

單位元組寫:

51單片機學習筆記12 SPI接口 使用1302時鐘

五、實作

1. 硬體原理圖

51單片機學習筆記12 SPI接口 使用1302時鐘

下面代碼在數位管顯示時間。

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

繼續閱讀