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.

MSP-EXP432E401Y: What do I pass as the function argument here?

Part Number: MSP-EXP432E401Y
Other Parts Discussed in Thread: ADS131M08,

Tool/software:

Hi,

I have a MSP-EXP432E401Y Launchpad and an ADS131M08 Evaluation module. I am able to Write and re-read a written register in the ADS module (SPI Interface). There is a sample code provided by TI.

I have been out of touch with any programming for over 2 decades. Thus, I would like some help. I refer to this example function to read data (code below). 

What should I be providing as a function argument for the function in Line 40? It is a pointer, but what must be the argument? How do I call this function on line 40? I am seeing that it stores the status and the data values from all the channels, but what is the argument I need to provide to the function? Is it the channel number, or an address of a array or something?

bool bit_status = readData(??); // channel number?

//****************************************************************************
//
// Channel data structure
//
//****************************************************************************

typedef struct
{
    uint16_t response;
    uint16_t crc;
    int32_t channel0;

#if (CHANNEL_COUNT > 1)
    int32_t channel1;
#endif
#if (CHANNEL_COUNT > 2)
    int32_t channel2;
#endif
#if (CHANNEL_COUNT > 3)
    int32_t channel3;
#endif
#if (CHANNEL_COUNT > 4)
    int32_t channel4;
#endif
#if (CHANNEL_COUNT > 5)
    int32_t channel5;
#endif
#if (CHANNEL_COUNT > 6)
    int32_t channel6;
 #endif
 #if (CHANNEL_COUNT > 7)
    int32_t channel7;
#endif
} adc_channel_data;





bool readData(adc_channel_data *DataStruct)
{
    int i;
    uint8_t crcTx[4]                        = { 0 };
    uint8_t dataRx[4]                       = { 0 };
    uint8_t bytesPerWord                    = getWordByteLength();

#ifdef ENABLE_CRC_IN
    // Build CRC word (only if "RX_CRC_EN" register bit is enabled)
    uint16_t crcWordIn = calculateCRC(&DataTx[0], bytesPerWord * 2, 0xFFFF);
    crcTx[0] = upperByte(crcWordIn);
    crcTx[1] = lowerByte(crcWordIn);
#endif

    /* Set the nCS pin LOW */
    setCS(LOW);

    // Send NULL word, receive response word
    for (i = 0; i < bytesPerWord; i++)
    {
        dataRx[i] = spiSendReceiveByte(0x00);
    }
    DataStruct->response = combineBytes(dataRx[0], dataRx[1]);

    // (OPTIONAL) Do something with the response (STATUS) word.
    // ...Here we only use the response for calculating the CRC-OUT
    //uint16_t crcWord = calculateCRC(&dataRx[0], bytesPerWord, 0xFFFF);

    // (OPTIONAL) Ignore CRC error checking
    uint16_t crcWord = 0;

    // Send 2nd word, receive channel 1 data
    for (i = 0; i < bytesPerWord; i++)
    {
        dataRx[i] = spiSendReceiveByte(crcTx[i]);
    }
    DataStruct->channel0 = signExtend(&dataRx[0]);
    //crcWord = calculateCRC(&dataRx[0], bytesPerWord, crcWord);

#if (CHANNEL_COUNT > 1)

    // Send 3rd word, receive channel 2 data
    for (i = 0; i < bytesPerWord; i++)
    {
        dataRx[i] = spiSendReceiveByte(0x00);
    }
    DataStruct->channel1 = signExtend(&dataRx[0]);
    //crcWord = calculateCRC(&dataRx[0], bytesPerWord, crcWord);

#endif
#if (CHANNEL_COUNT > 2)

    // Send 4th word, receive channel 3 data
    for (i = 0; i < bytesPerWord; i++)
    {
        dataRx[i] = spiSendReceiveByte(0x00);
    }
    DataStruct->channel2 = signExtend(&dataRx[0]);
    //crcWord = calculateCRC(&dataRx[0], bytesPerWord, crcWord);

#endif
#if (CHANNEL_COUNT > 3)

    // Send 5th word, receive channel 4 data
    for (i = 0; i < bytesPerWord; i++)
    {
        dataRx[i] = spiSendReceiveByte(0x00);
    }
    DataStruct->channel3 = signExtend(&dataRx[0]);
    //crcWord = calculateCRC(&dataRx[0], bytesPerWord, crcWord);

#endif
#if (CHANNEL_COUNT > 4)

    // Send 6th word, receive channel 5 data
    for (i = 0; i < bytesPerWord; i++)
    {
        dataRx[i] = spiSendReceiveByte(0x00);
    }
    DataStruct->channel4 = signExtend(&dataRx[0]);
    //crcWord = calculateCRC(&dataRx[0], bytesPerWord, crcWord);

#endif
#if (CHANNEL_COUNT > 5)

    // Send 7th word, receive channel 6 data
    for (i = 0; i < bytesPerWord; i++)
    {
        dataRx[i] = spiSendReceiveByte(0x00);
    }
    DataStruct->channel5 = signExtend(&dataRx[0]);
    //crcWord = calculateCRC(&dataRx[0], bytesPerWord, crcWord);

#endif
#if (CHANNEL_COUNT > 6)

    // Send 8th word, receive channel 7 data
    for (i = 0; i < bytesPerWord; i++)
    {
        dataRx[i] = spiSendReceiveByte(0x00);
    }
    DataStruct->channel6 = signExtend(&dataRx[0]);
    //crcWord = calculateCRC(&dataRx[0], bytesPerWord, crcWord);

#endif
#if (CHANNEL_COUNT > 7)

    // Send 9th word, receive channel 8 data
    for (i = 0; i < bytesPerWord; i++)
    {
        dataRx[i] = spiSendReceiveByte(0x00);
    }
    DataStruct->channel7 = signExtend(&dataRx[0]);
    //crcWord = calculateCRC(&dataRx[0], bytesPerWord, crcWord);

#endif

    // Send the next word, receive CRC data
    for (i = 0; i < bytesPerWord; i++)
    {
        dataRx[i] = spiSendReceiveByte(0x00);
    }
    DataStruct->crc = combineBytes(dataRx[0], dataRx[1]);

    /* NOTE: If we continue calculating the CRC with a matching CRC, the result should be zero.
     * Any non-zero result will indicate a mismatch.
     */
    //crcWord = calculateCRC(&dataRx[0], bytesPerWord, crcWord);

    /* Set the nCS pin HIGH */
    setCS(HIGH);

    // Returns true when a CRC error occurs
    return ((bool) crcWord);
}

Thanks

Ram.

  • Hi,

    What should I be providing as a function argument for the function in Line 40? It is a pointer, but what must be the argument? How do I call this function on line 40?

      Well, this is probably some custom code you inherited from your organization. This is not from the TivaWare SDK which we own. On line 62 and 76, the function will store the received data from the SPI into the elements of a structure pointed by DataStruct. 

    adc_channel_data *My_DataStruct;

    readData(My_DataStruct);

    After you call the readData function, My_DataStruct will be populated with the received SPI data and it is up to your application how to process them.  

  • Hi,

    Oh, so I guess everything is defined. I just need to have the structure (*My_DataStruct) and call the function? So the function will store the received data from Lines, 62, 76, 86, 97, so on... until 162?

    Ram.

  • Hi,

    I have another snag now, not sure what I am missing. My code enters an infinite loop on line 996 (below). As you had mentioned, I am passing the 'DataStruct', in Line 93. It enters the function 'readData' and the goes to an infinite loop, inside another default handler function in another startup.c file. The main code is below. 

    /*
    
    1. Make all the hardware connections in the ADS board. SYNC/RESET, CS, DRDY, DIN, DOUT, AVDD, DVDD, GND. 
      
    2. This is a check code. There is a function called adcStartup(), in which one register is read back. 
      
    3. Ensure that the valuse read back is correct as per the datasheet and then proceed to have your code 
      written after this function. 
    
    The functions are referred from an existing code example from TI. 
    */
    
    
    //****************************************************************************
    //
    // LaunchPad pinout...
    //
    //****************************************************************************
    //
    //                  LEFT                                RIGHT
    //               /--------\                          /--------\
    //        +3.3V -|3V3  +5V|- +5V                CLK -|PG1  GND|- GND
    //              -|PD2  GND|- GND       nSYNC/nRESET -|PK4  PM7|-
    //              -|PP0  PB4|-                    nCS -|PK5 *PP5|-
    //              -|PP1  PB5|-                  nDRDY -|PM0  PA7|-
    //              -|PD4  PK0|-                        -|PM1  RST|-
    //              -|PD5  PK1|-                        -|PM2  PQ2|- DIN
    //         SCLK -|PQ0  PK2|-                        -|PH0  PQ3|- DOUT
    //              -|PP4* PK3|-                        -|PH1 *PP3|-
    //              -|PN5  PA4|-                        -|PK6  PQ1|-
    //              -|PN4  PA5|-                        -|PK7  PM6|-
    //               \--------/                          \--------/
    //
    
    
    
    #include <stdint.h>
    #include <stdbool.h>
    #include "ti/devices/msp432e4/driverlib/driverlib.h"
    #include "ads131m0x.h"
    
    uint32_t systemClock;
    uint16_t register_value, ID_Register, Status_Register, Mode_Register, Clock_Register, CFG_Register, Gain2_Register;
    uint32_t Channel_0_Value, Channel_1_Value, Channel_2_Value, Channel_3_Value, Channel_4_Value, Channel_5_Value, Channel_6_Value, Channel_7_Value;
    adc_channel_data *DataStruct;
    uint16_t crcWord = 0;
    int32_t Voltage_Channel_7;
    
    // Flag to indicate if a /DRDY interrupt has occurred
    static volatile bool flag_nDRDY_INTERRUPT = false;
    static volatile bool read_flag = false;
    static volatile bool lock_unlock_flag;
    #define SSI_BASE_ADDR       (SSI3_BASE)
    
    //*****************************************************************************
    //
    // The error routine that is called if the driver library encounters an error.
    //
    //*****************************************************************************
    #ifdef DEBUG
    void
    __error__(char *pcFilename, uint32_t ui32Line)
    {
        while(1);
    }
    #endif
    
    //*****************************************************************************
    //
    // Main Function. All other functions are written down
    //
    //*****************************************************************************
    int main(void)
    {
        // IMPORTANT: Make sure device is powered before setting GPIOs pins to HIGH state.
    
        // Initialize GPIOs pins used by ADS131M0x
        InitGPIO();
    
        // Initialize SPI peripheral used by ADS131M0x
        InitSPI();
        // Run ADC startup function
    
        while(1)
        {
            adcStartup();
            writeSingleRegister(CLOCK_ADDRESS, 0x800E);
            delay_ms(10);
            register_read();
            lock_unlock_flag = lockRegisters();
            delay_ms(10);
            register_read();
            read_flag = readData(DataStruct);
            lock_unlock_flag = unlockRegisters();
            delay_ms(10);
            register_read();
            // Initialize SPI peripheral used by ADS131M0x
            InitSPI();
            adcStartup();
            delay_ms(10000);
            Voltage_Channel_7 = (((Channel_7_Value*3.3)/16777216)*1000);
            delay_ms(90000);
            delay_ms(10000);
            delay_ms(90000);
        }
    
    }
    
    
    void register_read(void)
    {
        Clock_Register = readSingleRegister(CLOCK_ADDRESS);
        delay_ms(10);
        Status_Register = readSingleRegister(STATUS_ADDRESS);
        delay_ms(10);
        CFG_Register = readSingleRegister(CFG_ADDRESS);
        delay_ms(10);
        Gain2_Register = readSingleRegister(GAIN2_ADDRESS);
        delay_ms(10);
    }
    //*****************************************************************************
    //
    //! Configures the MCU's GPIO pins that interface with the ADC.
    //!
    //! \fn void InitGPIO(void)
    //!
    //! \return None.
    //
    //*****************************************************************************
    void InitGPIO(void)
    {
    
        /* Enable the clock to the GPIO Port K and wait for it to be ready */
        MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOK);
        while(!(SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOK)))
        {
        }
    
        /* Configure the GPIO for 'nSYNC_nRESET' as output and set high */
        MAP_GPIOPinTypeGPIOOutput(nSYNC_nRESET_PORT, nSYNC_nRESET_PIN);
        MAP_GPIOPinWrite(nSYNC_nRESET_PORT, nSYNC_nRESET_PIN, nSYNC_nRESET_PIN);
    
        /* Configure the GPIO for 'nCS' as output and set high */
        MAP_GPIOPinTypeGPIOOutput(nCS_PORT, nCS_PIN);
        MAP_GPIOPinWrite(nCS_PORT, nCS_PIN, nCS_PIN);
    
        /* Enable the clock to the GPIO Port M and wait for it to be ready */
        MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOM);
        while(!(SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOM)))
        {
        }
    
        /* Configure the GPIO for 'nDRDY' as input with falling edge interrupt */
        GPIOIntRegister(nDRDY_PORT, GPIO_DRDY_IRQHandler);
        MAP_GPIOPinTypeGPIOInput(nDRDY_PORT, nDRDY_PIN);
        MAP_GPIOIntTypeSet(nDRDY_PORT, nDRDY_PIN, GPIO_FALLING_EDGE);
        MAP_GPIOIntEnable(nDRDY_PORT, nDRDY_PIN);
        MAP_IntEnable(nDRDY_INT);
    }
    
    //*****************************************************************************
    //
    // Interrupt handler for nDRDY GPIO
    //
    //*****************************************************************************
    
    
    
    //*****************************************************************************
    //
    //! Interrupt handler for /DRDY falling edge interrupt.
    //!
    //! \fn void GPIO_DRDY_IRQHandler(void)
    //!
    //! \return None.
    //
    //*****************************************************************************
    void GPIO_DRDY_IRQHandler(void)
    {
    
        // Possible ways to handle this interrupt:
        // If you decide to read data here, you may want to disable other interrupts to avoid partial data reads.
    
        // In this example we set a flag and exit the interrupt routine. In the main program loop, your application can examine
        // all state flags and decide which state (operation) to perform next.
    
        /* Get the interrupt status from the GPIO and clear the status */
        uint32_t getIntStatus = MAP_GPIOIntStatus(nDRDY_PORT, true);
    
        /* Check if the nDRDY pin triggered the interrupt */
        if(getIntStatus & nDRDY_PIN)
        {
            /* Interrupt action: Set a flag */
            flag_nDRDY_INTERRUPT = true;
        }
    
        /* Clear interrupt */
        MAP_GPIOIntClear(nDRDY_PORT, getIntStatus);
    
        // NOTE: We add a short delay at the end to prevent re-entrance. Refer to E2E issue:
        // https://e2e.ti.com/support/microcontrollers/tiva_arm/f/908/p/332605/1786938#1786938
        SysCtlDelay(3);
    }
    
    //*****************************************************************************
    //
    //! Configures the MCU's SPI peripheral, for interfacing with the ADC.
    //!
    //! \fn void InitSPI(void)
    //!
    //! \return None.
    //
    //*****************************************************************************
    void InitSPI(void)
    {
        //
        // Enable the clock to SSI-3 module and configure the SSI Master
        //
        MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI3);
        while(!(MAP_SysCtlPeripheralReady(SYSCTL_PERIPH_SSI3)))
        {
        }
    
        //
        // Enable clocks to GPIO Port Q and configure pins as SSI
        //
        MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOQ);
        while(!(MAP_SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOQ)))
        {
        }
    
        MAP_GPIOPinConfigure(GPIO_PQ0_SSI3CLK);
        //MAP_GPIOPinConfigure(GPIO_PA3_SSI0FSS); // Using GPIO for nCS instead of the FSS pin.
        MAP_GPIOPinConfigure(GPIO_PQ2_SSI3XDAT0);
        MAP_GPIOPinConfigure(GPIO_PQ3_SSI3XDAT1);
        MAP_GPIOPinTypeSSI(GPIO_PORTQ_BASE, (GPIO_PIN_0 | GPIO_PIN_2 | GPIO_PIN_3));
    
        /* Configure the system clock for 120 MHz */
        systemClock = MAP_SysCtlClockFreqSet((SYSCTL_XTAL_25MHZ | SYSCTL_OSC_MAIN |
                                                  SYSCTL_USE_PLL | SYSCTL_CFG_VCO_480),
                                                  120000000);
    
        // Configure: SPI MODE 1, 5 MHz SCLK, 8-bits per frame
        MAP_SSIConfigSetExpClk(SSI_BASE_ADDR, systemClock, SSI_FRF_MOTO_MODE_1,   \
                               SSI_MODE_MASTER, (systemClock/24), 8);
        //MAP_SSIEnable(SSI_BASE_ADDR);
    
        //
        // Enable the SSI2 module.
        //
        SSIEnable(SSI_BASE_ADDR);
        SSIAdvModeSet(SSI_BASE_ADDR, SSI_ADV_MODE_READ_WRITE);
        SSIAdvFrameHoldDisable(SSI_BASE_ADDR);
    
        //
        // Read any residual data from the SSI port.  This makes sure the receive
        // FIFOs are empty, so we don't read any unwanted junk.  This is done here
        // because the SPI SSI mode is full-duplex, which allows you to send and
        // receive at the same time.  The SSIDataGetNonBlocking function returns
        // "true" when data was returned, and "false" when no data was returned.
        // The "non-blocking" function checks if there is any data in the receive
        // FIFO and does not "hang" if there isn't.
        //
        uint32_t junk;
        while(MAP_SSIDataGetNonBlocking(SSI_BASE_ADDR, &junk));
    }
    
    //*****************************************************************************
    //
    //! Start up sequence for the ADS131M0x.
    //!
    //! \fn void adcStartup(void)
    //!
    //! Before calling this function, the device must be powered,
    //! the SPI/GPIO pins of the MCU must have already been configured,
    //! and (if applicable) the external clock source should be provided to CLKIN.
    //!
    //! \return None.
    //
    //*****************************************************************************
    void adcStartup(void)
    {
        /* (OPTIONAL) Provide additional delay time for power supply settling */
    	delay_ms(50);
    	setCS(LOW);
    	delay_ms(2);
    	/* (REQUIRED) Set nRESET pin high for ADC operation */
    	setSYNC_RESET(HIGH);
    
    	/* (OPTIONAL) Toggle nRESET pin to ensure default register settings. */
    	/* NOTE: This also ensures that the device registers are unlocked.	 */
    	toggleRESET();
    
        /* (REQUIRED) Initialize internal 'registerMap' array with device default settings */
    	restoreRegisterDefaults();
    
        /* (OPTIONAL) Validate first response word when beginning SPI communication: (0xFF20 | CHANCNT) */
    	//uint16_t response = sendCommand(OPCODE_NULL);
    
    	/* (OPTIONAL) Define your initial register settings here */
        //writeSingleRegister(CLOCK_ADDRESS, (CLOCK_DEFAULT & ~CLOCK_OSR_MASK) | CLOCK_OSR_256);
    
        /* (REQUIRED) Configure MODE register settings
         * NOTE: This function call is required here for this particular code implementation to work.
         * This function will enforce the MODE register settings as selected in the 'ads131m0x.h' header file.
         */
       // writeSingleRegister(MODE_ADDRESS, MODE_DEFAULT);
    
        /* (OPTIONAL) Read back all registers */
    
    	/* (OPTIONAL) Check STATUS register for faults */
    }
    
    
    //*****************************************************************************
    //
    //! Provides a timing delay with 'ms' resolution.
    //!
    //! \fn void delay_ms(const uint32_t delay_time_ms)
    //!
    //! \param delay_time_ms is the number of milliseconds to delay.
    //!
    //! \return None.
    //
    //*****************************************************************************
    void delay_ms(const uint32_t delay_time_ms)
    {
        const uint32_t cycles_per_loop = 3;
        MAP_SysCtlDelay( delay_time_ms * systemClock / (cycles_per_loop * 1000u) );
    }
    
    
    //*****************************************************************************
    //
    //! Controls the state of the nSYNC/nRESET GPIO pin.
    //!
    //! \fn void setSYNC_RESET(const bool state)
    //!
    //! \param state boolean indicating which state to set the nSYNC/nRESET pin (0=low, 1=high)
    //!
    //! NOTE: The 'HIGH' and 'LOW' macros defined in hal.h can be passed to this
    //! function for the 'state' parameter value.
    //!
    //! \return None.
    //
    //*****************************************************************************
    void setSYNC_RESET(const bool state)
    {
        uint8_t value = (uint8_t) (state ? nSYNC_nRESET_PIN : 0);
        MAP_GPIOPinWrite(nSYNC_nRESET_PORT, nSYNC_nRESET_PIN, value);
    }
    
    
    //*****************************************************************************
    //
    //! Controls the state of the /CS GPIO pin.
    //!
    //! \fn void setCS(const bool state)
    //!
    //! \param state boolean indicating which state to set the /CS pin (0=low, 1=high)
    //!
    //! NOTE: The 'HIGH' and 'LOW' macros defined in hal.h can be passed to this
    //! function for the 'state' parameter value.
    //!
    //! \return None.
    //
    //*****************************************************************************
    void setCS(const bool state)
    {
        // td(CSSC) delay
        if(state) { SysCtlDelay(2); }
    
        uint8_t value = (uint8_t) (state ? nCS_PIN : 0);
        MAP_GPIOPinWrite(nCS_PORT, nCS_PIN, value);
    
        // td(SCCS) delay
        if(!state) { SysCtlDelay(2); }
    }
    
    //*****************************************************************************
    //
    //! Toggles the "nSYNC/nRESET" pin to trigger a reset
    //! (LOW, delay 2 ms, then HIGH).
    //!
    //! \fn void toggleRESET(void)
    //!
    //! \return None.
    //
    //*****************************************************************************
    void toggleRESET(void)
    {
        setCS(LOW);
        delay_ms(100);
        MAP_GPIOPinWrite(nSYNC_nRESET_PORT, nSYNC_nRESET_PIN, 0);
    
        // Minimum /RESET pulse width (tSRLRST) equals 2,048 CLKIN periods (1 ms @ 2.048 MHz)
        delay_ms(100);
        MAP_GPIOPinWrite(nSYNC_nRESET_PORT, nSYNC_nRESET_PIN, nSYNC_nRESET_PIN);
    
        // tREGACQ delay before communicating with the device again
        delay_ms(100);
        setCS(LOW);
        delay_ms(100);
        // NOTE: The ADS131M0x's next response word should be (0xFF20 | CHANCNT).
        // A different response may be an indication that the device did not reset.
    
        // Update register array
        //restoreRegisterDefaults();
    
        // Write to MODE register to enforce mode settings
        //writeSingleRegister(MODE_ADDRESS, MODE_DEFAULT);
    }
    
    
    //*****************************************************************************
    //
    //! Updates the registerMap[] array to its default values.
    //!
    //! \fn void restoreRegisterDefaults(void)
    //!
    //! NOTES:
    //! - If the MCU keeps a copy of the ADS131M0x register settings in memory,
    //! then it is important to ensure that these values remain in sync with the
    //! actual hardware settings. In order to help facilitate this, this function
    //! should be called after powering up or resetting the device (either by
    //! hardware pin control or SPI software command).
    //!
    //! - Reading back all of the registers after resetting the device can
    //! accomplish the same result; however, this might be problematic if the
    //! device was previously in CRC mode or the WLENGTH was modified, since
    //! resetting the device exits these modes. If the MCU is not aware of this
    //! mode change, then read register commands will return invalid data due to
    //! the expectation of data appearing in a different byte position.
    //!
    //! \return None.
    //
    //*****************************************************************************
    void restoreRegisterDefaults(void)
    {
        registerMap[ID_ADDRESS]             =   0x00;               /* NOTE: This a read-only register */
        registerMap[STATUS_ADDRESS]         =   STATUS_DEFAULT;
        registerMap[MODE_ADDRESS]           =   MODE_DEFAULT;
        registerMap[CLOCK_ADDRESS]          =   CLOCK_DEFAULT;
        registerMap[GAIN1_ADDRESS]          =   GAIN1_DEFAULT;
        registerMap[GAIN2_ADDRESS]          =   GAIN2_DEFAULT;
        registerMap[CFG_ADDRESS]            =   CFG_DEFAULT;
        registerMap[THRSHLD_MSB_ADDRESS]    =   THRSHLD_MSB_DEFAULT;
        registerMap[THRSHLD_LSB_ADDRESS]    =   THRSHLD_LSB_DEFAULT;
        registerMap[CH0_CFG_ADDRESS]        =   CH0_CFG_DEFAULT;
        registerMap[CH0_OCAL_MSB_ADDRESS]   =   CH0_OCAL_MSB_DEFAULT;
        registerMap[CH0_OCAL_LSB_ADDRESS]   =   CH0_OCAL_LSB_DEFAULT;
        registerMap[CH0_GCAL_MSB_ADDRESS]   =   CH0_GCAL_MSB_DEFAULT;
        registerMap[CH0_GCAL_LSB_ADDRESS]   =   CH0_GCAL_LSB_DEFAULT;
    #if (CHANNEL_COUNT > 1)
        registerMap[CH1_CFG_ADDRESS]        =   CH1_CFG_DEFAULT;
        registerMap[CH1_OCAL_MSB_ADDRESS]   =   CH1_OCAL_MSB_DEFAULT;
        registerMap[CH1_OCAL_LSB_ADDRESS]   =   CH1_OCAL_LSB_DEFAULT;
        registerMap[CH1_GCAL_MSB_ADDRESS]   =   CH1_GCAL_MSB_DEFAULT;
        registerMap[CH1_GCAL_LSB_ADDRESS]   =   CH1_GCAL_LSB_DEFAULT;
    #endif
    #if (CHANNEL_COUNT > 2)
        registerMap[CH2_CFG_ADDRESS]        =   CH2_CFG_DEFAULT;
        registerMap[CH2_OCAL_MSB_ADDRESS]   =   CH2_OCAL_MSB_DEFAULT;
        registerMap[CH2_OCAL_LSB_ADDRESS]   =   CH2_OCAL_LSB_DEFAULT;
        registerMap[CH2_GCAL_MSB_ADDRESS]   =   CH2_GCAL_MSB_DEFAULT;
        registerMap[CH2_GCAL_LSB_ADDRESS]   =   CH2_GCAL_LSB_DEFAULT;
    #endif
    #if (CHANNEL_COUNT > 3)
        registerMap[CH3_CFG_ADDRESS]        =   CH3_CFG_DEFAULT;
        registerMap[CH3_OCAL_MSB_ADDRESS]   =   CH3_OCAL_MSB_DEFAULT;
        registerMap[CH3_OCAL_LSB_ADDRESS]   =   CH3_OCAL_LSB_DEFAULT;
        registerMap[CH3_GCAL_MSB_ADDRESS]   =   CH3_GCAL_MSB_DEFAULT;
        registerMap[CH3_GCAL_LSB_ADDRESS]   =   CH3_GCAL_LSB_DEFAULT;
    #endif
    #if (CHANNEL_COUNT > 4)
        registerMap[CH4_CFG_ADDRESS]        =   CH4_CFG_DEFAULT;
        registerMap[CH4_OCAL_MSB_ADDRESS]   =   CH4_OCAL_MSB_DEFAULT;
        registerMap[CH4_OCAL_LSB_ADDRESS]   =   CH4_OCAL_LSB_DEFAULT;
        registerMap[CH4_GCAL_MSB_ADDRESS]   =   CH4_GCAL_MSB_DEFAULT;
        registerMap[CH4_GCAL_LSB_ADDRESS]   =   CH4_GCAL_LSB_DEFAULT;
    #endif
    #if (CHANNEL_COUNT > 5)
        registerMap[CH5_CFG_ADDRESS]        =   CH5_CFG_DEFAULT;
        registerMap[CH5_OCAL_MSB_ADDRESS]   =   CH5_OCAL_MSB_DEFAULT;
        registerMap[CH5_OCAL_LSB_ADDRESS]   =   CH5_OCAL_LSB_DEFAULT;
        registerMap[CH5_GCAL_MSB_ADDRESS]   =   CH5_GCAL_MSB_DEFAULT;
        registerMap[CH5_GCAL_LSB_ADDRESS]   =   CH5_GCAL_LSB_DEFAULT;
    #endif
    #if (CHANNEL_COUNT > 6)
        registerMap[CH6_CFG_ADDRESS]        =   CH6_CFG_DEFAULT;
        registerMap[CH6_OCAL_MSB_ADDRESS]   =   CH6_OCAL_MSB_DEFAULT;
        registerMap[CH6_OCAL_LSB_ADDRESS]   =   CH6_OCAL_LSB_DEFAULT;
        registerMap[CH6_GCAL_MSB_ADDRESS]   =   CH6_GCAL_MSB_DEFAULT;
        registerMap[CH6_GCAL_LSB_ADDRESS]   =   CH6_GCAL_LSB_DEFAULT;
    #endif
    #if (CHANNEL_COUNT > 7)
        registerMap[CH7_CFG_ADDRESS]        =   CH7_CFG_DEFAULT;
        registerMap[CH7_OCAL_MSB_ADDRESS]   =   CH7_OCAL_MSB_DEFAULT;
        registerMap[CH7_OCAL_LSB_ADDRESS]   =   CH7_OCAL_LSB_DEFAULT;
        registerMap[CH7_GCAL_MSB_ADDRESS]   =   CH7_GCAL_MSB_DEFAULT;
        registerMap[CH7_GCAL_LSB_ADDRESS]   =   CH7_GCAL_LSB_DEFAULT;
    #endif
        registerMap[REGMAP_CRC_ADDRESS]     =   REGMAP_CRC_DEFAULT;
    }
    
    
    //*****************************************************************************
    //
    //! Writes data to a single register.
    //!
    //! \fn void writeSingleRegister(uint8_t address, uint16_t data)
    //!
    //! \param address is the address of the register to write to.
    //! \param data is the value to write.
    //!
    //! This command will be ignored if device registers are locked.
    //!
    //! \return None.
    //
    //*****************************************************************************
    void writeSingleRegister(uint8_t address, uint16_t data)
    {
        /* Check that the register address is in range */
        assert(address < NUM_REGISTERS);
    
        // (OPTIONAL) Enforce certain register field values when
        // writing to the MODE register to fix the operation mode
        if (MODE_ADDRESS == address)
        {
            data = enforce_selected_device_modes(data);
        }
    
        // Build TX and RX byte array
    #ifdef ENABLE_CRC_IN
        uint8_t dataTx[12] = { 0 };     // 3 words, up to 4 bytes each = 12 bytes maximum
        uint8_t dataRx[12] = { 0 };
    #else
        uint8_t dataTx[8] = { 0 };      // 2 words, up to 4 bytes long = 8 bytes maximum
        uint8_t dataRx[8] = { 0 };
    #endif
        uint16_t opcodes[2];
        opcodes[0] = OPCODE_WREG | (((uint16_t) address) << 7);
        opcodes[1] = data;
        uint8_t numberOfBytes = buildSPIarray(&opcodes[0], 2, dataTx);
    
        // Send command
        spiSendReceiveArrays(dataTx, dataRx, numberOfBytes);
    
        // Update internal array
        registerMap[address] = data;
    
        // (RECOMMENDED) Read back register to confirm register write was successful
        register_value = readSingleRegister(address);
        // NOTE: Enabling the CRC words in the SPI command will NOT prevent an invalid W
    }
    
    
    //*****************************************************************************
    //
    //! Modifies MODE register data to maintain device operation according to
    //! preselected mode(s) (RX_CRC_EN, WLENGTH, etc.).
    //!
    //! \fn uint16_t enforce_selected_device_mode(uint16_t data)
    //!
    //! \param data uint16_t register data.
    //!
    //! \return uint16_t modified register data.
    //
    //*****************************************************************************
    uint16_t enforce_selected_device_modes(uint16_t data)
    {
    
    
        ///////////////////////////////////////////////////////////////////////////
        // Enforce RX_CRC_EN setting
    
    #ifdef ENABLE_CRC_IN
        // When writing to the MODE register, ensure RX_CRC_EN bit is ALWAYS set
        data |= MODE_RX_CRC_EN_ENABLED;
    #else
        // When writing to the MODE register, ensure RX_CRC_EN bit is NEVER set
        data &= ~MODE_RX_CRC_EN_ENABLED;
    #endif // ENABLE_CRC_IN
    
    
        ///////////////////////////////////////////////////////////////////////////
        // Enforce WLENGH setting
    
    #ifdef WORD_LENGTH_24BIT
        // When writing to the MODE register, ensure WLENGTH bits are ALWAYS set to 01b
        data = (data & ~MODE_WLENGTH_MASK) | MODE_WLENGTH_24BIT;
    #elif defined WORD_LENGTH_32BIT_SIGN_EXTEND
        // When writing to the MODE register, ensure WLENGH bits are ALWAYS set to 11b
        data = (data & ~MODE_WLENGTH_MASK) | MODE_WLENGTH_32BIT_MSB_SIGN_EXT;
    #elif defined WORD_LENGTH_32BIT_ZERO_PADDED
        // When writing to the MODE register, ensure WLENGH bits are ALWAYS set to 10b
        data = (data & ~MODE_WLENGTH_MASK) | MODE_WLENGTH_32BIT_LSB_ZEROES;
    #elif defined WORD_LENGTH_16BIT_TRUNCATED
        // When writing to the MODE register, ensure WLENGH bits are ALWAYS set to 00b
        data = (data & ~MODE_WLENGTH_MASK) | MODE_WLENGTH_16BIT;
    #endif
    
    
        ///////////////////////////////////////////////////////////////////////////
        // Enforce DRDY_FMT setting
    
    #ifdef DRDY_FMT_PULSE
        // When writing to the MODE register, ensure DRDY_FMT bit is ALWAYS set
        data = (data & ~MODE_DRDY_FMT_MASK) | MODE_DRDY_FMT_NEG_PULSE_FIXED_WIDTH;
    #else
        // When writing to the MODE register, ensure DRDY_FMT bit is NEVER set
        data = (data & ~MODE_DRDY_FMT_MASK) | MODE_DRDY_FMT_LOGIC_LOW;
    #endif
    
    
        ///////////////////////////////////////////////////////////////////////////
        // Enforce CRC_TYPE setting
    
    #ifdef CRC_CCITT
        // When writing to the MODE register, ensure CRC_TYPE bit is NEVER set
        data = (data & ~STATUS_CRC_TYPE_MASK) | STATUS_CRC_TYPE_16BIT_CCITT;
    #elif defined CRC_ANSI
        // When writing to the MODE register, ensure CRC_TYPE bit is ALWAYS set
        data = (data & ~STATUS_CRC_TYPE_MASK) | STATUS_CRC_TYPE_16BIT_ANSI;
    #endif
    
        // Return modified register data
        return data;
    }
    
    
    //*****************************************************************************
    //
    //! Builds SPI TX data arrays according to number of opcodes provided and
    //! currently programmed device word length.
    //!
    //! \fn uint8_t buildSPIarray(const uint16_t opcodeArray[], uint8_t numberOpcodes, uint8_t byteArray[])
    //!
    //! \param opcodeArray[] pointer to an array of 16-bit opcodes to use in the SPI command.
    //! \param numberOpcodes the number of opcodes provided in opcodeArray[].
    //! \param byteArray[] pointer to an array of 8-bit SPI bytes to send to the device.
    //!
    //! NOTE: The calling function must ensure it reserves sufficient memory for byteArray[]!
    //!
    //! \return number of bytes added to byteArray[].
    //
    //*****************************************************************************
    uint8_t buildSPIarray(const uint16_t opcodeArray[], uint8_t numberOpcodes, uint8_t byteArray[])
    {
        /*
         * Frame size = opcode word(s) + optional CRC word
         * Number of bytes per word = 2, 3, or 4
         * Total bytes = bytes per word * number of words
         */
        uint8_t numberWords     = numberOpcodes + (SPI_CRC_ENABLED ? 1 : 0);
        uint8_t bytesPerWord    = getWordByteLength();
        uint8_t numberOfBytes   = numberWords * bytesPerWord;
    
        int i;
        for (i = 0; i < numberOpcodes; i++)
        {
            // NOTE: Be careful not to accidentally overflow the array here.
            // The array and opcodes are defined in the calling function, so
            // we are trusting that no mistakes were made in the calling function!
            byteArray[(i*bytesPerWord) + 0] = upperByte(opcodeArray[i]);
            byteArray[(i*bytesPerWord) + 1] = lowerByte(opcodeArray[i]);
        }
    
    #ifdef ENABLE_CRC_IN
        // Calculate CRC and put it into TX array
        uint16_t crcWord = calculateCRC(&byteArray[0], numberOfBytes, 0xFFFF);
        byteArray[(i*bytesPerWord) + 0] = upperByte(crcWord);
        byteArray[(i*bytesPerWord) + 1] = lowerByte(crcWord);
    #endif
    
        return numberOfBytes;
    }
    
    //*****************************************************************************
    //
    //! Getter function to access registerMap array from outside of this module.
    //!
    //! \fn uint16_t getRegisterValue(uint8_t address)
    //!
    //! NOTE: The internal registerMap arrays stores the last know register value,
    //! since the last read or write operation to that register. This function
    //! does not communicate with the device to retrieve the current register value.
    //! For the most up-to-date register data or retrieving the value of a hardware
    //! controlled register, it is recommend to use readSingleRegister() to read the
    //! current register value.
    //!
    //! \return unsigned 16-bit register value.
    //
    //*****************************************************************************
    uint16_t getRegisterValue(uint8_t address)
    {
        assert(address < NUM_REGISTERS);
        return registerMap[address];
    }
    
    //*****************************************************************************
    //
    //! Returns the ADS131M0x configured word length used for SPI communication.
    //!
    //! \fn uint8_t getWordByteLength(void)
    //!
    //! NOTE: It is important that the MODE register value stored in registerMap[]
    //! remains in sync with the device. If these values get out of sync then SPI
    //! communication may fail!
    //!
    //! \return SPI word byte length (2, 3, or 4)
    //
    //*****************************************************************************
    uint8_t getWordByteLength(void)
    {
        return wlength_byte_values[WLENGTH];
    }
    
    //****************************************************************************
    //
    // Helper functions
    //
    //****************************************************************************
    
    
    //*****************************************************************************
    //
    //! Takes a 16-bit word and returns the most-significant byte.
    //!
    //! \fn uint8_t upperByte(uint16_t uint16_Word)
    //!
    //! \param temp_word is the original 16-bit word.
    //!
    //! \return 8-bit most-significant byte.
    //
    //*****************************************************************************
    uint8_t upperByte(uint16_t uint16_Word)
    {
        uint8_t msByte;
        msByte = (uint8_t) ((uint16_Word >> 8) & 0x00FF);
    
        return msByte;
    }
    
    
    
    //*****************************************************************************
    //
    //! Takes a 16-bit word and returns the least-significant byte.
    //!
    //! \fn uint8_t lowerByte(uint16_t uint16_Word)
    //!
    //! \param temp_word is the original 16-bit word.
    //!
    //! \return 8-bit least-significant byte.
    //
    //*****************************************************************************
    uint8_t lowerByte(uint16_t uint16_Word)
    {
        uint8_t lsByte;
        lsByte = (uint8_t) (uint16_Word & 0x00FF);
    
        return lsByte;
    }
    
    //*****************************************************************************
    //
    //! Takes two 8-bit words and returns a concatenated 16-bit word.
    //!
    //! \fn uint16_t combineBytes(uint8_t upperByte, uint8_t lowerByte)
    //!
    //! \param upperByte is the 8-bit value that will become the MSB of the 16-bit word.
    //! \param lowerByte is the 8-bit value that will become the LSB of the 16-bit word.
    //!
    //! \return concatenated 16-bit word.
    //
    //*****************************************************************************
    uint16_t combineBytes(uint8_t upperByte, uint8_t lowerByte)
    {
        uint16_t combinedValue;
        combinedValue = ((uint16_t) upperByte << 8) | ((uint16_t) lowerByte);
    
        return combinedValue;
    }
    
    
    //*****************************************************************************
    //
    //! Sends SPI byte array on MOSI pin and captures MISO data to a byte array.
    //!
    //! \fn void spiSendReceiveArrays(const uint8_t dataTx[], uint8_t dataRx[], const uint8_t byteLength)
    //!
    //! \param const uint8_t dataTx[] byte array of SPI data to send on MOSI.
    //!
    //! \param uint8_t dataRx[] byte array of SPI data captured on MISO.
    //!
    //! \param uint8_t byteLength number of bytes to send & receive.
    //!
    //! NOTE: Make sure 'dataTx[]' and 'dataRx[]' contain at least as many bytes of data,
    //! as indicated by 'byteLength'.
    //!
    //! \return None.
    //
    //*****************************************************************************
    void spiSendReceiveArrays(const uint8_t dataTx[], uint8_t dataRx[], const uint8_t byteLength)
    {
        /*
         *
         *  This function should send and receive multiple bytes over the SPI.
         *
         *  A typical SPI send/receive sequence may look like the following:
         *  1) Make sure SPI receive buffer is empty
         *  2) Set the /CS pin low (if controlled by GPIO)
         *  3) Send command bytes to SPI transmit buffer
         *  4) Wait for SPI receive interrupt
         *  5) Retrieve data from SPI receive buffer
         *  6) Set the /CS pin high (if controlled by GPIO)
         */
    
        // Require that dataTx and dataRx are not NULL pointers
        assert(dataTx && dataRx);
    
        // Set the nCS pin LOW
        setCS(LOW);
        delay_ms(100);
        // Send all dataTx[] bytes on MOSI, and capture all MISO bytes in dataRx[]
        int i;
        for (i = 0; i < byteLength; i++)
        {
            dataRx[i] = spiSendReceiveByte(dataTx[i]);
        }
    
        // Set the nCS pin HIGH
        setCS(HIGH);
        delay_ms(100);
    }
    
    //*****************************************************************************
    //
    //! Sends SPI byte on MOSI pin and captures MISO return byte value.
    //!
    //! \fn uint8_t spiSendReceiveByte(const uint8_t dataTx)
    //!
    //! \param const uint8_t dataTx data byte to send on MOSI pin.
    //!
    //! NOTE: This function is called by spiSendReceiveArrays(). If it is called
    //! directly, then the /CS pin must also be directly controlled.
    //!
    //! \return Captured MISO response byte.
    //
    //*****************************************************************************
    uint8_t spiSendReceiveByte(const uint8_t dataTx)
    {
        /*
         *  This function should send and receive single bytes over the SPI.
         *  NOTE: This function does not control the /CS pin to allow for
         *  more programming flexibility.
         */
    
        // Remove any residual or old data from the receive FIFO
        uint32_t junk;
        while (SSIDataGetNonBlocking(SSI_BASE_ADDR, &junk));
    
        // SSI TX & RX
        uint8_t dataRx;
        MAP_SSIDataPut(SSI_BASE_ADDR, (uint32_t) dataTx);
        MAP_SSIDataGet(SSI_BASE_ADDR, (uint32_t *) &dataRx);
    
        return dataRx;
    }
    
    //*****************************************************************************
    //
    //! Reads the contents of a single register at the specified address.
    //!
    //! \fn uint16_t readSingleRegister(uint8_t address)
    //!
    //! \param address is the 8-bit address of the register to read.
    //!
    //! \return Returns the 8-bit register read result.
    //
    //*****************************************************************************
    uint16_t readSingleRegister(uint8_t address)
    {
        /* Check that the register address is in range */
        assert(address < NUM_REGISTERS);
    
    // Build TX and RX byte array
    #ifdef ENABLE_CRC_IN
        uint8_t dataTx[8] = { 0 };      // 2 words, up to 4 bytes each = 8 bytes maximum
        uint8_t dataRx[8] = { 0 };
    #else
        uint8_t dataTx[4] = { 0 };      // 1 word, up to 4 bytes long = 4 bytes maximum
        uint8_t dataRx[4] = { 0 };
    #endif
        uint16_t opcode = OPCODE_RREG | (((uint16_t) address) << 7);
        uint8_t numberOfBytes = buildSPIarray(&opcode, 1, dataTx);
    
        // [FRAME 1] Send RREG command
        spiSendReceiveArrays(dataTx, dataRx, numberOfBytes);
    
        // [FRAME 2] Send NULL command to retrieve the register data
        registerMap[address] = sendCommand(OPCODE_NULL);
    
        return registerMap[address];
    }
    
    //*****************************************************************************
    //
    //! Sends the specified SPI command to the ADC (NULL, STANDBY, or WAKEUP).
    //!
    //! \fn uint16_t sendCommand(uint16_t opcode)
    //!
    //! \param opcode SPI command byte.
    //!
    //! NOTE: Other commands have their own dedicated functions to support
    //! additional functionality.
    //!
    //! \return ADC response byte (typically the STATUS byte).
    //
    //*****************************************************************************
    uint16_t sendCommand(uint16_t opcode)
    {
        /* Assert if this function is used to send any of the following opcodes */
        assert(OPCODE_RREG != opcode);      /* Use "readSingleRegister()"   */
        assert(OPCODE_WREG != opcode);      /* Use "writeSingleRegister()"  */
        assert(OPCODE_LOCK != opcode);      /* Use "lockRegisters()"        */
        assert(OPCODE_UNLOCK != opcode);    /* Use "unlockRegisters()"      */
        assert(OPCODE_RESET != opcode);     /* Use "resetDevice()"          */
    
        // Build TX and RX byte array
    #ifdef ENABLE_CRC_IN
        uint8_t dataTx[8] = { 0 };      // 2 words, up to 4 bytes each = 8 bytes maximum
        uint8_t dataRx[8] = { 0 };
    #else
        uint8_t dataTx[4] = { 0 };      // 1 word, up to 4 bytes long = 4 bytes maximum
        uint8_t dataRx[4] = { 0 };
    #endif
        uint8_t numberOfBytes = buildSPIarray(&opcode, 1, dataTx);
    
        /* Set the nCS pin LOW */
        setCS(LOW);
    
        // Send the opcode (and crc word, if enabled)
        int i;
        for (i = 0; i < numberOfBytes; i++)
        {
           dataRx[i] = spiSendReceiveByte(dataTx[i]);
        }
    
        /* Set the nCS pin HIGH */
        setCS(HIGH);
    
        // Combine response bytes and return as a 16-bit word
        uint16_t adcResponse = combineBytes(dataRx[0], dataRx[1]);
        return adcResponse;
    }
    
    
    //*****************************************************************************
    //
    //! Reads ADC data.
    //!
    //! \fn bool readData(adc_channel_data *DataStruct)
    //!
    //! \param *DataStruct points to an adc_channel_data type-defined structure/
    //!
    //! NOTE: Should be called after /DRDY goes low, and not during a /DRDY falling edge!
    //!
    //! \return Returns true if the CRC-OUT of the data read detects an error.
    //
    //*****************************************************************************
    bool readData(adc_channel_data *DataStruct)
    {
        int i;
        uint8_t crcTx[4]                        = { 0 };
        uint8_t dataRx[4]                       = { 0 };
        uint8_t bytesPerWord                    = getWordByteLength();
    
    #ifdef ENABLE_CRC_IN
        // Build CRC word (only if "RX_CRC_EN" register bit is enabled)
        uint16_t crcWordIn = calculateCRC(&DataTx[0], bytesPerWord * 2, 0xFFFF);
        crcTx[0] = upperByte(crcWordIn);
        crcTx[1] = lowerByte(crcWordIn);
    #endif
    
        /* Set the nCS pin LOW */
        setCS(LOW);
    
        // Send NULL word, receive response word
        for (i = 0; i < bytesPerWord; i++)
        {
            dataRx[i] = spiSendReceiveByte(0x00);
        }
        DataStruct->response = combineBytes(dataRx[0], dataRx[1]);
        delay_ms(100);
    
        // (OPTIONAL) Do something with the response (STATUS) word.
        // ...Here we only use the response for calculating the CRC-OUT
        uint16_t crcWord = calculateCRC(&dataRx[0], bytesPerWord, 0xFFFF);
    
        // (OPTIONAL) Ignore CRC error checking
        //crcWord = 0;
    
        // Send 2nd word, receive channel 1 data
        for (i = 0; i < bytesPerWord; i++)
        {
            dataRx[i] = spiSendReceiveByte(crcTx[i]);
        }
        DataStruct->channel0 = signExtend(&dataRx[0]);
        crcWord = calculateCRC(&dataRx[0], bytesPerWord, crcWord);
        Channel_0_Value = DataStruct->channel0;
        delay_ms(1000);
        rewriteregisters();
    
    #if (CHANNEL_COUNT > 1)
    
        // Send 3rd word, receive channel 2 data
        for (i = 0; i < bytesPerWord; i++)
        {
            dataRx[i] = spiSendReceiveByte(0x00);
        }
        DataStruct->channel1 = signExtend(&dataRx[0]);
        crcWord = calculateCRC(&dataRx[0], bytesPerWord, crcWord);
        Channel_1_Value = DataStruct->channel1;
        delay_ms(1000);
        rewriteregisters();
    
    #endif
    #if (CHANNEL_COUNT > 2)
    
        // Send 4th word, receive channel 3 data
        for (i = 0; i < bytesPerWord; i++)
        {
            dataRx[i] = spiSendReceiveByte(0x00);
        }
        DataStruct->channel2 = signExtend(&dataRx[0]);
        crcWord = calculateCRC(&dataRx[0], bytesPerWord, crcWord);
        Channel_2_Value = DataStruct->channel2;
        delay_ms(1000);
        rewriteregisters();
    
    #endif
    #if (CHANNEL_COUNT > 3)
    
        // Send 5th word, receive channel 4 data
        for (i = 0; i < bytesPerWord; i++)
        {
            dataRx[i] = spiSendReceiveByte(0x00);
        }
        DataStruct->channel3 = signExtend(&dataRx[0]);
        crcWord = calculateCRC(&dataRx[0], bytesPerWord, crcWord);
        Channel_3_Value = DataStruct->channel3;
        delay_ms(1000);
        rewriteregisters();
    
    #endif
    #if (CHANNEL_COUNT > 4)
    
        // Send 6th word, receive channel 5 data
        for (i = 0; i < bytesPerWord; i++)
        {
            dataRx[i] = spiSendReceiveByte(0x00);
        }
        DataStruct->channel4 = signExtend(&dataRx[0]);
        crcWord = calculateCRC(&dataRx[0], bytesPerWord, crcWord);
        Channel_4_Value = DataStruct->channel4;
        delay_ms(1000);
        rewriteregisters();
    
    #endif
    #if (CHANNEL_COUNT > 5)
    
        // Send 7th word, receive channel 6 data
        for (i = 0; i < bytesPerWord; i++)
        {
            dataRx[i] = spiSendReceiveByte(0x00);
        }
        DataStruct->channel5 = signExtend(&dataRx[0]);
        crcWord = calculateCRC(&dataRx[0], bytesPerWord, crcWord);
        Channel_5_Value = DataStruct->channel5;
        delay_ms(1000);
        rewriteregisters();
    
    #endif
    #if (CHANNEL_COUNT > 6)
    
        // Send 8th word, receive channel 7 data
        for (i = 0; i < bytesPerWord; i++)
        {
            dataRx[i] = spiSendReceiveByte(0x00);
        }
        DataStruct->channel6 = signExtend(&dataRx[0]);
        crcWord = calculateCRC(&dataRx[0], bytesPerWord, crcWord);
        Channel_6_Value = DataStruct->channel6;
        delay_ms(1000);
        rewriteregisters();
    
    #endif
    #if (CHANNEL_COUNT > 7)
    
        // Send 9th word, receive channel 8 data
        for (i = 0; i < bytesPerWord; i++)
        {
            dataRx[i] = spiSendReceiveByte(0x00);
        }
        DataStruct->channel7 = signExtend(&dataRx[0]);
        crcWord = calculateCRC(&dataRx[0], bytesPerWord, crcWord);
        Channel_7_Value = DataStruct->channel7;
        delay_ms(1000);
        rewriteregisters();
    
    #endif
    
        // Send the next word, receive CRC data
        for (i = 0; i < bytesPerWord; i++)
        {
            dataRx[i] = spiSendReceiveByte(0x00);
        }
        DataStruct->crc = combineBytes(dataRx[0], dataRx[1]);
        delay_ms(1000);
    
        /* NOTE: If we continue calculating the CRC with a matching CRC, the result should be zero.
         * Any non-zero result will indicate a mismatch.
         */
        crcWord = calculateCRC(&dataRx[0], bytesPerWord, crcWord);
    
        /* Set the nCS pin HIGH */
        setCS(HIGH);
    
        // Returns true when a CRC error occurs
        return ((bool) crcWord);
    }
    
    
    //*****************************************************************************
    //
    //! Combines ADC data bytes into a single signed 32-bit word.
    //!
    //! \fn int32_t combineDataBytes(const uint8_t dataBytes[])
    //!
    //! \param dataBytes is a pointer to uint8_t[] where the first element is the MSB.
    //!
    //! \return Returns the signed-extend 32-bit result.
    //
    //*****************************************************************************
    int32_t signExtend(const uint8_t dataBytes[])
    {
    
    #ifdef WORD_LENGTH_24BIT
    
        int32_t upperByte   = ((int32_t) dataBytes[0] << 24);
        int32_t middleByte  = ((int32_t) dataBytes[1] << 16);
        int32_t lowerByte   = ((int32_t) dataBytes[2] << 8);
    
        return (((int32_t) (upperByte | middleByte | lowerByte)) >> 8);     // Right-shift of signed data maintains signed bit
    
    #elif defined WORD_LENGTH_32BIT_SIGN_EXTEND
    
        int32_t signByte    = ((int32_t) dataBytes[0] << 24);
        int32_t upperByte   = ((int32_t) dataBytes[1] << 16);
        int32_t middleByte  = ((int32_t) dataBytes[2] << 8);
        int32_t lowerByte   = ((int32_t) dataBytes[3] << 0);
    
        return (signByte | upperByte | middleByte | lowerByte);
    
    #elif defined WORD_LENGTH_32BIT_ZERO_PADDED
    
        int32_t upperByte   = ((int32_t) dataBytes[0] << 24);
        int32_t middleByte  = ((int32_t) dataBytes[1] << 16);
        int32_t lowerByte   = ((int32_t) dataBytes[2] << 8);
    
        return (((int32_t) (upperByte | middleByte | lowerByte)) >> 8);     // Right-shift of signed data maintains signed bit
    
    #elif defined WORD_LENGTH_16BIT_TRUNCATED
    
        int32_t upperByte   = ((int32_t) dataBytes[0] << 24);
        int32_t lowerByte   = ((int32_t) dataBytes[1] << 16);
    
        return (((int32_t) (upperByte | lowerByte)) >> 16);                 // Right-shift of signed data maintains signed bit
    
    #endif
    }
    
    //*****************************************************************************
    //
    //! Sends the LOCK command and verifies that registers are locked.
    //!
    //! \fn bool lockRegisters(void)
    //!
    //! \return boolean to indicate if an error occurred (0 = no error; 1 = error)
    //
    //*****************************************************************************
    bool lockRegisters(void)
    {
        bool b_lock_error = 0;
    
        // Build TX and RX byte array
    #ifdef ENABLE_CRC_IN
        uint8_t dataTx[8] = { 0 };      // 2 words, up to 4 bytes each = 8 bytes maximum
        uint8_t dataRx[8] = { 0 };
    #else
        uint8_t dataTx[4] = { 0 };      // 1 word, up to 4 bytes long = 4 bytes maximum
        uint8_t dataRx[4] = { 0 };
    #endif
        uint16_t opcode         = OPCODE_LOCK;
        uint8_t numberOfBytes   = buildSPIarray(&opcode, 1, dataTx);
    
        // Send command
        spiSendReceiveArrays(dataTx, dataRx, numberOfBytes);
    
        /* (OPTIONAL) Check for SPI errors by sending the NULL command and checking STATUS */
    
        /* (OPTIONAL) Read back the STATUS register and check if LOCK bit is set... */
        Status_Register = readSingleRegister(STATUS_ADDRESS);
        if (!SPI_LOCKED) { b_lock_error = true; }
    
        /* If the STATUS register is NOT read back,
         * then make sure to manually update the global register map variable... */
        //registerMap[STATUS_ADDRESS]  |= STATUS_LOCK_LOCKED;
    
        /* (OPTIONAL) Error handler */
        //if (b_lock_error)
        //{
            // Insert error handler function call here...
        //}
    
        return b_lock_error;
    }
    
    
    //*****************************************************************************
    //
    //! Sends the UNLOCK command and verifies that registers are unlocked
    //!
    //! \fn bool unlockRegisters(void)
    //!
    //! \return boolean to indicate if an error occurred (0 = no error; 1 = error)
    //
    //*****************************************************************************
    bool unlockRegisters(void)
    {
        bool b_unlock_error;
    
        // Build TX and RX byte array
    #ifdef ENABLE_CRC_IN
        uint8_t dataTx[8] = { 0 };      // 2 words, up to 4 bytes each = 8 bytes maximum
        uint8_t dataRx[8] = { 0 };
    #else
        uint8_t dataTx[4] = { 0 };      // 1 word, up to 4 bytes long = 4 bytes maximum
        uint8_t dataRx[4] = { 0 };
    #endif
        uint16_t opcode = OPCODE_UNLOCK;
        uint8_t numberOfBytes = buildSPIarray(&opcode, 1, dataTx);
    
        // Send command
        spiSendReceiveArrays(dataTx, dataRx, numberOfBytes);
    
        /* (OPTIONAL) Check for SPI errors by sending the NULL command and checking STATUS */
    
        /* (OPTIONAL) Read the STATUS register and check if LOCK bit is cleared... */
        readSingleRegister(STATUS_ADDRESS);
        if (SPI_LOCKED) { b_unlock_error = true; }
    
        /* If the STATUS register is NOT read back,
         * then make sure to manually update the global register map variable... */
        //registerMap[STATUS_ADDRESS]  &= !STATUS_LOCK_LOCKED;
    
        /* (OPTIONAL) Error handler */
        if (b_unlock_error)
        {
            // Insert error handler function call here...
        }
    
        return b_unlock_error;
    }
    
    //*****************************************************************************
    //
    //! Calculates the 16-bit CRC for the selected CRC polynomial.
    //!
    //! \fn uint16_t calculateCRC(const uint8_t dataBytes[], uint8_t numberBytes, uint16_t initialValue)
    //!
    //! \param dataBytes[] pointer to first element in the data byte array
    //! \param numberBytes number of bytes to be used in CRC calculation
    //! \param initialValue the seed value (or partial crc calculation), use 0xFFFF when beginning a new CRC computation
    //!
    //! NOTE: This calculation is shown as an example and is not optimized for speed.
    //!
    //! \return 16-bit calculated CRC word
    //
    //*****************************************************************************
    uint16_t calculateCRC(const uint8_t dataBytes[], uint8_t numberBytes, uint16_t initialValue)
    {
        /* Check that "dataBytes" is not a null pointer */
        assert(dataBytes != 0x00);
    
        int         bitIndex, byteIndex;
        bool        dataMSb;                        /* Most significant bit of data byte */
        bool        crcMSb;                         /* Most significant bit of crc byte  */
        uint8_t     bytesPerWord = wlength_byte_values[WLENGTH];
    
        /*
         * Initial value of crc register
         * NOTE: The ADS131M0x defaults to 0xFFFF,
         * but can be set at function call to continue an on-going calculation
         */
        uint16_t crc = initialValue;
    
        #ifdef CRC_CCITT
        /* CCITT CRC polynomial = x^16 + x^12 + x^5 + 1 */
        const uint16_t poly = 0x1021;
        #endif
    
        #ifdef CRC_ANSI
        /* ANSI CRC polynomial = x^16 + x^15 + x^2 + 1 */
        const uint16_t poly = 0x8005;
        #endif
    
        //
        // CRC algorithm
        //
    
        // Loop through all bytes in the dataBytes[] array
        for (byteIndex = 0; byteIndex < numberBytes; byteIndex++)
        {
            // Point to MSb in byte
            bitIndex = 0x80u;
    
            // Loop through all bits in the current byte
            while (bitIndex > 0)
            {
                // Check MSB's of data and crc
                dataMSb = (bool) (dataBytes[byteIndex] & bitIndex);
                crcMSb  = (bool) (crc & 0x8000u);
    
                crc <<= 1;              /* Left shift CRC register */
    
                // Check if XOR operation of MSBs results in additional XOR operations
                if (dataMSb ^ crcMSb)
                {
                    crc ^= poly;        /* XOR crc with polynomial */
                }
    
                /* Shift MSb pointer to the next data bit */
                bitIndex >>= 1;
            }
        }
    
        return crc;
    }
    
    void rewriteregisters(void)
    {
        lock_unlock_flag = unlockRegisters();
        delay_ms(10);
        register_read();
        writeSingleRegister(CLOCK_ADDRESS, 0x800E);
        delay_ms(10);
        writeSingleRegister(CFG_ADDRESS, 0x0600);
        delay_ms(10);
        lock_unlock_flag = lockRegisters();
        delay_ms(10);
        register_read();
    }
    

    Thanks

    Ram.

  • Hi,

      You might want to try something like below. 

    adc_channel_data DataStruct;

    adc_channel_data *My_DataStruct = &DataStruct;

    read_flag = readData(My_DataStruct );