一、准备条件
- 树莓派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纽扣电池,作为备份电源,当模块断电时也可以继续走时,保证时间信息不丢失。
模块是这样的:
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的驱动程序可以在wiringPi的examples目录下找到,我这里修改了源文件,源文件在将linux时间设置到DS1302中时使用了UTC时间,这样与我们的CST时间有偏差,所以使用localtime将CST当地时间写入到DS1302中,修改后的文件,大家可以将原来的ds1302.c文件覆盖。
修改后的ds1302.c文件。
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <time.h>
#include <wiringPi.h>
#include <ds1302.h>
// 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
将树莓派的当前时间写入
sudo ./ds1302 -sdsc
Setting the clock in the DS1302 from Linux time... OK
直接执行ds1302就可以查看DS1302实时时钟模块的当前时间,命令和输出如下:
pi@raspberrypi:~/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文件时,在后面加入参数-slc 可以将DS1302模块的时间写入树莓派。示例如下:
pi@raspberrypi:~/wiringPi/examples $ sudo ./ds1302 -slc Setting the Linux Clock from the DS1302... 2016年 07月 12日 星期二 15:42:29 CST
七、开机同步时间
将树莓派的时间与网络同步后,就可以写入到DS1302中,这样DS1302具有准确的当前时间,就算掉电也不会丢失,在树莓派离线时,可以在每次开机时将DS1302的时间写入到树莓派中,从而使得树莓派实现了实时时钟的功能。
获得可执行文件ds1302的目录
pi@raspberrypi:~/wiringPi/examples $ pwd /home/pi/wiringPi/examples
然后修改 /etc/rc.local 文件,在exit 0前面添加时间同步命令,这样在树莓派开机时就会自动将DS1302的时间同步到树莓派上。添加以下命令:
sudo /home/pi/wiringPi/examples/ds1302 -slc
保存后退出。我们将树莓派断网,然后树莓派关机,过一会开机,使用date命令读取时间,可以看到树莓派的走时和当前时间一致,说明时间自动同步成功!