未加星标

树莓派使用DS1302实现实时时钟功能

字体大小 | |
[系统(linux) 所属分类 系统(linux) | 发布者 店小二04 | 时间 2016 | 作者 红领巾 ] 0人收藏点击收藏

树莓派在设计时由于对成本和体积的考虑,没有实时时钟功能(RTC:real-time clock),树莓派在使用时只能通过网络来同步时间,如果树莓派未连接网络,则无法实现时间同步,所以本文为树莓派添加了一个DS1302时钟芯片,使得树莓派在离线的情况下从DS1302中同步时间,从而实现硬件实时时钟功能。

一、准备条件 树莓派2B一个 DS1302时钟模块一个 5条杜邦线 二、DS1302介绍

DS1302 是DALLAS 公司推出的涓流充电时钟芯片,内含有一个实时时钟/日历和31 字节静态RAM ,通过简单的串行接口与单片机进行通信。实时时钟/日历电路提供秒、分、时、日、周、月、年的信息,每月的天数和闰年的天数可自动调整。时钟操作可通过AM/PM 指示决定采用24 或12 小时格式。DS1302 与单片机之间能简单地采用同步串行的方式进行通信,仅需用到三个口线:(1) RES 复位 (2) I/O 数据线 (3) SCLK 串行时钟 。时钟/RAM 的读/写数据以一个字节或多达31个字节的字符组方式通信。DS1302 工作时功耗很低保持数据和时钟信息时功率小于1mW。

DS1302具有以下特性:

实时时钟具有能计算2100 年之前的秒、分、时、日、星期、月、年的能力,还有闰年调整的能力 串行I/O口方式使得管脚数量最少 宽范围工作电压2.0-5.5V 工作电流2.0V时,小于300nA,具有低功耗的特性 读/写时钟或RAM 数据时有单字节传送和多字节传送字符组方式两种传送方式 简单3线接口 双电源管用于主电源和备份电源供应 三、DS1302模块介绍

我使用的是DS1302实时时钟模块,模块如下图。DS1302模块价格低廉,2元人民币就可以买到,并且附带一个CR2032纽扣电池,作为备份电源,当模块断电时也可以继续走时,保证时间信息不丢失。

模块是这样的:


树莓派使用DS1302实现实时时钟功能
DS1302时钟模块

模块共有5个引脚,引脚功能和与树莓派连接方式分别为:

VCC:模块的电源引脚,接树莓派的1号引脚( 3.3V )

GND:模块的地,接树莓派的6号引脚( Grand )

CLK:SCLK 串行时钟,接树莓派 wiringPi 0 (物理编号:11号)

DAT:I/O 数据线,接树莓派 wiringPi 1 (物理编号:12号)

RST:复位引脚,接树莓派 wiringPi 2 (物理编号:13号)

我使用的是树莓派2B,在实际使用时没有在VCC和DAT之间接上拉电阻,发现也可以正常的读取时间信号,如果你使用其他版本的树莓派,并且发现读取的时间不正确的话,那么需要在VCC和DAT引脚之间并连接一个10K的上拉电阻。

四、硬件电路连接

DS1302实时时钟模块的引脚在上面介绍了,如果你不清楚树莓派的wiringPi编号以及gpio编号,可以参考 树莓派gpio引脚对照表 。

DS1302和树莓派的整体连接方式如下图。


树莓派使用DS1302实现实时时钟功能

连接实物图:


树莓派使用DS1302实现实时时钟功能
树莓派与DS1302连接实物图 五、程序设计

ds1302的驱动程序可以在wiringPi的examples目录下找到,我这里修改了源文件,源文件在将linux时间设置到DS1302中时使用了UTC时间,这样与我们的CST时间有偏差,所以使用localtime将CST当地时间写入到DS1302中,修改后的文件,大家可以将原来的ds1302.c文件覆盖。

修改后的ds1302.c文件。

#include
#include
#include
#include
#include
#include
#include
// Register defines
#define RTC_SECS 0
#define RTC_MINS 1
#define RTC_HOURS 2
#define RTC_DATE 3
#define RTC_MONTH 4
#define RTC_DAY 5
#define RTC_YEAR 6
#define RTC_WP 7
#define RTC_TC 8
#define RTC_BM 31
static unsigned int masks [] = { 0x7F, 0x7F, 0x3F, 0x3F, 0x1F, 0x07, 0xFF } ;
/*
* bcdToD: dToBCD:
* BCD decode/encode
*********************************************************************************
*/
static int bcdToD (unsigned int byte, unsigned int mask)
{
unsigned int b1, b2 ;
byte &= mask ;
b1 = byte & 0x0F ;
b2 = ((byte >> 4) & 0x0F) * 10 ;
return b1 + b2 ;
}
static unsigned int dToBcd (unsigned int byte)
{
return ((byte / 10) << 4) + (byte % 10) ;
}
/*
* ramTest:
* Simple test of the 31 bytes of RAM inside the DS1302 chip
*********************************************************************************
*/
static int ramTestValues [] =
{ 0x00, 0xFF, 0xAA, 0x55, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x00, 0xF0, 0x0F, -1 } ;
static int ramTest (void)
{
int addr ;
int got ;
int i = 0 ;
int errors = 0 ;
int testVal ;
printf ("DS1302 RAM TEST\n") ;
testVal = ramTestValues [i] ;
while (testVal != -1)
{
for (addr = 0 ; addr < 31 ; ++addr)
ds1302ramWrite (addr, testVal) ;
for (addr = 0 ; addr < 31 ; ++addr)
if ((got = ds1302ramRead (addr)) != testVal)
{
printf ("DS1302 RAM Failure: Address: %2d, Expected: 0x%02X, Got: 0x%02X\n", addr, testVal, got) ;
++errors ;
}
testVal = ramTestValues [++i] ;
}
for (addr = 0 ; addr < 31 ; ++addr)
ds1302ramWrite (addr, addr) ;
for (addr = 0 ; addr < 31 ; ++addr) if ((got = ds1302ramRead (addr)) != addr) { printf ("DS1302 RAM Failure: Address: %2d, Expected: 0x%02X, Got: 0x%02X\n", addr, addr, got) ; ++errors ; } if (errors == 0) printf ("-- DS1302 RAM TEST: OK\n") ; else printf ("-- DS1302 RAM TEST FAILURE. %d errors.\n", errors) ; return 0 ; } /* * setLinuxClock: * Set the Linux clock from the hardware ********************************************************************************* */ static int setLinuxClock (void) { char dateTime [20] ; char command [64] ; int clock [8] ; printf ("Setting the Linux Clock from the DS1302... ") ; fflush (stdout) ; ds1302clockRead (clock) ; // [MMDDhhmm[[CC]YY][.ss]] sprintf (dateTime, "%02d%02d%02d%02d%02d%02d.%02d", bcdToD (clock [RTC_MONTH], masks [RTC_MONTH]), bcdToD (clock [RTC_DATE], masks [RTC_DATE]), bcdToD (clock [RTC_HOURS], masks [RTC_HOURS]), bcdToD (clock [RTC_MINS], masks [RTC_MINS]), 20, bcdToD (clock [RTC_YEAR], masks [RTC_YEAR]), bcdToD (clock [RTC_SECS], masks [RTC_SECS])) ; sprintf (command, "/bin/date %s", dateTime) ; system (command) ; return 0 ; } /* * setDSclock: * Set the DS1302 block from Linux time ********************************************************************************* */ static int setDSclock (void) { int clock[8] ; time_t now = time(NULL) ; struct tm* t = localtime(&now) ; int sec = t -> tm_sec ;
int min = t -> tm_min ;
int hour = t -> tm_hour ;
int mday = t -> tm_mday ;
int mon = t -> tm_mon ;
int wday = t -> tm_wday ;
int year = t -> tm_year ;
printf ("Setting the clock in the DS1302 from Linux time... ") ;
clock [ 0] = dToBcd (sec) ; // seconds
clock [ 1] = dToBcd (min) ; // mins
clock [ 2] = dToBcd (hour) ; // hours
clock [ 3] = dToBcd (mday) ; // date
clock [ 4] = dToBcd (mon + 1) ; // months 0-11 --> 1-12
clock [ 5] = dToBcd (wday + 1) ; // weekdays (sun 0)
clock [ 6] = dToBcd (year - 100) ; // years
clock [ 7] = 0 ; // W-Protect off
ds1302clockWrite (clock) ;
printf ("OK\n") ;
return 0 ;
}
int main (int argc, char *argv [])
{
int i ;
int clock [8] ;
wiringPiSetup () ;
ds1302setup (0, 1, 2) ;
if (argc == 2)
{
/**/ if (strcmp (argv [1], "-slc") == 0)
return setLinuxClock () ;
else if (strcmp (argv [1], "-sdsc") == 0)
return setDSclock () ;
else if (strcmp (argv [1], "-rtest") == 0)
return ramTest () ;
else
{
printf ("Usage: ds1302 [-slc | -sdsc | -rtest]\n") ;
return EXIT_FAILURE ;
}
}
for (i = 0 ;; ++i)
{
printf ("%5d: ", i) ;
ds1302clockRead (clock) ;
printf (" %2d:%02d:%02d",
bcdToD (clock [2], masks [2]), bcdToD (clock [1], masks [1]), bcdToD (clock [0], masks [0])) ;
printf (" %2d/%02d/%04d",
bcdToD (clock [3], masks [3]), bcdToD (clock [4], masks [4]), bcdToD (clock [6], masks [6]) + 2000) ;
printf ("\n") ;
delay (200) ;
}
return 0 ;
} 六、同步时间 编译ds1302.c

将ds1302.c修改完成后,在examples目录下编译ds1302.c。

make ds1302.c

编译成功则显示:

[CC]
ds1302.c[link]

如果出现错误,按照屏幕提示进行修改,然后再次编译,直到成功编译,会在当前目录下生成可执行文件 ds1302 。

测试电路是否连接正确

将硬件电路连接完毕后开始测试,首先测试电路是否连接正确。在生成的可执行文件ds1302目录下执行测试命令。

sudo ./ds1302 -rtest

如果成功则出现:

DS1302 RAM TEST
-- DS1302 RAM TEST: OK

如果测试失败,则需要检查线路连接是否正确。

将树莓派的当前时间写入 sudo ./ds1302 -sdsc

提示写入成功:

Setting the clock in the DS1302 from Linux time... OK 查看DS1302当前的时间

直接执行ds1302就可以查看DS1302实时时钟模块的当前时间,命令和输出如下:

[email protected]:~/wiringPi/examples $ sudo ./ds1302
0: 15:38:38 12/07/2016
1: 15:38:38 12/07/2016
2: 15:38:38 12/07/2016
3: 15:38:38 12/07/2016
4: 15:38:39 12/07/2016
5: 15:38:39 12/07/2016
6: 15:38:39 12/07/2016
7: 15:38:39 12/07/2016
8: 15:38:39 12/07/2016
9: 15:38:40 12/07/2016
10: 15:38:40 12/07/2016
11: 15:38:40 12/07/2016
12: 15:38:40 12/07/2016
树莓派使用DS1302实现实时时钟功能

可以看到已经将树莓派的系统时间写入到了DS1302实时时钟模块中了,这样在以后树莓派未连接网络时,就可以从DS1302实时时钟中读取时间同步。

将树莓派时间与DS1302时钟模块的时间同步

在执行ds1302文件时,在后面加入参数-slc 可以将DS1302模块的时间写入树莓派。示例如下:

[email protected]:~/wiringPi/examples $ sudo ./ds1302 -slc
Setting the Linux Clock from the DS1302... 2016年 07月 12日 星期二 15:42:29 CST 七、开机同步时间

将树莓派的时间与网络同步后,就可以写入到DS1302中,这样DS1302具有准确的当前时间,就算掉电也不会丢失,在树莓派离线时,可以在每次开机时将DS1302的时间写入到树莓派中,从而使得树莓派实现了实时时钟的功能。

获得可执行文件ds1302的目录

[email protected]:~/wiringPi/examples $ pwd
/home/pi/wiringPi/examples

然后修改 /etc/rc.local 文件,在exit 0前面添加时间同步命令,这样在树莓派开机时就会自动将DS1302的时间同步到树莓派上。添加以下命令:

sudo /home/pi/wiringPi/examples/ds1302 -slc

保存后退出。我们将树莓派断网,然后树莓派关机,过一会开机,使用date命令读取时间,可以看到树莓派的走时和当前时间一致,说明时间自动同步成功!

原创文章,转载请注明:转载自 科技爱好者博客【http://blog.lxx1.com】

本文地址: 树莓派使用DS1302实现实时时钟功能

链接: http://blog.lxx1.com/1995

本文系统(linux)相关术语:linux系统 鸟哥的linux私房菜 linux命令大全 linux操作系统

主题: Linux数据UTUTC博客人民币偏差成本
分页:12
转载请注明
本文标题:树莓派使用DS1302实现实时时钟功能
本站链接:http://www.codesec.net/view/480368.html
分享请点击:


1.凡CodeSecTeam转载的文章,均出自其它媒体或其他官网介绍,目的在于传递更多的信息,并不代表本站赞同其观点和其真实性负责;
2.转载的文章仅代表原创作者观点,与本站无关。其原创性以及文中陈述文字和内容未经本站证实,本站对该文以及其中全部或者部分内容、文字的真实性、完整性、及时性,不作出任何保证或承若;
3.如本站转载稿涉及版权等问题,请作者及时联系本站,我们会及时处理。
登录后可拥有收藏文章、关注作者等权限...
技术大类 技术大类 | 系统(linux) | 评论(0) | 阅读(40)