This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

TMDSCNCD28388D: ePWM Triggered ADC with DMA Transfer works only with a breakpoint in Debug Mode

Part Number: TMDSCNCD28388D

Hello Forum,

Here is a summary of my issue:

  1. Started with example code: adc_ex11_multiple_soc_epwm.
  2. Modified the code to read 3 channels from each of ADCA, ADCB and ADCC
  3. Added DMA transfer of ADC data to memory
  4. Added FSI Lead to transmit data to another control board via two FSI adapter boards

The code works well only when I have an ESTOP or a break point in debug mode.

When I run the code without a breakpoint all ADC channels read zero.

Please take a look at the code below and let me know what is wrong with it.

Note: The FSI part is working fine. It does transfer what it gets in the Tx buffer.

//#############################################################################
//
// TITLE:  ADC ePWM Triggering Multiple SOC
//
//!
//! This software sets up ePWM3 to periodically trigger a set of conversions on
//! ADCA, ADCB and ADCC. Multiple ADCs work together to process a batch of conversions
//! using the available parallelism across the ADCs.
//!
//! ADCA Interrupt ISRs are used to read results of ADCA, ADCB and ADCC.
//!
//! \b External \b Connections \n
//!  - A0, A4, A5, B0, B2, B3, C2 and C4 pins should be connected to signals to be
//!    converted.
//!
//!
//! \b External \b Connections \n
//! - A0, A4, A5, B0, B2, B3, C2 and C4 should be connected to signals to convert
//! \b Watch \b Variables \n
//! - \b adcAResult0 - Digital representation of the voltage on pin A0 (pin-43)
//! - \b adcAResult1 - Digital representation of the voltage on pin A4 (pin-39) 
//! - \b adcAResult2 - Digital representation of the voltage on pin A5 (pin-38) 
//! - \b adcBResult0 - Digital representation of the voltage on pin B0 (pin-46)
//! - \b adcBResult1 - Digital representation of the voltage on pin B2 (pin-48)
//! - \b adcBResult2 - Digital representation of the voltage on pin B3 (pin-49)
//! - \b adcCResult0 - Digital representation of the voltage on pin C2 (pin-31)
//! - \b adcCResult1 - Digital representation of the voltage on pin C4 (pin-29)
//
//#############################################################################
// $TI Release: F2838x Support Library v3.04.00.00 $
// $Release Date: Fri Feb 12 19:08:49 IST 2021 $
// $Copyright:
// Copyright (C) 2021 Texas Instruments Incorporated - http://www.ti.com/
//#############################################################################
//
//
// Included Files
//
#include "driverlib.h"
#include "device.h"
#include "fsi_app.h"
//
// Defines
//
#define RESULTS_BUFFER_SIZE      3
//buffer for storing conversion results
// 1024 x 16-bit words
#define EX_ADC_RESOLUTION       12
// 12 for 12-bit conversion resolution, which supports single-ended signaling
// Or 16 for 16-bit conversion resolution, which supports single-ended or
// differential signaling
#define EX_ADC_SIGNALMODE       "SINGLE-ENDED"
//"SINGLE-ENDED" for ADC_MODE_SINGLE_ENDED:
// Sample on single pin (VREFLO is the low reference)
// Or "Differential" for ADC_MODE_DIFFERENTIAL:
// Sample on pair of pins (difference between pins is converted, subject to
// common mode voltage requirements; see the device data manual)

#define PRESCALER_VAL    FSI_PRESCALE_50MHZ

//
// Globals
//

#pragma DATA_SECTION(adcADataBuffer, "ramgs0");
#pragma DATA_SECTION(adcBDataBuffer, "ramgs0");
#pragma DATA_SECTION(adcCDataBuffer, "ramgs0");
#pragma DATA_SECTION(adcResults, "ramgs0");
uint16_t adcADataBuffer[RESULTS_BUFFER_SIZE];
uint16_t adcBDataBuffer[RESULTS_BUFFER_SIZE];
uint16_t adcCDataBuffer[RESULTS_BUFFER_SIZE];
uint16_t adcResults[8];
volatile uint16_t done;
//
// Function Prototypes
//
void configureADC(uint32_t adcBase);
void initEPWM();
void initADCSOC(void);
void initializeDMA(void);
void configureDMAChannels(void);
__interrupt void adcA1ISR(void);
__interrupt void dmach1ISR(void);
//
// Main
//
void main(void)
{
    uint16_t resultsIndex;
    //
    // Initialize device clock and peripherals
    //
    Device_init();

    //
    // Disable pin locks and enable internal pullups.
    //
    Device_initGPIO();
    //
    // Initialize basic settings for FSI
    //
    initFSI();

    //
    // Initialize PIE and clear PIE registers. Disables CPU interrupts.
    //
    Interrupt_initModule();

    //
    // Initialize the PIE vector table with pointers to the shell Interrupt
    // Service Routines (ISR).
    //
    Interrupt_initVectorTable();

    //
    // Interrupts that are re-mapped to ISR functions
    //
    // ISR for ADCA INT1 - occurs after first conversion
    // ISR for DMA ch1 - occurs when DMA transfer is complete
    //
    Interrupt_register(INT_ADCA1, &adcA1ISR);
    Interrupt_register(INT_DMA_CH1, &dmach1ISR);
    //
    // ISR for FSI Tx/Rx :: INT1/INT2
    //
    Interrupt_register(INT_FSITXA1, &fsiTxInt1ISR);
    Interrupt_register(INT_FSITXA2, &fsiTxInt2ISR);
    Interrupt_register(INT_FSIRXA1, &fsiRxInt1ISR);
    Interrupt_register(INT_FSIRXA2, &fsiRxInt2ISR);
    //
    // Enable specific PIE & CPU interrupts:
    // ADCA INT1 - Group 1, interrupt 1
    // DMA interrupt - Group 7, interrupt 1
    //
    Interrupt_enable(INT_ADCA1);
    Interrupt_enable(INT_DMA_CH1);
    //
    // Enable FSI Tx/Rx interrupts
    //
    Interrupt_enable(INT_FSITXA1);
    Interrupt_enable(INT_FSITXA2);
    Interrupt_enable(INT_FSIRXA1);
    Interrupt_enable(INT_FSIRXA2);

    //
    // Initialize basic settings for FSI
    //
    //initFSI();
    //
    // Set up the ADC and the ePWM and initialize the SOC
    //
    configureADC(ADCA_BASE);
    configureADC(ADCB_BASE);
    configureADC(ADCC_BASE);
    initEPWM();

    initADCSOC();

    //
    // Initialize the DMA & configure DMA channels 1, 2 & 3
    //
    initializeDMA();
    configureDMAChannels();

    //
    // Initialize results buffer
    //
    for(resultsIndex = 0; resultsIndex < RESULTS_BUFFER_SIZE; resultsIndex++)
    {
        adcADataBuffer[resultsIndex] = 0;
        adcBDataBuffer[resultsIndex] = 0;
        adcCDataBuffer[resultsIndex] = 0;
    }
    //
    // Clearing all pending interrupt flags
    //
    DMA_clearTriggerFlag(DMA_CH1_BASE);   // DMA channel 1
    DMA_clearTriggerFlag(DMA_CH2_BASE);   // DMA channel 2
    DMA_clearTriggerFlag(DMA_CH3_BASE);   // DMA channel 3
    HWREGH(ADCA_BASE + ADC_O_INTFLGCLR) = 0x3U; // ADCA
    HWREGH(ADCB_BASE + ADC_O_INTFLGCLR) = 0x3U; // ADCB
    HWREGH(ADCC_BASE + ADC_O_INTFLGCLR) = 0x3U; // ADCC
    EPWM_forceADCTriggerEventCountInit(EPWM3_BASE, EPWM_SOC_A); // EPWM3 SOCA
    EPWM_clearADCTriggerFlag(EPWM3_BASE, EPWM_SOC_A); // EPWM3 SOCA

    //
    // Enable Global Interrupt (INTM) and realtime interrupt (DBGM)
    //
    EINT;
    ERTM;

    fsi_SetupTransfer();

    //
    // Start DMA
    //
    done = 0;
    DMA_startChannel(DMA_CH1_BASE);
    DMA_startChannel(DMA_CH2_BASE);
    DMA_startChannel(DMA_CH3_BASE);

    //
    // Start ePWM3, enabling SOCA and putting the counter in up-count mode
    //
    EPWM_enableADCTrigger(EPWM3_BASE, EPWM_SOC_A);
    EPWM_setTimeBaseCounterMode(EPWM3_BASE, EPWM_COUNTER_MODE_UP);

    //
    // Loop until the ISR signals the transfer is complete
    //

   while(1)
   {
       if(done == 0)
       {
           __asm(" NOP");
       }
    }
}

//
// configureADC - Write ADC configurations and power up the ADC for the
// selected ADC
//
void configureADC(uint32_t adcBase)
{
    //
    // Set ADCCLK divider to /4 (This is 50 MHz)
    //
    ADC_setPrescaler(adcBase, ADC_CLK_DIV_4_0);
    //
    // Set resolution and signal mode (see #defines above) and load
    // corresponding trims.
    //
    ADC_setMode(adcBase, ADC_RESOLUTION_12BIT, ADC_MODE_SINGLE_ENDED);
    //
    // Set pulse positions to late (end of conversion)
    //
    ADC_setInterruptPulseMode(adcBase, ADC_PULSE_END_OF_CONV);
    //
    // Power up the ADCs and then delay for 1 ms
    //
    ADC_enableConverter(adcBase);
    //
    // Delay for 1ms to allow ADC time to power up
    //
    DEVICE_DELAY_US(1000);
}

//
// Function to configure ePWM3 to generate the SOC.
//
void initEPWM(void)
{
    //
    // Disable SOCA
    //
    EPWM_disableADCTrigger(EPWM3_BASE, EPWM_SOC_A);

    //
    // Configure the SOC to occur on the first up-count event
    //
    EPWM_setADCTriggerSource(EPWM3_BASE, EPWM_SOC_A, EPWM_SOC_TBCTR_U_CMPA);
    EPWM_setADCTriggerEventPrescale(EPWM3_BASE, EPWM_SOC_A, 1);
    //
    // Set the compare A value to 1250 and the period to 2499
    // Assuming ePWM clock is 100MHz, this would give 40kHz sampling
    // 50MHz ePWM clock would give 25kHz sampling, etc. 
    // The sample rate can also be modulated by changing the ePWM period
    // directly (ensure that the compare A value is less than the period). 
    // Tpwm = (TBPRD+1) x TBCLK // Tpwm = 2500 * 1/100E+6 = 25 Us
    // Fpwm = 1/Tpwm = 1/25E-6 = 40kHz
    //
    EPWM_setCounterCompareValue(EPWM3_BASE, EPWM_COUNTER_COMPARE_A, 1250);//1250/2500 = 50% duty cycle
    EPWM_setTimeBasePeriod(EPWM3_BASE, 2499);//2499
    //EPWM_disablePhaseShiftLoad(EPWM3_BASE);// Clear PHSEN bit
    //EPWM_setPeriodLoadMode(EPWM3_BASE, EPWM_PERIOD_SHADOW_LOAD);
    //EPWM_enableSyncOutPulseSource(EPWM3_BASE, EPWM_SYNC_OUT_PULSE_ON_CNTR_ZERO);
    //
    // Set the local ePWM module clock divider to /1
    //
    EPWM_setClockPrescaler(EPWM3_BASE,
                           EPWM_CLOCK_DIVIDER_1,
                           EPWM_HSCLOCK_DIVIDER_1);

    //
    // Freeze the counter
    //
    EPWM_setTimeBaseCounterMode(EPWM3_BASE, EPWM_COUNTER_MODE_STOP_FREEZE);
}

//
// Function to configure SOCs on ADCA, ADCB and ADCC to be triggered by ePWM3.
//
void initADCSOC(void)
{
    uint16_t acqps;
    //
    // Minimum acquisition window (in SYSCLKS) based on 12-bit resolution
    //
    acqps = 14;// 15 is 80ns
    //
    // - NOTE: A longer sampling window will be required if the ADC driving
    //   source is less than ideal (an ideal source would be a high bandwidth
    //   op-amp with a small series resistance). See TI application report
    //   SPRACT6 for guidance on ADC driver design.
    // - NOTE: SOCs need not use the same S+H window duration, but SOCs 
    //   occurring in parallel (SOC0 on ADCA, ADCB, ADCC occur in
    //   parallel, as do the SOC1s on the 3 ADCs, etc.) should usually
    //   use the same value to ensure simultaneous samples and synchronous 
    //   operation.  

    //
    // Select the channels to convert and configure the ePWM trigger
    //
    ADC_setupSOC(ADCA_BASE, ADC_SOC_NUMBER0, ADC_TRIGGER_EPWM3_SOCA,
                 ADC_CH_ADCIN0, acqps);
    ADC_setupSOC(ADCA_BASE, ADC_SOC_NUMBER1, ADC_TRIGGER_EPWM3_SOCA,
                 ADC_CH_ADCIN4, acqps);
    ADC_setupSOC(ADCA_BASE, ADC_SOC_NUMBER2, ADC_TRIGGER_EPWM3_SOCA,
                 ADC_CH_ADCIN5, acqps);
    ADC_setupSOC(ADCB_BASE, ADC_SOC_NUMBER0, ADC_TRIGGER_EPWM3_SOCA,
                 ADC_CH_ADCIN0, acqps);
    ADC_setupSOC(ADCB_BASE, ADC_SOC_NUMBER1, ADC_TRIGGER_EPWM3_SOCA,
                 ADC_CH_ADCIN2, acqps);
    ADC_setupSOC(ADCB_BASE, ADC_SOC_NUMBER2, ADC_TRIGGER_EPWM3_SOCA,
                 ADC_CH_ADCIN3, acqps);
    ADC_setupSOC(ADCC_BASE, ADC_SOC_NUMBER0, ADC_TRIGGER_EPWM3_SOCA,
                 ADC_CH_ADCIN2, acqps);
    ADC_setupSOC(ADCC_BASE, ADC_SOC_NUMBER1, ADC_TRIGGER_EPWM3_SOCA,
                 ADC_CH_ADCIN4, acqps);
    ADC_setupSOC(ADCC_BASE, ADC_SOC_NUMBER2, ADC_TRIGGER_EPWM3_SOCA,
                 ADC_CH_ADCIN5, acqps);

    //
    // Select SOC2 on ADCA as the interrupt source.
    //
    ADC_setInterruptSource(ADCA_BASE, ADC_INT_NUMBER1, ADC_SOC_NUMBER2);
    ADC_enableInterrupt(ADCA_BASE, ADC_INT_NUMBER1);
    ADC_disableInterrupt(ADCA_BASE, ADC_INT_NUMBER2);
    ADC_disableInterrupt(ADCA_BASE, ADC_INT_NUMBER3);
    ADC_disableInterrupt(ADCA_BASE, ADC_INT_NUMBER4);
    ADC_clearInterruptStatus(ADCA_BASE, ADC_INT_NUMBER1);
}

//
// ADC A Interrupt 1 ISR
//
#pragma CODE_SECTION(adcA1ISR, ".TI.ramfunc");
__interrupt void adcA1ISR(void)
{
    //
    // Clear the interrupt flag
    //
    ADC_clearInterruptStatus(ADCA_BASE, ADC_INT_NUMBER1);

    //
    // Check if overflow has occurred
    //
    if(true == ADC_getInterruptOverflowStatus(ADCA_BASE, ADC_INT_NUMBER1))
    {
        ADC_clearInterruptOverflowStatus(ADCA_BASE, ADC_INT_NUMBER1);
        ADC_clearInterruptStatus(ADCA_BASE, ADC_INT_NUMBER1);
    }
    //
    // Acknowledge interrupt
    //
    Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP1);
}
//
// dmach1ISR - This is called at the end of the DMA transfer, the conversions
//
#pragma CODE_SECTION(dmach1ISR, ".TI.ramfunc");
__interrupt void dmach1ISR(void)
{
    //
    done = 1;
    fsi_DataTransfer();

    // Acknowledge interrupt
    //
    Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP7);
    //ESTOP0;
    done = 0;
}
//
// initializeDMA - Initialize DMA through hard reset
//
void initializeDMA(void)
{
    //
    // Perform a hard reset on DMA
    //
    DMA_initController();
    //
    // Allow DMA to run free on emulation suspend
    //
    DMA_setEmulationMode(DMA_EMULATION_FREE_RUN);
}
//
// configureDMAChannels - Initialize DMA ch 1 to transfer ADCA results
//                        , DMA ch 2 to transfer ADCB results
//                        and ch 3 to transfer ADCC results
//
void configureDMAChannels(void)
{
    //
    // DMA channel 1 set up for ADCA
    //
    DMA_configAddresses(DMA_CH1_BASE, (uint16_t *)&adcADataBuffer,
                        (uint16_t *)ADCARESULT_BASE);

    //
    // Perform enough 3-word burst to fill the results buffer. Data will be
    // transferred 16 bits at a time hence the address steps below.
    //
    DMA_configBurst(DMA_CH1_BASE, 3, 1, 1);
    DMA_configTransfer(DMA_CH1_BASE, 1, 0, 0);
    DMA_configMode(DMA_CH1_BASE, DMA_TRIGGER_ADCA1,
                   (DMA_CFG_ONESHOT_DISABLE | DMA_CFG_CONTINUOUS_ENABLE |
                    DMA_CFG_SIZE_16BIT));

    DMA_enableTrigger(DMA_CH1_BASE);
    DMA_disableOverrunInterrupt(DMA_CH1_BASE);
    DMA_setInterruptMode(DMA_CH1_BASE, DMA_INT_AT_END);
    DMA_enableInterrupt(DMA_CH1_BASE);


    //
    // DMA channel 2 set up for ADCB
    //
    DMA_configAddresses(DMA_CH2_BASE, (uint16_t *)&adcBDataBuffer,
                        (uint16_t *)ADCBRESULT_BASE);

    //
    // Perform enough 3-word burst to fill the results buffer. Data will be
    // transferred 32 bits at a time hence the address steps below.
    //
    DMA_configBurst(DMA_CH2_BASE, 3, 1, 1);
    DMA_configTransfer(DMA_CH2_BASE, 1, 0, 0);
    DMA_configMode(DMA_CH2_BASE, DMA_TRIGGER_ADCA1,
                   (DMA_CFG_ONESHOT_DISABLE | DMA_CFG_CONTINUOUS_ENABLE |
                           DMA_CFG_SIZE_16BIT));

    DMA_enableTrigger(DMA_CH2_BASE);
    DMA_disableOverrunInterrupt(DMA_CH2_BASE);
    DMA_setInterruptMode(DMA_CH2_BASE, DMA_INT_AT_END);
    DMA_enableInterrupt(DMA_CH2_BASE);

    //
    // DMA channel 3 set up for ADCC
    //
    DMA_configAddresses(DMA_CH3_BASE, (uint16_t *)&adcCDataBuffer,
                        (uint16_t *)ADCCRESULT_BASE);

    //
    // Perform enough 3-word burst to fill the results buffer. Data will be
    // transferred 16 bits at a time hence the address steps below.
    //
    DMA_configBurst(DMA_CH3_BASE, 3, 1, 1);
    DMA_configTransfer(DMA_CH3_BASE, 1, 0, 0);
    DMA_configMode(DMA_CH3_BASE, DMA_TRIGGER_ADCA1,
                   (DMA_CFG_ONESHOT_DISABLE | DMA_CFG_CONTINUOUS_ENABLE |
                    DMA_CFG_SIZE_16BIT));

    DMA_enableTrigger(DMA_CH3_BASE);
    DMA_disableOverrunInterrupt(DMA_CH3_BASE);
    DMA_setInterruptMode(DMA_CH3_BASE, DMA_INT_AT_END);
    DMA_enableInterrupt(DMA_CH3_BASE);
}

// initFSI - Initializes FSI Tx/Rx with internal loopback and also sends FLUSH
//           sequence.
//
void initFSI(void)
{
    FSI_disableRxInternalLoopback(FSIRXA_BASE);

    GPIO_setPinConfig(DEVICE_GPIO_CFG_FSI_TXCLK);
    GPIO_setPinConfig(DEVICE_GPIO_CFG_FSI_TX0);

    GPIO_setPinConfig(DEVICE_GPIO_CFG_FSI_RXCLKA);
    GPIO_setPinConfig(DEVICE_GPIO_CFG_FSI_RX0A);
    if(nLanes == FSI_DATA_WIDTH_2_LANE)
    {
        GPIO_setPinConfig(DEVICE_GPIO_CFG_FSI_TX1);
        GPIO_setPinConfig(DEVICE_GPIO_CFG_FSI_RX1A);
    }

    //
    // Set RX GPIO to be asynchronous
    // (pass through without delay)
    // Default setting is to have 2 SYS_CLK cycles delay
    //
    GPIO_setQualificationMode(DEVICE_GPIO_PIN_FSI_RX0A, GPIO_QUAL_ASYNC);
    GPIO_setQualificationMode(DEVICE_GPIO_PIN_FSI_RXCLKA, GPIO_QUAL_ASYNC);
    if(nLanes == FSI_DATA_WIDTH_2_LANE)
    {
        GPIO_setQualificationMode(DEVICE_GPIO_PIN_FSI_RX1A, GPIO_QUAL_ASYNC);
    }

    // TODO- Add logic to calculate PRESCALER_VAL based on user input FSI CLK
    FSI_performTxInitialization(FSITXA_BASE, PRESCALER_VAL);
    FSI_performRxInitialization(FSIRXA_BASE);
    txBufAddr = (uint16_t *)FSI_getTxBufferAddress(FSITXA_BASE);
    rxBufAddr = (uint16_t *)FSI_getRxBufferAddress(FSIRXA_BASE);
}

//
// fsiTxInt1ISR - FSI Tx Interrupt on INsT1 line
//
__interrupt void fsiTxInt1ISR(void)
{
    fsiTxInt1Received = 1U;

    txEventSts = FSI_getTxEventStatus(FSITXA_BASE);

    //
    // Clear the interrupt flag and issue ACK
    //
    FSI_clearTxEvents(FSITXA_BASE, FSI_TX_EVTMASK);
    Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP4);
}

//
// fsiTxInt2ISR - FSI Tx Interrupt on INT2 line
//
__interrupt void fsiTxInt2ISR(void)
{
    fsiTxInt2Received = 1U;

    txEventSts = FSI_getTxEventStatus(FSITXA_BASE);

    //
    // Clear the interrupt flag and issue ACK
    //
    FSI_clearTxEvents(FSITXA_BASE, FSI_TX_EVTMASK);
    Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP4);

    disableAllFSIInterrupts();

    //
    // INT2 line is set to fire for error events, stop immediately. Actual Error
    // is captured in txEventSts for debug
    //
    ESTOP0;
}

//
// fsiRxInt1ISR - FSI Rx Interrupt on INT1 line
//
__interrupt void fsiRxInt1ISR(void)
{
    rxEventSts = FSI_getRxEventStatus(FSIRXA_BASE);

    fsiRxInt1Received = 1U;

    //
    // Clear the interrupt flag and issue ACK
    //
    FSI_clearRxEvents(FSIRXA_BASE,rxEventSts);
    Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP4);
}

//
// fsiRxInt2ISR - FSI Rx Interrupt on INT2 line
//
__interrupt void fsiRxInt2ISR(void)
{
    rxEventSts = FSI_getRxEventStatus(FSIRXA_BASE);

    fsiRxInt2Received = fsiRxInt2Received + 1U;

    //
    // Clear the interrupt flag and issue ACK
    //
    FSI_clearRxEvents(FSIRXA_BASE,rxEventSts);
    Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP4);

    disableAllFSIInterrupts();

    //
    // INT2 line is set to fire for error events, stop immediately. Error
    // is captured in rxEventSts for debug
    //
    ESTOP0;
}

//
// disableAllFSIInterrupts - Disables all event interrupts in both FSI Tx/Rx,
//                           also clear them
//
void disableAllFSIInterrupts(void)
{
    FSI_disableTxInterrupt(FSITXA_BASE, FSI_INT1, FSI_TX_EVTMASK);
    FSI_disableTxInterrupt(FSITXA_BASE, FSI_INT2, FSI_TX_EVTMASK);
    FSI_disableRxInterrupt(FSIRXA_BASE, FSI_INT1, FSI_RX_EVTMASK);
    FSI_disableRxInterrupt(FSIRXA_BASE, FSI_INT2, FSI_RX_EVTMASK);

    FSI_clearTxEvents(FSITXA_BASE, FSI_TX_EVTMASK);
    FSI_clearRxEvents(FSIRXA_BASE, FSI_RX_EVTMASK);
}

//
// compare16 - Compares two 16 bit values and increments global error flag by 1
//             for mismatch
//
static inline void compare16(uint16_t val1, uint16_t val2)
{
    if(val1 != val2)
    {
        error++;
    }
}

//
// compareBufData - Compares if received data is same as transmitted ones
//                  It doesn't consider wrap-up cases, but, can be enhanced
//
void compareBufData(uint16_t txBufIndex, uint16_t rxBufIndex, uint16_t nWords)
{
    uint16_t i;
    uint16_t rxDataArray[16];

    FSI_readRxBuffer(FSIRXA_BASE, rxDataArray, nWords, rxBufIndex);

    for(i = 0; i < nWords; i++)
    {
        if(rxDataArray[i] != txBufAddr[txBufIndex])
        {
            error++;
            return;
        }

        txBufIndex++;
    }
}

//
// checkReceivedFrameTypeTag - Checks received frame type/tag and updates global
//                             error flag
//
void checkReceivedFrameTypeTag(FSI_FrameType type, FSI_FrameTag tag)
{
    compare16((uint16_t)FSI_getRxFrameType(FSIRXA_BASE), (uint16_t)type);

    if(type == FSI_FRAME_TYPE_PING)
    {
        compare16(FSI_getRxPingTag(FSIRXA_BASE), (uint16_t)tag);
    }
    else
    {
        compare16(FSI_getRxFrameTag(FSIRXA_BASE), (uint16_t)tag);
    }
}
#pragma CODE_SECTION(fsi_SetupTransfer, ".TI.ramfunc")
void fsi_SetupTransfer(void)
{
    //
    // Enable normal data transfer events to be sent over INT1 line
    //
    FSI_enableTxInterrupt(FSITXA_BASE, FSI_INT1, FSI_TX_EVT_FRAME_DONE);
    FSI_enableRxInterrupt(FSIRXA_BASE, FSI_INT1, FSI_RX_EVT_PING_FRAME);

    //
    // Wait till interrupt is received on FSIRX INT1 line, verify it's for FRAME
    // DONE event for PING Frame reception
    //
    while(1)
    {
        //
        // Send the flush sequence
        //
        FSI_executeTxFlushSequence(FSITXA_BASE, PRESCALER_VAL);

        //
        // Send a ping frame with frame tag 0000b
        //
        FSI_setTxFrameTag(FSITXA_BASE, FSI_FRAME_TAG0);
        FSI_setTxFrameType(FSITXA_BASE, FSI_FRAME_TYPE_PING);
        FSI_startTxTransmit(FSITXA_BASE);

        while(fsiRxInt1Received != 1U && rxTimeOutCntr != 0U)
        {
            DEVICE_DELAY_US(1);
            rxTimeOutCntr--;
        }

        if(rxTimeOutCntr == 0)
        {
            rxTimeOutCntr = 0x100000;
            continue;
        }
        else
        {
            compare16(rxEventSts, (FSI_RX_EVT_PING_FRAME | FSI_RX_EVT_FRAME_DONE));
            checkReceivedFrameTypeTag(FSI_FRAME_TYPE_PING, FSI_FRAME_TAG1);
            //
            // If received frame type and tag matches, exit this loop and proceed
            // to next step by sending flush sequence, otherwise clear error and
            // interrupt flag and continue looping.
            //
            if(error == 0)
            {
                fsiRxInt1Received = 0;
                break;
            }

            fsiRxInt1Received = 0;
            error = 0;
        }
    }
    //
    // Send a ping frame with frame tag 0001b
    //
    FSI_setTxFrameTag(FSITXA_BASE, FSI_FRAME_TAG1);
    FSI_setTxFrameType(FSITXA_BASE, FSI_FRAME_TYPE_PING);
    FSI_startTxTransmit(FSITXA_BASE);
    DEVICE_DELAY_US(100);
    FSI_setTxSoftwareFrameSize(FSITXA_BASE, nWords);
    FSI_setTxDataWidth(FSITXA_BASE, nLanes);
    FSI_setTxUserDefinedData(FSITXA_BASE, txUserData);
    FSI_setTxFrameTag(FSITXA_BASE, txDataFrameTag);
    FSI_setTxFrameType(FSITXA_BASE, FSI_FRAME_TYPE_NWORD_DATA);
}
#pragma CODE_SECTION(fsi_DataTransfer, ".TI.ramfunc")
void fsi_DataTransfer(void)
{
    //
    // Write data into Tx buffer and set other Frame specific fields
    //
    memcpy(txBufData, &adcADataBuffer, 8);// 8 ADC channels
    FSI_writeTxBuffer(FSITXA_BASE, txBufData, nWords, 0U);
    while(fsiTxInt1Received != 1U);
    //
    // Start Transfer
    //
    FSI_startTxTransmit(FSITXA_BASE);
    while(fsiTxInt1Received != 1);
    //
    // Re-initialize flags and buffer pointer before next transmission
    //
    fsiTxInt1Received = 0U;
    dataFrameCntr++;
    FSI_setTxBufferPtr(FSITXA_BASE, 0U);
}
//
// End of file
//