![](/media/cache/img/default_profile.jpg.50x50_q85.jpg)
ADISense1000 Version 2.1 code base
Fork of AdiSense1000_V21 by
Diff: src/adi_sense_1000.c
- Revision:
- 7:4dbae381f693
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/adi_sense_1000.c Fri Oct 20 15:58:01 2017 +0000 @@ -0,0 +1,1766 @@ +/*! + ****************************************************************************** + * @file: adi_sense_1000.c + * @brief: ADI Sense API implementation for ADI Sense 1000 + *----------------------------------------------------------------------------- + */ + +/****************************************************************************** +Copyright (c) 2017 Emutex Ltd. / 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. + - Modified versions of the software must be conspicuously marked as such. + - This software is licensed solely and exclusively for use with processors + manufactured by or for Analog Devices, Inc. + - This software may not be combined or merged with other code in any manner + that would cause the software to become subject to terms and conditions + which differ from those listed here. + - 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. + +THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES, INC. AND CONTRIBUTORS "AS IS" AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, +TITLE, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN +NO EVENT SHALL ANALOG DEVICES, INC. OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, PUNITIVE OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, DAMAGES ARISING OUT OF CLAIMS OF INTELLECTUAL +PROPERTY RIGHTS INFRINGEMENT; 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. + * + *****************************************************************************/ +#include <float.h> +#include <math.h> + +#include "inc/adi_sense_api.h" +#include "inc/adi_sense_platform.h" + +#include "adi_sense_1000/ADISENSE1000_REGISTERS_typedefs.h" +#include "adi_sense_1000/ADISENSE1000_REGISTERS.h" + +#include "crc16.h" + +#define REG_READ_DELAY_USEC (20) + +#define ADI_SENSE_CHANNEL_IS_ADC(c) \ + ((c) >= ADI_SENSE_CHANNEL_ID_CJC_0 && (c) <= ADI_SENSE_CHANNEL_ID_CURRENT_0) + +#define ADI_SENSE_CHANNEL_IS_ADC_CJC(c) \ + ((c) >= ADI_SENSE_CHANNEL_ID_CJC_0 && (c) <= ADI_SENSE_CHANNEL_ID_CJC_1) + +#define ADI_SENSE_CHANNEL_IS_ADC_SENSOR(c) \ + ((c) >= ADI_SENSE_CHANNEL_ID_SENSOR_0 && (c) <= ADI_SENSE_CHANNEL_ID_SENSOR_3) + +#define ADI_SENSE_CHANNEL_IS_ADC_VOLTAGE(c) \ + ((c) == ADI_SENSE_CHANNEL_ID_VOLTAGE_0) + +#define ADI_SENSE_CHANNEL_IS_ADC_CURRENT(c) \ + ((c) == ADI_SENSE_CHANNEL_ID_CURRENT_0) + +typedef struct +{ + unsigned nDeviceIndex; + ADI_SENSE_SPI_HANDLE hSpi; + ADI_SENSE_GPIO_HANDLE hGpio; +} ADI_SENSE_DEVICE_CONTEXT; + +static ADI_SENSE_DEVICE_CONTEXT gDeviceCtx[ADI_SENSE_MAX_DEVICES]; + +/* + * Open an ADI Sense device instance. + */ +ADI_SENSE_RESULT adi_sense_Open( + unsigned const nDeviceIndex, + ADI_SENSE_CONNECTION * const pConnectionInfo, + ADI_SENSE_DEVICE_HANDLE * const phDevice) +{ + ADI_SENSE_DEVICE_CONTEXT *pCtx; + ADI_SENSE_RESULT eRet; + + if (nDeviceIndex >= ADI_SENSE_MAX_DEVICES) + return ADI_SENSE_INVALID_DEVICE_NUM; + + pCtx = &gDeviceCtx[nDeviceIndex]; + pCtx->nDeviceIndex = nDeviceIndex; + + eRet = adi_sense_LogOpen(); + if (eRet != ADI_SENSE_SUCCESS) + return eRet; + + eRet = adi_sense_GpioOpen(&pConnectionInfo->gpio, &pCtx->hGpio); + if (eRet != ADI_SENSE_SUCCESS) + return eRet; + + eRet = adi_sense_SpiOpen(&pConnectionInfo->spi, &pCtx->hSpi); + if (eRet != ADI_SENSE_SUCCESS) + return eRet; + + *phDevice = pCtx; + return ADI_SENSE_SUCCESS; +} + +/* + * Get the current state of the specified GPIO input signal. + */ +ADI_SENSE_RESULT adi_sense_GetGpioState( + ADI_SENSE_DEVICE_HANDLE const hDevice, + ADI_SENSE_GPIO_PIN const ePinId, + bool_t * const pbAsserted) +{ + ADI_SENSE_DEVICE_CONTEXT *pCtx = hDevice; + + return adi_sense_GpioGet(pCtx->hGpio, ePinId, pbAsserted); +} + +/* + * Register an application-defined callback function for GPIO interrupts. + */ +ADI_SENSE_RESULT adi_sense_RegisterGpioCallback( + ADI_SENSE_DEVICE_HANDLE const hDevice, + ADI_SENSE_GPIO_PIN const ePinId, + ADI_SENSE_GPIO_CALLBACK const callbackFunction, + void * const pCallbackParam) +{ + ADI_SENSE_DEVICE_CONTEXT *pCtx = hDevice; + + if (callbackFunction) + { + return adi_sense_GpioIrqEnable(pCtx->hGpio, ePinId, callbackFunction, + pCallbackParam); + } + else + { + return adi_sense_GpioIrqDisable(pCtx->hGpio, ePinId); + } +} + +/* + * Reset the specified ADI Sense device. + */ +ADI_SENSE_RESULT adi_sense_Reset( + ADI_SENSE_DEVICE_HANDLE const hDevice) +{ + ADI_SENSE_DEVICE_CONTEXT *pCtx = hDevice; + ADI_SENSE_RESULT eRet; + + /* Pulse the Reset GPIO pin low for a minimum of 4 microseconds */ + eRet = adi_sense_GpioSet(pCtx->hGpio, ADI_SENSE_GPIO_PIN_RESET, false); + if (eRet != ADI_SENSE_SUCCESS) + return eRet; + + adi_sense_TimeDelayUsec(4); + + eRet = adi_sense_GpioSet(pCtx->hGpio, ADI_SENSE_GPIO_PIN_RESET, true); + if (eRet != ADI_SENSE_SUCCESS) + return eRet; + + return ADI_SENSE_SUCCESS; +} + + +/*! + * @brief Get general status of ADISense module. + * + * @param[in] + * @param[out] pStatus : Pointer to CORE Status struct. + * + * @return Status + * - #ADI_SENSE_SUCCESS Call completed successfully. + * - #ADI_SENSE_FAILURE If status register read fails. + * + * @details Read the general status register for the ADISense + * module. Indicates Error, Alert conditions, data ready + * and command running. + * + */ +ADI_SENSE_RESULT adi_sense_GetStatus( + ADI_SENSE_DEVICE_HANDLE const hDevice, + ADI_SENSE_STATUS * const pStatus) +{ + ADI_ADISENSE_CORE_Status_t statusReg; + ADI_SENSE_RESULT eRet; + + eRet = adi_sense_ReadRegister(hDevice, REG_ADISENSE_CORE_STATUS, + &statusReg, sizeof(statusReg)); + if (eRet) + { + return eRet; + } + + pStatus->deviceStatus = 0; + + if (statusReg.Cmd_Running) + pStatus->deviceStatus |= ADI_SENSE_DEVICE_STATUS_BUSY; + + if (statusReg.Drdy) + pStatus->deviceStatus |= ADI_SENSE_DEVICE_STATUS_DATAREADY; + + if (statusReg.Error) + pStatus->deviceStatus |= ADI_SENSE_DEVICE_STATUS_ERROR; + + if (statusReg.Alert_Limit) + { + pStatus->deviceStatus |= ADI_SENSE_DEVICE_STATUS_ALERT; + + ADI_ADISENSE_CORE_Channel_Alert_Status_t channelAlertStatusReg; + eRet = adi_sense_ReadRegister(hDevice, REG_ADISENSE_CORE_CHANNEL_ALERT_STATUS, + &channelAlertStatusReg, + sizeof(channelAlertStatusReg)); + if (eRet) + { + return eRet; + } + + for (unsigned i = 0; i < ADI_SENSE_MAX_CHANNELS; i++) + { + pStatus->channelAlerts[i] = 0; + + if (channelAlertStatusReg.VALUE16 & (1 << i)) + { + ADI_ADISENSE_CORE_Alert_Detail_Ch_t alertDetailReg; + eRet = adi_sense_ReadRegister(hDevice, + REG_ADISENSE_CORE_ALERT_DETAIL_CHn(i), + &alertDetailReg, + sizeof(alertDetailReg)); + if (eRet) + { + return eRet; + } + + if (alertDetailReg.Time_Out) + pStatus->channelAlerts[i] |= ADI_SENSE_CHANNEL_ALERT_TIMEOUT; + if (alertDetailReg.Under_Range) + pStatus->channelAlerts[i] |= ADI_SENSE_CHANNEL_ALERT_UNDER_RANGE; + if (alertDetailReg.Over_Range) + pStatus->channelAlerts[i] |= ADI_SENSE_CHANNEL_ALERT_OVER_RANGE; + if (alertDetailReg.Low_Limit) + pStatus->channelAlerts[i] |= ADI_SENSE_CHANNEL_ALERT_LOW_LIMIT; + if (alertDetailReg.High_Limit) + pStatus->channelAlerts[i] |= ADI_SENSE_CHANNEL_ALERT_HIGH_LIMIT; + if (alertDetailReg.Sensor_Open) + pStatus->channelAlerts[i] |= ADI_SENSE_CHANNEL_ALERT_SENSOR_OPEN; + if (alertDetailReg.Ref_Detect) + pStatus->channelAlerts[i] |= ADI_SENSE_CHANNEL_ALERT_REF_DETECT; + } + } + } + + /* TODO - fill diagnosticsStatus field */ + + return ADI_SENSE_SUCCESS; +} + +ADI_SENSE_RESULT adi_sense_GetCommandRunningState( + ADI_SENSE_DEVICE_HANDLE hDevice, + bool_t *pbCommandRunning) +{ + ADI_SENSE_RESULT eRet; + ADI_ADISENSE_CORE_Status_t statusReg; + + eRet = adi_sense_ReadRegister(hDevice, REG_ADISENSE_CORE_STATUS, + &statusReg, sizeof(statusReg)); + if (eRet) + return eRet; + + *pbCommandRunning = statusReg.Cmd_Running; + + return ADI_SENSE_SUCCESS; +} + + +static ADI_SENSE_RESULT executeCommand( + ADI_SENSE_DEVICE_HANDLE const hDevice, + ADI_ADISENSE_CORE_Command_Special_Command const command, + bool_t const bWaitForCompletion) +{ + ADI_ADISENSE_CORE_Command_t commandReg = { + .Special_Command = command + }; + bool_t bCommandRunning; + ADI_SENSE_RESULT eRet; + + /* + * Don't allow another command to be issued if one is already running, but + * make an exception for ADISENSE_CORE_COMMAND_NOP which can be used to + * request a running command to be stopped (e.g. continuous measurement) + */ + if (command != ADISENSE_CORE_COMMAND_NOP) + { + eRet = adi_sense_GetCommandRunningState(hDevice, &bCommandRunning); + if (eRet) + return eRet; + + if (bCommandRunning) + return ADI_SENSE_IN_USE; + } + + eRet = adi_sense_WriteRegister(hDevice, REG_ADISENSE_CORE_COMMAND, + &commandReg, sizeof(commandReg)); + if (eRet) + return eRet; + + if (bWaitForCompletion) + { + do { + eRet = adi_sense_GetCommandRunningState(hDevice, &bCommandRunning); + if (eRet) + return eRet; + } while (bCommandRunning); + } + + return ADI_SENSE_SUCCESS; +} + +ADI_SENSE_RESULT adi_sense_ApplyConfigUpdates( + ADI_SENSE_DEVICE_HANDLE const hDevice) +{ + return executeCommand(hDevice, ADISENSE_CORE_COMMAND_LATCH_CONFIG, true); +} + +/*! + * @brief Start a measurement cycle. + * + * @param[out] + * + * @return Status + * - #ADI_SENSE_SUCCESS Call completed successfully. + * - #ADI_SENSE_FAILURE + * + * @details Sends the latch config command. Configuration for channels in + * conversion cycle should be completed before this function. + * Channel enabled bit should be set before this function. + * Starts a conversion and configures the format of the sample. + * + */ +ADI_SENSE_RESULT adi_sense_StartMeasurement( + ADI_SENSE_DEVICE_HANDLE const hDevice) +{ + return executeCommand(hDevice, ADISENSE_CORE_COMMAND_CONVERT_WITH_RAW, false); +} + +/* + * Store the configuration settings to persistent memory on the device. + * No other command must be running when this is called. + * Do not power down the device while this command is running. + */ +ADI_SENSE_RESULT adi_sense_SaveConfig( + ADI_SENSE_DEVICE_HANDLE const hDevice) +{ + return executeCommand(hDevice, ADISENSE_CORE_COMMAND_SAVE_CONFIG, true); +} + +/* + * Restore the configuration settings from persistent memory on the device. + * No other command must be running when this is called. + */ +ADI_SENSE_RESULT adi_sense_RestoreConfig( + ADI_SENSE_DEVICE_HANDLE const hDevice) +{ + return executeCommand(hDevice, ADISENSE_CORE_COMMAND_LOAD_CONFIG, true); +} + +/* + * Stop the measurement cycles on the device. + * To be used only if a measurement command is currently running. + */ +ADI_SENSE_RESULT adi_sense_StopMeasurement( + ADI_SENSE_DEVICE_HANDLE const hDevice) +{ + return executeCommand(hDevice, ADISENSE_CORE_COMMAND_NOP, true); +} + +/* + * Run built-in diagnostic checks on the device. + * Diagnostics are executed according to the current applied settings. + * No other command must be running when this is called. + */ +ADI_SENSE_RESULT adi_sense_RunDiagnostics( + ADI_SENSE_DEVICE_HANDLE const hDevice) +{ + return executeCommand(hDevice, ADISENSE_CORE_COMMAND_RUN_DIAGNOSTICS, true); +} + +/* + * Read a set of data samples from the device. + * This may be called at any time. + */ +ADI_SENSE_RESULT adi_sense_GetData( + ADI_SENSE_DEVICE_HANDLE const hDevice, + ADI_SENSE_DATA_SAMPLE * const pSamples, + uint32_t const nRequested, + uint32_t * const pnReturned) +{ + ADI_SENSE_DEVICE_CONTEXT *pCtx = hDevice; + uint16_t command = REG_ADISENSE_CORE_DATA_FIFO; + uint8_t commandData[sizeof(command)] = { + command >> 8, + command & 0xFF + }; + unsigned nValidSamples = 0; + ADI_SENSE_RESULT eRet; + + eRet = adi_sense_SpiTransfer(pCtx->hSpi, commandData, NULL, + sizeof(command), false); + if (eRet) + return eRet; + + adi_sense_TimeDelayUsec(REG_READ_DELAY_USEC); + + for (unsigned i = 0; i < nRequested; i++) + { + ADI_ADISENSE_CORE_Data_FIFO_t dataFifoReg; + bool_t bHoldCs = true; + + /* Keep the CS signal asserted for all but the last sample */ + if ((i + 1) == nRequested) + bHoldCs = false; + + eRet = adi_sense_SpiTransfer(pCtx->hSpi, NULL, &dataFifoReg, + sizeof(dataFifoReg), bHoldCs); + if (eRet) + return eRet; + + if (! dataFifoReg.Ch_Valid) + { + ADI_SENSE_LOG_WARN("Read invalid data sample"); + continue; + } + + ADI_SENSE_DATA_SAMPLE *pSample = &pSamples[nValidSamples]; + + pSample->status = 0; + if (dataFifoReg.Ch_Error) + pSample->status |= ADI_SENSE_DEVICE_STATUS_ERROR; + if (dataFifoReg.Ch_Alert) + pSample->status |= ADI_SENSE_DEVICE_STATUS_ALERT; + + if (dataFifoReg.Ch_Raw) + pSample->rawValue = dataFifoReg.Raw_Sample; + else + pSample->rawValue = 0; + + pSample->channelId = dataFifoReg.Channel_ID; + pSample->processedValue = dataFifoReg.Sensor_Result; + + nValidSamples++; + } + *pnReturned = nValidSamples; + + return ADI_SENSE_SUCCESS; +} + +/* + * Close the given ADI Sense device. + */ +ADI_SENSE_RESULT adi_sense_Close( + ADI_SENSE_DEVICE_HANDLE const hDevice) +{ + ADI_SENSE_DEVICE_CONTEXT *pCtx = hDevice; + + adi_sense_GpioClose(pCtx->hGpio); + adi_sense_SpiClose(pCtx->hSpi); + + return ADI_SENSE_SUCCESS; +} + +ADI_SENSE_RESULT adi_sense_WriteRegister( + ADI_SENSE_DEVICE_HANDLE hDevice, + uint16_t nAddress, + void *pData, + unsigned nLength) +{ + ADI_SENSE_RESULT eRet; + ADI_SENSE_DEVICE_CONTEXT *pCtx = hDevice; + uint16_t command = 0x8000 | (nAddress & 0x7FFF); + uint8_t commandData[sizeof(command)] = { + command >> 8, + command & 0xFF + }; + + eRet = adi_sense_SpiTransfer(pCtx->hSpi, commandData, NULL, + sizeof(commandData), false); + if (eRet) + { + return eRet; + } + + return adi_sense_SpiTransfer(pCtx->hSpi, pData, NULL, nLength, false); +} + +ADI_SENSE_RESULT adi_sense_ReadRegister( + ADI_SENSE_DEVICE_HANDLE hDevice, + uint16_t nAddress, + void *pData, + unsigned nLength) +{ + ADI_SENSE_RESULT eRet; + ADI_SENSE_DEVICE_CONTEXT *pCtx = hDevice; + uint16_t command = nAddress & 0x7FFF; + uint8_t commandData[sizeof(command)] = { + command >> 8, + command & 0xFF + }; + + eRet = adi_sense_SpiTransfer(pCtx->hSpi, commandData, NULL, + sizeof(command), false); + if (eRet) + { + return eRet; + } + + adi_sense_TimeDelayUsec(REG_READ_DELAY_USEC); + + eRet = adi_sense_SpiTransfer(pCtx->hSpi, NULL, pData, nLength, false); + if (eRet) + { + return eRet; + } + + return ADI_SENSE_SUCCESS; +} + +ADI_SENSE_RESULT adi_sense_GetDeviceReadyState( + ADI_SENSE_DEVICE_HANDLE const hDevice, + bool_t * const bReady) +{ + ADI_ADISENSE_SPI_Chip_Type_t chipTypeReg; + ADI_SENSE_RESULT eRet; + + eRet = adi_sense_ReadRegister(hDevice, REG_ADISENSE_SPI_CHIP_TYPE, &chipTypeReg, + sizeof(chipTypeReg)); + if (eRet) + { + ADI_SENSE_LOG_ERROR("Failed to read chip-type register"); + return eRet; + } + + /* If we read this register successfully, assume the device is ready */ + *bReady = (chipTypeReg.VALUE8 == REG_ADISENSE_SPI_CHIP_TYPE_RESET); + + return ADI_SENSE_SUCCESS; +} + +ADI_SENSE_RESULT adi_sense_GetDataPublishingInfo( + ADI_SENSE_DEVICE_HANDLE const hDevice, + ADI_SENSE_OPERATING_MODE * const peOperatingMode, + ADI_SENSE_DATA_PUBLISH_MODE * const peDataPublishMode, + uint32_t * const pnSamplesPerDataready, + uint32_t * const pnSamplesPerCycle) +{ + ADI_ADISENSE_CORE_Channel_Count_t channelCountReg; + ADI_ADISENSE_CORE_Mode_t modeReg; + ADI_SENSE_RESULT eRet; + + unsigned nChannelsEnabled = 0; + unsigned nSamplesPerCycle = 0; + for (ADI_SENSE_CHANNEL_ID chId = 0; chId < ADI_SENSE_MAX_CHANNELS; chId++) + { + eRet = adi_sense_ReadRegister(hDevice, REG_ADISENSE_CORE_CHANNEL_COUNTn(chId), + &channelCountReg, + sizeof(channelCountReg)); + if (eRet) + return eRet; + + if (channelCountReg.Channel_Enable) + { + nChannelsEnabled++; + nSamplesPerCycle += (channelCountReg.Channel_Count + 1); + } + } + + if (nChannelsEnabled == 0) + { + *pnSamplesPerDataready = 0; + *pnSamplesPerCycle = 0; + return ADI_SENSE_SUCCESS; + } + + eRet = adi_sense_ReadRegister(hDevice, REG_ADISENSE_CORE_MODE, + &modeReg, sizeof(modeReg)); + if (eRet) + return eRet; + + *pnSamplesPerCycle = nSamplesPerCycle; + if (modeReg.Drdy_Mode == ADISENSE_CORE_MODE_DRDY_PER_CONVERSION) + { + *pnSamplesPerDataready = 1; + } + else if (modeReg.Drdy_Mode == ADISENSE_CORE_MODE_DRDY_PER_CYCLE) + { + *pnSamplesPerDataready = nSamplesPerCycle; + } + else + { + ADI_ADISENSE_CORE_Fifo_Num_Cycles_t fifoNumCyclesReg; + + eRet = adi_sense_ReadRegister(hDevice, REG_ADISENSE_CORE_FIFO_NUM_CYCLES, + &fifoNumCyclesReg, + sizeof(fifoNumCyclesReg)); + if (eRet) + return eRet; + + *pnSamplesPerDataready = + nSamplesPerCycle * fifoNumCyclesReg.Fifo_Num_Cycles; + } + + if (modeReg.Conversion_Mode == ADISENSE_CORE_MODE_SINGLECYCLE) + *peOperatingMode = ADI_SENSE_OPERATING_MODE_SINGLECYCLE; + else if (modeReg.Conversion_Mode == ADISENSE_CORE_MODE_MULTICYCLE) + *peOperatingMode = ADI_SENSE_OPERATING_MODE_MULTICYCLE; + else + *peOperatingMode = ADI_SENSE_OPERATING_MODE_CONTINUOUS; + + if (modeReg.Drdy_Mode == ADISENSE_CORE_MODE_DRDY_PER_CONVERSION) + *peDataPublishMode = ADI_SENSE_DATA_PUBLISH_PER_CONVERSION; + else if (modeReg.Drdy_Mode == ADISENSE_CORE_MODE_DRDY_PER_CYCLE) + *peDataPublishMode = ADI_SENSE_DATA_PUBLISH_PER_CYCLE; + else + *peDataPublishMode = ADI_SENSE_DATA_PUBLISH_PER_MULTICYCLE_BURST; + + return ADI_SENSE_SUCCESS; +} + +ADI_SENSE_RESULT adi_sense_GetProductID( + ADI_SENSE_DEVICE_HANDLE hDevice, + ADI_SENSE_PRODUCT_ID *pProductId) +{ + ADI_ADISENSE_SPI_Product_ID_L_t productIdLoReg; + ADI_ADISENSE_SPI_Product_ID_H_t productIdHiReg; + ADI_SENSE_RESULT eRet; + + eRet = adi_sense_ReadRegister(hDevice, REG_ADISENSE_SPI_PRODUCT_ID_L, + &productIdLoReg, sizeof(productIdLoReg)); + if (eRet) + return eRet; + + eRet = adi_sense_ReadRegister(hDevice, REG_ADISENSE_SPI_PRODUCT_ID_H, + &productIdHiReg, sizeof(productIdHiReg)); + if (eRet) + return eRet; + + *pProductId = (productIdHiReg.VALUE8 << 8) | productIdLoReg.VALUE8; + return ADI_SENSE_SUCCESS; +} + +static ADI_SENSE_RESULT adi_sense_SetPowerMode( + ADI_SENSE_DEVICE_HANDLE hDevice, + ADI_SENSE_POWER_MODE powerMode) +{ + ADI_ADISENSE_CORE_Power_Config_t powerConfigReg; + + if (powerMode == ADI_SENSE_POWER_MODE_LOW) + { + powerConfigReg.Power_Mode_ADC = ADISENSE_CORE_POWER_CONFIG_ADC_LOW_POWER; + /* TODO - we need an enum in the register map for the MCU power modes */ + powerConfigReg.Power_Mode_MCU = 0x0; + } + else if (powerMode == ADI_SENSE_POWER_MODE_MID) + { + powerConfigReg.Power_Mode_ADC = ADISENSE_CORE_POWER_CONFIG_ADC_MID_POWER; + powerConfigReg.Power_Mode_MCU = 0x1; + } + else if (powerMode == ADI_SENSE_POWER_MODE_FULL) + { + powerConfigReg.Power_Mode_ADC = ADISENSE_CORE_POWER_CONFIG_ADC_FULL_POWER; + powerConfigReg.Power_Mode_MCU = 0x2; + } + else + { + return ADI_SENSE_INVALID_PARAM; + } + + return adi_sense_WriteRegister(hDevice, REG_ADISENSE_CORE_POWER_CONFIG, + &powerConfigReg, sizeof(powerConfigReg)); +} + +static ADI_SENSE_RESULT adi_sense_SetVddVoltage( + ADI_SENSE_DEVICE_HANDLE hDevice, + float32_t vddVoltage) +{ + ADI_ADISENSE_CORE_AVDD_Voltage_t avddVoltageReg = { + .Avdd_Voltage = vddVoltage + }; + + return adi_sense_WriteRegister(hDevice, REG_ADISENSE_CORE_AVDD_VOLTAGE, + &avddVoltageReg, sizeof(avddVoltageReg)); +} + +ADI_SENSE_RESULT adi_sense_SetPowerConfig( + ADI_SENSE_DEVICE_HANDLE hDevice, + ADI_SENSE_POWER_CONFIG *pPowerConfig) +{ + ADI_SENSE_RESULT eRet; + + eRet = adi_sense_SetPowerMode(hDevice, pPowerConfig->powerMode); + if (eRet) + { + return eRet; + } + + return adi_sense_SetVddVoltage(hDevice, pPowerConfig->supplyVoltage); +} + +static ADI_SENSE_RESULT adi_sense_SetMode( + ADI_SENSE_DEVICE_HANDLE hDevice, + ADI_SENSE_OPERATING_MODE eOperatingMode, + ADI_SENSE_DATA_PUBLISH_MODE ePublishMode) +{ + ADI_ADISENSE_CORE_Mode_t modeReg = { .VALUE8 = 0 }; + + if (eOperatingMode == ADI_SENSE_OPERATING_MODE_SINGLECYCLE) + { + modeReg.Conversion_Mode = ADISENSE_CORE_MODE_SINGLECYCLE; + } + else if (eOperatingMode == ADI_SENSE_OPERATING_MODE_CONTINUOUS) + { + modeReg.Conversion_Mode = ADISENSE_CORE_MODE_CONTINUOUS; + } + else if (eOperatingMode == ADI_SENSE_OPERATING_MODE_MULTICYCLE) + { + modeReg.Conversion_Mode = ADISENSE_CORE_MODE_MULTICYCLE; + } + else + { + return ADI_SENSE_INVALID_PARAM; + } + + if (ePublishMode == ADI_SENSE_DATA_PUBLISH_PER_CONVERSION) + { + modeReg.Drdy_Mode = ADISENSE_CORE_MODE_DRDY_PER_CONVERSION; + } + else if (ePublishMode == ADI_SENSE_DATA_PUBLISH_PER_CYCLE) + { + modeReg.Drdy_Mode = ADISENSE_CORE_MODE_DRDY_PER_CYCLE; + } + else if (ePublishMode == ADI_SENSE_DATA_PUBLISH_PER_MULTICYCLE_BURST) + { + if (eOperatingMode != ADI_SENSE_OPERATING_MODE_MULTICYCLE) + { + return ADI_SENSE_INVALID_PARAM; + } + else + { + modeReg.Drdy_Mode = ADISENSE_CORE_MODE_DRDY_PER_FIFO_FILL; + } + } + else + { + return ADI_SENSE_INVALID_PARAM; + } + + return adi_sense_WriteRegister(hDevice, REG_ADISENSE_CORE_MODE, + &modeReg, sizeof(modeReg)); +} + +ADI_SENSE_RESULT adi_sense_SetCycleInterval( + ADI_SENSE_DEVICE_HANDLE hDevice, + uint32_t nCycleInterval) +{ + ADI_ADISENSE_CORE_Cycle_Control_t cycleControlReg; + + if (nCycleInterval < (1 << 12)) + { + cycleControlReg.Cycle_Time = nCycleInterval; + cycleControlReg.Cycle_Time_Units = ADISENSE_CORE_CYCLE_CONTROL_MICROSECONDS; + } + else if (nCycleInterval < (1000 * (1 << 12))) + { + cycleControlReg.Cycle_Time = nCycleInterval / 1000; + cycleControlReg.Cycle_Time_Units = ADISENSE_CORE_CYCLE_CONTROL_MILLISECONDS; + } + else + { + cycleControlReg.Cycle_Time = nCycleInterval / 1000000; + cycleControlReg.Cycle_Time_Units = ADISENSE_CORE_CYCLE_CONTROL_SECONDS; + } + + return adi_sense_WriteRegister(hDevice, REG_ADISENSE_CORE_CYCLE_CONTROL, + &cycleControlReg, sizeof(cycleControlReg)); +} + +static ADI_SENSE_RESULT adi_sense_SetMultiCycleConfig( + ADI_SENSE_DEVICE_HANDLE hDevice, + ADI_SENSE_MULTICYCLE_CONFIG *pMultiCycleConfig) +{ + ADI_ADISENSE_CORE_Fifo_Num_Cycles_t fifoNumCyclesReg = { + .VALUE8 = REG_ADISENSE_CORE_FIFO_NUM_CYCLES_RESET + }; + ADI_ADISENSE_CORE_Multi_Cycle_Repeat_Interval_t multiCycleIntervalReg = { + .VALUE32 = REG_ADISENSE_CORE_MULTI_CYCLE_REPEAT_INTERVAL_RESET + }; + ADI_SENSE_RESULT eRet; + + fifoNumCyclesReg.Fifo_Num_Cycles = pMultiCycleConfig->cyclesPerBurst; + eRet = adi_sense_WriteRegister(hDevice, REG_ADISENSE_CORE_FIFO_NUM_CYCLES, + &fifoNumCyclesReg, + sizeof(fifoNumCyclesReg)); + if (eRet) + { + return eRet; + } + + multiCycleIntervalReg.Multi_Cycle_Repeat_Interval = pMultiCycleConfig->burstInterval; + return adi_sense_WriteRegister(hDevice, REG_ADISENSE_CORE_MULTI_CYCLE_REPEAT_INTERVAL, + &multiCycleIntervalReg, + sizeof(multiCycleIntervalReg)); +} + +static ADI_SENSE_RESULT adi_sense_SetExternalReferenceValues( + ADI_SENSE_DEVICE_HANDLE hDevice, + float32_t externalRef1Value, + float32_t externalRef2Value) +{ + ADI_SENSE_RESULT eRet; + ADI_ADISENSE_CORE_External_Reference1_t externalReference1Reg = { + .Ext_Refin1_Value = externalRef1Value + }; + ADI_ADISENSE_CORE_External_Reference2_t externalReference2Reg = { + .Ext_Refin2_Value = externalRef2Value + }; + + eRet = adi_sense_WriteRegister(hDevice, REG_ADISENSE_CORE_EXTERNAL_REFERENCE1, + &externalReference1Reg, + sizeof(externalReference1Reg)); + if (eRet) + { + return eRet; + } + + return adi_sense_WriteRegister(hDevice, REG_ADISENSE_CORE_EXTERNAL_REFERENCE2, + &externalReference2Reg, + sizeof(externalReference2Reg)); +} + +ADI_SENSE_RESULT adi_sense_SetMeasurementConfig( + ADI_SENSE_DEVICE_HANDLE hDevice, + ADI_SENSE_MEASUREMENT_CONFIG *pMeasConfig) +{ + ADI_SENSE_RESULT eRet; + + eRet = adi_sense_SetMode(hDevice, + pMeasConfig->operatingMode, + pMeasConfig->dataPublishMode); + if (eRet) + { + return eRet; + } + + if (pMeasConfig->operatingMode != ADI_SENSE_OPERATING_MODE_SINGLECYCLE) + { + eRet = adi_sense_SetCycleInterval(hDevice, pMeasConfig->cycleInterval); + if (eRet) + { + return eRet; + } + } + + if (pMeasConfig->operatingMode == ADI_SENSE_OPERATING_MODE_MULTICYCLE) + { + eRet = adi_sense_SetMultiCycleConfig(hDevice, + &pMeasConfig->multiCycleConfig); + if (eRet) + { + return eRet; + } + } + + return adi_sense_SetExternalReferenceValues(hDevice, + pMeasConfig->externalRef1Value, + pMeasConfig->externalRef2Value); +} + +ADI_SENSE_RESULT adi_sense_SetDiagnosticsConfig( + ADI_SENSE_DEVICE_HANDLE hDevice, + ADI_SENSE_DIAGNOSTICS_CONFIG *pDiagnosticsConfig) +{ + ADI_ADISENSE_CORE_Diagnostics_Control_t diagnosticsControlReg = { + .VALUE16 = REG_ADISENSE_CORE_DIAGNOSTICS_CONTROL_RESET + }; + + if (pDiagnosticsConfig->enableGlobalDiag) + diagnosticsControlReg.Diag_Global_En = 1; + + if (pDiagnosticsConfig->enableMeasurementDiag) + { + diagnosticsControlReg.Diag_Meas_En = 1; + + switch (pDiagnosticsConfig->openCircuitDetectionFreq) + { + case ADI_SENSE_OCD_DISABLED: + diagnosticsControlReg.Diag_OCD_Freq = ADISENSE_CORE_DIAGNOSTICS_CONTROL_OCD_OFF; + break; + case ADI_SENSE_OCD_PER_CYCLE: + diagnosticsControlReg.Diag_OCD_Freq = ADISENSE_CORE_DIAGNOSTICS_CONTROL_OCD_PER_1_CYCLE; + break; + case ADI_SENSE_OCD_PER_100_CYCLES: + diagnosticsControlReg.Diag_OCD_Freq = ADISENSE_CORE_DIAGNOSTICS_CONTROL_OCD_PER_100_CYCLES; + break; + case ADI_SENSE_OCD_PER_1000_CYCLES: + diagnosticsControlReg.Diag_OCD_Freq = ADISENSE_CORE_DIAGNOSTICS_CONTROL_OCD_PER_1000_CYCLES; + break; + default: + return ADI_SENSE_INVALID_PARAM; + } + } + + /* TODO - what should be set in the REG_ADISENSE_CORE_DIAGNOSTICS_EXTRA register? */ + + return adi_sense_WriteRegister(hDevice, REG_ADISENSE_CORE_DIAGNOSTICS_CONTROL, + &diagnosticsControlReg, + sizeof(diagnosticsControlReg)); +} + +ADI_SENSE_RESULT adi_sense_SetChannelCount( + ADI_SENSE_DEVICE_HANDLE hDevice, + ADI_SENSE_CHANNEL_ID eChannelId, + uint32_t nMeasurementsPerCycle) +{ + ADI_ADISENSE_CORE_Channel_Count_t channelCountReg = { + .VALUE8 = REG_ADISENSE_CORE_CHANNEL_COUNTn_RESET + }; + + if (nMeasurementsPerCycle > 0) + { + channelCountReg.Channel_Enable = 1; + channelCountReg.Channel_Count = nMeasurementsPerCycle - 1; + } + else + { + channelCountReg.Channel_Enable = 0; + } + + return adi_sense_WriteRegister(hDevice, REG_ADISENSE_CORE_CHANNEL_COUNTn(eChannelId), + &channelCountReg, sizeof(channelCountReg)); +} + +static ADI_SENSE_RESULT adi_sense_SetChannelAdcSensorType( + ADI_SENSE_DEVICE_HANDLE hDevice, + ADI_SENSE_CHANNEL_ID eChannelId, + ADI_SENSE_ADC_SENSOR_TYPE sensorType) +{ + ADI_ADISENSE_CORE_Sensor_Type_t sensorTypeReg = { + .VALUE16 = REG_ADISENSE_CORE_SENSOR_TYPEn_RESET + }; + + /* Ensure that the sensor type is valid for this channel */ + switch(sensorType) + { + case ADI_SENSE_ADC_SENSOR_THERMOCOUPLE_J: + case ADI_SENSE_ADC_SENSOR_THERMOCOUPLE_K: + case ADI_SENSE_ADC_SENSOR_THERMOCOUPLE_T: + case ADI_SENSE_ADC_SENSOR_THERMOCOUPLE_CUSTOM: + case ADI_SENSE_ADC_SENSOR_RTD_3WIRE_PT100: + case ADI_SENSE_ADC_SENSOR_RTD_3WIRE_PT1000: + case ADI_SENSE_ADC_SENSOR_RTD_3WIRE_CUSTOM: + case ADI_SENSE_ADC_SENSOR_RTD_4WIRE_PT100: + case ADI_SENSE_ADC_SENSOR_RTD_4WIRE_PT1000: + case ADI_SENSE_ADC_SENSOR_RTD_4WIRE_CUSTOM: + case ADI_SENSE_ADC_SENSOR_BRIDGE_4WIRE_TRANSDUCER: + case ADI_SENSE_ADC_SENSOR_BRIDGE_4WIRE_CUSTOM: + case ADI_SENSE_ADC_SENSOR_BRIDGE_6WIRE_TRANSDUCER: + case ADI_SENSE_ADC_SENSOR_BRIDGE_6WIRE_CUSTOM: + case ADI_SENSE_ADC_SENSOR_THERMISTOR_10K_NTC: + case ADI_SENSE_ADC_SENSOR_THERMISTOR_CUSTOM: + if (! ADI_SENSE_CHANNEL_IS_ADC_SENSOR(eChannelId)) + return ADI_SENSE_INVALID_PARAM; + break; + case ADI_SENSE_ADC_SENSOR_RTD_2WIRE_PT100: + case ADI_SENSE_ADC_SENSOR_RTD_2WIRE_PT1000: + case ADI_SENSE_ADC_SENSOR_RTD_2WIRE_CUSTOM: + if (! ADI_SENSE_CHANNEL_IS_ADC_SENSOR(eChannelId) && + ! ADI_SENSE_CHANNEL_IS_ADC_CJC(eChannelId)) + return ADI_SENSE_INVALID_PARAM; + break; + case ADI_SENSE_ADC_SENSOR_VOLTAGE_PRESSURE_1: + case ADI_SENSE_ADC_SENSOR_VOLTAGE_PRESSURE_2: + case ADI_SENSE_ADC_SENSOR_VOLTAGE_PRESSURE_CUSTOM: + if (! ADI_SENSE_CHANNEL_IS_ADC_VOLTAGE(eChannelId)) + return ADI_SENSE_INVALID_PARAM; + break; + case ADI_SENSE_ADC_SENSOR_CURRENT_PRESSURE_1: + case ADI_SENSE_ADC_SENSOR_CURRENT_PRESSURE_CUSTOM: + if (! ADI_SENSE_CHANNEL_IS_ADC_CURRENT(eChannelId)) + return ADI_SENSE_INVALID_PARAM; + break; + default: + return ADI_SENSE_INVALID_PARAM; + } + + switch(sensorType) + { + case ADI_SENSE_ADC_SENSOR_THERMOCOUPLE_T: + sensorTypeReg.Sensor_Type = ADISENSE_CORE_SENSOR_TYPE_SENSOR_THERMOCOUPLE_T_DEF_L1; + break; + case ADI_SENSE_ADC_SENSOR_THERMOCOUPLE_J: + sensorTypeReg.Sensor_Type = ADISENSE_CORE_SENSOR_TYPE_SENSOR_THERMOCOUPLE_J_DEF_L1; + break; + case ADI_SENSE_ADC_SENSOR_THERMOCOUPLE_K: + sensorTypeReg.Sensor_Type = ADISENSE_CORE_SENSOR_TYPE_SENSOR_THERMOCOUPLE_K_DEF_L1; + break; + case ADI_SENSE_ADC_SENSOR_RTD_2WIRE_PT100: + sensorTypeReg.Sensor_Type = ADISENSE_CORE_SENSOR_TYPE_SENSOR_RTD_2W_PT100_DEF_L1; + break; + case ADI_SENSE_ADC_SENSOR_RTD_2WIRE_PT1000: + sensorTypeReg.Sensor_Type = ADISENSE_CORE_SENSOR_TYPE_SENSOR_RTD_2W_PT1000_DEF_L1; + break; + case ADI_SENSE_ADC_SENSOR_RTD_3WIRE_PT100: + sensorTypeReg.Sensor_Type = ADISENSE_CORE_SENSOR_TYPE_SENSOR_RTD_3W_PT100_DEF_L1; + break; + case ADI_SENSE_ADC_SENSOR_RTD_3WIRE_PT1000: + sensorTypeReg.Sensor_Type = ADISENSE_CORE_SENSOR_TYPE_SENSOR_RTD_3W_PT1000_DEF_L1; + break; + case ADI_SENSE_ADC_SENSOR_RTD_4WIRE_PT100: + sensorTypeReg.Sensor_Type = ADISENSE_CORE_SENSOR_TYPE_SENSOR_RTD_4W_PT100_DEF_L1; + break; + case ADI_SENSE_ADC_SENSOR_RTD_4WIRE_PT1000: + sensorTypeReg.Sensor_Type = ADISENSE_CORE_SENSOR_TYPE_SENSOR_RTD_4W_PT1000_DEF_L1; + break; + case ADI_SENSE_ADC_SENSOR_BRIDGE_4WIRE_TRANSDUCER: + sensorTypeReg.Sensor_Type = ADISENSE_CORE_SENSOR_TYPE_SENSOR_BRIDGE_4W_1_DEF_L2; + break; + case ADI_SENSE_ADC_SENSOR_BRIDGE_6WIRE_TRANSDUCER: + sensorTypeReg.Sensor_Type = ADISENSE_CORE_SENSOR_TYPE_SENSOR_BRIDGE_6W_1_DEF_L2; + break; + case ADI_SENSE_ADC_SENSOR_THERMISTOR_10K_NTC: + sensorTypeReg.Sensor_Type = ADISENSE_CORE_SENSOR_TYPE_SENSOR_THERMISTOR_A_10K_DEF_L1; + break; + case ADI_SENSE_ADC_SENSOR_VOLTAGE_PRESSURE_1: + sensorTypeReg.Sensor_Type = ADISENSE_CORE_SENSOR_TYPE_SENSOR_VOLTAGE_PRESSURE_HONEYWELL_TRUSTABILITY; + break; + case ADI_SENSE_ADC_SENSOR_VOLTAGE_PRESSURE_2: + sensorTypeReg.Sensor_Type = ADISENSE_CORE_SENSOR_TYPE_SENSOR_VOLTAGE_PRESSURE_AMPHENOL_NPA300X; + break; + case ADI_SENSE_ADC_SENSOR_CURRENT_PRESSURE_1: + sensorTypeReg.Sensor_Type = ADISENSE_CORE_SENSOR_TYPE_SENSOR_CURRENT_PRESSURE_HONEYWELL_PX2; + break; + default: + /* TODO - add support for custom sensor types? */ + return ADI_SENSE_INVALID_PARAM; + } + + return adi_sense_WriteRegister(hDevice, REG_ADISENSE_CORE_SENSOR_TYPEn(eChannelId), + &sensorTypeReg, sizeof(sensorTypeReg)); +} + +static ADI_SENSE_RESULT adi_sense_SetChannelAdcSensorDetails( + ADI_SENSE_DEVICE_HANDLE hDevice, + ADI_SENSE_CHANNEL_ID eChannelId, + ADI_SENSE_CHANNEL_CONFIG *pChannelConfig) +/* + * TODO - it would be nice if the general- vs. ADC-specific sensor details could be split into separate registers + * General details: + * - Measurement_Units + * - Compensation_Channel + * - CJC_Publish (if "CJC" was removed from the name) + * ADC-specific details: + * - PGA_Gain + * - Reference_Select + * - Reference_Buffer_Disable + * - Vbias + */ +{ + ADI_SENSE_ADC_CHANNEL_CONFIG *pAdcChannelConfig = &pChannelConfig->adcChannelConfig; + ADI_SENSE_ADC_REFERENCE_CONFIG *pRefConfig = &pAdcChannelConfig->reference; + ADI_ADISENSE_CORE_Sensor_Details_t sensorDetailsReg = { + .VALUE32 = REG_ADISENSE_CORE_SENSOR_DETAILSn_RESET + }; + +/* TODO - measurementType will most likely be replaced with a unit-conversion option */ +#if 0 + switch(pChannelConfig->eMeasurementType) + { + case ADI_SENSE_MEASUREMENT_TEMPERATURE_CELCIUS: + sensorDetailsReg.Measurement_Units = CORE_SENSOR_DETAILS_UNITS_DEGC; + break; + case ADI_SENSE_MEASUREMENT_TEMPERATURE_FAHRENHEIT: + sensorDetailsReg.Measurement_Units = CORE_SENSOR_DETAILS_UNITS_DEGF; + break; + /* TODO - register map needs to define measurement units for the following options: */ + case ADI_SENSE_MEASUREMENT_HUMIDITY_RH: + case ADI_SENSE_MEASUREMENT_PRESSURE_BAR: + case ADI_SENSE_MEASUREMENT_PRESSURE_PSI: + case ADI_SENSE_MEASUREMENT_ACCELERATION_G: + default: + return ADI_SENSE_INVALID_PARAM; + } +#endif + + sensorDetailsReg.Compensation_Channel = pChannelConfig->compensationChannel; + + switch(pRefConfig->type) + { + case ADI_SENSE_ADC_REFERENCE_RESISTOR_INTERNAL_1: + sensorDetailsReg.Reference_Select = ADISENSE_CORE_SENSOR_DETAILS_REF_RINT1; + break; + case ADI_SENSE_ADC_REFERENCE_RESISTOR_INTERNAL_2: + sensorDetailsReg.Reference_Select = ADISENSE_CORE_SENSOR_DETAILS_REF_RINT2; + break; + case ADI_SENSE_ADC_REFERENCE_VOLTAGE_INTERNAL: + sensorDetailsReg.Reference_Select = ADISENSE_CORE_SENSOR_DETAILS_REF_INT; + break; + case ADI_SENSE_ADC_REFERENCE_VOLTAGE_AVDD: + sensorDetailsReg.Reference_Select = ADISENSE_CORE_SENSOR_DETAILS_REF_AVDD; + break; + case ADI_SENSE_ADC_REFERENCE_RESISTOR_EXTERNAL_1: + sensorDetailsReg.Reference_Select = ADISENSE_CORE_SENSOR_DETAILS_REF_REXT1; + break; + case ADI_SENSE_ADC_REFERENCE_RESISTOR_EXTERNAL_2: + sensorDetailsReg.Reference_Select = ADISENSE_CORE_SENSOR_DETAILS_REF_REXT2; + break; + case ADI_SENSE_ADC_REFERENCE_VOLTAGE_EXTERNAL_1: + sensorDetailsReg.Reference_Select = ADISENSE_CORE_SENSOR_DETAILS_REF_VEXT1; + break; + case ADI_SENSE_ADC_REFERENCE_VOLTAGE_EXTERNAL_2: + sensorDetailsReg.Reference_Select = ADISENSE_CORE_SENSOR_DETAILS_REF_VEXT2; + break; + default: + return ADI_SENSE_INVALID_PARAM; + } + + switch(pAdcChannelConfig->gain) + { + case ADI_SENSE_ADC_GAIN_1X: + /* TODO - add enum for gain options in register map headers */ + sensorDetailsReg.PGA_Gain = 0; + break; + case ADI_SENSE_ADC_GAIN_2X: + sensorDetailsReg.PGA_Gain = 1; + break; + case ADI_SENSE_ADC_GAIN_4X: + sensorDetailsReg.PGA_Gain = 2; + break; + case ADI_SENSE_ADC_GAIN_8X: + sensorDetailsReg.PGA_Gain = 3; + break; + case ADI_SENSE_ADC_GAIN_16X: + sensorDetailsReg.PGA_Gain = 4; + break; + case ADI_SENSE_ADC_GAIN_32X: + sensorDetailsReg.PGA_Gain = 5; + break; + case ADI_SENSE_ADC_GAIN_64X: + sensorDetailsReg.PGA_Gain = 6; + break; + case ADI_SENSE_ADC_GAIN_128X: + sensorDetailsReg.PGA_Gain = 7; + break; + default: + return ADI_SENSE_INVALID_PARAM; + } + + if (pAdcChannelConfig->enableVbias) + sensorDetailsReg.Vbias = 1; + else + sensorDetailsReg.Vbias = 0; + + if (pAdcChannelConfig->reference.disableBuffer) + sensorDetailsReg.Reference_Buffer_Disable = 1; + else + sensorDetailsReg.Reference_Buffer_Disable = 0; + + if (pChannelConfig->disablePublishing) + sensorDetailsReg.Do_Not_Publish = 1; + else + sensorDetailsReg.Do_Not_Publish = 0; + + return adi_sense_WriteRegister(hDevice, REG_ADISENSE_CORE_SENSOR_DETAILSn(eChannelId), + &sensorDetailsReg, sizeof(sensorDetailsReg)); +} + +static ADI_SENSE_RESULT adi_sense_SetChannelAdcFilter( + ADI_SENSE_DEVICE_HANDLE hDevice, + ADI_SENSE_CHANNEL_ID eChannelId, + ADI_SENSE_ADC_FILTER_CONFIG *pFilterConfig) +{ + ADI_ADISENSE_CORE_Filter_Select_t filterSelectReg = { + .VALUE32 = REG_ADISENSE_CORE_FILTER_SELECTn_RESET + }; + + if (pFilterConfig->type == ADI_SENSE_ADC_FILTER_SINC4) + { + filterSelectReg.ADC_Filter_Type = ADISENSE_CORE_FILTER_SELECT_FILTER_SINC4; + filterSelectReg.ADC_FS = pFilterConfig->fs; + } + else if (pFilterConfig->type == ADI_SENSE_ADC_FILTER_FIR_20SPS) + { + filterSelectReg.ADC_Filter_Type = ADISENSE_CORE_FILTER_SELECT_FILTER_FIR_20SPS; + } + else if (pFilterConfig->type == ADI_SENSE_ADC_FILTER_FIR_25SPS) + { + filterSelectReg.ADC_Filter_Type = ADISENSE_CORE_FILTER_SELECT_FILTER_FIR_25SPS; + } + else + { + return ADI_SENSE_INVALID_PARAM; + } + + return adi_sense_WriteRegister(hDevice, REG_ADISENSE_CORE_FILTER_SELECTn(eChannelId), + &filterSelectReg, sizeof(filterSelectReg)); +} + +static ADI_SENSE_RESULT adi_sense_SetChannelAdcCurrentConfig( + ADI_SENSE_DEVICE_HANDLE hDevice, + ADI_SENSE_CHANNEL_ID eChannelId, + ADI_SENSE_ADC_CURRENT_CONFIG *pCurrentConfig) +{ + ADI_ADISENSE_CORE_Channel_Excitation_t channelExcitationReg = { + .VALUE8 = REG_ADISENSE_CORE_CHANNEL_EXCITATIONn_RESET + }; + + if (pCurrentConfig->outputLevel == ADI_SENSE_ADC_CURRENT_LEVEL_NONE) + { + /* TODO - how should the IOUT0/1 Disable options be represented on the API? */ + channelExcitationReg.IOUT0_Disable = 1; + channelExcitationReg.IOUT1_Disable = 1; + + channelExcitationReg.IOUT_Excitation_Current = ADISENSE_CORE_CHANNEL_EXCITATION_IEXC_OFF; + } + else + { + channelExcitationReg.IOUT0_Disable = 0; + channelExcitationReg.IOUT1_Disable = 0; + + if (pCurrentConfig->outputLevel == ADI_SENSE_ADC_CURRENT_LEVEL_50uA) + channelExcitationReg.IOUT_Excitation_Current = ADISENSE_CORE_CHANNEL_EXCITATION_IEXC_50UA; + else if (pCurrentConfig->outputLevel == ADI_SENSE_ADC_CURRENT_LEVEL_100uA) + channelExcitationReg.IOUT_Excitation_Current = ADISENSE_CORE_CHANNEL_EXCITATION_IEXC_100UA; + else if (pCurrentConfig->outputLevel == ADI_SENSE_ADC_CURRENT_LEVEL_250uA) + channelExcitationReg.IOUT_Excitation_Current = ADISENSE_CORE_CHANNEL_EXCITATION_IEXC_250UA; + else if (pCurrentConfig->outputLevel == ADI_SENSE_ADC_CURRENT_LEVEL_500uA) + channelExcitationReg.IOUT_Excitation_Current = ADISENSE_CORE_CHANNEL_EXCITATION_IEXC_500UA; + else if (pCurrentConfig->outputLevel == ADI_SENSE_ADC_CURRENT_LEVEL_750uA) + channelExcitationReg.IOUT_Excitation_Current = ADISENSE_CORE_CHANNEL_EXCITATION_IEXC_750UA; + else if (pCurrentConfig->outputLevel == ADI_SENSE_ADC_CURRENT_LEVEL_1000uA) + channelExcitationReg.IOUT_Excitation_Current = ADISENSE_CORE_CHANNEL_EXCITATION_IEXC_1000UA; + else + return ADI_SENSE_INVALID_PARAM; + + if (pCurrentConfig->sourceOption == ADI_SENSE_ADC_CURRENT_SOURCE_DEFAULT) + { + channelExcitationReg.IOUT_Dont_Swap_3Wire = 1; + channelExcitationReg.IOUT_Static_Swap_3Wire = 0; + } + else if (pCurrentConfig->sourceOption == ADI_SENSE_ADC_CURRENT_SOURCE_SWAP_STATIC) + { + channelExcitationReg.IOUT_Dont_Swap_3Wire = 1; + channelExcitationReg.IOUT_Static_Swap_3Wire = 1; + } + else if (pCurrentConfig->sourceOption == ADI_SENSE_ADC_CURRENT_SOURCE_SWAP_DYNAMIC) + { + channelExcitationReg.IOUT_Dont_Swap_3Wire = 0; + channelExcitationReg.IOUT_Static_Swap_3Wire = 0; + } + else + { + return ADI_SENSE_INVALID_PARAM; + } + } + + return adi_sense_WriteRegister(hDevice, + REG_ADISENSE_CORE_CHANNEL_EXCITATIONn(eChannelId), + &channelExcitationReg, + sizeof(channelExcitationReg)); +} + +ADI_SENSE_RESULT adi_sense_SetAdcChannelConfig( + ADI_SENSE_DEVICE_HANDLE hDevice, + ADI_SENSE_CHANNEL_ID eChannelId, + ADI_SENSE_CHANNEL_CONFIG *pChannelConfig) +{ + ADI_SENSE_RESULT eRet; + ADI_SENSE_ADC_CHANNEL_CONFIG *pAdcChannelConfig = + &pChannelConfig->adcChannelConfig; + + eRet = adi_sense_SetChannelAdcSensorType(hDevice, eChannelId, + pAdcChannelConfig->sensor); + if (eRet) + { + return eRet; + } + + eRet = adi_sense_SetChannelAdcSensorDetails(hDevice, eChannelId, + pChannelConfig); + if (eRet) + { + return eRet; + } + + eRet = adi_sense_SetChannelAdcFilter(hDevice, eChannelId, + &pAdcChannelConfig->filter); + if (eRet) + { + return eRet; + } + + eRet = adi_sense_SetChannelAdcCurrentConfig(hDevice, eChannelId, + &pAdcChannelConfig->current); + if (eRet) + { + return eRet; + } + + return ADI_SENSE_SUCCESS; +} + +static ADI_SENSE_RESULT adi_sense_SetChannelI2cSensorType( + ADI_SENSE_DEVICE_HANDLE hDevice, + ADI_SENSE_CHANNEL_ID eChannelId, + ADI_SENSE_I2C_SENSOR_TYPE sensorType) +{ + ADI_ADISENSE_CORE_Sensor_Type_t sensorTypeReg = { + .VALUE16 = REG_ADISENSE_CORE_SENSOR_TYPEn_RESET + }; + + /* Ensure that the sensor type is valid for this channel */ + switch(sensorType) + { + case ADI_SENSE_I2C_SENSOR_HUMIDITY_HONEYWELL_HIH: + sensorTypeReg.Sensor_Type = ADISENSE_CORE_SENSOR_TYPE_SENSOR_I2C_HUMIDITY_HONEYWELL_HUMIDICON; + break; + case ADI_SENSE_I2C_SENSOR_HUMIDITY_SENSIRION_SHT35: + sensorTypeReg.Sensor_Type = ADISENSE_CORE_SENSOR_TYPE_SENSOR_I2C_HUMIDITY_SENSIRION_SHT3X; + break; + default: + /* TODO - add support for custom I2C sensors */ + return ADI_SENSE_INVALID_PARAM; + } + + return adi_sense_WriteRegister(hDevice, REG_ADISENSE_CORE_SENSOR_TYPEn(eChannelId), + &sensorTypeReg, sizeof(sensorTypeReg)); +} + +static ADI_SENSE_RESULT adi_sense_SetChannelI2cSensorAddress( + ADI_SENSE_DEVICE_HANDLE hDevice, + ADI_SENSE_CHANNEL_ID eChannelId, + uint32_t deviceAddress) +{ + ADI_ADISENSE_CORE_Digital_Sensor_Address_t sensorAddressReg = { + .VALUE8 = REG_ADISENSE_CORE_DIGITAL_SENSOR_ADDRESSn_RESET + }; + + sensorAddressReg.Digital_Sensor_Address = deviceAddress; + + return adi_sense_WriteRegister(hDevice, + REG_ADISENSE_CORE_DIGITAL_SENSOR_ADDRESSn(eChannelId), + &sensorAddressReg, sizeof(sensorAddressReg)); +} + +ADI_SENSE_RESULT adi_sense_SetI2cChannelConfig( + ADI_SENSE_DEVICE_HANDLE hDevice, + ADI_SENSE_CHANNEL_ID eChannelId, + ADI_SENSE_I2C_CHANNEL_CONFIG *pI2cChannelConfig) +{ + ADI_SENSE_RESULT eRet; + + eRet = adi_sense_SetChannelI2cSensorType(hDevice, eChannelId, + pI2cChannelConfig->sensor); + if (eRet) + { + return eRet; + } + + eRet = adi_sense_SetChannelI2cSensorAddress(hDevice, eChannelId, + pI2cChannelConfig->deviceAddress); + if (eRet) + { + return eRet; + } + + return ADI_SENSE_SUCCESS; +} + +static ADI_SENSE_RESULT adi_sense_SetChannelSpiSensorType( + ADI_SENSE_DEVICE_HANDLE hDevice, + ADI_SENSE_CHANNEL_ID eChannelId, + ADI_SENSE_SPI_SENSOR_TYPE sensorType) +{ + ADI_ADISENSE_CORE_Sensor_Type_t sensorTypeReg = { + .VALUE16 = REG_ADISENSE_CORE_SENSOR_TYPEn_RESET + }; + + /* Ensure that the sensor type is valid for this channel */ + switch(sensorType) + { + case ADI_SENSE_SPI_SENSOR_PRESSURE_HONEYWELL_HSCDRN1: + sensorTypeReg.Sensor_Type = ADISENSE_CORE_SENSOR_TYPE_SENSOR_SPI_PRESSURE_HONEYWELL_TRUSTABILITY; + break; + case ADI_SENSE_SPI_SENSOR_PRESSURE_ADI_ADXL362: + sensorTypeReg.Sensor_Type = ADISENSE_CORE_SENSOR_TYPE_SENSOR_SPI_ACCELEROMETER_1; + break; + default: + /* TODO - add support for custom SPI sensors */ + return ADI_SENSE_INVALID_PARAM; + } + + return adi_sense_WriteRegister(hDevice, REG_ADISENSE_CORE_SENSOR_TYPEn(eChannelId), + &sensorTypeReg, sizeof(sensorTypeReg)); +} + +ADI_SENSE_RESULT adi_sense_SetSpiChannelConfig( + ADI_SENSE_DEVICE_HANDLE hDevice, + ADI_SENSE_CHANNEL_ID eChannelId, + ADI_SENSE_SPI_CHANNEL_CONFIG *pSpiChannelConfig) +{ + ADI_SENSE_RESULT eRet; + + eRet = adi_sense_SetChannelSpiSensorType(hDevice, eChannelId, + pSpiChannelConfig->sensor); + if (eRet) + { + return eRet; + } + + return ADI_SENSE_SUCCESS; +} + +ADI_SENSE_RESULT adi_sense_SetChannelThresholdLimits( + ADI_SENSE_DEVICE_HANDLE hDevice, + ADI_SENSE_CHANNEL_ID eChannelId, + float32_t highThresholdLimit, + float32_t lowThresholdLimit) +{ + ADI_ADISENSE_CORE_High_Threshold_Limit_t highLimitReg = { + .High_Threshold = highThresholdLimit + }; + ADI_ADISENSE_CORE_Low_Threshold_Limit_t lowLimitReg = { + .Low_Threshold = lowThresholdLimit + }; + ADI_SENSE_RESULT eRet; + + eRet = adi_sense_WriteRegister(hDevice, + REG_ADISENSE_CORE_HIGH_THRESHOLD_LIMITn(eChannelId), + &highLimitReg, sizeof(highLimitReg)); + if (eRet) + { + return eRet; + } + + return adi_sense_WriteRegister(hDevice, + REG_ADISENSE_CORE_LOW_THRESHOLD_LIMITn(eChannelId), + &lowLimitReg, sizeof(lowLimitReg));; +} + +ADI_SENSE_RESULT adi_sense_SetChannelSettlingTime( + ADI_SENSE_DEVICE_HANDLE hDevice, + ADI_SENSE_CHANNEL_ID eChannelId, + uint32_t settlingTime) +{ + ADI_ADISENSE_CORE_Settling_Time_t settlingTimeReg = { + .VALUE16 = REG_ADISENSE_CORE_SETTLING_TIMEn_RESET + }; + + settlingTimeReg.Settling_Time = settlingTime; + + return adi_sense_WriteRegister(hDevice, REG_ADISENSE_CORE_SETTLING_TIMEn(eChannelId), + &settlingTimeReg, sizeof(settlingTimeReg)); +} + +ADI_SENSE_RESULT adi_sense_SetChannelConfig( + ADI_SENSE_DEVICE_HANDLE hDevice, + ADI_SENSE_CHANNEL_ID eChannelId, + ADI_SENSE_CHANNEL_CONFIG *pChannelConfig) +{ + ADI_SENSE_RESULT eRet; + + /* If the channel is not enabled, disable it and return */ + if (! pChannelConfig->enableChannel) + return adi_sense_SetChannelCount(hDevice, eChannelId, 0); + + eRet = adi_sense_SetChannelCount(hDevice, eChannelId, + pChannelConfig->measurementsPerCycle); + if (eRet) + return eRet; + + switch (eChannelId) + { + case ADI_SENSE_CHANNEL_ID_CJC_0: + case ADI_SENSE_CHANNEL_ID_CJC_1: + case ADI_SENSE_CHANNEL_ID_SENSOR_0: + case ADI_SENSE_CHANNEL_ID_SENSOR_1: + case ADI_SENSE_CHANNEL_ID_SENSOR_2: + case ADI_SENSE_CHANNEL_ID_SENSOR_3: + case ADI_SENSE_CHANNEL_ID_VOLTAGE_0: + case ADI_SENSE_CHANNEL_ID_CURRENT_0: + eRet = adi_sense_SetAdcChannelConfig(hDevice, eChannelId, pChannelConfig); + break; + case ADI_SENSE_CHANNEL_ID_I2C_0: + case ADI_SENSE_CHANNEL_ID_I2C_1: + eRet = adi_sense_SetI2cChannelConfig(hDevice, eChannelId, + &pChannelConfig->i2cChannelConfig); + break; + case ADI_SENSE_CHANNEL_ID_SPI_0: + eRet = adi_sense_SetSpiChannelConfig(hDevice, eChannelId, + &pChannelConfig->spiChannelConfig); + break; + default: + return ADI_SENSE_INVALID_PARAM; + } + + eRet = adi_sense_SetChannelThresholdLimits(hDevice, eChannelId, + pChannelConfig->measurementMaxValue, + pChannelConfig->measurementMinValue); + if (eRet) + { + return eRet; + } + + eRet = adi_sense_SetChannelSettlingTime(hDevice, eChannelId, + pChannelConfig->extraSettlingTime); + if (eRet) + { + return eRet; + } + + return ADI_SENSE_SUCCESS; +} + +ADI_SENSE_RESULT adi_sense_SetDeviceConfig( + ADI_SENSE_DEVICE_HANDLE const hDevice, + ADI_SENSE_CONFIG * const pConfig) +{ + ADI_SENSE_DEVICE_CONTEXT *pCtx = hDevice; + ADI_SENSE_DEVICE_CONFIG *pDeviceConfig; + ADI_SENSE_PRODUCT_ID productId; + ADI_SENSE_RESULT eRet; + + if (pCtx->nDeviceIndex >= pConfig->numDevices) + { + return ADI_SENSE_INVALID_DEVICE_NUM; + } + pDeviceConfig = &pConfig->devices[pCtx->nDeviceIndex]; + + /* Check that the Product ID is a match? */ + eRet = adi_sense_GetProductID(hDevice, &productId); + if (eRet) + { + return eRet; + } + if (pDeviceConfig->productId != productId) + { + return ADI_SENSE_INVALID_PARAM; + } + + eRet = adi_sense_SetPowerConfig(hDevice, &pDeviceConfig->power); + if (eRet) + { + return eRet; + } + + eRet = adi_sense_SetMeasurementConfig(hDevice, &pDeviceConfig->measurement); + if (eRet) + { + return eRet; + } + + eRet = adi_sense_SetDiagnosticsConfig(hDevice, &pDeviceConfig->diagnostics); + if (eRet) + { + return eRet; + } + + for (ADI_SENSE_CHANNEL_ID id = 0; id < ADI_SENSE_MAX_CHANNELS; id++) + { + eRet = adi_sense_SetChannelConfig(hDevice, id, + &pDeviceConfig->channels[id]); + if (eRet) + { + return eRet; + } + } + + return ADI_SENSE_SUCCESS; +} + +ADI_SENSE_RESULT adi_sense_SetDspData( + ADI_SENSE_DEVICE_HANDLE const hDevice, + ADI_SENSE_DSP_LUT_RAW * const pDspData) +{ + ADI_SENSE_DSP_LUT *pLut = (ADI_SENSE_DSP_LUT *)pDspData; + ADI_SENSE_DSP_LUT_TABLE *pLutTable = pLut->tables; + unsigned actualLength = 0; + + if (pLut->header.signature != ADI_SENSE_DSP_LUT_SIGNATURE) + { + ADI_SENSE_LOG_ERROR("DSP LUT signature incorrect (expected 0x%X, actual 0x%X)", + ADI_SENSE_DSP_LUT_SIGNATURE, pLut->header.signature); + return ADI_SENSE_INVALID_SIGNATURE; + } + + for (unsigned i = 0; i < pLut->header.numTables; i++) + { + ADI_SENSE_DSP_LUT_DESCRIPTOR *pDesc = &pLutTable->descriptor; + unsigned short calculatedCrc; + + switch (pDesc->geometry) + { + case ADI_SENSE_DSP_LUT_GEOMETRY_COEFFICIENT_LIST: + case ADI_SENSE_DSP_LUT_GEOMETRY_EQUALLY_SPACED_1D: + case ADI_SENSE_DSP_LUT_GEOMETRY_EQUALLY_SPACED_2D: + break; + default: + ADI_SENSE_LOG_ERROR("Invalid geometry %u specified for DSP LUT table %u", + pDesc->geometry, i); + return ADI_SENSE_INVALID_PARAM; + } + + switch (pDesc->equation) + { + case ADI_SENSE_DSP_LUT_EQUATION_NONE: + case ADI_SENSE_DSP_LUT_EQUATION_POLYNOMIAL: + case ADI_SENSE_DSP_LUT_EQUATION_POLYNOMIAL_EXPONENTIAL: + case ADI_SENSE_DSP_LUT_EQUATION_QUADRATIC: + case ADI_SENSE_DSP_LUT_EQUATION_EXPONENTIAL: + case ADI_SENSE_DSP_LUT_EQUATION_LOGARITHMIC: + break; + default: + ADI_SENSE_LOG_ERROR("Invalid equation %u specified for DSP LUT table %u", + pDesc->equation, i); + return ADI_SENSE_INVALID_PARAM; + } + + switch (pDesc->function) + { + case ADI_SENSE_DSP_LUT_FUNCTION_TCJ_MV2C: + case ADI_SENSE_DSP_LUT_FUNCTION_TCJ_C2MV: + case ADI_SENSE_DSP_LUT_FUNCTION_TCK_MV2C: + case ADI_SENSE_DSP_LUT_FUNCTION_TCK_C2MV: + case ADI_SENSE_DSP_LUT_FUNCTION_TCT_MV2C: + case ADI_SENSE_DSP_LUT_FUNCTION_TCT_C2MV: + case ADI_SENSE_DSP_LUT_FUNCTION_RTD_OHM2C: + case ADI_SENSE_DSP_LUT_FUNCTION_VOUT_V2BAR: + case ADI_SENSE_DSP_LUT_FUNCTION_VOUT_V2PSI: + case ADI_SENSE_DSP_LUT_FUNCTION_IOUT_MA2PSI: + break; + default: + ADI_SENSE_LOG_ERROR("Invalid function %u specified for DSP LUT table %u", + pDesc->function, i); + return ADI_SENSE_INVALID_PARAM; + } + + switch (pDesc->vectorFormat) + { + case ADI_SENSE_DSP_LUT_VECTOR_FORMAT_FLOAT32: + case ADI_SENSE_DSP_LUT_VECTOR_FORMAT_FLOAT64: + break; + default: + ADI_SENSE_LOG_ERROR("Invalid vector format %u specified for DSP LUT table %u", + pDesc->vectorFormat, i); + return ADI_SENSE_INVALID_PARAM; + } + + if (((pDesc->rangeMin != NAN) && (pDesc->rangeMax != NAN)) && + (pDesc->rangeMax <= pDesc->rangeMin)) + { + ADI_SENSE_LOG_ERROR("Invalid range specified for DSP LUT table %u", i); + return ADI_SENSE_INVALID_PARAM; + } + + calculatedCrc = crc16_ccitt(pLutTable->data, pDesc->dataLength); + if (calculatedCrc != pDesc->crc16) + { + ADI_SENSE_LOG_ERROR("CRC validation failed on DSP LUT table %u (expected 0x%04X, actual 0x%04X)", + i, pDesc->crc16, calculatedCrc); + return ADI_SENSE_CRC_ERROR; + } + + actualLength += sizeof(*pDesc) + pDesc->dataLength; + + /* Move to the next look-up table */ + pLutTable = (ADI_SENSE_DSP_LUT_TABLE *)(pLutTable->data + pDesc->dataLength); + } + + if (actualLength != pLut->header.totalLength) + { + ADI_SENSE_LOG_ERROR("DSP LUT table length mismatch (expected %u, actual %u)", + pLut->header.totalLength, actualLength); + return ADI_SENSE_WRONG_SIZE; + } + + /* TODO - add a check to ensure that the total length doesn't exceed the maximum internal LUT storage size (TBD) */ + + /* TODO - write the LUT data to the device */ + + return ADI_SENSE_SUCCESS; +} + +#define CAL_TABLE_ROWS 56 +#define CAL_TABLE_COLS 3 +#define CAL_TABLE_SIZE (sizeof(float) * CAL_TABLE_ROWS * CAL_TABLE_COLS) + +/*! + * @brief Read the contents of the ADISense internal calibration table + * + * Calibration coefficients/gains/offsets are stored internally in a table + * of 56x3 32-bit floating point values + * + * @param[in] uint8_t* : Pointer to destination buffer for the calibration data + * @param[in] maxLen : The buffer capacity in bytes (minimum 672 bytes) + * @param[out] dataLen : The number of bytes written to the buffer + * @param[out] nRows : The number of rows in the table (56) + * @param[out] nRows : The number of columns in the table (3) + * + * @return Status + * - #ADI_SENSE_SUCCESS Call completed successfully. + * - #ADI_SENSE_FAILURE + * - #ADI_SENSE_INVALID_OPERATION Invalid register identifier. + */ +ADI_SENSE_RESULT adi_sense_ReadCalTable( + ADI_SENSE_DEVICE_HANDLE hDevice, + float *buffer, + unsigned maxLen, + unsigned *dataLen, + unsigned *nRows, + unsigned *nColumns) +{ + ADI_SENSE_RESULT eRet; + ADI_ADISENSE_CORE_CAL_Offset_t calOffsetReg; + + *dataLen = 0; + *nRows = CAL_TABLE_ROWS; + *nColumns = CAL_TABLE_COLS; + /* Read back in 256-byte chunks due to limitation in underlying layer */ + for (unsigned offset = 0; offset < CAL_TABLE_SIZE; offset += 256) + { + unsigned readSize = 256; + + if (readSize > maxLen) + readSize = maxLen; + + if (readSize > (CAL_TABLE_SIZE - offset)) + readSize = CAL_TABLE_SIZE - offset; + + calOffsetReg.CAL_Offset = offset; + eRet = adi_sense_WriteRegister(hDevice, REG_ADISENSE_CORE_CAL_OFFSET, + &calOffsetReg, sizeof(calOffsetReg)); + if (eRet) + { + return eRet; + } + + eRet = adi_sense_ReadRegister(hDevice, REG_ADISENSE_CORE_CAL_DATA, + (uint8_t *)buffer + offset, readSize); + if (eRet) + { + return eRet; + } + *dataLen += readSize; + } + + return ADI_SENSE_SUCCESS; +} +