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.

MSPM0G3507: I2C example source code issue

Part Number: MSPM0G3507
Other Parts Discussed in Thread: , MSP430F5529

Tool/software:

Hi

I using LP-MSPM0G3507 to interface with EEPROM(24C02). I tried to use i2c_target_rw_multibyte_fifo_interrupts_LP_MSPM0G3507_nortos_ticlang example code

Slave id changed to 0x50 and 47kOhm pull-up are not used. I2C SDA and SCL lines are always high which shows communication is not properly established

I had used MSP430F5529 launchpad with same EEPROM and it worked all operations.

Thanks in advance.

Regards,

Yogesh

  • The i2c_target_XXX examples are designed to operate as I2C targets (slaves), but to work with an EEPROM the MSPM0 needs to operate as a controller (master). In particular, a target doesn't (can't) ever start a transaction.

    There is an analogous example "i2c_controller_rw_multibyte_fifo_interrupts" here:

    https://dev.ti.com/tirex/explore/node?node=A__ACr6MhbD3KdyU.V6hxHG7A__MSPM0-SDK__a3PaaoK__LATEST

    Based on a quick glance over the code, I think you can just fill in gTxPacket[] and adjust the lengths.

    The delay_cycles() between write and readback is only (1000) clocks, which is quite a bit shorter than Twr=5ms, so you should probably extend it to something like (5*32000) to get 5ms.

  • Hi Bruce,

    Thanks for the information. Will test and update you the same

    Regards,

    Yogesh

  • Hi Bruce,

    I have adjusted timing  and now I am able to write to EEPROM. However need some more clarification. 

    gRxPacket is always showing 0xFF that means read from EEPROM operation is not working ?

    Regards,

    Yogesh 

  • A new EEPROM is typically (though not always) filled with 0xFF, so that suggests you're reading from an area that you haven't written yet.

    After performing a write, the EEPROM's internal address pointer points after the data you just wrote. To read back what you wrote, you need to issue a "short" write containing only the EEPROM address you want. That sets the internal pointer, so your next read reads from there. This is shown in the data sheet (doc0180) Fig 11. (Fig 11 shows a Repeated Start, i.e. a single transaction, but you can do it as two transactions if that's simpler.)

  • Hi Bruce,

    I am not able to see Rx transactions on logic analyzer (attached waveforms)

    I am able to see Rx only transactions if Tx related code is commented

  • I'm supposing(?) that that first trace is from the (failing) Rx transaction. There's not much detail, but my first guess is that you're encountering a NACK.

    After a Write (with data), the EEPROM halts for up to Twr=5ms while it writes its memory. During that time, it will NACK any request. So that would be the symptom you'd see if your delay_cycles() is too short. I was guessing that the MCU was running at 32MHz (I think that's what you get if you don't say otherwise), and thus that 5ms would be (5*32000) clock cycles. You might try increasing that; since the max speed is 80MHz, you could try (5*80000).

  • Hi Bruce,

    Thanks for your support. I am using the same code provided by you. 

    /*
     * Copyright (c) 2021, Texas Instruments Incorporated
     * All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without
     * modification, are permitted provided that the following conditions
     * are met:
     *
     * *  Redistributions of source code must retain the above copyright
     *    notice, this list of conditions and the following disclaimer.
     *
     * *  Redistributions in binary form must reproduce the above copyright
     *    notice, this list of conditions and the following disclaimer in the
     *    documentation and/or other materials provided with the distribution.
     *
     * *  Neither the name of Texas Instruments Incorporated nor the names of
     *    its contributors may be used to endorse or promote products derived
     *    from this software without specific prior written permission.
     *
     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
     * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
     * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
     * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
     * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     */
    
    #include "ti_msp_dl_config.h"
    
    /* Maximum size of TX packet */
    #define I2C_TX_MAX_PACKET_SIZE (3)
    
    /* Number of bytes to send to target device */
    #define I2C_TX_PACKET_SIZE (3)
    
    /* Maximum size of RX packet */
    #define I2C_RX_MAX_PACKET_SIZE (3)
    
    /* Number of bytes to received from target */
    #define I2C_RX_PACKET_SIZE (3)
    
    /* I2C Target address */
    #define I2C_TARGET_ADDRESS (0x50)
    
    /* Data sent to the Target */
    uint8_t gTxPacket[I2C_TX_MAX_PACKET_SIZE] = {0x00, 0x00, 0x02};
      // 0x03, 0x04,
       // 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F};
    /* Counters for TX length and bytes sent */
    uint32_t gTxLen, gTxCount;
    
    /* Data received from Target */
    uint8_t gRxPacket[I2C_RX_MAX_PACKET_SIZE];
    /* Counters for TX length and bytes sent */
    uint32_t gRxLen, gRxCount;
    /* Indicates status of I2C */
    enum I2cControllerStatus {
        I2C_STATUS_IDLE = 0,
        I2C_STATUS_TX_STARTED,
        I2C_STATUS_TX_INPROGRESS,
        I2C_STATUS_TX_COMPLETE,
        I2C_STATUS_RX_STARTED,
        I2C_STATUS_RX_INPROGRESS,
        I2C_STATUS_RX_COMPLETE,
        I2C_STATUS_ERROR,
    } gI2cControllerStatus;
    
    int main(void)
    {
        SYSCFG_DL_init();
    
        /* Set LED to indicate start of transfer */
        DL_GPIO_setPins(GPIO_LEDS_PORT, GPIO_LEDS_USER_LED_1_PIN);
    
        NVIC_EnableIRQ(I2C_INST_INT_IRQN);
        DL_SYSCTL_disableSleepOnExit();
    
        gI2cControllerStatus = I2C_STATUS_IDLE;
    
        gTxLen = I2C_TX_PACKET_SIZE;
        gTxCount = DL_I2C_fillControllerTXFIFO(I2C_INST, &gTxPacket[0], gTxLen);
        if (gTxCount < gTxLen) {
            DL_I2C_enableInterrupt(I2C_INST, DL_I2C_INTERRUPT_CONTROLLER_TXFIFO_TRIGGER);
        } else {
            DL_I2C_disableInterrupt(I2C_INST, DL_I2C_INTERRUPT_CONTROLLER_TXFIFO_TRIGGER);
        }
        gI2cControllerStatus = I2C_STATUS_TX_STARTED;
        while (!(DL_I2C_getControllerStatus(I2C_INST) & DL_I2C_CONTROLLER_STATUS_IDLE));
        DL_I2C_startControllerTransfer(I2C_INST, I2C_TARGET_ADDRESS, DL_I2C_CONTROLLER_DIRECTION_TX, gTxLen);
    
        while ((gI2cControllerStatus != I2C_STATUS_TX_COMPLETE) && (gI2cControllerStatus != I2C_STATUS_ERROR)) {
            __WFE();
        }
        while (DL_I2C_getControllerStatus(I2C_INST) & DL_I2C_CONTROLLER_STATUS_BUSY_BUS);
        /* Trap if there was an error */
        if (DL_I2C_getControllerStatus(I2C_INST) &  DL_I2C_CONTROLLER_STATUS_ERROR) {
            /* LED will remain high if there is an error */
            __BKPT(0);
        }
        while (!(DL_I2C_getControllerStatus(I2C_INST) & DL_I2C_CONTROLLER_STATUS_IDLE));
    
        /* Add delay between transfers */
        delay_cycles(300000);
    
        /* Send a read request to Target */
        gRxLen               = I2C_RX_PACKET_SIZE;
        gRxCount             = 0;
        gI2cControllerStatus = I2C_STATUS_RX_STARTED;
        DL_I2C_startControllerTransfer(I2C_INST, I2C_TARGET_ADDRESS, DL_I2C_CONTROLLER_DIRECTION_RX, gRxLen);
        //************I2C read register **********************
        while (gI2cControllerStatus != I2C_STATUS_RX_COMPLETE) {
            __WFE();
        }
    
        while (DL_I2C_getControllerStatus(I2C_INST) &  DL_I2C_CONTROLLER_STATUS_BUSY_BUS) ;
    
        /* If write and read were successful, toggle LED */
        while (1) {
            DL_GPIO_togglePins(GPIO_LEDS_PORT,
                GPIO_LEDS_USER_LED_1_PIN | GPIO_LEDS_USER_TEST_PIN);
            delay_cycles(16000000);
        }
    }
    
    void I2C_INST_IRQHandler(void)
    {
        switch (DL_I2C_getPendingInterrupt(I2C_INST)) {
            case DL_I2C_IIDX_CONTROLLER_RX_DONE:
                gI2cControllerStatus = I2C_STATUS_RX_COMPLETE;
                break;
            case DL_I2C_IIDX_CONTROLLER_TX_DONE:
                DL_I2C_disableInterrupt(
                    I2C_INST, DL_I2C_INTERRUPT_CONTROLLER_TXFIFO_TRIGGER);
                gI2cControllerStatus = I2C_STATUS_TX_COMPLETE;
                break;
            case DL_I2C_IIDX_CONTROLLER_RXFIFO_TRIGGER:
                gI2cControllerStatus = I2C_STATUS_RX_INPROGRESS;
                /* Receive all bytes from target */
                while (DL_I2C_isControllerRXFIFOEmpty(I2C_INST) != true) {
                    if (gRxCount < gRxLen) {
                        gRxPacket[gRxCount++] =
                            DL_I2C_receiveControllerData(I2C_INST);
                    } else {
                        /* Ignore and remove from FIFO if the buffer is full */
                        DL_I2C_receiveControllerData(I2C_INST);
                    }
                }
                break;
            case DL_I2C_IIDX_CONTROLLER_TXFIFO_TRIGGER:
                gI2cControllerStatus = I2C_STATUS_TX_INPROGRESS;
                /* Fill TX FIFO with next bytes to send */
                if (gTxCount < gTxLen) {
                    gTxCount += DL_I2C_fillControllerTXFIFO(
                        I2C_INST, &gTxPacket[gTxCount], gTxLen - gTxCount);
                }
                break;
                /* Not used for this example */
            case DL_I2C_IIDX_CONTROLLER_ARBITRATION_LOST:
            case DL_I2C_IIDX_CONTROLLER_NACK:
                if ((gI2cControllerStatus == I2C_STATUS_RX_STARTED) ||
                    (gI2cControllerStatus == I2C_STATUS_TX_STARTED)) {
                    /* NACK interrupt if I2C Target is disconnected */
                    gI2cControllerStatus = I2C_STATUS_ERROR;
                }
            case DL_I2C_IIDX_CONTROLLER_RXFIFO_FULL:
            case DL_I2C_IIDX_CONTROLLER_TXFIFO_EMPTY:
            case DL_I2C_IIDX_CONTROLLER_START:
            case DL_I2C_IIDX_CONTROLLER_STOP:
            case DL_I2C_IIDX_CONTROLLER_EVENT1_DMA_DONE:
            case DL_I2C_IIDX_CONTROLLER_EVENT2_DMA_DONE:
            default:
                break;
        }
    }

    I am trying to write 0x0000 memory location with 0x02. At same time , i am trying to read 0x0000 address of EEPROM. 

    Thanks in advance.

    Regards,

    Yogesh

  • The second trace appears to be a normal read, and the results aren't 0xFF, so this is progress. 

    The Tx writes {0x00, 0x02} starting at EEPROM address 0x00. Then the Rx reads the data starting at EEPROM address 0x02 (after the written data). I suppose some of your earlier experiments wrote 0x00 there.

    Here's a quick (I think) experiment: Change I2C_TX_PACKET_SIZE to 1 (you don't need to change I2C_TX_MAX_PACKET_SIZE). Then see what you get from the Rx transaction. This version won't write any new data, but the Tx operation will set the EEPROM's internal address pointer back to 0x00 (=gTxPacket[0]). Then the Rx will read starting at the beginning of the memory.

    As an extra experiment, you can change I2C_RX_MAX_PACKET_SIZE and I2C_RX_PACKET_SIZE to something larger, maybe 8 or 16, to see if those 0x00-s show up at address 0x02.

  • Hi Bruce,

    Thanks for your kind support. 

    I have modified I2C_TX_PACKET_SIZE to 1 and then I am able to see previous writes on those memory location

    Multiple doubts on MSPM0 implementation. On MSP430 ..some functions like 

    EEPROM_ByteWrite(0x0005,0x89);
    __delay_cycles(3000);
    EEPROM_AckPolling();
    __delay_cycles(3000);
    unsigned char read_val = EEPROM_RandomRead(0x0005); // Read from address 0x0000
    __delay_cycles(3000);

     Above functions very useful and user friendly also. Do we have similar functions to read or write particular memory location. 

    Regards,

    Yogesh

      

  • I haven't seen an (I2C) EEPROM example for the MSPM0 series from TI. They might someday update SLAA208 for the MSPM0, but I wouldn't know. There may be something on Github.

    That said,

    (a) Your current code effectively implements EEPROM_RandomRead [Write address, then Read]

    (b) The first half (Tx side) of your previous code effectively implements EEPROM_ByteWrite [Write address followed by data]

    so you could just re-package them as functions. [You may find it slightly easier to start with the "_poll" rather than the "_interrupt" variant, but that's your call.]

    (c) I suggest that for the moment you set aside EEPROM_AckPolling and just use a delay for Twr=5ms. Implementing ACK polling requires dealing with a NACK as a "normal" result, which doesn't easily fit into the rest of the structure. It's also a relatively minor optimization.

    [Edit: Don't forget about the EEPROM's page mechanism for writes. DOC0180 says that the page size for the 24C02 is 8 bytes ("Page Write" on p. 9).]

  • Hi Yogesh, Hi Bruce,

    Glad you were able to find a working solution here. To comment on the EEPROM_write and EEPROM_ackPolling functions, we do not have a plan to provide these specific functions in our source, we expect customers to implement this manually as needed.