Mbed Development branch for MeasrueWare

common/utils.c

Committer:
Vkadaba
Date:
2020-05-20
Revision:
79:dddd92b3a416
Parent:
78:2301b7437902

File content as of revision 79:dddd92b3a416:

/*
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:  utils.c
 * @brief: Interface Utilities for ADMW1001
 *-----------------------------------------------------------------------------
 */
#include <stdlib.h>

#include "utils.h"
#include "admw_log.h"

void utils_printStatus(
    ADMW_STATUS *pStatus)
{
    ADMW_LOG_INFO("Status Summary:");

    if (pStatus->deviceStatus == 0) {
        ADMW_LOG_INFO("\tNo errors detected");
    } else {
        if (pStatus->deviceStatus & ADMW_DEVICE_STATUS_BUSY)
            ADMW_LOG_INFO("\tCommand running");
        if (pStatus->deviceStatus & ADMW_DEVICE_STATUS_DATAREADY)
            ADMW_LOG_INFO("\tData ready");
        if (pStatus->deviceStatus & ADMW_DEVICE_STATUS_ERROR)
            ADMW_LOG_INFO("\tActive Errors - RESET REQUIRED");
        if (pStatus->deviceStatus & ADMW_DEVICE_STATUS_FIFO_ERROR)
        {
            ADMW_LOG_INFO("\tActive FIFO Errors - ATTENTION REQUIRED");
            ADMW_LOG_INFO("\tFIFO buffer full");
            ADMW_LOG_INFO("\tExceeding measurements per cycle limit of %d",MAXIMUM_MEASUREMENTS_PER_CYCLE);
        }
        if (pStatus->deviceStatus & ADMW_DEVICE_STATUS_CONFIG_ERROR)
            ADMW_LOG_INFO("\tActive Configuration Errors - ATTENTION REQUIRED");
        if (pStatus->deviceStatus & ADMW_DEVICE_STATUS_ALERT) {
            ADMW_LOG_INFO("\tActive Alerts - ATTENTION REQUIRED:");

            for (unsigned i = 0; i < ADMW1001_MAX_CHANNELS; i++) {
                if (pStatus->channelAlerts[i] == 0)
                    continue;

                ADMW_LOG_INFO("\t\tChannel #%u:", i);
                if (pStatus->channelAlerts[i] & ADMW_ALERT_DETAIL_CH_ADC_NEAR_OVERRANGE)
                    ADMW_LOG_INFO("\t\t\ADC near overrange detected");
                if (pStatus->channelAlerts[i] & ADMW_ALERT_DETAIL_CH_SENSOR_UNDERRANGE)
                    ADMW_LOG_INFO("\t\t\tSensor underrange detected");
                if (pStatus->channelAlerts[i] & ADMW_ALERT_DETAIL_CH_SENSOR_OVERRANGE)
                    ADMW_LOG_INFO("\t\t\tSensor overrange detected");
                if (pStatus->channelAlerts[i] & ADMW_ALERT_DETAIL_CH_CJ_SOFT_FAULT)
                    ADMW_LOG_INFO("\t\t\tCJC soft fault alert detected");
                if (pStatus->channelAlerts[i] & ADMW_ALERT_DETAIL_CH_CJ_HARD_FAULT)
                    ADMW_LOG_INFO("\t\t\tCJC hard fault alert detected");
                if (pStatus->channelAlerts[i] & ADMW_ALERT_DETAIL_CH_ADC_INPUT_OVERRANGE)
                    ADMW_LOG_INFO("\t\t\tADC input overranage alert detected");
                if (pStatus->channelAlerts[i] & ADMW_ALERT_DETAIL_CH_SENSOR_HARDFAULT)
                    ADMW_LOG_INFO("\t\t\tChannel sensor hardfault alert detected");
            }
        }

        if ((pStatus->deviceStatus & ADMW_DEVICE_STATUS_ERROR) ||
                (pStatus->deviceStatus & ADMW_DEVICE_STATUS_ALERT)) {
            ADMW_LOG_INFO("\t\tLast Debug Code: %u-%u",
                          (pStatus->debugCode >> 16) & 0xFFFF,
                          (pStatus->debugCode >> 0) & 0xFFFF);
        }
    }
}

void utils_printSamples(
    ADMW_DATA_SAMPLE *pSampleBuffer,
    uint32_t nNumSamples,
    ADMW_MEASUREMENT_MODE eMeasurementMode)
{
    for (uint32_t i = 0; i < nNumSamples; i++) {
        ADMW_LOG_INFO("Sample # %2d Channel_ID # %2d :: Raw_Sample %.9f  :: processedValue %f :: flags: ERROR:%X , ALERT:%X",
                      i+1,
                      pSampleBuffer[i].Channel_ID,
                      pSampleBuffer[i].Raw_Sample,
                      pSampleBuffer[i].processedValue,
                      pSampleBuffer[i].Ch_Error,
                      pSampleBuffer[i].Ch_Alert);
    }
}

static void gpioCallbackFn(ADMW_GPIO_PIN ePinId, void * pArg)
{
    volatile bool *pbFlag = (volatile bool *)pArg;
    *pbFlag = true;
}

ADMW_RESULT utils_registerCallbacks(
    ADMW_DEVICE_HANDLE hDevice,
    volatile bool *pbDataReady,
    volatile bool *pbError,
    volatile bool *pbAlert)
{
    ADMW_RESULT res;
    bool state;

    res = admw_RegisterGpioCallback(hDevice, ADMW_GPIO_PIN_DATAREADY,
                                    gpioCallbackFn, (void *)pbDataReady);
    if (res != ADMW_SUCCESS) {
        ADMW_LOG_ERROR("Failed to register DATAREADY callback");
        return res;
    }

    res = admw_GetGpioState(hDevice, ADMW_GPIO_PIN_ALERT_ERROR, &state);
    if (res != ADMW_SUCCESS) {
        ADMW_LOG_ERROR("Failed to get current ERROR state");
        return res;
    }
    if (state) {
        ADMW_LOG_ERROR("ERROR signal already asserted");
        return ADMW_FAILURE;
    }
    res = admw_RegisterGpioCallback(hDevice, ADMW_GPIO_PIN_ALERT_ERROR,
                                    gpioCallbackFn, (void *)pbError);
    if (res != ADMW_SUCCESS) {
        ADMW_LOG_ERROR("Failed to register ERROR callback");
        return res;
    }

    /*res = admw_GetGpioState(hDevice, ADMW_GPIO_PIN_ALERT, &state);
    if (res != ADMW_SUCCESS)
    {
        ADMW_LOG_ERROR("Failed to get current ALERT state");
        return res;
    }
    if (state)
    {
        ADMW_LOG_ERROR("ALERT signal already asserted");
        return ADMW_FAILURE;
    }*/
    res = admw_RegisterGpioCallback(hDevice, ADMW_GPIO_PIN_ALERT_ERROR,
                                    gpioCallbackFn, (void *)pbAlert);
    if (res != ADMW_SUCCESS) {
        ADMW_LOG_ERROR("Failed to register ALERT callback");
        return res;
    }

    return ADMW_SUCCESS;
}

ADMW_RESULT utils_deregisterCallbacks(
    ADMW_DEVICE_HANDLE hDevice)
{
    ADMW_RESULT res;

    res = admw_RegisterGpioCallback(hDevice, ADMW_GPIO_PIN_DATAREADY,
                                    NULL, NULL);
    if (res != ADMW_SUCCESS) {
        ADMW_LOG_ERROR("Failed to deregister DATAREADY callback");
        return res;
    }

    res = admw_RegisterGpioCallback(hDevice, ADMW_GPIO_PIN_ALERT_ERROR,
                                    NULL, NULL);
    if (res != ADMW_SUCCESS) {
        ADMW_LOG_ERROR("Failed to deregister ERROR callback");
        return res;
    }

    res = admw_RegisterGpioCallback(hDevice, ADMW_GPIO_PIN_ALERT_ERROR,
                                    NULL, NULL);
    if (res != ADMW_SUCCESS) {
        ADMW_LOG_INFO("Failed to deregister ALERT callback");
        return res;
    }

    return ADMW_SUCCESS;
}

ADMW_RESULT utils_runMeasurement(
    ADMW_DEVICE_HANDLE hDevice,
    ADMW_MEASUREMENT_MODE eMeasurementMode)
{
    ADMW_RESULT res;
    volatile bool bDataReady = false;
    volatile bool bError = false;
    volatile bool bAlert = false;
    res = utils_registerCallbacks(hDevice, &bDataReady, &bError, &bAlert);
    if (res != ADMW_SUCCESS)
        return res;

    /*
     * Retrieve the number of samples per cycle, per DATAREADY pulse, etc. for
     * this configuration.
     */
    ADMW1001_OPERATING_MODE eOperatingMode;
    ADMW1001_DATAREADY_MODE eDataReadyMode;
    uint32_t nSamplesPerDataready;
    uint32_t nSamplesPerCycle;
    uint8_t nBytesPerSample;
    res = admw1001_GetDataReadyModeInfo(hDevice,
                                        eMeasurementMode,
                                        &eOperatingMode,
                                        &eDataReadyMode,
                                        &nSamplesPerDataready,
                                        &nSamplesPerCycle,
                                        &nBytesPerSample);
    if (res != ADMW_SUCCESS)
        return res;

    /*
     * Allocate a buffer to store the samples retrieved on each DATAREADY pulse
     */
    ADMW_DATA_SAMPLE *pSampleBuffer;
    pSampleBuffer = malloc(sizeof(ADMW_DATA_SAMPLE) *
                           nSamplesPerDataready);
    if (pSampleBuffer == NULL) {
        ADMW_LOG_ERROR("Failed to allocate sample buffer");
        return ADMW_NO_MEM;
    }

    /*
     * Kick off the measurement cycle(s) here
     */
    ADMW_LOG_INFO("Starting measurement");
    res = admw_StartMeasurement(hDevice, eMeasurementMode);
    if (res != ADMW_SUCCESS) {
        ADMW_LOG_ERROR("Failed to start measurement");
        return res;
    }

    /*
     * Loop continuously unless operating mode is single-cycle
     */
    uint32_t nSampleCount = 0;
    uint32_t nReturned;
    while (true) {
        ADMW_STATUS status;

        /*
         * Wait until the next batch of 1 or more samples is ready, continuously
         * checking DATAREADY until it is asserted
         */
        res = admw_GetStatus(hDevice, &status);
         if (res != ADMW_SUCCESS) {
            ADMW_LOG_ERROR("Failed to retrieve device status");
            }
         if (status.deviceStatus & ADMW_DEVICE_STATUS_FIFO_ERROR)
         {
            utils_printStatus(&status);
            break;
         }
          while (! (bDataReady || bError));
            if (!bError) {
                /*
                 * Get data samples from the measurement cycle, if no error has occurred
                 */
                bDataReady = false;
                res = admw_GetData(hDevice, eMeasurementMode, pSampleBuffer,
                                   nBytesPerSample, nSamplesPerDataready,
                                   &nReturned);
                if (res != ADMW_SUCCESS) {
                    if (res == ADMW_INCOMPLETE) {
                        /*
                         * This is expected in cases where cycleSkipCount may
                         * be non-zero for some channels, resulting in
                         * variable-length sequences
                         */
                         
                        ADMW_LOG_DEBUG("Retrieved %u of %u requested data samples",
                                       nReturned, nSamplesPerDataready);           
                        continue;
                    } else {
                        ADMW_LOG_WARN("Failed to get data samples from device");
                        return res;
                    }
                }

                /*
                 * Display the data samples.
                 *
                 * NOTE: this requires a sufficient idle time between subsequent
                 * DATAREADY pulses to allow printing to occur.  Otherwise,
                 * subsequent samples may be missed if not retrieved promptly when
                 * the next DATAREADY assertion occurs.
                 */
                utils_printSamples(pSampleBuffer, nReturned, eMeasurementMode);
                nSampleCount += nReturned;
            }

        /*
         * Check and print device status if errors/alerts have been triggered
         */
        if (bError || bAlert) {
            res = admw_GetStatus(hDevice, &status);
            if (res != ADMW_SUCCESS) {
                ADMW_LOG_ERROR("Failed to retrieve device status");
                return res;
            }

            if (status.deviceStatus &
                    (ADMW_DEVICE_STATUS_ERROR | ADMW_DEVICE_STATUS_ALERT)) {
                utils_printStatus(&status);

                /* Break out of the loop if any errors are raised */
                if (bError)
                    break;
            }
        }

        if (eOperatingMode == ADMW1001_OPERATING_MODE_SINGLECYCLE) {
            /*
             * In this mode, break out of the loop when the measurement command
             * has completed.
             *
             * One option is to check for the expected number of samples to be
             * returned for the cycle.  However, cycles may have variable-length
             * sequences if the cycleSkipCount option is non-zero for any of the
             * channels.
             *
             * So, instead, we check for the command-running status, which
             * will de-assert in this mode when the measurement command has
             * completed a single cycle.
             */
            bool bCommandRunning;
            res = admw_GetCommandRunningState(hDevice, &bCommandRunning);
            if (res != ADMW_SUCCESS) {
                ADMW_LOG_ERROR("Failed to get command-running status");
                return res;
            }

            if (!bCommandRunning && !bDataReady)
                break;
        }
    }

    ADMW_LOG_INFO("Stopping measurement");
    res = admw_StopMeasurement(hDevice);
    if (res != ADMW_SUCCESS) {
        ADMW_LOG_ERROR("Failed to send stop measurement");
        return res;
    }

    free(pSampleBuffer);

    res = utils_deregisterCallbacks(hDevice);
    if (res != ADMW_SUCCESS)
        return res;

    return ADMW_SUCCESS;
}