![](/media/cache/img/default_profile.jpg.50x50_q85.jpg)
Minor changes to support ADMW FWv1.17.75
src/admw_1001.c
- Committer:
- Vkadaba
- Date:
- 2019-12-10
- Revision:
- 42:c9c5a22e539e
- Parent:
- 41:df78b7d7b675
- Child:
- 43:e1789b7214cf
File content as of revision 42:c9c5a22e539e:
/* 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 "crc16.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) #define ADMW1001_CHANNEL_IS_VIRTUAL(c) \ ((c) == ADMW1001_CH_ID_DIG_SPI_1 || (c) == ADMW1001_CH_ID_DIG_SPI_2) //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; if (statusReg.Drdy) pStatus->deviceStatus |= ADMW_DEVICE_STATUS_DATAREADY; if (statusReg.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) { 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 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 50usec delay for status update before checking */ admw_TimeDelayUsec(50); 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]; pSample->status = (ADMW_DEVICE_STATUS_FLAGS)0; if (sensorResult.Ch_Error) pSample->status |= ADMW_DEVICE_STATUS_ERROR; if (sensorResult.Ch_Alert) pSample->status |= ADMW_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++; 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 admw1001_Write_Debug_Register( 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_DEBUG_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 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_Read_Debug_Register( 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_DEBUG_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 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_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(eMeasurementMode==ADMW_MEASUREMENT_MODE_NORMAL) { 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 = 5; } else { *pnBytesPerSample = 8; } 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; if (ADMW1001_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) { unsigned nActualChannels = 1; if (chId == ADMW1001_CH_ID_DIG_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 0 /* SPI sensors arent supported at present to be added back once there is * support for these sensors */ ADMW_CORE_Sensor_Type_t sensorTypeReg; READ_REG_U16(hDevice, sensorTypeReg.VALUE16, CORE_SENSOR_TYPEn(chId)); if ((sensorTypeReg.Sensor_Type >= CORE_SENSOR_TYPE_SPI_ACCELEROMETER_A) && (sensorTypeReg.Sensor_Type <= CORE_SENSOR_TYPE_SPI_ACCELEROMETER_B)) { nActualChannels += 2; } #endif } 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 { *pnSamplesPerDataready = nSamplesPerCycle; } if (modeReg.Drdy_Mode == CORE_MODE_DRDY_PER_CONVERSION) { *peDataReadyMode = ADMW1001_DATAREADY_PER_CONVERSION; } else { *peDataReadyMode = ADMW1001_DATAREADY_PER_CYCLE; } 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; 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 { 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) { ADMW_CORE_Cycle_Control_t cycleControlReg; cycleControlReg.VALUE16 = REG_RESET_VAL(CORE_CYCLE_CONTROL); if (nCycleInterval < (1000 * (1 << 12))) { cycleControlReg.Cycle_Time_Units = CORE_CYCLE_CONTROL_MILLISECONDS; nCycleInterval /= 1000; } else { cycleControlReg.Cycle_Time_Units = CORE_CYCLE_CONTROL_SECONDS; nCycleInterval /= 1000000; } if (vBiasEnable == true) { cycleControlReg.Vbias = 1; } CHECK_REG_FIELD_VAL(CORE_CYCLE_CONTROL_CYCLE_TIME, nCycleInterval); cycleControlReg.Cycle_Time = nCycleInterval; 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_SetExternalReferenceValues( ADMW_DEVICE_HANDLE hDevice, float32_t externalRef1Value) { WRITE_REG_FLOAT(hDevice, externalRef1Value, CORE_EXTERNAL_REFERENCE_RESISTOR); 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); if (eRet != ADMW_SUCCESS) { ADMW_LOG_ERROR("Failed to set cycle control"); 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; } 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; switch (pDiagnosticsConfig->osdFrequency) { case ADMW1001_OPEN_SENSOR_DIAGNOSTICS_DISABLED: diagnosticsControlReg.Diag_OSD_Freq = CORE_DIAGNOSTICS_CONTROL_OCD_OFF; break; case ADMW1001_OPEN_SENSOR_DIAGNOSTICS_PER_CYCLE: diagnosticsControlReg.Diag_OSD_Freq = CORE_DIAGNOSTICS_CONTROL_OCD_PER_1_CYCLE; break; case ADMW1001_OPEN_SENSOR_DIAGNOSTICS_PER_100_CYCLES: diagnosticsControlReg.Diag_OSD_Freq = CORE_DIAGNOSTICS_CONTROL_OCD_PER_10_CYCLES; break; case ADMW1001_OPEN_SENSOR_DIAGNOSTICS_PER_1000_CYCLES: diagnosticsControlReg.Diag_OSD_Freq = CORE_DIAGNOSTICS_CONTROL_OCD_PER_100_CYCLES; break; default: 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; } 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_3WIRE_PT100: case ADMW1001_ADC_SENSOR_RTD_3WIRE_PT1000: case ADMW1001_ADC_SENSOR_RTD_3WIRE_1: case ADMW1001_ADC_SENSOR_RTD_3WIRE_2: case ADMW1001_ADC_SENSOR_RTD_3WIRE_3: case ADMW1001_ADC_SENSOR_RTD_3WIRE_4: case ADMW1001_ADC_SENSOR_RTD_4WIRE_PT100: case ADMW1001_ADC_SENSOR_RTD_4WIRE_PT1000: case ADMW1001_ADC_SENSOR_RTD_4WIRE_1: case ADMW1001_ADC_SENSOR_RTD_4WIRE_2: case ADMW1001_ADC_SENSOR_RTD_4WIRE_3: case ADMW1001_ADC_SENSOR_RTD_4WIRE_4: case ADMW1001_ADC_SENSOR_BRIDGE_4WIRE_1: case ADMW1001_ADC_SENSOR_BRIDGE_4WIRE_2: case ADMW1001_ADC_SENSOR_BRIDGE_4WIRE_3: case ADMW1001_ADC_SENSOR_BRIDGE_4WIRE_4: case ADMW1001_ADC_SENSOR_BRIDGE_6WIRE_1: case ADMW1001_ADC_SENSOR_BRIDGE_6WIRE_2: case ADMW1001_ADC_SENSOR_BRIDGE_6WIRE_3: case ADMW1001_ADC_SENSOR_BRIDGE_6WIRE_4: case ADMW1001_ADC_SENSOR_RTD_2WIRE_PT100: case ADMW1001_ADC_SENSOR_RTD_2WIRE_PT1000: case ADMW1001_ADC_SENSOR_RTD_2WIRE_1: case ADMW1001_ADC_SENSOR_RTD_2WIRE_2: case ADMW1001_ADC_SENSOR_RTD_2WIRE_3: case ADMW1001_ADC_SENSOR_RTD_2WIRE_4: case ADMW1001_ADC_SENSOR_DIODE_2C_TYPEA: case ADMW1001_ADC_SENSOR_DIODE_3C_TYPEA: case ADMW1001_ADC_SENSOR_DIODE_2C_1: case ADMW1001_ADC_SENSOR_DIODE_3C_1: case ADMW1001_ADC_SENSOR_THERMISTOR_A_10K: case ADMW1001_ADC_SENSOR_THERMISTOR_B_10K: case ADMW1001_ADC_SENSOR_THERMISTOR_1: case ADMW1001_ADC_SENSOR_THERMISTOR_2: case ADMW1001_ADC_SENSOR_THERMISTOR_3: case ADMW1001_ADC_SENSOR_THERMISTOR_4: 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_SENSOR(eChannelId) || 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_VOLTAGE: case ADMW1001_ADC_SENSOR_VOLTAGE_PRESSURE_A: case ADMW1001_ADC_SENSOR_VOLTAGE_PRESSURE_B: case ADMW1001_ADC_SENSOR_VOLTAGE_PRESSURE_1: case ADMW1001_ADC_SENSOR_VOLTAGE_PRESSURE_2: case ADMW1001_ADC_SENSOR_THERMOCOUPLE_J: case ADMW1001_ADC_SENSOR_THERMOCOUPLE_K: case ADMW1001_ADC_SENSOR_THERMOCOUPLE_T: case ADMW1001_ADC_SENSOR_THERMOCOUPLE_1: case ADMW1001_ADC_SENSOR_THERMOCOUPLE_2: case ADMW1001_ADC_SENSOR_THERMOCOUPLE_3: case ADMW1001_ADC_SENSOR_THERMOCOUPLE_4: 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; case ADMW1001_ADC_SENSOR_CURRENT: case ADMW1001_ADC_SENSOR_CURRENT_PRESSURE_A: case ADMW1001_ADC_SENSOR_CURRENT_PRESSURE_1: case ADMW1001_ADC_SENSOR_CURRENT_PRESSURE_2: if (! ADMW1001_CHANNEL_IS_ADC_CURRENT(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; case ADMW1001_ADC_GAIN_128X: sensorDetailsReg.PGA_Gain = CORE_SENSOR_DETAILS_PGA_GAIN_128; 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_UNITY: 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.PST_MEAS_EXC_CTRL = pAdcChannelConfig->current.excitationState; 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; switch(pFilterConfig->groundSwitch) { case ADMW1001_ADC_GND_SW_OPEN: MeasSetupReg.GND_SW = CORE_MEASUREMENT_SETUP_GND_SW_OPEN; break; case ADMW1001_ADC_GND_SW_CLOSED: MeasSetupReg.GND_SW = CORE_MEASUREMENT_SETUP_GND_SW_CLOSED; break; default: ADMW_LOG_ERROR("Invalid ground switch state %d specified", pFilterConfig->groundSwitch); return ADMW_INVALID_PARAM; } 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_SetDigitalCalibrationParam( ADMW_DEVICE_HANDLE hDevice, ADMW1001_CH_ID eChannelId, ADMW1001_DIGITAL_CALIBRATION_COMMAND *pCalibrationParam) { // ADMW_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 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_A: case ADMW1001_I2C_SENSOR_HUMIDITY_B: 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) { digitalSensorComms.Digital_Sensor_Comms_En = 1; 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; } if(pDigitalComms->spiMode == ADMW1001_DIGITAL_SENSOR_COMMS_SPI_MODE_0) { digitalSensorComms.SPI_Mode = CORE_DIGITAL_SENSOR_COMMS_SPI_MODE_0; } else if(pDigitalComms->spiMode == ADMW1001_DIGITAL_SENSOR_COMMS_SPI_MODE_1) { digitalSensorComms.SPI_Mode = CORE_DIGITAL_SENSOR_COMMS_SPI_MODE_1; } else if(pDigitalComms->spiMode == ADMW1001_DIGITAL_SENSOR_COMMS_SPI_MODE_2) { digitalSensorComms.SPI_Mode = CORE_DIGITAL_SENSOR_COMMS_SPI_MODE_2; } else if(pDigitalComms->spiMode == ADMW1001_DIGITAL_SENSOR_COMMS_SPI_MODE_3) { digitalSensorComms.SPI_Mode = CORE_DIGITAL_SENSOR_COMMS_SPI_MODE_3; } else { ADMW_LOG_ERROR("Invalid SPI mode %d specified", pDigitalComms->spiMode); return ADMW_INVALID_PARAM; } switch (pDigitalComms->spiClock) { case ADMW1001_DIGITAL_SENSOR_COMMS_SPI_CLOCK_8MHZ: digitalSensorComms.SPI_Clock = CORE_DIGITAL_SENSOR_COMMS_SPI_8MHZ; break; case ADMW1001_DIGITAL_SENSOR_COMMS_SPI_CLOCK_4MHZ: digitalSensorComms.SPI_Clock = CORE_DIGITAL_SENSOR_COMMS_SPI_4MHZ; break; case ADMW1001_DIGITAL_SENSOR_COMMS_SPI_CLOCK_2MHZ: digitalSensorComms.SPI_Clock = CORE_DIGITAL_SENSOR_COMMS_SPI_2MHZ; break; case ADMW1001_DIGITAL_SENSOR_COMMS_SPI_CLOCK_1MHZ: digitalSensorComms.SPI_Clock = CORE_DIGITAL_SENSOR_COMMS_SPI_1MHZ; break; case ADMW1001_DIGITAL_SENSOR_COMMS_SPI_CLOCK_500KHZ: digitalSensorComms.SPI_Clock = CORE_DIGITAL_SENSOR_COMMS_SPI_500KHZ; break; case ADMW1001_DIGITAL_SENSOR_COMMS_SPI_CLOCK_250KHZ: digitalSensorComms.SPI_Clock = CORE_DIGITAL_SENSOR_COMMS_SPI_250KHZ; break; case ADMW1001_DIGITAL_SENSOR_COMMS_SPI_CLOCK_125KHZ: digitalSensorComms.SPI_Clock = CORE_DIGITAL_SENSOR_COMMS_SPI_125KHZ; break; case ADMW1001_DIGITAL_SENSOR_COMMS_SPI_CLOCK_62P5KHZ: digitalSensorComms.SPI_Clock = CORE_DIGITAL_SENSOR_COMMS_SPI_62P5KHZ; break; case ADMW1001_DIGITAL_SENSOR_COMMS_SPI_CLOCK_31P3KHZ: digitalSensorComms.SPI_Clock = CORE_DIGITAL_SENSOR_COMMS_SPI_31P3KHZ; break; case ADMW1001_DIGITAL_SENSOR_COMMS_SPI_CLOCK_15P6KHZ: digitalSensorComms.SPI_Clock = CORE_DIGITAL_SENSOR_COMMS_SPI_15P6KHZ; break; case ADMW1001_DIGITAL_SENSOR_COMMS_SPI_CLOCK_7P8KHZ: digitalSensorComms.SPI_Clock = CORE_DIGITAL_SENSOR_COMMS_SPI_7P8KHZ; break; case ADMW1001_DIGITAL_SENSOR_COMMS_SPI_CLOCK_3P9KHZ: digitalSensorComms.SPI_Clock = CORE_DIGITAL_SENSOR_COMMS_SPI_3P9KHZ; break; case ADMW1001_DIGITAL_SENSOR_COMMS_SPI_CLOCK_1P9KHZ: digitalSensorComms.SPI_Clock = CORE_DIGITAL_SENSOR_COMMS_SPI_1P9KHZ; break; case ADMW1001_DIGITAL_SENSOR_COMMS_SPI_CLOCK_977HZ: digitalSensorComms.SPI_Clock = CORE_DIGITAL_SENSOR_COMMS_SPI_977HZ; break; case ADMW1001_DIGITAL_SENSOR_COMMS_SPI_CLOCK_488HZ: digitalSensorComms.SPI_Clock = CORE_DIGITAL_SENSOR_COMMS_SPI_488HZ; break; case ADMW1001_DIGITAL_SENSOR_COMMS_SPI_CLOCK_244HZ: digitalSensorComms.SPI_Clock = CORE_DIGITAL_SENSOR_COMMS_SPI_244HZ; break; default: ADMW_LOG_ERROR("Invalid SPI clock %d specified", pDigitalComms->spiClock); return ADMW_INVALID_PARAM; } } else { digitalSensorComms.Digital_Sensor_Comms_En = 0; } 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_SetDigitalCalibrationParam(hDevice, eChannelId, &pI2cChannelConfig->digitalCalibrationParam); if (eRet != ADMW_SUCCESS) { ADMW_LOG_ERROR("Failed to set I2C digital calibration param 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_PRESSURE_A: case ADMW1001_SPI_SENSOR_ACCELEROMETER_A: case ADMW1001_SPI_SENSOR_ACCELEROMETER_B: 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 admw_SetSpiChannelConfig( ADMW_DEVICE_HANDLE hDevice, ADMW1001_CH_ID eChannelId, ADMW1001_CHANNEL_CONFIG *pChannelConfig) { ADMW_RESULT eRet; ADMW1001_SPI_CHANNEL_CONFIG *pSpiChannelConfig = &pChannelConfig->spiChannelConfig; eRet = admw_SetChannelSpiSensorType(hDevice, eChannelId, pSpiChannelConfig->sensor); if (eRet != ADMW_SUCCESS) { ADMW_LOG_ERROR("Failed to set SPI sensor type for channel %d", eChannelId); return eRet; } eRet = admw_SetChannelDigitalSensorDetails(hDevice, eChannelId, pChannelConfig); if (eRet != ADMW_SUCCESS) { ADMW_LOG_ERROR("Failed to set SPI sensor details for channel %d", eChannelId); return eRet; } eRet = admw_SetDigitalSensorFormat(hDevice, eChannelId, &pSpiChannelConfig->dataFormat); if (eRet != ADMW_SUCCESS) { ADMW_LOG_ERROR("Failed to set SPI sensor data format for channel %d", eChannelId); return eRet; } eRet = admw_SetDigitalCalibrationParam(hDevice, eChannelId, &pSpiChannelConfig->digitalCalibrationParam); if (eRet != ADMW_SUCCESS) { ADMW_LOG_ERROR("Failed to set SPI digital calibration param for channel %d", eChannelId); return eRet; } eRet = admw_SetDigitalChannelComms(hDevice, eChannelId, &pSpiChannelConfig->configureComms); if (eRet != ADMW_SUCCESS) { ADMW_LOG_ERROR("Failed to set SPI comms for channel %d", eChannelId); return eRet; } 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; if (nSettlingTime < (1 << 12)) { settlingTimeReg.Settling_Time_Units = CORE_SETTLING_TIME_MICROSECONDS; } else if (nSettlingTime < (1000 * (1 << 12))) { settlingTimeReg.Settling_Time_Units = CORE_SETTLING_TIME_MILLISECONDS; nSettlingTime /= 1000; } else { settlingTimeReg.Settling_Time_Units = 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 ADMW_SUCCESS; } ADMW_RESULT admw1001_SetChannelConfig( ADMW_DEVICE_HANDLE hDevice, ADMW1001_CH_ID eChannelId, ADMW1001_CHANNEL_CONFIG *pChannelConfig) { ADMW_RESULT eRet; if (! ADMW1001_CHANNEL_IS_VIRTUAL(eChannelId)) { 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; case ADMW1001_CH_ID_DIG_SPI_0: eRet = admw_SetSpiChannelConfig(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_SetLutData( ADMW_DEVICE_HANDLE const hDevice, ADMW1001_LUT * const pLutData) { 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; } for (unsigned i = 0; i < pLutHeader->numTables; i++) { ADMW1001_LUT_DESCRIPTOR *pDesc = &pLutTable->descriptor; ADMW1001_LUT_TABLE_DATA *pData = &pLutTable->data; unsigned short calculatedCrc; 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: case ADMW1001_LUT_GEOMETRY_NES_2D: case ADMW1001_LUT_GEOMETRY_ES_1D: case ADMW1001_LUT_GEOMETRY_ES_2D: if (pDesc->equation != ADMW1001_LUT_EQUATION_LUT) { ADMW_LOG_ERROR("Invalid equation %u specified for LUT table %u", pDesc->equation, i); return ADMW_INVALID_PARAM; } 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; } calculatedCrc = admw_crc16_ccitt(pData, pDesc->length); if (calculatedCrc != pDesc->crc16) { ADMW_LOG_ERROR("CRC validation failed on LUT table %u (expected 0x%04X, actual 0x%04X)", i, pDesc->crc16, calculatedCrc); return ADMW_CRC_ERROR; } 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; } 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_COEFFS: if (pDesc->equation == ADMW1001_LUT_EQUATION_BIVARIATE_POLYN) *pLength = ADMW1001_LUT_2D_POLYN_COEFF_LIST_SIZE(pData->coeffList2d); else *pLength = ADMW1001_LUT_COEFF_LIST_SIZE(pData->coeffList); break; case ADMW1001_LUT_GEOMETRY_NES_1D: *pLength = ADMW1001_LUT_1D_NES_SIZE(pData->lut1dNes); break; case ADMW1001_LUT_GEOMETRY_NES_2D: *pLength = ADMW1001_LUT_2D_NES_SIZE(pData->lut2dNes); break; case ADMW1001_LUT_GEOMETRY_ES_1D: *pLength = ADMW1001_LUT_1D_ES_SIZE(pData->lut1dEs); break; case ADMW1001_LUT_GEOMETRY_ES_2D: *pLength = ADMW1001_LUT_2D_ES_SIZE(pData->lut2dEs); 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; pDesc->crc16 = admw_crc16_ccitt(pData, 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; }