Wednesday, May 3, 2017

Programming the MeinEnigma - Real-Time Clock


For our next programming example we'll look at the real-time clock that is present on the MeinEnigma.

A DS3231 real-time clock module allows the MeinEnigma to implement the clock feature that displays the current time. The module is controlled by the Arduino using an I2C interface and maintains the time even when the unit is powered off using a CR2032 coin battery mounted on the module.

You can refer to the datasheet for the device to understand how to program it. It uses an I2C interface, one of several I2C devices present on the MeinEnigma. One of the advantages of I2C is that it allows controlling multiple devices using only two wires (and therefore two Arduino pins). It is well supported by the Arduino Wire library.

To handle reading from and writing to the Real-time clock, I've implemented functions (actually, I've taken the code almost as is from the MeinEnigma source) called i2c_read() and i2c_write(). Writing for example, involves sending the chip's I2C address and then one or more
bytes of data. Reading from a register in the RTC involves sending the address, followed by sending the address of the register to be read, and then reading the returned data from the register. The Wire libraries take care of the details of the I2C protocol. You can read about I2C and Arduino programming in any one of a number of documents and tutorials.

The DS3231 device registers store the date and time, including year, month, day, day of week, hour, minute, and second. One wrinkle is that it stores these values in binary-coded decimal (BCD) format. Compared to binary, BCD makes it easy to extract the individual decimal digits for operations like displaying them on an LED, without having to perform division or multiplication operations. Those operations can be slow and/or complex to perform a low-end microcontroller like a PIC, although an Arduino has no trouble. In some cases, such as a larger program we will look at later, you may choose to convert the values to binary format before working with them.

The example program continuously reads the current date and time from the RTC chip and displays it in readable format over the Arduino's serial interface. For fun, I made it print the month and day of the week as words rather than numbers.

Here is some typical output:

Real-time Clock Demo
18:37:55 02-Mar-2017 Tue
18:37:56 02-Mar-2017 Tue
18:37:57 02-Mar-2017 Tue
18:37:58 02-Mar-2017 Tue

As the code is well commented, I'll just present the source code here for you to review. The latest version can be found on github here.

/*
  MeinEnigma Example

  Demonstrates reading DS3231 real-time clock over I2C.
  See results through Arduino IDE serial monitor set to 9600 bps.

  Jeff Tranter <tranter@pobox.com.

*/

#include

// I2C address of DS3231 real-time clock.
#define DS3231_ADDR 0x68

// Lookup tables for names of the days of the week and months of the year.
const char *dayName[] = { "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" };
const char *monthName[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };

// Read a byte from a specific I2C address. Send one byte (address to read) and read a byte.
uint8_t i2c_read(uint8_t unitaddr, uint8_t addr) {
  i2c_write(unitaddr, addr);
  Wire.requestFrom(unitaddr, (uint8_t)1);
  return Wire.read();    // Read one byte
}

// Write a single byte to I2C.
uint8_t i2c_write(uint8_t unitaddr, uint8_t val) {
  Wire.beginTransmission(unitaddr);
  Wire.write(val);
  return Wire.endTransmission();
}

// Print a BCD number to the serial port with leading zero added if it
// is less than 0x10, e.g. prints "01" for 1, "11" for 11.
void printLeadingZero(int val)
{
  if (val < 0x10) {
    Serial.print("0");
  }
  Serial.print(val, HEX);
}

// Get time and date from RTC and print it out the serial port.
// Note that these could be wrong, especially day of the week, if you
// have not set the real-time clock.
void printTime() {
  uint8_t hour, minute, second;
  uint8_t year, month, day, dow;

  year   = i2c_read(DS3231_ADDR, 6);
  month  = i2c_read(DS3231_ADDR, 5);
  day    = i2c_read(DS3231_ADDR, 4);
  dow    = i2c_read(DS3231_ADDR, 3);
  hour   = i2c_read(DS3231_ADDR, 2 );
  minute = i2c_read(DS3231_ADDR, 1);
  second = i2c_read(DS3231_ADDR, 0);

  printLeadingZero(hour);
  Serial.print(":");
  printLeadingZero(minute);
  Serial.print(":");
  printLeadingZero(second);
  Serial.print(" ");
  printLeadingZero(day);
  Serial.print("-");
  Serial.print(monthName[month - 1]);
  Serial.print("-");
  Serial.print("20"); // RTC does not store century.
  printLeadingZero(year);
  Serial.print(" ");
  Serial.println(dayName[dow - 1]);
}

void setup() {
  Serial.begin(9600); // Initialize serial port.
  Wire.begin();       // Initialize the Wire library.
  Serial.println("Real-time Clock Demo");
}

void loop() {
  printTime(); // Display the current time.
  delay(1000); // Wait one second.
}

References


  1. https://www.arduino.cc/en/reference/wire
  2. https://www.maximintegrated.com/en/products/digital/real-time-clocks/DS3231.html
  3. https://github.com/jefftranter/meinEnigma/tree/master/Examples/RTC

No comments: