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.

TM4C123GH6PM: Tiva i2c driver for Cypress FM24CL04B and Fujitsu MB85RC1MTPNF FRAM, two byte register address

Part Number: TM4C123GH6PM

I have an ultra low power application that runs on three different boards, 2 use TM4C123GH6PGEI 144 pin and the latest uses TM4C123GH6PMIR 64pin.   I2C0 is on the same pin mux pins.

The application requires external low power memory to store volatile state variables and things like 'sample num'.   Onchip EEPROM is not an option as we are well over the rated 500K write cycles with deployments that last over a year.    I have tried the HIB module RAM, however, we wish to store more than the HIB RAM can hold hence our use of FRAM.

The first two boards use Cypress FRAM FM24CL04B which has a single byte register address and the latest board uses a much larger Fujitsu MB85RC1MTPNF FRAM as we plan to cache sample data and then write to external NAND flash or microSD in a burst block fashion.

The i2c driver for for the smaller 4KB FRAM works fine.     My question is how to write two bytes of address for the bigger 128KB Fujitsu FRAM.    The image shows the i2c byte sequence.

My i2c driver for Cypress has one call to ROM_i2cmasterdataput...     What i'd like to do is something like the following:

// Place the address to be written in the data register.
#if FUJITSU_BIGFRAM == 1
ROM_I2CMasterDataPut(I2C0_BASE, (uint8_t)(ucStart_addr/256));
#endif
ROM_I2CMasterDataPut(I2C0_BASE, (uint8_t)ucStart_addr);

This is incorrect (as it does not work), but I'm not seeing a 16 bit (2 byte) API.       Please advise on how to do the two byte address.     I've seen other posts in the e2e forum but not an answer.

Thanks


void fram_test(void)
{
    int indx;

    uprintf("\r\n1st block of 128, write 127 to 0\r\n");
    for(indx=0; indx<128; indx++)               // 0->127
        framBuf[indx] = 127-indx;
    i2c0_write(I2C_FRAM_ADDR, framBuf, 128, 0x00);
    for(indx=0; indx<128; indx++)
    {
        uprintf("%02x ", framBuf[indx]);
        if(indx%16 == 0x0f)
            uprintf("\r\n");
    }

    uprintf("\r\n2nd block of 128, write 0 to 127\r\n");
    for(indx=0; indx<128; indx++)
        framBuf[indx] = indx;
    i2c0_write(I2C_FRAM_ADDR, framBuf, 128, 128);
    for(indx=0; indx<128; indx++)
    {
        uprintf("%02x ", framBuf[indx]);
        if(indx%16 == 0x0f)
            uprintf("\r\n");
    }

    wait_consoleTx();

    uprintf("\r\n3rd block of 128, write 255 to 128\r\n");
    for(indx=0; indx<128; indx++)
        framBuf[indx] = 255-indx;
    i2c0_write(I2C_FRAM_ADDR, framBuf, 128, 256);
    for(indx=0; indx<128; indx++)
    {
        uprintf("%02x ", framBuf[indx]);
        if(indx%16 == 0x0f)
            uprintf("\r\n");
    }

    uprintf("\r\n4th block of 128, write 128 to 255\r\n");
    for(indx=0; indx<128; indx++)
        framBuf[indx] = 128+indx;
    i2c0_write(I2C_FRAM_ADDR, framBuf, 128, 384);
    for(indx=0; indx<128; indx++)
    {
        uprintf("%02x ", framBuf[indx]);
        if(indx%16 == 0x0f)
            uprintf("\r\n");
    }

    wait_consoleTx();

    for(indx=0; indx<128; indx++)
        framBuf[indx] = 0x55;


    uprintf("\r\n1st block of 128, read:\r\n");
    i2c0_read(I2C_FRAM_ADDR, framBuf, 128, 0, FALSE);
    for(indx=0; indx<128; indx++)
    {
        uprintf("%02x ", framBuf[indx]);
        if(indx%16 == 0x0f)
            uprintf("\r\n");
    }

    uprintf("\r\n2nd block of 128, read:\r\n");
    i2c0_read(I2C_FRAM_ADDR, framBuf, 128, 128, FALSE);
    for(indx=0; indx<128; indx++)
    {
        uprintf("%02x ", framBuf[indx]);
        if(indx%16 == 0x0f)
            uprintf("\r\n");
    }

    wait_consoleTx();

    uprintf("\r\n3rd block of 128, read:\r\n");
    //i2c0_read(I2C_FRAM_ADDR|0x01, framBuf, 128, 256, FALSE);
    i2c0_read(I2C_FRAM_ADDR, framBuf, 128, 256, FALSE);
    for(indx=0; indx<128; indx++)
    {
        uprintf("%02x ", framBuf[indx]);
        if(indx%16 == 0x0f)
            uprintf("\r\n");
    }

    uprintf("\r\n4th block of 128, read:\r\n");
    i2c0_read(I2C_FRAM_ADDR, framBuf, 128, 384, FALSE);
    for(indx=0; indx<128; indx++)
    {
        uprintf("%02x ", framBuf[indx]);
        if(indx%16 == 0x0f)
            uprintf("\r\n");
    }

    wait_consoleTx();

}

/******************************************************************************
 * Initialize and configure I2C module in TIVA tm4c123gh6pge
 *            MFET uses I2C0_BASE on PB2/PB3, the other i2c periph base addr are not used
 *
 * Parameters:
 *          ui32Base: I2Cx base address. The address is one of the following values:
 *              I2C0_BASE, I2C1_BASE, I2C2_BASE, I2C3_BASE.
 *
 *      bFast:
 *              true : I2C will run in fast mode (400kbps)
 *              false: I2C will run in standard mode (100kbps)
 *
 * Return: none
 *****************************************************************************/
void i2c0_init(bool bFast)
{

    //ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);      // this is done prior in init.c

    //enable onchip I2C peripheral
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C0);

    //reset I2C module
    ROM_SysCtlPeripheralReset(SYSCTL_PERIPH_I2C0);

    // Configure the pin muxing for I2C0 functions on port PB2(SCL) and PB3(SDA).
    ROM_GPIOPinConfigure(GPIO_PB2_I2C0SCL);     // This is the same on 144 pin part and 64pin part
    ROM_GPIOPinConfigure(GPIO_PB3_I2C0SDA);

    // Configure the appropriate pins to be I2C instead of GPIO.
    ROM_GPIOPinTypeI2C(GPIO_PORTB_BASE, GPIO_PIN_2 | GPIO_PIN_3);
    //GPIOPinTypeI2CSCL(GPIO_PORTB_BASE, GPIO_PIN_2);
    //GPIOPinTypeI2C(GPIO_PORTB_BASE, GPIO_PIN_3);


    ROM_GPIOPadConfigSet(GPIO_PORTB_BASE, GPIO_PIN_2, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD);
    ROM_GPIOPadConfigSet(GPIO_PORTB_BASE, GPIO_PIN_3, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_OD);

    // Initialize the I2C master - Transfer speed is defined depend on variable bFast
    ROM_I2CMasterInitExpClk(I2C0_BASE, SysCtlClockGet(), bFast);

    //clear I2C FIFOs
    HWREG(I2C0_BASE + I2C_O_FIFOCTL) = 80008000;

    ROM_I2CMasterEnable(I2C0_BASE);      //I2C is ready to use

}

/******************************************************************************
 * Write multiple bytes to a slave device
 *
 * Parameters:
 *  uiSlave_addr : the slave address
 *  *ucData    : the data that are going to be sent
 *  uiCount    : number of byte will be sent
 *  ucStart_addr : register address you want to write or a control byte
 *
 * Return: none
 * Useful info:  https://github.com/sosandroid/FRAM_MB85RC_I2C
 *****************************************************************************/
void i2c0_write(unsigned char uiSlave_addr, unsigned char *ucData, uint16_t uiCount, uint16_t ucStart_addr)  //void i2c0_write(unsigned char uiSlave_add, unsigned char *ucData, uint16_t uiCount, unsigned char ucStart_add)
{
    unsigned int temp = 1;
    IntMasterDisable();

    // Set the slave address and setup for a transmit operation.
    ROM_I2CMasterSlaveAddrSet(I2C0_BASE, uiSlave_addr, false);

    // Place the address to be written in the data register.
#if FUJITSU_BIGFRAM == 1      // This does not work, need to find a two byte API
    ROM_I2CMasterDataPut(I2C0_BASE, (uint8_t)(ucStart_addr/256));
#endif
    ROM_I2CMasterDataPut(I2C0_BASE, (uint8_t)ucStart_addr);

    while (ROM_I2CMasterBusy(I2C0_BASE));
    if (uiCount == 0)
        // Initiate send of character from Master to Slave
        ROM_I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_SINGLE_SEND);
    else
    {
        // Start the burst cycle, writing the address as the first byte.
        ROM_I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_START);
        // Write the next byte to the data register.
        while (ROM_I2CMasterBusy(I2C0_BASE));
        ROM_I2CMasterDataPut(I2C0_BASE, ucData[0]);
        for( ; temp < uiCount; temp++)        //Loop to send data if not the last byte
        {
            // Continue the burst write.
            ROM_I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_CONT);
            // Write the next byte to the data register.
            while (ROM_I2CMasterBusy(I2C0_BASE));
            ROM_I2CMasterDataPut(I2C0_BASE, ucData[temp]);
        }
        // Finish the burst write.
        ROM_I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_FINISH);
    }
    while (ROM_I2CMasterBusy(I2C0_BASE));
    IntMasterEnable();
}

/******************************************************************************
 * Read multiple bytes from slave device
 *
 * Parameters:
 *  uiSlave_addr: the slave address
 *  *ucRec_Data: a variable to save the data received
 *  uiCount    : number of byte will be received
 *  ucStart_addr: register address you want to read or a control byte (Bug fix 9/9/2022:  NanoFET has large 128KB FRAM, according to Fujitsu datasheet, large FRAM requires 2 bytes of address)
 *
 * Return: none
 *****************************************************************************/
void i2c0_read(unsigned char uiSlave_addr, unsigned char *ucRec_Data, uint16_t uiCount, uint16_t ucStart_addr, bool bDummyRead)   //void I2C_Read(uint32_t ui32Base, unsigned char uiSlave_add, unsigned char *ucRec_Data, uint16_t uiCount, unsigned char ucStart_add, bool bDummyRead)
{
    unsigned int uitemp = 0;
        // Set the slave address and setup for a transmit operation.

    IntMasterDisable();
    ROM_I2CMasterSlaveAddrSet(I2C0_BASE, uiSlave_addr, false);

    // Place the address to be written in the data register.
#if FUJITSU_BIGFRAM == 1      // This does not work, need to find a two byte API
    ROM_I2CMasterDataPut(I2C0_BASE, (uint8_t)(ucStart_addr/256));
#endif
    ROM_I2CMasterDataPut(I2C0_BASE, (uint8_t)ucStart_addr);

    // Start the burst cycle, writing the address as the first byte.
    ROM_I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_START);

    while (ROM_I2CMasterBusy(I2C0_BASE))
        ;
        //while (!(ROM_I2CMasterErr(ui32Base) == I2C_MASTER_ERR_NONE));

    ROM_I2CMasterSlaveAddrSet(I2C0_BASE, uiSlave_addr, true);
    if (uiCount == 1)
    {
        ROM_I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_SINGLE_RECEIVE);
        while (ROM_I2CMasterBusy(I2C0_BASE));
        ucRec_Data[0]  = ROM_I2CMasterDataGet(I2C0_BASE);
        if (bDummyRead)
        {
            while (ROM_I2CMasterBusy(I2C0_BASE));
            ucRec_Data[0]  = ROM_I2CMasterDataGet(I2C0_BASE);
        }
    }
    else
    {
        ROM_I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_RECEIVE_START);
        if (bDummyRead)
        {
            while (ROM_I2CMasterBusy(I2C0_BASE))
                ;
            ucRec_Data[0]  = ROM_I2CMasterDataGet(I2C0_BASE);
        }
        while (ROM_I2CMasterBusy(I2C0_BASE))
            ;
        ucRec_Data[uitemp++]  = ROM_I2CMasterDataGet(I2C0_BASE);
        for ( ; uitemp < (uiCount - 1); uitemp++)
        {
            ROM_I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_RECEIVE_CONT);
            while (ROM_I2CMasterBusy(I2C0_BASE))
                ;
            ucRec_Data[uitemp]  = ROM_I2CMasterDataGet(I2C0_BASE);
        }
        ROM_I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_RECEIVE_FINISH);
        while (ROM_I2CMasterBusy(I2C0_BASE))
            ;
        ucRec_Data[uitemp]  = ROM_I2CMasterDataGet(I2C0_BASE);
    }

    while (ROM_I2CMasterBusy(I2C0_BASE))
        ;
    IntMasterEnable();
}

  • Follow up note:   Though this is for the 129 and I'm using 123, this doc is useful:  https://www.ti.com/lit/an/spma073/spma073.pdf

    spma073.pdf alludes to the use of a LAPIS FeRAM (FRAM), MR44V064A 64k (8,192-Word × 8-Bit) part.    I have not been able to find example code or a driver, is that available?   Even though it likely does not require a two byte address, it would be helpful.

  • Found my problem - had misunderstood the use of the i2c api.    I've still got work todo on the FRAM driver and need to refactor things to support single vs double byte addressing.   See code below for updated i2c0_wrte and i2c0_read now work with the 128KB Fujitsu part.

    /******************************************************************************
     * Write multiple bytes to a slave device
     *
     * Parameters:
     *  uiSlave_addr : the slave address
     *  *ucData    : the data that are going to be sent
     *  uiCount    : number of byte will be sent
     *  ucStart_addr : register address you want to write or a control byte
     *
     * Return: none
     * Useful info:  https://github.com/sosandroid/FRAM_MB85RC_I2C
     *****************************************************************************/
    void i2c0_write(unsigned char uiSlave_addr, unsigned char *ucData, uint16_t uiCount, uint16_t ucStart_addr)  //void i2c0_write(unsigned char uiSlave_add, unsigned char *ucData, uint16_t uiCount, unsigned char ucStart_add)
    {
        unsigned int indx;
        unsigned char i2c_addr;
    
        IntMasterDisable();
    #ifdef FROMARDUINO
        switch(density) {
            case 4:
                //chipaddress = (i2c_addr | ((framAddr >> 8) & 0x1)); //Issue #10
                i2c_addr = ((i2c_addr & 0b11111110) | ((framAddr >> 8) & 0b00000001));
                break;
            case 16:
                //chipaddress = (i2c_addr | ((framAddr >> 8) & 0x7));   //Issue #10
                i2c_addr = ((i2c_addr & 0b11111000) | ((framAddr >> 8) & 0b00000111));
                break;
            default:
                chipaddress = i2c_addr;
                break;
        }
    #endif
    
    
    
    #if FRAM_FM24CL04B == 1
        i2c_addr = (uiSlave_addr & 0xFE) | ((ucStart_addr/256) & 0x01);  // 4KB part has bit.1 of i2c_addr as the 256 byte page select
        //uprintf("\r\ni2c_addr: 0x%02x\r\n", i2c_addr);  // DEBUG
    
        //Master is initiating a write to the slave.
        ROM_I2CMasterSlaveAddrSet(I2C0_BASE, i2c_addr, FALSE); // FALSE indicates a write
    
        //write the single byte of FRAM word address
        ROM_I2CMasterDataPut(I2C0_BASE, (uint8_t)(ucStart_addr & 0x00ff));
        while (ROM_I2CMasterBusy(I2C0_BASE));
    #endif
    #if FRAM_MB85RC1M == 1
        i2c_addr = uiSlave_addr;
        // Set the slave address and setup for a transmit operation.
        ROM_I2CMasterSlaveAddrSet(I2C0_BASE, i2c_addr, false);
    
        //write the hi byte of FRAM word address
        ROM_I2CMasterDataPut(I2C0_BASE, (uint8_t)(ucStart_addr/256));
        while (ROM_I2CMasterBusy(I2C0_BASE)); // This was not here when successfully tested on NanoFET
    #endif
    
        if (uiCount == 0)
            // Initiate send of character from Master to Slave (NEEDS TESTING)
            ROM_I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_SINGLE_SEND);
        else
        {
            // Start the burst cycle, writing the address as the first byte.
            ROM_I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_START);
            while (ROM_I2CMasterBusy(I2C0_BASE));         // wait for completion
    
    #if FRAM_MB85RC1M == 1
            //write the lo byte of FRAM word address for 128KB Fujitsu
            ROM_I2CMasterDataPut(I2C0_BASE, (uint8_t)ucStart_addr);
            while (ROM_I2CMasterBusy(I2C0_BASE));        // wait for completion
    
            ROM_I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_CONT);    // send byte
            while (ROM_I2CMasterBusy(I2C0_BASE));        // wait for completion
    #endif
    
            ROM_I2CMasterDataPut(I2C0_BASE, ucData[0]);
            for(indx=1; indx<uiCount; indx++)        // loop is structured so that last dataPut is followed by SEND_FINISH
            {
                // Continue the burst write.
                ROM_I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_CONT);
                // Write the next byte to the data register.
                while (ROM_I2CMasterBusy(I2C0_BASE));
                ROM_I2CMasterDataPut(I2C0_BASE, ucData[indx]);
            }
            // Finish the burst write.
            ROM_I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_FINISH);
        }
        while (ROM_I2CMasterBusy(I2C0_BASE));
    
        IntMasterEnable();
    }
    
    /******************************************************************************
     * Read multiple bytes from slave device
     *
     * Parameters:
     *  uiSlave_addr: the slave address
     *  *ucRec_Data: a variable to save the data received
     *  uiCount    : number of byte will be received
     *  ucStart_addr: register address you want to read or a control byte (Bug fix 9/9/2022:  NanoFET has large 128KB FRAM, according to Fujitsu datasheet, large FRAM requires 2 bytes of address)
     *
     * Return: none
     *****************************************************************************/
    void i2c0_read(unsigned char uiSlave_addr, unsigned char *ucRec_Data, uint16_t uiCount, uint16_t ucStart_addr, bool bDummyRead)   //void I2C_Read(uint32_t ui32Base, unsigned char uiSlave_add, unsigned char *ucRec_Data, uint16_t uiCount, unsigned char ucStart_add, bool bDummyRead)
    {
        unsigned int indx = 0;
        unsigned char i2c_addr;
    
    
        IntMasterDisable();
    
    #if FRAM_FM24CL04B == 1
        i2c_addr = (uiSlave_addr & 0xFE) | ((ucStart_addr/256) & 0x01);  // 4Kb 512byte part has bit.1 of i2c_addr as the 256 byte page select
        //uprintf("\r\ni2c_addr: 0x%02x\r\n", i2c_addr);   // DEBUG
    
        // Set the slave address and setup for a transmit operation.
        ROM_I2CMasterSlaveAddrSet(I2C0_BASE, i2c_addr, FALSE);  // FALSE indicates a write
    
        // send single byte address for 4KB cypress fram
        ROM_I2CMasterDataPut(I2C0_BASE, (uint8_t)(ucStart_addr & 0x00ff));  // 0x00ff is not needed...
    
        // Start the burst cycle, writing the address as the first byte.
        ROM_I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_START);
        while (ROM_I2CMasterBusy(I2C0_BASE));
    #endif
    
    #if FRAM_MB85RC1M == 1
        i2c_addr = uiSlave_addr;
        // Set the slave address and setup for a transmit operation.
        ROM_I2CMasterSlaveAddrSet(I2C0_BASE, i2c_addr, false);
    
        // send the hi byte address
        ROM_I2CMasterDataPut(I2C0_BASE, (uint8_t)(ucStart_addr/256));
        //while (ROM_I2CMasterBusy(I2C0_BASE));
    
        // Start the burst cycle, writing the address as the first byte.
        ROM_I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_START);
        while (ROM_I2CMasterBusy(I2C0_BASE));
    
        //send the lo byte address
        ROM_I2CMasterDataPut(I2C0_BASE, (uint8_t)ucStart_addr);
        ROM_I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_CONT);
        while (ROM_I2CMasterBusy(I2C0_BASE));
    #endif
    
        //ROM_I2CMasterSlaveAddrSet(I2C0_BASE, uiSlave_addr, TRUE);   // True indicates a read (get)
        ROM_I2CMasterSlaveAddrSet(I2C0_BASE, i2c_addr, TRUE);   // True indicates a read (get)
        if (uiCount == 1)
        {
            ROM_I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_SINGLE_RECEIVE);
            while (ROM_I2CMasterBusy(I2C0_BASE));
            ucRec_Data[0]  = ROM_I2CMasterDataGet(I2C0_BASE);
            if (bDummyRead)
            {
                while (ROM_I2CMasterBusy(I2C0_BASE));
                ucRec_Data[0]  = ROM_I2CMasterDataGet(I2C0_BASE);
            }
        }
        else
        {
            ROM_I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_RECEIVE_START);
            if (bDummyRead)
            {
                while (ROM_I2CMasterBusy(I2C0_BASE));
                ucRec_Data[0]  = ROM_I2CMasterDataGet(I2C0_BASE);
            }
            while (ROM_I2CMasterBusy(I2C0_BASE));
    
            indx=0;
            ucRec_Data[indx++]  = ROM_I2CMasterDataGet(I2C0_BASE);
            for ( ; indx < (uiCount - 1); indx++)
            {
                ROM_I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_RECEIVE_CONT);
                while (ROM_I2CMasterBusy(I2C0_BASE));
    
                ucRec_Data[indx]  = ROM_I2CMasterDataGet(I2C0_BASE);
            }
            ROM_I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_RECEIVE_FINISH);
            while (ROM_I2CMasterBusy(I2C0_BASE));
    
            ucRec_Data[indx]  = ROM_I2CMasterDataGet(I2C0_BASE);
        }
    
        while (ROM_I2CMasterBusy(I2C0_BASE));
    
        IntMasterEnable();
    }