1 RTC介绍 【树莓派——实时时钟模块(RTC)】树莓派为了节约成本以及减小体积,没有板载的实时时钟(real-time clock,RTC),或者叫作硬件时钟。
在某些场合下,如果树莓派无法联网,但是它上面运行的程序又和时间紧密相关,要求系统时间是正确的,在这种情况下,我们就可以为树莓派添加一个外部的RTC,使之重启之后也能保持时间正确。
此实验以DS1302为例
2 DS1302
2.1 模块简介
DS1302模块自带一块CR2032纽扣电池,电池使用时间至少应该有一年以上。
DS1302与单片机之间能简单地采用同步串行的方式进行通信,仅需用到三个口线:
- RST复位
- I/O数据线
- SCLK串行时钟
DS1302工作时功耗很低保持数据和时钟信息时功率小于1mW。
- 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°。
DS1302 | 树莓派 | wiringPi引脚编号 |
---|---|---|
VCC | 3.3V输出 | 3.3V输出 |
GND | Ground(地) | Ground(地) |
CLK | SCLK | 14 |
DAT | SDA0 | 30 |
RST | CEO | 10 |
读寄存器地址:81h~8Dh
写寄存器地址:80h~8Ch
文章图片
2.5 控制字简介
文章图片
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优先的
- 以上博客有什么问题,请在评论区联系博主
推荐阅读
- stm32|基于STM32和freeRTOS智能门锁设计方案
- 日常分享|共享充电宝方案原理,具体部件组成以及主控MUC参数
- #|ARM裸机开发(汇编LED灯实验(I.MX6UL芯片))
- 物联网|从零开始开发物联网项目(8)——云服务器初体验
- 那些卖物联网卡起家的公司,为什么大都撑不过三年()
- 物联网|无线WIFI“信道”