/* --COPYRIGHT--,BSD
 * Copyright (c) 2014, 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--*/
//*****************************************************************************
//
// esi.h - Driver for the ESI Module.
//
//*****************************************************************************

//*****************************************************************************
//
//! \addtogroup esi_api
//! @{
//
//*****************************************************************************

#include "inc/hw_regaccess.h"
#include "inc/hw_memmap.h"

#ifdef __MSP430_HAS_ESI__
#include "esi.h"

#include <assert.h>

// Uncomment for finding lower peak of the lower half cycle.
// This required to set ESI comparator output as inverted
#define INVERTED

static uint16_t measureESIOSC(void);
static void FindDAC(uint8_t selected_channel,
                    uint8_t software_trigger);

const ESI_AFE1_InitParams ESI_AFE1_INITPARAMS_DEFAULT =
{ESI_EXCITATION_CIRCUIT_DISABLED,
 ESI_SAMPLE_HOLD_DISABLED,
 ESI_MID_VOLTAGE_GENERATOR_DISABLED,
 ESI_SAMPLE_HOLD_VSS_TO_ESIVSS,
 ESI_INVERTER_FOR_AFE1_DISABLE};

const ESI_AFE2_InitParams ESI_AFE2_INITPARAMS_DEFAULT = {
    ESI_AFE2_INPUT_SELECT_CHx,
    ESI_INVERTER_FOR_AFE2_DISABLE,
    ESI_TSM_COMPARATOR_CONTROL_AFE2_DISABLE,
    ESI_TSM_DAC_CONTROL_AFE2_DISABLE
};

const ESI_TSM_InitParams ESI_TSM_INITPARAMS_DEFAULT = { ESI_TSM_SMCLK_DIV_1,
                                                        ESI_TSM_ACLK_DIV_1,
                                                        ESI_TSM_START_TRIGGER_DIV_2,
                                                        ESI_TSM_REPEAT_NEW_TRIGGER,
                                                        ESI_TSM_STOP_SEQUENCE,
                                                        ESI_TSM_HIGH_FREQ_CLK_FUNCTION_ON};

const ESI_PSM_InitParams ESI_PSM_INITPARAMS_DEFAULT = { ESI_PSM_Q6_DISABLE,
                                                        ESI_PSM_Q7_TRIGGER_DISABLE,
                                                        ESI_PSM_CNT0_DISABLE,
                                                        ESI_PSM_CNT0_RESET,
                                                        ESI_PSM_CNT1_DISABLE,
                                                        ESI_PSM_CNT1_RESET,
                                                        ESI_PSM_CNT2_DISABLE,
                                                        ESI_PSM_CNT2_RESET,
                                                        ESI_PSM_S3_SELECT,
                                                        ESI_PSM_TEST4_IS_Q2,};

//*****************************************************************************
//
//! Get ESI PSM Counter 0 Value
//!
//! This function reads the ESI Counter 0 register
//!
//! \return  Counter value
//
//*****************************************************************************
uint16_t ESI_getCounter0(void)
{
    return (ESICNT0);
}

//*****************************************************************************
//
//! Get ESI PSM Counter 1 Value
//!
//! This function reads the ESI Counter1 register
//!
//! \return Counter value
//
//*****************************************************************************
uint16_t ESI_getCounter1(void)
{
    return (ESICNT1);
}

//*****************************************************************************
//
//! Get ESI PSM Counter 2 Value
//!
//! This function reads the ESI Counter2 register
//!
//! \return Counter value
//
//*****************************************************************************
uint16_t ESI_getCounter2(void)
{
    return (ESICNT2);
}

//*****************************************************************************
//
//! Get ESI PSM Oscillator Counter Value
//!
//! This function reads the ESI Oscillator Counter register
//!
//! \return Counter value
//
//*****************************************************************************
uint16_t ESI_getOscCounter(void)
{
    return (ESICNT3);
}

//*****************************************************************************
//
//! Initializes the ESI analog front end AFE1
//!
//! \param params is ESI_AFE1_InitParams struct
//!
//! This functions initializes the ESI analog front end AFE1.
//!
//! \return None
//
//*****************************************************************************

void ESI_AFE1_init(ESI_AFE1_InitParams *params)
{
    // Unset the AFE1 bits
    ESIAFE &= ~(ESITEN + ESISH + ESIVCC2 + ESIVSS + ESICACI3 + ESICISEL +
                ESICA1X + ESICA1INV);
    ESIAFE |=
        params->excitationCircuitSelect +
        params->sampleAndHoldSelect +
        params->midVoltageGeneratorSelect +
        params->sampleAndHoldVSSConnect +
        params->inverterSelectOutputAFE1
    ;

    switch(params->inputSelectAFE1)
    {
    case ESI_AFE1_INPUT_SELECT_CHx:
        break;
    case ESI_AFE1_INPUT_SELECT_CIx:
        ESIAFE |= ESICA1X;
        break;
    case ESI_AFE1_INPUT_SELECT_CI3:
        ESIAFE |= ESICA1X;
        ESIAFE &= ~ESICISEL;
        ESIAFE |= ESICACI3;
        break;
    case ESI_AFE1_INPUT_SELECT_CI:
        ESIAFE |= ESICA1X;
        ESIAFE |= ESICISEL;
        break;
    default:
        break;
    }
}

//*****************************************************************************
//
//! Initializes the ESI analog front end - AFE2
//!
//! \param params is ESI_AFE2_InitParams struct
//!
//! This functions initializes the ESI analog front end AFE2
//!
//! \return None
//
//*****************************************************************************

void ESI_AFE2_init(ESI_AFE2_InitParams *params)
{
    // Unset the AFE2 bits
    ESIAFE &= ~(ESICA2X + ESICA2INV + ESICA2EN + ESIDAC2EN);

    ESIAFE |=
        params->inputSelectAFE2 +
        params->inverterSelectOutputAFE2 +
        params->tsmControlComparatorAFE2 +
        params->tsmControlDacAFE2
    ;
}

//*****************************************************************************
//
//! Reads the latched comparator outputs form the AFEs
//!
//! \param channelSelect. Valid values are
//!                 ESI_AFE1_CHANNEL0_SELECT
//!                 ESI_AFE1_CHANNEL1_SELECT
//!                 ESI_AFE1_CHANNEL2_SELECT
//!                 ESI_AFE1_CHANNEL3_SELECT
//!                 ESI_AFE2_CHANNEL0_SELECT
//!                 ESI_AFE2_CHANNEL1_SELECT
//!                 ESI_AFE2_CHANNEL2_SELECT
//!                 ESI_AFE2_CHANNEL3_SELECT
//!                 ESI_AFE1_TEST_CHANNEL0_SELECT
//!                 ESI_AFE1_TEST_CHANNEL1_SELECT
//!
//! This function gets the ESIPPU register to get latched output values of the
//! comparator outputs for AFE1 and AFE2
//!
//! \return Valid values are
//!                 ESI_AFE_OUTPUT_LOW
//!                 ESI_AFE_OUTPUT_HIGH
//
//*****************************************************************************
uint16_t ESI_getLatchedComparatorOutput(uint16_t channelSelect)
{
    uint16_t result;

    result = ESIPPU;

    return (result &= channelSelect);
}

//*****************************************************************************
//
//! Initializes the TSM
//!
//! \param params is ESI_TSM_InitParams struct
//!
//! This function initializes the TSM.
//!
//! \return None
//
//*****************************************************************************

void ESI_TSM_init(ESI_TSM_InitParams *params)
{
    ESITSM =
        params->smclkDivider +
        params->aclkDivider +
        params->startTriggerAclkDivider +
        params->repeatMode +
        params->startTriggerSelection +
        params->tsmFunctionSelection
    ;
}

//*****************************************************************************
//
//! Clear TSM entries
//!
//! This function clears all TSM entries
//!
//! \return None
//
//*****************************************************************************
void ESI_TSM_clearTable(void)
{
    uint16_t *pTsm, i;

    // Clear TSM Table (for testing only. not neccessary in real application)
    pTsm = (uint16_t *)&ESITSM0;
    for(i = 0; i < 32; i++)
    {
        *pTsm++ = 0x0200;
    }
}

//*****************************************************************************
//
//! Copy TSM entries
//!
//! This function copies all TSM entries
//!
//! \return None
//
//*****************************************************************************
void ESI_TSM_copyTable(uint16_t* tsmTable,
                       uint16_t size)
{
    uint16_t *pt_tsmTable;
    uint16_t i;

    // Copy the TSM_Table into ESI TSM registers
    // Destination pointer
    pt_tsmTable = (uint16_t *)&ESITSM0;
    // Divided by 2 because of unsigned integer (2bytes)
    i = size / 2;

    do
    {
        *pt_tsmTable++ = *tsmTable++;
    }
    while(--i);
}

//*****************************************************************************
//
//! TSM trigger using software
//!
//! This function starts a software initiated TSM sequence
//!
//! \return None
//
//*****************************************************************************
void ESI_TSM_softwareTrigger(void)
{
    ESITSM |= ESISTART;
}

//*****************************************************************************
//
//! TSM trigger using software
//!
//! This function starts a software initiated TSM sequence
//!
//! \return ESIREATx bits from selected stateRegNum
//
//*****************************************************************************
uint8_t ESI_TSM_getTSMStateDuration(uint8_t stateRegNum)
{
    volatile uint16_t* stateRegBase = (volatile uint16_t*)&ESITSM0;

    return((*(stateRegBase + stateRegNum) & 0xf800) >> 11);
}

//*****************************************************************************
//
//! TSM trigger using software
//!
//! This function starts a software initiated TSM sequence
//!
//! \return ESIREATx bits from selected stateRegNum
//
//*****************************************************************************
void ESI_TSM_setTSMStateDuration(uint8_t stateRegNum,
                                 uint8_t duration)
{
    assert(stateRegNum <= ESI_TSM_STATE_REG_31);
    assert(duration <= ESI_TSM_STATE_DURATION_MAX);

    volatile uint16_t* stateRegBase = (volatile uint16_t*)&ESITSM0;

    *(stateRegBase + stateRegNum) &= ~0xF800;

    *(stateRegBase + stateRegNum) |= (duration << 11);
}

//*****************************************************************************
//
//! Initialize Processing State Machine
//
//! \param params is ESI_PSM_InitParams struct
//!
//! This function initializes the PSM registers.
//!
//! \return None
//
//*****************************************************************************
void ESI_PSM_init(ESI_PSM_InitParams *params)
{
    ESIPSM =
        params->Q6Select +
        params->Q7TriggerSelect +
        params->count0Select +
        params->count0Reset +
        params->count1Select +
        params->count1Reset +
        params->count2Select +
        params->count2Reset +
        params->V2Select +
        params->TEST4Select
    ;
}

//*****************************************************************************
//
//! Clear PSM entries
//!
//! This function clears all PSM entries
//!
//! \return None
//
//*****************************************************************************
void ESI_PSM_clearTable(void)
{
    uint8_t *pPsm, i;

    // Clear TSM Table (for testing only. not neccessary in real application)
    pPsm = (uint8_t *)&ESIRAM0;
    for(i = 0; i < 128; i++)
    {
        *pPsm++ = 0x0;
    }
}

//*****************************************************************************
//
//! Copy PSM entries
//!
//! This function copies all PSM entries
//!
//! \return None
//
//*****************************************************************************
void ESI_PSM_copyTable(uint8_t* psmTable,
                       uint8_t size)
{
    uint8_t *pt_psmTable;
    uint8_t i;

    assert(size <= 128);

    // Copy the TSM_Table into ESI TSM registers
    pt_psmTable = (uint8_t *)&ESIRAM0;                                  // Destination pointer
    i = size;

    do
    {
        *pt_psmTable++ = *psmTable++;
    }
    while(--i);
}

//*****************************************************************************
//
//! Reset PSM counters
//!
//! \param counterToReset is the counter that needs t be reset
//!
//! This function resets the PSM counters
//!
//! \return None
//
//*****************************************************************************
void ESI_PSM_resetCounter(uint16_t counterToReset)
{
    ESIPSM |= counterToReset;
}

//*****************************************************************************
//
//! Enables the internal Oscillator
//!
//!
//! This function enables the high frequency internal oscillator
//!
//! \return None
//
//*****************************************************************************
void ESI_enableInternalOscillator(void)
{
    ESIOSC |= ESIHFSEL;
}

//*****************************************************************************
//
//! Disables the internal Oscillator
//!
//!
//! This function disables the high frequency internal oscillator
//!
//! \return None
//
//*****************************************************************************
void ESI_disableInternalOscillator(void)
{
    ESIOSC &= ~ESIHFSEL;
}

//*****************************************************************************
//
//! Connects comparator output to timerA input
//!
//! \param counterToReset ESI_TIMERA_INPUT_TSM_COMPOUT or
//!                       ESI_TIMERA_INPUT_TSM_PPUSRC
//!
//! This function connects the chosen comparator output to TimerA
//!
//! \return None
//
//*****************************************************************************
void ESI_timerAInputSelect(uint16_t select)
{
    ESICTL |= select;
}

//*****************************************************************************
//
//! Connects psm source to comparator output
//!
//! \param sourceNum PSM_S1_SOURCE, PSM_S2_SOURCE  or PSM_S3_SOURCE
//! \param sourceSelect can have the following values
//!         ESI_PSM_SOURCE_IS_ESIOUT0
//!         ESI_PSM_SOURCE_IS_ESIOUT1
//!         ESI_PSM_SOURCE_IS_ESIOUT2
//!         ESI_PSM_SOURCE_IS_ESIOUT3
//!         ESI_PSM_SOURCE_IS_ESIOUT4
//!         ESI_PSM_SOURCE_IS_ESIOUT5
//!         ESI_PSM_SOURCE_IS_ESIOUT6
//!         ESI_PSM_SOURCE_IS_ESIOUT7
//!
//! This function connects the chosen comparator output to TimerA
//!
//! \return None
//
//*****************************************************************************
void ESI_psmSourceSelect(uint16_t sourceNum,
                         uint16_t sourceSelect)
{
    switch(sourceNum)
    {
    case PSM_S1_SOURCE:
        ESICTL &= ~(ESIS1SEL0 | ESIS1SEL1 | ESIS1SEL2);
        ESICTL |= (sourceSelect << 7);
        break;
    case PSM_S2_SOURCE:
        ESICTL &= ~(ESIS2SEL0 | ESIS2SEL1 | ESIS2SEL2);
        ESICTL |= (sourceSelect << 10);
        break;
    case PSM_S3_SOURCE:
        ESICTL &= ~(ESIS3SEL0 | ESIS3SEL1 | ESIS3SEL2);
        ESICTL |= (sourceSelect << 13);
        break;
    default:
        break;
    }
}

//*****************************************************************************
//
//! Connects testChannel0 to comparator input
//!
//! \param sourceSelect can have the following values
//!         ESI_TEST_CHANNEL0_SOURCE_IS_CH0_CI0
//!         ESI_TEST_CHANNEL0_SOURCE_IS_CH1_CI1
//!         ESI_TEST_CHANNEL0_SOURCE_IS_CH2_CI2
//!         ESI_TEST_CHANNEL0_SOURCE_IS_CH3_CI3
//!
//! This function connects the chosen comparator input to the test channel0
//!
//! \return None
//
//*****************************************************************************
void ESI_testChannel0SourceSelect(uint16_t sourceSelect)
{
    ESICTL &= ~(ESI_TEST_CHANNEL0_SOURCE_IS_CH3_CI3);
    ESICTL |= sourceSelect;
}

//*****************************************************************************
//
//! Connects testChannel1to comparator input
//!
//! \param sourceSelect can have the following values
//!         ESI_TEST_CHANNEL1_SOURCE_IS_CH0_CI0
//!         ESI_TEST_CHANNEL1_SOURCE_IS_CH1_CI1
//!         ESI_TEST_CHANNEL1_SOURCE_IS_CH2_CI2
//!         ESI_TEST_CHANNEL1_SOURCE_IS_CH3_CI3
//!
//! This function connects the chosen comparator input to the test channel1
//!
//! \return None
//
//*****************************************************************************
void ESI_testChannel1SourceSelect(uint16_t sourceSelect)
{
    ESICTL &= ~(ESI_TEST_CHANNEL1_SOURCE_IS_CH3_CI3);
    ESICTL |= sourceSelect;
}

//*****************************************************************************
//
//! Enable ESI peripheral
//!
//! \return None
//
//*****************************************************************************
void ESI_enable(void)
{
    ESICTL |= ESIEN;
}

//*****************************************************************************
//
//! Disable ESI peripheral
//!
//! \return None
//
//*****************************************************************************
void ESI_disable(void)
{
    ESICTL &= ~ESIEN;
}

//*****************************************************************************
//
//! Start calibration on ESI internal Oscillator
//!
//! This function starts calibration of internal osciallator. After calling this
//! function the user and use ESI_adjustInternalOscFreq() to adjust the freq. of
//! the oscillator.
//!
//! \return None
//
//*****************************************************************************
void ESI_startInternalOscCal(void)
{
    assert(ESIOSC | ESIHFSEL);
    ESIOSC |= ESICLKGON;
}

//*****************************************************************************
//
//! Adjusts frequency ESI internal Oscillator
//!
//! This function adjusts frequency ESI internal Oscillator. It increases or
//! decrease the freq by 3% based on incOrDec value.
//!
//! \return None
//
//*****************************************************************************
void ESI_adjustInternalOscFreq(uint16_t incOrDec)
{
    uint16_t adjustValue;

    assert(ESIOSC | ESIHFSEL);

    adjustValue = ESIOSC >> 8;

    if(incOrDec == ESI_INTERNAL_OSC_FREQ_INCREASE)
    {
        adjustValue = adjustValue + 1;
        adjustValue = adjustValue << 8;
    }
    else
    {
        adjustValue = adjustValue - 1;
        adjustValue = adjustValue << 8;
    }

    ESIOSC |= adjustValue;
}

//*****************************************************************************
//
//! Sets frequency of ESI internal Oscillator
//!
//!
//! \return None
//
//*****************************************************************************
void ESI_setNominalInternalOscFreq(void)
{
    ESIOSC = ESICLKFQ5 + ESICLKGON + ESIHFSEL;
}

//*****************************************************************************
//
//! The following function return the number of ESIOSC cycle during an ACLK
//! cycle.
//!
//!
//! \return None
//
//*****************************************************************************
static uint16_t measureESIOSC(void){
    // This and next instruction realizes a clear->set ESICLKGON bit.
    ESIOSC &= ~(ESICLKGON);

    // This starts measurement.
    ESIOSC |= ESICLKGON + ESIHFSEL;

    // Reading ESICNT3 while counting always result in reading a 0x01.
    while(ESICNT3 == 1)
    {
        ;
    }

    // Stop ESIOSC oscillator
    ESIOSC &= ~(ESICLKGON);

    return (ESICNT3);
}

//******************************************************************************
//! The following function returns the ESICLKFQx bits on ESIOSC register
//
//! \param none
//
//! \return ESICLKFQ bits only
//******************************************************************************

uint8_t ESI_getESICLKFQ(void){
    uint16_t temp;

    // Store ESIOSC content
    temp = ESIOSC;
    // Get ESICLKFQx bits
    temp = (temp >> 8) & 0x3F;

    return(temp);
}

//******************************************************************************
//! The following function sets ESICLKFQx bits on ESIOSC register
//
//! \param setting is to the loaded to ESIOSC. Valid parameters a value between
//!		   0x00 and 0x3F. 0x00 corresponds to minimum frequency, 0x20
//!		   corresponds to nominal frequency and 0x3F corresponds to maximum
//!		   frequency.
//
//! \return none
//******************************************************************************
void ESI_setESICLKFQ(uint8_t setting)
{
    uint16_t temp;

    assert(setting < 0x40);

    temp = ESIOSC;                         // get actual ESIOSC register content
    temp &= ~(0x3F00);
    temp = ((uint16_t) setting << 8) + temp;     // and update ESICLKFQ bits
    ESIOSC = temp;
}

//*****************************************************************************
//
//! Calibrate ESI internal Oscillator
//!
//!
//! \return None
//
//*****************************************************************************
void ESI_calibrateInternalOscFreq(uint16_t targetAclkCounts)
{
    ESI_setNominalInternalOscFreq();

    ESI_measureESIOSC(ESI_ESIOSC_OVERSAMPLE_4);

    if(ESICNT3 > targetAclkCounts)
    {
        //freq is too high
        do
        {
            ESI_adjustInternalOscFreq(ESI_INTERNAL_OSC_FREQ_DECREASE);
        }
        while(ESI_measureESIOSC(ESI_ESIOSC_OVERSAMPLE_4) > targetAclkCounts);
    }
    else
    {
        //freq is too low
        do
        {
            ESI_adjustInternalOscFreq(ESI_INTERNAL_OSC_FREQ_INCREASE);
        }
        while(ESI_measureESIOSC(ESI_ESIOSC_OVERSAMPLE_4) > targetAclkCounts);
        ESI_adjustInternalOscFreq(ESI_INTERNAL_OSC_FREQ_DECREASE);
    }
}

//*****************************************************************************
//
//! The following function returns an average of ESIOSC measurement.
//!
//! \param
//!
//! \return averaged ESIOSC measurement.
//
//*****************************************************************************
uint16_t ESI_measureESIOSC(uint8_t oversample){
    uint8_t i;
    uint16_t temp = 0;

    assert(oversample < 9);

    for(i = oversample; i > 0; i--)
    {
        temp += measureESIOSC();
    }

    temp /= oversample;
    return(temp);
}

//*****************************************************************************
//
//! Set upper threshold for PSM counter 1
//!
//! \param threshold is the upper threashold that causes ESIIFG3 to get set.
//!
//! This function sets the threshold value for PSM counter 1. ESIIFG3 gets set
//! when counter value and this threahold are equal.
//!
//! \return None
//
//*****************************************************************************
void ESI_setPSMCounter1UpperThreshold(uint16_t threshold)
{
    ESITHR1 = threshold;
}

//*****************************************************************************
//
//! Set lower threshold for PSM counter 1
//!
//! \param threshold is the lower threashold that causes ESIIFG3 to get set.
//!
//! This function set the threshold value for PSM counter 1. ESIIFG3 gets set
//! when counter value and this threahold are equal.
//!
//! \return None
//
//*****************************************************************************
void ESI_setPSMCounter1LowerThreshold(uint16_t threshold)
{
    ESITHR2 = threshold;
}

//*****************************************************************************
//
//! sets AFE1 DAC threshold Value
//!
//! \param dacValue is value to be written to DAC register.
//! \param dacRegNum is DAC register number
//!
//! Write DAC threshold value into selected DAC register
//!
//! \return None
//
//*****************************************************************************
void ESI_setAFE1DACValue(uint16_t dacValue,
                         uint8_t dacRegNum)
{
    volatile uint16_t* dacRegBase = (volatile uint16_t*) &ESIDAC1R0;
    *(dacRegBase + dacRegNum) = dacValue;
}

//*****************************************************************************
//
//! gets AFE1 DAC threshold Value
//!
//! \param dacValue is value to be written to DAC register.
//! \param dacRegNum is DAC register number
//!
//! Read DAC threshold value into selected DAC register
//!
//! \return DAC value from selected DAC register.
//
//*****************************************************************************
uint16_t ESI_getAFE1DACValue(uint8_t dacRegNum)
{
    volatile uint16_t* dacRegBase = (volatile uint16_t*) &ESIDAC1R0;
    return(*(dacRegBase + dacRegNum));
}

//*****************************************************************************
//
//! sets AFE2 DAC threshold Value
//!
//! \param dacValue is value to be written to DAC register.
//! \param dacRegNum is DAC register number
//!
//! Write DAC threshold value into selected DAC register
//!
//! \return None
//
//*****************************************************************************
void ESI_setAFE2DACValue(uint16_t dacValue,
                         uint8_t dacRegNum)
{
    volatile uint16_t* dacRegBase = (volatile uint16_t*) &ESIDAC2R0;
    *(dacRegBase + dacRegNum) = dacValue;
}

//*****************************************************************************
//
//! gets AFE2 DAC threshold Value
//!
//! \param dacValue is value to be written to DAC register.
//! \param dacRegNum is DAC register number
//!
//! Read DAC threshold value into selected DAC register
//!
//! \return DAC value from selected DAC register.
//
//*****************************************************************************
uint16_t ESI_getAFE2DACValue(uint8_t dacRegNum)
{
    volatile uint16_t* dacRegBase = (volatile uint16_t*) &ESIDAC2R0;
    return(*(dacRegBase + dacRegNum));
}

//*****************************************************************************
//
//! sets TSM state register
//!
//! \param params constructs the state value
//! \param stateRegNum is state register offset
//!
//! Sets selected TSM state register.
//!
//! \return None
//
//*****************************************************************************
void ESI_setTSMstateReg(ESI_TSM_StateParams *params,
                        uint8_t stateRegNum)
{
    volatile uint16_t* stateRegBase = (volatile uint16_t*) &ESITSM0;
    *(stateRegBase + stateRegNum) =
        (params->inputChannelSelect +
         params->LCDampingSelect +
         params->excitationSelect +
         params->comparatorSelect +
         params->highFreqClkOn_or_compAutoZeroCycle +
         params->outputLatchSelect +
         params->testCycleSelect +
         params->dacSelect +
         params->tsmStop +
         params->tsmClkSrc) |
        (params->duration << 11);
}

//*****************************************************************************
//
//! Get ESI interrupt Vector Register
//!
//! \return None
//
//*****************************************************************************
uint16_t ESI_getInterruptVectorRegister(void)
{
    return (ESIIV);
}

//*****************************************************************************
//
//! Enables ESI interrupts
//!
//! \param interruptMask is the bit mask of the interrupt sources to
//!        be enabled. Mask value is the logical OR of any of the following:
//!        \b ESI_INTERRUPT_AFE1_ESIOUTX
//!		   \b ESI_INTERRUPT_ESISTOP
//!		   \b ESI_INTERRUPT_ESISTART
//!		   \b ESI_INTERRUPT_ESICNT1
//!		   \b ESI_INTERRUPT_ESICNT2
//!		   \b ESI_INTERRUPT_Q6_BIT_SET
//!		   \b ESI_INTERRUPT_Q7_BIT_SET
//!		   \b ESI_INTERRUPT_ESICNT0_COUNT_INTERVAL
//!		   \b ESI_INTERRUPT_AFE2_ESIOUTX
//!
//! Modified bits of \b ESIINT1 register.
//!
//! \return None
//
//*****************************************************************************
void ESI_enableInterrupt(uint16_t interruptMask)
{
    ESIINT1 |= (interruptMask);
}

//*****************************************************************************
//
//! Disables ESI interrupts
//!
//! \param interruptMask is the bit mask of the interrupt sources to
//!        be disabled. Mask value is the logical OR of any of the following:
//!        \b ESI_INTERRUPT_AFE1_ESIOUTX
//!		   \b ESI_INTERRUPT_ESISTOP
//!		   \b ESI_INTERRUPT_ESISTART
//!		   \b ESI_INTERRUPT_ESICNT1
//!		   \b ESI_INTERRUPT_ESICNT2
//!		   \b ESI_INTERRUPT_Q6_BIT_SET
//!		   \b ESI_INTERRUPT_Q7_BIT_SET
//!		   \b ESI_INTERRUPT_ESICNT0_COUNT_INTERVAL
//!		   \b ESI_INTERRUPT_AFE2_ESIOUTX
//!
//! Modified bits of \b ESIINT1 register.
//!
//! \return None
//
//*****************************************************************************
void ESI_disableInterrupt(uint16_t interruptMask)
{
    ESIINT1 &= ~(interruptMask);
}

//*****************************************************************************
//
//! Get ESI interrupt status
//!
//! \param interruptMask is the masked interrupt flag status to be returned.
//!        Mask value is the logical OR of any of the following:
//!        - \b ESI_INTERRUPT_FLAG_AFE1_ESIOUTX
//!        - \b ESI_INTERRUPT_FLAG_ESISTOP
//!        - \b ESI_INTERRUPT_FLAG_ESISTART
//!        - \b ESI_INTERRUPT_FLAG_ESICNT1
//!        - \b ESI_INTERRUPT_FLAG_ESICNT2
//!		   - \b ESI_INTERRUPT_FLAG_Q6_BIT_SET
//!        - \b ESI_INTERRUPT_FLAG_Q7_BIT_SET
//!        - \b ESI_INTERRUPT_FLAG_ESICNT0_COUNT_INTERVAL
//!        - \b ESI_INTERRUPT_FLAG_AFE2_ESIOUTX
//!
//! \return Logical OR of any of the following:
//!        - \b ESI_INTERRUPT_FLAG_AFE1_ESIOUTX
//!        - \b ESI_INTERRUPT_FLAG_ESISTOP
//!        - \b ESI_INTERRUPT_FLAG_ESISTART
//!        - \b ESI_INTERRUPT_FLAG_ESICNT1
//!        - \b ESI_INTERRUPT_FLAG_ESICNT2
//!        - \b ESI_INTERRUPT_FLAG_Q6_BIT_SET
//!        - \b ESI_INTERRUPT_FLAG_Q7_BIT_SET
//!        - \b ESI_INTERRUPT_FLAG_ESICNT0_COUNT_INTERVAL
//!        - \b ESI_INTERRUPT_FLAG_AFE2_ESIOUTX
//!         \n indicating the status of the masked flags
//
//*****************************************************************************
uint16_t ESI_getInterruptStatus(uint16_t interruptMask)
{
    return (ESIINT2 & interruptMask);
}

//*****************************************************************************
//
//! Clear ESI interrupt flag
//!
//! \param interruptMask is the masked interrupt flag status to be returned.
//!        Mask value is the logical OR of any of the following:
//!        - \b ESI_INTERRUPT_FLAG_AFE1_ESIOUTX
//!        - \b ESI_INTERRUPT_FLAG_ESISTOP
//!        - \b ESI_INTERRUPT_FLAG_ESISTART
//!        - \b ESI_INTERRUPT_FLAG_ESICNT1
//!        - \b ESI_INTERRUPT_FLAG_ESICNT2
//!		   - \b ESI_INTERRUPT_FLAG_Q6_BIT_SET
//!        - \b ESI_INTERRUPT_FLAG_Q7_BIT_SET
//!        - \b ESI_INTERRUPT_FLAG_ESICNT0_COUNT_INTERVAL
//!        - \b ESI_INTERRUPT_FLAG_AFE2_ESIOUTX
//!
//! \return None
//
//*****************************************************************************
void ESI_clearInterrupt(uint16_t interruptMask)
{
    ESIINT2 &= ~(interruptMask);
}

//*****************************************************************************
//
//! Set source of IFG0 interrupt flag
//!
//! \param ifg0Src values are as follows
//!         ESI_IFG0_SET_WHEN_ESIOUT0_SET
//!         ESI_IFG0_SET_WHEN_ESIOUT0_RESET
//!         ESI_IFG0_SET_WHEN_ESIOUT1_SET
//!         ESI_IFG0_SET_WHEN_ESIOUT1_RESET
//!         ESI_IFG0_SET_WHEN_ESIOUT2_SET
//!         ESI_IFG0_SET_WHEN_ESIOUT2_RESET
//!         ESI_IFG0_SET_WHEN_ESIOUT3_SET
//!         ESI_IFG0_SET_WHEN_ESIOUT3_RESET
//!
//! \return None
//
//*****************************************************************************
void ESI_setIFG0Source(uint16_t ifg0Src)
{
    ESIINT1 &= ~ESI_IFG0_SET_WHEN_ESIOUT3_RESET;
    ESIINT1 |= ifg0Src;
}

//*****************************************************************************
//
//! Set source of IFG8 interrupt flag
//!
//! \param ifg8Src values are as follows
//!         ESI_IFG8_SET_WHEN_ESIOUT4_SET
//!         ESI_IFG8_SET_WHEN_ESIOUT4_RESET
//!         ESI_IFG8_SET_WHEN_ESIOUT5_SET
//!         ESI_IFG8_SET_WHEN_ESIOUT5_RESET
//!         ESI_IFG8_SET_WHEN_ESIOUT6_SET
//!         ESI_IFG8_SET_WHEN_ESIOUT6_RESET
//!         ESI_IFG8_SET_WHEN_ESIOUT7_SET
//!         ESI_IFG8_SET_WHEN_ESIOUT7_RESET
//!
//! \return None
//
//*****************************************************************************
void ESI_setIFG8Source(uint16_t ifg8Src)
{
    ESIINT1 &= ~ESI_IFG8_SET_WHEN_ESIOUT7_RESET;
    ESIINT1 |= ifg8Src;
}

//*****************************************************************************
//
//! Set source of IFG7 interrupt flag
//!
//! \param ifg7Src values are as follows
//!         ESI_IFG7_SOURCE_EVERY_COUNT_OF_CNT0
//!         ESI_IFG7_SOURCE_CNT0_MOD4
//!         ESI_IFG7_SOURCE_CNT0_MOD256
//!         ESI_IFG7_SOURCE_CNT0_ROLLOVER
//!
//! \return None
//
//*****************************************************************************
void ESI_setIFG7Source(uint16_t ifg7Src)
{
    ESIINT2 &= ~ESI_IFG7_SOURCE_CNT0_ROLLOVER;
    ESIINT2 |= ifg7Src;
}

//*****************************************************************************
//
//! Set source of IFG4 interrupt flag
//!
//! \param ifg4Src values are as follows
//!         ESI_IFG4_SOURCE_EVERY_COUNT_OF_CNT2
//!         ESI_IFG4_SOURCE_CNT2_MOD4
//!         ESI_IFG4_SOURCE_CNT2_MOD256
//!         ESI_IFG4_SOURCE_CNT2_ROLLOVER
//!
//! \return None
//
//*****************************************************************************
void ESI_setIFG4Source(uint16_t ifg4Src)
{
    ESIINT2 &= ~ESI_IFG4_SOURCE_CNT2_ROLLOVER;
    ESIINT2 |= ifg4Src;
}

//*****************************************************************************
//
//! Simple DAC calibration code using pre-defined TSM
//! Supports AFE1 only.
//! \param selected_channel acceptable values
//!         ESI_AFE1_CHANNEL0_SELECT
//!         ESI_AFE1_CHANNEL1_SELECT
//!         ESI_AFE1_CHANNEL2_SELECT
//!         ESI_AFE1_CHANNEL3_SELECT
//!
//!
//! \return None
//
//*****************************************************************************
void ESI_LC_DAC_calibration(uint8_t selected_channel)
{
#define NUM_SENSOR_CAL  4
#define MIN_HYSTERESIS  30
#define STEP_TO_FINISH  4

    unsigned int i;
    unsigned char test_bit, done;
    unsigned int hysteresis[NUM_SENSOR_CAL],
                 hysteresis_hi[NUM_SENSOR_CAL],
                 hysteresis_lo[NUM_SENSOR_CAL],
                 current[NUM_SENSOR_CAL],
                 average[NUM_SENSOR_CAL],
                 max[NUM_SENSOR_CAL],
                 min[NUM_SENSOR_CAL];

    // State:	0 = output low
    //			1 = output high
    //			2 = undetermined (between 2 hysteresis level)
    unsigned char previous_state[NUM_SENSOR_CAL],
                  current_state[NUM_SENSOR_CAL],
                  step[NUM_SENSOR_CAL];

    // Reset values
    for(i = 0; i < NUM_SENSOR_CAL; i++)
    {
        max[i] = 0;
        min[i] = 0xffff;
        previous_state[i] = 2;
        step[i] = 0;
    }

    do
    {
        // Find the current oscillating level, using software mode
        FindDAC(selected_channel, 1);

        test_bit = 1;
        done = 1;

        for(i = 0; i < NUM_SENSOR_CAL; i++)
        {
            // skip if the channel is not selected
            if(test_bit & selected_channel)
            {
                current[i] = ESI_getAFE1DACValue(i * 2);

                // Record max and min value
                if(current[i] > max[i])
                {
                    max[i] = current[i];
                }
                if(current[i] < min[i])
                {
                    min[i] = current[i];
                }

                // Update average and hysteresis level
                average[i] = (max[i] + min[i]) >> 1;
                hysteresis[i] = (max[i] - min[i]) >> 3;

                if(hysteresis[i] < MIN_HYSTERESIS)
                {
                    hysteresis[i] = MIN_HYSTERESIS;
                }

                hysteresis[i] >>= 1;
                hysteresis_hi[i] = average[i] + hysteresis[i];
                hysteresis_lo[i] = average[i] - hysteresis[i];

                // Determine output state based on hysteresis_hi and hysteresis_lo
                if(current[i] < hysteresis_lo[i])
                {
                    current_state[i] = 0;
                }
                else if(current[i] > hysteresis_hi[i])
                {
                    current_state[i] = 1;
                }
                else
                {
                    current_state[i] = 2;
                }

                // If there is state change, proceed to next step
                switch(current_state[i])
                {
                case 0:
                case 1:
                    if(previous_state[i] != current_state[i])
                    {
                        step[i]++;
                        previous_state[i] = current_state[i];
                    }
                    break;

                default:
                    break;
                }

                // Any selected sensor which has not finished calibration will set done to zero
                if(step[i] < STEP_TO_FINISH)
                {
                    done = 0;
                }
            }
            test_bit <<= 1;
        }
    }
    while(!done);

    // Record DAC Values
    test_bit = 1;
    done = ESI_DAC1_REG0;               // Temp value for recording DAC
    for(i = 0; i < NUM_SENSOR_CAL; i++)
    {
        if(test_bit & selected_channel)
        {
            ESI_setAFE1DACValue(hysteresis_hi[i], done++);
            ESI_setAFE1DACValue(hysteresis_lo[i], done++);
        }
        test_bit <<= 1;
    }
}

//*****************************************************************************
//
//! Find the current oscillating level, using software mode
//!
//!
//! \return None
//
//*****************************************************************************

static void FindDAC(unsigned char selected_channel,
                    unsigned char software_trigger)
{
    // DAC Level tester, using successive approximation approach
    unsigned int DAC_BIT = 0x0800, Prev_DAC_BIT = 0x0C00;

    unsigned int i;
    unsigned int test_bit, DAC_index;

    // Set initial DAC value for each selected channel

    // AFE 1
    if(selected_channel & 0x0f)
    {
        test_bit = 0x01;
        DAC_index = ESI_DAC1_REG0;
        for(i = 0; i < 4; i++)
        {
            if(selected_channel & test_bit)
            {
                ESI_setAFE1DACValue(DAC_BIT, DAC_index++);
                ESI_setAFE1DACValue(DAC_BIT, DAC_index++);
            }
            else
            {
                DAC_index += 2;
            }
            test_bit <<= 1;
        }
    }

    // AFE 2
    if(selected_channel & 0xf0)
    {
        test_bit = 0x10;
        DAC_index = ESI_DAC2_REG0;
        for(i = 0; i < 4; i++)
        {
            if(selected_channel & test_bit)
            {
                ESI_setAFE2DACValue(DAC_BIT, DAC_index++);
                ESI_setAFE2DACValue(DAC_BIT, DAC_index++);
            }
            else
            {
                DAC_index += 2;
            }
            test_bit <<= 1;
        }
    }

    ESI_enableInterrupt(ESI_INTERRUPT_ESISTOP);                 // enable ESISTOP INT

    // Find the DAC value for each selected channel
    do
    {
        ESI_clearInterrupt (ESI_INTERRUPT_FLAG_ESISTOP);

        if(software_trigger)
        {
            ESI_TSM_softwareTrigger();
        }

        __bis_SR_register(LPM3_bits + GIE);                     // wait for the ESISTOP flag
        DAC_BIT >>= 1;                                                          // right shift one bit

        // AFE 1
        if(selected_channel & 0x0f)
        {
            test_bit = 0x01;
            DAC_index = ESI_DAC1_REG0;
            for(i = 0; i < 4; i++)
            {
                if(selected_channel & test_bit)
                {
#ifndef INVERTED
                    if(ESI_getLatchedComparatorOutput(test_bit) ==
                       ESI_AFE_OUTPUT_HIGH)
#else
                    if(ESI_getLatchedComparatorOutput(test_bit) ==
                       ESI_AFE_OUTPUT_LOW)
#endif
                    {
                        ESI_setAFE1DACValue(ESI_getAFE1DACValue(
                                                DAC_index) | DAC_BIT,
                                            DAC_index);
                        DAC_index++;
                        ESI_setAFE1DACValue(ESI_getAFE1DACValue(
                                                DAC_index) | DAC_BIT,
                                            DAC_index);
                        DAC_index++;
                    }
                    else
                    {
                        ESI_setAFE1DACValue(ESI_getAFE1DACValue(
                                                DAC_index) ^ Prev_DAC_BIT,
                                            DAC_index);
                        DAC_index++;
                        ESI_setAFE1DACValue(ESI_getAFE1DACValue(
                                                DAC_index) ^ Prev_DAC_BIT,
                                            DAC_index);
                        DAC_index++;
                    }
                }
                else
                {
                    DAC_index += 2;
                }
                test_bit <<= 1;
            }
        }

        // AFE 2
        if(selected_channel & 0xf0)
        {
            test_bit = 0x10;
            DAC_index = ESI_DAC2_REG0;
            for(i = 0; i < 4; i++)
            {
                if(selected_channel & test_bit)
                {
#ifndef INVERTED
                    if(ESI_getLatchedComparatorOutput(test_bit) ==
                       ESI_AFE_OUTPUT_HIGH)
#else
                    if(ESI_getLatchedComparatorOutput(test_bit) ==
                       ESI_AFE_OUTPUT_LOW)
#endif
                    {
                        ESI_setAFE1DACValue(ESI_getAFE2DACValue(
                                                DAC_index) | DAC_BIT,
                                            DAC_index);
                        DAC_index++;
                        ESI_setAFE1DACValue(ESI_getAFE2DACValue(
                                                DAC_index) | DAC_BIT,
                                            DAC_index);
                        DAC_index++;
                    }
                    else
                    {
                        ESI_setAFE1DACValue(ESI_getAFE2DACValue(
                                                DAC_index) ^ Prev_DAC_BIT,
                                            DAC_index);
                        DAC_index++;
                        ESI_setAFE1DACValue(ESI_getAFE2DACValue(
                                                DAC_index) ^ Prev_DAC_BIT,
                                            DAC_index);
                        DAC_index++;
                    }
                }
                else
                {
                    DAC_index += 2;
                }
                test_bit <<= 1;
            }
        }
        Prev_DAC_BIT >>= 1;                                             // right shift one bit
    }
    while(DAC_BIT);

    ESI_disableInterrupt(ESI_INTERRUPT_ESISTOP);
    __no_operation();
}

//*****************************************************************************
//
//! Close the doxygen group for esi_api
//! @}
//
//*****************************************************************************
#endif
