Real Time Clock in FreeBSD on Raspberry Pi

The Raspberry Pi doesn’t have any ‘Real Time Clock’ (RTC) module, which keeps actual time.

This is not a problem when we connect our system to the Internet.
We can setup NTP (network time protocol) for updating the time from the global ntp servers.
But for stand-alone systems we need to use external RTC module.

I decide to use DS1307 Real Time Clock breakout board kit from the Adafruit.
It is compact and inexpensive.
Adafruit provides good tutorial how to use this kit with Raspberry Pi with Raspbian.

Here is instruction how to use it with FreeBSD on Raspberry Pi.

We can find description for the DS1307 real-time clock here.

The DS1307 serial real-time clock (RTC) is a low-power, full binary-coded decimal (BCD) clock/calendar plus 56 bytes of NV SRAM. The clock/calendar provides seconds, minutes, hours, day, date, month, and year information.
Address and data are transferred serially through an I2C, bidirectional bus.
That’s why we need first to study how to work with I2C in FreeBSD on Raspberry Pi.

To connect the DS1307 module to the Raspberry Pi we need to use 4 pins: +5V, GND, SDA and SCL.

pi-ds1307

SDIM6527e

The 7-bit DS1307 address on I2C bus is 1101000. This is 0x68 in HEX.
The time and calendar information is obtained by reading the appropriate register bytes.

Registers

We need to work with first 7 registers. All we need is to read and to write this 7 registers.

I wrote a small program to work with DS1307 module in FreeBSD.
With option -r it will read the date and time from the module.
With option -s it will write the system’s date and time to the module.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <dev/iicbus/iic.h>
#include <time.h>

#define READ 1
#define WRITE 0

static uint8_t bcd2bin (uint8_t val) { return val - 6 * (val >> 4); }
static uint8_t bin2bcd (uint8_t val) { return val + 6 * (val / 10); }

void usage ( char *pname )  {
  printf( "Usage: %s -r [-a <addr>] [-f <iic-dev>]\n", pname );
  printf( "       %s -s [-a <addr>] [-f <iic-dev>]\n", pname );
  exit(1);
}

char i2c_read ( int fd, uint16_t slave, uint8_t offset, uint8_t *buf )  {
  struct iic_msg msg[2];
  struct iic_rdwr_data rdwr;

  msg[0].slave = slave;
  msg[0].flags = !IIC_M_RD;
  msg[0].len = sizeof( uint8_t );
  msg[0].buf = &offset;

  msg[1].slave = slave;
  msg[1].flags = IIC_M_RD;
  msg[1].len = sizeof( uint8_t );
  msg[1].buf = buf;

  rdwr.msgs = msg;
  rdwr.nmsgs = 2;

  if ( ioctl(fd, I2CRDWR, &rdwr) < 0 )  {
    perror("I2CRDWR");
    return(-1);
  }
  
  return(0);
}

char i2c_write ( int fd, uint16_t slave, uint8_t offset, uint8_t val )  {
  uint8_t buf[2];
  struct iic_msg msg;
  struct iic_rdwr_data rdwr;

  buf[0] = offset;
  buf[1] = val;
  msg.slave = slave;
  msg.flags = !IIC_M_RD;
  msg.len = 2*sizeof( uint8_t );
  msg.buf = buf;

  rdwr.msgs = &msg;
  rdwr.nmsgs = 1;

  if ( ioctl(fd, I2CRDWR, &rdwr) < 0 )  {
    perror("I2CRDWR");
    return(-1);
  }
  
  return(0);
}

int main ( int argc, char *argv[] )  {

  int i, ch, fd, fl;
  int ss, mm, hh, wd, d, m, y;
  char dev[] = "/dev/iic0";
  uint8_t buf;
  uint16_t slave = 0x68;
  time_t tloc;
  struct tm *ptm;

  if ( argc == 1 )  usage(argv[0]);

  while ( (ch = getopt(argc, argv, "hrsa:f:")) != -1 )  {
    switch (ch)  {
      case 'r': fl = READ;
                break;
      case 's': fl = WRITE;
                break;
      case 'a': sscanf( optarg, "%hX", &slave );
                break;
      case 'f': strcpy( dev, optarg );
                break;
      case 'h':
      default:  usage(argv[0]);
    }
  }
  
  if ( (fd = open(dev, O_RDWR)) < 0 )  {
    perror("open");
    exit(-1);
  }

  if ( fl == WRITE )  {

    tloc = time( &tloc );
    ptm = localtime( &tloc );

    i2c_write( fd, slave, 0, bin2bcd(ptm->tm_sec) );
    i2c_write( fd, slave, 1, bin2bcd(ptm->tm_min) );
    i2c_write( fd, slave, 2, bin2bcd(ptm->tm_hour) );
    i2c_write( fd, slave, 3, bin2bcd(ptm->tm_wday+1) );
    i2c_write( fd, slave, 4, bin2bcd(ptm->tm_mday) );
    i2c_write( fd, slave, 5, bin2bcd(ptm->tm_mon+1) );
    i2c_write( fd, slave, 6, bin2bcd(ptm->tm_year-100) );

    exit(0);
  }

  if ( fl == READ )  {

    i2c_read( fd, slave, 0, &buf );
    ss = bcd2bin(buf & 0x7F);

    i2c_read( fd, slave, 1, &buf );
    mm = bcd2bin(buf & 0x7F);

    i2c_read( fd, slave, 2, &buf );
    hh = bcd2bin(buf & 0x3F);

    i2c_read( fd, slave, 3, &buf );
    wd = bcd2bin((buf & 0x07) - 1);

    i2c_read( fd, slave, 4, &buf );
    d = bcd2bin(buf & 0x3F);

    i2c_read( fd, slave, 5, &buf );
    m = bcd2bin(buf & 0x1F);

    i2c_read( fd, slave, 6, &buf );
    y = 2000 + bcd2bin(buf);

    printf("%.2d:%.2d:%.2d %.2d/%.2d/%.4d\n", hh, mm, ss, d, m, y );
    
    exit(0);
  }

  exit(0);
}

Source code is available here: https://github.com/vzaigrin/ds1307.git

rtc2

It works!

Advertisements

14 thoughts on “Real Time Clock in FreeBSD on Raspberry Pi

  1. Yes, I’m using FreeBSD 11

    DS1307(4) FreeBSD Kernel Interfaces Manual DS1307(4)

    NAME
    ds1307 – 64 x 8, serial, i2c real-time clock

    SYNOPSIS
    device iic
    device iicbus
    device ds1307

    DESCRIPTION
    The ds1307 serial real-time clock (RTC) is a low-power, full binary-coded
    decimal (BCD) clock/calendar plus 56 bytes of NV SRAM.

    The ds1307 has a built-in power-sense circuit that detects power failures
    and automatically switches to the backup supply. Timekeeping operation
    continues while the part operates from the backup supply.

    Access to ds1307 settings is made with the sysctl(8) interface:

    dev.ds1307.0.%desc: Maxim DS1307 RTC
    dev.ds1307.0.%driver: ds1307
    dev.ds1307.0.%location: addr=0xd0
    dev.ds1307.0.%pnpinfo: name=rtc compat=maxim,ds1307
    dev.ds1307.0.%parent: iicbus1
    dev.ds1307.0.sqwe: 1
    dev.ds1307.0.sqw_freq: 32768
    dev.ds1307.0.sqw_out: 0

    dev.ds1307.%d.sqwe If set to 1, the SQW pin drives a square-wave
    of dev.ds1307.%d.sqw_freq frequency. If set
    to 0, the output level of SQW pin is
    controlled by dev.ds1307.%d.sqw_out.

    dev.ds1307.%d.sqw_freq Select the frequency of the SQW pin when the
    square-wave output is enabled on
    dev.ds1307.%d.sqwe. It can be set to 1,
    4096, 8192 and 32768.

    dev.ds1307.%d.sqw_out Set the output level of the SQW pin when
    dev.ds1307.%d.sqwe is set to 0.

    Please check the ds1307 datasheet for more details.

    On a device.hints(5) based system, such as MIPS, these values are
    configurable for ds1307:

    hint.ds1307.%d.at The iicbus(4) that the ds1307 is connected to.

    hint.ds1307.%d.addr The i2c address of ds1307.

    On a FDT(4) based system the following properties must be set:

    compatible Must always be set to “dallas,ds1307” or “maxim,ds1307”.

    reg The i2c address of ds1307. The default address for
    ds1307 is 0xd0.

    SEE ALSO
    fdt(4), iic(4), iicbus(4), sysctl(8)

    HISTORY
    The ds1307 driver first appeared in FreeBSD 11.0.

    AUTHORS
    The ds1307 driver and this manual page were written by Luiz Otavio O
    Souza .

    FreeBSD 11.0-CURRENT March 7, 2015 FreeBSD 11.0-CURRENT

    • Yes, I see.
      Raspberry Pi is a FDT(4) based system, so we need to add ds1307 into rpi.dts file to use it.
      Try to add to the file sys/boot/fdt/dts/arm/rpi.dts this strings:

      ds1307 {
      compatible = “maxim,ds1307”;
      reg = 0xd0;
      };

      After that we need to compile rpi.dts:
      # cd /usr/src/sys/tools/fdt
      # setenv MACHINE arm
      # ./make_dtb.sh /usr/src/sys /usr/src/sys/boot/fdt/dts/arm/rpi.dts rpi.dtb

      And to copy the result to the directory /boot/msdos:
      # cp /boot/msdos/rpi.dtb /boot/msdos/rpidtb.old
      # cp rpi.dtb /boot/msdos

      After reboot we should see ds1307 in the list of devices.

      I didn’t do it but think this should work.

    • No success. I have been tried with and without “device ds1307″ in kernel configuration file.

      root@raspberry-pi:~ # ofwdump -a
      Node 0x38:
      Node 0xf4: cpus
      Node 0x120: cpu@0
      Node 0x180: axi
      Node 0x1ec: vc_mem
      Node 0x210: interrupt-controller
      …..
      Node 0x16d4: vchiq
      Node 0x173c: usb
      Node 0x17ec: hub
      Node 0x1844: ethernet
      Node 0x18a8: iicbus1
      Node 0x18b4: ds1307

      And no changes in dmesg, even with “boot -v”

  2. 1) patch for rpi.dts

    > Index: rpi.dts
    > ===================================================================
    > — sys/boot/dst/dts/arm/rpi.dts (revision 279408)
    > +++ sys/boot/dst/dts/arm/rpi.dts (working copy)
    > -292,6 +292,14
    > broadcom,function = “ALT3”;
    > };
    > };
    > +
    > + bsc1 {
    > + rtc {
    > + compatible = “maxim,ds1307”;
    > + reg = ;
    > + };
    > + };
    > +
    > usb {
    > hub {
    > compatible = “usb,hub”, “usb,device”;

    2) add “device ds1307” to kernel config

    3) If you enable the ntpd together with RTC your system will crash, it is
    a known issue (now…), the workaround is:

    sysctl machdep.rtc_save_period=0

    source: http://permalink.gmane.org/gmane.os.freebsd.devel.arm/10991

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s