Host API Example for the ADMW1001

src/adi_sense_1000.c

Committer:
abrophy
Date:
2019-06-04
Revision:
4:2ca06eee5735
Parent:
0:85855ecd3257

File content as of revision 4:2ca06eee5735:

/*
Copyright 2018 (c) Analog Devices, Inc.

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 Analog Devices, Inc. nor the names of its
    contributors may be used to endorse or promote products derived
    from this software without specific prior written permission.
  - The use of this software may or may not infringe the patent rights
    of one or more patent holders. This license does not release you
    from the requirement that you obtain separate licenses from these
    patent holders to use this software.
  - Use of the software either in source or binary form, must be run
    on or directly connected to an Analog Devices Inc. component.

THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT,
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, 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 2017 (c) Analog Devices, Inc.

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 Analog Devices, Inc. nor the names of its
    contributors may be used to endorse or promote products derived
    from this software without specific prior written permission.
  - The use of this software may or may not infringe the patent rights
    of one or more patent holders. This license does not release you
    from the requirement that you obtain separate licenses from these
    patent holders to use this software.
  - Use of the software either in source or binary form, must be run
    on or directly connected to an Analog Devices Inc. component.

THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT,
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, 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.
 *
 *****************************************************************************/

/*!
 ******************************************************************************
 * @file:
 * @brief: ADISENSE API implementation for ADSNS1000
 *-----------------------------------------------------------------------------
 */

#include <float.h>
#include <math.h>
#include <string.h>

#include "adi_sense_platform.h"
#include "adi_sense_api.h"
#include "adi_sense_1000/adi_sense_1000_api.h"

#include "adi_sense_1000/ADISENSE1000_REGISTERS_typedefs.h"
#include "adi_sense_1000/ADISENSE1000_REGISTERS.h"
#include "adi_sense_1000/adi_sense_1000_lut_data.h"
#include "adi_sense_1000/adi_sense_1000_host_comms.h"

#include "crc16.h"


uint32_t    getDataCnt = 0;

/*
 * The following macros are used to encapsulate the register access code
 * to improve readability in the functions further below in this file
 */
#define STRINGIFY(name) #name

/* Expand the full name of the reset value macro for the specified register */
#define REG_RESET_VAL(_name) REG_ADISENSE_##_name##_RESET

/* Checks if a value is outside the bounds of the specified register field */
#define CHECK_REG_FIELD_VAL(_field, _val)                               \
    do {                                                                \
        uint32_t _mask  = BITM_ADISENSE_##_field;                       \
        uint32_t _shift = BITP_ADISENSE_##_field;                       \
        if ((((_val) << _shift) & ~(_mask)) != 0) {                     \
            ADI_SENSE_LOG_ERROR("Value 0x%08X invalid for register field %s", \
                                (uint32_t)(_val),                       \
                                STRINGIFY(ADISENSE_##_field));          \
            return ADI_SENSE_INVALID_PARAM;                             \
        }                                                               \
    } while(false)

/*
 * Encapsulates the write to a specified register
 * NOTE - this will cause the calling function to return on error
 */
#define WRITE_REG(_hdev, _val, _name, _type)                            \
    do {                                                                \
        ADI_SENSE_RESULT _res;                                          \
        _type _regval = _val;                                           \
        _res = adi_sense_1000_WriteRegister((_hdev),                    \
                                            REG_ADISENSE_##_name,       \
                                            &_regval, sizeof(_regval)); \
        if (_res != ADI_SENSE_SUCCESS)                                  \
            return _res;                                                \
    } while(false)

/* Wrapper macro to write a value to a uint32_t register */
#define WRITE_REG_U32(_hdev, _val, _name)       \
    WRITE_REG(_hdev, _val, _name, uint32_t)
/* Wrapper macro to write a value to a uint16_t register */
#define WRITE_REG_U16(_hdev, _val, _name)       \
    WRITE_REG(_hdev, _val, _name, uint16_t)
/* Wrapper macro to write a value to a uint8_t register */
#define WRITE_REG_U8(_hdev, _val, _name)        \
    WRITE_REG(_hdev, _val, _name, uint8_t)
/* Wrapper macro to write a value to a float32_t register */
#define WRITE_REG_FLOAT(_hdev, _val, _name)     \
    WRITE_REG(_hdev, _val, _name, float32_t)

/*
 * Encapsulates the read from a specified register
 * NOTE - this will cause the calling function to return on error
 */
#define READ_REG(_hdev, _val, _name, _type)                             \
    do {                                                                \
        ADI_SENSE_RESULT _res;                                          \
        _type _regval;                                                  \
        _res = adi_sense_1000_ReadRegister((_hdev),                     \
                                           REG_ADISENSE_##_name,        \
                                           &_regval, sizeof(_regval));  \
        if (_res != ADI_SENSE_SUCCESS)                                  \
            return _res;                                                \
        _val = _regval;                                                 \
    } while(false)

/* Wrapper macro to read a value from a uint32_t register */
#define READ_REG_U32(_hdev, _val, _name)        \
    READ_REG(_hdev, _val, _name, uint32_t)
/* Wrapper macro to read a value from a uint16_t register */
#define READ_REG_U16(_hdev, _val, _name)        \
    READ_REG(_hdev, _val, _name, uint16_t)
/* Wrapper macro to read a value from a uint8_t register */
#define READ_REG_U8(_hdev, _val, _name)         \
    READ_REG(_hdev, _val, _name, uint8_t)
/* Wrapper macro to read a value from a float32_t register */
#define READ_REG_FLOAT(_hdev, _val, _name)      \
    READ_REG(_hdev, _val, _name, float32_t)

/*
 * Wrapper macro to write an array of values to a uint8_t register
 * NOTE - this is intended only for writing to a keyhole data register
 */
#define WRITE_REG_U8_ARRAY(_hdev, _arr, _len, _name)                \
    do {                                                            \
        ADI_SENSE_RESULT _res;                                      \
        _res = adi_sense_1000_WriteRegister(_hdev,                  \
                                            REG_ADISENSE_##_name,   \
                                            _arr, _len);            \
        if (_res != ADI_SENSE_SUCCESS)                              \
            return _res;                                            \
    } while(false)

/*
 * Wrapper macro to read an array of values from a uint8_t register
 * NOTE - this is intended only for reading from a keyhole data register
 */
#define READ_REG_U8_ARRAY(_hdev, _arr, _len, _name)                 \
    do {                                                            \
        ADI_SENSE_RESULT _res;                                      \
        _res = adi_sense_1000_ReadRegister((_hdev),                 \
                                           REG_ADISENSE_##_name,    \
                                           _arr, _len);             \
        if (_res != ADI_SENSE_SUCCESS)                              \
            return _res;                                            \
    } while(false)

#define ADI_SENSE_1000_CHANNEL_IS_ADC(c)                                \
    ((c) >= ADI_SENSE_1000_CHANNEL_ID_CJC_0 && (c) <= ADI_SENSE_1000_CHANNEL_ID_CURRENT_0)

#define ADI_SENSE_1000_CHANNEL_IS_ADC_CJC(c)                            \
    ((c) >= ADI_SENSE_1000_CHANNEL_ID_CJC_0 && (c) <= ADI_SENSE_1000_CHANNEL_ID_CJC_1)

#define ADI_SENSE_1000_CHANNEL_IS_ADC_SENSOR(c)                         \
    ((c) >= ADI_SENSE_1000_CHANNEL_ID_SENSOR_0 && (c) <= ADI_SENSE_1000_CHANNEL_ID_SENSOR_3)

#define ADI_SENSE_1000_CHANNEL_IS_ADC_VOLTAGE(c)    \
    ((c) == ADI_SENSE_1000_CHANNEL_ID_VOLTAGE_0)

#define ADI_SENSE_1000_CHANNEL_IS_ADC_CURRENT(c)    \
    ((c) == ADI_SENSE_1000_CHANNEL_ID_CURRENT_0)

#define ADI_SENSE_1000_CHANNEL_IS_VIRTUAL(c)                            \
    ((c) == ADI_SENSE_1000_CHANNEL_ID_SPI_1 || (c) == ADI_SENSE_1000_CHANNEL_ID_SPI_2)

typedef struct
{
    unsigned nDeviceIndex;
    ADI_SENSE_SPI_HANDLE hSpi;
    ADI_SENSE_GPIO_HANDLE hGpio;
} ADI_SENSE_DEVICE_CONTEXT;

static ADI_SENSE_DEVICE_CONTEXT gDeviceCtx[ADI_SENSE_PLATFORM_MAX_DEVICES];

/*
 * Open an ADISENSE device instance.
 */
ADI_SENSE_RESULT adi_sense_Open(
    unsigned                   const nDeviceIndex,
    ADI_SENSE_CONNECTION     * const pConnectionInfo,
    ADI_SENSE_DEVICE_HANDLE  * const phDevice)
{
    ADI_SENSE_DEVICE_CONTEXT *pCtx;
    ADI_SENSE_RESULT eRet;

    if (nDeviceIndex >= ADI_SENSE_PLATFORM_MAX_DEVICES)
        return ADI_SENSE_INVALID_DEVICE_NUM;

    pCtx = &gDeviceCtx[nDeviceIndex];
    pCtx->nDeviceIndex = nDeviceIndex;

    eRet = adi_sense_LogOpen(&pConnectionInfo->log);
    if (eRet != ADI_SENSE_SUCCESS)
        return eRet;

    eRet = adi_sense_GpioOpen(&pConnectionInfo->gpio, &pCtx->hGpio);
    if (eRet != ADI_SENSE_SUCCESS)
        return eRet;

    eRet = adi_sense_SpiOpen(&pConnectionInfo->spi, &pCtx->hSpi);
    if (eRet != ADI_SENSE_SUCCESS)
        return eRet;

    *phDevice = pCtx;
    return ADI_SENSE_SUCCESS;
}

/*
 * Get the current state of the specified GPIO input signal.
 */
ADI_SENSE_RESULT adi_sense_GetGpioState(
    ADI_SENSE_DEVICE_HANDLE   const hDevice,
    ADI_SENSE_GPIO_PIN        const ePinId,
    bool                  * const pbAsserted)
{
    ADI_SENSE_DEVICE_CONTEXT *pCtx = hDevice;

    return adi_sense_GpioGet(pCtx->hGpio, ePinId, pbAsserted);
}

/*
 * Register an application-defined callback function for GPIO interrupts.
 */
ADI_SENSE_RESULT adi_sense_RegisterGpioCallback(
    ADI_SENSE_DEVICE_HANDLE          const hDevice,
    ADI_SENSE_GPIO_PIN               const ePinId,
    ADI_SENSE_GPIO_CALLBACK          const callbackFunction,
    void                           * const pCallbackParam)
{
    ADI_SENSE_DEVICE_CONTEXT *pCtx = hDevice;

    if (callbackFunction)
    {
        return adi_sense_GpioIrqEnable(pCtx->hGpio, ePinId, callbackFunction,
                                       pCallbackParam);
    }
    else
    {
        return adi_sense_GpioIrqDisable(pCtx->hGpio, ePinId);
    }
}

/*
 * Reset the specified ADISENSE device.
 */
ADI_SENSE_RESULT adi_sense_Reset(
    ADI_SENSE_DEVICE_HANDLE    const hDevice)
{
    ADI_SENSE_DEVICE_CONTEXT *pCtx = hDevice;
    ADI_SENSE_RESULT eRet;

    /* Pulse the Reset GPIO pin low for a minimum of 4 microseconds */
    eRet = adi_sense_GpioSet(pCtx->hGpio, ADI_SENSE_GPIO_PIN_RESET, false);
    if (eRet != ADI_SENSE_SUCCESS)
        return eRet;

    adi_sense_TimeDelayUsec(4);

    eRet = adi_sense_GpioSet(pCtx->hGpio, ADI_SENSE_GPIO_PIN_RESET, true);
    if (eRet != ADI_SENSE_SUCCESS)
        return eRet;

    return ADI_SENSE_SUCCESS;
}


/*!
 * @brief Get general status of ADISense module.
 *
 * @param[in]
 * @param[out] pStatus : Pointer to CORE Status struct.
 *
 * @return Status
 *         - #ADI_SENSE_SUCCESS Call completed successfully.
 *         - #ADI_SENSE_FAILURE If status register read fails.
 *
 * @details Read the general status register for the ADISense
 *          module. Indicates Error, Alert conditions, data ready
 *          and command running.
 *
 */
ADI_SENSE_RESULT adi_sense_GetStatus(
    ADI_SENSE_DEVICE_HANDLE    const hDevice,
    ADI_SENSE_STATUS         * const pStatus)
{
    ADI_ADISENSE_CORE_Status_t statusReg;
    READ_REG_U8(hDevice, statusReg.VALUE8, CORE_STATUS);

    ADI_ADISENSE_CORE_Alert_Status_2_t alert2Reg;
    READ_REG_U16(hDevice, alert2Reg.VALUE16, CORE_ALERT_STATUS_2);

    memset(pStatus, 0, sizeof(*pStatus));

    if (!statusReg.Cmd_Running) /* Active-low, so invert it */
        pStatus->deviceStatus |= ADI_SENSE_DEVICE_STATUS_BUSY;
    if (statusReg.Drdy)
        pStatus->deviceStatus |= ADI_SENSE_DEVICE_STATUS_DATAREADY;
    if (statusReg.FIFO_Error)
        pStatus->deviceStatus |= ADI_SENSE_DEVICE_STATUS_FIFO_ERROR;
    if (alert2Reg.Ext_Flash_Error)
        pStatus->deviceStatus |= ADI_SENSE_DEVICE_STATUS_EXT_FLASH_ERROR;
    if (statusReg.Alert_Active)
    {
        pStatus->deviceStatus |= ADI_SENSE_DEVICE_STATUS_ALERT;

        ADI_ADISENSE_CORE_Alert_Code_t alertCodeReg;
        READ_REG_U16(hDevice, alertCodeReg.VALUE16, CORE_ALERT_CODE);
        pStatus->alertCode = alertCodeReg.Alert_Code;

        ADI_ADISENSE_CORE_Channel_Alert_Status_t channelAlertStatusReg;
        READ_REG_U16(hDevice, channelAlertStatusReg.VALUE16,
                     CORE_CHANNEL_ALERT_STATUS);

        for (unsigned i = 0; i < ADI_SENSE_1000_MAX_CHANNELS; i++)
        {
            if (channelAlertStatusReg.VALUE16 & (1 << i))
            {
                ADI_ADISENSE_CORE_Alert_Code_Ch_t channelAlertCodeReg;
                READ_REG_U16(hDevice, channelAlertCodeReg.VALUE16, CORE_ALERT_CODE_CHn(i));
                pStatus->channelAlertCodes[i] = channelAlertCodeReg.Alert_Code_Ch;

                ADI_ADISENSE_CORE_Alert_Detail_Ch_t alertDetailReg;
                READ_REG_U16(hDevice, alertDetailReg.VALUE16,
                             CORE_ALERT_DETAIL_CHn(i));

                if (alertDetailReg.Time_Out)
                    pStatus->channelAlerts[i] |= ADI_SENSE_CHANNEL_ALERT_TIMEOUT;
                if (alertDetailReg.Under_Range)
                    pStatus->channelAlerts[i] |= ADI_SENSE_CHANNEL_ALERT_UNDER_RANGE;
                if (alertDetailReg.Over_Range)
                    pStatus->channelAlerts[i] |= ADI_SENSE_CHANNEL_ALERT_OVER_RANGE;
                if (alertDetailReg.Low_Limit)
                    pStatus->channelAlerts[i] |= ADI_SENSE_CHANNEL_ALERT_LOW_LIMIT;
                if (alertDetailReg.High_Limit)
                    pStatus->channelAlerts[i] |= ADI_SENSE_CHANNEL_ALERT_HIGH_LIMIT;
                if (alertDetailReg.Sensor_Open)
                    pStatus->channelAlerts[i] |= ADI_SENSE_CHANNEL_ALERT_SENSOR_OPEN;
                if (alertDetailReg.Ref_Detect)
                    pStatus->channelAlerts[i] |= ADI_SENSE_CHANNEL_ALERT_REF_DETECT;
                if (alertDetailReg.Config_Err)
                    pStatus->channelAlerts[i] |= ADI_SENSE_CHANNEL_ALERT_CONFIG_ERR;
                if (alertDetailReg.LUT_Error_Ch)
                    pStatus->channelAlerts[i] |= ADI_SENSE_CHANNEL_ALERT_LUT_ERR;
                if (alertDetailReg.Sensor_Not_Ready)
                    pStatus->channelAlerts[i] |= ADI_SENSE_CHANNEL_ALERT_SENSOR_NOT_READY;
                if (alertDetailReg.Comp_Not_Ready)
                    pStatus->channelAlerts[i] |= ADI_SENSE_CHANNEL_ALERT_COMP_NOT_READY;
                if (alertDetailReg.Correction_UnderRange)
                    pStatus->channelAlerts[i] |= ADI_SENSE_CHANNEL_ALERT_LUT_UNDER_RANGE;
                if (alertDetailReg.Correction_OverRange)
                    pStatus->channelAlerts[i] |= ADI_SENSE_CHANNEL_ALERT_LUT_OVER_RANGE;
            }
        }

        if (alert2Reg.Configuration_Error)
            pStatus->deviceStatus |= ADI_SENSE_DEVICE_STATUS_CONFIG_ERROR;
        if (alert2Reg.LUT_Error)
            pStatus->deviceStatus |= ADI_SENSE_DEVICE_STATUS_LUT_ERROR;
    }

    if (statusReg.Error)
    {
        pStatus->deviceStatus |= ADI_SENSE_DEVICE_STATUS_ERROR;

        ADI_ADISENSE_CORE_Error_Code_t errorCodeReg;
        READ_REG_U16(hDevice, errorCodeReg.VALUE16, CORE_ERROR_CODE);
        pStatus->errorCode = errorCodeReg.Error_Code;

        ADI_ADISENSE_CORE_Diagnostics_Status_t diagStatusReg;
        READ_REG_U16(hDevice, diagStatusReg.VALUE16, CORE_DIAGNOSTICS_STATUS);

        if (diagStatusReg.Diag_Checksum_Error)
            pStatus->diagnosticsStatus |= ADI_SENSE_DIAGNOSTICS_STATUS_CHECKSUM_ERROR;
        if (diagStatusReg.Diag_Comms_Error)
            pStatus->diagnosticsStatus |= ADI_SENSE_DIAGNOSTICS_STATUS_COMMS_ERROR;
        if (diagStatusReg.Diag_Supply_Monitor_Error)
            pStatus->diagnosticsStatus |= ADI_SENSE_DIAGNOSTICS_STATUS_SUPPLY_MONITOR_ERROR;
        if (diagStatusReg.Diag_Supply_Cap_Error)
            pStatus->diagnosticsStatus |= ADI_SENSE_DIAGNOSTICS_STATUS_SUPPLY_CAP_ERROR;
        if (diagStatusReg.Diag_Conversion_Error)
            pStatus->diagnosticsStatus |= ADI_SENSE_DIAGNOSTICS_STATUS_CONVERSION_ERROR;
        if (diagStatusReg.Diag_Calibration_Error)
            pStatus->diagnosticsStatus |= ADI_SENSE_DIAGNOSTICS_STATUS_CALIBRATION_ERROR;
    }

    if (statusReg.Alert_Active || statusReg.Error)
    {
        ADI_ADISENSE_CORE_Debug_Code_t debugCodeReg;
        READ_REG_U32(hDevice, debugCodeReg.VALUE32, CORE_DEBUG_CODE);
        pStatus->debugCode = debugCodeReg.Debug_Code;
    }

    return ADI_SENSE_SUCCESS;
}

ADI_SENSE_RESULT adi_sense_GetCommandRunningState(
    ADI_SENSE_DEVICE_HANDLE hDevice,
    bool *pbCommandRunning)
{
    ADI_ADISENSE_CORE_Status_t statusReg;

    READ_REG_U8(hDevice, statusReg.VALUE8, CORE_STATUS);

    /* We should never normally see 0xFF here if the module is operational */
    if (statusReg.VALUE8 == 0xFF)
        return ADI_SENSE_ERR_NOT_INITIALIZED;

    *pbCommandRunning = !statusReg.Cmd_Running; /* Active-low, so invert it */

    return ADI_SENSE_SUCCESS;
}

static ADI_SENSE_RESULT executeCommand(
    ADI_SENSE_DEVICE_HANDLE const hDevice,
    ADI_ADISENSE_CORE_Command_Special_Command const command,
    bool const bWaitForCompletion)
{
    ADI_ADISENSE_CORE_Command_t commandReg;
    bool bCommandRunning;
    ADI_SENSE_RESULT eRet;

    /*
     * Don't allow another command to be issued if one is already running, but
     * make an exception for ADISENSE_CORE_COMMAND_NOP which can be used to
     * request a running command to be stopped (e.g. continuous measurement)
     */
    if (command != ADISENSE_CORE_COMMAND_NOP)
    {
        eRet = adi_sense_GetCommandRunningState(hDevice, &bCommandRunning);
        if (eRet)
            return eRet;

        if (bCommandRunning)
            return ADI_SENSE_IN_USE;
    }

    commandReg.Special_Command = command;
    WRITE_REG_U8(hDevice, commandReg.VALUE8, CORE_COMMAND);

    if (bWaitForCompletion)
    {
        do {
            /* Allow a minimum 50usec delay for status update before checking */
            adi_sense_TimeDelayUsec(50);

            eRet = adi_sense_GetCommandRunningState(hDevice, &bCommandRunning);
            if (eRet)
                return eRet;
        } while (bCommandRunning);
    }

    return ADI_SENSE_SUCCESS;
}

ADI_SENSE_RESULT adi_sense_Shutdown(
    ADI_SENSE_DEVICE_HANDLE const hDevice)
{
    return executeCommand(hDevice, ADISENSE_CORE_COMMAND_POWER_DOWN, false);
}


ADI_SENSE_RESULT adi_sense_ApplyConfigUpdates(
    ADI_SENSE_DEVICE_HANDLE const hDevice)
{
    return executeCommand(hDevice, ADISENSE_CORE_COMMAND_LATCH_CONFIG, true);
}

/*!
 * @brief Start a measurement cycle.
 *
 * @param[out]
 *
 * @return Status
 *         - #ADI_SENSE_SUCCESS Call completed successfully.
 *         - #ADI_SENSE_FAILURE
 *
 * @details Sends the latch config command. Configuration for channels in
 *          conversion cycle should be completed before this function.
 *          Channel enabled bit should be set before this function.
 *          Starts a conversion and configures the format of the sample.
 *
 */
ADI_SENSE_RESULT adi_sense_StartMeasurement(
    ADI_SENSE_DEVICE_HANDLE    const hDevice,
    ADI_SENSE_MEASUREMENT_MODE const eMeasurementMode)
{
    switch (eMeasurementMode)
    {
    case ADI_SENSE_MEASUREMENT_MODE_HEALTHCHECK:
        return executeCommand(hDevice, ADISENSE_CORE_COMMAND_SYSTEM_CHECK, false);
    case ADI_SENSE_MEASUREMENT_MODE_NORMAL:
        return executeCommand(hDevice, ADISENSE_CORE_COMMAND_CONVERT_WITH_RAW, false);
    case ADI_SENSE_MEASUREMENT_MODE_OMIT_RAW:
        return executeCommand(hDevice, ADISENSE_CORE_COMMAND_CONVERT, false);
    case ADI_SENSE_MEASUREMENT_MODE_FFT:
        return executeCommand(hDevice, ADISENSE_CORE_COMMAND_CONVERT_FFT, false);
    default:
        ADI_SENSE_LOG_ERROR("Invalid measurement mode %d specified",
                            eMeasurementMode);
        return ADI_SENSE_INVALID_PARAM;
    }
}

/*
 * Store the configuration settings to persistent memory on the device.
 * The settings can be saved to 4 different flash memory areas (slots).
 * No other command must be running when this is called.
 * Do not power down the device while this command is running.
 */
ADI_SENSE_RESULT adi_sense_SaveConfig(
    ADI_SENSE_DEVICE_HANDLE    const hDevice,
    ADI_SENSE_USER_CONFIG_SLOT const eSlotId)
{
    switch (eSlotId)
    {
        case ADI_SENSE_FLASH_CONFIG_1:
            return executeCommand(hDevice, ADISENSE_CORE_COMMAND_SAVE_CONFIG_1, true);
        case ADI_SENSE_FLASH_CONFIG_2:
            return executeCommand(hDevice, ADISENSE_CORE_COMMAND_SAVE_CONFIG_2, true);
        case ADI_SENSE_FLASH_CONFIG_3:
            return executeCommand(hDevice, ADISENSE_CORE_COMMAND_SAVE_CONFIG_3, true);
        case ADI_SENSE_FLASH_CONFIG_4:
            return executeCommand(hDevice, ADISENSE_CORE_COMMAND_SAVE_CONFIG_4, true);
        default:
            ADI_SENSE_LOG_ERROR("Invalid user config target slot %d specified",
                                eSlotId);
            return ADI_SENSE_INVALID_PARAM;
    }
}

/*
 * Restore the configuration settings from persistent memory on the device.
 * No other command must be running when this is called.
 */
ADI_SENSE_RESULT adi_sense_RestoreConfig(
    ADI_SENSE_DEVICE_HANDLE    const hDevice,
    ADI_SENSE_USER_CONFIG_SLOT const eSlotId)
{
    switch (eSlotId)
    {
        case ADI_SENSE_FLASH_CONFIG_1:
            return executeCommand(hDevice, ADISENSE_CORE_COMMAND_LOAD_CONFIG_1, true);
        case ADI_SENSE_FLASH_CONFIG_2:
            return executeCommand(hDevice, ADISENSE_CORE_COMMAND_LOAD_CONFIG_2, true);
        case ADI_SENSE_FLASH_CONFIG_3:
            return executeCommand(hDevice, ADISENSE_CORE_COMMAND_LOAD_CONFIG_3, true);
        case ADI_SENSE_FLASH_CONFIG_4:
            return executeCommand(hDevice, ADISENSE_CORE_COMMAND_LOAD_CONFIG_4, true);
        default:
            ADI_SENSE_LOG_ERROR("Invalid user config source slot %d specified",
                                eSlotId);
            return ADI_SENSE_INVALID_PARAM;
    }
}

/*
 * Erase the entire external flash memory.
 * No other command must be running when this is called.
 */
ADI_SENSE_RESULT adi_sense_EraseExternalFlash(
    ADI_SENSE_DEVICE_HANDLE    const hDevice)
{
    return executeCommand(hDevice, ADISENSE_CORE_COMMAND_ERASE_EXTERNAL_FLASH, true);
}

/*
 * Read the number of samples stored in external flash memory.
 * No other command must be running when this is called.
 */
ADI_SENSE_RESULT adi_sense_GetExternalFlashSampleCount(
    ADI_SENSE_DEVICE_HANDLE    const hDevice,
    uint32_t * nSampleCount)
{
    ADI_ADISENSE_CORE_Ext_Flash_Sample_Count_t nCount;

    READ_REG_U32(hDevice, nCount.VALUE32, CORE_EXT_FLASH_SAMPLE_COUNT);

    *nSampleCount = nCount.VALUE32;

    return ADI_SENSE_SUCCESS;
}

// DEBUG - TO BE DELETED
ADI_SENSE_RESULT adi_sense_SetExternalFlashIndex(
    ADI_SENSE_DEVICE_HANDLE    const hDevice,
    uint32_t nStartIndex)
{
    WRITE_REG_U32(hDevice, nStartIndex, CORE_EXT_FLASH_INDEX);

    return ADI_SENSE_SUCCESS;
}

/*
 * Read a set of data samples stored in the device external flash memory.
 * This may be called at any time.
 */
ADI_SENSE_RESULT adi_sense_GetExternalFlashData(
    ADI_SENSE_DEVICE_HANDLE    const hDevice,
    ADI_SENSE_DATA_SAMPLE    * const pSamples,
    uint32_t                   const nStartIndex,
    uint32_t                   const nRequested,
    uint32_t                 * const pnReturned)
{
    ADI_SENSE_DEVICE_CONTEXT *pCtx = hDevice;
    uint16_t command = ADI_SENSE_1000_HOST_COMMS_READ_CMD |
        (REG_ADISENSE_CORE_EXT_FLASH_DATA & ADI_SENSE_1000_HOST_COMMS_ADR_MASK);
    uint8_t commandData[2] = {
        command >> 8,
        command & 0xFF
    };
    uint8_t commandResponse[2];
    unsigned nValidSamples = 0;
    ADI_SENSE_RESULT eRet = ADI_SENSE_SUCCESS;

    /* Setup initial sample */
    WRITE_REG_U32(hDevice, nStartIndex, CORE_EXT_FLASH_INDEX);

    /* Send flash read command */
    do {
        eRet = adi_sense_SpiTransfer(pCtx->hSpi, commandData, commandResponse,
                                     sizeof(command), false);
        if (eRet)
        {
            ADI_SENSE_LOG_ERROR("Failed to send read command for external flash");
            return eRet;
        }
        
        adi_sense_TimeDelayUsec(ADI_SENSE_1000_HOST_COMMS_XFER_DELAY);
    } while ((commandResponse[0] != ADI_SENSE_1000_HOST_COMMS_CMD_RESP_0) ||
             (commandResponse[1] != ADI_SENSE_1000_HOST_COMMS_CMD_RESP_1));

    /* Read samples from external flash memory */
    for (unsigned i = 0; i < nRequested; i++)
    {
        ADI_SENSE_1000_Sensor_Result_t sensorResult;
        bool bHoldCs = true;

        /* Keep the CS signal asserted for all but the last sample */
        if ((i + 1) == nRequested)
            bHoldCs = false;

        eRet = adi_sense_SpiTransfer(pCtx->hSpi, NULL, (uint8_t *) (&sensorResult),
                                     8, bHoldCs);
        if (eRet)
        {
            ADI_SENSE_LOG_ERROR("Failed to read data from external flash");
            return eRet;
        }

        ADI_SENSE_DATA_SAMPLE *pSample = &pSamples[nValidSamples];

        pSample->status = (ADI_SENSE_DEVICE_STATUS_FLAGS)0;
        if (sensorResult.Ch_Error)
            pSample->status |= ADI_SENSE_DEVICE_STATUS_ERROR;
        if (sensorResult.Ch_Alert)
            pSample->status |= ADI_SENSE_DEVICE_STATUS_ALERT;

        if (sensorResult.Ch_Raw)
            pSample->rawValue = sensorResult.Raw_Sample;
        else
            pSample->rawValue = 0;

        pSample->channelId = sensorResult.Channel_ID;
        pSample->processedValue = sensorResult.Sensor_Result;

        nValidSamples++;
    }
    *pnReturned = nValidSamples;

    adi_sense_TimeDelayUsec(ADI_SENSE_1000_HOST_COMMS_XFER_DELAY);

    return eRet;
}


/*
 * Store the LUT data to persistent memory on the device.
 * No other command must be running when this is called.
 * Do not power down the device while this command is running.
 */
ADI_SENSE_RESULT adi_sense_SaveLutData(
    ADI_SENSE_DEVICE_HANDLE    const hDevice)
{
    return executeCommand(hDevice, ADISENSE_CORE_COMMAND_SAVE_LUT, true);
}

/*
 * Restore the LUT data from persistent memory on the device.
 * No other command must be running when this is called.
 */
ADI_SENSE_RESULT adi_sense_RestoreLutData(
    ADI_SENSE_DEVICE_HANDLE    const hDevice)
{
    return executeCommand(hDevice, ADISENSE_CORE_COMMAND_LOAD_LUT, true);
}

/*
 * Stop the measurement cycles on the device.
 * To be used only if a measurement command is currently running.
 */
ADI_SENSE_RESULT adi_sense_StopMeasurement(
    ADI_SENSE_DEVICE_HANDLE    const hDevice)
{
    return executeCommand(hDevice, ADISENSE_CORE_COMMAND_NOP, true);
}

/*
 * Run built-in diagnostic checks on the device.
 * Diagnostics are executed according to the current applied settings.
 * No other command must be running when this is called.
 */
ADI_SENSE_RESULT adi_sense_RunDiagnostics(
    ADI_SENSE_DEVICE_HANDLE    const hDevice)
{
    return executeCommand(hDevice, ADISENSE_CORE_COMMAND_RUN_DIAGNOSTICS, true);
}

/*
 * Run self-calibration routines on the device.
 * Calibration is executed according to the current applied settings.
 * No other command must be running when this is called.
 */
ADI_SENSE_RESULT adi_sense_RunCalibration(
    ADI_SENSE_DEVICE_HANDLE    const hDevice)
{
    return executeCommand(hDevice, ADISENSE_CORE_COMMAND_SELF_CALIBRATION, true);
}

/*
 * Run digital calibration routines on the device.
 * Calibration is executed according to the current applied settings.
 * No other command must be running when this is called.
 */
ADI_SENSE_RESULT adi_sense_RunDigitalCalibration(
    ADI_SENSE_DEVICE_HANDLE    const hDevice)
{
    return executeCommand(hDevice, ADISENSE_CORE_COMMAND_CALIBRATE_DIGITAL, true);
}

/*
 * Read a set of data samples from the device.
 * This may be called at any time.
 */
ADI_SENSE_RESULT adi_sense_GetData(
    ADI_SENSE_DEVICE_HANDLE    const hDevice,
    ADI_SENSE_MEASUREMENT_MODE const eMeasurementMode,
    ADI_SENSE_DATA_SAMPLE    * const pSamples,
    uint8_t                    const nBytesPerSample,
    uint32_t                   const nRequested,
    uint32_t                 * const pnReturned)
{
    ADI_SENSE_1000_Sensor_Result_t sensorResult;
    ADI_SENSE_DEVICE_CONTEXT *pCtx = hDevice;
    uint16_t command = ADI_SENSE_1000_HOST_COMMS_READ_CMD |
        (REG_ADISENSE_CORE_DATA_FIFO & ADI_SENSE_1000_HOST_COMMS_ADR_MASK);
    uint8_t commandData[2] = {
        command >> 8,
        command & 0xFF
    };
    uint8_t commandResponse[2];
    unsigned nValidSamples = 0;
    ADI_SENSE_RESULT eRet = ADI_SENSE_SUCCESS;

    do {
        eRet = adi_sense_SpiTransfer(pCtx->hSpi, commandData, commandResponse,
                                     sizeof(command), false);
        if (eRet)
        {
            ADI_SENSE_LOG_ERROR("Failed to send read command for FIFO register");
            return eRet;
        }
        adi_sense_TimeDelayUsec(ADI_SENSE_1000_HOST_COMMS_XFER_DELAY);
    } while ((commandResponse[0] != ADI_SENSE_1000_HOST_COMMS_CMD_RESP_0) ||
             (commandResponse[1] != ADI_SENSE_1000_HOST_COMMS_CMD_RESP_1));

    for (unsigned i = 0; i < nRequested; i++)
    {
        bool bHoldCs = true;

        /* Keep the CS signal asserted for all but the last sample */
        if ((i + 1) == nRequested)
            bHoldCs = false;

        getDataCnt++;

        eRet = adi_sense_SpiTransfer(pCtx->hSpi, NULL, &sensorResult,
                                     nBytesPerSample, bHoldCs);
        if (eRet)
        {
            ADI_SENSE_LOG_ERROR("Failed to read data from FIFO register");
            return eRet;
        }

        if (! sensorResult.Ch_Valid)
        {
            /*
             * Reading an invalid sample indicates that there are no
             * more samples available or we've lost sync with the device.
             * In the latter case, it might be recoverable, but return here
             * to let the application check the device status and decide itself.
             */
            eRet = ADI_SENSE_INCOMPLETE;
            break;
        }

        ADI_SENSE_DATA_SAMPLE *pSample = &pSamples[nValidSamples];

        pSample->status = (ADI_SENSE_DEVICE_STATUS_FLAGS)0;
        if (sensorResult.Ch_Error)
            pSample->status |= ADI_SENSE_DEVICE_STATUS_ERROR;
        if (sensorResult.Ch_Alert)
            pSample->status |= ADI_SENSE_DEVICE_STATUS_ALERT;

        if (sensorResult.Ch_Raw)
            pSample->rawValue = sensorResult.Raw_Sample;
        else
            pSample->rawValue = 0;

        pSample->channelId = sensorResult.Channel_ID;
        pSample->processedValue = sensorResult.Sensor_Result;

        nValidSamples++;

        adi_sense_TimeDelayUsec(ADI_SENSE_1000_HOST_COMMS_XFER_DELAY);
    }
    *pnReturned = nValidSamples;

    return eRet;
}

/*
 * Close the given ADISENSE device.
 */
ADI_SENSE_RESULT adi_sense_Close(
    ADI_SENSE_DEVICE_HANDLE    const hDevice)
{
    ADI_SENSE_DEVICE_CONTEXT *pCtx = hDevice;

    adi_sense_GpioClose(pCtx->hGpio);
    adi_sense_SpiClose(pCtx->hSpi);
    adi_sense_LogClose();

    return ADI_SENSE_SUCCESS;
}

ADI_SENSE_RESULT adi_sense_1000_WriteRegister(
    ADI_SENSE_DEVICE_HANDLE hDevice,
    uint16_t nAddress,
    void *pData,
    unsigned nLength)
{
    ADI_SENSE_RESULT eRet;
    ADI_SENSE_DEVICE_CONTEXT *pCtx = hDevice;
    uint16_t command = ADI_SENSE_1000_HOST_COMMS_WRITE_CMD |
        (nAddress & ADI_SENSE_1000_HOST_COMMS_ADR_MASK);
    uint8_t commandData[2] = {
        command >> 8,
        command & 0xFF
    };
    uint8_t commandResponse[2];

    do {
        eRet = adi_sense_SpiTransfer(pCtx->hSpi, commandData, commandResponse,
                                     sizeof(command), false);
        if (eRet)
        {
            ADI_SENSE_LOG_ERROR("Failed to send write command for register %u",
                                nAddress);
            return eRet;
        }

        adi_sense_TimeDelayUsec(ADI_SENSE_1000_HOST_COMMS_XFER_DELAY);
    } while ((commandResponse[0] != ADI_SENSE_1000_HOST_COMMS_CMD_RESP_0) ||
             (commandResponse[1] != ADI_SENSE_1000_HOST_COMMS_CMD_RESP_1));

    eRet = adi_sense_SpiTransfer(pCtx->hSpi, pData, NULL, nLength, false);
    if (eRet)
    {
        ADI_SENSE_LOG_ERROR("Failed to write data (%dB) to register %u",
                            nLength, nAddress);
        return eRet;
    }

    adi_sense_TimeDelayUsec(ADI_SENSE_1000_HOST_COMMS_XFER_DELAY);

    return ADI_SENSE_SUCCESS;
}

ADI_SENSE_RESULT adi_sense_1000_ReadRegister(
    ADI_SENSE_DEVICE_HANDLE hDevice,
    uint16_t nAddress,
    void *pData,
    unsigned nLength)
{
    ADI_SENSE_RESULT eRet;
    ADI_SENSE_DEVICE_CONTEXT *pCtx = hDevice;
    uint16_t command = ADI_SENSE_1000_HOST_COMMS_READ_CMD |
        (nAddress & ADI_SENSE_1000_HOST_COMMS_ADR_MASK);
    uint8_t commandData[2] = {
        command >> 8,
        command & 0xFF
    };
    uint8_t commandResponse[2];

    do {
        eRet = adi_sense_SpiTransfer(pCtx->hSpi, commandData, commandResponse,
                                     sizeof(command), false);
        if (eRet)
        {
            ADI_SENSE_LOG_ERROR("Failed to send read command for register %u",
                                nAddress);
            return eRet;
        }

        adi_sense_TimeDelayUsec(ADI_SENSE_1000_HOST_COMMS_XFER_DELAY);
    } while ((commandResponse[0] != ADI_SENSE_1000_HOST_COMMS_CMD_RESP_0) ||
             (commandResponse[1] != ADI_SENSE_1000_HOST_COMMS_CMD_RESP_1));

    eRet = adi_sense_SpiTransfer(pCtx->hSpi, NULL, pData, nLength, false);
    if (eRet)
    {
        ADI_SENSE_LOG_ERROR("Failed to read data (%uB) from register %u",
                            nLength, nAddress);
        return eRet;
    }

    adi_sense_TimeDelayUsec(ADI_SENSE_1000_HOST_COMMS_XFER_DELAY);

    return ADI_SENSE_SUCCESS;
}

ADI_SENSE_RESULT adi_sense_GetDeviceReadyState(
    ADI_SENSE_DEVICE_HANDLE   const hDevice,
    bool                  * const bReady)
{
    ADI_ADISENSE_SPI_Chip_Type_t chipTypeReg;

    READ_REG_U8(hDevice, chipTypeReg.VALUE8, SPI_CHIP_TYPE);
    /* If we read this register successfully, assume the device is ready */
    *bReady = (chipTypeReg.VALUE8 == REG_ADISENSE_SPI_CHIP_TYPE_RESET);

    return ADI_SENSE_SUCCESS;
}

ADI_SENSE_RESULT adi_sense_1000_GetDataReadyModeInfo(
    ADI_SENSE_DEVICE_HANDLE         const hDevice,
    ADI_SENSE_MEASUREMENT_MODE      const eMeasurementMode,
    ADI_SENSE_1000_OPERATING_MODE * const peOperatingMode,
    ADI_SENSE_1000_DATAREADY_MODE * const peDataReadyMode,
    uint32_t                      * const pnSamplesPerDataready,
    uint32_t                      * const pnSamplesPerCycle,
    uint8_t                       * const pnBytesPerSample)
{
    unsigned nChannelsEnabled = 0;
    unsigned nSamplesPerCycle = 0;

    ADI_ADISENSE_CORE_Mode_t modeReg;
    READ_REG_U8(hDevice, modeReg.VALUE8, CORE_MODE);

    if ((eMeasurementMode == ADI_SENSE_MEASUREMENT_MODE_HEALTHCHECK) ||
        (modeReg.Conversion_Mode == ADISENSE_CORE_MODE_SINGLECYCLE))
        *peOperatingMode = ADI_SENSE_1000_OPERATING_MODE_SINGLECYCLE;
    else if (modeReg.Conversion_Mode == ADISENSE_CORE_MODE_MULTICYCLE)
        *peOperatingMode = ADI_SENSE_1000_OPERATING_MODE_MULTICYCLE;
    else
        *peOperatingMode = ADI_SENSE_1000_OPERATING_MODE_CONTINUOUS;


    /* FFT mode is quite different to the other modes:
     * - Each FFT result produces a batch of samples
     * - The size of the batch depends on selected FFT size and output config options
     * - DATAREADY will fire for each FFT result (once per channel)
     * - The size of the cycle depends on the number of channels enabled for FFT
     */
    if (eMeasurementMode == ADI_SENSE_MEASUREMENT_MODE_FFT)
    {
        ADI_ADISENSE_CORE_FFT_Config_t fftConfigReg;

        unsigned nFftChannels;
        unsigned nSamplesPerChannel;

        READ_REG_U32(hDevice, fftConfigReg.VALUE32, CORE_FFT_CONFIG);

        nFftChannels = fftConfigReg.FFT_Num_Channels + 1;

        if (fftConfigReg.FFT_Output == ADISENSE_CORE_FFT_CONFIG_FFT_OUTPUT_MAX16)
        {
            nSamplesPerChannel = 16;
            *pnBytesPerSample = 8;
        }
        else if (fftConfigReg.FFT_Output == ADISENSE_CORE_FFT_CONFIG_FFT_OUTPUT_FULL)
        {
            nSamplesPerChannel = (256 << fftConfigReg.FFT_Num_Bins) >> 1;
            *pnBytesPerSample = 5;
        }
        else if (fftConfigReg.FFT_Output == ADISENSE_CORE_FFT_CONFIG_FFT_OUTPUT_FULL_WITH_RAW)
        {
            nSamplesPerChannel = (256 << fftConfigReg.FFT_Num_Bins);
            *pnBytesPerSample = 8;
        }
        else
        {
            ADI_SENSE_LOG_ERROR("Invalid FFT output format option %d configured",
                                fftConfigReg.FFT_Output);
            return ADI_SENSE_INVALID_PARAM;
        }

        *pnSamplesPerDataready = nSamplesPerChannel;
        *pnSamplesPerCycle = nSamplesPerChannel * nFftChannels;

        *peDataReadyMode = ADI_SENSE_1000_DATAREADY_PER_CYCLE;

        if (modeReg.FFT_Mode == ADISENSE_CORE_MODE_FFT_MODE_CONTINUOUS)
        {
            *peOperatingMode = ADI_SENSE_1000_OPERATING_MODE_CONTINUOUS;
        }
        else
        {
            *peOperatingMode = ADI_SENSE_1000_OPERATING_MODE_SINGLECYCLE;
        }
    }
    else
    {
        if (eMeasurementMode == ADI_SENSE_MEASUREMENT_MODE_OMIT_RAW)
        {
            *pnBytesPerSample = 5;
        }
        else
        {
            *pnBytesPerSample = 8;
        }

        for (ADI_SENSE_1000_CHANNEL_ID chId = ADI_SENSE_1000_CHANNEL_ID_CJC_0;
             chId < ADI_SENSE_1000_MAX_CHANNELS;
             chId++)
        {
            ADI_ADISENSE_CORE_Sensor_Details_t sensorDetailsReg;
            ADI_ADISENSE_CORE_Channel_Count_t channelCountReg;

            if (ADI_SENSE_1000_CHANNEL_IS_VIRTUAL(chId))
                continue;

            READ_REG_U8(hDevice, channelCountReg.VALUE8, CORE_CHANNEL_COUNTn(chId));
            READ_REG_U32(hDevice, sensorDetailsReg.VALUE32, CORE_SENSOR_DETAILSn(chId));

            if (channelCountReg.Channel_Enable && !sensorDetailsReg.Do_Not_Publish)
            {
                ADI_ADISENSE_CORE_Sensor_Type_t sensorTypeReg;
                unsigned nActualChannels = 1;

                READ_REG_U16(hDevice, sensorTypeReg.VALUE16, CORE_SENSOR_TYPEn(chId));

                if (chId == ADI_SENSE_1000_CHANNEL_ID_SPI_0)
                {
                    /* Some sensors automatically generate samples on additional "virtual" channels
                     * so these channels must be counted as active when those sensors are selected
                     * and we use the count from the corresponding "physical" channel */
                    if ((sensorTypeReg.Sensor_Type >=
                         ADISENSE_CORE_SENSOR_TYPE_SENSOR_SPI_ACCELEROMETER_A_DEF_L1) &&
                        (sensorTypeReg.Sensor_Type <=
                         ADISENSE_CORE_SENSOR_TYPE_SENSOR_SPI_ACCELEROMETER_B_ADV_L2))
                        nActualChannels += 2;
                }

                nChannelsEnabled += nActualChannels;
                if (eMeasurementMode == ADI_SENSE_MEASUREMENT_MODE_HEALTHCHECK)
                    /* Assume a single sample per channel in test mode */
                    nSamplesPerCycle += nActualChannels;
                else
                    nSamplesPerCycle += nActualChannels *
                        (channelCountReg.Channel_Count + 1);
            }
        }

        if (nChannelsEnabled == 0)
        {
            *pnSamplesPerDataready = 0;
            *pnSamplesPerCycle = 0;
            return ADI_SENSE_SUCCESS;
        }

        *pnSamplesPerCycle = nSamplesPerCycle;

        if (modeReg.Drdy_Mode == ADISENSE_CORE_MODE_DRDY_PER_CONVERSION)
        {
            *pnSamplesPerDataready = 1;
        }
        else if (modeReg.Drdy_Mode == ADISENSE_CORE_MODE_DRDY_PER_CYCLE)
        {
            *pnSamplesPerDataready = nSamplesPerCycle;
        }
        else
        {
            /* Assume DRDY will be asserted after max. 1 cycle in test mode */
            if (eMeasurementMode == ADI_SENSE_MEASUREMENT_MODE_HEALTHCHECK)
            {
                *pnSamplesPerDataready = nSamplesPerCycle;
            }
            else
            {
                ADI_ADISENSE_CORE_Fifo_Num_Cycles_t fifoNumCyclesReg;
                READ_REG_U8(hDevice, fifoNumCyclesReg.VALUE8, CORE_FIFO_NUM_CYCLES);

                *pnSamplesPerDataready =
                nSamplesPerCycle * fifoNumCyclesReg.Fifo_Num_Cycles;
            }
        }

        if (modeReg.Drdy_Mode == ADISENSE_CORE_MODE_DRDY_PER_CONVERSION)
            *peDataReadyMode = ADI_SENSE_1000_DATAREADY_PER_CONVERSION;
        else if (modeReg.Drdy_Mode == ADISENSE_CORE_MODE_DRDY_PER_CYCLE)
            *peDataReadyMode = ADI_SENSE_1000_DATAREADY_PER_CYCLE;
        else
        {
            /* Assume DRDY will be asserted after max. 1 cycle in test mode */
            if (eMeasurementMode == ADI_SENSE_MEASUREMENT_MODE_HEALTHCHECK)
                *peDataReadyMode = ADI_SENSE_1000_DATAREADY_PER_CYCLE;
            else
                *peDataReadyMode = ADI_SENSE_1000_DATAREADY_PER_MULTICYCLE_BURST;
        }
    }

    return ADI_SENSE_SUCCESS;
}

ADI_SENSE_RESULT adi_sense_GetProductID(
    ADI_SENSE_DEVICE_HANDLE hDevice,
    ADI_SENSE_PRODUCT_ID *pProductId)
{
    ADI_ADISENSE_SPI_Product_ID_L_t productIdLoReg;
    ADI_ADISENSE_SPI_Product_ID_H_t productIdHiReg;

    READ_REG_U8(hDevice, productIdLoReg.VALUE8, SPI_PRODUCT_ID_L);
    READ_REG_U8(hDevice, productIdHiReg.VALUE8, SPI_PRODUCT_ID_H);

    *pProductId = (ADI_SENSE_PRODUCT_ID)((productIdHiReg.VALUE8 << 8)
                                         | productIdLoReg.VALUE8);
    return ADI_SENSE_SUCCESS;
}

static ADI_SENSE_RESULT adi_sense_SetPowerMode(
    ADI_SENSE_DEVICE_HANDLE hDevice,
    ADI_SENSE_1000_POWER_MODE powerMode)
{
    ADI_ADISENSE_CORE_Power_Config_t powerConfigReg;

    if (powerMode == ADI_SENSE_1000_POWER_MODE_LOW)
    {
        powerConfigReg.Power_Mode_ADC = ADISENSE_CORE_POWER_CONFIG_ADC_LOW_POWER;
    }
    else if (powerMode == ADI_SENSE_1000_POWER_MODE_MID)
    {
        powerConfigReg.Power_Mode_ADC = ADISENSE_CORE_POWER_CONFIG_ADC_MID_POWER;
    }
    else if (powerMode == ADI_SENSE_1000_POWER_MODE_FULL)
    {
        powerConfigReg.Power_Mode_ADC = ADISENSE_CORE_POWER_CONFIG_ADC_FULL_POWER;
    }
    else
    {
        ADI_SENSE_LOG_ERROR("Invalid power mode %d specified", powerMode);
        return ADI_SENSE_INVALID_PARAM;
    }

    WRITE_REG_U8(hDevice, powerConfigReg.VALUE8, CORE_POWER_CONFIG);

    return ADI_SENSE_SUCCESS;
}

ADI_SENSE_RESULT adi_sense_1000_SetPowerConfig(
    ADI_SENSE_DEVICE_HANDLE hDevice,
    ADI_SENSE_1000_POWER_CONFIG *pPowerConfig)
{
    ADI_SENSE_RESULT eRet;

    eRet = adi_sense_SetPowerMode(hDevice, pPowerConfig->powerMode);
    if (eRet != ADI_SENSE_SUCCESS)
    {
        ADI_SENSE_LOG_ERROR("Failed to set power mode");
        return eRet;
    }

    return ADI_SENSE_SUCCESS;
}

static ADI_SENSE_RESULT adi_sense_SetMode(
    ADI_SENSE_DEVICE_HANDLE hDevice,
    ADI_SENSE_1000_OPERATING_MODE eOperatingMode,
    ADI_SENSE_1000_DATAREADY_MODE eDataReadyMode,
    ADI_SENSE_1000_CALIBRATION_MODE eCalibrationMode,
    bool bEnableExtFlash)
{
    ADI_ADISENSE_CORE_Mode_t modeReg;

    modeReg.VALUE8 = REG_RESET_VAL(CORE_MODE);

    if (eOperatingMode == ADI_SENSE_1000_OPERATING_MODE_SINGLECYCLE)
    {
        modeReg.Conversion_Mode = ADISENSE_CORE_MODE_SINGLECYCLE;
    }
    else if (eOperatingMode == ADI_SENSE_1000_OPERATING_MODE_CONTINUOUS)
    {
        modeReg.Conversion_Mode = ADISENSE_CORE_MODE_CONTINUOUS;
    }
    else if (eOperatingMode == ADI_SENSE_1000_OPERATING_MODE_MULTICYCLE)
    {
        modeReg.Conversion_Mode = ADISENSE_CORE_MODE_MULTICYCLE;
    }
    else
    {
        ADI_SENSE_LOG_ERROR("Invalid operating mode %d specified",
                            eOperatingMode);
        return ADI_SENSE_INVALID_PARAM;
    }

    if (eDataReadyMode == ADI_SENSE_1000_DATAREADY_PER_CONVERSION)
    {
        modeReg.Drdy_Mode = ADISENSE_CORE_MODE_DRDY_PER_CONVERSION;
    }
    else if (eDataReadyMode == ADI_SENSE_1000_DATAREADY_PER_CYCLE)
    {
        modeReg.Drdy_Mode = ADISENSE_CORE_MODE_DRDY_PER_CYCLE;
    }
    else if (eDataReadyMode == ADI_SENSE_1000_DATAREADY_PER_MULTICYCLE_BURST)
    {
        if (eOperatingMode != ADI_SENSE_1000_OPERATING_MODE_MULTICYCLE)
        {
            ADI_SENSE_LOG_ERROR(
                "Data-ready mode %d cannot be used with operating mode %d",
                eDataReadyMode, eOperatingMode);
            return ADI_SENSE_INVALID_PARAM;
        }
        else
        {
            modeReg.Drdy_Mode = ADISENSE_CORE_MODE_DRDY_PER_FIFO_FILL;
        }
    }
    else
    {
        ADI_SENSE_LOG_ERROR("Invalid data-ready mode %d specified", eDataReadyMode);
        return ADI_SENSE_INVALID_PARAM;
    }

    if (eCalibrationMode == ADI_SENSE_1000_NO_CALIBRATION)
    {
        modeReg.Calibration_Method = ADISENSE_CORE_MODE_NO_CAL;
    }
    else if (eCalibrationMode == ADI_SENSE_1000_DO_CALIBRATION)
    {
        modeReg.Calibration_Method = ADISENSE_CORE_MODE_DO_CAL;
    }
    else
    {
        ADI_SENSE_LOG_ERROR("Invalid calibration mode %d specified",
                            eCalibrationMode);
        return ADI_SENSE_INVALID_PARAM;
    }

    modeReg.Ext_Flash_Store = (bEnableExtFlash ?
                                ADISENSE_CORE_MODE_EXT_FLASH_USED :
                                ADISENSE_CORE_MODE_EXT_FLASH_NOT_USED);

    WRITE_REG_U8(hDevice, modeReg.VALUE8, CORE_MODE);

    return ADI_SENSE_SUCCESS;
}

ADI_SENSE_RESULT adi_sense_SetCycleControl(
    ADI_SENSE_DEVICE_HANDLE hDevice,
    uint32_t nCycleInterval,

#ifdef __V2_3_CFG_FMT__    
    ADI_SENSE_1000_CYCLE_TYPE eCycleType,
    ADI_SENSE_1000_FILTER_SETTLING eFilterSettling)
#else
    ADI_SENSE_1000_CYCLE_TYPE eCycleType)
#endif
{
    ADI_ADISENSE_CORE_Cycle_Control_t cycleControlReg;

    cycleControlReg.VALUE16 = REG_RESET_VAL(CORE_CYCLE_CONTROL);

    if (nCycleInterval < (1 << 12))
    {
        cycleControlReg.Cycle_Time_Units = ADISENSE_CORE_CYCLE_CONTROL_MICROSECONDS;
    }
    else if (nCycleInterval < (1000 * (1 << 12)))
    {
        cycleControlReg.Cycle_Time_Units = ADISENSE_CORE_CYCLE_CONTROL_MILLISECONDS;
        nCycleInterval /= 1000;
    }
    else
    {
        cycleControlReg.Cycle_Time_Units = ADISENSE_CORE_CYCLE_CONTROL_SECONDS;
        nCycleInterval /= 1000000;
    }

    CHECK_REG_FIELD_VAL(CORE_CYCLE_CONTROL_CYCLE_TIME, nCycleInterval);
    cycleControlReg.Cycle_Time = nCycleInterval;

    if (eCycleType == ADI_SENSE_1000_CYCLE_TYPE_SWITCH)
    {
        cycleControlReg.Cycle_Type = ADISENSE_CORE_CYCLE_CONTROL_CYCLE_TYPE_SWITCH;
    }
    else if (eCycleType == ADI_SENSE_1000_CYCLE_TYPE_FULL)
    {
        cycleControlReg.Cycle_Type = ADISENSE_CORE_CYCLE_CONTROL_CYCLE_TYPE_FULL;
    }
    else
    {
        ADI_SENSE_LOG_ERROR("Invalid cycle type %d specified", eCycleType);
        return ADI_SENSE_INVALID_PARAM;
    }

#ifdef __V2_3_CFG_FMT__   
    if (eFilterSettling == ADI_SENSE_1000_FILTER_SETTLING_ALWAYS)
    {
        cycleControlReg.Filter_Settling = ADISENSE_CORE_CYCLE_CONTROL_FILTER_SETTLING_SETTLED;
    }
    else if (eFilterSettling == ADI_SENSE_1000_FILTER_SETTLING_FAST)
    {
        cycleControlReg.Filter_Settling = ADISENSE_CORE_CYCLE_CONTROL_FILTER_SETTLING_FAST;
    }
    else
    {
        ADI_SENSE_LOG_ERROR("Invalid filter settling option %d specified", eFilterSettling);
        return ADI_SENSE_INVALID_PARAM;
    }
#endif
    
    WRITE_REG_U16(hDevice, cycleControlReg.VALUE16, CORE_CYCLE_CONTROL);

    return ADI_SENSE_SUCCESS;
}

static ADI_SENSE_RESULT adi_sense_SetMultiCycleConfig(
    ADI_SENSE_DEVICE_HANDLE hDevice,
    ADI_SENSE_1000_MULTICYCLE_CONFIG *pMultiCycleConfig)
{
    CHECK_REG_FIELD_VAL(CORE_FIFO_NUM_CYCLES_FIFO_NUM_CYCLES,
                        pMultiCycleConfig->cyclesPerBurst);

    WRITE_REG_U8(hDevice, pMultiCycleConfig->cyclesPerBurst,
                 CORE_FIFO_NUM_CYCLES);

    WRITE_REG_U32(hDevice, pMultiCycleConfig->burstInterval,
                  CORE_MULTI_CYCLE_REPEAT_INTERVAL);

    return ADI_SENSE_SUCCESS;
}

static ADI_SENSE_RESULT adi_sense_SetExternalReferenceValues(
    ADI_SENSE_DEVICE_HANDLE hDevice,
    float32_t externalRef1Value,
    float32_t externalRef2Value)
{
    WRITE_REG_FLOAT(hDevice, externalRef1Value, CORE_EXTERNAL_REFERENCE1);
    WRITE_REG_FLOAT(hDevice, externalRef2Value, CORE_EXTERNAL_REFERENCE2);

    return ADI_SENSE_SUCCESS;
}

ADI_SENSE_RESULT adi_sense_1000_SetMeasurementConfig(
    ADI_SENSE_DEVICE_HANDLE hDevice,
    ADI_SENSE_1000_MEASUREMENT_CONFIG *pMeasConfig)
{
    ADI_SENSE_RESULT eRet;

    eRet = adi_sense_SetMode(hDevice,
                             pMeasConfig->operatingMode,
                             pMeasConfig->dataReadyMode,
                             pMeasConfig->calibrationMode,
                             pMeasConfig->enableExternalFlash);
    if (eRet != ADI_SENSE_SUCCESS)
    {
        ADI_SENSE_LOG_ERROR("Failed to set operating mode");
        return eRet;
    }

    eRet = adi_sense_SetCycleControl(hDevice,
                                     pMeasConfig->cycleInterval,
                                     pMeasConfig->cycleType);
    if (eRet != ADI_SENSE_SUCCESS)
    {
        ADI_SENSE_LOG_ERROR("Failed to set cycle control");
        return eRet;
    }

    if (pMeasConfig->operatingMode == ADI_SENSE_1000_OPERATING_MODE_MULTICYCLE)
    {
        eRet = adi_sense_SetMultiCycleConfig(hDevice,
                                            &pMeasConfig->multiCycleConfig);
        if (eRet != ADI_SENSE_SUCCESS)
        {
            ADI_SENSE_LOG_ERROR("Failed to set multi-cycle configuration");
            return eRet;
        }
    }

    eRet = adi_sense_SetExternalReferenceValues(hDevice,
                                                pMeasConfig->externalRef1Value,
                                                pMeasConfig->externalRef2Value);
    if (eRet != ADI_SENSE_SUCCESS)
    {
        ADI_SENSE_LOG_ERROR("Failed to set external reference values");
        return eRet;
    }

    return ADI_SENSE_SUCCESS;
}

ADI_SENSE_RESULT adi_sense_1000_SetDiagnosticsConfig(
    ADI_SENSE_DEVICE_HANDLE hDevice,
    ADI_SENSE_1000_DIAGNOSTICS_CONFIG *pDiagnosticsConfig)
{
    ADI_ADISENSE_CORE_Diagnostics_Control_t diagnosticsControlReg;

    diagnosticsControlReg.VALUE16 = REG_RESET_VAL(CORE_DIAGNOSTICS_CONTROL);

    if (pDiagnosticsConfig->disableGlobalDiag)
        diagnosticsControlReg.Diag_Global_En = 0;
    else
        diagnosticsControlReg.Diag_Global_En = 1;

    if (pDiagnosticsConfig->disableMeasurementDiag)
        diagnosticsControlReg.Diag_Meas_En = 0;
    else
        diagnosticsControlReg.Diag_Meas_En = 1;

    switch (pDiagnosticsConfig->osdFrequency)
    {
    case ADI_SENSE_1000_OPEN_SENSOR_DIAGNOSTICS_DISABLED:
        diagnosticsControlReg.Diag_OSD_Freq = ADISENSE_CORE_DIAGNOSTICS_CONTROL_OCD_OFF;
        break;
    case ADI_SENSE_1000_OPEN_SENSOR_DIAGNOSTICS_PER_CYCLE:
        diagnosticsControlReg.Diag_OSD_Freq = ADISENSE_CORE_DIAGNOSTICS_CONTROL_OCD_PER_1_CYCLE;
        break;
    case ADI_SENSE_1000_OPEN_SENSOR_DIAGNOSTICS_PER_100_CYCLES:
        diagnosticsControlReg.Diag_OSD_Freq = ADISENSE_CORE_DIAGNOSTICS_CONTROL_OCD_PER_100_CYCLES;
        break;
    case ADI_SENSE_1000_OPEN_SENSOR_DIAGNOSTICS_PER_1000_CYCLES:
        diagnosticsControlReg.Diag_OSD_Freq = ADISENSE_CORE_DIAGNOSTICS_CONTROL_OCD_PER_1000_CYCLES;
        break;
    default:
        ADI_SENSE_LOG_ERROR("Invalid open-sensor diagnostic frequency %d specified",
                            pDiagnosticsConfig->osdFrequency);
        return ADI_SENSE_INVALID_PARAM;
    }

    WRITE_REG_U16(hDevice, diagnosticsControlReg.VALUE16, CORE_DIAGNOSTICS_CONTROL);

    return ADI_SENSE_SUCCESS;
}

ADI_SENSE_RESULT adi_sense_1000_SetFftConfig(
    ADI_SENSE_DEVICE_HANDLE hDevice,
    ADI_SENSE_1000_FFT_CONFIG *pFftConfig,
    ADI_SENSE_1000_CHANNEL_CONFIG *pChannels)
{
    ADI_ADISENSE_CORE_FFT_Config_t fftConfigReg;
    ADI_ADISENSE_CORE_Mode_t modeReg;
    uint32_t numFftChannels = 0;

    fftConfigReg.VALUE32 = REG_RESET_VAL(CORE_FFT_CONFIG);

    for (ADI_SENSE_1000_CHANNEL_ID id = ADI_SENSE_1000_CHANNEL_ID_CJC_0;
         id < ADI_SENSE_1000_MAX_CHANNELS;
         id++)
    {
        if (pChannels[id].enableFFT)
        {
            if (numFftChannels >= 4) /* TODO - temporary limit */
            {
                ADI_SENSE_LOG_ERROR("Maximum limit of 4 FFT channels exceeded");
                return ADI_SENSE_INVALID_PARAM;
            }

            numFftChannels++;
        }
    }

    if (numFftChannels > 0)
    {
        fftConfigReg.FFT_Num_Channels = numFftChannels - 1;

        switch (pFftConfig->size)
        {
        case ADI_SENSE_1000_FFT_SIZE_256:
            fftConfigReg.FFT_Num_Bins = ADISENSE_CORE_FFT_CONFIG_FFT_BINS_256;
            break;
        case ADI_SENSE_1000_FFT_SIZE_512:
            fftConfigReg.FFT_Num_Bins = ADISENSE_CORE_FFT_CONFIG_FFT_BINS_512;
            break;
        case ADI_SENSE_1000_FFT_SIZE_1024:
            fftConfigReg.FFT_Num_Bins = ADISENSE_CORE_FFT_CONFIG_FFT_BINS_1024;
            break;
        case ADI_SENSE_1000_FFT_SIZE_2048:
            fftConfigReg.FFT_Num_Bins = ADISENSE_CORE_FFT_CONFIG_FFT_BINS_2048;
            break;
        default:
            ADI_SENSE_LOG_ERROR("Invalid FFT size option %d specified",
                                pFftConfig->size);
            return ADI_SENSE_INVALID_PARAM;
        }

        switch (pFftConfig->window)
        {
        case ADI_SENSE_1000_FFT_WINDOW_NONE:
            fftConfigReg.FFT_Window = ADISENSE_CORE_FFT_CONFIG_FFT_WINDOW_NONE;
            break;
        case ADI_SENSE_1000_FFT_WINDOW_HANN:
            fftConfigReg.FFT_Window = ADISENSE_CORE_FFT_CONFIG_FFT_WINDOW_HANN;
            break;
        case ADI_SENSE_1000_FFT_WINDOW_BLACKMAN_HARRIS:
            fftConfigReg.FFT_Window = ADISENSE_CORE_FFT_CONFIG_FFT_WINDOW_BLACKMANN_HARRIS;
            break;
        default:
            ADI_SENSE_LOG_ERROR("Invalid FFT window option %d specified",
                                pFftConfig->window);
            return ADI_SENSE_INVALID_PARAM;
        }

        switch (pFftConfig->output)
        {
        case ADI_SENSE_1000_FFT_OUTPUT_FULL:
            fftConfigReg.FFT_Output = ADISENSE_CORE_FFT_CONFIG_FFT_OUTPUT_FULL;
            break;
        case ADI_SENSE_1000_FFT_OUTPUT_MAX16:
            fftConfigReg.FFT_Output = ADISENSE_CORE_FFT_CONFIG_FFT_OUTPUT_MAX16;
            break;
        case ADI_SENSE_1000_FFT_OUTPUT_FULL_WITH_RAW:
            fftConfigReg.FFT_Output = ADISENSE_CORE_FFT_CONFIG_FFT_OUTPUT_FULL_WITH_RAW;
            break;
        default:
            ADI_SENSE_LOG_ERROR("Invalid FFT output format option %d specified",
                                pFftConfig->output);
            return ADI_SENSE_INVALID_PARAM;
        }
    }
    WRITE_REG_U32(hDevice, fftConfigReg.VALUE32, CORE_FFT_CONFIG);

    if (numFftChannels > 0)
    {
        READ_REG_U8(hDevice, modeReg.VALUE8, CORE_MODE);

        if (pFftConfig->mode == ADI_SENSE_1000_FFT_MODE_SINGLE)
        {
            modeReg.FFT_Mode = ADISENSE_CORE_MODE_FFT_MODE_SINGLE;
        }
        else if (pFftConfig->mode == ADI_SENSE_1000_FFT_MODE_CONTINUOUS)
        {
            modeReg.FFT_Mode = ADISENSE_CORE_MODE_FFT_MODE_CONTINUOUS;
        }
        else
        {
            ADI_SENSE_LOG_ERROR("Invalid FFT mode %d specified",
                                pFftConfig->mode);
            return ADI_SENSE_INVALID_PARAM;
        }

        WRITE_REG_U8(hDevice, modeReg.VALUE8, CORE_MODE);
    }

    return ADI_SENSE_SUCCESS;
}

ADI_SENSE_RESULT adi_sense_1000_SetChannelCount(
    ADI_SENSE_DEVICE_HANDLE hDevice,
    ADI_SENSE_1000_CHANNEL_ID eChannelId,
    uint32_t nMeasurementsPerCycle)
{
    ADI_ADISENSE_CORE_Channel_Count_t channelCountReg;

    channelCountReg.VALUE8 = REG_RESET_VAL(CORE_CHANNEL_COUNTn);

    if (nMeasurementsPerCycle > 0)
    {
        nMeasurementsPerCycle -= 1;

        CHECK_REG_FIELD_VAL(CORE_CHANNEL_COUNT_CHANNEL_COUNT,
                            nMeasurementsPerCycle);

        channelCountReg.Channel_Enable = 1;
        channelCountReg.Channel_Count = nMeasurementsPerCycle;
    }
    else
    {
        channelCountReg.Channel_Enable = 0;
    }

    WRITE_REG_U8(hDevice, channelCountReg.VALUE8, CORE_CHANNEL_COUNTn(eChannelId));

    return ADI_SENSE_SUCCESS;
}

ADI_SENSE_RESULT adi_sense_1000_SetChannelOptions(
    ADI_SENSE_DEVICE_HANDLE hDevice,
    ADI_SENSE_1000_CHANNEL_ID eChannelId,
    ADI_SENSE_1000_CHANNEL_PRIORITY ePriority,
    bool bEnableFft)
{
    ADI_ADISENSE_CORE_Channel_Options_t channelOptionsReg;

    channelOptionsReg.VALUE8 = REG_RESET_VAL(CORE_CHANNEL_OPTIONSn);

    CHECK_REG_FIELD_VAL(CORE_CHANNEL_OPTIONS_CHANNEL_PRIORITY, ePriority);
    channelOptionsReg.Channel_Priority = ePriority;
    channelOptionsReg.FFT_Enable_Ch = bEnableFft ? 1 : 0;

    WRITE_REG_U8(hDevice, channelOptionsReg.VALUE8, CORE_CHANNEL_OPTIONSn(eChannelId));

    return ADI_SENSE_SUCCESS;
}

ADI_SENSE_RESULT adi_sense_1000_SetChannelSkipCount(
    ADI_SENSE_DEVICE_HANDLE hDevice,
    ADI_SENSE_1000_CHANNEL_ID eChannelId,
    uint32_t nCycleSkipCount)
{
    ADI_ADISENSE_CORE_Channel_Skip_t channelSkipReg;

    channelSkipReg.VALUE16 = REG_RESET_VAL(CORE_CHANNEL_SKIPn);

    CHECK_REG_FIELD_VAL(CORE_CHANNEL_SKIP_CHANNEL_SKIP, nCycleSkipCount);

    channelSkipReg.Channel_Skip = nCycleSkipCount;

    WRITE_REG_U16(hDevice, channelSkipReg.VALUE16, CORE_CHANNEL_SKIPn(eChannelId));

    return ADI_SENSE_SUCCESS;
}

static ADI_SENSE_RESULT adi_sense_SetChannelAdcSensorType(
    ADI_SENSE_DEVICE_HANDLE hDevice,
    ADI_SENSE_1000_CHANNEL_ID eChannelId,
    ADI_SENSE_1000_ADC_SENSOR_TYPE sensorType)
{
    ADI_ADISENSE_CORE_Sensor_Type_t sensorTypeReg;

    sensorTypeReg.VALUE16 = REG_RESET_VAL(CORE_SENSOR_TYPEn);

    /* Ensure that the sensor type is valid for this channel */
    switch(sensorType)
    {
    case ADI_SENSE_1000_ADC_SENSOR_THERMOCOUPLE_J_DEF_L1:
    case ADI_SENSE_1000_ADC_SENSOR_THERMOCOUPLE_K_DEF_L1:
    case ADI_SENSE_1000_ADC_SENSOR_THERMOCOUPLE_T_DEF_L1:
    case ADI_SENSE_1000_ADC_SENSOR_THERMOCOUPLE_1_DEF_L2:
    case ADI_SENSE_1000_ADC_SENSOR_THERMOCOUPLE_2_DEF_L2:
    case ADI_SENSE_1000_ADC_SENSOR_THERMOCOUPLE_3_DEF_L2:
    case ADI_SENSE_1000_ADC_SENSOR_THERMOCOUPLE_4_DEF_L2:
    case ADI_SENSE_1000_ADC_SENSOR_THERMOCOUPLE_J_ADV_L1:
    case ADI_SENSE_1000_ADC_SENSOR_THERMOCOUPLE_K_ADV_L1:
    case ADI_SENSE_1000_ADC_SENSOR_THERMOCOUPLE_T_ADV_L1:
    case ADI_SENSE_1000_ADC_SENSOR_THERMOCOUPLE_1_ADV_L2:
    case ADI_SENSE_1000_ADC_SENSOR_THERMOCOUPLE_2_ADV_L2:
    case ADI_SENSE_1000_ADC_SENSOR_THERMOCOUPLE_3_ADV_L2:
    case ADI_SENSE_1000_ADC_SENSOR_THERMOCOUPLE_4_ADV_L2:
    case ADI_SENSE_1000_ADC_SENSOR_BRIDGE_4WIRE_1_DEF_L2:
    case ADI_SENSE_1000_ADC_SENSOR_BRIDGE_4WIRE_2_DEF_L2:
    case ADI_SENSE_1000_ADC_SENSOR_BRIDGE_4WIRE_3_DEF_L2:
    case ADI_SENSE_1000_ADC_SENSOR_BRIDGE_4WIRE_4_DEF_L2:
    case ADI_SENSE_1000_ADC_SENSOR_BRIDGE_4WIRE_1_ADV_L2:
    case ADI_SENSE_1000_ADC_SENSOR_BRIDGE_4WIRE_2_ADV_L2:
    case ADI_SENSE_1000_ADC_SENSOR_BRIDGE_4WIRE_3_ADV_L2:
    case ADI_SENSE_1000_ADC_SENSOR_BRIDGE_4WIRE_4_ADV_L2:
    case ADI_SENSE_1000_ADC_SENSOR_BRIDGE_6WIRE_1_DEF_L2:
    case ADI_SENSE_1000_ADC_SENSOR_BRIDGE_6WIRE_2_DEF_L2:
    case ADI_SENSE_1000_ADC_SENSOR_BRIDGE_6WIRE_3_DEF_L2:
    case ADI_SENSE_1000_ADC_SENSOR_BRIDGE_6WIRE_4_DEF_L2:
    case ADI_SENSE_1000_ADC_SENSOR_BRIDGE_6WIRE_1_ADV_L2:
    case ADI_SENSE_1000_ADC_SENSOR_BRIDGE_6WIRE_2_ADV_L2:
    case ADI_SENSE_1000_ADC_SENSOR_BRIDGE_6WIRE_3_ADV_L2:
    case ADI_SENSE_1000_ADC_SENSOR_BRIDGE_6WIRE_4_ADV_L2:
        if (! ADI_SENSE_1000_CHANNEL_IS_ADC_SENSOR(eChannelId))
        {
            ADI_SENSE_LOG_ERROR(
                "Invalid ADC sensor type %d specified for channel %d",
                sensorType, eChannelId);
            return ADI_SENSE_INVALID_PARAM;
        }
        break;
    case ADI_SENSE_1000_ADC_SENSOR_RTD_2WIRE_PT100_DEF_L1:
    case ADI_SENSE_1000_ADC_SENSOR_RTD_2WIRE_PT1000_DEF_L1:
    case ADI_SENSE_1000_ADC_SENSOR_RTD_2WIRE_1_DEF_L2:
    case ADI_SENSE_1000_ADC_SENSOR_RTD_2WIRE_2_DEF_L2:
    case ADI_SENSE_1000_ADC_SENSOR_RTD_2WIRE_3_DEF_L2:
    case ADI_SENSE_1000_ADC_SENSOR_RTD_2WIRE_4_DEF_L2:
    case ADI_SENSE_1000_ADC_SENSOR_RTD_2WIRE_PT100_ADV_L1:
    case ADI_SENSE_1000_ADC_SENSOR_RTD_2WIRE_PT1000_ADV_L1:
    case ADI_SENSE_1000_ADC_SENSOR_RTD_2WIRE_1_ADV_L2:
    case ADI_SENSE_1000_ADC_SENSOR_RTD_2WIRE_2_ADV_L2:
    case ADI_SENSE_1000_ADC_SENSOR_RTD_2WIRE_3_ADV_L2:
    case ADI_SENSE_1000_ADC_SENSOR_RTD_2WIRE_4_ADV_L2:
    case ADI_SENSE_1000_ADC_SENSOR_RTD_3WIRE_PT100_DEF_L1:
    case ADI_SENSE_1000_ADC_SENSOR_RTD_3WIRE_PT1000_DEF_L1:
    case ADI_SENSE_1000_ADC_SENSOR_RTD_3WIRE_1_DEF_L2:
    case ADI_SENSE_1000_ADC_SENSOR_RTD_3WIRE_2_DEF_L2:
    case ADI_SENSE_1000_ADC_SENSOR_RTD_3WIRE_3_DEF_L2:
    case ADI_SENSE_1000_ADC_SENSOR_RTD_3WIRE_4_DEF_L2:
    case ADI_SENSE_1000_ADC_SENSOR_RTD_3WIRE_PT100_ADV_L1:
    case ADI_SENSE_1000_ADC_SENSOR_RTD_3WIRE_PT1000_ADV_L1:
    case ADI_SENSE_1000_ADC_SENSOR_RTD_3WIRE_1_ADV_L2:
    case ADI_SENSE_1000_ADC_SENSOR_RTD_3WIRE_2_ADV_L2:
    case ADI_SENSE_1000_ADC_SENSOR_RTD_3WIRE_3_ADV_L2:
    case ADI_SENSE_1000_ADC_SENSOR_RTD_3WIRE_4_ADV_L2:
    case ADI_SENSE_1000_ADC_SENSOR_RTD_4WIRE_PT100_DEF_L1:
    case ADI_SENSE_1000_ADC_SENSOR_RTD_4WIRE_PT1000_DEF_L1:
    case ADI_SENSE_1000_ADC_SENSOR_RTD_4WIRE_1_DEF_L2:
    case ADI_SENSE_1000_ADC_SENSOR_RTD_4WIRE_2_DEF_L2:
    case ADI_SENSE_1000_ADC_SENSOR_RTD_4WIRE_3_DEF_L2:
    case ADI_SENSE_1000_ADC_SENSOR_RTD_4WIRE_4_DEF_L2:
    case ADI_SENSE_1000_ADC_SENSOR_RTD_4WIRE_PT100_ADV_L1:
    case ADI_SENSE_1000_ADC_SENSOR_RTD_4WIRE_PT1000_ADV_L1:
    case ADI_SENSE_1000_ADC_SENSOR_RTD_4WIRE_1_ADV_L2:
    case ADI_SENSE_1000_ADC_SENSOR_RTD_4WIRE_2_ADV_L2:
    case ADI_SENSE_1000_ADC_SENSOR_RTD_4WIRE_3_ADV_L2:
    case ADI_SENSE_1000_ADC_SENSOR_RTD_4WIRE_4_ADV_L2:
            if (!ADI_SENSE_1000_CHANNEL_IS_ADC_CJC(eChannelId))
        {
            ADI_SENSE_LOG_ERROR(
                "Invalid ADC sensor type %d specified for channel %d",
                sensorType, eChannelId);
            return ADI_SENSE_INVALID_PARAM;
        }
        break;
    case ADI_SENSE_1000_ADC_SENSOR_DIODE_2C_TYPEA_DEF_L1:
    case ADI_SENSE_1000_ADC_SENSOR_DIODE_3C_TYPEA_DEF_L1:
    case ADI_SENSE_1000_ADC_SENSOR_DIODE_2C_1_DEF_L2:
    case ADI_SENSE_1000_ADC_SENSOR_DIODE_3C_1_DEF_L2:
    case ADI_SENSE_1000_ADC_SENSOR_DIODE_2C_TYPEA_ADV_L1:
    case ADI_SENSE_1000_ADC_SENSOR_DIODE_3C_TYPEA_ADV_L1:
    case ADI_SENSE_1000_ADC_SENSOR_DIODE_2C_1_ADV_L2:
    case ADI_SENSE_1000_ADC_SENSOR_DIODE_3C_1_ADV_L2:
    case ADI_SENSE_1000_ADC_SENSOR_THERMISTOR_A_10K_DEF_L1:
    case ADI_SENSE_1000_ADC_SENSOR_THERMISTOR_B_10K_DEF_L1:
    case ADI_SENSE_1000_ADC_SENSOR_THERMISTOR_1_DEF_L2:
    case ADI_SENSE_1000_ADC_SENSOR_THERMISTOR_2_DEF_L2:
    case ADI_SENSE_1000_ADC_SENSOR_THERMISTOR_3_DEF_L2:
    case ADI_SENSE_1000_ADC_SENSOR_THERMISTOR_4_DEF_L2:
    case ADI_SENSE_1000_ADC_SENSOR_THERMISTOR_A_10K_ADV_L1:
    case ADI_SENSE_1000_ADC_SENSOR_THERMISTOR_B_10K_ADV_L1:
    case ADI_SENSE_1000_ADC_SENSOR_THERMISTOR_1_ADV_L2:
    case ADI_SENSE_1000_ADC_SENSOR_THERMISTOR_2_ADV_L2:
    case ADI_SENSE_1000_ADC_SENSOR_THERMISTOR_3_ADV_L2:
    case ADI_SENSE_1000_ADC_SENSOR_THERMISTOR_4_ADV_L2:
        if (! (ADI_SENSE_1000_CHANNEL_IS_ADC_SENSOR(eChannelId) ||
               ADI_SENSE_1000_CHANNEL_IS_ADC_CJC(eChannelId)))
        {
            ADI_SENSE_LOG_ERROR(
                "Invalid ADC sensor type %d specified for channel %d",
                sensorType, eChannelId);
            return ADI_SENSE_INVALID_PARAM;
        }
        break;
    case ADI_SENSE_1000_ADC_SENSOR_VOLTAGE:
    case ADI_SENSE_1000_ADC_SENSOR_VOLTAGE_PRESSURE_A_DEF_L1:
    case ADI_SENSE_1000_ADC_SENSOR_VOLTAGE_PRESSURE_B_DEF_L1:
    case ADI_SENSE_1000_ADC_SENSOR_VOLTAGE_PRESSURE_1_DEF_L2:
    case ADI_SENSE_1000_ADC_SENSOR_VOLTAGE_PRESSURE_2_DEF_L2:
    case ADI_SENSE_1000_ADC_SENSOR_VOLTAGE_PRESSURE_A_ADV_L1:
    case ADI_SENSE_1000_ADC_SENSOR_VOLTAGE_PRESSURE_B_ADV_L1:
    case ADI_SENSE_1000_ADC_SENSOR_VOLTAGE_PRESSURE_1_ADV_L2:
    case ADI_SENSE_1000_ADC_SENSOR_VOLTAGE_PRESSURE_2_ADV_L2:
        if (! ADI_SENSE_1000_CHANNEL_IS_ADC_VOLTAGE(eChannelId))
        {
            ADI_SENSE_LOG_ERROR(
                "Invalid ADC sensor type %d specified for channel %d",
                sensorType, eChannelId);
            return ADI_SENSE_INVALID_PARAM;
        }
        break;
    case ADI_SENSE_1000_ADC_SENSOR_CURRENT:
    case ADI_SENSE_1000_ADC_SENSOR_CURRENT_PRESSURE_A_DEF_L1:
    case ADI_SENSE_1000_ADC_SENSOR_CURRENT_PRESSURE_1_DEF_L2:
    case ADI_SENSE_1000_ADC_SENSOR_CURRENT_PRESSURE_2_DEF_L2:
    case ADI_SENSE_1000_ADC_SENSOR_CURRENT_PRESSURE_A_ADV_L1:
    case ADI_SENSE_1000_ADC_SENSOR_CURRENT_PRESSURE_1_ADV_L2:
    case ADI_SENSE_1000_ADC_SENSOR_CURRENT_PRESSURE_2_ADV_L2:
        if (! ADI_SENSE_1000_CHANNEL_IS_ADC_CURRENT(eChannelId))
        {
            ADI_SENSE_LOG_ERROR(
                "Invalid ADC sensor type %d specified for channel %d",
                sensorType, eChannelId);
            return ADI_SENSE_INVALID_PARAM;
        }
        break;
    default:
        ADI_SENSE_LOG_ERROR("Invalid/unsupported ADC sensor type %d specified",
                            sensorType);
        return ADI_SENSE_INVALID_PARAM;
    }

    sensorTypeReg.Sensor_Type = sensorType;

    WRITE_REG_U16(hDevice, sensorTypeReg.VALUE16, CORE_SENSOR_TYPEn(eChannelId));

    return ADI_SENSE_SUCCESS;
}

static ADI_SENSE_RESULT adi_sense_SetChannelAdcSensorDetails(
    ADI_SENSE_DEVICE_HANDLE hDevice,
    ADI_SENSE_1000_CHANNEL_ID eChannelId,
    ADI_SENSE_1000_CHANNEL_CONFIG *pChannelConfig)
/*
 * TODO - it would be nice if the general- vs. ADC-specific sensor details could be split into separate registers
 * General details:
 * - Measurement_Units
 * - Compensation_Channel
 * - CJC_Publish (if "CJC" was removed from the name)
 * ADC-specific details:
 * - PGA_Gain
 * - Reference_Select
 * - Reference_Buffer_Disable
 * - Vbias
 */
{
    ADI_SENSE_1000_ADC_CHANNEL_CONFIG *pAdcChannelConfig = &pChannelConfig->adcChannelConfig;
    ADI_SENSE_1000_ADC_REFERENCE_CONFIG *pRefConfig = &pAdcChannelConfig->reference;
    ADI_ADISENSE_CORE_Sensor_Details_t sensorDetailsReg;

    sensorDetailsReg.VALUE32 = REG_RESET_VAL(CORE_SENSOR_DETAILSn);

    switch(pChannelConfig->measurementUnit)
    {
    case ADI_SENSE_1000_MEASUREMENT_UNIT_FAHRENHEIT:
        sensorDetailsReg.Measurement_Units = ADISENSE_CORE_SENSOR_DETAILS_UNITS_DEGF;
        break;
    case ADI_SENSE_1000_MEASUREMENT_UNIT_CELSIUS:
        sensorDetailsReg.Measurement_Units = ADISENSE_CORE_SENSOR_DETAILS_UNITS_DEGC;
        break;
    case ADI_SENSE_1000_MEASUREMENT_UNIT_UNSPECIFIED:
        sensorDetailsReg.Measurement_Units = ADISENSE_CORE_SENSOR_DETAILS_UNITS_UNSPECIFIED;
        break;
    default:
        ADI_SENSE_LOG_ERROR("Invalid measurement unit %d specified",
                            pChannelConfig->measurementUnit);
        return ADI_SENSE_INVALID_PARAM;
    }

    if (pChannelConfig->compensationChannel == ADI_SENSE_1000_CHANNEL_ID_NONE)
    {
        sensorDetailsReg.Compensation_Disable = 1;
        sensorDetailsReg.Compensation_Channel = 0;
    }
    else
    {
        sensorDetailsReg.Compensation_Disable = 0;
        sensorDetailsReg.Compensation_Channel = pChannelConfig->compensationChannel;
    }

    switch(pRefConfig->type)
    {
    case ADI_SENSE_1000_ADC_REFERENCE_RESISTOR_INTERNAL_1:
        sensorDetailsReg.Reference_Select = ADISENSE_CORE_SENSOR_DETAILS_REF_RINT1;
        break;
    case ADI_SENSE_1000_ADC_REFERENCE_RESISTOR_INTERNAL_2:
        sensorDetailsReg.Reference_Select = ADISENSE_CORE_SENSOR_DETAILS_REF_RINT2;
        break;
    case ADI_SENSE_1000_ADC_REFERENCE_VOLTAGE_INTERNAL:
        sensorDetailsReg.Reference_Select = ADISENSE_CORE_SENSOR_DETAILS_REF_INT;
        break;
    case ADI_SENSE_1000_ADC_REFERENCE_VOLTAGE_AVDD:
        sensorDetailsReg.Reference_Select = ADISENSE_CORE_SENSOR_DETAILS_REF_AVDD;
        break;
    case ADI_SENSE_1000_ADC_REFERENCE_RESISTOR_EXTERNAL_1:
        sensorDetailsReg.Reference_Select = ADISENSE_CORE_SENSOR_DETAILS_REF_REXT1;
        break;
    case ADI_SENSE_1000_ADC_REFERENCE_RESISTOR_EXTERNAL_2:
        sensorDetailsReg.Reference_Select = ADISENSE_CORE_SENSOR_DETAILS_REF_REXT2;
        break;
    case ADI_SENSE_1000_ADC_REFERENCE_VOLTAGE_EXTERNAL_1:
        sensorDetailsReg.Reference_Select = ADISENSE_CORE_SENSOR_DETAILS_REF_VEXT1;
        break;
    case ADI_SENSE_1000_ADC_REFERENCE_VOLTAGE_EXTERNAL_2:
        sensorDetailsReg.Reference_Select = ADISENSE_CORE_SENSOR_DETAILS_REF_VEXT2;
        break;
    case ADI_SENSE_1000_ADC_REFERENCE_BRIDGE_EXCITATION:
        sensorDetailsReg.Reference_Select = ADISENSE_CORE_SENSOR_DETAILS_REF_EXC;
        break;
    default:
        ADI_SENSE_LOG_ERROR("Invalid ADC reference type %d specified",
                            pRefConfig->type);
        return ADI_SENSE_INVALID_PARAM;
    }

    switch(pAdcChannelConfig->gain)
    {
    case ADI_SENSE_1000_ADC_GAIN_1X:
        sensorDetailsReg.PGA_Gain = ADISENSE_CORE_SENSOR_DETAILS_PGA_GAIN_1;
        break;
    case ADI_SENSE_1000_ADC_GAIN_2X:
        sensorDetailsReg.PGA_Gain = ADISENSE_CORE_SENSOR_DETAILS_PGA_GAIN_2;
        break;
    case ADI_SENSE_1000_ADC_GAIN_4X:
        sensorDetailsReg.PGA_Gain = ADISENSE_CORE_SENSOR_DETAILS_PGA_GAIN_4;
        break;
    case ADI_SENSE_1000_ADC_GAIN_8X:
        sensorDetailsReg.PGA_Gain = ADISENSE_CORE_SENSOR_DETAILS_PGA_GAIN_8;
        break;
    case ADI_SENSE_1000_ADC_GAIN_16X:
        sensorDetailsReg.PGA_Gain = ADISENSE_CORE_SENSOR_DETAILS_PGA_GAIN_16;
        break;
    case ADI_SENSE_1000_ADC_GAIN_32X:
        sensorDetailsReg.PGA_Gain = ADISENSE_CORE_SENSOR_DETAILS_PGA_GAIN_32;
        break;
    case ADI_SENSE_1000_ADC_GAIN_64X:
        sensorDetailsReg.PGA_Gain = ADISENSE_CORE_SENSOR_DETAILS_PGA_GAIN_64;
        break;
    case ADI_SENSE_1000_ADC_GAIN_128X:
        sensorDetailsReg.PGA_Gain = ADISENSE_CORE_SENSOR_DETAILS_PGA_GAIN_128;
        break;
    default:
        ADI_SENSE_LOG_ERROR("Invalid ADC gain %d specified",
                            pAdcChannelConfig->gain);
        return ADI_SENSE_INVALID_PARAM;
    }

    if (pAdcChannelConfig->enableVbias)
        sensorDetailsReg.Vbias = 1;
    else
        sensorDetailsReg.Vbias = 0;

    if (pAdcChannelConfig->reference.disableBuffer)
        sensorDetailsReg.Reference_Buffer_Disable = 1;
    else
        sensorDetailsReg.Reference_Buffer_Disable = 0;

    if (pChannelConfig->disablePublishing)
        sensorDetailsReg.Do_Not_Publish = 1;
    else
        sensorDetailsReg.Do_Not_Publish = 0;

    if (pChannelConfig->enableUnityLut)
        sensorDetailsReg.Unity_LUT_Select = 1;
    else
        sensorDetailsReg.Unity_LUT_Select = 0;

    WRITE_REG_U32(hDevice, sensorDetailsReg.VALUE32, CORE_SENSOR_DETAILSn(eChannelId));

    return ADI_SENSE_SUCCESS;
}

static ADI_SENSE_RESULT adi_sense_SetChannelAdcFilter(
    ADI_SENSE_DEVICE_HANDLE hDevice,
    ADI_SENSE_1000_CHANNEL_ID eChannelId,
    ADI_SENSE_1000_ADC_FILTER_CONFIG *pFilterConfig)
{
    ADI_ADISENSE_CORE_Filter_Select_t filterSelectReg;

    filterSelectReg.VALUE32 = REG_RESET_VAL(CORE_FILTER_SELECTn);

    if (pFilterConfig->type == ADI_SENSE_1000_ADC_FILTER_SINC4)
    {
        filterSelectReg.ADC_Filter_Type = ADISENSE_CORE_FILTER_SELECT_FILTER_SINC4;
        filterSelectReg.ADC_FS = pFilterConfig->fs;
    }
    else if (pFilterConfig->type == ADI_SENSE_1000_ADC_FILTER_FIR_20SPS)
    {
        filterSelectReg.ADC_Filter_Type = ADISENSE_CORE_FILTER_SELECT_FILTER_FIR_20SPS;
    }
    else if (pFilterConfig->type == ADI_SENSE_1000_ADC_FILTER_FIR_25SPS)
    {
        filterSelectReg.ADC_Filter_Type = ADISENSE_CORE_FILTER_SELECT_FILTER_FIR_25SPS;
    }
    else
    {
        ADI_SENSE_LOG_ERROR("Invalid ADC filter type %d specified",
                            pFilterConfig->type);
        return ADI_SENSE_INVALID_PARAM;
    }

    WRITE_REG_U32(hDevice, filterSelectReg.VALUE32, CORE_FILTER_SELECTn(eChannelId));

    return ADI_SENSE_SUCCESS;
}

static ADI_SENSE_RESULT adi_sense_SetChannelAdcCurrentConfig(
    ADI_SENSE_DEVICE_HANDLE hDevice,
    ADI_SENSE_1000_CHANNEL_ID eChannelId,
    ADI_SENSE_1000_ADC_EXC_CURRENT_CONFIG *pCurrentConfig)
{
    ADI_ADISENSE_CORE_Channel_Excitation_t channelExcitationReg;

    channelExcitationReg.VALUE8 = REG_RESET_VAL(CORE_CHANNEL_EXCITATIONn);

    if (pCurrentConfig->outputLevel == ADI_SENSE_1000_ADC_EXC_CURRENT_NONE)
    {
        channelExcitationReg.IOUT_Excitation_Current = ADISENSE_CORE_CHANNEL_EXCITATION_IEXC_OFF;
    }
    else
    {
        if (pCurrentConfig->outputLevel == ADI_SENSE_1000_ADC_EXC_CURRENT_50uA)
            channelExcitationReg.IOUT_Excitation_Current = ADISENSE_CORE_CHANNEL_EXCITATION_IEXC_50UA;
        else if (pCurrentConfig->outputLevel == ADI_SENSE_1000_ADC_EXC_CURRENT_100uA)
            channelExcitationReg.IOUT_Excitation_Current = ADISENSE_CORE_CHANNEL_EXCITATION_IEXC_100UA;
        else if (pCurrentConfig->outputLevel == ADI_SENSE_1000_ADC_EXC_CURRENT_250uA)
            channelExcitationReg.IOUT_Excitation_Current = ADISENSE_CORE_CHANNEL_EXCITATION_IEXC_250UA;
        else if (pCurrentConfig->outputLevel == ADI_SENSE_1000_ADC_EXC_CURRENT_500uA)
            channelExcitationReg.IOUT_Excitation_Current = ADISENSE_CORE_CHANNEL_EXCITATION_IEXC_500UA;
        else if (pCurrentConfig->outputLevel == ADI_SENSE_1000_ADC_EXC_CURRENT_750uA)
            channelExcitationReg.IOUT_Excitation_Current = ADISENSE_CORE_CHANNEL_EXCITATION_IEXC_750UA;
        else if (pCurrentConfig->outputLevel == ADI_SENSE_1000_ADC_EXC_CURRENT_1000uA)
            channelExcitationReg.IOUT_Excitation_Current = ADISENSE_CORE_CHANNEL_EXCITATION_IEXC_1000UA;
        else
        {
            ADI_SENSE_LOG_ERROR("Invalid ADC excitation current %d specified",
                                pCurrentConfig->outputLevel);
            return ADI_SENSE_INVALID_PARAM;
        }
    }

    if (pCurrentConfig->diodeRatio == ADI_SENSE_1000_ADC_EXC_CURRENT_IOUT_DIODE_DEFAULT)
    {
        channelExcitationReg.IOUT_Diode_Ratio = 0;
    }
    else
    {
        channelExcitationReg.IOUT_Diode_Ratio = 1;
    }

    WRITE_REG_U8(hDevice, channelExcitationReg.VALUE8, CORE_CHANNEL_EXCITATIONn(eChannelId));

    return ADI_SENSE_SUCCESS;
}

ADI_SENSE_RESULT adi_sense_SetAdcChannelConfig(
    ADI_SENSE_DEVICE_HANDLE hDevice,
    ADI_SENSE_1000_CHANNEL_ID eChannelId,
    ADI_SENSE_1000_CHANNEL_CONFIG *pChannelConfig)
{
    ADI_SENSE_RESULT eRet;
    ADI_SENSE_1000_ADC_CHANNEL_CONFIG *pAdcChannelConfig =
        &pChannelConfig->adcChannelConfig;

    eRet = adi_sense_SetChannelAdcSensorType(hDevice, eChannelId,
                                             pAdcChannelConfig->sensor);
    if (eRet != ADI_SENSE_SUCCESS)
    {
        ADI_SENSE_LOG_ERROR("Failed to set ADC sensor type for channel %d",
                            eChannelId);
        return eRet;
    }

    eRet = adi_sense_SetChannelAdcSensorDetails(hDevice, eChannelId,
                                                pChannelConfig);
    if (eRet != ADI_SENSE_SUCCESS)
    {
        ADI_SENSE_LOG_ERROR("Failed to set ADC sensor details for channel %d",
                            eChannelId);
        return eRet;
    }

    eRet = adi_sense_SetChannelAdcFilter(hDevice, eChannelId,
                                         &pAdcChannelConfig->filter);
    if (eRet != ADI_SENSE_SUCCESS)
    {
        ADI_SENSE_LOG_ERROR("Failed to set ADC filter for channel %d",
                            eChannelId);
        return eRet;
    }

    eRet = adi_sense_SetChannelAdcCurrentConfig(hDevice, eChannelId,
                                                &pAdcChannelConfig->current);
    if (eRet != ADI_SENSE_SUCCESS)
    {
        ADI_SENSE_LOG_ERROR("Failed to set ADC current for channel %d",
                            eChannelId);
        return eRet;
    }

    return ADI_SENSE_SUCCESS;
}

static ADI_SENSE_RESULT adi_sense_SetChannelDigitalSensorDetails(
    ADI_SENSE_DEVICE_HANDLE hDevice,
    ADI_SENSE_1000_CHANNEL_ID eChannelId,
    ADI_SENSE_1000_CHANNEL_CONFIG *pChannelConfig)
{
    ADI_ADISENSE_CORE_Sensor_Details_t sensorDetailsReg;

    sensorDetailsReg.VALUE32 = REG_RESET_VAL(CORE_SENSOR_DETAILSn);

    if (pChannelConfig->compensationChannel == ADI_SENSE_1000_CHANNEL_ID_NONE)
    {
        sensorDetailsReg.Compensation_Disable = 1;
        sensorDetailsReg.Compensation_Channel = 0;
    }
    else
    {
        ADI_SENSE_LOG_ERROR("Invalid compensation channel specified for digital sensor");
        return ADI_SENSE_INVALID_PARAM;
    }

    if (pChannelConfig->measurementUnit == ADI_SENSE_1000_MEASUREMENT_UNIT_UNSPECIFIED)
    {
        sensorDetailsReg.Measurement_Units = ADISENSE_CORE_SENSOR_DETAILS_UNITS_UNSPECIFIED;
    }
    else
    {
        ADI_SENSE_LOG_ERROR("Invalid measurement unit specified for digital channel");
        return ADI_SENSE_INVALID_PARAM;
    }

    if (pChannelConfig->disablePublishing)
        sensorDetailsReg.Do_Not_Publish = 1;
    else
        sensorDetailsReg.Do_Not_Publish = 0;

    if (pChannelConfig->enableUnityLut)
        sensorDetailsReg.Unity_LUT_Select = 1;
    else
        sensorDetailsReg.Unity_LUT_Select = 0;

    sensorDetailsReg.Vbias = 0;
    sensorDetailsReg.Reference_Buffer_Disable = 1;

    WRITE_REG_U32(hDevice, sensorDetailsReg.VALUE32, CORE_SENSOR_DETAILSn(eChannelId));

    return ADI_SENSE_SUCCESS;
}

static ADI_SENSE_RESULT adi_sense_SetDigitalSensorCommands(
    ADI_SENSE_DEVICE_HANDLE hDevice,
    ADI_SENSE_1000_CHANNEL_ID eChannelId,
    ADI_SENSE_1000_DIGITAL_SENSOR_COMMAND *pConfigCommand,
    ADI_SENSE_1000_DIGITAL_SENSOR_COMMAND *pDataRequestCommand)
{
    ADI_ADISENSE_CORE_Digital_Sensor_Num_Cmds_t numCmdsReg;

    numCmdsReg.VALUE8 = REG_RESET_VAL(CORE_DIGITAL_SENSOR_NUM_CMDSn);

    CHECK_REG_FIELD_VAL(CORE_DIGITAL_SENSOR_NUM_CMDS_DIGITAL_SENSOR_NUM_CFG_CMDS,
                        pConfigCommand->commandLength);
    CHECK_REG_FIELD_VAL(CORE_DIGITAL_SENSOR_NUM_CMDS_DIGITAL_SENSOR_NUM_READ_CMDS,
                        pDataRequestCommand->commandLength);

    numCmdsReg.Digital_Sensor_Num_Cfg_Cmds = pConfigCommand->commandLength;
    numCmdsReg.Digital_Sensor_Num_Read_Cmds = pDataRequestCommand->commandLength;

    WRITE_REG_U8(hDevice, numCmdsReg.VALUE8,
                 CORE_DIGITAL_SENSOR_NUM_CMDSn(eChannelId));

    /*
     * NOTE - the fall-through cases in the switch statement below are
     * intentional, so temporarily disable related compiler warnings which may
     * be produced here by GCC
     */
#ifndef __CC_ARM
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
#endif

    switch (pConfigCommand->commandLength)
    {
    case 7:
        WRITE_REG_U8(hDevice, pConfigCommand->command[6],
                     CORE_DIGITAL_SENSOR_COMMAND7n(eChannelId));
    case 6:
        WRITE_REG_U8(hDevice, pConfigCommand->command[5],
                     CORE_DIGITAL_SENSOR_COMMAND6n(eChannelId));
    case 5:
        WRITE_REG_U8(hDevice, pConfigCommand->command[4],
                     CORE_DIGITAL_SENSOR_COMMAND5n(eChannelId));
    case 4:
        WRITE_REG_U8(hDevice, pConfigCommand->command[3],
                     CORE_DIGITAL_SENSOR_COMMAND4n(eChannelId));
    case 3:
        WRITE_REG_U8(hDevice, pConfigCommand->command[2],
                     CORE_DIGITAL_SENSOR_COMMAND3n(eChannelId));
    case 2:
        WRITE_REG_U8(hDevice, pConfigCommand->command[1],
                     CORE_DIGITAL_SENSOR_COMMAND2n(eChannelId));
    case 1:
        WRITE_REG_U8(hDevice, pConfigCommand->command[0],
                     CORE_DIGITAL_SENSOR_COMMAND1n(eChannelId));
    case 0:
    default:
        break;
    };

    switch (pDataRequestCommand->commandLength)
    {
    case 7:
        WRITE_REG_U8(hDevice, pDataRequestCommand->command[6],
                     CORE_DIGITAL_SENSOR_READ_CMD7n(eChannelId));
    case 6:
        WRITE_REG_U8(hDevice, pDataRequestCommand->command[5],
                     CORE_DIGITAL_SENSOR_READ_CMD6n(eChannelId));
    case 5:
        WRITE_REG_U8(hDevice, pDataRequestCommand->command[4],
                     CORE_DIGITAL_SENSOR_READ_CMD5n(eChannelId));
    case 4:
        WRITE_REG_U8(hDevice, pDataRequestCommand->command[3],
                     CORE_DIGITAL_SENSOR_READ_CMD4n(eChannelId));
    case 3:
        WRITE_REG_U8(hDevice, pDataRequestCommand->command[2],
                     CORE_DIGITAL_SENSOR_READ_CMD3n(eChannelId));
    case 2:
        WRITE_REG_U8(hDevice, pDataRequestCommand->command[1],
                     CORE_DIGITAL_SENSOR_READ_CMD2n(eChannelId));
    case 1:
        WRITE_REG_U8(hDevice, pDataRequestCommand->command[0],
                     CORE_DIGITAL_SENSOR_READ_CMD1n(eChannelId));
    case 0:
    default:
        break;
    };

    /* Re-enable the implicit-fallthrough warning */
#ifndef __CC_ARM
#pragma GCC diagnostic pop
#endif

    return ADI_SENSE_SUCCESS;
}

static ADI_SENSE_RESULT adi_sense_SetDigitalSensorFormat(
    ADI_SENSE_DEVICE_HANDLE hDevice,
    ADI_SENSE_1000_CHANNEL_ID eChannelId,
    ADI_SENSE_1000_DIGITAL_SENSOR_DATA_FORMAT *pDataFormat)
{
    ADI_ADISENSE_CORE_Digital_Sensor_Config_t sensorConfigReg;

    sensorConfigReg.VALUE16 = REG_RESET_VAL(CORE_DIGITAL_SENSOR_CONFIGn);

    if (pDataFormat->coding != ADI_SENSE_1000_DIGITAL_SENSOR_DATA_CODING_NONE)
    {
        if (pDataFormat->frameLength == 0)
        {
            ADI_SENSE_LOG_ERROR("Invalid frame length specified for digital sensor data format");
            return ADI_SENSE_INVALID_PARAM;
        }
        if (pDataFormat->numDataBits == 0)
        {
            ADI_SENSE_LOG_ERROR("Invalid frame length specified for digital sensor data format");
            return ADI_SENSE_INVALID_PARAM;
        }

        CHECK_REG_FIELD_VAL(CORE_DIGITAL_SENSOR_CONFIG_DIGITAL_SENSOR_READ_BYTES,
                            pDataFormat->frameLength - 1);
        CHECK_REG_FIELD_VAL(CORE_DIGITAL_SENSOR_CONFIG_DIGITAL_SENSOR_DATA_BITS,
                            pDataFormat->numDataBits - 1);
        CHECK_REG_FIELD_VAL(CORE_DIGITAL_SENSOR_CONFIG_DIGITAL_SENSOR_BIT_OFFSET,
                            pDataFormat->bitOffset);

        sensorConfigReg.Digital_Sensor_Read_Bytes = pDataFormat->frameLength - 1;
        sensorConfigReg.Digital_Sensor_Data_Bits = pDataFormat->numDataBits - 1;
        sensorConfigReg.Digital_Sensor_Bit_Offset = pDataFormat->bitOffset;
        sensorConfigReg.Digital_Sensor_Left_Aligned = pDataFormat->leftJustified ? 1 : 0;
        sensorConfigReg.Digital_Sensor_Little_Endian = pDataFormat->littleEndian ? 1 : 0;

        switch (pDataFormat->coding)
        {
        case ADI_SENSE_1000_DIGITAL_SENSOR_DATA_CODING_UNIPOLAR:
            sensorConfigReg.Digital_Sensor_Coding = ADISENSE_CORE_DIGITAL_SENSOR_CONFIG_CODING_UNIPOLAR;
            break;
        case ADI_SENSE_1000_DIGITAL_SENSOR_DATA_CODING_TWOS_COMPLEMENT:
            sensorConfigReg.Digital_Sensor_Coding = ADISENSE_CORE_DIGITAL_SENSOR_CONFIG_CODING_TWOS_COMPL;
            break;
        case ADI_SENSE_1000_DIGITAL_SENSOR_DATA_CODING_OFFSET_BINARY:
            sensorConfigReg.Digital_Sensor_Coding = ADISENSE_CORE_DIGITAL_SENSOR_CONFIG_CODING_OFFSET_BINARY;
            break;
        default:
            ADI_SENSE_LOG_ERROR("Invalid coding specified for digital sensor data format");
            return ADI_SENSE_INVALID_PARAM;
        }
    }
    else
    {
        sensorConfigReg.Digital_Sensor_Coding = ADISENSE_CORE_DIGITAL_SENSOR_CONFIG_CODING_NONE;
    }

    WRITE_REG_U16(hDevice, sensorConfigReg.VALUE16,
                  CORE_DIGITAL_SENSOR_CONFIGn(eChannelId));


    return ADI_SENSE_SUCCESS;
}

static ADI_SENSE_RESULT adi_sense_SetDigitalCalibrationParam(
        ADI_SENSE_DEVICE_HANDLE hDevice,
        ADI_SENSE_1000_CHANNEL_ID eChannelId,
        ADI_SENSE_1000_DIGITAL_CALIBRATION_COMMAND *pCalibrationParam)
{
    ADI_ADISENSE_CORE_Calibration_Parameter_t calibrationParamReg;

    calibrationParamReg.VALUE32 = REG_RESET_VAL(CORE_CALIBRATION_PARAMETERn);

    if (pCalibrationParam->enableCalibrationParam == false)
        calibrationParamReg.Calibration_Parameter_Enable = 0;
    else
        calibrationParamReg.Calibration_Parameter_Enable = 1;

    CHECK_REG_FIELD_VAL(CORE_CALIBRATION_PARAMETER_CALIBRATION_PARAMETER,
        pCalibrationParam->calibrationParam);

    calibrationParamReg.Calibration_Parameter = pCalibrationParam->calibrationParam;

    WRITE_REG_U32(hDevice, calibrationParamReg.VALUE32,
                  CORE_CALIBRATION_PARAMETERn(eChannelId));

    return ADI_SENSE_SUCCESS;
}

static ADI_SENSE_RESULT adi_sense_SetChannelI2cSensorType(
    ADI_SENSE_DEVICE_HANDLE hDevice,
    ADI_SENSE_1000_CHANNEL_ID eChannelId,
    ADI_SENSE_1000_I2C_SENSOR_TYPE sensorType)
{
    ADI_ADISENSE_CORE_Sensor_Type_t sensorTypeReg;

    sensorTypeReg.VALUE16 = REG_RESET_VAL(CORE_SENSOR_TYPEn);

    /* Ensure that the sensor type is valid for this channel */
    switch(sensorType)
    {
    case ADI_SENSE_1000_I2C_SENSOR_HUMIDITY_A_DEF_L1:
    case ADI_SENSE_1000_I2C_SENSOR_HUMIDITY_B_DEF_L1:
    case ADI_SENSE_1000_I2C_SENSOR_HUMIDITY_A_DEF_L2:
    case ADI_SENSE_1000_I2C_SENSOR_HUMIDITY_B_DEF_L2:
    case ADI_SENSE_1000_I2C_SENSOR_HUMIDITY_A_ADV_L1:
    case ADI_SENSE_1000_I2C_SENSOR_HUMIDITY_B_ADV_L1:
    case ADI_SENSE_1000_I2C_SENSOR_HUMIDITY_A_ADV_L2:
    case ADI_SENSE_1000_I2C_SENSOR_HUMIDITY_B_ADV_L2:
    case ADI_SENSE_1000_I2C_SENSOR_AMBIENTLIGHT_A_DEF_L1:
    case ADI_SENSE_1000_I2C_SENSOR_AMBIENTLIGHT_A_DEF_L2:
    case ADI_SENSE_1000_I2C_SENSOR_AMBIENTLIGHT_A_ADV_L1:
    case ADI_SENSE_1000_I2C_SENSOR_AMBIENTLIGHT_A_ADV_L2:
        sensorTypeReg.Sensor_Type = sensorType;
        break;
    default:
        ADI_SENSE_LOG_ERROR("Unsupported I2C sensor type %d specified", sensorType);
        return ADI_SENSE_INVALID_PARAM;
    }

    WRITE_REG_U16(hDevice, sensorTypeReg.VALUE16, CORE_SENSOR_TYPEn(eChannelId));

    return ADI_SENSE_SUCCESS;
}

static ADI_SENSE_RESULT adi_sense_SetChannelI2cSensorAddress(
    ADI_SENSE_DEVICE_HANDLE hDevice,
    ADI_SENSE_1000_CHANNEL_ID eChannelId,
    uint32_t deviceAddress)
{
    CHECK_REG_FIELD_VAL(CORE_DIGITAL_SENSOR_ADDRESS_DIGITAL_SENSOR_ADDRESS, deviceAddress);
    WRITE_REG_U8(hDevice, deviceAddress, CORE_DIGITAL_SENSOR_ADDRESSn(eChannelId));

    return ADI_SENSE_SUCCESS;
}

static ADI_SENSE_RESULT adi_sense_SetDigitalChannelComms(
    ADI_SENSE_DEVICE_HANDLE hDevice,
    ADI_SENSE_1000_CHANNEL_ID eChannelId,
    ADI_SENSE_1000_DIGITAL_SENSOR_COMMS *pDigitalComms)
{
    ADI_ADISENSE_CORE_Digital_Sensor_Comms_t digitalSensorComms;

    digitalSensorComms.VALUE16 = REG_RESET_VAL(CORE_DIGITAL_SENSOR_COMMSn);

    if(pDigitalComms->useCustomCommsConfig)
    {
        digitalSensorComms.Digital_Sensor_Comms_En = 1;

        if(pDigitalComms->i2cClockSpeed == ADI_SENSE_1000_DIGITAL_SENSOR_COMMS_I2C_CLOCK_SPEED_100K)
        {
            digitalSensorComms.I2C_Clock = ADISENSE_CORE_DIGITAL_SENSOR_COMMS_I2C_100K;
        }
        else if(pDigitalComms->i2cClockSpeed == ADI_SENSE_1000_DIGITAL_SENSOR_COMMS_I2C_CLOCK_SPEED_400K)
        {
            digitalSensorComms.I2C_Clock = ADISENSE_CORE_DIGITAL_SENSOR_COMMS_I2C_400K;
        }
        else
        {
            ADI_SENSE_LOG_ERROR("Invalid I2C clock speed %d specified",
                                pDigitalComms->i2cClockSpeed);
            return ADI_SENSE_INVALID_PARAM;
        }

        if(pDigitalComms->spiMode == ADI_SENSE_1000_DIGITAL_SENSOR_COMMS_SPI_MODE_0)
        {
            digitalSensorComms.SPI_Mode = ADISENSE_CORE_DIGITAL_SENSOR_COMMS_SPI_MODE_0;
        }
        else if(pDigitalComms->spiMode == ADI_SENSE_1000_DIGITAL_SENSOR_COMMS_SPI_MODE_1)
        {
            digitalSensorComms.SPI_Mode = ADISENSE_CORE_DIGITAL_SENSOR_COMMS_SPI_MODE_1;
        }
        else if(pDigitalComms->spiMode == ADI_SENSE_1000_DIGITAL_SENSOR_COMMS_SPI_MODE_2)
        {
            digitalSensorComms.SPI_Mode = ADISENSE_CORE_DIGITAL_SENSOR_COMMS_SPI_MODE_2;
        }
        else if(pDigitalComms->spiMode == ADI_SENSE_1000_DIGITAL_SENSOR_COMMS_SPI_MODE_3)
        {
            digitalSensorComms.SPI_Mode = ADISENSE_CORE_DIGITAL_SENSOR_COMMS_SPI_MODE_3;
        }
        else
        {
            ADI_SENSE_LOG_ERROR("Invalid SPI mode %d specified",
                                pDigitalComms->spiMode);
            return ADI_SENSE_INVALID_PARAM;
        }

        switch (pDigitalComms->spiClock)
        {
        case ADI_SENSE_1000_DIGITAL_SENSOR_COMMS_SPI_CLOCK_13MHZ:
            digitalSensorComms.SPI_Clock = ADISENSE_CORE_DIGITAL_SENSOR_COMMS_SPI_13MHZ;
            break;
        case ADI_SENSE_1000_DIGITAL_SENSOR_COMMS_SPI_CLOCK_6_5MHZ:
            digitalSensorComms.SPI_Clock = ADISENSE_CORE_DIGITAL_SENSOR_COMMS_SPI_6_5MHZ;
            break;
        case ADI_SENSE_1000_DIGITAL_SENSOR_COMMS_SPI_CLOCK_3_25MHZ:
            digitalSensorComms.SPI_Clock = ADISENSE_CORE_DIGITAL_SENSOR_COMMS_SPI_3_25MHZ;
            break;
        case ADI_SENSE_1000_DIGITAL_SENSOR_COMMS_SPI_CLOCK_1_625MHZ:
            digitalSensorComms.SPI_Clock = ADISENSE_CORE_DIGITAL_SENSOR_COMMS_SPI_1_625MHZ;
            break;
        case ADI_SENSE_1000_DIGITAL_SENSOR_COMMS_SPI_CLOCK_812KHZ:
            digitalSensorComms.SPI_Clock = ADISENSE_CORE_DIGITAL_SENSOR_COMMS_SPI_812KHZ;
            break;
        case ADI_SENSE_1000_DIGITAL_SENSOR_COMMS_SPI_CLOCK_406KHZ:
            digitalSensorComms.SPI_Clock = ADISENSE_CORE_DIGITAL_SENSOR_COMMS_SPI_406KHZ;
            break;
        case ADI_SENSE_1000_DIGITAL_SENSOR_COMMS_SPI_CLOCK_203KHZ:
            digitalSensorComms.SPI_Clock = ADISENSE_CORE_DIGITAL_SENSOR_COMMS_SPI_203KHZ;
            break;
        case ADI_SENSE_1000_DIGITAL_SENSOR_COMMS_SPI_CLOCK_101KHZ:
            digitalSensorComms.SPI_Clock = ADISENSE_CORE_DIGITAL_SENSOR_COMMS_SPI_101KHZ;
            break;
        case ADI_SENSE_1000_DIGITAL_SENSOR_COMMS_SPI_CLOCK_50KHZ:
            digitalSensorComms.SPI_Clock = ADISENSE_CORE_DIGITAL_SENSOR_COMMS_SPI_50KHZ;
            break;
        case ADI_SENSE_1000_DIGITAL_SENSOR_COMMS_SPI_CLOCK_25KHZ:
            digitalSensorComms.SPI_Clock = ADISENSE_CORE_DIGITAL_SENSOR_COMMS_SPI_25KHZ;
            break;
        case ADI_SENSE_1000_DIGITAL_SENSOR_COMMS_SPI_CLOCK_12KHZ:
            digitalSensorComms.SPI_Clock = ADISENSE_CORE_DIGITAL_SENSOR_COMMS_SPI_12KHZ;
            break;
        case ADI_SENSE_1000_DIGITAL_SENSOR_COMMS_SPI_CLOCK_6KHZ:
            digitalSensorComms.SPI_Clock = ADISENSE_CORE_DIGITAL_SENSOR_COMMS_SPI_6KHZ;
            break;
        case ADI_SENSE_1000_DIGITAL_SENSOR_COMMS_SPI_CLOCK_3KHZ:
            digitalSensorComms.SPI_Clock = ADISENSE_CORE_DIGITAL_SENSOR_COMMS_SPI_3KHZ;
            break;
        case ADI_SENSE_1000_DIGITAL_SENSOR_COMMS_SPI_CLOCK_1_5KHZ:
            digitalSensorComms.SPI_Clock = ADISENSE_CORE_DIGITAL_SENSOR_COMMS_SPI_1_5KHZ;
            break;
        case ADI_SENSE_1000_DIGITAL_SENSOR_COMMS_SPI_CLOCK_793HZ:
            digitalSensorComms.SPI_Clock = ADISENSE_CORE_DIGITAL_SENSOR_COMMS_SPI_793HZ;
            break;
        case ADI_SENSE_1000_DIGITAL_SENSOR_COMMS_SPI_CLOCK_396HZ:
            digitalSensorComms.SPI_Clock = ADISENSE_CORE_DIGITAL_SENSOR_COMMS_SPI_396HZ;
            break;
        default:
            ADI_SENSE_LOG_ERROR("Invalid SPI clock %d specified",
                                pDigitalComms->spiClock);
            return ADI_SENSE_INVALID_PARAM;
        }

        switch (pDigitalComms->uartLineConfig)
        {
        case ADI_SENSE_1000_DIGITAL_SENSOR_COMMS_UART_LINE_CONFIG_8N1:
            digitalSensorComms.Uart_Mode = ADISENSE_CORE_DIGITAL_SENSOR_COMMS_LINECONTROL_8N1;
            break;
        case ADI_SENSE_1000_DIGITAL_SENSOR_COMMS_UART_LINE_CONFIG_8N2:
            digitalSensorComms.Uart_Mode = ADISENSE_CORE_DIGITAL_SENSOR_COMMS_LINECONTROL_8N2;
            break;
        case ADI_SENSE_1000_DIGITAL_SENSOR_COMMS_UART_LINE_CONFIG_8N3:
            digitalSensorComms.Uart_Mode = ADISENSE_CORE_DIGITAL_SENSOR_COMMS_LINECONTROL_8N3;
            break;
        case ADI_SENSE_1000_DIGITAL_SENSOR_COMMS_UART_LINE_CONFIG_8E1:
            digitalSensorComms.Uart_Mode = ADISENSE_CORE_DIGITAL_SENSOR_COMMS_LINECONTROL_8E1;
            break;
        case ADI_SENSE_1000_DIGITAL_SENSOR_COMMS_UART_LINE_CONFIG_8E2:
            digitalSensorComms.Uart_Mode = ADISENSE_CORE_DIGITAL_SENSOR_COMMS_LINECONTROL_8E2;
            break;
        case ADI_SENSE_1000_DIGITAL_SENSOR_COMMS_UART_LINE_CONFIG_8E3:
            digitalSensorComms.Uart_Mode = ADISENSE_CORE_DIGITAL_SENSOR_COMMS_LINECONTROL_8E3;
            break;
        case ADI_SENSE_1000_DIGITAL_SENSOR_COMMS_UART_LINE_CONFIG_8O1:
            digitalSensorComms.Uart_Mode = ADISENSE_CORE_DIGITAL_SENSOR_COMMS_LINECONTROL_8O1;
            break;
        case ADI_SENSE_1000_DIGITAL_SENSOR_COMMS_UART_LINE_CONFIG_8O2:
            digitalSensorComms.Uart_Mode = ADISENSE_CORE_DIGITAL_SENSOR_COMMS_LINECONTROL_8O2;
            break;
        case ADI_SENSE_1000_DIGITAL_SENSOR_COMMS_UART_LINE_CONFIG_8O3:
            digitalSensorComms.Uart_Mode = ADISENSE_CORE_DIGITAL_SENSOR_COMMS_LINECONTROL_8O3;
            break;
        default:
            ADI_SENSE_LOG_ERROR("Invalid UART mode %d specified",
                                pDigitalComms->uartLineConfig);
            return ADI_SENSE_INVALID_PARAM;
        }

        switch (pDigitalComms->uartBaudRate)
        {
        case ADI_SENSE_1000_DIGITAL_SENSOR_COMMS_UART_BAUD_RATE_115200:
            digitalSensorComms.Uart_Baud = ADISENSE_CORE_DIGITAL_SENSOR_COMMS_UART_115200;
            break;
        case ADI_SENSE_1000_DIGITAL_SENSOR_COMMS_UART_BAUD_RATE_57600:
            digitalSensorComms.Uart_Baud = ADISENSE_CORE_DIGITAL_SENSOR_COMMS_UART_57600;
            break;
        case ADI_SENSE_1000_DIGITAL_SENSOR_COMMS_UART_BAUD_RATE_38400:
            digitalSensorComms.Uart_Baud = ADISENSE_CORE_DIGITAL_SENSOR_COMMS_UART_38400;
            break;
        case ADI_SENSE_1000_DIGITAL_SENSOR_COMMS_UART_BAUD_RATE_19200:
            digitalSensorComms.Uart_Baud = ADISENSE_CORE_DIGITAL_SENSOR_COMMS_UART_19200;
            break;
        case ADI_SENSE_1000_DIGITAL_SENSOR_COMMS_UART_BAUD_RATE_9600:
            digitalSensorComms.Uart_Baud = ADISENSE_CORE_DIGITAL_SENSOR_COMMS_UART_9600;
            break;
        case ADI_SENSE_1000_DIGITAL_SENSOR_COMMS_UART_BAUD_RATE_4800:
            digitalSensorComms.Uart_Baud = ADISENSE_CORE_DIGITAL_SENSOR_COMMS_UART_4800;
            break;
        case ADI_SENSE_1000_DIGITAL_SENSOR_COMMS_UART_BAUD_RATE_2400:
            digitalSensorComms.Uart_Baud = ADISENSE_CORE_DIGITAL_SENSOR_COMMS_UART_2400;
            break;
        case ADI_SENSE_1000_DIGITAL_SENSOR_COMMS_UART_BAUD_RATE_1200:
            digitalSensorComms.Uart_Baud = ADISENSE_CORE_DIGITAL_SENSOR_COMMS_UART_1200;
            break;
        default:
            ADI_SENSE_LOG_ERROR("Invalid UART baud rate %d specified",
                                pDigitalComms->uartBaudRate);
            return ADI_SENSE_INVALID_PARAM;
        }
    }
    else
    {
        digitalSensorComms.Digital_Sensor_Comms_En = 0;
    }

    WRITE_REG_U16(hDevice, digitalSensorComms.VALUE16, CORE_DIGITAL_SENSOR_COMMSn(eChannelId));

    return ADI_SENSE_SUCCESS;
}

ADI_SENSE_RESULT adi_sense_SetI2cChannelConfig(
    ADI_SENSE_DEVICE_HANDLE hDevice,
    ADI_SENSE_1000_CHANNEL_ID eChannelId,
    ADI_SENSE_1000_CHANNEL_CONFIG *pChannelConfig)
{
    ADI_SENSE_RESULT eRet;
    ADI_SENSE_1000_I2C_CHANNEL_CONFIG *pI2cChannelConfig =
        &pChannelConfig->i2cChannelConfig;

    eRet = adi_sense_SetChannelI2cSensorType(hDevice, eChannelId,
                                            pI2cChannelConfig->sensor);
    if (eRet != ADI_SENSE_SUCCESS)
    {
        ADI_SENSE_LOG_ERROR("Failed to set I2C sensor type for channel %d",
                            eChannelId);
        return eRet;
    }

    eRet = adi_sense_SetChannelI2cSensorAddress(hDevice, eChannelId,
                                               pI2cChannelConfig->deviceAddress);
    if (eRet != ADI_SENSE_SUCCESS)
    {
        ADI_SENSE_LOG_ERROR("Failed to set I2C sensor address for channel %d",
                            eChannelId);
        return eRet;
    }

    eRet = adi_sense_SetChannelDigitalSensorDetails(hDevice, eChannelId,
                                                    pChannelConfig);
    if (eRet != ADI_SENSE_SUCCESS)
    {
        ADI_SENSE_LOG_ERROR("Failed to set I2C sensor details for channel %d",
                            eChannelId);
        return eRet;
    }

    eRet = adi_sense_SetDigitalSensorCommands(hDevice, eChannelId,
                                              &pI2cChannelConfig->configurationCommand,
                                              &pI2cChannelConfig->dataRequestCommand);
    if (eRet != ADI_SENSE_SUCCESS)
    {
        ADI_SENSE_LOG_ERROR("Failed to set I2C sensor commands for channel %d",
                            eChannelId);
        return eRet;
    }

    eRet = adi_sense_SetDigitalSensorFormat(hDevice, eChannelId,
                                            &pI2cChannelConfig->dataFormat);
    if (eRet != ADI_SENSE_SUCCESS)
    {
        ADI_SENSE_LOG_ERROR("Failed to set I2C sensor data format for channel %d",
                            eChannelId);
        return eRet;
    }

    eRet = adi_sense_SetDigitalCalibrationParam(hDevice, eChannelId,
                                            &pI2cChannelConfig->digitalCalibrationParam);
    if (eRet != ADI_SENSE_SUCCESS)
    {
        ADI_SENSE_LOG_ERROR("Failed to set I2C digital calibration param for channel %d",
                            eChannelId);
        return eRet;
    }

    eRet = adi_sense_SetDigitalChannelComms(hDevice, eChannelId,
                                            &pI2cChannelConfig->configureComms);
    if (eRet != ADI_SENSE_SUCCESS)
    {
        ADI_SENSE_LOG_ERROR("Failed to set I2C comms for channel %d",
                            eChannelId);
        return eRet;
    }

    return ADI_SENSE_SUCCESS;
}

static ADI_SENSE_RESULT adi_sense_SetChannelSpiSensorType(
    ADI_SENSE_DEVICE_HANDLE hDevice,
    ADI_SENSE_1000_CHANNEL_ID eChannelId,
    ADI_SENSE_1000_SPI_SENSOR_TYPE sensorType)
{
    ADI_ADISENSE_CORE_Sensor_Type_t sensorTypeReg;

    sensorTypeReg.VALUE16 = REG_RESET_VAL(CORE_SENSOR_TYPEn);

    /* Ensure that the sensor type is valid for this channel */
    switch(sensorType)
    {
    case ADI_SENSE_1000_SPI_SENSOR_PRESSURE_A_DEF_L1:
    case ADI_SENSE_1000_SPI_SENSOR_PRESSURE_A_DEF_L2:
    case ADI_SENSE_1000_SPI_SENSOR_PRESSURE_A_ADV_L1:
    case ADI_SENSE_1000_SPI_SENSOR_PRESSURE_A_ADV_L2:
    case ADI_SENSE_1000_SPI_SENSOR_ACCELEROMETER_A_DEF_L1:
    case ADI_SENSE_1000_SPI_SENSOR_ACCELEROMETER_B_DEF_L1:
    case ADI_SENSE_1000_SPI_SENSOR_ACCELEROMETER_A_DEF_L2:
    case ADI_SENSE_1000_SPI_SENSOR_ACCELEROMETER_B_DEF_L2:
    case ADI_SENSE_1000_SPI_SENSOR_ACCELEROMETER_A_ADV_L1:
    case ADI_SENSE_1000_SPI_SENSOR_ACCELEROMETER_B_ADV_L1:
    case ADI_SENSE_1000_SPI_SENSOR_ACCELEROMETER_A_ADV_L2:
    case ADI_SENSE_1000_SPI_SENSOR_ACCELEROMETER_B_ADV_L2:
        sensorTypeReg.Sensor_Type = sensorType;
        break;
    default:
        ADI_SENSE_LOG_ERROR("Unsupported SPI sensor type %d specified", sensorType);
        return ADI_SENSE_INVALID_PARAM;
    }

    WRITE_REG_U16(hDevice, sensorTypeReg.VALUE16, CORE_SENSOR_TYPEn(eChannelId));

    return ADI_SENSE_SUCCESS;
}

ADI_SENSE_RESULT adi_sense_SetSpiChannelConfig(
    ADI_SENSE_DEVICE_HANDLE hDevice,
    ADI_SENSE_1000_CHANNEL_ID eChannelId,
    ADI_SENSE_1000_CHANNEL_CONFIG *pChannelConfig)
{
    ADI_SENSE_RESULT eRet;
    ADI_SENSE_1000_SPI_CHANNEL_CONFIG *pSpiChannelConfig =
        &pChannelConfig->spiChannelConfig;

    eRet = adi_sense_SetChannelSpiSensorType(hDevice, eChannelId,
                                             pSpiChannelConfig->sensor);
    if (eRet != ADI_SENSE_SUCCESS)
    {
        ADI_SENSE_LOG_ERROR("Failed to set SPI sensor type for channel %d",
                            eChannelId);
        return eRet;
    }

    eRet = adi_sense_SetChannelDigitalSensorDetails(hDevice, eChannelId,
                                                    pChannelConfig);
    if (eRet != ADI_SENSE_SUCCESS)
    {
        ADI_SENSE_LOG_ERROR("Failed to set SPI sensor details for channel %d",
                            eChannelId);
        return eRet;
    }

    eRet = adi_sense_SetDigitalSensorCommands(hDevice, eChannelId,
                                              &pSpiChannelConfig->configurationCommand,
                                              &pSpiChannelConfig->dataRequestCommand);
    if (eRet != ADI_SENSE_SUCCESS)
    {
        ADI_SENSE_LOG_ERROR("Failed to set SPI sensor commands for channel %d",
                            eChannelId);
        return eRet;
    }

    eRet = adi_sense_SetDigitalSensorFormat(hDevice, eChannelId,
                                            &pSpiChannelConfig->dataFormat);
    if (eRet != ADI_SENSE_SUCCESS)
    {
        ADI_SENSE_LOG_ERROR("Failed to set SPI sensor data format for channel %d",
                            eChannelId);
        return eRet;
    }

    eRet = adi_sense_SetDigitalCalibrationParam(hDevice, eChannelId,
                                            &pSpiChannelConfig->digitalCalibrationParam);
    if (eRet != ADI_SENSE_SUCCESS)
    {
        ADI_SENSE_LOG_ERROR("Failed to set SPI digital calibration param for channel %d",
                            eChannelId);
        return eRet;
    }

    eRet = adi_sense_SetDigitalChannelComms(hDevice, eChannelId,
                                            &pSpiChannelConfig->configureComms);
    if (eRet != ADI_SENSE_SUCCESS)
    {
        ADI_SENSE_LOG_ERROR("Failed to set SPI comms for channel %d",
                            eChannelId);
        return eRet;
    }

    return ADI_SENSE_SUCCESS;
}

static ADI_SENSE_RESULT adi_sense_SetChannelUartSensorType(
    ADI_SENSE_DEVICE_HANDLE hDevice,
    ADI_SENSE_1000_CHANNEL_ID eChannelId,
    ADI_SENSE_1000_UART_SENSOR_TYPE sensorType)
{
    ADI_ADISENSE_CORE_Sensor_Type_t sensorTypeReg;

    sensorTypeReg.VALUE16 = REG_RESET_VAL(CORE_SENSOR_TYPEn);

    /* Ensure that the sensor type is valid for this channel */
    switch(sensorType)
    {
    case ADI_SENSE_1000_UART_SENSOR_UART_CO2_A_DEF_L1:
    case ADI_SENSE_1000_UART_SENSOR_UART_CO2_B_DEF_L1:
    case ADI_SENSE_1000_UART_SENSOR_UART_CO2_A_DEF_L2:
    case ADI_SENSE_1000_UART_SENSOR_UART_CO2_B_DEF_L2:
    case ADI_SENSE_1000_UART_SENSOR_UART_CO2_A_ADV_L1:
    case ADI_SENSE_1000_UART_SENSOR_UART_CO2_B_ADV_L1:
    case ADI_SENSE_1000_UART_SENSOR_UART_CO2_A_ADV_L2:
    case ADI_SENSE_1000_UART_SENSOR_UART_CO2_B_ADV_L2:
        sensorTypeReg.Sensor_Type = sensorType;
        break;
    default:
        ADI_SENSE_LOG_ERROR("Unsupported UART sensor type %d specified", sensorType);
        return ADI_SENSE_INVALID_PARAM;
    }

    WRITE_REG_U16(hDevice, sensorTypeReg.VALUE16, CORE_SENSOR_TYPEn(eChannelId));

    return ADI_SENSE_SUCCESS;
}

ADI_SENSE_RESULT adi_sense_SetUartChannelConfig(
    ADI_SENSE_DEVICE_HANDLE hDevice,
    ADI_SENSE_1000_CHANNEL_ID eChannelId,
    ADI_SENSE_1000_CHANNEL_CONFIG *pChannelConfig)
{
    ADI_SENSE_RESULT eRet;
    ADI_SENSE_1000_UART_CHANNEL_CONFIG *pUartChannelConfig =
        &pChannelConfig->uartChannelConfig;

    eRet = adi_sense_SetChannelUartSensorType(hDevice, eChannelId,
                                             pUartChannelConfig->sensor);
    if (eRet != ADI_SENSE_SUCCESS)
    {
        ADI_SENSE_LOG_ERROR("Failed to set UART sensor type for channel %d",
                            eChannelId);
        return eRet;
    }

    eRet = adi_sense_SetChannelDigitalSensorDetails(hDevice, eChannelId,
                                                    pChannelConfig);
    if (eRet != ADI_SENSE_SUCCESS)
    {
        ADI_SENSE_LOG_ERROR("Failed to set UART sensor details for channel %d",
                            eChannelId);
        return eRet;
    }

    eRet = adi_sense_SetDigitalCalibrationParam(hDevice, eChannelId,
                                            &pUartChannelConfig->digitalCalibrationParam);
    if (eRet != ADI_SENSE_SUCCESS)
    {
        ADI_SENSE_LOG_ERROR("Failed to set UART digital calibration param for channel %d",
                            eChannelId);
        return eRet;
    }

    eRet = adi_sense_SetDigitalChannelComms(hDevice, eChannelId,
                                            &pUartChannelConfig->configureComms);
    if (eRet != ADI_SENSE_SUCCESS)
    {
        ADI_SENSE_LOG_ERROR("Failed to set UART comms for channel %d",
                            eChannelId);
        return eRet;
    }


    return ADI_SENSE_SUCCESS;
}

ADI_SENSE_RESULT adi_sense_1000_SetChannelThresholdLimits(
    ADI_SENSE_DEVICE_HANDLE hDevice,
    ADI_SENSE_1000_CHANNEL_ID eChannelId,
    float32_t fHighThresholdLimit,
    float32_t fLowThresholdLimit)
{
    /*
     * If the low/high limits are *both* set to 0 in memory, or NaNs, assume
     * that they are unset, or not required, and use infinity defaults instead
     */
    if (fHighThresholdLimit == 0.0f && fLowThresholdLimit == 0.0f)
    {
        fHighThresholdLimit = INFINITY;
        fLowThresholdLimit = -INFINITY;
    }
    else
    {
        if (isnan(fHighThresholdLimit))
            fHighThresholdLimit = INFINITY;
        if (isnan(fLowThresholdLimit))
            fLowThresholdLimit = -INFINITY;
    }

    WRITE_REG_FLOAT(hDevice, fHighThresholdLimit,
                    CORE_HIGH_THRESHOLD_LIMITn(eChannelId));
    WRITE_REG_FLOAT(hDevice, fLowThresholdLimit,
                    CORE_LOW_THRESHOLD_LIMITn(eChannelId));

    return ADI_SENSE_SUCCESS;
}

ADI_SENSE_RESULT adi_sense_1000_SetOffsetGain(
    ADI_SENSE_DEVICE_HANDLE hDevice,
    ADI_SENSE_1000_CHANNEL_ID eChannelId,
    float32_t fOffsetAdjustment,
    float32_t fGainAdjustment)
{
    /* Replace with default values if NaNs are specified (or 0.0 for gain) */
    if (isnan(fGainAdjustment) || (fGainAdjustment == 0.0f))
        fGainAdjustment = 1.0f;
    if (isnan(fOffsetAdjustment))
        fOffsetAdjustment = 0.0f;

    WRITE_REG_FLOAT(hDevice, fGainAdjustment, CORE_SENSOR_GAINn(eChannelId));
    WRITE_REG_FLOAT(hDevice, fOffsetAdjustment, CORE_SENSOR_OFFSETn(eChannelId));

    return ADI_SENSE_SUCCESS;
}

ADI_SENSE_RESULT adi_sense_1000_SetSensorParameter(
    ADI_SENSE_DEVICE_HANDLE hDevice,
    ADI_SENSE_1000_CHANNEL_ID eChannelId,
    float32_t fSensorParam)
{
    if (fSensorParam == 0.0f)
        fSensorParam = NAN;

    WRITE_REG_FLOAT(hDevice, fSensorParam, CORE_SENSOR_PARAMETERn(eChannelId));

    return ADI_SENSE_SUCCESS;
}

ADI_SENSE_RESULT adi_sense_1000_SetChannelSettlingTime(
    ADI_SENSE_DEVICE_HANDLE hDevice,
    ADI_SENSE_1000_CHANNEL_ID eChannelId,
    uint32_t nSettlingTime)
{
    ADI_ADISENSE_CORE_Settling_Time_t settlingTimeReg;

    if (nSettlingTime < (1 << 12))
    {
        settlingTimeReg.Settling_Time_Units = ADISENSE_CORE_SETTLING_TIME_MICROSECONDS;
    }
    else if (nSettlingTime < (1000 * (1 << 12)))
    {
        settlingTimeReg.Settling_Time_Units = ADISENSE_CORE_SETTLING_TIME_MILLISECONDS;
        nSettlingTime /= 1000;
    }
    else
    {
        settlingTimeReg.Settling_Time_Units = ADISENSE_CORE_SETTLING_TIME_SECONDS;
        nSettlingTime /= 1000000;
    }

    CHECK_REG_FIELD_VAL(CORE_SETTLING_TIME_SETTLING_TIME, nSettlingTime);
    settlingTimeReg.Settling_Time = nSettlingTime;

    WRITE_REG_U16(hDevice, settlingTimeReg.VALUE16, CORE_SETTLING_TIMEn(eChannelId));

    return ADI_SENSE_SUCCESS;
}

ADI_SENSE_RESULT adi_sense_1000_SetChannelConfig(
    ADI_SENSE_DEVICE_HANDLE hDevice,
    ADI_SENSE_1000_CHANNEL_ID eChannelId,
    ADI_SENSE_1000_CHANNEL_CONFIG *pChannelConfig)
{
    ADI_SENSE_RESULT eRet;

    if (! ADI_SENSE_1000_CHANNEL_IS_VIRTUAL(eChannelId))
    {
        eRet = adi_sense_1000_SetChannelCount(hDevice, eChannelId,
                                              pChannelConfig->enableChannel ?
                                              pChannelConfig->measurementsPerCycle : 0);
        if (eRet != ADI_SENSE_SUCCESS)
        {
            ADI_SENSE_LOG_ERROR("Failed to set measurement count for channel %d",
                                eChannelId);
            return eRet;
        }

        eRet = adi_sense_1000_SetChannelOptions(hDevice, eChannelId,
                                                pChannelConfig->priority,
                                                pChannelConfig->enableFFT);
        if (eRet != ADI_SENSE_SUCCESS)
        {
            ADI_SENSE_LOG_ERROR("Failed to set priority for channel %d",
                                eChannelId);
            return eRet;
        }

        /* If the channel is not enabled, we can skip the following steps */
        if (pChannelConfig->enableChannel || pChannelConfig->enableFFT)
        {
            eRet = adi_sense_1000_SetChannelSkipCount(hDevice, eChannelId,
                                                      pChannelConfig->cycleSkipCount);
            if (eRet != ADI_SENSE_SUCCESS)
            {
                ADI_SENSE_LOG_ERROR("Failed to set cycle skip count for channel %d",
                                    eChannelId);
                return eRet;
            }

            switch (eChannelId)
            {
            case ADI_SENSE_1000_CHANNEL_ID_CJC_0:
            case ADI_SENSE_1000_CHANNEL_ID_CJC_1:
            case ADI_SENSE_1000_CHANNEL_ID_SENSOR_0:
            case ADI_SENSE_1000_CHANNEL_ID_SENSOR_1:
            case ADI_SENSE_1000_CHANNEL_ID_SENSOR_2:
            case ADI_SENSE_1000_CHANNEL_ID_SENSOR_3:
            case ADI_SENSE_1000_CHANNEL_ID_VOLTAGE_0:
            case ADI_SENSE_1000_CHANNEL_ID_CURRENT_0:
                eRet = adi_sense_SetAdcChannelConfig(hDevice, eChannelId, pChannelConfig);
                break;
            case ADI_SENSE_1000_CHANNEL_ID_I2C_0:
            case ADI_SENSE_1000_CHANNEL_ID_I2C_1:
                eRet = adi_sense_SetI2cChannelConfig(hDevice, eChannelId, pChannelConfig);
                break;
            case ADI_SENSE_1000_CHANNEL_ID_SPI_0:
                eRet = adi_sense_SetSpiChannelConfig(hDevice, eChannelId, pChannelConfig);
                break;
            case ADI_SENSE_1000_CHANNEL_ID_UART:
                eRet = adi_sense_SetUartChannelConfig(hDevice, eChannelId, pChannelConfig);
                break;
            default:
                ADI_SENSE_LOG_ERROR("Invalid channel ID %d specified", eChannelId);
                return ADI_SENSE_INVALID_PARAM;
            }

            eRet = adi_sense_1000_SetChannelSettlingTime(hDevice, eChannelId,
                                                         pChannelConfig->extraSettlingTime);
            if (eRet != ADI_SENSE_SUCCESS)
            {
                ADI_SENSE_LOG_ERROR("Failed to set settling time for channel %d",
                                    eChannelId);
                return eRet;
            }
        }
    }

    if (pChannelConfig->enableChannel || pChannelConfig->enableFFT)
    {
        /* Threshold limits can be configured individually for virtual channels */
        eRet = adi_sense_1000_SetChannelThresholdLimits(hDevice, eChannelId,
                                                        pChannelConfig->highThreshold,
                                                        pChannelConfig->lowThreshold);
        if (eRet != ADI_SENSE_SUCCESS)
        {
            ADI_SENSE_LOG_ERROR("Failed to set threshold limits for channel %d",
                                eChannelId);
            return eRet;
        }

        /* Offset and gain can be configured individually for virtual channels */
        eRet = adi_sense_1000_SetOffsetGain(hDevice, eChannelId,
                                            pChannelConfig->offsetAdjustment,
                                            pChannelConfig->gainAdjustment);
        if (eRet != ADI_SENSE_SUCCESS)
        {
            ADI_SENSE_LOG_ERROR("Failed to set offset/gain for channel %d",
                                eChannelId);
            return eRet;
        }

        /* Set sensor specific parameter */
        eRet = adi_sense_1000_SetSensorParameter(hDevice, eChannelId,
                                                 pChannelConfig->sensorParameter);
        if (eRet != ADI_SENSE_SUCCESS)
        {
            ADI_SENSE_LOG_ERROR("Failed to set sensor parameter for channel %d",
                                eChannelId);
            return eRet;
        }
    }

    return ADI_SENSE_SUCCESS;
}

ADI_SENSE_RESULT adi_sense_SetConfig(
    ADI_SENSE_DEVICE_HANDLE    const hDevice,
    ADI_SENSE_CONFIG         * const pConfig)
{
    ADI_SENSE_1000_CONFIG *pDeviceConfig;
    ADI_SENSE_PRODUCT_ID productId;
    ADI_SENSE_RESULT eRet;

    if (pConfig->productId != ADI_SENSE_PRODUCT_ID_ADSNS1000)
    {
        ADI_SENSE_LOG_ERROR("Configuration Product ID (0x%X) is not supported (0x%0X)",
                            pConfig->productId, ADI_SENSE_PRODUCT_ID_ADSNS1000);
        return ADI_SENSE_INVALID_PARAM;
    }

    /* Check that the actual Product ID is a match? */
    eRet = adi_sense_GetProductID(hDevice, &productId);
    if (eRet)
    {
        ADI_SENSE_LOG_ERROR("Failed to read device Product ID register");
        return eRet;
    }
    if (pConfig->productId != productId)
    {
        ADI_SENSE_LOG_ERROR("Configuration Product ID (0x%X) does not match device (0x%0X)",
                            pConfig->productId, productId);
        return ADI_SENSE_INVALID_PARAM;
    }

    pDeviceConfig = &pConfig->adisense1000;

    eRet = adi_sense_1000_SetPowerConfig(hDevice, &pDeviceConfig->power);
    if (eRet)
    {
        ADI_SENSE_LOG_ERROR("Failed to set power configuration");
        return eRet;
    }

    eRet = adi_sense_1000_SetMeasurementConfig(hDevice, &pDeviceConfig->measurement);
    if (eRet)
    {
        ADI_SENSE_LOG_ERROR("Failed to set measurement configuration");
        return eRet;
    }

    eRet = adi_sense_1000_SetDiagnosticsConfig(hDevice, &pDeviceConfig->diagnostics);
    if (eRet)
    {
        ADI_SENSE_LOG_ERROR("Failed to set diagnostics configuration");
        return eRet;
    }

    for (ADI_SENSE_1000_CHANNEL_ID id = ADI_SENSE_1000_CHANNEL_ID_CJC_0;
         id < ADI_SENSE_1000_MAX_CHANNELS;
         id++)
    {
        eRet = adi_sense_1000_SetChannelConfig(hDevice, id,
                                               &pDeviceConfig->channels[id]);
        if (eRet)
        {
            ADI_SENSE_LOG_ERROR("Failed to set channel %d configuration", id);
            return eRet;
        }
    }

    eRet = adi_sense_1000_SetFftConfig(hDevice, &pDeviceConfig->fft,
                                       pDeviceConfig->channels);
    if (eRet)
    {
        ADI_SENSE_LOG_ERROR("Failed to set FFT configuration");
        return eRet;
    }

    return ADI_SENSE_SUCCESS;
}

ADI_SENSE_RESULT adi_sense_1000_SetLutData(
    ADI_SENSE_DEVICE_HANDLE    const hDevice,
    ADI_SENSE_1000_LUT       * const pLutData)
{
    ADI_SENSE_1000_LUT_HEADER *pLutHeader = &pLutData->header;
    ADI_SENSE_1000_LUT_TABLE *pLutTable = pLutData->tables;
    unsigned actualLength = 0;

    if (pLutData->header.signature != ADI_SENSE_LUT_SIGNATURE)
    {
        ADI_SENSE_LOG_ERROR("LUT signature incorrect (expected 0x%X, actual 0x%X)",
                            ADI_SENSE_LUT_SIGNATURE, pLutHeader->signature);
        return ADI_SENSE_INVALID_SIGNATURE;
    }

    for (unsigned i = 0; i < pLutHeader->numTables; i++)
    {
        ADI_SENSE_1000_LUT_DESCRIPTOR *pDesc = &pLutTable->descriptor;
        ADI_SENSE_1000_LUT_TABLE_DATA *pData = &pLutTable->data;
        unsigned short calculatedCrc;

        switch (pDesc->geometry)
        {
        case ADI_SENSE_1000_LUT_GEOMETRY_COEFFS:
                switch (pDesc->equation)
                {
                case ADI_SENSE_1000_LUT_EQUATION_POLYN:
                case ADI_SENSE_1000_LUT_EQUATION_POLYNEXP:
                case ADI_SENSE_1000_LUT_EQUATION_QUADRATIC:
                case ADI_SENSE_1000_LUT_EQUATION_STEINHART:
                case ADI_SENSE_1000_LUT_EQUATION_LOGARITHMIC:
                case ADI_SENSE_1000_LUT_EQUATION_BIVARIATE_POLYN:
                break;
                default:
                    ADI_SENSE_LOG_ERROR("Invalid equation %u specified for LUT table %u",
                                        pDesc->equation, i);
                    return ADI_SENSE_INVALID_PARAM;
                }
            break;
        case ADI_SENSE_1000_LUT_GEOMETRY_NES_1D:
        case ADI_SENSE_1000_LUT_GEOMETRY_NES_2D:
        case ADI_SENSE_1000_LUT_GEOMETRY_ES_1D:
        case ADI_SENSE_1000_LUT_GEOMETRY_ES_2D:
                if (pDesc->equation != ADI_SENSE_1000_LUT_EQUATION_LUT) {
                    ADI_SENSE_LOG_ERROR("Invalid equation %u specified for LUT table %u",
                                        pDesc->equation, i);
                    return ADI_SENSE_INVALID_PARAM;
                }
            break;
        default:
            ADI_SENSE_LOG_ERROR("Invalid geometry %u specified for LUT table %u",
                                pDesc->geometry, i);
            return ADI_SENSE_INVALID_PARAM;
        }

        switch (pDesc->dataType)
        {
        case ADI_SENSE_1000_LUT_DATA_TYPE_FLOAT32:
        case ADI_SENSE_1000_LUT_DATA_TYPE_FLOAT64:
            break;
        default:
            ADI_SENSE_LOG_ERROR("Invalid vector format %u specified for LUT table %u",
                                pDesc->dataType, i);
            return ADI_SENSE_INVALID_PARAM;
        }

        calculatedCrc = adi_sense_crc16_ccitt(pData, pDesc->length);
        if (calculatedCrc != pDesc->crc16)
        {
            ADI_SENSE_LOG_ERROR("CRC validation failed on LUT table %u (expected 0x%04X, actual 0x%04X)",
                                i, pDesc->crc16, calculatedCrc);
            return ADI_SENSE_CRC_ERROR;
        }

        actualLength += sizeof(*pDesc) + pDesc->length;

        /* Move to the next look-up table */
        pLutTable = (ADI_SENSE_1000_LUT_TABLE *)((uint8_t *)pLutTable + sizeof(*pDesc) + pDesc->length);
    }

    if (actualLength != pLutHeader->totalLength)
    {
        ADI_SENSE_LOG_ERROR("LUT table length mismatch (expected %u, actual %u)",
                            pLutHeader->totalLength, actualLength);
        return ADI_SENSE_WRONG_SIZE;
    }

    if (sizeof(*pLutHeader) + pLutHeader->totalLength > ADI_SENSE_LUT_MAX_SIZE)
    {
        ADI_SENSE_LOG_ERROR("Maximum LUT table length (%u bytes) exceeded",
                            ADI_SENSE_LUT_MAX_SIZE);
        return ADI_SENSE_WRONG_SIZE;
    }

    /* Write the LUT data to the device */
    unsigned lutSize = sizeof(*pLutHeader) + pLutHeader->totalLength;
    WRITE_REG_U16(hDevice, 0, CORE_LUT_OFFSET);
    WRITE_REG_U8_ARRAY(hDevice, (uint8_t *)pLutData, lutSize, CORE_LUT_DATA);

    return ADI_SENSE_SUCCESS;
}

ADI_SENSE_RESULT adi_sense_1000_SetLutDataRaw(
    ADI_SENSE_DEVICE_HANDLE    const hDevice,
    ADI_SENSE_1000_LUT_RAW   * const pLutData)
{
    return adi_sense_1000_SetLutData(hDevice,
                                     (ADI_SENSE_1000_LUT *)pLutData);
}

static ADI_SENSE_RESULT getLutTableSize(
    ADI_SENSE_1000_LUT_DESCRIPTOR * const pDesc,
    ADI_SENSE_1000_LUT_TABLE_DATA * const pData,
    unsigned *pLength)
{
    switch (pDesc->geometry)
    {
    case ADI_SENSE_1000_LUT_GEOMETRY_COEFFS:
        if (pDesc->equation == ADI_SENSE_1000_LUT_EQUATION_BIVARIATE_POLYN)
            *pLength = ADI_SENSE_1000_LUT_2D_POLYN_COEFF_LIST_SIZE(pData->coeffList2d);
        else
            *pLength = ADI_SENSE_1000_LUT_COEFF_LIST_SIZE(pData->coeffList);
        break;
    case ADI_SENSE_1000_LUT_GEOMETRY_NES_1D:
        *pLength = ADI_SENSE_1000_LUT_1D_NES_SIZE(pData->lut1dNes);
        break;
    case ADI_SENSE_1000_LUT_GEOMETRY_NES_2D:
        *pLength = ADI_SENSE_1000_LUT_2D_NES_SIZE(pData->lut2dNes);
        break;
    case ADI_SENSE_1000_LUT_GEOMETRY_ES_1D:
        *pLength = ADI_SENSE_1000_LUT_1D_ES_SIZE(pData->lut1dEs);
        break;
    case ADI_SENSE_1000_LUT_GEOMETRY_ES_2D:
        *pLength = ADI_SENSE_1000_LUT_2D_ES_SIZE(pData->lut2dEs);
        break;
    default:
        ADI_SENSE_LOG_ERROR("Invalid LUT table geometry %d specified\r\n",
                            pDesc->geometry);
        return ADI_SENSE_INVALID_PARAM;
    }

    return ADI_SENSE_SUCCESS;
}

ADI_SENSE_RESULT adi_sense_1000_AssembleLutData(
    ADI_SENSE_1000_LUT                  * pLutBuffer,
    unsigned                              nLutBufferSize,
    unsigned                        const nNumTables,
    ADI_SENSE_1000_LUT_DESCRIPTOR * const ppDesc[],
    ADI_SENSE_1000_LUT_TABLE_DATA * const ppData[])
{
    ADI_SENSE_1000_LUT_HEADER *pHdr = &pLutBuffer->header;
    uint8_t *pLutTableData = (uint8_t *)pLutBuffer + sizeof(*pHdr);

    if (sizeof(*pHdr) > nLutBufferSize)
    {
        ADI_SENSE_LOG_ERROR("Insufficient LUT buffer size provided");
        return ADI_SENSE_INVALID_PARAM;
    }

    /* First initialise the top-level header */
    pHdr->signature = ADI_SENSE_LUT_SIGNATURE;
    pHdr->version.major = 1;
    pHdr->version.minor = 0;
    pHdr->numTables = 0;
    pHdr->totalLength = 0;

    /*
     * Walk through the list of table pointers provided, appending the table
     * descriptor+data from each one to the provided LUT buffer
     */
    for (unsigned i = 0; i < nNumTables; i++)
    {
        ADI_SENSE_1000_LUT_DESCRIPTOR * const pDesc = ppDesc[i];
        ADI_SENSE_1000_LUT_TABLE_DATA * const pData = ppData[i];
        ADI_SENSE_RESULT res;
        unsigned dataLength = 0;

        /* Calculate the length of the table data */
        res = getLutTableSize(pDesc, pData, &dataLength);
        if (res != ADI_SENSE_SUCCESS)
            return res;

        /* Fill in the table descriptor length and CRC fields */
        pDesc->length = dataLength;
        pDesc->crc16 = adi_sense_crc16_ccitt(pData, dataLength);

        if ((sizeof(*pHdr) + pHdr->totalLength + sizeof(*pDesc) + dataLength) > nLutBufferSize)
        {
            ADI_SENSE_LOG_ERROR("Insufficient LUT buffer size provided");
            return ADI_SENSE_INVALID_PARAM;
        }

        /* Append the table to the LUT buffer (desc + data) */
        memcpy(pLutTableData + pHdr->totalLength, pDesc, sizeof(*pDesc));
        pHdr->totalLength += sizeof(*pDesc);
        memcpy(pLutTableData + pHdr->totalLength, pData, dataLength);
        pHdr->totalLength += dataLength;

        pHdr->numTables++;
    }

    return ADI_SENSE_SUCCESS;
}