普中51单片机学习笔记-DS1302实时时钟芯片

芯片简介

DS1302是Dallas Semiconductor(现为Maxim Integrated)推出的涓流充电实时时钟芯片,主要特点:

  1. 实时时钟功能:秒、分、时、日、月、星期、年(2000年闰年补偿)

  2. 31字节RAM:用于数据存储

  3. 串行接口:三线SPI兼容接口

  4. 双电源供电:主电源和备用电池

  5. 涓流充电:可编程充电电路

  6. 低功耗:工作电流<300nA(典型值@2.0V)

  7. 宽电压范围:2.0V-5.5V

管脚定义

引脚 名称 功能描述
1 VCC2 主电源,接+5V
2 X1 32.768kHz晶振输入
3 X2 32.768kHz晶振输出
4 GND
5 CE (RST) 片选/复位,高电平有效
6 I/O 数据输入/输出
7 SCLK 串行时钟输入
8 VCC1 备用电源,接3V电池

内部寄存器

1. 时间寄存器(12个)

地址(写) 地址(读) 寄存器 范围 位7 位6-位0
0x80 0x81 00-59 CH 秒(BCD)
0x82 0x83 00-59 0 分(BCD)
0x84 0x85 01-12或00-23 12/24 时(BCD)
0x86 0x87 01-31 0 日(BCD)
0x88 0x89 01-12 0 月(BCD)
0x8A 0x8B 星期 01-07 0 星期(BCD)
0x8C 0x8D 00-99 0 年(BCD)
0x8E 0x8F 控制 - WP 0
0x90 0x91 涓流充电 - TCS DS/RS
0x92 0x93 时钟突发 - - -
0xC0 0xC1 RAM 0 - - -
... ... ... ... ... ...
0xFC 0xFD RAM 30 - - -
0xBE 0xBF 时钟突发 - - -
0xFE 0xFF RAM突发 - - -

2. 特殊寄存器说明

控制寄存器(0x8E/0x8F):

位7:WP(写保护位),1=禁止写入,0=允许写入

涓流充电寄存器(0x90/0x91):

位4-7:TCS(涓流充电选择)

● 1010:使能涓流充电

● 其他:禁止充电

位2-3:DS(二极管选择)

● 01:1个二极管

● 10:2个二极管

位0-1:RS(电阻选择)

● 01:2kΩ

● 10:4kΩ

● 11:8kΩ

示例代码

ds1302.h

复制代码
#ifndef _DS1302_H_
#define _DS1302_H_

#include <reg51.h>
#include <intrins.h>

// 管脚定义
sbit DS1302_CE   = P3^5;    // 片选/复位
sbit DS1302_IO   = P3^4;    // 数据线
sbit DS1302_SCLK = P3^6;    // 时钟线

// 寄存器写地址
#define DS1302_SECOND_W  0x80
#define DS1302_MINUTE_W  0x82
#define DS1302_HOUR_W    0x84
#define DS1302_DATE_W    0x86
#define DS1302_MONTH_W   0x88
#define DS1302_DAY_W     0x8A
#define DS1302_YEAR_W    0x8C
#define DS1302_CONTROL_W 0x8E

// 寄存器读地址
#define DS1302_SECOND_R  0x81
#define DS1302_MINUTE_R  0x83
#define DS1302_HOUR_R    0x85
#define DS1302_DATE_R    0x87
#define DS1302_MONTH_R   0x89
#define DS1302_DAY_R     0x8B
#define DS1302_YEAR_R    0x8D

// 时间结构体
typedef struct {
    unsigned char second;
    unsigned char minute;
    unsigned char hour;
    unsigned char date;
    unsigned char month;
    unsigned char day;
    unsigned char year;
} Time_Type;

// 函数声明
void DS1302_Init(void);
void DS1302_WriteByte(unsigned char addr, unsigned char dat);
unsigned char DS1302_ReadByte(unsigned char addr);
void DS1302_SetTime(Time_Type *time);
void DS1302_GetTime(Time_Type *time);

#endif

ds1302.c

复制代码
#include "ds1302.h"

// 读取和写入的地址数组
unsigned char READ_RTC_ADDR[7] = {0x81, 0x83, 0x85, 0x87, 0x89, 0x8B, 0x8D}; 
unsigned char WRITE_RTC_ADDR[7] = {0x80, 0x82, 0x84, 0x86, 0x88, 0x8A, 0x8C};

// BCD转十进制
unsigned char BCD2Dec(unsigned char bcd) {
    return ((bcd >> 4) * 10) + (bcd & 0x0F);
}

// 十进制转BCD
unsigned char Dec2BCD(unsigned char dec) {
    if(dec > 99) dec = 99;
    return ((dec / 10) << 4) | (dec % 10);
}

// 写入一个字节
void DS1302_WriteByte(unsigned char addr, unsigned char dat) {
    unsigned char i;
    
    DS1302_CE = 0;
    _nop_();
    DS1302_SCLK = 0;
    _nop_();
    DS1302_CE = 1;
    _nop_();
    
    // 写入地址
    for(i = 0; i < 8; i++) {
        DS1302_IO = addr & 0x01;
        addr >>= 1;
        DS1302_SCLK = 1;
        _nop_();
        DS1302_SCLK = 0;
        _nop_();
    }
    
    // 写入数据
    for(i = 0; i < 8; i++) {
        DS1302_IO = dat & 0x01;
        dat >>= 1;
        DS1302_SCLK = 1;
        _nop_();
        DS1302_SCLK = 0;
        _nop_();
    }
    
    DS1302_CE = 0;
    _nop_();
}

// 读取一个字节
unsigned char DS1302_ReadByte(unsigned char addr) {
    unsigned char i, temp, value = 0;
    
    DS1302_CE = 0;
    _nop_();
    DS1302_SCLK = 0;
    _nop_();
    DS1302_CE = 1;
    _nop_();
    
    // 写入地址
    for(i = 0; i < 8; i++) {
        DS1302_IO = addr & 0x01;
        addr >>= 1;
        DS1302_SCLK = 1;
        _nop_();
        DS1302_SCLK = 0;
        _nop_();
    }
    
    // 读取数据
    for(i = 0; i < 8; i++) {
        temp = DS1302_IO;
        value = (temp << 7) | (value >> 1);
        DS1302_SCLK = 1;
        _nop_();
        DS1302_SCLK = 0;
        _nop_();
    }
    
    DS1302_CE = 0;
    _nop_();
    
    // 参考代码中的特殊操作,确保IO口状态正确
    DS1302_SCLK = 1;
    _nop_();
    DS1302_IO = 0;
    _nop_();
    DS1302_IO = 1;
    _nop_();
    
    return value;
}

// DS1302初始化
void DS1302_Init(void) {
    unsigned char temp;  // 将变量声明移到函数开头
    
    // 先关闭写保护
    DS1302_WriteByte(0x8E, 0x00);
    
    // 检查并启动时钟
    temp = DS1302_ReadByte(0x81);
    if(temp & 0x80) {
        DS1302_WriteByte(0x80, temp & 0x7F);
    }
    
    // 重新开启写保护
    DS1302_WriteByte(0x8E, 0x80);
}

// 设置时间
void DS1302_SetTime(Time_Type *time) {
    unsigned char time_buf[7];
    unsigned char i;  // 将变量声明移到函数开头
    
    // 转换为BCD码
    time_buf[0] = Dec2BCD(time->second);
    time_buf[1] = Dec2BCD(time->minute);
    time_buf[2] = Dec2BCD(time->hour);
    time_buf[3] = Dec2BCD(time->date);
    time_buf[4] = Dec2BCD(time->month);
    time_buf[5] = Dec2BCD(time->day);
    time_buf[6] = Dec2BCD(time->year);
    
    // 关闭写保护
    DS1302_WriteByte(0x8E, 0x00);
    
    // 写入时间
    DS1302_WriteByte(WRITE_RTC_ADDR[0], time_buf[0]);
    DS1302_WriteByte(WRITE_RTC_ADDR[1], time_buf[1]);
    DS1302_WriteByte(WRITE_RTC_ADDR[2], time_buf[2]);
    DS1302_WriteByte(WRITE_RTC_ADDR[3], time_buf[3]);
    DS1302_WriteByte(WRITE_RTC_ADDR[4], time_buf[4]);
    DS1302_WriteByte(WRITE_RTC_ADDR[5], time_buf[5]);
    DS1302_WriteByte(WRITE_RTC_ADDR[6], time_buf[6]);
    
    // 开启写保护
    DS1302_WriteByte(0x8E, 0x80);
}

// 读取时间
void DS1302_GetTime(Time_Type *time) {
    unsigned char time_buf[7];
    unsigned char i;  // 将变量声明移到函数开头
    
    // 读取所有时间寄存器
    for(i = 0; i < 7; i++) {
        time_buf[i] = DS1302_ReadByte(READ_RTC_ADDR[i]);
    }
    
    // 转换为十进制
    time->second = BCD2Dec(time_buf[0] & 0x7F);  // 清除时钟停止位
    time->minute = BCD2Dec(time_buf[1] & 0x7F);
    
    // 处理小时(24小时制)
    if(time_buf[2] & 0x80) {
        // 12小时制
        time->hour = BCD2Dec(time_buf[2] & 0x1F);
        if(time_buf[2] & 0x20) {
            // PM
            if(time->hour != 12) time->hour += 12;
        } else {
            // AM
            if(time->hour == 12) time->hour = 0;
        }
    } else {
        // 24小时制
        time->hour = BCD2Dec(time_buf[2] & 0x3F);
    }
    
    time->date = BCD2Dec(time_buf[3] & 0x3F);
    time->month = BCD2Dec(time_buf[4] & 0x1F);
    time->day = BCD2Dec(time_buf[5] & 0x07);
    time->year = BCD2Dec(time_buf[6]);
}

main.c

复制代码
#include <reg51.h>
#include "ds1302.h"

// 串口初始化
void UART_Init(void) {
    SCON = 0x50;
    TMOD = 0x20;
    TH1 = 0xFD;
    TL1 = 0xFD;
    PCON &= 0x7F;
    TR1 = 1;
}

// 发送字符
void UART_SendChar(char ch) {
    SBUF = ch;
    while(!TI);
    TI = 0;
}

// 发送字符串
void UART_SendString(const char *str) {
    while(*str) {
        UART_SendChar(*str++);
    }
}

// 发送两位数字
void UART_Send2Digit(unsigned char num) {
    if(num > 99) num = 99;
    UART_SendChar((num / 10) + '0');
    UART_SendChar((num % 10) + '0');
}

// 简单延时
void DelayMs(unsigned int ms) {
    unsigned int i, j;
    for(i = 0; i < ms; i++) {
        for(j = 0; j < 110; j++);
    }
}

// 延时1秒
void Delay1s(void) {
    DelayMs(1000);
}

// 主函数
void main(void) {
    Time_Type set_time, current_time;
    
    // 初始化串口
    UART_Init();
    DelayMs(100);
    
    UART_SendString("\r\nDS1302 Real-Time Clock\r\n");
    UART_SendString("=======================\r\n");
    
    // 初始化DS1302
    DS1302_Init();
    
    // 设置初始时间:2023-12-29 14:30:00 星期四
    set_time.second = 0;
    set_time.minute = 30;
    set_time.hour = 14;
    set_time.date = 29;
    set_time.month = 12;
    set_time.day = 5;  // 5=星期四
    set_time.year = 23;
    
    DS1302_SetTime(&set_time);
    
    UART_SendString("Time set to: 2023-12-29 14:30:00 Thu\r\n\r\n");
    
    while(1) {
        // 读取时间
        DS1302_GetTime(&current_time);
        
        // 显示日期
        UART_SendString("20");
        UART_Send2Digit(current_time.year);
        UART_SendChar('-');
        UART_Send2Digit(current_time.month);
        UART_SendChar('-');
        UART_Send2Digit(current_time.date);
        
        // 显示时间
        UART_SendChar(' ');
        UART_Send2Digit(current_time.hour);
        UART_SendChar(':');
        UART_Send2Digit(current_time.minute);
        UART_SendChar(':');
        UART_Send2Digit(current_time.second);
        
        // 显示星期
        UART_SendString(" Week:");
        switch(current_time.day) {
            case 1: UART_SendString("Sun"); break;
            case 2: UART_SendString("Mon"); break;
            case 3: UART_SendString("Tue"); break;
            case 4: UART_SendString("Wed"); break;
            case 5: UART_SendString("Thu"); break;
            case 6: UART_SendString("Fri"); break;
            case 7: UART_SendString("Sat"); break;
            default: UART_SendString("---"); break;
        }
        
        UART_SendString("\r\n");
        
        Delay1s();
    }
}

实验结果

完结,撒花~~~

相关推荐
车载测试工程师7 小时前
CAPL学习-SOME/IP交互层-值处理类函数2
学习·tcp/ip·以太网·capl·canoe
车载测试工程师7 小时前
CAPL学习-SOME/IP交互层-值处理类函数1
学习·tcp/ip·交互·以太网·capl·canoe
历程里程碑7 小时前
C++ 4:内存管理
java·c语言·开发语言·数据结构·c++·笔记·算法
LXS_3578 小时前
Day17 C++提高 之 类模板案例
开发语言·c++·笔记·算法·学习方法
Howie Zphile8 小时前
在 WSL Ubuntu 上从零到数据迁移:通过 pgloader 将 SQL Server 转 PostgreSQL 实战笔记
笔记·ubuntu·postgresql
零Suger8 小时前
React Router v7数据模式使用指南
javascript·笔记·react.js
子墨将8 小时前
Agent 智能体的经典三范式——笔记
笔记
zore_c8 小时前
【C语言】贪吃蛇游戏超详解(包含音效、颜色、封装成应用等)
c语言·数据结构·笔记·stm32·游戏·链表
专业开发者8 小时前
学习模块:Wi-Fi 测试与认证
学习