This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

MSP430F5529: I2C interface with DS3231 Real Time Clock using MSP driverlib

Part Number: MSP430F5529

Hi,

I am having some trouble with communicating with the DS3231 RTC chip. Here is my code below.

I am using the UCB1SCL and UCB1SDA pins on the Launchpad.

The address of the real time clock is 0xD0, that includes the R/W bit. I'm not sure if I should be shifting this to the left and send only the 7 bit address.

Hence I commented out i2cAdd = i2cAdd >> 1. Anyway, I tried sending both 8 bit(including R/W) and 7 bit but still can't talk to the chip. Am I missing anything here?

By the way, I noticed that the code gets stuck in the function WriteRTCByte when the function "USCI_B_I2C_masterSendMultiByteNext(USCI_B1_BASE, data);" is called

Please see code below

Thanks. 

AJ

void InitializeRTC(void)
{
    //Assign I2C pins to USCI_B1
    GPIO_setAsPeripheralModuleFunctionInputPin(
            GPIO_PORT_P4, GPIO_PIN1 + GPIO_PIN2);

    //Initialize Master
    USCI_B_I2C_initMasterParam param = {0};
    param.selectClockSource = USCI_B_I2C_CLOCKSOURCE_SMCLK;
    param.i2cClk = UCS_getSMCLK();
    param.dataRate = USCI_B_I2C_SET_DATA_RATE_100KBPS;
    USCI_B_I2C_initMaster(USCI_B1_BASE, &param);

    //Enable I2C Module to start operations
    USCI_B_I2C_enable(USCI_B1_BASE);
}//end void InitializeRTC(void)
/***********************************************/
void WriteRTCByte(uint8_t i2cAdd,  uint8_t rtcAdd, uint8_t data)
{
    //i2cAdd = i2cAdd >> 1;
    //Specify slave address
    USCI_B_I2C_setSlaveAddress(USCI_B1_BASE, i2cAdd);
    //Set Transmit mode
    USCI_B_I2C_setMode(USCI_B1_BASE, USCI_B_I2C_TRANSMIT_MODE);

    USCI_B_I2C_masterSendMultiByteStart(USCI_B1_BASE, rtcAdd);
    USCI_B_I2C_masterSendMultiByteNext(USCI_B1_BASE, data);
    USCI_B_I2C_masterSendMultiByteStop(USCI_B1_BASE);
    while(USCI_B_I2C_isBusBusy(USCI_B1_BASE));
}//end void WriteRTCByte(uint8_t i2cAdd,  uint8_t rtcAdd, uint8_t data)
/***********************************************/
uint8_t ReadRTCByte(uint8_t i2cAdd, uint8_t rtcAdd)
{
    uint8_t i2cByte;

    //i2cAdd = i2cAdd >> 1;
    //Specify slave address
    USCI_B_I2C_setSlaveAddress(USCI_B1_BASE, i2cAdd);
    //Set Transmit mode
    USCI_B_I2C_setMode(USCI_B1_BASE, USCI_B_I2C_TRANSMIT_MODE);
    USCI_B_I2C_masterSendMultiByteStart(USCI_B1_BASE, rtcAdd);
    while(USCI_B_I2C_isBusBusy(USCI_B1_BASE));

    //Specify slave address
    USCI_B_I2C_setSlaveAddress(USCI_B1_BASE, (i2cAdd | 0x01));
    //Set receive mode
    USCI_B_I2C_setMode(USCI_B1_BASE, USCI_B_I2C_RECEIVE_MODE);
    USCI_B_I2C_masterReceiveSingleStart(USCI_B1_BASE);
    i2cByte = USCI_B_I2C_masterReceiveSingle(USCI_B1_BASE);
    while(USCI_B_I2C_isBusBusy(USCI_B1_BASE ));

    return(i2cByte);
}//end uint8_t ReadRTCByte(uint8_t i2cAdd, uint8_t rtcAdd)
/***********************************************/

  • Correction on my previous post... I meant shifting the address to the right..
  • Hi AJ,

    First I'd like to note that the MSP430F5529 has a built in RTC peripheral. Can you elaborate on the need for an external RTC?

    When communicating using I2C on an MSP430, you do not need to shift the slave address before placing it in the slave address registers. For example, if the RTC address is 0xD0, you would simply call:

    USCI_B_I2C_setSlaveAddress(USCI_B1_BASE, 0xD0);

    If you look into the source code for the USCI_B_I2C_masterSendMultiByteNext() function, you'll see that it first polls the TXIFG flag if not using interrupts:

        //If interrupts are not used, poll for flags
        if (!(HWREG8(baseAddress + OFS_UCBxIE) & UCTXIE)){
            //Poll for transmit interrupt flag.
            while (!(HWREG8(baseAddress + OFS_UCBxIFG) & UCTXIFG)) ;
        }

    This is where you're probably getting stuck in your code. The TXIFG flag is never getting set which means the data that was placed in the TX buffer during the multi-byte start function never got sent out.

    Have you probed the I2C lines with a logic analyzer or oscilloscope to view what data is or isn't being sent? Also, what value pull-ups do you have on your I2C lines?

    Finally, please read through the General and I2C sections of Solutions to Common eUSCI and USCI Serial Communication Issues on MSP430 MCUs for information regarding the most common I2C communication issues. 

    Best regards, 
    Caleb Overbay

  • Caleb Overbay said:

    Hi AJ,

    First I'd like to note that the MSP430F5529 has a built in RTC peripheral. Can you elaborate on the need for an external RTC?

    When communicating using I2C on an MSP430, you do not need to shift the slave address before placing it in the slave address registers. For example, if the RTC address is 0xD0, you would simply call:

    USCI_B_I2C_setSlaveAddress(USCI_B1_BASE, 0xD0);

    If you look into the source code for the USCI_B_I2C_masterSendMultiByteNext() function, you'll see that it first polls the TXIFG flag if not using interrupts:

        //If interrupts are not used, poll for flags
        if (!(HWREG8(baseAddress + OFS_UCBxIE) & UCTXIE)){
            //Poll for transmit interrupt flag.
            while (!(HWREG8(baseAddress + OFS_UCBxIFG) & UCTXIFG)) ;
        }

    This is where you're probably getting stuck in your code. The TXIFG flag is never getting set which means the data that was placed in the TX buffer during the multi-byte start function never got sent out.

    Have you probed the I2C lines with a logic analyzer or oscilloscope to view what data is or isn't being sent? Also, what value pull-ups do you have on your I2C lines?

    Finally, please read through the General and I2C sections of Solutions to Common eUSCI and USCI Serial Communication Issues on MSP430 MCUs for information regarding the most common I2C communication issues. 

    Best regards, 
    Caleb Overbay

    Hi Caleb,

    Thank you for your response. I'm migrating the design from another MCU to this one and I figured I'd just keep the hardware the same just to minimize changes in the main program. This is just for testing anyways and also a good way for me to learn MSP's I2C bus. I did notice the built in RTC and I plan to use that eventually for future projects.

    Just to make sure we are on the same page, the address 0XD0 in the datasheet includes the R/W bit at the LSB. In this case, R/W is 0. When you said no shifting is necessary, I just want to make sure you were aware of this. 

    I was finally able to get through the flag check and I am no longer stuck. However, now I'm reading incorrect bits.

    Here is what I did to get pass the flag check

    1) The RTC is a breakout board I got from Adafruit and it already had 10K resistors built in. I added 7.5K in parallel to each 10K on my proto board to change the pull up resistance to 4.3K each line. My I2C speed is set to 100kbps

    2) I shifted the address 1 bit to the write... so instead of 0xD0, it became 0x68. Without this shifting, I'd still get stuck.  ---  Does this mean the I2C_setMode function takes care of the R/W bit?

    Here are additional things that I am not sure about:

    1) I noticed that in all the examples, the function USCI_B_I2C_enable(USCI_B1_BASE) gets called after calling USCI_B_I2C_setSlaveAddress(USCI_B1_BASE, i2cAdd); and 

    USCI_B_I2C_setMode(USCI_B1_BASE, USCI_B_I2C_TRANSMIT_MODE); first.

    In my case, I called the enable function first under the initialization and then called the other two inside the write and read routines. 

    But If the order below is necessary

    USCI_B_I2C_setSlaveAddress(USCI_B1_BASE, i2cAdd);

    USCI_B_I2C_setMode(USCI_B1_BASE, USCI_B_I2C_TRANSMIT_MODE);

    USCI_B_I2C_enable(USCI_B1_BASE);

    that means I'd have to disable I2C after every write and read right? And it gets even complicated when I read a byte because reading involves two operations - write address then read the byte. How will I handle this here?

    2) Should I put a delay in between send byte and receive byte API functions and a delay after I2C_enable function? I tried doing this but I was still reading the wrong bits.

    3)In my ReadRTCByte function, did I split the two operations correctly? Was it right for me to use "USCI_B_I2C_masterSendMultiByteStart(USCI_B1_BASE, rtcAdd);" to send the RTC address or should have I used single write instead? My understanding is that after writing the address, I should not stop because I'll be reading in the data and the single write function implements a stop. I'm not really sure about this. Also, should I eliminate the I2C is busy check right after the write operation in my ReadRTCByte function? 

    Thanks.

    AJ 

  • Just additional info, my SMCLK is set to 24MHZ just like my MCLK = 24MHZ. I used the FLL and I also made sure the get_SMCLK() returned 24000000
  • Hi Caleb,

    I finally got it to work. But the solution did not make sense. In the ReadRTCByte function, I replaced USCI_B_I2C_masterSendMultiByteStart(USCI_B1_BASE, rtcAdd); with USCI_B_I2C_masterSendSingleByte(USCI_B1_BASE, rtcAdd); and I got rid of the "if busy wait" after it. I get the if busy but what I don't get is that the function USCI_B_I2C_masterSendSingleByte(USCI_B1_BASE, rtcAdd) sends a stop. According to the RTC datasheet, after sending the address of the register you want to read, next is a repeated start and then you read the data and send a NAK then a stop. The stop comes after the NAK. According to the driverlib user's manual, the USCI_B_I2C_masterSendSingleByte(USCI_B1_BASE, rtcAdd) sends a stop. This bugs me a lot. Nonetheless I got it to work reading the registers consecutively.

    I was still able to read a single register correctly with this function USCI_B_I2C_masterSendMultiByteStart(USCI_B1_BASE, rtcAdd) in place. But I would have to call the WriteRTCByte function before calling ReadRTCByte in order to read another register correctly. But since I am reading time from the RTC, I would need to read the registers consecutively and I was able to do this only by doing what I did as explained above. I suppose I could just read the registers consecutively in a stream and I think that would work using the USCI_B_I2C_masterSendMultiByteStart(USCI_B1_BASE, rtcAdd) function.

    void InitializeRTC(void)
    {
    //Assign I2C pins to USCI_B1
    GPIO_setAsPeripheralModuleFunctionInputPin(
    GPIO_PORT_P4, GPIO_PIN1 + GPIO_PIN2);
    
    //Initialize Master
    USCI_B_I2C_initMasterParam param = {0};
    param.selectClockSource = USCI_B_I2C_CLOCKSOURCE_SMCLK;
    param.i2cClk = UCS_getSMCLK();
    param.dataRate = USCI_B_I2C_SET_DATA_RATE_100KBPS;
    USCI_B_I2C_initMaster(USCI_B1_BASE, &param);
    
    USCI_B_I2C_setSlaveAddress(USCI_B1_BASE, 0x68);
    //Set Transmit mode
    USCI_B_I2C_setMode(USCI_B1_BASE, USCI_B_I2C_TRANSMIT_MODE);
    USCI_B_I2C_enable(USCI_B1_BASE);
    __delay_cycles(50);
    //Enable I2C Module to start operations
    WriteRTCByte(0x68, 0x0F, 0x00);
    WriteRTCByte(0x68, 0x0E, 0x04);
    }//end void InitializeRTC(void)
    /***********************************************/
    void WriteRTCByte(uint8_t i2cAdd, uint8_t rtcAdd, uint8_t data)
    {
    USCI_B_I2C_setSlaveAddress(USCI_B1_BASE, i2cAdd);
    //Set Transmit mode
    USCI_B_I2C_setMode(USCI_B1_BASE, USCI_B_I2C_TRANSMIT_MODE);
    __delay_cycles(50);
    USCI_B_I2C_masterSendMultiByteStart(USCI_B1_BASE, rtcAdd);
    USCI_B_I2C_masterSendMultiByteNext(USCI_B1_BASE, data);
    USCI_B_I2C_masterSendMultiByteStop(USCI_B1_BASE);
    while(USCI_B_I2C_isBusBusy(USCI_B1_BASE));
    }//end void WriteRTCByte(uint8_t i2cAdd, uint8_t rtcAdd, uint8_t data)
    /***********************************************/
    uint8_t ReadRTCByte(uint8_t i2cAdd, uint8_t rtcAdd)
    {
    uint8_t i2cByte;
    
    //Specify slave address
    USCI_B_I2C_setSlaveAddress(USCI_B1_BASE, i2cAdd);
    //Set Transmit mode
    USCI_B_I2C_setMode(USCI_B1_BASE, USCI_B_I2C_TRANSMIT_MODE);
    __delay_cycles(50);
    USCI_B_I2C_masterSendSingleByte(USCI_B1_BASE, rtcAdd);
    //Specify slave address
    USCI_B_I2C_setSlaveAddress(USCI_B1_BASE, i2cAdd);
    //Set receive mode
    USCI_B_I2C_setMode(USCI_B1_BASE, USCI_B_I2C_RECEIVE_MODE);
    USCI_B_I2C_masterReceiveSingleStart(USCI_B1_BASE);
    i2cByte = USCI_B_I2C_masterReceiveSingle(USCI_B1_BASE);
    while(USCI_B_I2C_isBusBusy(USCI_B1_BASE));
    return(i2cByte);
    }//end uint8_t ReadRTCByte(uint8_t i2cAdd, uint8_t rtcAdd)
    /***********************************************/

  • Hi AJ, 

    AJ_ee said:
    but what I don't get is that the function USCI_B_I2C_masterSendSingleByte(USCI_B1_BASE, rtcAdd) sends a stop.

    The SendSingleByte function is meant to only send one byte to the slave. That's why it sends a stop at the end because there is no expectation to send any further information to the slave. Instead you should use the SendMultiByteStart function to start the read operation since it is a multi-byte transaction. I've reworked your code below to the ideal way to perform a single read and single write. This code is untested so please try it out and let me know if there needs to be any changes made:

    void InitializeRTC(void)
    {
        //Assign I2C pins to USCI_B1
        GPIO_setAsPeripheralModuleFunctionInputPin(
        GPIO_PORT_P4, GPIO_PIN1 + GPIO_PIN2);
        
        //Initialize Master
        USCI_B_I2C_initMasterParam param = {0};
        param.selectClockSource = USCI_B_I2C_CLOCKSOURCE_SMCLK;
        param.i2cClk = UCS_getSMCLK();
        param.dataRate = USCI_B_I2C_SET_DATA_RATE_100KBPS;
        USCI_B_I2C_initMaster(USCI_B1_BASE, &param);
    
        USCI_B_I2C_enable(USCI_B1_BASE);
        __delay_cycles(50);
        //Enable I2C Module to start operations
        WriteRTCByte(0x68, 0x0F, 0x00);
        WriteRTCByte(0x68, 0x0E, 0x04);
    }
    
    // Reading from a single RTC register
    uint8_t ReadRTCByte(uint8_t i2cAdd, uint8_t rtcAdd)
    {
        uint8_t i2cByte;
    
        USCI_B_I2C_setSlaveAddress(USCI_B1_BASE, i2cAdd);          // Set slave address
        USCI_B_I2C_masterSendMultiByteStart(USCI_B1_BASE, rtcAdd); // Sets to transmit mode, sends start, sends byte
        USCI_B_I2C_masterReceiveSingleStart(USCI_B1_BASE);         // Sets to receive mode, sends repeated start, receives byte, NAK?, sends stop
        i2cByte = USCI_B_I2C_masterReceiveSingle(USCI_B1_BASE);    // Reads from I2C RX buffer
        return(i2cByte);
    }
    
    // Write to a single RTC register
    void WriteRTCByte(uint8_t i2cAdd, uint8_t rtcAdd, uint8_t data)
    {
        USCI_B_I2C_setSlaveAddress(USCI_B1_BASE, i2cAdd);           // Set slave address
        USCI_B_I2C_masterSendMultiByteStart(USCI_B1_BASE, rtcAdd);  // Sets to transmit mode, sends start, sends byte
        USCI_B_I2C_masterSendMultiByteNext(USCI_B1_BASE, data);     // Sends byte
        USCI_B_I2C_masterSendMultiByteStop(USCI_B1_BASE);           // Sends stop
        while(USCI_B_I2C_isBusBusy(USCI_B1_BASE));                  // Wait for stop to be sent
    }

    AJ_ee said:
    I shifted the address 1 bit to the write... so instead of 0xD0, it became 0x68. Without this shifting, I'd still get stuck.  ---  Does this mean the I2C_setMode function takes care of the R/W bit?

    The I2C_setMode functions takes care of the read/write bit. There is no need to include it in the slave device address.

    AJ_ee said:
    that means I'd have to disable I2C after every write and read right? And it gets even complicated when I read a byte because reading involves two operations - write address then read the byte. How will I handle this here?

    You do not have to disable I2C to change the slave address or the transmit mode. 

    Best regards, 

    Caleb Overbay

  • Hi Caleb,

    Thanks for your response. Your code makes complete sense and I tried it out but it did not work. THis is the response I got from a quick test reading the RTC every second 5 consecutive times. Bottom line is when I'm reading the RTC bytes consecutively, I don't read the right bytes. This is exactly the same problem I have been getting before I placed the singlewrite function in RTC read. There must be a bug somewhere in the API

    Set RTC time in YYMMDDhhmmss: 111111111111
    Read RTC time: 12/11/11  02:11:11
    Read RTC time: 11/00/00  00:00:00
    Read RTC time: 00/00/04  00:00:1;
    Read RTC time: <0/15/11  11:02:11
    Read RTC time: 11/11/00  00:00:00
    Done

    Using the code I last wrote previously, I was able to make it work but how I did it did not make any sense since the singlewrite sends a stop.

    Set RTC time in YYMMDDhhmmss: 111111111111
    Read RTC time: 11/11/11  11:11:12
    Read RTC time: 11/11/11  11:11:13
    Read RTC time: 11/11/11  11:11:14
    Read RTC time: 11/11/11  11:11:15
    Read RTC time: 11/11/11  11:11:16
    Done

  • correction I meant the masterSendSingleByte function, not singlewrite..
  • Hi AJ,

    At this point I need to see the activity on the I2C lines. These functions should be operating as we expect them to. Can you probe the line with a logic analyzer or oscilloscope so we can verify that the correct or incorrect data is being transferred. This will greatly help with the debug of the code.

    Best regards,
    Caleb Overbay
  • Hi Caleb,

    Unfortunately, I don't have a logic analyzer at home. This is just a personal side project so all the scope and stuff I use are at work. Anyways. I'll probably get a logic analyzer eventually. I could give you the part though if you are interested in wiring it up with the MSP4305529 Launchpad. It's the DS3231 RTC break out board from Adafruit.
  • When I get a chance I'll try to scope the signals at work.

    Thanks a lot for spending time on this.

    Regards,
    Albert
  • Caleb,

    I forgot to mention, I did a quick experiment. First I did it using my code with the masterSingleSend
    WriteRTCByte(0x0E, 0x04);
    ReadRTCByte(0x0E, 0x04);
    ReadRTCByte(0x0E, 0x04);
    ReadRTCByte(0x0E, 0x04);
    ReadRTCByte(0x0E, 0x04);

    The output was 04 04 04 04 04 --> which is what I would expect since I read the same address 5 times. This is with the masterSingleSend function

    When I replaced ReadRTCByte with your code and did
    WriteRTCByte(0x0E, 0x04);
    ReadRTCByte(0x0E, 0x04);
    ReadRTCByte(0x0E, 0x04);
    ReadRTCByte(0x0E, 0x04);
    ReadRTCByte(0x0E, 0x04);

    This is what came out
    04 04 00 0\ garbage garbage garbage..

    Then I tried doing this instead, still using the code you sent me
    WriteRTCByte(0x0E, 0x04);
    ReadRTCByte(0x0E, 0x04);
    WriteRTCByte(0x0E, 0x04);
    ReadRTCByte(0x0E, 0x04);
    WriteRTCByte(0x0E, 0x04);
    ReadRTCByte(0x0E, 0x04);
    WriteRTCByte(0x0E, 0x04);
    ReadRTCByte(0x0E, 0x04);
    WriteRTCByte(0x0E, 0x04);
    ReadRTCByte(0x0E, 0x04);

    The result was 04 04 04 04 04...

    It looks like after doing a write, the read works but the functions need to be called alternately. But you get garbage when you do ReadRTC consecutively.

    Regards,
    AJ
  • Hi AJ,

    That's a very interesting experiment. My first thought is that the stop condition isn't being sent correctly and this is causing the slave to get out of sync with the master.

    Can you add this line of code at the end of the ReadRTCByte function to wait for the stop condition to be sent?

     while(USCI_B_I2C_isBusBusy(USCI_B1_BASE));                  // Wait for stop to be sent

    Best regards, 

    Caleb Overbay

  • Hi AJ,

    I just wanted to check up on this and see if you had the chance to probe the I2C lines?

    Best regards,
    Caleb Overbay
  • Caleb Overbay said:

    Hi AJ,

    That's a very interesting experiment. My first thought is that the stop condition isn't being sent correctly and this is causing the slave to get out of sync with the master.

    Can you add this line of code at the end of the ReadRTCByte function to wait for the stop condition to be sent?

     while(USCI_B_I2C_isBusBusy(USCI_B1_BASE));                  // Wait for stop to be sent

    Best regards, 

    Caleb Overbay

    Hi Caleb,

    I tried this and still got the same results. I have not had a chance to probe the I2C lines. I got side tracked into something else. For now I'll settle with the one I had previously. It didn't make complete sense because I'm putting a stop after doing a single write but it works

    Thanks for your help.

    Albert 

  • Hi AJ,

    That's alright. I'll close out this post for now and if you get time to look into this further feel free to reply and re-open the post.

    Best regards,
    Caleb Overbay

**Attention** This is a public forum