美文网首页
Day10. 时钟芯片 DS1302

Day10. 时钟芯片 DS1302

作者: xqiiitan | 来源:发表于2025-04-03 18:53 被阅读0次

10.时钟芯片 DS1302

精度更高,不消耗cpu,可以掉电继续运行。

10.1 时钟显示,显示年月日时分秒。

// DS1302.h
#ifndef __DS1302_H__
#define __DS1302_H__
//外部可调用时间数组,索引0~6分别为年、月、日、时、分、秒、星期
extern unsigned char DS1302_Time[];

void DS1302_Init(void);
void DS1302_WriteByte(unsigned char Command,Data);
unsigned char DS1302_ReadByte(unsigned char Command);
void DS1302_SetTime(void);
void DS1302_ReadTime(void);
#endif

// DS1302.c
#include <REGX52.H>
//引脚定义
sbit DS1302_SCLK=P3^6;
sbit DS1302_IO=P3^4;
sbit DS1302_CE=P3^5;

//寄存器写入地址/指令定义
#define DS1302_SECOND       0x80
#define DS1302_MINUTE       0x82
#define DS1302_HOUR         0x84
#define DS1302_DATE         0x86
#define DS1302_MONTH        0x88
#define DS1302_DAY          0x8A
#define DS1302_YEAR         0x8C
#define DS1302_WP           0x8E

//时间数组,索引0~6分别为: 年、月、日、时、分、秒、星期
 char DS1302_Time[]={19,11,16,12,59,55,6};

/**
  * @brief  DS1302初始化
  * @param  无
  * @retval 无
  */
void DS1302_Init(void)
{
    DS1302_CE=0;
    DS1302_SCLK=0;
}

/**
  * @brief  DS1302写一个字节
  * @param  Command 命令字/地址
  * @param  Data 要写入的数据
  * @retval 无
  */
void DS1302_WriteByte(unsigned char Command,Data)
{
    unsigned char i;
    DS1302_CE=1;
    for(i=0;i<8;i++)
    {
        DS1302_IO=Command&(0x01<<i);
        DS1302_SCLK=1;
        DS1302_SCLK=0;
    }
    for(i=0;i<8;i++)
    {
        DS1302_IO=Data&(0x01<<i);
        DS1302_SCLK=1;
        DS1302_SCLK=0;
    }
    DS1302_CE=0;
}

/**
  * @brief  DS1302读一个字节
  * @param  Command 命令字/地址
  * @retval 读出的数据
  */
unsigned char DS1302_ReadByte(unsigned char Command)
{
    unsigned char i,Data=0x00;
    Command |=0x01; //将指令转换为读指令
    DS1302_CE=1;
    for(i=0;i<8;i++)
    {
        DS1302_IO=Command&(0x01<<i);
        DS1302_SCLK=0;
        DS1302_SCLK=1;
    }
    for(i=0;i<8;i++)
    {
        DS1302_SCLK=1;
        DS1302_SCLK=0;
        if(DS1302_IO){Data|=(0x01<<i);}
    }
    DS1302_CE=0;
    DS1302_IO=0;    //读取后将IO设置为0,否则读出的数据会出错
    return Data;
}

/**
  * @brief  DS1302设置时间,调用之后,DS1302_Time数组的数字会被设置到时钟芯片DS1302中
  * @param  无
  * @retval 无
  */
void DS1302_SetTime(void)
{
    DS1302_WriteByte(DS1302_WP,    0x00); // 关闭写保护
    DS1302_WriteByte(DS1302_YEAR,  DS1302_Time[0]/10*16+DS1302_Time[0]%10);//十进制转BCD码后写入
    DS1302_WriteByte(DS1302_MONTH, DS1302_Time[1]/10*16+DS1302_Time[1]%10);
    DS1302_WriteByte(DS1302_DATE,  DS1302_Time[2]/10*16+DS1302_Time[2]%10);
    DS1302_WriteByte(DS1302_HOUR,  DS1302_Time[3]/10*16+DS1302_Time[3]%10);
    DS1302_WriteByte(DS1302_MINUTE,DS1302_Time[4]/10*16+DS1302_Time[4]%10);
    DS1302_WriteByte(DS1302_SECOND,DS1302_Time[5]/10*16+DS1302_Time[5]%10);
    DS1302_WriteByte(DS1302_DAY,   DS1302_Time[6]/10*16+DS1302_Time[6]%10);
    DS1302_WriteByte(DS1302_WP,    0x80); // 打开写保护
}

/**
  * @brief  DS1302读取时间,调用之后,DS1302中的数据会被读取到DS1302_Time数组中
  * @param  无
  * @retval 无
  */
void DS1302_ReadTime(void)
{
    unsigned char Temp;
    Temp=DS1302_ReadByte(DS1302_YEAR); // 年
    DS1302_Time[0]=Temp/16*10+Temp%16; //BCD码转十进制后读取
    Temp=DS1302_ReadByte(DS1302_MONTH);
    DS1302_Time[1]=Temp/16*10+Temp%16;
    Temp=DS1302_ReadByte(DS1302_DATE);
    DS1302_Time[2]=Temp/16*10+Temp%16;
    Temp=DS1302_ReadByte(DS1302_HOUR);
    DS1302_Time[3]=Temp/16*10+Temp%16;
    Temp=DS1302_ReadByte(DS1302_MINUTE);
    DS1302_Time[4]=Temp/16*10+Temp%16;
    Temp=DS1302_ReadByte(DS1302_SECOND);
    DS1302_Time[5]=Temp/16*10+Temp%16;
    Temp=DS1302_ReadByte(DS1302_DAY);
    DS1302_Time[6]=Temp/16*10+Temp%16;
}

#include <REGX52.H>
#include "LCD1602.h"
#include "DS1302.h"

void main()
{
    LCD_Init();
    DS1302_Init();
    LCD_ShowString(1,1, "  -  -  ");//静态字符初始化显示
    LCD_ShowString(2,1, "  :  :  ");
    
    DS1302_SetTime();//设置时间
    while(1)
    {
        DS1302_ReadTime();//读取时间,时间被读取到DS1302_Time数组中,后序直接操作数组。
        LCD_ShowNum(1,1,DS1302_Time[0],2);//显示年
        LCD_ShowNum(1,4,DS1302_Time[1],2);//显示月
        LCD_ShowNum(1,7,DS1302_Time[2],2);//显示日
        LCD_ShowNum(2,1,DS1302_Time[3],2);//显示时
        LCD_ShowNum(2,4,DS1302_Time[4],2);//显示分
        LCD_ShowNum(2,7,DS1302_Time[5],2);//显示秒
    }
}

=================================================

10.2可调的时钟。

显示模式到调节模式,调节模式闪烁1s为周期。
按键1-4,按键1模式切换;按键2调节位移动;按键34加减。
// 导入Key.hc库,Delay.hc定时器库,LCD1602.hc显示屏模块。
时钟显示功能,时钟设置功能。

#include <REGX52.H>
#include "LCD1602.h"
#include "DS1302.h"
#include "Key.h"
#include "Timer0.h"

// KeyNum: 按下的按键
// TimeSetSelect: 设置功能选择0~5,选择设置年月日时分秒
// MODE 模式选择: 0显示,1设置
// TimeSetFlashFlag: 设置时间标记。
unsigned char KeyNum,MODE,TimeSetSelect,TimeSetFlashFlag;

void TimeShow(void)//时间显示功能
{
    DS1302_ReadTime();//读取时间
    LCD_ShowNum(1,1,DS1302_Time[0],2);//显示年
    LCD_ShowNum(1,4,DS1302_Time[1],2);//显示月
    LCD_ShowNum(1,7,DS1302_Time[2],2);//显示日
    LCD_ShowNum(2,1,DS1302_Time[3],2);//显示时
    LCD_ShowNum(2,4,DS1302_Time[4],2);//显示分
    LCD_ShowNum(2,7,DS1302_Time[5],2);//显示秒
}
void TimeSet(void)//时间设置功能
{
    if(KeyNum==2)//按键2按下,设置功能选择
    {
        TimeSetSelect++;//设置选择位加1
        TimeSetSelect%=6;//越界清零
    }
    if(KeyNum==3)//按键3按下,功能加
    {
        DS1302_Time[TimeSetSelect]++;//时间设置位数值加1
        
        if(DS1302_Time[0]>99){DS1302_Time[0]=0;}//年越界判断
        if(DS1302_Time[1]>12){DS1302_Time[1]=1;}//月越界判断
        if( DS1302_Time[1]==1 || DS1302_Time[1]==3 || DS1302_Time[1]==5 || DS1302_Time[1]==7 || 
            DS1302_Time[1]==8 || DS1302_Time[1]==10 || DS1302_Time[1]==12)//日越界判断
        {
            if(DS1302_Time[2]>31){DS1302_Time[2]=1;}//大月
        }
        else if(DS1302_Time[1]==4 || DS1302_Time[1]==6 || DS1302_Time[1]==9 || DS1302_Time[1]==11)
        {
            if(DS1302_Time[2]>30){DS1302_Time[2]=1;}//小月
        }
        else if(DS1302_Time[1]==2)
        {
            if(DS1302_Time[0]%4==0)
            {
                if(DS1302_Time[2]>29){DS1302_Time[2]=1;}//闰年2月
            }
            else
            {
                if(DS1302_Time[2]>28){DS1302_Time[2]=1;}//平年2月
            }
        }
        if(DS1302_Time[3]>23){DS1302_Time[3]=0;}//时越界判断
        if(DS1302_Time[4]>59){DS1302_Time[4]=0;}//分越界判断
        if(DS1302_Time[5]>59){DS1302_Time[5]=0;}//秒越界判断
    }
    if(KeyNum==4)//按键4按下,功能减
    {
        DS1302_Time[TimeSetSelect]--;//时间设置位数值减1
        
        if(DS1302_Time[0]<0){DS1302_Time[0]=99;}//年越界判断
        if(DS1302_Time[1]<1){DS1302_Time[1]=12;}//月越界判断
        if( DS1302_Time[1]==1 || DS1302_Time[1]==3 || DS1302_Time[1]==5 || DS1302_Time[1]==7 || 
            DS1302_Time[1]==8 || DS1302_Time[1]==10 || DS1302_Time[1]==12)//日越界判断
        {
            if(DS1302_Time[2]<1){DS1302_Time[2]=31;}//大月
            if(DS1302_Time[2]>31){DS1302_Time[2]=1;}
        }
        else if(DS1302_Time[1]==4 || DS1302_Time[1]==6 || DS1302_Time[1]==9 || DS1302_Time[1]==11)
        {
            if(DS1302_Time[2]<1){DS1302_Time[2]=30;}//小月
            if(DS1302_Time[2]>30){DS1302_Time[2]=1;}
        }
        else if(DS1302_Time[1]==2)
        {
            if(DS1302_Time[0]%4==0)
            {
                if(DS1302_Time[2]<1){DS1302_Time[2]=29;}//闰年2月
                if(DS1302_Time[2]>29){DS1302_Time[2]=1;}
            }
            else
            {
                if(DS1302_Time[2]<1){DS1302_Time[2]=28;}//平年2月
                if(DS1302_Time[2]>28){DS1302_Time[2]=1;}
            }
        }
        if(DS1302_Time[3]<0){DS1302_Time[3]=23;}//时越界判断
        if(DS1302_Time[4]<0){DS1302_Time[4]=59;}//分越界判断
        if(DS1302_Time[5]<0){DS1302_Time[5]=59;}//秒越界判断
    }
    // UI更新显示,根据TimeSetSelect和TimeSetFlashFlag判断可完成闪烁功能
    // 年设置中TimeSetSelect==0,熄灭ShowString;显示LCD_ShowNum。实现闪烁效果。
    if(TimeSetSelect==0 && TimeSetFlashFlag==1){LCD_ShowString(1,1,"  ");}
    else {LCD_ShowNum(1,1,DS1302_Time[0],2);}
    if(TimeSetSelect==1 && TimeSetFlashFlag==1){LCD_ShowString(1,4,"  ");}
    else {LCD_ShowNum(1,4,DS1302_Time[1],2);}
    if(TimeSetSelect==2 && TimeSetFlashFlag==1){LCD_ShowString(1,7,"  ");}
    else {LCD_ShowNum(1,7,DS1302_Time[2],2);}
    if(TimeSetSelect==3 && TimeSetFlashFlag==1){LCD_ShowString(2,1,"  ");}
    else {LCD_ShowNum(2,1,DS1302_Time[3],2);}
    if(TimeSetSelect==4 && TimeSetFlashFlag==1){LCD_ShowString(2,4,"  ");}
    else {LCD_ShowNum(2,4,DS1302_Time[4],2);}
    if(TimeSetSelect==5 && TimeSetFlashFlag==1){LCD_ShowString(2,7,"  ");}
    else {LCD_ShowNum(2,7,DS1302_Time[5],2);}
    
    // LCD_ShowNum(2,10, TimeSetSelect, 2); // 显示当前操作
    // LCD_ShowNum(2,13, TimeSetFlashFlag, 2);
}

void main()
{
    LCD_Init();
    DS1302_Init();
    Timer0Init(); // 定时器0初始化。
    LCD_ShowString(1,1,"  -  -  ");//静态字符初始化显示
    LCD_ShowString(2,1,"  :  :  ");
    
    DS1302_SetTime();//设置时间
    
    while(1)
    {
        KeyNum =Key();//读取键码
        if(KeyNum ==1)//按键1按下
        {
            if(MODE==0){MODE=1; TimeSetSelect=0;}//功能切换
            else if(MODE==1){MODE=0; DS1302_SetTime();} // 设置时间。
        }
        switch(MODE)//根据不同的功能执行不同的函数
        {
            case 0: TimeShow(); break;
            case 1: TimeSet(); break;
        }
    }
}
// 中断函数。
void Timer0_Routine() interrupt 1
{
    static unsigned int T0Count;
    TL0 = 0x18;     //设置定时初值
    TH0 = 0xFC;     //设置定时初值
    T0Count++;
    if(T0Count>=500)//每500ms进入一次
    {
        T0Count=0;
        TimeSetFlashFlag = !TimeSetFlashFlag;//闪烁标志位取反
    }
}

定时器中断,扫描按键时间。优化按键持续不放时,程序卡住的bug。

相关文章

网友评论

      本文标题:Day10. 时钟芯片 DS1302

      本文链接:https://www.haomeiwen.com/subject/pxbvvjtx.html