/*
 * i2c_fram.c
 *
 *  Created on: Aug 5, 2019
 *      Author: tm
 */



#include <stdint.h>
#include <stdbool.h>
#include "system.h"
#include "user_io.h"
#include "uartstdio.h"  // User local version with larger RX buffer
#include "inc/tm4c123gh6pm.h"
#include "inc/hw_i2c.h"
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "inc/hw_gpio.h"
#include "driverlib/i2c.h"
#include "driverlib/sysctl.h"
#include "driverlib/gpio.h"
#include "driverlib/pin_map.h"

#include "i2c_fram.h"

extern struct systemSamplingData sys_samp;

//void i2c0_init(bool bFast);
//void i2c0_write(unsigned char uiSlave_add, unsigned char *ucData, uint16_t uiCount, unsigned char ucStart_add );
//void i2c0_read(unsigned char uiSlave_add, unsigned char *ucRec_Data, uint16_t uiCount, unsigned char ucStart_add, bool bDummyRead);
//uint32_t fram_probe(void);

unsigned char framBuf[132];

//extern unsigned char keytape_buf[132];         // 128 bytes with 4 byte pad
//extern struct keytapeVars  keytape;

#define I2C_FRAM_ADDR   0x50       // 0xA0/2 and bit.0 is the 256 byte page address - the i2c API's take care of stuffing the R/W* bit

//#define FRAM_KEYTAPE_SIZE   128

#define FRAM_PROBESIG_ADDR      0       // fram probe signature
#define FRAM_PROBESIG_BLKSIZE   16

//#define FRAM_SYSDATCHG_ADDR     16      // start the data structure storage at address 16, first 4 bytes are for fram_probe signature, the 12 spare
#define FRAM_SYSSAMP_ADDR       64      // padded (TODO: Get rid of SYSDATCHG entirely). size is 6 * 4, pad to 88=*4 = 32
//#define FRAM_KEYTAPE_ADDR       96      // size is 128
//#define FRAM_START_SPARE    (FRAM_KEYTAPE_ADDR + FRAM_KEYTAPE_SIZE)

//#define FRAM_KEYTAPE_ADDR_END (FRAM_KEYTAPE_ADDR + FRAM_KEYTAPE_SIZE - 1)

#if BOARD_MFET >= 2 || BOARD_MPHOX >= 1
#define FRAM_FM24CL04B  1
#define FRAM_MB85RC1M   0
#endif

#if BOARD_NANOFET == 1
#define FRAM_FM24CL04B  0
#define FRAM_MB85RC1M   1
#endif

void fram_init(void)
{
    i2c0_init(TRUE);        // fast mode 400khz

    if( fram_probe() != 0xAA55AA55)           // read the manufacturer id, or read byte signature in the case of no manufactuerer id
    {
        uprintf("\r\nERROR: FRAM fails probe, do not deploy until resolved\r\n");
    }

    //fram_test();
}

uint32_t fram_probe(void)
{
    // assumes i2c0_init was called
    // apparently the 24cl04b does not have a device id or a manufacturer id
    // allocate the first 4 bytes to be the id, demonstrate write/read as a means for indicating FRAM is there and useful
    framBuf[0] = 0x55;
    framBuf[1] = 0xAA;
    framBuf[2] = 0x55;
    framBuf[3] = 0xAA;
    i2c0_write(I2C_FRAM_ADDR, framBuf, 4, FRAM_PROBESIG_ADDR);      // probe signature is first four bytes (starting address is 0)
    framBuf[0] = 0x00;
    framBuf[1] = 0x00;
    framBuf[2] = 0x00;
    framBuf[3] = 0x00;

    i2c0_read(I2C_FRAM_ADDR, framBuf, 4, 0x00, TRUE);
    if((framBuf[0] == 0x55) && (framBuf[1] == 0xAA) && (framBuf[2] == 0x55) && (framBuf[3] == 0xAA))
    {
        return(0xAA55AA55);
    }

    return(0);


}



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, TRUE);
    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, TRUE);
    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, TRUE);
    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, TRUE);
    for(indx=0; indx<128; indx++)
    {
        uprintf("%02x ", framBuf[indx]);
        if(indx%16 == 0x0f)
            uprintf("\r\n");
    }

    wait_consoleTx();

}

void fram_sys_samp_store(void)
{
    i2c0_write(I2C_FRAM_ADDR, (unsigned char *)&sys_samp, sizeof(sys_samp), FRAM_SYSSAMP_ADDR);     // 64
}

void fram_sys_samp_retrieve(void)
{
    i2c0_read(I2C_FRAM_ADDR, (unsigned char *)&sys_samp, sizeof(sys_samp), FRAM_SYSSAMP_ADDR, TRUE);
}



//void fram_keytape_store(void)
//{
//    i2c0_write(I2C_FRAM_ADDR, (unsigned char *)&keytape_buf, FRAM_KEYTAPE_SIZE, FRAM_KEYTAPE_ADDR);     // size 128
//}

//void fram_keytape_retrieve(void)
//{
//    i2c0_read(I2C_FRAM_ADDR, (unsigned char *)&keytape_buf, FRAM_KEYTAPE_SIZE, FRAM_KEYTAPE_ADDR, TRUE);
//}

#ifdef OLDCODE
void fram_store(void)
{
    // note: the struct packing might be an issue, in that case write longs/floats into frambuf as bytes per variable
    i2c0_write(I2C_FRAM_ADDR, (unsigned char *)&sys_data_chg, sizeof(sys_data_chg), FRAM_SYSDATCHG_ADDR);   // 16
}

void fram_retrieve(void)
{
    i2c0_read(I2C_FRAM_ADDR, (unsigned char *)&sys_data_chg, sizeof(sys_data_chg), FRAM_SYSDATCHG_ADDR, TRUE);
}
#endif

// this routine is not used, has not been tested.
uint32_t fram_read(uint32_t offset, void* pBuf, uint32_t byteCount)
{
    i2c0_read(I2C_FRAM_ADDR, (unsigned char *)pBuf, byteCount, offset, TRUE);

    return(byteCount);
}





/******************************************************************************
 * 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 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
#ifdef THISWORKS
    i2c_addr = uiSlave_addr & 0xFE;
    if(ucStart_addr >= 256)
        i2c_addr |= 0x01;       // set bit.1
    uprintf("\r\ni2c_addr: 0x%02x\r\n", i2c_addr);
#endif

    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

    // This does not work
    // bit.7-bit.4 is slave addr = 0x5, bit.3 and bit.2 are device addr from pins on chip, bit.1 is 256 byte page address, bit.0 is 0 for write, 1 for read
    //i2c_addr = (uiSlave_addr & 0xFC) | (((ucStart_addr/256) & 0x01)<<1);  // 4KB part has bit.1 of i2c_addr as the 256 byte page select
    //uprintf("\r\ni2c_addr: 0x%02x\r\n", i2c_addr);

    // Set the slave address and setup for a transmit operation.  void ROM_I2CMasterSlaveAddrSet (uint32_t ui32Base, uint8_t ui8SlaveAddr, bool bReceive)
    //When the bReceive parameter is set to true, the address will indicate that the
    //I2C Master is initiating a read from the slave; otherwise the address will indicate that the I2C
    //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
#ifdef THISWORKS
    i2c_addr = uiSlave_addr & 0xFE;  // 0x50 & 0xFD  0101 0000 & 1111 1101
    if(ucStart_addr >= 256)
    {
        i2c_addr |= 0x01;       // set bit.1  (0x02 fails on the read)
        //i2c_addr = 0x53;
    }
    uprintf("\r\ni2c_addr: 0x%02x\r\n", i2c_addr);
#endif

    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

    //i2c_addr = (uiSlave_addr & 0xFD) | (((ucStart_addr/256) & 0x01)<<1);  // 4KB part has bit.1 of i2c_addr as the 256 byte page select
    //uprintf("\r\ni2c_addr: 0x%02x\r\n", i2c_addr);
    // 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();
}



uint8_t i2c0_read8(uint16_t device_address, uint16_t device_register)
{
    //specify that we want to communicate to device address with an intended write to bus
    ROM_I2CMasterSlaveAddrSet(I2C0_BASE, device_address, false);

    //the register to be read
    ROM_I2CMasterDataPut(I2C0_BASE, device_register);

    //send control byte and register address byte to slave device
    ROM_I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_SINGLE_SEND);

    //wait for MCU to complete send transaction
    while(ROM_I2CMasterBusy(I2C0_BASE));

    //read from the specified slave device
    ROM_I2CMasterSlaveAddrSet(I2C0_BASE, device_address, true);

    //send control byte and read from the register from the MCU
    ROM_I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_SINGLE_RECEIVE);

    //wait while checking for MCU to complete the transaction
    while(ROM_I2CMasterBusy(I2C0_BASE));

    //Get the data from the MCU register and return to caller
    return( ROM_I2CMasterDataGet(I2C0_BASE));
}

void i2c0_write8(uint16_t device_address, uint16_t device_register, uint8_t device_data)
{
    //specify that we want to communicate to device address with an intended write to bus
    ROM_I2CMasterSlaveAddrSet(I2C0_BASE, device_address, false);

    //register to be read
    ROM_I2CMasterDataPut(I2C0_BASE, device_register);

    //send control byte and register address byte to slave device
    ROM_I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_START);

    //wait for MCU to finish transaction
    while(ROM_I2CMasterBusy(I2C0_BASE))
        ;

    ROM_I2CMasterSlaveAddrSet(I2C0_BASE, device_address, true);

    //specify data to be written to the above mentioned device_register
    ROM_I2CMasterDataPut(I2C0_BASE, device_data);

    //wait while checking for MCU to complete the transaction
    ROM_I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_RECEIVE_FINISH);

    //wait for MCU & device to complete transaction
    while(ROM_I2CMasterBusy(I2C0_BASE))
        ;
}




uint8_t fram_getDeviceID(void)
{

    //uint8_t localbuffer[3] = { 0, 0, 0 };
    uint8_t result;
#ifdef NOCODE
    /* Get device IDs sequence  */
    /* 1/ Send 0xF8 to the I2C bus as a write instruction. bit 0: 0 => 0xF8 >> 1 */
    /* Send 0xF8 to 12C bus. Bit shift to right as beginTransmission() requires a 7bit. beginTransmission() 0 for write => 0xF8 */
    /* Send device address as 8 bits. Bit shift to left as we are using a simple write()                                        */
    /* Send 0xF9 to I2C bus. By requesting 3 bytes to read, requestFrom() add a 1 bit at the end of a 7 bits address => 0xF9    */
    /* See p.10 of http://www.fujitsu.com/downloads/MICRO/fsa/pdf/products/memory/fram/MB85RC-DS501-00017-3v0-E.pdf             */


    Wire.beginTransmission(MASTER_CODE >> 1);
    Wire.write((byte)(i2c_addr << 1));
    result = Wire.endTransmission(false);

    Wire.requestFrom(MASTER_CODE >> 1, 3);
    localbuffer[0] = (uint8_t) Wire.read();
    localbuffer[1] = (uint8_t) Wire.read();
    localbuffer[2] = (uint8_t) Wire.read();

    I2CMasterSlaveAddrSet(I2C2_BASE, SLAVE_ADDRESS_EXT, false);  // false means write




    /* Shift values to separate IDs */
    manufacturer = (localbuffer[0] << 4) + (localbuffer[1] >> 4);
    densitycode = (uint16_t)(localbuffer[1] & 0x0F);
    productid = ((localbuffer[1] & 0x0F) << 8) + localbuffer[2];

    if (manufacturer == FUJITSU_MANUFACT_ID) {
            switch (densitycode) {
                case DENSITY_MB85RC04V:
                    density = 4;
                    maxaddress = MAXADDRESS_04;
                    break;
                case DENSITY_MB85RC64TA:
                    density = 64;
                    maxaddress = MAXADDRESS_64;
                    break;
                case DENSITY_MB85RC256V:
                    density = 256;
                    maxaddress = MAXADDRESS_256;
                    break;
                case DENSITY_MB85RC512T:
                    density = 512;
                    maxaddress = MAXADDRESS_512;
                    break;
                case DENSITY_MB85RC1MT:
                    density = 1024;
                    maxaddress = MAXADDRESS_1024;
                    break;
                default:
                    density = 0; /* means error */
                    maxaddress = 0; /* means error */
                    if (result == 0) result = ERROR_7; /*device unidentified, comminication ok*/
                    break;
            }
    }
    else if (manufacturer == CYPRESS_MANUFACT_ID) {
            switch (densitycode) {
                case DENSITY_CY15B128J:
                    density = 128;
                    maxaddress = MAXADDRESS_128;
                    break;
                case DENSITY_CY15B256J:
                    density = 256;
                    maxaddress = MAXADDRESS_256;
                    break;
                case DENSITY_FM24V05:
                    density = 512;
                    maxaddress = MAXADDRESS_512;
                    break;
                case DENSITY_FM24V10:
                    density = 1024;
                    maxaddress = MAXADDRESS_1024;
                    break;
                default:
                    density = 0; /* means error */
                    maxaddress = 0; /* means error */
                    if (result == 0) result = ERROR_7; /*device unidentified, comminication ok*/
                    break;
            }
    }
    else {
                    density = 0; /* means error */
                    maxaddress = 0; /* means error */
                    if (result == 0) result = ERROR_7; /*device unidentified, comminication ok*/

    }
#endif
  return result;
}


#ifdef NOCODE

//*****************************************************************************
//
// pressure_bmp180.c - Example to use of the SensorLib with the BMP180
//
// Copyright (c) 2013-2020 Texas Instruments Incorporated.  All rights reserved.
// Software License Agreement
//
// Texas Instruments (TI) is supplying this software for use solely and
// exclusively on TI's microcontroller products. The software is owned by
// TI and/or its suppliers, and is protected under applicable copyright
// laws. You may not combine this software with "viral" open-source
// software in order to form a larger program.
//
// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS.
// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT
// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY
// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
// DAMAGES, FOR ANY REASON WHATSOEVER.
//
// This is part of revision 2.2.0.295 of the EK-TM4C123GXL Firmware Package.
//
//*****************************************************************************

#include <stdint.h>
#include <stdbool.h>
#include <math.h>
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "inc/hw_ints.h"
#include "driverlib/debug.h"
#include "driverlib/gpio.h"
#include "driverlib/interrupt.h"
#include "driverlib/pin_map.h"
#include "driverlib/rom.h"
#include "driverlib/rom_map.h"
#include "driverlib/sysctl.h"
#include "driverlib/systick.h"
#include "driverlib/uart.h"
#include "utils/uartstdio.h"
#include "sensorlib/hw_bmp180.h"
#include "sensorlib/i2cm_drv.h"
#include "sensorlib/bmp180.h"
#include "drivers/rgb.h"

//*****************************************************************************
//
//! \addtogroup example_list
//! <h1>Pressure Measurement with the BMP180 (pressure_bmp180)</h1>
//!
//! This example demonstrates the basic use of the Sensor Library, TM4C123G
//! LaunchPad and the SensHub BoosterPack to obtain air pressure and
//! temperature measurements with the BMP180 sensor.
//!
//! Connect a serial terminal program to the LaunchPad's ICDI virtual serial
//! port at 115,200 baud.  Use eight bits per byte, no parity and one stop bit.
//! The raw sensor measurements are printed to the terminal.  The RGB LED
//! blinks at 1Hz once the initialization is complete and the example is
//! running.
//
//*****************************************************************************

//*****************************************************************************
//
// Define BMP180 I2C Address.
//
//*****************************************************************************
#define BMP180_I2C_ADDRESS      0x77

//*****************************************************************************
//
// Global array for holding the color values for the RGB.
//
//*****************************************************************************
uint32_t g_pui32Colors[3];

//*****************************************************************************
//
// Global instance structure for the I2C master driver.
//
//*****************************************************************************
tI2CMInstance g_sI2CInst;

//*****************************************************************************
//
// Global instance structure for the BMP180 sensor driver.
//
//*****************************************************************************
tBMP180 g_sBMP180Inst;

//*****************************************************************************
//
// Global new data flag to alert main that BMP180 data is ready.
//
//*****************************************************************************
volatile uint_fast8_t g_vui8DataFlag;

//*****************************************************************************
//
// The error routine that is called if the driver library encounters an error.
//
//*****************************************************************************
#ifdef DEBUG
void
__error__(char *pcFilename, uint32_t ui32Line)
{
}
#endif

//*****************************************************************************
//
// BMP180 Sensor callback function.  Called at the end of BMP180 sensor driver
// transactions. This is called from I2C interrupt context. Therefore, we just
// set a flag and let main do the bulk of the computations and display.
//
//*****************************************************************************
void BMP180AppCallback(void* pvCallbackData, uint_fast8_t ui8Status)
{
    if(ui8Status == I2CM_STATUS_SUCCESS)
    {
        g_vui8DataFlag = 1;
    }
}

//*****************************************************************************
//
// Called by the NVIC as a result of I2C3 Interrupt. I2C3 is the I2C connection
// to the BMP180.
//
//*****************************************************************************
void
BMP180I2CIntHandler(void)
{
    //
    // Pass through to the I2CM interrupt handler provided by sensor library.
    // This is required to be at application level so that I2CMIntHandler can
    // receive the instance structure pointer as an argument.
    //
    I2CMIntHandler(&g_sI2CInst);
}

//*****************************************************************************
//
// Called by the NVIC as a SysTick interrupt, which is used to generate the
// sample interval
//
//*****************************************************************************
void
SysTickIntHandler()
{
    GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1, GPIO_PIN_1);
    BMP180DataRead(&g_sBMP180Inst, BMP180AppCallback, &g_sBMP180Inst);
    GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1, 0x00);
}

//*****************************************************************************
//
// Configure the UART and its pins.  This must be called before UARTprintf().
//
//*****************************************************************************
void
ConfigureUART(void)
{
    //
    // Enable the GPIO Peripheral used by the UART.
    //
    MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);

    //
    // Enable UART0
    //
    MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);

    //
    // Configure GPIO Pins for UART mode.
    //
    MAP_GPIOPinConfigure(GPIO_PA0_U0RX);
    MAP_GPIOPinConfigure(GPIO_PA1_U0TX);
    MAP_GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);

    //
    // Use the internal 16MHz oscillator as the UART clock source.
    //
    UARTClockSourceSet(UART0_BASE, UART_CLOCK_PIOSC);

    //
    // Initialize the UART for console I/O.
    //
    UARTStdioConfig(0, 115200, 16000000);
}

//*****************************************************************************
//
// Main 'C' Language entry point.
//
//*****************************************************************************
int
main(void)
{
    float fTemperature, fPressure, fAltitude;
    int32_t i32IntegerPart;
    int32_t i32FractionPart;

    //
    // Setup the system clock to run at 40 MHz from PLL with crystal reference
    //
    MAP_SysCtlClockSet(SYSCTL_SYSDIV_5 | SYSCTL_USE_PLL | SYSCTL_XTAL_16MHZ |
                       SYSCTL_OSC_MAIN);

    //
    // Initialize the UART.
    //
    ConfigureUART();

    //
    // Print the welcome message to the terminal.
    //
    UARTprintf("\033[2JBMP180 Example\n");

    //
    // Set the color to a white approximation.
    //
    g_pui32Colors[RED] = 0x8000;
    g_pui32Colors[BLUE] = 0x8000;
    g_pui32Colors[GREEN] = 0x8000;

    //
    // Initialize RGB driver. Use a default intensity and blink rate.
    //
    RGBInit(0);
    RGBColorSet(g_pui32Colors);
    RGBIntensitySet(0.5f);
    RGBEnable();

    //
    // The I2C3 peripheral must be enabled before use.
    //
    MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C3);
    MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOD);

    //
    // Configure the pin muxing for I2C3 functions on port D0 and D1.
    // This step is not necessary if your part does not support pin muxing.
    //
    MAP_GPIOPinConfigure(GPIO_PD0_I2C3SCL);
    MAP_GPIOPinConfigure(GPIO_PD1_I2C3SDA);

    //
    // Select the I2C function for these pins.  This function will also
    // configure the GPIO pins pins for I2C operation, setting them to
    // open-drain operation with weak pull-ups.  Consult the data sheet
    // to see which functions are allocated per pin.
    //
    GPIOPinTypeI2CSCL(GPIO_PORTD_BASE, GPIO_PIN_0);
    MAP_GPIOPinTypeI2C(GPIO_PORTD_BASE, GPIO_PIN_1);

    //
    // Initialize the GPIO for the LED.
    //
    MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
    MAP_GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_1);
    MAP_GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1, 0x00);

    //
    // Enable interrupts to the processor.
    //
    MAP_IntMasterEnable();

    //
    // Initialize the I2C3 peripheral.
    //
    I2CMInit(&g_sI2CInst, I2C3_BASE, INT_I2C3, 0xff, 0xff,
             MAP_SysCtlClockGet());

    //
    // Initialize the BMP180.
    //
    BMP180Init(&g_sBMP180Inst, &g_sI2CInst, BMP180_I2C_ADDRESS,
               BMP180AppCallback, &g_sBMP180Inst);

    //
    // Wait for initialization callback to indicate reset request is complete.
    //
    while(g_vui8DataFlag == 0)
    {
        //
        // Wait for I2C Transactions to complete.
        //
    }

    //
    // Reset the data ready flag
    //
    g_vui8DataFlag = 0;

    //
    // Enable the system ticks at 10 Hz.
    //
    MAP_SysTickPeriodSet(MAP_SysCtlClockGet() / (10 * 3));
    MAP_SysTickIntEnable();
    MAP_SysTickEnable();

    //
    // After all the init and config we start blink the LED
    //
    RGBBlinkRateSet(1.0f);

    //
    // Begin the data collection and printing.  Loop Forever.
    //
    while(1)
    {
        //
        // Read the data from the BMP180 over I2C.  This command starts a
        // temperature measurement.  Then polls until temperature is ready.
        // Then automatically starts a pressure measurement and polls for that
        // to complete. When both measurement are complete and in the local
        // buffer then the application callback is called from the I2C
        // interrupt context.  Polling is done on I2C interrupts allowing
        // processor to continue doing other tasks as needed.
        //
        BMP180DataRead(&g_sBMP180Inst, BMP180AppCallback, &g_sBMP180Inst);
        while(g_vui8DataFlag == 0)
        {
            //
            // Wait for the new data set to be available.
            //
        }

        //
        // Reset the data ready flag.
        //
        g_vui8DataFlag = 0;

        //
        // Get a local copy of the latest temperature data in float format.
        //
        BMP180DataTemperatureGetFloat(&g_sBMP180Inst, &fTemperature);

        //
        // Convert the floats to an integer part and fraction part for easy
        // print.
        //
        i32IntegerPart = (int32_t) fTemperature;
        i32FractionPart =(int32_t) (fTemperature * 1000.0f);
        i32FractionPart = i32FractionPart - (i32IntegerPart * 1000);
        if(i32FractionPart < 0)
        {
            i32FractionPart *= -1;
        }

        //
        // Print temperature with three digits of decimal precision.
        //
        UARTprintf("Temperature %3d.%03d\t\t", i32IntegerPart, i32FractionPart);

        //
        // Get a local copy of the latest air pressure data in float format.
        //
        BMP180DataPressureGetFloat(&g_sBMP180Inst, &fPressure);

        //
        // Convert the floats to an integer part and fraction part for easy
        // print.
        //
        i32IntegerPart = (int32_t) fPressure;
        i32FractionPart =(int32_t) (fPressure * 1000.0f);
        i32FractionPart = i32FractionPart - (i32IntegerPart * 1000);
        if(i32FractionPart < 0)
        {
            i32FractionPart *= -1;
        }

        //
        // Print Pressure with three digits of decimal precision.
        //
        UARTprintf("Pressure %3d.%03d\t\t", i32IntegerPart, i32FractionPart);

        //
        // Calculate the altitude.
        //
        fAltitude = 44330.0f * (1.0f - powf(fPressure / 101325.0f,
                                            1.0f / 5.255f));

        //
        // Convert the floats to an integer part and fraction part for easy
        // print.
        //
        i32IntegerPart = (int32_t) fAltitude;
        i32FractionPart =(int32_t) (fAltitude * 1000.0f);
        i32FractionPart = i32FractionPart - (i32IntegerPart * 1000);
        if(i32FractionPart < 0)
        {
            i32FractionPart *= -1;
        }

        //
        // Print altitude with three digits of decimal precision.
        //
        UARTprintf("Altitude %3d.%03d", i32IntegerPart, i32FractionPart);

        //
        // Print new line.
        //
        UARTprintf("\n");

        //
        // Delay to keep printing speed reasonable. About 100 milliseconds.
        //
        MAP_SysCtlDelay(MAP_SysCtlClockGet() / (10 * 3));

    }//while end
}

#endif





/*
 * I2C.c - I2C Library Source File
 *
 * Date:        01/10/2016
 * Revision:        1.05
 * Author:      PIFers
 *
 * This function set supports the communication between TIVA and
 * fram 24L via I2C interface.
 *
 * This library supports 4 I2C modules (from 0 to 3).
 *
 *      Hardware connections:
 *      I2Cx |  SDA  |  SCL
 *      ---- + ----- + -----
 *      I2C0 |  PB3  |  PB2
 *      I2C1 |  PA7  |  PA6
 *      I2C2 |  PE5  |  PE4
 *      I2C3 |  PD1  |  PD0
 *
 */
/*
 *
 *
    The Inter-Integrated Circuit (I2C) API provides a set of functions for using the Tiva I2C master and
    slave modules. Functions are provided to initialize the I2C modules, to send and receive data,
    obtain status, and to manage interrupts for the I2C modules.
    The I2C master and slave modules provide the ability to communicate to other IC devices over an
    I2C bus. The I2C bus is specified to support devices that can both transmit and receive (write and
    read) data. Also, devices on the I2C bus can be designated as either a master or a slave. The
    Tiva I2C modules support both sending and receiving data as either a master or a slave, and also
    support the simultaneous operation as both a master and a slave. Finally, the Tiva I2C modules
    can operate at the following speeds: Standard (100 kbps), Fast (400 kbps), Fast plus (1 Mbps) and
    High Speed (3.33 Mbps).
    Both the master and slave I2C modules can generate interrupts. The I2C master module generates
    interrupts when a transmit or receive operation is completed (or aborted due to an error); and on
    some devices when a clock low timeout has occurred. The I2C slave module generates interrupts
    when data has been sent or requested by a master; and on some devices, when a START or STOP
    condition is present.

    When using this API to drive the I2C master module, the user must first initialize the I2C master
    module with a call to I2CMasterInitExpClk(). That function sets the bus speed and enables the
    master module.
    The user may transmit or receive data after the successful initialization of the I2C master module.
    Data is transferred by first setting the slave address using I2CMasterSlaveAddrSet(). That function
    is also used to define whether the transfer is a send (a write to the slave from the master) or a
    receive (a read from the slave by the master). Then, if connected to an I2C bus that has multiple
    masters, the Tiva I2C master must first call I2CMasterBusBusy() before attempting to initiate the
    desired transaction. After determining that the bus is not busy, if trying to send data, the user must
    call the I2CMasterDataPut() function. The transaction can then be initiated on the bus by calling
    the I2CMasterControl() function with any of the following commands:
      I2C_MASTER_CMD_SINGLE_SEND
      I2C_MASTER_CMD_SINGLE_RECEIVE
      I2C_MASTER_CMD_BURST_SEND_START
      I2C_MASTER_CMD_BURST_RECEIVE_START
    Any of those commands results in the master arbitrating for the bus, driving the start sequence
    onto the bus, and sending the slave address and direction bit across the bus. The remainder of the
    transaction can then be driven using either a polling or interrupt-driven method.

    For the single send and receive cases, the polling method involves looping on the return from
    I2CMasterBusy(). Once that function indicates that the I2C master is no longer busy, the bus transaction
    has been completed and can be checked for errors using I2CMasterErr(). If there are no
    errors, then the data has been sent or is ready to be read using I2CMasterDataGet(). For the burst
    send and receive cases, the polling method also involves calling the I2CMasterControl() function for
    each byte transmitted or received (using either the I2C_MASTER_CMD_BURST_SEND_CONT
    or I2C_MASTER_CMD_BURST_RECEIVE_CONT commands), and for the last byte
    sent or received (using either the I2C_MASTER_CMD_BURST_SEND_FINISH or
    I2C_MASTER_CMD_BURST_RECEIVE_FINISH commands). If any error is detected
    during the burst transfer, the I2CMasterControl() function should be called using the
    appropriate stop command (I2C_MASTER_CMD_BURST_SEND_ERROR_STOP or
    I2C_MASTER_CMD_BURST_RECEIVE_ERROR_STOP).
    For the interrupt-driven transaction, the user must register an interrupt handler for the I2C devices
    and enable the I2C master interrupt; the interrupt occurs when the master is no longer busy

 *
 */


