RTC M41T62 Configuration Help

Trying to set up the RTC I was recommended by @Justin

I have communication and can read/write to the registers, but I do not understand this register map.

Can someone please give me an example of how I would set the time to these registers as well as decode them? My I2C works I just need help with how the buffers should be set up.

If I’m reading that data sheet correctly then to set the time as:

seventeen hours and nineteen minutes (17:19:55) then send

Register Address 0x1 = 0101 0101 (0x55)
Register Address 0x2 = 0001 1001 (0x19)
Register Address 0x3 = 0001 0111 (0x17)

Converting from decimal however you’re going to have to take the byte number for 55 (0x35) and calculate it as:

seconds = 55 / 10 << 4 | 55 mod 10

same for the other numbers. To get it back

seconds = (value >> 4) * 10 + (value & 0xF)

I dunno, i’m just guessing.

1 Like

I got lost on this part:

seconds = 55 / 10 << 4 | 55 mod 10

 private static byte BcdToDec(byte val)
 {
      return (byte)((val / 16 * 10) + (val % 16));
 }
 private static byte DecToBcd(int val)
 {
     return (byte)((val / 10 * 16) + (val % 10));
 }

@Justin can you explain to me how I would store the year 2017 in the register mapped below? I understand it a little better now that I see how you turn one digit into 4 bits but what about the registers that are only 3 bits like 10 seconds in 01h.

If I was storing just 17, would I store DecToBcd(1) in the 10 years bits and DecToBcd(7) in the year bits? But what about the 2000 part? I read something about bits for the centuries too would I use them for the 2000 part or would I just put 200 in the 10 years bits?

Data Sheet

public void SetDate(DateTime date)
        {
            I2CDevice.I2CTransaction[] xaction = new I2CDevice.I2CWriteTransaction[1];
            byte[] sb = new byte[] { 1,
                                   DecToBcd(date.Second),
                                   DecToBcd(date.Minute),
                                   DecToBcd(date.Hour),
                                   DecToBcd((int)date.DayOfWeek),
                                   DecToBcd(date.Day),
                                   DecToBcd(date.Month),
                                   DecToBcd(date.Year - 2000)
                                 };
            xaction[0] = I2CDevice.CreateWriteTransaction(sb);
            Write(xaction, 100);
        }
2 Likes

@Justin thank you so much. What registers would I start reading back from? I keep reading back zeros after I set date the date.

Have you ever used the alarm/interrupt pin on one of these before?

This should return an int correct?

Nope it returns a byte

How are you reading the date?

byte = unsigned int8

@Justin those values I read back are the date in integer form and don’t have to be converted from bytes (if I do it gives me insane numbers in the millions). So I think Mike is on the right track…it must be returning unsigned int8’s. Woudn’t I want to change the return type of BcdToDec in that case to keep it straight?

As i said - how are you reading the date? You need to show code to highlight your issues.

@Mike is teaching you fundamentals, but you still need to dig deeper.

From looking at the datasheet you can see that the data is stored and read in BCD format correct?

Reading the registers like so:

public byte Read(int Address)
    {
        var Data = new byte[1];
        var xActions = new I2CDevice.I2CTransaction[1];
        xActions[0] = I2CDevice.CreateWriteTransaction(new byte[] { (byte)(Address & 0xFF) });
        Thread.Sleep(5);
        if (I2C.Execute(xActions, 1000) == 0)
        {
            Debug.Print("Failed to perform I2C transaction");
        }
    }

Its the code I used for the EPPROM. I think the reading is working correctly, it would probably be better to have Read() take in a hexadecimal instead of an int so that I can directly follow the register map in the data sheet. What do you think?

using System;
using System.Threading;
using Microsoft.SPOT;

namespace RTC_Test
{
    public class Program
    {
        private static M41T62 _rtc;
        public static void Main()
        {
            _rtc = new M41T62();

            if (_rtc.GetDate().Year < 2017)
            {
                _rtc.SetDate(new DateTime(2017, 11, 16, 12, 55, 30));
            }

            for (;;)
            {
                Debug.Print(_rtc.GetDate().ToString());
                Thread.Sleep(1000);
            }
        }
    }
}

using System;
using Microsoft.SPOT.Hardware;

namespace RTC_Test
{
    public enum Register : byte
    {
        M41T62_TNTH_SEC = 0x00,
        M41T62_SEC = 0x01,
        M41T62_MIN = 0x02,
        M41T62_HRS = 0x03,
        M41T62_SQWFQ_DOW = 0x04,
        M41T62_DAY = 0x05,
        M41T62_MON = 0x06,
        M41T62_YRS = 0x07,
        M41T62_CAL = 0x08, // Calibration
        M41T62_WDOG = 0x09, // Watchdog
        M41T62_SQWEN_AMO = 0x0A, // SQW Enable / Alarm Month
        M41T62_ADOM = 0x0B, // Alarm Day of Month
        M41T62_AHRS = 0x0C, // Alarm Hour
        M41T62_AMIN = 0x0D, // Alarm Minutes
        M41T62_ASEC = 0x0E, // Alarm Seconds
        M41T62_FLAGS = 0x0F // Flags: WDF | AF | 0 | 0 | 0 | OF | 0 | 0
    }


    public class M41T62 : AbstractI2CDevice
    {
        private byte[] _result = new byte[8];
        private static int milliseconds;
        private static int seconds;
        private static int minutes;
        private static int hours;
        private static int day;
        private static int month;
        private static int year;


        public M41T62(byte address = 0x68, int clockRate = 400, int timeout = 1000) : base(address, clockRate, timeout)
        {
            Init();
        }

        private void Init()
        {
            
        }
        public DateTime GetDate()
        {
            _result = Read((byte)Register.M41T62_TNTH_SEC, 8);
            seconds = BcdToDec(_result[1]) & 0x7F;
            minutes = BcdToDec(_result[2]);
            hours = BcdToDec(_result[3]) & 0x3F;
            day = BcdToDec(_result[5]);
            month = BcdToDec(_result[6]);
            year = BcdToDec(_result[7]) + 2000;

            try
            {
                return new DateTime(year, month, day, hours, minutes, seconds);
            }
            catch (Exception)
            {
                return DateTime.MinValue;
            }

        }

        public void SetDate(DateTime date)
        {
            I2CDevice.I2CTransaction[] xaction = new I2CDevice.I2CWriteTransaction[1];
            byte[] sb = new byte[] { 1,
                                   DecToBcd(date.Second),
                                   DecToBcd(date.Minute),
                                   DecToBcd(date.Hour),
                                   DecToBcd((int)date.DayOfWeek),
                                   DecToBcd(date.Day),
                                   DecToBcd(date.Month),
                                   DecToBcd(date.Year - 2000)
                                 };
            xaction[0] = I2CDevice.CreateWriteTransaction(sb);
            Write(xaction, 100);

        }
        private static byte BcdToDec(byte val)
        {
            return (byte)((val / 16 * 10) + (val % 16));
        }
        private static byte DecToBcd(int val)
        {
            return (byte)((val / 10 * 16) + (val % 10));
        }
        public override bool Connected()
        {
            throw new NotImplementedException();
        }

        public override byte[] DeviceIdentifier()
        {
            throw new NotImplementedException();
        }
    }
}

Wowzers! Thanks a lot!

@Justin I have quick question about pull ups on my I2C lines + multiple devices. So the G30 data sheet says it needs 2.2k pulls on SDA and SCL. My EEPROM just needs 2.2k on one of them, display needs them on both, and so does this RTC. So would I have a resistor on the G30 pin, then VCC, then another resistor on the other side of VCC to act as the pull up for the slaves? And what about the EEPROM only needing a pull up on one line, will having pull ups on both affect it?

As @Brett as already mentioned, you only need to pull up SDA and SCL lines once not per device.

1 Like

what?

hexadecimal is a REPRESENTATION of a number. INT is a size of number. 0x80050041 is a hex number, but it’s nothing to do with a size. Your Read() can be called like read(156) or Read(0x9C), and it’ll behave exactly the same. It’s really important to get the difference of these fundamental concepts…

What Justin really needed you to do is not to reproduce the raw read function, but show the way you repeatedly call it to get the time values. As you can see from his driver, it’s all about the sequence of calls to the different registers, showing us that would have showed how you needed to modify your code to make it work.

I thought int stood for integer which means whole number and is by default 16 bits. Then there is 8bit and 32bit as well correct? I think I am misunderstanding what you mean by it being a size of a number. I did not realize hexadecimal was interchangeable like that, thank you for clarifying.

I am calling read like this (note - I was not sure of a good way to automatically determine the size of i for this loop so let me know if you think of something):

for(int i=0; i<50;i++){

buffer[i] = clock.Read(i);
time[i] = RTC.BcdToDec(buffer[i]);

}

@Justin As for my pullup question, I understand that you only need one set of pullups for all the devices, but in the G30 datasheet it also says it needs pull ups. Doesn’t that mean the g30 needs a resistor between itself and VCC and the slave devices also need a resistor between Vcc? Here is what I mean (imagine slave devices are off the page to the right):

If there is nothing between G30 and Vcc, does that still work even though data sheet says it needs 2 pull up resistors?

NO.

You need one set of pullups for the signal lines. It has nothing to do with what devices, or how many devices, are attached. The pullup does this - when nothing is actively driving the signal low, it is passively pulled high by that pullup; so a device that reads this (in your case it’s the G30) is just sampling the line and seeing it is high or low, and driving the signal when it’s sending. ONE SET.

An int is 32bit

In the micro world since you are very resource constrained you want to use the correct data types to use as little memory as possible.

Why are you using a loop to read the registers?