树莓派——实时时钟模块(RTC)

1 RTC介绍 【树莓派——实时时钟模块(RTC)】树莓派为了节约成本以及减小体积,没有板载的实时时钟(real-time clock,RTC),或者叫作硬件时钟。
在某些场合下,如果树莓派无法联网,但是它上面运行的程序又和时间紧密相关,要求系统时间是正确的,在这种情况下,我们就可以为树莓派添加一个外部的RTC,使之重启之后也能保持时间正确。
此实验以DS1302为例
2 DS1302 2.1 模块简介
DS1302模块自带一块CR2032纽扣电池,电池使用时间至少应该有一年以上。
DS1302与单片机之间能简单地采用同步串行的方式进行通信,仅需用到三个口线:

  • RST复位
  • I/O数据线
  • SCLK串行时钟
    DS1302工作时功耗很低保持数据和时钟信息时功率小于1mW。
2.2 模块参数
  • PCB为单面板,尺寸:44mm23mm1.6mm;
  • 带4个定位孔,直径3.1mm;
  • 备用电池为正品天球CR2032,电压3V,电流260mAh,非可充电电池。理论数据保持时间大于10年;
  • 晶振32.768KHz,日本原装进口晶振,匹配电容为6pF,尺寸2*6mm;
  • DS1302为8脚直插国产大芯片,芯片下面有IC座,方便更换及插拔芯片;
  • 模块工作电压兼容3.3V/5V,可与5V及3.3V单片机连接;
  • 工作温度:0°—70°。
2.3 接线
DS1302 树莓派 wiringPi引脚编号
VCC 3.3V输出 3.3V输出
GND Ground(地) Ground(地)
CLK SCLK 14
DAT SDA0 30
RST CEO 10
2.4 寄存器简介
读寄存器地址:81h~8Dh
写寄存器地址:80h~8Ch
树莓派——实时时钟模块(RTC)
文章图片

2.5 控制字简介
树莓派——实时时钟模块(RTC)
文章图片

3 代码块 通过对寄存器的操作来读写时间,代码(参考wiringPi自带示例代码)如下:
#include #include #include #include #include#include//---------------------------------------------------#define RTC_SECS0 #define RTC_MINS1 #define RTC_HOURS2 #define RTC_DATE3 #define RTC_MONTH4 #define RTC_DAY5 #define RTC_YEAR6 #define RTC_WP7 #define RTC_TC8 #define RTC_BM31static int dPin, cPin, sPin; static unsigned int masks[] = { 0x7F, 0x7F, 0x3F, 0x3F, 0x1F, 0x07, 0xFF }; /* 0x7F 01111111 0x3F 00111111 0x1F 00011111 0x07 00000111 0xFF 11111111 *///--------------------------------------------------- void ds1302Setup(const int clockPin, const int dataPin, const int csPin); void ds1302rtcWrite(int reg, unsigned int data); void ds1302regWrite(const int reg, const unsigned int data); void dsShiftOut(unsigned int data); int setLinuxClock(); void ds1302clockRead(int clockdata[8]); int dsShiftIn(); int bcdToD(unsigned int byte, unsigned int mask); int setDSClock(); int dToBcd(int tmp); void ds1302clockWrite(int clock[8]); int main(int argc, char *argv[]) { int i; int clock[8]; wiringPiSetup(); ds1302Setup(14, 30, 10); if (argc == 2) { if (strcmp(argv[1], "-slc") == 0) { return setLinuxClock(); //读取DS1302内的时间来设置Linux的时间 } else if (strcmp(argv[1], "-sdsc") == 0) { return setDSClock(); //读取Linux的时间来设置DS1302的时间 } /*else if (strcmp(argv[1], "-rtest") == 0) { return ramtest(); //进行ram测试,此处因为当时觉得没必要所以没有写,具体可以参考wiringPi示例程序 }*/ else { printf("Usage: ds1302 [-slc | -sdsc | -rtest]\n"); return EXIT_FAILURE; } } i = 0; while (true) { printf("%5d: ", i); ds1302clockRead(clock); //读取DS1302内的时间 //由于DS1302内的数据是BCD编码,所以需要转化为十进制后才能输出 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); i++; } return 0; }void ds1302Setup(const int clockPin, const int dataPin, const int csPin) { dPin = dataPin; //30 cPin = clockPin; //14 sPin = csPin; //10 digitalWrite(dPin, LOW); digitalWrite(cPin, LOW); digitalWrite(sPin, LOW); pinMode(dPin, OUTPUT); pinMode(cPin, OUTPUT); pinMode(sPin, OUTPUT); ds1302rtcWrite(RTC_WP, 0); //关闭写保护 }void ds1302rtcWrite(int reg, unsigned int data) { ds1302regWrite(0x80 | ((reg & 0x1f) << 1), data); //将reg转化为写寄存器的实际地址 }void ds1302regWrite(const int reg, const unsigned int data) { digitalWrite(sPin, HIGH); delayMicroseconds(1); dsShiftOut(reg); dsShiftOut(data); digitalWrite(sPin, LOW); delayMicroseconds(1); }void dsShiftOut(unsigned int data) { pinMode(dPin, OUTPUT); for (int i = 0; i < 8; i++) { digitalWrite(dPin, (data&(1 << i))); delayMicroseconds(1); //LSB优先,从低位开始传输 digitalWrite(cPin, HIGH); delayMicroseconds(1); digitalWrite(cPin, LOW); delayMicroseconds(1); } }int setLinuxClock() { 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); //向Linux输出命令 return 0; }void ds1302clockRead(int clockdata[8]) { int i; unsigned int regVal = 0x81 | ((RTC_BM & 0x1F) << 1); digitalWrite(sPin, HIGH); delayMicroseconds(1); dsShiftOut(regVal); //控制字 for (i = 0; i < 8; i++) { clockdata[i] = dsShiftIn(); //读取时钟数据 //printf("%d %d\n", i, clockdata[i]); } digitalWrite(sPin, LOW); delayMicroseconds(1); }int dsShiftIn() { uint8_t value = https://www.it610.com/article/0; pinMode(dPin, INPUT); delayMicroseconds(1); for (int i = 0; i < 8; i++) { value |= (digitalRead(dPin) << i); digitalWrite(cPin, HIGH); delayMicroseconds(1); digitalWrite(cPin, LOW); delayMicroseconds(1); } return value; }int bcdToD(unsigned int byte, unsigned int mask) { byte &= mask; int b1 = (byte & 0xF0)>> 4; //取高四位作为十进制的十位数 int b2 = byte & 0x0F; //取低四位作为十进制的个位数 return b1 * 10 + b2; }int setDSClock() { struct tm t; //Linux时间结构体 time_t now; int clock[8]; printf("Setting the clock in the DS1302 from Linux time... "); now = time(NULL); //取得当前时间UTC秒数,无时区转换 gmtime_r(&now, &t); //获取当前时间结构,UTC时间,无时区转换 //DS1302内数据采用BCD编码,故需转化 clock[0] = dToBcd(t.tm_sec); // seconds clock[1] = dToBcd(t.tm_min); // mins clock[2] = dToBcd(t.tm_hour); // hours clock[3] = dToBcd(t.tm_mday); // date clock[4] = dToBcd(t.tm_mon + 1); // months 0-11 --> 1-12 clock[5] = dToBcd(t.tm_wday + 1); // weekdays (sun 0) clock[6] = dToBcd(t.tm_year - 100); // years clock[7] = 0; // W-Protect off ds1302clockWrite(clock); printf("OK\n"); return 0; }int dToBcd(int tmp) { return ((tmp / 10) << 4) + tmp % 10; }void ds1302clockWrite(int clock[8]) { unsigned int regVal = 0x80 | ((RTC_BM & 0x1f) << 1); digitalWrite(sPin, HIGH); delayMicroseconds(1); dsShiftOut(regVal); for (int i = 0; i < 8; i++) { dsShiftOut(clock[i]); } digitalWrite(sPin, LOW); delayMicroseconds(1); }

4 注意之处
  • 如果要使用wiringPi自带的那个示例程序ds1302.c文件,编译命令可用:
    gcc -Wall -o ds1302 ds1302.c -lwiringPi -lwiringPiDev
    -o 表示自命名生成的文件
    -l 表示需要连接到哪些库
  • ds1302内的数据是用BCD编码的,读取和存储都需要先进行转化
  • ds1302是LSB优先的
  • 以上博客有什么问题,请在评论区联系博主

    推荐阅读