No announcement yet.

Using Microchip MCP7940n RTC

  • Filter
  • Time
  • Show
Clear All
new posts

  • Using Microchip MCP7940n RTC


    We are attempting to use a MCP7940n RTC with the Diablo16, the actual circuit board is actually a PiFace RTC module for the Raspberri Pi we had lying around. I've searched the forums for implementations of RTCs with the Diablo16 and certainly there are a few, but all of them use the common DS1307 RTC. I figured it should be doable to change the base code found in the old 4D site to make it run with the 7940N, but I haven't managed to make it work so here we are.

    Microchip was kind enough to create a small migration guide from DS1307 to MCP7940n, so I went on and changed the device address from 0xD0 (DS1307) to 0xDE (7940N), unfortunately that is not enough. The migration guide mentions that the oscillator bit has reversed polarity in the MCP, which I assume is the part in the code that reads
    n:=readbyte(0) & 0x7F;                      // ensure CH bit is clear else clock wont run  
    writebyte(0, n);
    Unfortunately, I'm not very electronics-savvy so it all reads very weird to me. If I understood the code correctly, this first reads the address for the seconds and retrieves the value in bit 7 (I assume that's what the '& 0x7F' is there for), then rewrites that same value in the same address? I'm assuming that (according to the migration guide) this value should read '1' indicating that the oscillator is enabled, so I tried hard-writing a 1 but it only changes the 'seconds' value to 1.

    I've made sure (I think?) that the board is properly wired - the pins corresponding to SDA and SCL are wired to the corresponding Diablo16 I²C PAs set in Designer code; ground is wired to one of the Diablo16 GND pins and the board's Vcc pin is wired to the Diablo16's 3V3 out which should be enough, the I²C bus should work as long as voltage don't dips under 1V8 as per MCP7940N's datasheet.

    A few months ago this same RTC board was used on a Raspberri Pi and it worked, so it doesn't seem like it's a hardware problem. We tested the board continuity and it was all right, so I guess I'm skipping something on the conversion from DS1307 to MCP7940 that I can't quite see. Any help is appreciated.

    Below is the code I'm currently testing. This is *not* the one from the old 4D site, it was posted by a forum member that was also having issues with a RTC (in his case, DS1307).
    #platform "uLCD-70DT"
    #inherit "4DGL_16bitColours.fnc"
    #constant SECONDS 0 //seconds register address
    #constant MINUTES 1 //minutes register address
    #constant HOURS 2 //hours register adress
    #constant DATE 4 //date register address
    #constant MONTH 5 //month register address
    #constant YEAR 6 //year register address
    #constant DS1307 0xDE
    #constant WR 1 //Write or Read bit
    // global variables
    var seconds, minutes, hours, date, month, year;
    // Convert binary coded decimal to normal decimal numbers
    func bcdToDec(var val)
        return( (val/16*10) + (val%16) );
    // read a single byte from the required register
    func readbyte(var address)
        var b;
        I2C1_Start(); // Generate Start condition
        I2C1_Write(DS1307); // send slave address
        I2C1_Write(LObyte(address)); // Send register address
        I2C1_Restart(); // Generate Restart
        I2C1_Write(DS1307+WR); // send control byte for Read
        b := I2C1_Read(); // read the byte
        I2C1_Nack(); // finished reading, send Not Ack
        I2C1_Stop(); // Send Stop Condition
        return b; // return the register value
    // write a single byte to the required register
    func writebyte(var register, var value)
        I2C1_Start(); // Generate Start condition
        I2C1_Write(DS1307); // send slave address
        I2C1_Write(register); // select the register
        I2C1_Write(value); // save the value in selected register
        I2C1_Stop(); // finished with bus
    func main()
        var n, k;
        I2C1_Open(I2C_SLOW, PA4, PA5); // (100khz, SCL Pin, SDA Pin)
        n := readbyte(0) & 0x7F; // ensure CH bit is clear else clock wont run
        writebyte(0, n);
        // print time and date every second
            seconds := bcdToDec(readbyte(SECONDS)); // read xloxk xhip, convert to
            minutes := bcdToDec(readbyte(MINUTES));
            hours := bcdToDec(readbyte(HOURS));
            date := bcdToDec(readbyte(DATE));
            month := bcdToDec(readbyte(MONTH));
            year := bcdToDec(readbyte(YEAR)) + 2000;
            gfx_MoveTo(0, 0);
            print ("Time: ", hours, ":", minutes, ":", seconds, "\n");
            print ("Date: ", date, ":", month, ":", year, "\n");
            print ("\nCycles: ", ++k);

  • #2

    Because the 1 you are sending to register 0 is setting the seconds to 1 the RTC appears to be working but as bit 7 is 1 on the Microchip the value sent back is different.

    This command on the DS1307

    n:=readbyte(0) & 0x7F; // ensure CH bit is clear else clock wont run
    writebyte(0, n);

    gets bits 0 to 6 and then sends it back effectively making bit 7 a zero and then the DS1307 will run. We need to change this so that bit 7 becomes a 1

    n:=readbyte(0) | 0x80; // ensure CH bit is clear else clock wont run
    writebyte(0, n);

    This will make bit 7 a 1 and the clock should then run.

    I hope this helps

    Best regards



    • #3

      Thanks for the swift reply! That did make the clock start indeed, though a bit oddly: The seconds are offset by 80, that is, it will go from 80 to 139, then down to 80 while adding 1 minute to the counter.

      Again, thanks for the swift reply.

      Additionally, I've unplugged the Diablo16 from the computer effectively shutting it down. On power up again, the time was reset to 0. Checking the migration guide yet again, they comment that address 3 (for day of the week), bit 3 is used to enable/disable battery backup while in the DS1307 is set to 'don't care'. I assume I'll have to set this bit to 1 in order to make it retain the time and date (which I don't know how to do).
      Nevermind this, checking what you had done to set the oscillator bit to 1 I used
      n := readbyte(3) | 0x8;
      writebyte(3, n);
      to set the third bit in the Day of the Week register to 1. Now it does keep the time between power downs.
      Last edited by BlauLas; 18 October 2017, 08:02 PM.


      • #4
        When you read the seconds you will have to read it like this,

        n:=readbyte(0) & 0x7F;

        So that the start bit is not included.



        • #5

          That did work like a charm. Thank you very much!


          • #6

            I'm a bit bamboozled now. To put everyone in situation: the RTC does indeed work so I went ahead and implemented it in the main program. Right now the time (HH:MM) is printed on the lower left corner of the screen (Landscape mode) when loading each form. Then, ideally, it would only need to be updated every minute since that's the minimum fraction of time that will be displayed. Right now, there's a function that's being called every frame that should compare the last minute that the clock was updated against the current minute, and update it if its different:
            #constant MINUTES    1    // Address for Minutes register
            var i2cTrigger;     // Declared globally
            func CheckRTC()
                // Check if time has to be updated
                if(i2cTrigger != ReadByte(MINUTES))
                    i2cTrigger := ReadByte(MINUTES);
            PrintRTC() is a function that sets the font, font color, font size, clears the previous on-screen time via gfx_Rectangle then prints the current time in position. Due to the size of the font, drawing the rectangle and printing the time takes long enough that it's noticeable and causes a noticeable flicker on the time when it updates. It wouldn't be an issue if it did indeed happen when a minute had passed, but it attempts to update every single frame.

            I threw in a print to check what's the value of i2cTrigger and ReadByte(MINUTES) when the condition is (supposedly) met and it showed different values both before and after assigning i2cTrigger. I'm not quite sure what's going on, I assumed the MINUTES register only changed when a minute had indeed passed?

            Thanks for your time.

            EDIT: Nevermind the issue, it's fixed. GND wire was acting up it seems.
            Last edited by BlauLas; 23 October 2017, 06:15 PM.


            • #7

              Glad that you managed to solve the constant update. Is it really necessary to clear the previous time with a rectangle. Have you tried just overwriting the previous time.

              Best regards



              • #8

                Sadly, due to user interface design the fonts have to use transparent background everywhere else, so overwriting the old time with the new time would just cause a mess. I suppose I could turn transparency off then on again after the time has updated, but I'm unsure if that would be more efficient than the way it's done right now. When it's all set up properly I can run a test to see what's best.

                Thanks for your input!


                • #9

                  You could actually use the RTC to set the clock on the display, then use the display's clock to update your time display, it may save time rather than reading i2c constantly. You will find information on clock functions on page 285 of this datasheet

                  Best regards



                  • #10

                    As of now the application seems to run correctly polling the I²C channel constantly, but I'll keep the timer option in mind if there are issues further down the road.

                    About the display clock update, I ended up disabling transparency before the update so the new values overwrite the old ones, then reenabling transparency.

                    Now I'm having yet another problem that is quite surprising, really. In order to cut down costs, the boss came to me with a DS1307 based RTC board since it's cheaper than the PiFace RTC. I thought it would be quite easy to de-convert the code from the MCP7940N that's up and running right now back to the DS1307 version, but it's not working and I can't seem to find the problem and unfortunately I don't have another DS1307 based board or chip nearby to test if it's a hardware failure.

                    I'm adjunting a .txt file with the test application I'm using to check the DS1307, both for Read and Write. I ended up ignoring the unused bits even though they should read 0 to make sure that's not the problem. I also tried the codes that have been posted here by other users regarding the DS1307 before (which supposedly work) and didn't work either. Any help is appreciated.

                    Attached Files