3867|3

8616

帖子

8

资源

管理员

数字时钟芯片(二)数字时钟的设计 [复制链接]

文章节选自:《ARM Cortex-M0从这里开始》 作者:zhaojun_xf   http://bbs.eeworld.com.cn/thread-324656-1-1.html

 

数字时钟芯片
    数字时钟芯片的种类很多,市场上比较流行的有三线的DS1302、I2C总线的PCF8563等。其中,DS1302价格比较低廉、容易购买,非常适合DIY。下面将用数字时钟芯片DS1302设计电子时钟。
1  DS1302概述
DS1302 是DALLAS公司推出的涓流充电时钟芯片,内含有一个实时时钟/日历和31字节静态RAM,通过简单的串行接口与单片机进行通信。实时时钟/日历电路提供秒、分、时、日、月、年和星期等信息。DS1302与单片机之间能简单地采用同步串行的方式进行通信,仅需用到三个口线:(1)RES(复位),(2)I/O(数据线),(3)SCLK(串行时钟)。
1. DS1302的特点:
 实时时钟具有能计算2100 年之前的秒、分、时、日、星期、月、年的能力、还有闰年调整的能力;
 31×8位暂存数据存储RAM;
 串行I/O口方式使得管脚数量最少;
 宽范围工作电压2.0 ~5.5V;
 工作电流2.0V时,小于300nA;
 读/写时钟或RAM数据时有两种传送方式单字节传送和多字节传送字符组方式;
 8脚DIP封装或可选的8脚SOIC封装根据表面装配;
 与TTL兼容Vcc = 5V;
2.  DS1302管脚描述
DS1302有DIP和SOIC两种封装,引脚排列如图8-5所示。

                                      未命名1.jpg

表8-2是DS1302的管脚描述。

             未命名2.jpg

3.  DS1302指令与寄存器说明
DS1302有多条控制指令,其中比较常用的指令如表8-3所示。对于DS1302的数据格式均是采用BCD编码,高位没有使用的用“0”填充。

             未命名3.jpg

寄存器说明:
CH:时钟停止位
CH = 0 振荡器工作允许;
CH = 1 振荡器停止;

WP:写保护位
WP = 0 寄存器数据能够写入;
WP = 1 寄存器数据不能写入;

TCS:涓流充电选择
TCS = 1010 使能涓流充电;
TCS = 其他 禁止涓流充电;

DS:二极管选择位
DS = 01 选择一个二极管;
DS = 10 选择两个二极管;
DS = 00或11,即使TCS = 1010,充电功能也被禁止。
2  硬件电路的设计
对于DS1302的连接非常简单,只需要把它的3条控制线与LPC1114的3条GPIO相连就可以了,具体的硬件连接如图8-6所示。这里使用了一个3V纽扣电池作为备用电池,保证时钟在系统断电下也能够正常工作。

            未命名4.jpg

3  软件的实现
DS1302的控制并不难,只需要按照其时序和控制格式编写程序就能实现时钟控制。由于DS1302是专一的时钟芯片,并不需要开发者对时间进行控制。在应用中只要设置好正确的时间后,就可以直接从芯片中读取时间值来。
1. 指令和数据定义
为了方便控制,首先在头文件中定义DS1302的控制命令、数据格式,再定义个全面变量,用于存放时钟值,如程序清单8-1所示。
/******************************  程序清单8-1  **********************************/
/********************************************************************************
* 【宏定义】 macro definition                                                
********************************************************************************/

/***************     指令格式   ******************************/

#define DS1302_READ_SEC           (0x81) // 读秒
#define DS1302_READ_MIN           (0x83) // 读分
#define DS1302_READ_HOUR          (0x85) // 读时
#define DS1302_READ_DATE          (0x87) // 读日
#define DS1302_READ_MON           (0x89) // 读月
#define DS1302_READ_WEEK          (0x8B) // 读周
#define DS1302_READ_YEAR          (0x8D) // 读年

#define DS1302_WRITE_SEC          (0x80) // 写秒
#define DS1302_WRITE_MIN          (0x82) // 写分
#define DS1302_WRITE_HOUR         (0x84) // 写时
#define DS1302_WRITE_DATE         (0x86) // 写日
#define DS1302_WRITE_MON          (0x88) // 写月
#define DS1302_WRITE_WEEK         (0x8A) // 写周
#define DS1302_WRITE_YEAR         (0x8C) // 写年

#define DS1302_READ_CALENDAR      (0xBF) // 读多字节
#define DS1302_WRITE_CALENDAR     (0xBE) // 写多字节

#define DS1302_READ_PROTECT       (0x8F) // 读保护
#define DS1302_WRITE_PROTECT      (0x8E) // 写保护

#define DS1302_READ_CHARGE        (0x91) // 读充电指令
#define DS1302_WRITE_CHARGE       (0x90) // 写充电指令

/***************     数据格式   ******************************/

#define DS1302_PROTECT_DIS        (0x00) // 00000000 寄存器数据能够写入
#define DS1302_PROTECT_EN         (0x80) // 10000000 寄存器数据不能写入

#define DS1302_CHARGE_DIS0        (0xA4) // 101001xx 充电开一个二极管
#define DS1302_CHARGE_DIS1        (0xA8) // 101010xx 充电开两个二极管
#define DS1302_CHARGE_EN          (0x00) // 0000xxxx 充电关

#define DS1302_OSC_DIS            (0x00) // 0xxxxxxx 振荡器工作允许
#define DS1302_OSC_EN             (0x80) // 1xxxxxxx 振荡器停止

#define DS1302_MODE_12            (0x80) // 1xxxxxxx 12小时制
#define DS1302_MODE_24            (0x00) // 0xxxxxxx 24小时制

#define DS1302_MODE_AM            (0x00) // xxx0xxxx 上午
#define DS1302_MODE_PA            (0x10) // xxx1xxxx 下午

/********************************************************************************
* 【数据结构】 Data Structures                                             
********************************************************************************/
typedef struct 
{
 uint8  Sec;                          // 秒
 uint8  Min;                          // 分
 uint8  Hour;                         // 时
 uint8  Date;                         // 日
 uint8  Month;                         // 月
 uint8  Week;                         // 周
 uint8  Year;                            // 年
} DS1302Clock;                          // 时钟定义
2. 写操作
写操作完成从数据端口写入一个字节数据到DS1302,数据的写入是通过8次,按顺序写入8个位。
/******************************  程序清单8-2  **********************************/
/********************************************************************************
* FunctionName   : DS1302_WriteByte()
* Description    : 往DS1302写入1Byte数据
* EntryParameter : ucDa 写入的数据
* ReturnValue    : None
********************************************************************************/
void DS1302_WriteByte(uint8 ucDa)
{
    uint8 i;
 
    for(i=8; i>0; i--)
    {
     DS1302_CLK_CLR();                                       // 时钟拉低
  
  if (ucDa & 0x01)
  {
      DS1302_IO_SET();                                    // 写入1
  }
  else
  {
      DS1302_IO_CLR();                                    // 写入0
  }
  
  DS1302_CLK_SET();                                       // 时钟拉高
  ucDa >>= 1;                                             // 写入一位数据      
    }
}
3. 读操作
读操作是实现从DS1302的数据端口读取一个字节数据,在读取数据之前先要设置数据端口为输入模式;然后8次读取数据端口数据组成一字节数据;读取完数据后需要把数据端口设置成为输出端口,方便其他函数实现写入功能。
/******************************  程序清单8-3  **********************************/
/********************************************************************************
* FunctionName   : DS1302_ReadByte()
* Description    : 从DS1302读取1Byte数据
* EntryParameter : None
* ReturnValue    : 读取的数据
********************************************************************************/
uint8 DS1302_ReadByte(void)
{
    uint8 i,tmp,value;
 
 DS1302_IO_DIR_IN();                                  // 端口设置为输入
   
 for(i=8; i>0; i--)
  {
      tmp >>= 1;
  DS1302_CLK_SET();                                       // 时钟信号
  DS1302_Delay(1);

  DS1302_CLK_CLR();                                       // 时钟信号
  DS1302_Delay(1);
  
  value = DS1302_IO_PIN();
  if (value == 1)                                         // 位判断
  {
            tmp |= 0x80;                                      // 写入"1"
  }
 }
 
    DS1302_IO_DIR_OUT();                                        // 设置端口为输出
     return(tmp);
}
4.写时钟数据
图8-7所示为写一字节时钟数据的时序图,写入数据必须把CE拉高,然后先写入地址或指令字节,之后写入一字节的相应数据即可。

             未命名5.jpg

/******************************  程序清单8-4  **********************************/
/********************************************************************************
* FunctionName   : DS1302_WriteOne()
* Description    : 往DS1302地址写入数据。先写地址,后写命令/数据
* EntryParameter : ord - DS1302地址, dat - 要写的数据
* ReturnValue    : None
********************************************************************************/
void DS1302_WriteOne(uint8 ord, uint8 dat)
{
    DS1302_CE_CLR();                                            // DS1302_CE = 0
     DS1302_CLK_CLR();                                           // DS1302_CLK = 0
 DS1302_CE_SET();                                            // DS1302_CE = 1

    DS1302_WriteByte(ord);                                      // 命令
    DS1302_WriteByte(dat);                                      // 写1Byte数据
 DS1302_CE_CLR();                                            // DS1302_CE = 0
     DS1302_CLK_SET();                                           // DS1302_CLK = 1
}
5. 读时钟数据
如图8-8所示为从DS1302读取一字节时钟数据的时序图,读取数据的整个过程中也必须把CE拉高,然后写写入一字节地址或指令,之后就可以从数据端口读取一字节的数据了。
               未命名6.jpg

/******************************  程序清单8-5  **********************************/
/********************************************************************************
* FunctionName   : DS1302_ReadOne()
* Description    : 读取DS1302某地址的数据,先写地址,后读命令/数据
* EntryParameter : ord - DS1302命令
* ReturnValue    : 返回读取数据
********************************************************************************/
uint8 DS1302_ReadOne(uint8 ord)
{
    uint8 ucDa;
 
     DS1302_CE_CLR();                                            // DS1302_CE = 0
 DS1302_CLK_CLR();                    // DS1302_CLK = 0
 DS1302_CE_SET();                    // DS1302_CE = 1

    DS1302_WriteByte(ord);                                      // 地址,命令
    ucDa = DS1302_ReadByte();                                   // 读1Byte数据
     DS1302_CE_CLR();                    // DS1302_CE = 0
     DS1302_CLK_SET();                    // DS1302_CLK = 1
 
    return(ucDa);                     // 返回读取数据
}
6. 初始化DS1302
在进行DS1302的操作之前必须初始化DS1302。初始化DS1302包括设置端口为GPIO、设置端口的方向以及写保护操作和关闭涓流充电。
在设置端口为GPIO时需要特别注意,对于有AD功能的GPIO端口,必须设置成为数字输入模式而不是模拟输入模式,否则无法读取端口数据。
/******************************  程序清单8-6  **********************************/
  /********************************************************************************
* FunctionName   : DS1302Init()
* Description    : 初始化DS18B20
* EntryParameter : None
* ReturnValue    : None
********************************************************************************/
void DS1302Init(void)
{
    // 设置为GPIO
    DS1302_CLK_PIN_MOD();
 DS1302_IO_PIN_MOD();
 DS1302_CE_PIN_MOD();

 // 设置为输出端口
     DS1302_CLK_DIR_OUT();                      // CLK
 DS1302_IO_DIR_OUT();                      // IO
 DS1302_CE_DIR_OUT();                      // CE

DS1302_WriteOne(DS1302_WRITE_PROTECT,DS1302_PROTECT_EN);    // 写保护操作
    DS1302_WriteOne(DS1302_WRITE_CHARGE,DS1302_CHARGE_EN);      // 禁止充电
}
7. 设置时钟
对于时钟的设置,项目中一共提供了两个函数,这两个函数的操作是一样的,不同的只是数据存放的形式不同。一个是把时钟数据放入数组中,一个是把时钟数据放入结构体中。本项目中使用的是第二个函数,代码如程序清单8-7所示。
写入时钟值之前必须先开写入保护功能,再按照顺序写入时钟值,完成后需要恢复保护功能,预防干扰等更改时钟值。
/******************************  程序清单8-7  **********************************/
/********************************************************************************
* FunctionName   : DS1302SetClock1()
* Description    : 初始时间格式为: 秒 分 时 日 月 星期 年
* EntryParameter : pSecDa - 初始时间地址。
* ReturnValue    : None
********************************************************************************/
void DS1302SetClock1(DS1302Clock *clock)
{
    DS1302_WriteOne(DS1302_WRITE_PROTECT,DS1302_PROTECT_DIS);  // 控制命令,写操作
 
    DS1302_WriteOne(DS1302_WRITE_SEC, clock->Sec);       // 写秒
     DS1302_WriteOne(DS1302_WRITE_MIN, clock->Min);      // 写分
     DS1302_WriteOne(DS1302_WRITE_HOUR,clock->Hour);      // 写小时
 DS1302_WriteOne(DS1302_WRITE_DATE,clock->Date);      // 写日期
 DS1302_WriteOne(DS1302_WRITE_MON, clock->Month);     // 写月
 DS1302_WriteOne(DS1302_WRITE_WEEK,clock->Week);      // 写星期
 DS1302_WriteOne(DS1302_WRITE_YEAR,clock->Year);            // 写年

    DS1302_WriteOne(DS1302_WRITE_PROTECT,DS1302_PROTECT_EN);   // 控制命令,写保护
}
8. 获取时钟
同样读取时钟值也有两个函数,区别和设置时钟函数是一样的,代码如程序清单8-8所示,这里就不在叙述。
/******************************  程序清单8-8  **********************************/
/********************************************************************************
* FunctionName   : DS1302GetClock1()
* Description    : 读取DS1302当前时间,当前时间格式为: 秒 分 时 日 月 星期 年
* EntryParameter : clock - 保存当前时间地址。
* ReturnValue    : None
********************************************************************************/
void DS1302GetClock1(DS1302Clock *clock)
{
    clock->Sec   = DS1302_ReadOne(DS1302_READ_SEC);     // 读秒
 clock->Min   = DS1302_ReadOne(DS1302_READ_MIN);     // 读分
     clock->Hour  = DS1302_ReadOne(DS1302_READ_HOUR);    // 读小时
     clock->Date  = DS1302_ReadOne(DS1302_READ_DATE);    // 读日期
 clock->Month = DS1302_ReadOne(DS1302_READ_MON);     // 读月
 clock->Week  = DS1302_ReadOne(DS1302_READ_WEEK);    // 读星期
 clock->Year  = DS1302_ReadOne(DS1302_READ_YEAR);     // 读年
}



 

此帖出自NXP MCU论坛

扫一扫,关注 EEWORLD 微信订阅号

行业资讯、电子趣闻、技术干货、精彩活动……尽可掌握~


回复

308

帖子

0

资源

纯净的硅(高级)

请问1302我想通过串口修改时间重新设定有什么办法呢

回复

5346

帖子

19

资源

裸片初长成(初级)

这个很简单啊,按照一定格式把时间通过串口发送到下位机,下位机收到后,把数据通过参数传递给时钟设置函数。
个人签名我的博客

回复

314

帖子

0

资源

一粒金砂(高级)

受益颇深

回复
您需要登录后才可以回帖 登录 | 注册

最新文章 更多>>
    关闭
    站长推荐上一条 1/10 下一条

    About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

    站点相关: 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

    北京市海淀区知春路23号集成电路设计园量子银座1305 电话:(010)82350740 邮编:100191

    电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2021 EEWORLD.com.cn, Inc. All rights reserved
    快速回复 返回顶部 返回列表