/* --COPYRIGHT--,BSD
 * Copyright (c) 2023, 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.
 * --/COPYRIGHT--*/

#include <driverlib.h>
#include <msp430.h>

/*
 * This firmware programs EEPROM settings into the MCF8316A using the MSP-EXP430FR2355
 * The EEPROM register settings are hard coded in the eeprom_regmap array below
 *
 * The LaunchPad will program the corresponding EEPROM setting based on which user button is pressed:
 *
 *     S1 button pressed = Program the hard coded register settings in global array variable below
 *
 * After pressing S1 button
 *
 *       RED LED1 indicates EEPROM programming failure
 *     GREEN LED2 indicates EEPROM programming success
 *
 */

#define I2C_TARGET_ADDR 0x01
#define INTER_BYTE_DELAY 5600  // at least 100 us
#define I2C_TIMEOUT 100000

#define MCT_SPEED_OVERRIDE_REG_ADDR 0xE8
#define MCT_SPEED_OVERRIDE_REG_DATA 0x00008000
#define MCT_EEPROM_WRITE_ADDR 0xE6

#define MCF_SPEED_OVERRIDE_REG_ADDR 0xEC
#define MCF_EEPROM_WRITE_ADDR 0xEA
#define MCF_SPEED_OVERRIDE_REG_DATA 0x80000000

#define MCFT8329A_DEVICE_ID 0xC8832904
#define MCT8329_DEVICE_ID 0x15030

bool buttonS1Pressed = false;

// User Configured regmap
#define eeprom_array_size 24

unsigned char i2c_address;
unsigned long device_id, spd_override_addr, spd_override_data,eeprom_write_addr;
unsigned long pre_eeprom_write_regs[eeprom_array_size] = {0};
unsigned long post_eeprom_write_regs[eeprom_array_size] = {0};

// Values below are example default register settings
// Change the below register map settings to your desired settings
unsigned long eeprom_regmap[eeprom_array_size][2] = {
    {0x80, 0x7F404D01},  // ISD_CONFIG
    {0x82, 0x6D8665B3},  // REV_DRIVE_CONFIG
    {0x84, 0x30BC7855},  // MOTOR_STARTUP1
    {0x86, 0x09260600},  // MOTOR_STARTUP2
    {0x88, 0x02A1A404},  // CLOSED_LOOP1
    {0x8A, 0x34C92001},  // CLOSED_LOOP2
    {0x8C, 0x001AC953},  // CLOSED_LOOP3
    {0x8E, 0x30010000},  // CLOSED_LOOP4
    {0x90, 0x1F400640},  // FAULT_CONFIG1
    {0x92, 0x71783604},  // FAULT_CONFIG2
    {0x94, 0x0000000A},  // SPEED_PROFILES1
    {0x96, 0x24DB7200},  // SPEED_PROFILES2
    {0x98, 0x48DB6946},  // SPEED_PROFILES3
    {0x9A, 0x032184A6},  // SPEED_PROFILES4
    {0x9C, 0x34C80AFC},  // SPEED_PROFILES5
    {0x9E, 0x4787D70C},  // SPEED_PROFILES6
    {0xA0, 0x000C9932},  // INT_ALGO_1
    {0xA2, 0x1F6BF200},  // INT_ALGO_2
    {0xA4, 0x00708000},  // PIN_CONFIG
    {0xA6, 0x20600004},  // DEVICE_CONFIG1
    {0xA8, 0x180020AA},  // DEVICE_CONFIG2
    {0xAA, 0x27100008},  // PERI_CONFIG1
    {0xAC, 0x000600FC},  // GD_CONFIG1
    {0xAE, 0x00000000},  // GD_CONFIG2
};

/* Function Declarations */
void init_GPIO(void);
void init_CS(void);
void init_UART(void);
void init_I2C(void);
bool I2C_write(unsigned long addr, unsigned long writedata, uint32_t timeout);
bool I2C_read(unsigned long addr, unsigned long * result, uint32_t timeout);
uint8_t findI2CAddress(void);

/**
 * main.c
 */
int main(void)
{
    /* Stop watchdog timer */
    WDT_A_hold(WDT_A_BASE);

    init_GPIO();
    init_CS();
    init_I2C();

    __enable_interrupt();

    while (1)
    {
        if (buttonS1Pressed)
        {
            unsigned int i;
            bool prog_error = false;

            // Turn off both LED1 and LED2
            GPIO_setOutputLowOnPin(GPIO_PORT_P1, GPIO_PIN0);
            GPIO_setOutputLowOnPin(GPIO_PORT_P6, GPIO_PIN6);

            i2c_address = findI2CAddress();

            if (i2c_address == 0x80)
            {
                prog_error = true;
                break;
            }
            else
            {
                if(!I2C_read(0x7c, &device_id, I2C_TIMEOUT))
                {
                    prog_error = true;
                    break;
                }

                if (device_id == MCFT8329A_DEVICE_ID)
                {
                    if(!I2C_read(0x7e, &device_id, I2C_TIMEOUT))
                    {
                        prog_error = true;
                        break;
                    }
                    else
                    {
                        if (device_id == MCT8329_DEVICE_ID)
                            device_id = 8;
                        else
                            device_id = 5;
                    }
                }
                else
                {
                    device_id &= 0x0F000000;
                    device_id >>= 24;
                }

                if (device_id == 8)
                {
                    spd_override_addr = MCT_SPEED_OVERRIDE_REG_ADDR;
                    spd_override_data = MCT_SPEED_OVERRIDE_REG_DATA;
                    eeprom_write_addr =  MCT_EEPROM_WRITE_ADDR;
                }
                else
                {
                    spd_override_addr = MCF_SPEED_OVERRIDE_REG_ADDR;
                    spd_override_data = MCF_SPEED_OVERRIDE_REG_DATA;
                    eeprom_write_addr =  MCF_EEPROM_WRITE_ADDR;
                }
            }

            if (!I2C_write(MCT_SPEED_OVERRIDE_REG_ADDR, MCT_SPEED_OVERRIDE_REG_DATA, I2C_TIMEOUT))
            {
                prog_error = true;
                break;
            }

            /* Read the initial EEPROM registers into pre_eeprom_write_regs array */
            for (i = 0; i < eeprom_array_size; i++)
            {
                if(!I2C_read(eeprom_regmap[i][0], &(pre_eeprom_write_regs[i]), I2C_TIMEOUT))
                {
                    prog_error = true;
                    break;
                }
            }

            __no_operation();

            /* Write the user configured EEPROM registers to the shadow register */
            for (i = 0; i < eeprom_array_size; i++)
            {
                if (!I2C_write(eeprom_regmap[i][0], eeprom_regmap[i][1], I2C_TIMEOUT))
                {
                    prog_error = true;
                    break;
                }
            }

            __no_operation();  // For setting breakpoint while debugging


            /* Set the EEPROM_WRITE bit along with EEPROM_WRITE_ACCESS_KEY in the DEV_CTRL register (0xEA located in RAM)
             * This copies the content of the shadow registers into the EERPOM memory
             */
            if (!I2C_write(MCT_EEPROM_WRITE_ADDR, 0x8A500000, I2C_TIMEOUT))
                prog_error = true;

            __delay_cycles(14000000); // delay at least 100ms for copy operation from Shadow-register to EEPROM to complete
            __no_operation(); // For setting breakpoint while debugging

            /* Set the EEPROM_READ bit in the DEV_CTRL register (0xEA located in RAM
             * This copies the content of the EEPROM memory back into the shadow registers
             */
            if (!I2C_write(MCT_EEPROM_WRITE_ADDR, 0x40000000, I2C_TIMEOUT))
                prog_error = true;

            __delay_cycles(6000000); // delay at least 100ms for copy operation from EEPROM to Shadow-register to complete
            __no_operation(); // For setting breakpoint while debugging


            /* Read back the EEPROM registers into post_eeprom_write_regs array */
            for (i = 0; i < eeprom_array_size; i++)
            {
                if(!I2C_read(eeprom_regmap[i][0], &(post_eeprom_write_regs[i]), I2C_TIMEOUT))
                {
                    prog_error = true;
                    break;
                }

                if ((post_eeprom_write_regs[i] & 0x7FFFFFFF) != (eeprom_regmap[i][1] & 0x7FFFFFFF))
                {
                    prog_error = true;
                    break;
                }
            }

            if (prog_error)
                // Turn on red LED1
                GPIO_setOutputHighOnPin(GPIO_PORT_P1, GPIO_PIN0);
            else
                // Turn on green LED2
                GPIO_setOutputHighOnPin(GPIO_PORT_P6, GPIO_PIN6);

            buttonS1Pressed = false;
        }

        __bis_SR_register(LPM0_bits + GIE);
        __no_operation();
    }
}

void init_GPIO(void) {
    // Initialize push button S1
    GPIO_setAsInputPinWithPullUpResistor(GPIO_PORT_P4, GPIO_PIN1);
    GPIO_selectInterruptEdge(GPIO_PORT_P4, GPIO_PIN1, GPIO_HIGH_TO_LOW_TRANSITION);
    GPIO_clearInterrupt(GPIO_PORT_P4, GPIO_PIN1);
    GPIO_enableInterrupt(GPIO_PORT_P4, GPIO_PIN1);

    // Initialize LED1 and LED2
    GPIO_setOutputLowOnPin(GPIO_PORT_P1, GPIO_PIN0);
    GPIO_setOutputLowOnPin(GPIO_PORT_P6, GPIO_PIN6);

    GPIO_setAsOutputPin(GPIO_PORT_P1, GPIO_PIN0);
    GPIO_setAsOutputPin(GPIO_PORT_P6, GPIO_PIN6);

    PMM_unlockLPM5();
}

void init_CS(void) {
    // Configure Pins for XIN and XOUT
    //Set P2.6 and P2.7 as Module Function Input.
    GPIO_setAsPeripheralModuleFunctionInputPin(
        GPIO_PORT_P2,
        GPIO_PIN6 + GPIO_PIN7,
        GPIO_SECONDARY_MODULE_FUNCTION
    );

    //Initializes the XT1 and XT2 crystal frequencies being used
    CS_setExternalClockSource(
        32768
        );

    //Initialize XT1. Returns STATUS_SUCCESS if initializes successfully
    CS_turnOnXT1LFWithTimeout(CS_XT1_DRIVE_0, 50000);

     CS_initClockSignal(CS_FLLREF, CS_XT1CLK_SELECT, CS_CLOCK_DIVIDER_1);


     //Set ACLK = REFOCLK with clock divider of 1
     CS_initClockSignal(CS_ACLK,CS_XT1CLK_SELECT,CS_CLOCK_DIVIDER_1);

     //Set MCLK = DCO with frequency divider of 1
     CS_initClockSignal(CS_MCLK,SELMS__DCOCLKDIV,CS_CLOCK_DIVIDER_1);

     //Set SMCLK = DCO with frequency divider of 1
     CS_initClockSignal(CS_SMCLK,SELMS__DCOCLKDIV,CS_CLOCK_DIVIDER_1);


     CS_initFLL(16000, 488);          // Clock program to 16MHz
}

void init_I2C(void)
{
    // Configure Pins for I2C
    GPIO_setAsPeripheralModuleFunctionInputPin(
        GPIO_PORT_P4,
        GPIO_PIN7,
        GPIO_PRIMARY_MODULE_FUNCTION
    );
    GPIO_setAsPeripheralModuleFunctionInputPin(
        GPIO_PORT_P4,
        GPIO_PIN6,
        GPIO_PRIMARY_MODULE_FUNCTION
    );

    EUSCI_B_I2C_initMasterParam param = {0};
    param.selectClockSource = EUSCI_B_I2C_CLOCKSOURCE_SMCLK;
    param.i2cClk = CS_getSMCLK();
    param.dataRate = EUSCI_B_I2C_SET_DATA_RATE_400KBPS;
    param.byteCounterThreshold = 4;
    param.autoSTOPGeneration = EUSCI_B_I2C_SEND_STOP_AUTOMATICALLY_ON_BYTECOUNT_THRESHOLD;
    EUSCI_B_I2C_initMaster(EUSCI_B1_BASE, &param);

    //Set Master in receive mode
    EUSCI_B_I2C_setMode(EUSCI_B1_BASE,
        EUSCI_B_I2C_TRANSMIT_MODE
    );

    //Enable I2C Module to start operations
    EUSCI_B_I2C_enable(EUSCI_B1_BASE);
}

bool I2C_write(unsigned long addr, unsigned long writedata, uint32_t timeout) {
    EUSCI_B_I2C_initMasterParam param = {0};
    param.selectClockSource = EUSCI_B_I2C_CLOCKSOURCE_SMCLK;
    param.i2cClk = CS_getSMCLK();
    param.dataRate = EUSCI_B_I2C_SET_DATA_RATE_400KBPS;
    param.byteCounterThreshold = 0;
    param.autoSTOPGeneration = UCASTP_0;
    EUSCI_B_I2C_initMaster(EUSCI_B1_BASE, &param);

    //Enable I2C Module to start operations
    EUSCI_B_I2C_enable(EUSCI_B1_BASE);

    // Construct the 24 bit control word (refer to datasheet section 7.6.2.1)
    char control_word[3] = {0x10, (addr&0x00000F00)>>8, addr&0x000000FF};

    /* Set slave Address */
    EUSCI_B_I2C_setSlaveAddress(EUSCI_B1_BASE,
                                i2c_address
                                );

    /* Set master to transmit mode */
    EUSCI_B_I2C_setMode(EUSCI_B1_BASE,
        EUSCI_B_I2C_TRANSMIT_MODE);

    EUSCI_B_I2C_masterSendStart(EUSCI_B1_BASE);

    __delay_cycles(INTER_BYTE_DELAY);

    /* Send bits CW23-CW16 of the control word */
    if (!EUSCI_B_I2C_masterSendMultiByteNextWithTimeout(EUSCI_B1_BASE, control_word[0], timeout))
        return 0;

    __delay_cycles(INTER_BYTE_DELAY);

    /* Send bits CW15-CW8 of the control word */
    if (!EUSCI_B_I2C_masterSendMultiByteNextWithTimeout(EUSCI_B1_BASE, control_word[1], timeout))
        return 0;

    __delay_cycles(INTER_BYTE_DELAY);

    /* Send bits CW7-CW0 of the control word */
    if (!EUSCI_B_I2C_masterSendMultiByteNextWithTimeout(EUSCI_B1_BASE, control_word[2], timeout))
        return 0;

    __delay_cycles(INTER_BYTE_DELAY);

    /* Send D7-D0 of the 32-bit data */
    if (!EUSCI_B_I2C_masterSendMultiByteNextWithTimeout(EUSCI_B1_BASE, writedata & 0x000000FF, timeout))
        return 0;

    __delay_cycles(INTER_BYTE_DELAY);

    /* Send D15-D8 of the 32-bit data */
    if (!EUSCI_B_I2C_masterSendMultiByteNextWithTimeout(EUSCI_B1_BASE, (writedata & 0x0000FF00)>>8, timeout))
        return 0;

    __delay_cycles(INTER_BYTE_DELAY);

    /* Send D23-D16 of the 32-bit data */
    if (!EUSCI_B_I2C_masterSendMultiByteNextWithTimeout(EUSCI_B1_BASE, (writedata & 0x00FF0000)>>16, timeout))
        return 0;

    __delay_cycles(INTER_BYTE_DELAY);

    /* Send D31-D23 of the 32-bit data */
    if (!EUSCI_B_I2C_masterSendMultiByteNextWithTimeout(EUSCI_B1_BASE, (writedata & 0xFF000000)>>24, timeout))
        return 0;

    __delay_cycles(INTER_BYTE_DELAY);

    EUSCI_B_I2C_masterSendMultiByteStop(EUSCI_B1_BASE);

    return 1;
}

bool I2C_read(unsigned long addr, unsigned long *result, uint32_t timeout) {
    // Temp variables
    uint8_t val;
    unsigned long r = 0;

    EUSCI_B_I2C_initMasterParam param = {0};
    param.selectClockSource = EUSCI_B_I2C_CLOCKSOURCE_SMCLK;
    param.i2cClk = CS_getSMCLK();
    param.dataRate = EUSCI_B_I2C_SET_DATA_RATE_400KBPS;
    param.byteCounterThreshold = 4;
    param.autoSTOPGeneration = EUSCI_B_I2C_SEND_STOP_AUTOMATICALLY_ON_BYTECOUNT_THRESHOLD;
    EUSCI_B_I2C_initMaster(EUSCI_B1_BASE, &param);

    //Enable I2C Module to start operations
    EUSCI_B_I2C_enable(EUSCI_B1_BASE);

    // Construct the 24 bit control word (refer to datasheet section 7.6.2.1)
    char control_word[3] = {0x90, (addr&0x00000F00)>>8, addr&0x000000FF};

    /* Set slave Address */
    EUSCI_B_I2C_setSlaveAddress(EUSCI_B1_BASE,
                                i2c_address
                                );

    /* Set master to transmit mode */
    EUSCI_B_I2C_setMode(EUSCI_B1_BASE,
        EUSCI_B_I2C_TRANSMIT_MODE);

    EUSCI_B_I2C_masterSendStart(EUSCI_B1_BASE);

    __delay_cycles(INTER_BYTE_DELAY);

    /* Send bits CW23-CW16 of the control word */
    if (!EUSCI_B_I2C_masterSendMultiByteNextWithTimeout(EUSCI_B1_BASE, control_word[0], timeout))
        return 0;

    __delay_cycles(INTER_BYTE_DELAY);

    /* Send bits CW15-CW8 of the control word */
    if (!EUSCI_B_I2C_masterSendMultiByteNextWithTimeout(EUSCI_B1_BASE, control_word[1], timeout))
        return 0;

    __delay_cycles(INTER_BYTE_DELAY);

    /* Send bits CW7-CW0 of the control word */
    if (!EUSCI_B_I2C_masterSendMultiByteNextWithTimeout(EUSCI_B1_BASE, control_word[2], timeout))
        return 0;

    __delay_cycles(INTER_BYTE_DELAY);

    /*
     * Set master to receive mode and generate Start condition
     * This sends out the slave address and continues to read
     * until you issue a STOP
     */
    EUSCI_B_I2C_masterReceiveStart(EUSCI_B1_BASE);

    __delay_cycles(INTER_BYTE_DELAY);

    val = EUSCI_B_I2C_masterReceiveSingle(EUSCI_B1_BASE);
    r |= val;
    __delay_cycles(INTER_BYTE_DELAY);

    val = EUSCI_B_I2C_masterReceiveSingle(EUSCI_B1_BASE);
    r |= ((unsigned long)val)<<8;
    __delay_cycles(INTER_BYTE_DELAY);

    val = EUSCI_B_I2C_masterReceiveSingle(EUSCI_B1_BASE);
    r |= ((unsigned long)val)<<16;
    __delay_cycles(INTER_BYTE_DELAY);

    val = EUSCI_B_I2C_masterReceiveSingle(EUSCI_B1_BASE);
    r |= ((unsigned long)val)<<24;
    *result = r;
    __delay_cycles(INTER_BYTE_DELAY);

    return 1;
}

uint8_t findI2CAddress(void) {
    int i;
    unsigned long result;
    for (i = 0; i<127; i++) {
        if (I2C_read(i,&result, I2C_TIMEOUT))
            return i;
        __delay_cycles(2000);
    }
    return 128;
}

#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector=PORT4_VECTOR
__interrupt
#elif defined(__GNUC__)
__attribute__((interrupt(PORT4_VECTOR)))
#endif
void Port4_ISR(void)
{
    // Left button S1
    if (P4IFG & BIT1)
    {
        P4IFG &= ~BIT1;

        // Left button S1 released
        if (P4IN & BIT1)
        {
            P4IES |= BIT1;       // P4.1 Hi/Lo edge
            buttonS1Pressed = true;
            __bic_SR_register_on_exit(LPM0_bits);
        }
        else
            P4IES &= ~BIT1;       // P4.1 Lo/Hi edge
    }
}
