/*
 * 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 entireley). 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)

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

    if( fram_probe() != 0xAA55AA55)           // read the manufacturer id
    {
        uprintf("\r\nERROR: FRAM fails probe - switch to HIB module\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
    framBuf[0] = 0x55;
    framBuf[1] = 0xAA;
    framBuf[2] = 0x55;
    framBuf[3] = 0xAA;
    i2c0_write(I2C_FRAM_ADDR, framBuf, 4, FRAM_PROBESIG_ADDR);      // probe signatue 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;
    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++)               // 0->0
        framBuf[indx] = indx;
    i2c0_write(I2C_FRAM_ADDR, framBuf, 128, 128);

    //-- first block of 256 bytes is written

    for(indx=0; indx<128; indx++)               // 0->255
        framBuf[indx] = 255-indx;
    i2c0_write(I2C_FRAM_ADDR|0x01, framBuf, 128, 0x00);

    for(indx=0; indx<128; indx++)               // 0->128
        framBuf[indx] = 128+indx;
    i2c0_write(I2C_FRAM_ADDR|0x01, framBuf, 128, 128);

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

    i2c0_read(I2C_FRAM_ADDR, framBuf, 128, 0x00, TRUE);
    if(framBuf[0] == 127)
    {
        uprintf("\r\nFirst Block, First Byte of FRAM is good!\r\n");
    }
    else
    {
        uprintf("\r\nFirst Block, First Byte of FRAM is bad ;-{\r\n");
    }

    i2c0_read(I2C_FRAM_ADDR, framBuf, 128, 128, TRUE);
    if(framBuf[0] == 0)
    {
        uprintf("\r\nSecond block first Byte of FRAM is good!\r\n");
    }
    else
    {
        uprintf("\r\nSecond block First Byte of FRAM is bad ;-{\r\n");
    }

    i2c0_read(I2C_FRAM_ADDR|0x01, framBuf, 128, 0x00, TRUE);
    if(framBuf[0] == 255)
    {
        uprintf("\r\nThird Block, First Byte of FRAM is good!\r\n");
    }
    else
    {
        uprintf("\r\nThird Block, First Byte of FRAM is bad ;-{\r\n");
    }

    i2c0_read(I2C_FRAM_ADDR|0x01, framBuf, 128, 128, TRUE);
    if(framBuf[0] == 128)
    {
        uprintf("\r\nFourth block first Byte of FRAM is good!\r\n");
    }
    else
    {
        uprintf("\r\nFourth block First Byte of FRAM is bad ;-{\r\n");
    }


}

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 previously 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_add: the slave address
 *  *ucData    : the data that are going to be sent
 *  uiCount    : number of byte will be sent
 *  ucStart_add: register address you want to write or a control byte
 *
 * Return: none
 *****************************************************************************/
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.
    //I2CMasterSlaveAddrSet(ui32Base, uiSlave_add, false);
    ROM_I2CMasterSlaveAddrSet(I2C0_BASE, uiSlave_add, false);
    // Place the address to be written in the data register.
    ROM_I2CMasterDataPut(I2C0_BASE, ucStart_add);
    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_add: the slave address
 *  *ucRec_Data: a variable to save the data received
 *  uiCount    : number of byte will be received
 *  ucStart_add: register address you want to read or a control byte
 *
 * Return: none
 *****************************************************************************/
//void I2C_Read(uint32_t ui32Base, unsigned char uiSlave_add, unsigned char *ucRec_Data, uint16_t uiCount, unsigned char ucStart_add, bool bDummyRead)
void i2c0_read(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_add, false);

    // Place the address to be written in the data register.
    ROM_I2CMasterDataPut(I2C0_BASE, ucStart_add);

    // 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_add, 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_I2CMasterIntStatus(ui32Base, false) == 0)
//  {
//  }
    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))
        ;
}


/*
 * 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

 *
 */


