Zápisník experimentátora
Hierarchy: DS1307 Hodiny reálneho času
In this article we will explain the basics of using the integrated circuit DS1307. We will learn to set the time and learn how to read the time on the circuit. We'll explain how to use the entire RAM that the IC provides. Part of the explanation will be a little trip to the depths of the I2C protocol.
I used the following components:
I created three examples:
Before working with the circuit itself, it is advisable to test whether it is visible on the I2C bus. You may have accidentally swapped connecting wires, or you made some other mistake. The i2c_tester program is suitable for testing, so you can see all I2C slave devices on the bus. If you have made the circuit on the breadboard using my instructions, you will only see one device. If you used the finished module, you will see two devices because the module is usually also equipped with an external EEPROM.
The first action you have to do after purchasing the integrated circuit is setting the time. The easiest way is to set the same time as you have on your computer. You could also do this directly by entering values for hours, minutes and seconds, but there is a more convenient way. Using the macros __TIME__
and __DATE__
. These contain the text string with the current time at the time of program compilation. Because the compilation and uploading of the program into the Arduino takes place in sequence, the time in the integrated circuit will be only a few seconds different from the time you have on your computer.
There are two functions in the program to convert time from text to time. Function getDate
gets the date and getTime
gets the time. They both use function sscanf
. Text strings have the same form in each compiler, so do not worry that the program would not work because you have a different language mutation on your computer than English.
When setting this time, you need to remember what time you enter into the integrated circuit. It's so-called local time. So it is a time that is subject to regular changes in the summer when it moves for one hour. But as example to adjust the time it is enough. Later, we'll show you how to deal with summer time.
#include <Wire.h>
#include <TimeLib.h>
#include <DS1307RTC.h>
const char *monthName[12] = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
tmElements_t tm;
bool parse=false;
bool config=false;
void setup() {
Serial.begin(9600);
while (!Serial) ; // wait for Arduino Serial Monitor
delay(200);
Serial.print("Sketch compile time __TIME__='");
Serial.print(__TIME__);
Serial.print("', __DATE__='");
Serial.print(__DATE__);
Serial.println("'");
// get the date and time the compiler was run
if (getDate(__DATE__) && getTime(__TIME__)) {
parse = true;
// and configure the RTC with this info
if (RTC.write(tm)) {
config = true;
}
}
if (parse && config) {
Serial.println("Parse and write OK.");
} else if (parse) {
Serial.println("DS1307 Communication Error");
Serial.println("Please check your circuitry");
} else {
Serial.println("Could not parse info from the compiler");
}
}
void loop() {
if(parse && config) {
time_t t=RTC.get();
Serial.print("RTC.get()=");
Serial.println(t);
}
delay(1000);
}
bool getTime(const char *str)
{
int Hour, Min, Sec;
if (sscanf(str, "%d:%d:%d", &Hour, &Min, &Sec) != 3) return false;
tm.Hour = Hour;
tm.Minute = Min;
tm.Second = Sec;
return true;
}
bool getDate(const char *str)
{
char Month[12];
int Day, Year;
uint8_t monthIndex;
if (sscanf(str, "%s %d %d", Month, &Day, &Year) != 3) return false;
for (monthIndex = 0; monthIndex < 12; monthIndex++) {
if (strcmp(Month, monthName[monthIndex]) == 0) break;
}
if (monthIndex >= 12) return false;
tm.Day = Day;
tm.Month = monthIndex + 1;
tm.Year = CalendarYrToTm(Year);
return true;
}
When we have the time set, we can read it from the integrated circuit. We have two options, in what form we gain time. In this example, the time obtained is parsed into the tmElements_t
structure. The function read
is used for parsing. But we can also get time as time_t
. That's what the function get
is for. It all depends on the further processing of the time in the program, which form we choose. In this case, we were more suited to the parsed form in tmElements_t
. In this example, I added a conversion between both forms using the function makeTime
to see that change is possible at any time.
#include <Wire.h>
#include <TimeLib.h>
#include <DS1307RTC.h>
void setup() {
Serial.begin(9600);
while (!Serial) ; // wait for serial
delay(200);
Serial.println("DS1307RTC Get Time");
Serial.println("-------------------");
}
void loop() {
tmElements_t tm;
time_t t;
if (RTC.read(tm)) {
t=makeTime(tm);
Serial.print("Ok, Time=");
print2digits(tm.Hour);
Serial.write(':');
print2digits(tm.Minute);
Serial.write(':');
print2digits(tm.Second);
Serial.print(", Date(D/M/Y)=");
Serial.print(tm.Day);
Serial.write('/');
Serial.print(tm.Month);
Serial.write('/');
Serial.print(tmYearToCalendar(tm.Year));
Serial.print(", time_t=");
Serial.print(t);
Serial.println();
} else {
if (RTC.chipPresent()) {
Serial.println("The DS1307 is stopped. Please run the set_time");
Serial.println("sketch to initialize the time and begin running.");
Serial.println();
} else {
Serial.println("DS1307 read error! Please check the circuitry.");
Serial.println();
}
delay(9000);
}
delay(1000);
}
void print2digits(int number) {
if (number >= 0 && number < 10) {
Serial.write('0');
}
Serial.print(number);
}
In the last example we will directly read the contents of the RAM in the integrated circuit. Since the library used does not provide any functions for this, we need to add them. To learn a little about object programming, I derived the DS1307RTCex
from the DS1307RTC
class. I added a variable ram
into the class to have the place for the copy of entire RAM in the integrated circuit. I added three functions that will do all the work.
*ptr=ram
- I assigned the beginning of array ram into the variable ptr. In C++ ptr and the ram are pointers so I can write this. The helper variable ptr serves to move over the individual array elements and write the readings to it.*ptr++=Wire.read()
- This entry means that it stores the read value to the position where the pointer (*ptr) is currently showing, and then moves the pointer one position up (ptr++). C++ also allows us to perform such operations in one step.
#include <Wire.h>
#include <TimeLib.h>
#include <DS1307RTC.h>
#define DS1307_CTRL_ID 0x68
class DS1307RTCex : public DS1307RTC {
uint8_t ram[64];
public:
DS1307RTCex() : DS1307RTC() {}
bool readRam() {
uint8_t *ptr = ram;
for (uint8_t x = 0; x <= 4; x++) {
Wire.beginTransmission(DS1307_CTRL_ID);
Wire.write((uint8_t)x * 16);
if (Wire.endTransmission() != 0)
return false;
uint8_t sz = 16;
// block
Wire.requestFrom(DS1307_CTRL_ID, 16);
if (Wire.available() < 16) return false;
while (sz) {
*ptr++ = Wire.read();
sz--;
}
}
return true;
}
void dump() {
uint8_t nRows = 4;
char asciiDump[17];
int value;
for (uint8_t r = 0; r < nRows; r++) {
uint8_t a = 16 * r;
Serial.print(F("0x"));
if ( a < 16 * 16 * 16 ) Serial.print(F("0"));
if ( a < 16 * 16 ) Serial.print(F("0"));
if ( a < 16 ) Serial.print(F("0"));
Serial.print(a, HEX); Serial.print(F(" "));
for (int c = 0; c < 16; c++) {
value = ram[a + c];
if (value < 16)
Serial.print(F("0"));
Serial.print(value, HEX);
Serial.print(c == 7 ? " " : " ");
if ((value >= 0x20) && (value < 0x7f))
asciiDump[c] = value;
else
asciiDump[c] = '.';
}
asciiDump[16] = 0;
Serial.println(asciiDump);
}
}
void demoRam() {
uint8_t data = 0;
for (uint8_t i = 8; i < 64; i++) {
Wire.beginTransmission(DS1307_CTRL_ID);
Wire.write(i);
Wire.write(data++);
Wire.endTransmission();
}
}
};
DS1307RTCex ex;
void setup() {
Serial.begin(9600);
while (!Serial) ; // wait for serial
delay(200);
Serial.println("DS1307RTC Get RAM");
Serial.println("-------------------");
//ex.demoRam();
if (ex.readRam()) {
ex.dump();
}
else
Serial.println("DS1307RTC Communication error");
}
void loop() {
delay(1000);
}
The source code is located on the GitHub server.
26.09.2017