sensor result structure modified

src/admw_1001.c

Committer:
Vkadaba
Date:
2020-04-23
Revision:
71:7b5ebd969aa7
Parent:
70:420fac5132f5

File content as of revision 71:7b5ebd969aa7:

/*
Copyright 2019 (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: API implementation for ADMW1001
 *-----------------------------------------------------------------------------
 */

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

#include "admw_platform.h"
#include "admw_api.h"
#include "admw1001/admw1001_api.h"

#include "admw1001/ADMW1001_REGISTERS_typedefs.h"
#include "admw1001/ADMW1001_REGISTERS.h"
#include "admw1001/admw1001_lut_data.h"
#include "admw1001/admw1001_host_comms.h"
#include "inc/mbedVersion.h"
#define VERSIONID_MAJOR 2
#define VERSIONID_MINOR 0

uint32_t    getDataCnt = 0;
#define ADMW_VERSION_REG_VAL_SIZE 4u
#define ADMW_FORMATTED_VERSION_SIZE     11u

#define ADMW_SFL_READ_STATUS_SIZE       42u
/*
 * 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_##_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_##_field;                                \
        uint32_t _shift = BITP_##_field;                                \
        if ((((_val) << _shift) & ~(_mask)) != 0) {                     \
            ADMW_LOG_ERROR("Value 0x%08X invalid for register field %s",\
                                (uint32_t)(_val),                       \
                                STRINGIFY(ADMW_##_field));              \
            return ADMW_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 {                                                                \
        ADMW_RESULT _res;                                               \
        _type _regval = _val;                                           \
        _res = admw1001_WriteRegister((_hdev),                          \
                                            REG_##_name,                \
                                            &_regval, sizeof(_regval)); \
        if (_res != ADMW_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 {                                                                \
        ADMW_RESULT _res;                                               \
        _type _regval;                                                  \
        _res = admw1001_ReadRegister((_hdev),                           \
                                           REG_##_name,                 \
                                           &_regval, sizeof(_regval));  \
        if (_res != ADMW_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 {                                                                \
        ADMW_RESULT _res;                                               \
        _res = admw1001_WriteRegister(_hdev,                            \
                                            REG_##_name,                \
                                            _arr, _len);                \
        if (_res != ADMW_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 {                                                                \
        ADMW_RESULT _res;                                               \
        _res = admw1001_ReadRegister((_hdev),                           \
                                           REG##_name,                  \
                                           _arr, _len);                 \
        if (_res != ADMW_SUCCESS)                                       \
            return _res;                                                \
    } while(false)

#define ADMW1001_CHANNEL_IS_ADC(c)                                      \
    ((c) >= ADMW1001_CH_ID_ANLG_1_UNIVERSAL && (c) <= ADMW1001_CH_ID_ANLG_2_DIFFERENTIAL)

#define ADMW1001_CHANNEL_IS_ADC_CJC(c)                                  \
    ((c) >= ADMW1001_CH_ID_ANLG_1_UNIVERSAL && (c) <= ADMW1001_CH_ID_ANLG_2_UNIVERSAL)

#define ADMW1001_CHANNEL_IS_ADC_SENSOR(c)                               \
    ((c) >= ADMW1001_CH_ID_ANLG_1_UNIVERSAL && (c) <= ADMW1001_CH_ID_ANLG_2_UNIVERSAL)

#define ADMW1001_CHANNEL_IS_ADC_VOLTAGE(c)                              \
    ((c) == ADMW1001_CH_ID_ANLG_1_DIFFERENTIAL || ADMW1001_CH_ID_ANLG_2_DIFFERENTIAL)

#define ADMW1001_CHANNEL_IS_ADC_CURRENT(c)                              \
    ((c) == ADMW1001_CH_ID_ANLG_1_UNIVERSAL || (c) == ADMW1001_CH_ID_ANLG_2_UNIVERSAL)


//typedef struct {
//    unsigned nDeviceIndex;
//    ADMW_SPI_HANDLE hSpi;
//    ADMW_GPIO_HANDLE hGpio;
//
//}   ADMW_DEVICE_CONTEXT;

static ADMW_DEVICE_CONTEXT gDeviceCtx[ADMW_PLATFORM_MAX_DEVICES];

/*
 * Open an ADMW device instance.
 */
ADMW_RESULT admw_Open(
    unsigned              const nDeviceIndex,
    ADMW_CONNECTION     * const pConnectionInfo,
    ADMW_DEVICE_HANDLE  * const phDevice)
{
    ADMW_DEVICE_CONTEXT *pCtx;
    ADMW_RESULT eRet;

    if (nDeviceIndex >= ADMW_PLATFORM_MAX_DEVICES)
        return ADMW_INVALID_DEVICE_NUM;

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

    eRet = admw_LogOpen(&pConnectionInfo->log);
    if (eRet != ADMW_SUCCESS)
        return eRet;

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

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

    *phDevice = pCtx;
    return ADMW_SUCCESS;
}

/*
 * Get the current state of the specified GPIO input signal.
 */
ADMW_RESULT admw_GetGpioState(
    ADMW_DEVICE_HANDLE   const hDevice,
    ADMW_GPIO_PIN        const ePinId,
    bool                  * const pbAsserted)
{
    ADMW_DEVICE_CONTEXT *pCtx = hDevice;

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

/*
 * Register an application-defined callback function for GPIO interrupts.
 */
ADMW_RESULT admw_RegisterGpioCallback(
    ADMW_DEVICE_HANDLE          const hDevice,
    ADMW_GPIO_PIN               const ePinId,
    ADMW_GPIO_CALLBACK          const callbackFunction,
    void                           * const pCallbackParam)
{
    ADMW_DEVICE_CONTEXT *pCtx = hDevice;

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

/*!
 * @brief Reset the specified ADMW device.
 *
 * @param[in]   hDevice  - handle of ADMW device to reset.
 *
 * @return Status
 *         - #ADMW_SUCCESS Call completed successfully.
 *         - #ADMW_FAILURE If reseet faisl
 *
 * @details Toggle reset pin of the ADMW device low for a
 *          minimum of 4 usec.
 *
 */
ADMW_RESULT admw_Reset(ADMW_DEVICE_HANDLE    const hDevice)
{
    ADMW_DEVICE_CONTEXT *pCtx = hDevice;
    ADMW_RESULT eRet;

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

    admw_TimeDelayUsec(4);

    eRet = admw_GpioSet(pCtx->hGpio, ADMW_GPIO_PIN_RESET, true);
    if (eRet != ADMW_SUCCESS)
        return eRet;

    return ADMW_SUCCESS;
}

/*!
 * @brief Get general status of ADMW module.
 *
 * @param[in]
 * @param[out] pStatus : Pointer to CORE Status struct.
 *
 * @return Status
 *         - #ADMW_SUCCESS Call completed successfully.
 *         - #ADMW_FAILURE If status register read fails.
 *
 * @details Read the general status register for the ADMW
 *          module. Indicates Error, Alert conditions, data ready
 *          and command running.
 *
 */
ADMW_RESULT admw_GetStatus(
    ADMW_DEVICE_HANDLE    const hDevice,
    ADMW_STATUS         * const pStatus)
{
    ADMW_CORE_Status_t statusReg;
    READ_REG_U8(hDevice, statusReg.VALUE8, CORE_STATUS);

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

    if (!statusReg.Cmd_Running) /* Active-low, so invert it */
        pStatus->deviceStatus |= ADMW_DEVICE_STATUS_BUSY;
    else if (statusReg.Drdy)
        pStatus->deviceStatus |= ADMW_DEVICE_STATUS_DATAREADY;
    else if(statusReg.FIFO_Error == ADMW_DEVICE_STATUS_FIFO_ERROR)
        pStatus->deviceStatus |= ADMW_DEVICE_STATUS_FIFO_ERROR;
    if (statusReg.Alert_Active) {
        pStatus->deviceStatus |= ADMW_DEVICE_STATUS_ALERT;

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

        for (unsigned i = 0; i < ADMW1001_MAX_CHANNELS; i++) {
            if (channelAlertStatusReg.VALUE16 & (1 << i)) {
                ADMW_CORE_Alert_Detail_Ch_t alertDetailReg;
                READ_REG_U16(hDevice, alertDetailReg.VALUE16,
                             CORE_ALERT_DETAIL_CHn(i));

                if (alertDetailReg.ADC_Near_Overrange)
                    pStatus->channelAlerts[i] |= ADMW_ALERT_DETAIL_CH_ADC_NEAR_OVERRANGE;
                if (alertDetailReg.Sensor_UnderRange)
                    pStatus->channelAlerts[i] |= ADMW_ALERT_DETAIL_CH_SENSOR_UNDERRANGE;
                if (alertDetailReg.Sensor_OverRange)
                    pStatus->channelAlerts[i] |= ADMW_ALERT_DETAIL_CH_SENSOR_OVERRANGE ;
                if (alertDetailReg.CJ_Soft_Fault)
                    pStatus->channelAlerts[i] |= ADMW_ALERT_DETAIL_CH_CJ_SOFT_FAULT  ;
                if (alertDetailReg.CJ_Hard_Fault)
                    pStatus->channelAlerts[i] |= ADMW_ALERT_DETAIL_CH_CJ_HARD_FAULT;
                if (alertDetailReg.ADC_Input_OverRange)
                    pStatus->channelAlerts[i] |= ADMW_ALERT_DETAIL_CH_ADC_INPUT_OVERRANGE ;
                if (alertDetailReg.Sensor_HardFault)
                    pStatus->channelAlerts[i] |= ADMW_ALERT_DETAIL_CH_SENSOR_HARDFAULT;

            }
        }

        if (statusReg.Configuration_Error)
            pStatus->deviceStatus |= ADMW_DEVICE_STATUS_CONFIG_ERROR;
        if (statusReg.LUT_Error)
            pStatus->deviceStatus |= ADMW_DEVICE_STATUS_LUT_ERROR;
    }

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

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

    }
    return ADMW_SUCCESS;
}

ADMW_RESULT admw_GetCommandRunningState(
    ADMW_DEVICE_HANDLE hDevice,
    bool *pbCommandRunning)
{
    ADMW_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 ADMW_ERR_NOT_INITIALIZED;

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

    return ADMW_SUCCESS;
}

ADMW_RESULT admw_deviceInformation(ADMW_DEVICE_HANDLE hDevice)
{
    uint16_t nAddress = REG_CORE_REVISION;
    char nData[ADMW_VERSION_REG_VAL_SIZE];  //4 Bytes of version register data
    ADMW_RESULT res;
    res=admw1001_ReadRegister(hDevice,nAddress,nData,sizeof(nData));
    if(res != ADMW_SUCCESS) {
        //if reading version register failed, sending 00.00.0000 as ADMW1001 firmware version
        //strcat(nData, ADMW1001_FIRMWARE_VERSION_DEFAULT);
        ADMW_LOG_INFO("Firmware Version Id is %X.%X",nData[2],nData[0]);
    } else {
        char buffer[ADMW_FORMATTED_VERSION_SIZE]; //00.00.0000  8 digits + 2 Bytes "." + one null character at the end
        strcat(nData, buffer);
        ADMW_LOG_INFO("Firmware Version Id is %X.%X.%X",nData[3],nData[2],nData[0]);
    }
    return ADMW_SUCCESS;
}

static ADMW_RESULT executeCommand(
    ADMW_DEVICE_HANDLE const hDevice,
    ADMW_CORE_Command_Special_Command const command,
    bool const bWaitForCompletion)
{
    ADMW_CORE_Command_t commandReg;
    bool bCommandRunning;
    ADMW_RESULT eRet;

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

        if (bCommandRunning)
            return ADMW_IN_USE;
    }

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

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

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

    return ADMW_SUCCESS;
}

ADMW_RESULT admw_ApplyConfigUpdates(
    ADMW_DEVICE_HANDLE const hDevice)
{
    return executeCommand(hDevice, CORE_COMMAND_LATCH_CONFIG, true);
}

/*!
 * @brief Start a measurement cycle.
 *
 * @param[out]
 *
 * @return Status
 *         - #ADMW_SUCCESS Call completed successfully.
 *         - #ADMW_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.
 *
 */
ADMW_RESULT admw_StartMeasurement(
    ADMW_DEVICE_HANDLE    const hDevice,
    ADMW_MEASUREMENT_MODE const eMeasurementMode)
{
    switch (eMeasurementMode) {
        case ADMW_MEASUREMENT_MODE_NORMAL:
            return executeCommand(hDevice, CORE_COMMAND_CONVERT_WITH_RAW, false);
        case ADMW_MEASUREMENT_MODE_OMIT_RAW:
            return executeCommand(hDevice, CORE_COMMAND_CONVERT, false);
        default:
            ADMW_LOG_ERROR("Invalid measurement mode %d specified",
                           eMeasurementMode);
            return ADMW_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.
 */
ADMW_RESULT admw_SaveConfig(
    ADMW_DEVICE_HANDLE    const hDevice,
    ADMW_USER_CONFIG_SLOT const eSlotId)
{
    switch (eSlotId) {
        case ADMW_FLASH_CONFIG_1:
            return executeCommand(hDevice, CORE_COMMAND_SAVE_CONFIG_1, true);
        default:
            ADMW_LOG_ERROR("Invalid user config target slot %d specified",
                           eSlotId);
            return ADMW_INVALID_PARAM;
    }
}

/*
 * Restore the configuration settings from persistent memory on the device.
 * No other command must be running when this is called.
 */
ADMW_RESULT admw_RestoreConfig(
    ADMW_DEVICE_HANDLE    const hDevice,
    ADMW_USER_CONFIG_SLOT const eSlotId)
{
    switch (eSlotId) {
        case ADMW_FLASH_CONFIG_1:
            return executeCommand(hDevice, CORE_COMMAND_LOAD_CONFIG_1, true);
        default:
            ADMW_LOG_ERROR("Invalid user config source slot %d specified",
                           eSlotId);
            return ADMW_INVALID_PARAM;
    }
}

/*
 * 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.
 */
ADMW_RESULT admw_SaveLutData(
    ADMW_DEVICE_HANDLE    const hDevice)
{
    return executeCommand(hDevice, 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.
 */
ADMW_RESULT admw_RestoreLutData(
    ADMW_DEVICE_HANDLE    const hDevice)
{
    return executeCommand(hDevice, CORE_COMMAND_LOAD_LUT, true);
}

/*
 * Stop the measurement cycles on the device.
 * To be used only if a measurement command is currently running.
 */
ADMW_RESULT admw_StopMeasurement(
    ADMW_DEVICE_HANDLE    const hDevice)
{
    return executeCommand(hDevice, CORE_COMMAND_NOP, true);
}

/*
 *
 */
ADMW_RESULT admw1001_sendRun( ADMW_DEVICE_HANDLE   const hDevice)
{
    bool bitCommand;
    ADMW_RESULT eRet;
    uint8_t pinreg = 0x1;

    ADMW_DEVICE_CONTEXT *pCtx = hDevice;
    static uint8_t DataBuffer[SPI_BUFFER_SIZE] = {0};
    uint16_t nSize;

    //Construct Read Status command
    DataBuffer[0] = 0x07;
    DataBuffer[1] = 0x0E;   //Packet ID

    DataBuffer[2] = 0x00;
    DataBuffer[3] = 0x00;   //Data words

    DataBuffer[4] = 0x45;
    DataBuffer[5] = 0x00;   //Command ID

    DataBuffer[6] = 0x00;
    DataBuffer[7] = 0x50;
    DataBuffer[8] = 0x00;
    DataBuffer[9] = 0x00;   //Address

    DataBuffer[10] = 0x95;
    DataBuffer[11] = 0x00;
    DataBuffer[12] = 0x00;
    DataBuffer[13] = 0x00;  //Checksum

    nSize = SFL_READ_STATUS_HDR_SIZE;

    do {
        // Get the SFL command irq pin to check if SFL is ready to receive commands
        // Status pin is not checked since SFL is just booted, there should not be any issue with SFL
        eRet = admw_GetGpioState( hDevice, ADMW_GPIO_PIN_DATAREADY, &bitCommand );
        if( eRet != ADMW_SUCCESS) {
            return eRet;
        }

        // Command IRQ pin should be low and Status IRQ pin should be high for SFL to be in good state and ready to recieve commands
        // pinreg == '0x00' - Error occured in SFL
        // pinreg == '0x01' - SFL is ready to recieve commands
        // pinreg == '0x02' - Error occured in handling any commands in SFL
        // pinreg == '0x03' - SFL not booted

        pinreg = (bitCommand);

    } while(pinreg != 0x0u);

    eRet = admw_SpiTransfer(pCtx->hSpi, DataBuffer, NULL,
                            nSize, false);

    return eRet;
}

/*
 * Read a set of data samples from the device.
 * This may be called at any time.
 */

ADMW_RESULT admw_GetData(
    ADMW_DEVICE_HANDLE    const hDevice,
    ADMW_MEASUREMENT_MODE const eMeasurementMode,
    ADMW_DATA_SAMPLE    * const pSamples,
    uint8_t                    const nBytesPerSample,
    uint32_t                   const nRequested,
    uint32_t                 * const pnReturned)
{
    ADMW1001_Sensor_Result_t sensorResult;
    ADMW_DEVICE_CONTEXT *pCtx = hDevice;
    uint16_t command = ADMW1001_HOST_COMMS_READ_CMD |
                       (REG_CORE_DATA_FIFO & ADMW1001_HOST_COMMS_ADR_MASK);
    uint8_t commandData[2] = {
        command >> 8,
        command & 0xFF
    };
    uint8_t commandResponse[2];
    unsigned nValidSamples = 0;
    ADMW_RESULT eRet = ADMW_SUCCESS;

    do {
        eRet = admw_SpiTransfer(pCtx->hSpi, commandData, commandResponse,
                                sizeof(command), false);
        if (eRet) {
            ADMW_LOG_ERROR("Failed to send read command for FIFO register");
            return eRet;
        }
        admw_TimeDelayUsec(ADMW1001_HOST_COMMS_XFER_DELAY);
    } while ((commandResponse[0] != ADMW1001_HOST_COMMS_CMD_RESP_0) ||
             (commandResponse[1] != ADMW1001_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 = admw_SpiTransfer(pCtx->hSpi, NULL, &sensorResult,
                                nBytesPerSample, bHoldCs);
        if (eRet) {
            ADMW_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 = ADMW_INCOMPLETE;
            break;
        }

        ADMW_DATA_SAMPLE *pSample = &pSamples[nValidSamples];
        memcpy(pSample,&sensorResult,sizeof(sensorResult));
        nValidSamples++;

        admw_TimeDelayUsec(ADMW1001_HOST_COMMS_XFER_DELAY);
    }
    *pnReturned = nValidSamples;

    return eRet;
}

/*
 * Close the given ADMW device.
 */
ADMW_RESULT admw_Close(
    ADMW_DEVICE_HANDLE    const hDevice)
{
    ADMW_DEVICE_CONTEXT *pCtx = hDevice;

    admw_GpioClose(pCtx->hGpio);
    admw_SpiClose(pCtx->hSpi);
    admw_LogClose();

    return ADMW_SUCCESS;
}

ADMW_RESULT admw1001_WriteRegister(
    ADMW_DEVICE_HANDLE hDevice,
    uint16_t nAddress,
    void *pData,
    unsigned nLength)
{
    ADMW_RESULT eRet;
    ADMW_DEVICE_CONTEXT *pCtx = hDevice;
    uint16_t command = ADMW1001_HOST_COMMS_WRITE_CMD |
                       (nAddress & ADMW1001_HOST_COMMS_ADR_MASK);
    uint8_t commandData[2] = {
        command >> 8,
        command & 0xFF
    };
    uint8_t commandResponse[2];

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

        admw_TimeDelayUsec(ADMW1001_HOST_COMMS_XFER_DELAY);
    } while ((commandResponse[0] != ADMW1001_HOST_COMMS_CMD_RESP_0) ||
             (commandResponse[1] != ADMW1001_HOST_COMMS_CMD_RESP_1));

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

    admw_TimeDelayUsec(ADMW1001_HOST_COMMS_XFER_DELAY);

    return ADMW_SUCCESS;
}

ADMW_RESULT admw_GetDeviceReadyState(
    ADMW_DEVICE_HANDLE   const hDevice,
    bool                  * const bReady)
{
    ADMW_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_SPI_CHIP_TYPE_RESET);

    return ADMW_SUCCESS;
}

ADMW_RESULT admw1001_ReadRegister(
    ADMW_DEVICE_HANDLE hDevice,
    uint16_t nAddress,
    void *pData,
    unsigned nLength)
{
    ADMW_RESULT eRet;
    ADMW_DEVICE_CONTEXT *pCtx = hDevice;
    uint16_t command = ADMW1001_HOST_COMMS_READ_CMD |
                       (nAddress & ADMW1001_HOST_COMMS_ADR_MASK);
    uint8_t commandData[2] = {
        command >> 8,
        command & 0xFF
    };
    uint8_t commandResponse[2];

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

        admw_TimeDelayUsec(ADMW1001_HOST_COMMS_XFER_DELAY);
    } while ((commandResponse[0] != ADMW1001_HOST_COMMS_CMD_RESP_0) ||
             (commandResponse[1] != ADMW1001_HOST_COMMS_CMD_RESP_1));

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

    admw_TimeDelayUsec(ADMW1001_HOST_COMMS_XFER_DELAY);

    return ADMW_SUCCESS;
}

ADMW_RESULT admw1001_GetDataReadyModeInfo(
    ADMW_DEVICE_HANDLE        const hDevice,
    ADMW_MEASUREMENT_MODE     const eMeasurementMode,
    ADMW1001_OPERATING_MODE * const peOperatingMode,
    ADMW1001_DATAREADY_MODE * const peDataReadyMode,
    uint32_t                * const pnSamplesPerDataready,
    uint32_t                * const pnSamplesPerCycle,
    uint8_t                 * const pnBytesPerSample)
{
    unsigned nChannelsEnabled = 0;
    unsigned nSamplesPerCycle = 0;

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

    if (modeReg.Conversion_Mode == CORE_MODE_SINGLECYCLE)
        *peOperatingMode = ADMW1001_OPERATING_MODE_SINGLECYCLE;
    else
        *peOperatingMode = ADMW1001_OPERATING_MODE_CONTINUOUS;

    if (eMeasurementMode == ADMW_MEASUREMENT_MODE_OMIT_RAW) {
        *pnBytesPerSample = 8;
    } else {
        *pnBytesPerSample = 12;
    }

    for (ADMW1001_CH_ID chId = ADMW1001_CH_ID_ANLG_1_UNIVERSAL;
            chId < ADMW1001_MAX_CHANNELS;
            chId++) {
        ADMW_CORE_Sensor_Details_t sensorDetailsReg;
        ADMW_CORE_Channel_Count_t channelCountReg;


        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) {
            unsigned nActualChannels = 1;
            
            nChannelsEnabled += nActualChannels;

            nSamplesPerCycle += nActualChannels *
                                (channelCountReg.Channel_Count + 1);
        }
    }

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

    *pnSamplesPerCycle = nSamplesPerCycle;

    if (modeReg.Drdy_Mode == CORE_MODE_DRDY_PER_CONVERSION) {
        *pnSamplesPerDataready = 1;
    } else if (modeReg.Drdy_Mode == CORE_MODE_DRDY_PER_CYCLE) {
        *pnSamplesPerDataready = nSamplesPerCycle;
    } else if (modeReg.Drdy_Mode == CORE_MODE_DRDY_PER_FIFO_FILL) {
        ADMW_CORE_Fifo_Num_Cycles_t fifoNumCyclesReg;

        READ_REG_U8(hDevice, fifoNumCyclesReg.VALUE8, CORE_FIFO_NUM_CYCLES);

        *pnSamplesPerDataready = nSamplesPerCycle * fifoNumCyclesReg.Fifo_Num_Cycles;
    } else {
        ADMW_LOG_ERROR("Invalid DRDY mode %d specified",
                       modeReg.Drdy_Mode);
        return ADMW_INVALID_PARAM;
    }

    if (modeReg.Drdy_Mode == CORE_MODE_DRDY_PER_CONVERSION) {
        *peDataReadyMode = ADMW1001_DATAREADY_PER_CONVERSION;
    } else if (modeReg.Drdy_Mode == CORE_MODE_DRDY_PER_CYCLE) {
        *peDataReadyMode = ADMW1001_DATAREADY_PER_CYCLE;
    } else if (modeReg.Drdy_Mode == CORE_MODE_DRDY_PER_FIFO_FILL) {
        *peDataReadyMode = ADMW1001_DATAREADY_PER_FIFO_FILL;
    } else {
        ADMW_LOG_ERROR("Invalid DRDY mode %d specified",
                       modeReg.Drdy_Mode);
        return ADMW_INVALID_PARAM;
    }

    return ADMW_SUCCESS;
}

ADMW_RESULT admw_GetProductID(
    ADMW_DEVICE_HANDLE hDevice,
    ADMW_PRODUCT_ID *pProductId)
{
    ADMW_SPI_Product_ID_L_t productIdLoReg;
    ADMW_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 = (ADMW_PRODUCT_ID)((productIdHiReg.VALUE8 << 8) |
                                    productIdLoReg.VALUE8);
    return ADMW_SUCCESS;
}

static ADMW_RESULT admw_SetPowerMode(
    ADMW_DEVICE_HANDLE hDevice,
    ADMW1001_POWER_MODE powerMode)
{
    ADMW_CORE_Power_Config_t powerConfigReg = { 0 };

    if (powerMode == ADMW1001_POWER_MODE_HIBERNATION) {
        powerConfigReg.Power_Mode_MCU = CORE_POWER_CONFIG_HIBERNATION;
    } else if (powerMode == ADMW1001_POWER_MODE_ACTIVE) {
        powerConfigReg.Power_Mode_MCU = CORE_POWER_CONFIG_ACTIVE_MODE;
    } else {
        ADMW_LOG_ERROR("Invalid power mode %d specified", powerMode);
        return ADMW_INVALID_PARAM;
    }

    WRITE_REG_U8(hDevice, powerConfigReg.VALUE8, CORE_POWER_CONFIG);

    return ADMW_SUCCESS;
}

ADMW_RESULT admw1001_SetPowerConfig(
    ADMW_DEVICE_HANDLE hDevice,
    ADMW1001_POWER_CONFIG *pPowerConfig)
{
    ADMW_RESULT eRet;

    eRet = admw_SetPowerMode(hDevice, pPowerConfig->powerMode);
    if (eRet != ADMW_SUCCESS) {
        ADMW_LOG_ERROR("Failed to set power mode");
        return eRet;
    }

    return ADMW_SUCCESS;
}

static ADMW_RESULT admw_SetRSenseValue(
    ADMW_DEVICE_HANDLE hDevice,
    float32_t RSenseValue)
{
    ADMW_CORE_External_Reference_Resistor_t RefResistorConfigReg;

    RefResistorConfigReg.Ext_Refin1_Value = RSenseValue;

    WRITE_REG_FLOAT(hDevice, RefResistorConfigReg.VALUE32, CORE_EXTERNAL_REFERENCE_RESISTOR);

    return ADMW_SUCCESS;

}
static ADMW_RESULT admw_SetMode(
    ADMW_DEVICE_HANDLE hDevice,
    ADMW1001_OPERATING_MODE eOperatingMode,
    ADMW1001_DATAREADY_MODE eDataReadyMode)
{
    ADMW_CORE_Mode_t modeReg;

    modeReg.VALUE8 = REG_RESET_VAL(CORE_MODE);

    if (eOperatingMode == ADMW1001_OPERATING_MODE_SINGLECYCLE) {
        modeReg.Conversion_Mode = CORE_MODE_SINGLECYCLE;
    } else if (eOperatingMode == ADMW1001_OPERATING_MODE_CONTINUOUS) {
        modeReg.Conversion_Mode = CORE_MODE_CONTINUOUS;
    } else {
        ADMW_LOG_ERROR("Invalid operating mode %d specified",
                       eOperatingMode);
        return ADMW_INVALID_PARAM;
    }

    if (eDataReadyMode == ADMW1001_DATAREADY_PER_CONVERSION) {
        modeReg.Drdy_Mode = CORE_MODE_DRDY_PER_CONVERSION;
    } else if (eDataReadyMode == ADMW1001_DATAREADY_PER_CYCLE) {
        modeReg.Drdy_Mode = CORE_MODE_DRDY_PER_CYCLE;
    } else if (eDataReadyMode == ADMW1001_DATAREADY_PER_FIFO_FILL) {
        modeReg.Drdy_Mode = CORE_MODE_DRDY_PER_FIFO_FILL;
    } else {
        ADMW_LOG_ERROR("Invalid data-ready mode %d specified", eDataReadyMode);
        return ADMW_INVALID_PARAM;
    }

    WRITE_REG_U8(hDevice, modeReg.VALUE8, CORE_MODE);

    return ADMW_SUCCESS;
}

ADMW_RESULT admw_SetCycleControl(ADMW_DEVICE_HANDLE hDevice,
                                 uint32_t           nCycleInterval,
                                 bool               vBiasEnable,
                                 bool           vPostExecCurrentState,
                                 bool               vGroundSwitch)
{
    ADMW_CORE_Cycle_Control_t cycleControlReg;

    cycleControlReg.VALUE16 = REG_RESET_VAL(CORE_CYCLE_CONTROL);

    if (nCycleInterval < (1 << 12)) {
        cycleControlReg.Cycle_Time_Units = CORE_CYCLE_CONTROL_SECONDS;
    } else {
        ADMW_LOG_ERROR("Invalid nCycleInterval %d specified", nCycleInterval);
        return ADMW_INVALID_PARAM;
    }

    if (vBiasEnable == true) {
        cycleControlReg.Vbias = 1;
    }
    CHECK_REG_FIELD_VAL(CORE_CYCLE_CONTROL_CYCLE_TIME, nCycleInterval);
    cycleControlReg.Cycle_Time = nCycleInterval;

    switch(vPostExecCurrentState) {
        case ADMW1001_ADC_EXC_STATE_CYCLE_POWER:
            cycleControlReg.PST_MEAS_EXC_CTRL = CORE_CYCLE_CONTROL_POWERCYCLE;
            break;
        case ADMW1001_ADC_EXC_STATE_ALWAYS_ON:
            cycleControlReg.PST_MEAS_EXC_CTRL = CORE_CYCLE_CONTROL_ALWAYSON;
            break;
        default:
            ADMW_LOG_ERROR("Invalid Post measurement Excitation Current state %d specified",
                           vPostExecCurrentState);
            return ADMW_INVALID_PARAM;
    }

    switch(vGroundSwitch) {
        case ADMW1001_ADC_GND_SW_OPEN:
            cycleControlReg.GND_SW_CTRL = CORE_CYCLE_CONTROL_OPEN_SW;
            break;
        case ADMW1001_ADC_GND_SW_CLOSED:
            cycleControlReg.GND_SW_CTRL = CORE_CYCLE_CONTROL_CLOSE_SW;
            break;
        default:
            ADMW_LOG_ERROR("Invalid ground switch state %d specified",
                           vGroundSwitch);
            return ADMW_INVALID_PARAM;
    }

    WRITE_REG_U16(hDevice, cycleControlReg.VALUE16, CORE_CYCLE_CONTROL);

    return ADMW_SUCCESS;
}
static ADMW_RESULT admw_SetExternalReferenceVoltage(
    ADMW_DEVICE_HANDLE hDevice,
    float32_t externalRefVoltage)
{
    WRITE_REG_FLOAT(hDevice, externalRefVoltage, CORE_EXTERNAL_VOLTAGE_REFERENCE);

    return ADMW_SUCCESS;
}
static ADMW_RESULT admw_SetFifoNumCycles(
    ADMW_DEVICE_HANDLE hDevice,
    uint8_t fifoNumCycles)
{
    WRITE_REG_U8(hDevice, fifoNumCycles, CORE_FIFO_NUM_CYCLES);

    return ADMW_SUCCESS;
}

static ADMW_RESULT admw_SetExternalReferenceValues(
    ADMW_DEVICE_HANDLE hDevice,
    float32_t externalRef1Value)
{
    WRITE_REG_FLOAT(hDevice, externalRef1Value, CORE_EXTERNAL_REFERENCE_RESISTOR);

    return ADMW_SUCCESS;
}
static ADMW_RESULT admw_SetAVDDVoltage(
    ADMW_DEVICE_HANDLE hDevice,
    float32_t AVDDVoltage)
{

    WRITE_REG_FLOAT(hDevice, AVDDVoltage, CORE_AVDD_VOLTAGE);

    return ADMW_SUCCESS;
}

ADMW_RESULT admw1001_SetMeasurementConfig(
    ADMW_DEVICE_HANDLE hDevice,
    ADMW1001_MEASUREMENT_CONFIG *pMeasConfig)
{
    ADMW_RESULT eRet;

    eRet = admw_SetMode(hDevice,
                        pMeasConfig->operatingMode,
                        pMeasConfig->dataReadyMode);
    if (eRet != ADMW_SUCCESS) {
        ADMW_LOG_ERROR("Failed to set operating mode");
        return eRet;
    }

    eRet = admw_SetCycleControl(hDevice, pMeasConfig->cycleInterval,
                                pMeasConfig->vBiasEnable,
                                pMeasConfig->excitationState,
                                pMeasConfig->groundSwitch );
    if (eRet != ADMW_SUCCESS) {
        ADMW_LOG_ERROR("Failed to set cycle control");
        return eRet;
    }

    if (pMeasConfig->fifoNumCycles > 0) {
        eRet = admw_SetFifoNumCycles(hDevice,
                                     pMeasConfig->fifoNumCycles);
    }

    if (eRet != ADMW_SUCCESS) {
        ADMW_LOG_ERROR("Failed to set the FIFO number of cycles.");
        return eRet;
    }

    if(pMeasConfig->externalRef1Value > 0) {
        eRet = admw_SetExternalReferenceValues(hDevice,
                                               pMeasConfig->externalRef1Value);
    }

    if (eRet != ADMW_SUCCESS) {
        ADMW_LOG_ERROR("Failed to set external reference values");
        return eRet;
    }

    if((pMeasConfig->AVDDVoltage >= 3.0) && (pMeasConfig->AVDDVoltage <= 3.6)) {
        eRet = admw_SetAVDDVoltage(hDevice,
                                   pMeasConfig->AVDDVoltage);
    }

    if (eRet != ADMW_SUCCESS) {
        ADMW_LOG_ERROR("Failed to set AVDD Voltge");
        return eRet;
    }

    eRet = admw_SetRSenseValue(hDevice, pMeasConfig->RSenseValue);
    if (eRet != ADMW_SUCCESS) {
        ADMW_LOG_ERROR("Failed to set RSenseValue");
        return eRet;
    }

    eRet = admw_SetExternalReferenceVoltage(hDevice, pMeasConfig->externalRefVoltage);
    if (eRet != ADMW_SUCCESS) {
        ADMW_LOG_ERROR("Failed to set External reference Voltage");
        return eRet;
    }

    return ADMW_SUCCESS;
}
ADMW_RESULT admw1001_SetDiagnosticsConfig(
    ADMW_DEVICE_HANDLE hDevice,
    ADMW1001_DIAGNOSTICS_CONFIG *pDiagnosticsConfig)
{
    ADMW_CORE_Diagnostics_Control_t diagnosticsControlReg;

    diagnosticsControlReg.VALUE8 = REG_RESET_VAL(CORE_DIAGNOSTICS_CONTROL);

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

    if(pDiagnosticsConfig->osdFrequency <= 0x7F) {
        diagnosticsControlReg.Diag_OSD_Freq = pDiagnosticsConfig->osdFrequency;
    } else {
        ADMW_LOG_ERROR("Invalid open-sensor diagnostic frequency %d specified",
                       pDiagnosticsConfig->osdFrequency);
        return ADMW_INVALID_PARAM;
    }
    WRITE_REG_U8(hDevice, diagnosticsControlReg.VALUE8, CORE_DIAGNOSTICS_CONTROL);

    return ADMW_SUCCESS;
}

uint16_t totalChannelCount(ADMW_DEVICE_HANDLE hDevice,ADMW1001_CHANNEL_CONFIG*pChannelConfig)
{
    uint16_t channelCount=0;
    channelCount=pChannelConfig->measurementsPerCycle;
    return channelCount;
}

ADMW_RESULT admw1001_SetChannelCount(
    ADMW_DEVICE_HANDLE hDevice,
    ADMW1001_CH_ID eChannelId,
    uint32_t nMeasurementsPerCycle)
{
    ADMW_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 ADMW_SUCCESS;
}

ADMW_RESULT admw1001_SetChannelOptions(
    ADMW_DEVICE_HANDLE hDevice,
    ADMW1001_CH_ID eChannelId,
    ADMW1001_CHANNEL_PRIORITY ePriority)
{
    ADMW_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;

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

    return ADMW_SUCCESS;
}

ADMW_RESULT admw1001_SetChannelSkipCount(
    ADMW_DEVICE_HANDLE hDevice,
    ADMW1001_CH_ID eChannelId,
    uint32_t nCycleSkipCount)
{
    ADMW_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 ADMW_SUCCESS;
}

static ADMW_RESULT admw_SetChannelAdcSensorType(
    ADMW_DEVICE_HANDLE          hDevice,
    ADMW1001_CH_ID              eChannelId,
    ADMW1001_ADC_SENSOR_TYPE    sensorType)
{
    ADMW_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 ADMW1001_ADC_SENSOR_RTD_2WIRE_PT10:

        case ADMW1001_ADC_SENSOR_RTD_2WIRE_PT50:
        case ADMW1001_ADC_SENSOR_RTD_2WIRE_PT100:
        case ADMW1001_ADC_SENSOR_RTD_2WIRE_PT200:
        case ADMW1001_ADC_SENSOR_RTD_2WIRE_PT500:
        case ADMW1001_ADC_SENSOR_RTD_2WIRE_PT1000:
        case ADMW1001_ADC_SENSOR_RTD_2WIRE_PT1000_0P00375:
        case ADMW1001_ADC_SENSOR_RTD_2WIRE_NI120:
        case ADMW1001_ADC_SENSOR_RTD_2WIRE_CUSTOM:
        case ADMW1001_ADC_SENSOR_RTD_4WIRE_PT10:

        case ADMW1001_ADC_SENSOR_RTD_4WIRE_PT50:
        case ADMW1001_ADC_SENSOR_RTD_4WIRE_PT100:
        case ADMW1001_ADC_SENSOR_RTD_4WIRE_PT200:
        case ADMW1001_ADC_SENSOR_RTD_4WIRE_PT500:
        case ADMW1001_ADC_SENSOR_RTD_4WIRE_PT1000:
        case ADMW1001_ADC_SENSOR_RTD_4WIRE_PT1000_0P00375:
        case ADMW1001_ADC_SENSOR_RTD_4WIRE_NI120:
        case ADMW1001_ADC_SENSOR_RTD_4WIRE_CUSTOM:
        case ADMW1001_ADC_SENSOR_RTD_3WIRE_PT10:

        case ADMW1001_ADC_SENSOR_RTD_3WIRE_PT50:
        case ADMW1001_ADC_SENSOR_RTD_3WIRE_PT100:
        case ADMW1001_ADC_SENSOR_RTD_3WIRE_PT200:
        case ADMW1001_ADC_SENSOR_RTD_3WIRE_PT500:
        case ADMW1001_ADC_SENSOR_RTD_3WIRE_PT1000:
        case ADMW1001_ADC_SENSOR_RTD_3WIRE_PT1000_0P00375 :

        case ADMW1001_ADC_SENSOR_RTD_3WIRE_NI120:
        case ADMW1001_ADC_SENSOR_RTD_3WIRE_CUSTOM:
        case ADMW1001_ADC_SENSOR_BRIDGE_4WIRE:
        case ADMW1001_ADC_SENSOR_BRIDGE_6WIRE:
        case ADMW1001_ADC_SENSOR_DIODE:
        case ADMW1001_ADC_SENSOR_THERMISTOR_44004_44033_2P252K_AT_25C:
        case ADMW1001_ADC_SENSOR_THERMISTOR_44005_44030_3K_AT_25C:
        case ADMW1001_ADC_SENSOR_THERMISTOR_44007_44034_5K_AT_25C:
        case ADMW1001_ADC_SENSOR_THERMISTOR_44006_44031_10K_AT_25C:
        case ADMW1001_ADC_SENSOR_THERMISTOR_44008_44032_30K_AT_25C:
        case ADMW1001_ADC_SENSOR_THERMISTOR_YSI_400:
        case ADMW1001_ADC_SENSOR_THERMISTOR_SPECTRUM_1003K_1K:
        case ADMW1001_ADC_SENSOR_THERMISTOR_CUSTOM_STEINHART_HART:
        case ADMW1001_ADC_SENSOR_THERMISTOR_CUSTOM_TABLE:
        case ADMW1001_ADC_SENSOR_SINGLE_ENDED_ABSOLUTE:
        case ADMW1001_ADC_SENSOR_DIFFERENTIAL_ABSOLUTE:
        case ADMW1001_ADC_SENSOR_SINGLE_ENDED_RATIO:
        case ADMW1001_ADC_SENSOR_DIFFERENTIAL_RATIO:

            if (! (ADMW1001_CHANNEL_IS_ADC_CJC(eChannelId) ||
                    ADMW1001_CHANNEL_IS_ADC(eChannelId) )) {
                ADMW_LOG_ERROR(
                    "Invalid ADC sensor type %d specified for channel %d",
                    sensorType, eChannelId);
                return ADMW_INVALID_PARAM;
            }
            break;
        case ADMW1001_ADC_SENSOR_THERMOCOUPLE_J:
        case ADMW1001_ADC_SENSOR_THERMOCOUPLE_K:
        case ADMW1001_ADC_SENSOR_THERMOCOUPLE_T:
        case ADMW1001_ADC_SENSOR_THERMOCOUPLE_E:
        case ADMW1001_ADC_SENSOR_THERMOCOUPLE_N:
        case ADMW1001_ADC_SENSOR_THERMOCOUPLE_R:
        case ADMW1001_ADC_SENSOR_THERMOCOUPLE_S:
        case ADMW1001_ADC_SENSOR_THERMOCOUPLE_B:
        case ADMW1001_ADC_SENSOR_THERMOCOUPLE_CUSTOM:
            if (! ADMW1001_CHANNEL_IS_ADC_VOLTAGE(eChannelId)) {
                ADMW_LOG_ERROR(
                    "Invalid ADC sensor type %d specified for channel %d",
                    sensorType, eChannelId);
                return ADMW_INVALID_PARAM;
            }
            break;
        default:
            ADMW_LOG_ERROR("Invalid/unsupported ADC sensor type %d specified",
                           sensorType);
            return ADMW_INVALID_PARAM;
    }

    sensorTypeReg.Sensor_Type = sensorType;

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

    return ADMW_SUCCESS;
}

static ADMW_RESULT admw_SetChannelAdcSensorDetails(
    ADMW_DEVICE_HANDLE      hDevice,
    ADMW1001_CH_ID          eChannelId,
    ADMW1001_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
 */
{
    ADMW1001_ADC_CHANNEL_CONFIG *pAdcChannelConfig = &pChannelConfig->adcChannelConfig;
    ADMW1001_ADC_REFERENCE_TYPE refType            = pAdcChannelConfig->reference;
    ADMW_CORE_Sensor_Details_t  sensorDetailsReg;

    sensorDetailsReg.VALUE32 = REG_RESET_VAL(CORE_SENSOR_DETAILSn);

    switch(pChannelConfig->measurementUnit) {
        case ADMW1001_MEASUREMENT_UNIT_FAHRENHEIT:
            sensorDetailsReg.Measurement_Units = CORE_SENSOR_DETAILS_UNITS_DEGF;
            break;
        case ADMW1001_MEASUREMENT_UNIT_CELSIUS:
            sensorDetailsReg.Measurement_Units = CORE_SENSOR_DETAILS_UNITS_DEGC;
            break;
        case ADMW1001_MEASUREMENT_UNIT_UNSPECIFIED:
            sensorDetailsReg.Measurement_Units = CORE_SENSOR_DETAILS_UNITS_UNSPECIFIED;
            break;
        default:
            ADMW_LOG_ERROR("Invalid measurement unit %d specified",
                           pChannelConfig->measurementUnit);
            return ADMW_INVALID_PARAM;
    }

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

    switch(refType) {
        case ADMW1001_ADC_REFERENCE_VOLTAGE_INTERNAL:
            sensorDetailsReg.Reference_Select = CORE_SENSOR_DETAILS_REF_VINT;
            break;
        case ADMW1001_ADC_REFERENCE_VOLTAGE_EXTERNAL_1:
            sensorDetailsReg.Reference_Select = CORE_SENSOR_DETAILS_REF_VEXT1;
            break;
        case ADMW1001_ADC_REFERENCE_VOLTAGE_AVDD:
            sensorDetailsReg.Reference_Select = CORE_SENSOR_DETAILS_REF_AVDD;
            break;
        default:
            ADMW_LOG_ERROR("Invalid ADC reference type %d specified", refType);
            return ADMW_INVALID_PARAM;
    }

    switch(pAdcChannelConfig->gain) {
        case ADMW1001_ADC_GAIN_1X:
            sensorDetailsReg.PGA_Gain = CORE_SENSOR_DETAILS_PGA_GAIN_1;
            break;
        case ADMW1001_ADC_GAIN_2X:
            sensorDetailsReg.PGA_Gain = CORE_SENSOR_DETAILS_PGA_GAIN_2;
            break;
        case ADMW1001_ADC_GAIN_4X:
            sensorDetailsReg.PGA_Gain = CORE_SENSOR_DETAILS_PGA_GAIN_4;
            break;
        case ADMW1001_ADC_GAIN_8X:
            sensorDetailsReg.PGA_Gain = CORE_SENSOR_DETAILS_PGA_GAIN_8;
            break;
        case ADMW1001_ADC_GAIN_16X:
            sensorDetailsReg.PGA_Gain = CORE_SENSOR_DETAILS_PGA_GAIN_16;
            break;
        case ADMW1001_ADC_GAIN_32X:
            sensorDetailsReg.PGA_Gain = CORE_SENSOR_DETAILS_PGA_GAIN_32;
            break;
        case ADMW1001_ADC_GAIN_64X:
            sensorDetailsReg.PGA_Gain = CORE_SENSOR_DETAILS_PGA_GAIN_64;
            break;
        default:
            ADMW_LOG_ERROR("Invalid ADC gain %d specified",
                           pAdcChannelConfig->gain);
            return ADMW_INVALID_PARAM;
    }

    switch(pAdcChannelConfig->rtdCurve) {
        case ADMW1001_ADC_RTD_CURVE_EUROPEAN:
            sensorDetailsReg.RTD_Curve = CORE_SENSOR_DETAILS_EUROPEAN_CURVE;
            break;
        case ADMW1001_ADC_RTD_CURVE_AMERICAN:
            sensorDetailsReg.RTD_Curve = CORE_SENSOR_DETAILS_AMERICAN_CURVE;
            break;
        case ADMW1001_ADC_RTD_CURVE_JAPANESE:
            sensorDetailsReg.RTD_Curve = CORE_SENSOR_DETAILS_JAPANESE_CURVE;
            break;
        case ADMW1001_ADC_RTD_CURVE_ITS90:
            sensorDetailsReg.RTD_Curve = CORE_SENSOR_DETAILS_ITS90_CURVE;
            break;
        default:
            ADMW_LOG_ERROR("Invalid RTD Curve %d specified",
                           pAdcChannelConfig->rtdCurve);
            return ADMW_INVALID_PARAM;
    }

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

    switch (pChannelConfig->lutSelect) {
        case ADMW1001_LUT_DEFAULT:
        case ADMW1001_LUT_CUSTOM:
            sensorDetailsReg.LUT_Select = pChannelConfig->lutSelect;
            break;
        default:
            ADMW_LOG_ERROR("Invalid LUT selection %d specified",
                           pChannelConfig->lutSelect);
            return ADMW_INVALID_PARAM;
    }

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

    return ADMW_SUCCESS;
}

static ADMW_RESULT admw_SetChannelAdcMeasurementSetup(
    ADMW_DEVICE_HANDLE hDevice,
    ADMW1001_CH_ID eChannelId,
    ADMW1001_ADC_CHANNEL_CONFIG *pAdcChannelConfig)
{
    ADMW_CORE_Measurement_Setup_t MeasSetupReg;
    ADMW1001_ADC_FILTER_CONFIG *pFilterConfig = &pAdcChannelConfig->filter;
    MeasSetupReg.VALUE32 = REG_RESET_VAL(CORE_MEASUREMENT_SETUPn);
    MeasSetupReg.Buffer_Bypass = pAdcChannelConfig->bufferBypass;

    if (pFilterConfig->type == ADMW1001_ADC_FILTER_SINC4) {
        MeasSetupReg.ADC_Filter_Type = CORE_MEASUREMENT_SETUP_ENABLE_SINC4;
        MeasSetupReg.ADC_SF = pFilterConfig->sf;
    } else if (pFilterConfig->type == ADMW1001_ADC_FILTER_SINC3) {
        MeasSetupReg.ADC_Filter_Type = CORE_MEASUREMENT_SETUP_ENABLE_SINC3;
        MeasSetupReg.ADC_SF = pFilterConfig->sf;
    } else {
        ADMW_LOG_ERROR("Invalid ADC filter type %d specified",
                       pFilterConfig->type);
        return ADMW_INVALID_PARAM;
    }

    /* chop mod ecan be 0 (none), 1 (HW, 2 (SW, 3 (HW+SW). */
    MeasSetupReg.Chop_Mode = pFilterConfig->chopMode;

    if(pFilterConfig->notch1p2)
        MeasSetupReg.NOTCH_EN_2 = 1;
    else
        MeasSetupReg.NOTCH_EN_2 = 0;

    WRITE_REG_U32(hDevice, MeasSetupReg.VALUE32, CORE_MEASUREMENT_SETUPn(eChannelId));

    return ADMW_SUCCESS;
}

static ADMW_RESULT admw_SetChannelAdcCurrentConfig(
    ADMW_DEVICE_HANDLE hDevice,
    ADMW1001_CH_ID eChannelId,
    ADMW1001_ADC_EXC_CURRENT_CONFIG *pCurrentConfig)
{
    ADMW_CORE_Channel_Excitation_t channelExcitationReg;

    channelExcitationReg.VALUE16 = REG_RESET_VAL(CORE_CHANNEL_EXCITATIONn);

    if (pCurrentConfig->outputLevel == ADMW1001_ADC_NO_EXTERNAL_EXC_CURRENT)
        channelExcitationReg.IOUT_Excitation_Current = CORE_CHANNEL_EXCITATION_NONE;
    else if (pCurrentConfig->outputLevel == ADMW1001_ADC_EXC_CURRENT_EXTERNAL)
        channelExcitationReg.IOUT_Excitation_Current = CORE_CHANNEL_EXCITATION_EXTERNAL;
    else if (pCurrentConfig->outputLevel == ADMW1001_ADC_EXC_CURRENT_50uA)
        channelExcitationReg.IOUT_Excitation_Current = CORE_CHANNEL_EXCITATION_IEXC_50UA;
    else if (pCurrentConfig->outputLevel == ADMW1001_ADC_EXC_CURRENT_100uA)
        channelExcitationReg.IOUT_Excitation_Current = CORE_CHANNEL_EXCITATION_IEXC_100UA;
    else if (pCurrentConfig->outputLevel == ADMW1001_ADC_EXC_CURRENT_250uA)
        channelExcitationReg.IOUT_Excitation_Current = CORE_CHANNEL_EXCITATION_IEXC_250UA;
    else if (pCurrentConfig->outputLevel == ADMW1001_ADC_EXC_CURRENT_500uA)
        channelExcitationReg.IOUT_Excitation_Current = CORE_CHANNEL_EXCITATION_IEXC_500UA;
    else if (pCurrentConfig->outputLevel == ADMW1001_ADC_EXC_CURRENT_1000uA)
        channelExcitationReg.IOUT_Excitation_Current = CORE_CHANNEL_EXCITATION_IEXC_1000UA;
    else {
        ADMW_LOG_ERROR("Invalid ADC excitation current %d specified",
                       pCurrentConfig->outputLevel);
        return ADMW_INVALID_PARAM;
    }

    WRITE_REG_U16(hDevice, channelExcitationReg.VALUE16, CORE_CHANNEL_EXCITATIONn(eChannelId));

    return ADMW_SUCCESS;
}

ADMW_RESULT admw_SetAdcChannelConfig(
    ADMW_DEVICE_HANDLE hDevice,
    ADMW1001_CH_ID eChannelId,
    ADMW1001_CHANNEL_CONFIG *pChannelConfig)
{
    ADMW_RESULT eRet;
    ADMW1001_ADC_CHANNEL_CONFIG *pAdcChannelConfig =
        &pChannelConfig->adcChannelConfig;

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

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

    eRet = admw_SetChannelAdcMeasurementSetup(hDevice, eChannelId,
            pAdcChannelConfig);
    if (eRet != ADMW_SUCCESS) {
        ADMW_LOG_ERROR("Failed to set ADC filter for channel %d",
                       eChannelId);
        return eRet;
    }

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

    return ADMW_SUCCESS;
}

static ADMW_RESULT admw_SetChannelDigitalSensorDetails(
    ADMW_DEVICE_HANDLE hDevice,
    ADMW1001_CH_ID eChannelId,
    ADMW1001_CHANNEL_CONFIG *pChannelConfig)
{
    ADMW_CORE_Sensor_Details_t sensorDetailsReg;

    sensorDetailsReg.VALUE32 = REG_RESET_VAL(CORE_SENSOR_DETAILSn);

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

    if (pChannelConfig->measurementUnit == ADMW1001_MEASUREMENT_UNIT_UNSPECIFIED) {
        sensorDetailsReg.Measurement_Units = CORE_SENSOR_DETAILS_UNITS_UNSPECIFIED;
    } else {
        ADMW_LOG_ERROR("Invalid measurement unit specified for digital channel");
        return ADMW_INVALID_PARAM;
    }

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

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

    return ADMW_SUCCESS;
}

static ADMW_RESULT admw_SetDigitalSensorFormat(
    ADMW_DEVICE_HANDLE hDevice,
    ADMW1001_CH_ID eChannelId,
    ADMW1001_DIGITAL_SENSOR_DATA_FORMAT *pDataFormat)
{
    ADMW_CORE_Digital_Sensor_Config_t sensorConfigReg;

    sensorConfigReg.VALUE16 = REG_RESET_VAL(CORE_DIGITAL_SENSOR_CONFIGn);

    if (pDataFormat->coding != ADMW1001_DIGITAL_SENSOR_DATA_CODING_NONE) {
        if (pDataFormat->frameLength == 0) {
            ADMW_LOG_ERROR("Invalid frame length specified for digital sensor data format");
            return ADMW_INVALID_PARAM;
        }
        if (pDataFormat->numDataBits == 0) {
            ADMW_LOG_ERROR("Invalid frame length specified for digital sensor data format");
            return ADMW_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 ADMW1001_DIGITAL_SENSOR_DATA_CODING_UNIPOLAR:
                sensorConfigReg.Digital_Sensor_Coding = CORE_DIGITAL_SENSOR_CONFIG_CODING_UNIPOLAR;
                break;
            case ADMW1001_DIGITAL_SENSOR_DATA_CODING_TWOS_COMPLEMENT:
                sensorConfigReg.Digital_Sensor_Coding = CORE_DIGITAL_SENSOR_CONFIG_CODING_TWOS_COMPL;
                break;
            case ADMW1001_DIGITAL_SENSOR_DATA_CODING_OFFSET_BINARY:
                sensorConfigReg.Digital_Sensor_Coding = CORE_DIGITAL_SENSOR_CONFIG_CODING_OFFSET_BINARY;
                break;
            default:
                ADMW_LOG_ERROR("Invalid coding specified for digital sensor data format");
                return ADMW_INVALID_PARAM;
        }
    } else {
        sensorConfigReg.Digital_Sensor_Coding = CORE_DIGITAL_SENSOR_CONFIG_CODING_NONE;
    }

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


    return ADMW_SUCCESS;
}


static ADMW_RESULT admw_SetChannelI2cSensorType(
    ADMW_DEVICE_HANDLE hDevice,
    ADMW1001_CH_ID eChannelId,
    ADMW1001_I2C_SENSOR_TYPE sensorType)
{
    ADMW_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 ADMW1001_I2C_SENSOR_HUMIDITY:
        case ADMW1001_I2C_SENSOR_TEMPERATURE_ADT742X:
            sensorTypeReg.Sensor_Type = sensorType;
            break;
        default:
            ADMW_LOG_ERROR("Unsupported I2C sensor type %d specified", sensorType);
            return ADMW_INVALID_PARAM;
    }

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

    return ADMW_SUCCESS;
}

static ADMW_RESULT admw_SetChannelI2cSensorAddress(
    ADMW_DEVICE_HANDLE hDevice,
    ADMW1001_CH_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 ADMW_SUCCESS;
}

static ADMW_RESULT admw_SetDigitalChannelComms(
    ADMW_DEVICE_HANDLE hDevice,
    ADMW1001_CH_ID eChannelId,
    ADMW1001_DIGITAL_SENSOR_COMMS *pDigitalComms)
{
    ADMW_CORE_Digital_Sensor_Comms_t digitalSensorComms;

    digitalSensorComms.VALUE16 = REG_RESET_VAL(CORE_DIGITAL_SENSOR_COMMSn);

    if(pDigitalComms->useCustomCommsConfig) {

        if(pDigitalComms->i2cClockSpeed == ADMW1001_DIGITAL_SENSOR_COMMS_I2C_CLOCK_SPEED_100K) {
            digitalSensorComms.I2C_Clock = CORE_DIGITAL_SENSOR_COMMS_I2C_100K;
        } else if(pDigitalComms->i2cClockSpeed == ADMW1001_DIGITAL_SENSOR_COMMS_I2C_CLOCK_SPEED_400K) {
            digitalSensorComms.I2C_Clock = CORE_DIGITAL_SENSOR_COMMS_I2C_400K;
        } else {
            ADMW_LOG_ERROR("Invalid I2C clock speed %d specified",
                           pDigitalComms->i2cClockSpeed);
            return ADMW_INVALID_PARAM;
        }
}
    WRITE_REG_U16(hDevice, digitalSensorComms.VALUE16, CORE_DIGITAL_SENSOR_COMMSn(eChannelId));

    return ADMW_SUCCESS;
}

ADMW_RESULT admw_SetI2cChannelConfig(
    ADMW_DEVICE_HANDLE hDevice,
    ADMW1001_CH_ID eChannelId,
    ADMW1001_CHANNEL_CONFIG *pChannelConfig)
{
    ADMW_RESULT eRet;
    ADMW1001_I2C_CHANNEL_CONFIG *pI2cChannelConfig =
        &pChannelConfig->i2cChannelConfig;

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

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

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

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


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

    return ADMW_SUCCESS;
}

static ADMW_RESULT admw_SetChannelSpiSensorType(
    ADMW_DEVICE_HANDLE hDevice,
    ADMW1001_CH_ID eChannelId,
    ADMW1001_SPI_SENSOR_TYPE sensorType)
{
    ADMW_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 ADMW1001_SPI_SENSOR:

            sensorTypeReg.Sensor_Type = sensorType;
            break;
        default:
            ADMW_LOG_ERROR("Unsupported SPI sensor type %d specified", sensorType);
            return ADMW_INVALID_PARAM;
    }

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

    return ADMW_SUCCESS;
}


ADMW_RESULT admw1001_SetChannelThresholdLimits(
    ADMW_DEVICE_HANDLE hDevice,
    ADMW1001_CH_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 ADMW_SUCCESS;
}

ADMW_RESULT admw1001_SetOffsetGain(
    ADMW_DEVICE_HANDLE hDevice,
    ADMW1001_CH_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 ADMW_SUCCESS;
}

ADMW_RESULT admw1001_SetSensorParameter(
    ADMW_DEVICE_HANDLE hDevice,
    ADMW1001_CH_ID eChannelId,
    float32_t fSensorParam)
{
    if (fSensorParam == 0.0f)
        fSensorParam = NAN;

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

    return ADMW_SUCCESS;
}

ADMW_RESULT admw1001_SetChannelSettlingTime(
    ADMW_DEVICE_HANDLE hDevice,
    ADMW1001_CH_ID eChannelId,
    uint32_t nSettlingTime)
{
    ADMW_CORE_Settling_Time_t settlingTimeReg;

    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 ADMW_SUCCESS;
}

ADMW_RESULT admw1001_SetChannelConfig(
    ADMW_DEVICE_HANDLE hDevice,
    ADMW1001_CH_ID eChannelId,
    ADMW1001_CHANNEL_CONFIG *pChannelConfig)
{
    ADMW_RESULT eRet;

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

        eRet = admw1001_SetChannelOptions(hDevice, eChannelId,
                                          pChannelConfig->priority);
        if (eRet != ADMW_SUCCESS) {
            ADMW_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) {
            eRet = admw1001_SetChannelSkipCount(hDevice, eChannelId,
                                                pChannelConfig->cycleSkipCount);
            if (eRet != ADMW_SUCCESS) {
                ADMW_LOG_ERROR("Failed to set cycle skip count for channel %d",
                               eChannelId);
                return eRet;
            }

            switch (eChannelId) {
                case ADMW1001_CH_ID_ANLG_1_UNIVERSAL:
                case ADMW1001_CH_ID_ANLG_2_UNIVERSAL:
                case ADMW1001_CH_ID_ANLG_1_DIFFERENTIAL:
                case ADMW1001_CH_ID_ANLG_2_DIFFERENTIAL:
                    eRet = admw_SetAdcChannelConfig(hDevice, eChannelId, pChannelConfig);
                    break;
                case ADMW1001_CH_ID_DIG_I2C_0:
                case ADMW1001_CH_ID_DIG_I2C_1:
                    eRet = admw_SetI2cChannelConfig(hDevice, eChannelId, pChannelConfig);
                    break;
                default:
                    ADMW_LOG_ERROR("Invalid channel ID %d specified", eChannelId);
                    eRet = ADMW_INVALID_PARAM;
#if 0
                    /* when using i2c sensors there is an error ( dataformat->length=0)
                    the code below catches this error and this causes further problems.*/
                    break;
            }
            if (eRet != ADMW_SUCCESS) {
                ADMW_LOG_ERROR("Failed to set config for channel %d",
                               eChannelId);
                return eRet;
#endif
            }

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

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

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

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

    return ADMW_SUCCESS;
}

ADMW_RESULT admw_SetConfig(
    ADMW_DEVICE_HANDLE    const hDevice,
    ADMW_CONFIG         * const pConfig)
{
    ADMW1001_CONFIG *pDeviceConfig;
    ADMW_PRODUCT_ID productId;
    ADMW_RESULT eRet;

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

    if (!((pConfig->versionId.major==VERSIONID_MAJOR) &&
            (pConfig->versionId.minor==VERSIONID_MINOR))) {
        ADMW_LOG_ERROR("Configuration Version ID (0x%X) is not supported",
                       pConfig->versionId);
        return ADMW_INVALID_PARAM;
    }


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

    pDeviceConfig = &pConfig->admw1001;

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

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

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

    for (ADMW1001_CH_ID id = ADMW1001_CH_ID_ANLG_1_UNIVERSAL;
            id < ADMW1001_MAX_CHANNELS;
            id++) {
        eRet = admw1001_SetChannelConfig(hDevice, id,
                                         &pDeviceConfig->channels[id]);
        if (eRet) {
            ADMW_LOG_ERROR("Failed to set channel %d configuration", id);
            return eRet;
        }
    }

    return ADMW_SUCCESS;
}


ADMW_RESULT admw1001_SetLutDataRaw(
    ADMW_DEVICE_HANDLE    const hDevice,
    ADMW1001_LUT_RAW   * const pLutData)
{
    return admw1001_SetLutData(hDevice,
                               (ADMW1001_LUT *)pLutData);
}

static ADMW_RESULT getLutTableSize(
    ADMW1001_LUT_DESCRIPTOR * const pDesc,
    ADMW1001_LUT_TABLE_DATA * const pData,
    unsigned *pLength)
{
    switch (pDesc->geometry) {
        case ADMW1001_LUT_GEOMETRY_NES_1D:
            *pLength = ADMW1001_LUT_1D_NES_SIZE(pData->lut1dNes);
            break;
        default:
            ADMW_LOG_ERROR("Invalid LUT table geometry %d specified\r\n",
                           pDesc->geometry);
            return ADMW_INVALID_PARAM;
    }

    return ADMW_SUCCESS;
}

ADMW_RESULT admw1001_AssembleLutData(
    ADMW1001_LUT                  * pLutBuffer,
    unsigned                              nLutBufferSize,
    unsigned                        const nNumTables,
    ADMW1001_LUT_DESCRIPTOR * const ppDesc[],
    ADMW1001_LUT_TABLE_DATA * const ppData[])
{
    ADMW1001_LUT_HEADER *pHdr = &pLutBuffer->header;
    uint8_t *pLutTableData = (uint8_t *)pLutBuffer + sizeof(*pHdr);

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

    /* First initialise the top-level header */
    pHdr->signature = ADMW_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++)
    {
        ADMW1001_LUT_DESCRIPTOR * const pDesc = ppDesc[i];
        ADMW1001_LUT_TABLE_DATA * const pData = ppData[i];
        ADMW_RESULT res;
        unsigned dataLength = 0;

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

        /* Fill in the table descriptor length and CRC fields */
        pDesc->length = dataLength;

        if ((sizeof(*pHdr) + pHdr->totalLength + sizeof(*pDesc) + dataLength) > nLutBufferSize)
        {
            ADMW_LOG_ERROR("Insufficient LUT buffer size provided");
            return ADMW_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 ADMW_SUCCESS;
}
ADMW_RESULT admw1001_SetLutData(
    ADMW_DEVICE_HANDLE   const hDevice,
    ADMW1001_LUT       * const pLutData)
{
#pragma diag_suppress=Pa039
    ADMW1001_LUT_HEADER *pLutHeader = &pLutData->header;
    ADMW1001_LUT_TABLE  *pLutTable = pLutData->tables;

    unsigned actualLength = 0;

    if (pLutData->header.signature != ADMW_LUT_SIGNATURE)
    {
        ADMW_LOG_ERROR("LUT signature incorrect (expected 0x%X, actual 0x%X)",
                            ADMW_LUT_SIGNATURE, pLutHeader->signature);
        return ADMW_INVALID_SIGNATURE;
    }
    if ((pLutData->tables->descriptor.geometry!= ADMW1001_LUT_GEOMETRY_NES_1D) &&
        (pLutData->tables->data.lut1dNes.nElements > MAX_LUT_NUM_ENTRIES))
    {
        return ADMW_INVALID_PARAM;
    }
    for (unsigned i = 0; i < pLutHeader->numTables; i++)
    {
        ADMW1001_LUT_DESCRIPTOR *pDesc = &pLutTable->descriptor;

        switch (pDesc->geometry)
        {
        case ADMW1001_LUT_GEOMETRY_COEFFS:
                switch (pDesc->equation)
                {
                case ADMW1001_LUT_EQUATION_POLYN:
                case ADMW1001_LUT_EQUATION_POLYNEXP:
                case ADMW1001_LUT_EQUATION_QUADRATIC:
                case ADMW1001_LUT_EQUATION_STEINHART:
                case ADMW1001_LUT_EQUATION_LOGARITHMIC:
                case ADMW1001_LUT_EQUATION_BIVARIATE_POLYN:
                break;
                default:
                    ADMW_LOG_ERROR("Invalid equation %u specified for LUT table %u",
                                        pDesc->equation, i);
                    return ADMW_INVALID_PARAM;
                }
            break;
        case ADMW1001_LUT_GEOMETRY_NES_1D:
            break;
        default:
            ADMW_LOG_ERROR("Invalid geometry %u specified for LUT table %u",
                                pDesc->geometry, i);
            return ADMW_INVALID_PARAM;
        }

        switch (pDesc->dataType)
        {
        case ADMW1001_LUT_DATA_TYPE_FLOAT32:
        case ADMW1001_LUT_DATA_TYPE_FLOAT64:
            break;
        default:
            ADMW_LOG_ERROR("Invalid vector format %u specified for LUT table %u",
                                pDesc->dataType, i);
            return ADMW_INVALID_PARAM;
        }


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

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

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

    if (sizeof(*pLutHeader) + pLutHeader->totalLength > ADMW_LUT_MAX_SIZE)
    {
        ADMW_LOG_ERROR("Maximum LUT table length (%u bytes) exceeded",
                            ADMW_LUT_MAX_SIZE);
        return ADMW_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 ADMW_SUCCESS;
}