Analog Devices AD7124-8 - 8-Channel, Low Noise, Low Power, 24-Bit, Sigma-Delta ADC with PGA and Reference

Dependents:   CN0398 CN0391 CN0398_arduino

AD7124.cpp

Committer:
adisuciu
Date:
2016-11-08
Revision:
5:5fa802deb471
Parent:
4:502352a643e6

File content as of revision 5:5fa802deb471:

/**
*   @file     AD7124.cpp
*   @brief    Source file for AD7124 ADC
*   @author   Analog Devices Inc.
*
* For support please go to:
* Github: https://github.com/analogdevicesinc/mbed-adi
* Support: https://ez.analog.com/community/linux-device-drivers/microcontroller-no-os-drivers
* Product: http://www.analog.com/ad7124
* More: https://wiki.analog.com/resources/tools-software/mbed-drivers-all

********************************************************************************
* Copyright 2016(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.
*
********************************************************************************/

#include <stdint.h>
#include "mbed.h"
#include "AD7124.h"


/**
 * @brief AD7790 constructor, sets CS pin and SPI format
 * @param CS - (optional)chip select of the AD7790
 * @param MOSI - (optional)pin of the SPI interface
 * @param MISO - (optional)pin of the SPI interface
 * @param SCK  - (optional)pin of the SPI interface
 */
    
const static AD7124::ad7124_st_reg ad7124_regs_init[57] = { 
        {0x00, 0x00,   1, 2}, /* AD7124_Status */
        {0x01, 0x0000, 2, 1}, /* AD7124_ADC_Control */
        {0x02, 0x0000, 3, 2}, /* AD7124_Data */
        {0x03, 0x0000, 3, 1}, /* AD7124_IOCon1 */
        {0x04, 0x0000, 2, 1}, /* AD7124_IOCon2 */
        {0x05, 0x12,   1, 2}, /* AD7124_ID */
        {0x06, 0x0000, 3, 2}, /* AD7124_Error */
        {0x07, 0x0400, 3, 1}, /* AD7124_Error_En */
        {0x08, 0x00,   1, 2}, /* AD7124_Mclk_Count */
        {0x09, 0x8001, 2, 1}, /* AD7124_Channel_0 */
        {0x0A, 0x0001, 2, 1}, /* AD7124_Channel_1 */
        {0x0B, 0x0001, 2, 1}, /* AD7124_Channel_2 */
        {0x0C, 0x0001, 2, 1}, /* AD7124_Channel_3 */
        {0x0D, 0x0001, 2, 1}, /* AD7124_Channel_4 */
        {0x0E, 0x0001, 2, 1}, /* AD7124_Channel_5 */
        {0x0F, 0x0001, 2, 1}, /* AD7124_Channel_6 */
        {0x10, 0x0001, 2, 1}, /* AD7124_Channel_7 */
        {0x11, 0x0001, 2, 1}, /* AD7124_Channel_8 */
        {0x12, 0x0001, 2, 1}, /* AD7124_Channel_9 */
        {0x13, 0x0001, 2, 1}, /* AD7124_Channel_10 */
        {0x14, 0x0001, 2, 1}, /* AD7124_Channel_11 */
        {0x15, 0x0001, 2, 1}, /* AD7124_Channel_12 */
        {0x16, 0x0001, 2, 1}, /* AD7124_Channel_13 */
        {0x17, 0x0001, 2, 1}, /* AD7124_Channel_14 */
        {0x18, 0x0001, 2, 1}, /* AD7124_Channel_15 */
        {0x19, 0x0860, 2, 1}, /* AD7124_Config_0 */
        {0x1A, 0x0860, 2, 1}, /* AD7124_Config_1 */
        {0x1B, 0x0860, 2, 1}, /* AD7124_Config_2 */
        {0x1C, 0x0860, 2, 1}, /* AD7124_Config_3 */
        {0x1D, 0x0860, 2, 1}, /* AD7124_Config_4 */
        {0x1E, 0x0860, 2, 1}, /* AD7124_Config_5 */
        {0x1F, 0x0860, 2, 1}, /* AD7124_Config_6 */
        {0x20, 0x0860, 2, 1}, /* AD7124_Config_7 */
        {0x21, 0x060180, 3, 1}, /* AD7124_Filter_0 */
        {0x22, 0x060180, 3, 1}, /* AD7124_Filter_1 */
        {0x23, 0x060180, 3, 1}, /* AD7124_Filter_2 */
        {0x24, 0x060180, 3, 1}, /* AD7124_Filter_3 */
        {0x25, 0x060180, 3, 1}, /* AD7124_Filter_4 */
        {0x26, 0x060180, 3, 1}, /* AD7124_Filter_5 */
        {0x27, 0x060180, 3, 1}, /* AD7124_Filter_6 */
        {0x28, 0x060180, 3, 1}, /* AD7124_Filter_7 */
        {0x29, 0x800000, 3, 1}, /* AD7124_Offset_0 */
        {0x2A, 0x800000, 3, 1}, /* AD7124_Offset_1 */
        {0x2B, 0x800000, 3, 1}, /* AD7124_Offset_2 */
        {0x2C, 0x800000, 3, 1}, /* AD7124_Offset_3 */
        {0x2D, 0x800000, 3, 1}, /* AD7124_Offset_4 */
        {0x2E, 0x800000, 3, 1}, /* AD7124_Offset_5 */
        {0x2F, 0x800000, 3, 1}, /* AD7124_Offset_6 */
        {0x30, 0x800000, 3, 1}, /* AD7124_Offset_7 */
        {0x31, 0x500000, 3, 1}, /* AD7124_Gain_0 */
        {0x32, 0x500000, 3, 1}, /* AD7124_Gain_1 */
        {0x33, 0x500000, 3, 1}, /* AD7124_Gain_2 */
        {0x34, 0x500000, 3, 1}, /* AD7124_Gain_3 */
        {0x35, 0x500000, 3, 1}, /* AD7124_Gain_4 */
        {0x36, 0x500000, 3, 1}, /* AD7124_Gain_5 */
        {0x37, 0x500000, 3, 1}, /* AD7124_Gain_6 */
        {0x38, 0x500000, 3, 1}, /* AD7124_Gain_7 */
    }; 
 
AD7124::AD7124(PinName CS,
               PinName MOSI,
               PinName MISO,
               PinName SCK) :
    miso(MISO), ad7124(MOSI, MISO, SCK), cs(CS)
    
{
    cs = true; // cs is active low
    ad7124.format(8, _SPI_MODE);     
    memcpy(ad7124_regs, ad7124_regs_init, 57 * sizeof(ad7124_st_reg));
    this->regs = ad7124_regs;
    this->useCRC = false;
    
    }

/**
 * @brief Set AD7790 SPI frequency
 * @param hz - SPI bus frequency in hz
 * @return none
 */
void AD7124::frequency(int hz)
{
    ad7124.frequency(hz);
}

/**
 * @brief Resets the AD7790
 * @return none
 */
/*void AD7124::reset()
{
    ad7124.format(8, _SPI_MODE);
    cs = false;
    wait_us(_DELAY_TIMING);
    ad7124.write(_RESET);
    ad7124.write(_RESET);
    ad7124.write(_RESET);
    ad7124.write(_RESET);
    ad7124.write(_RESET);
    ad7124.write(_RESET);
    ad7124.write(_RESET);
    ad7124.write(_RESET);
    wait_us(_DELAY_TIMING);
    cs = true;
    //_continous_conversion = true;
}*/
/**
 * @brief Reads a register of the AD7790
 * @param  address - address of the register
 * @return value of the register
 */
uint16_t AD7124::read_reg(uint8_t address)
{
    uint16_t data = address << 8;
    data |= _DUMMY_BYTE;
    data |= _READ_FLAG;
    return	write_spi(data);
}

/**
 * @brief Writes a register of the AD7790
 * @param address - address of the register
 * @param reg_val - value to be written
 * @return none
 *
 */
void AD7124::write_reg(uint8_t address, uint8_t reg_val)
{
    uint16_t spi_data = address << 8;
    spi_data |= reg_val;
    write_spi(spi_data);
}

/**
 * @brief Writes 16bit data to the AD7790 SPI interface
 * @param reg_val to be written
 * @return data returned by the AD7790
 */
uint16_t AD7124::write_spi(uint16_t reg_val)
{
    uint16_t data_result;
    uint8_t upper_byte = (reg_val >> 8) & 0xFF;
    uint8_t lower_byte = reg_val & 0xFF;
    ad7124.format(8, _SPI_MODE);
    cs = false;
    data_result  =  (ad7124.write(upper_byte) << 8);
    data_result |=   ad7124.write(lower_byte);
    cs = true;
    return data_result;
}



/***************************************************************************//**
* @brief Reads the value of the specified register without checking if the
*        device is ready to accept user requests.
*
* @param device - The handler of the instance of the driver.
* @param pReg - Pointer to the register structure holding info about the
*               register to be read. The read value is stored inside the
*               register structure.
*
* @return Returns 0 for success or negative error code.
*******************************************************************************/
int32_t AD7124::NoCheckReadRegister(ad7124_st_reg* pReg)
{
    int32_t ret       = 0;
    uint8_t buffer[8] = {0, 0, 0, 0, 0, 0, 0, 0};
    uint8_t i         = 0;
    uint8_t check8    = 0;
    uint8_t msgBuf[8] = {0, 0, 0, 0, 0, 0, 0, 0};


    check8 = useCRC;

    /* Build the Command word */
    buffer[0] = AD7124_COMM_REG_WEN | AD7124_COMM_REG_RD |
                AD7124_COMM_REG_RA(pReg->addr);

    /* Read data from the device */
    ret = SPI_Read(buffer,
                   ((useCRC != AD7124_DISABLE_CRC) ? pReg->size + 1
                    : pReg->size) + 1);
    if(ret < 0)
        return ret;

    /* Check the CRC */
    if(check8 == AD7124_USE_CRC) {
        msgBuf[0] = AD7124_COMM_REG_WEN | AD7124_COMM_REG_RD |
                    AD7124_COMM_REG_RA(pReg->addr);
        for(i = 1; i < pReg->size + 2; ++i) {
            msgBuf[i] = buffer[i];
        }
        check8 = ComputeCRC8(msgBuf, pReg->size + 2);
    }

    if(check8 != 0) {
        /* ReadRegister checksum failed. */
        return COMM_ERR;
    }

    /* Build the result */
    pReg->value = 0;
    for(i = 1; i < pReg->size + 1; i++) {
        pReg->value <<= 8;
        pReg->value += buffer[i];
    }

    return ret;
}

/***************************************************************************//**
* @brief Writes the value of the specified register without checking if the
*        device is ready to accept user requests.
*
* @param device - The handler of the instance of the driver.
* @param reg - Register structure holding info about the register to be written
*
* @return Returns 0 for success or negative error code.
*******************************************************************************/
int32_t AD7124::NoCheckWriteRegister(ad7124_st_reg reg)
{
    int32_t ret      = 0;
    int32_t regValue = 0;
    uint8_t wrBuf[8] = {0, 0, 0, 0, 0, 0, 0, 0};
    uint8_t i        = 0;
    uint8_t crc8     = 0;


    /* Build the Command word */
    wrBuf[0] = AD7124_COMM_REG_WEN | AD7124_COMM_REG_WR |
               AD7124_COMM_REG_RA(reg.addr);

    /* Fill the write buffer */
    regValue = reg.value;
    for(i = 0; i < reg.size; i++) {
        wrBuf[reg.size - i] = regValue & 0xFF;
        regValue >>= 8;
    }

    /* Compute the CRC */
    if(useCRC != AD7124_DISABLE_CRC) {
        crc8 = ComputeCRC8(wrBuf, reg.size + 1);
        wrBuf[reg.size + 1] = crc8;
    }

    /* Write data to the device */
    ret = SPI_Write(wrBuf,
                    (useCRC != AD7124_DISABLE_CRC) ? reg.size + 2
                    : reg.size + 1);

    return ret;
}

/***************************************************************************//**
* @brief Reads the value of the specified register only when the device is ready
*        to accept user requests. If the device ready flag is deactivated the
*        read operation will be executed without checking the device state.
*
* @param device - The handler of the instance of the driver.
* @param pReg - Pointer to the register structure holding info about the
*               register to be read. The read value is stored inside the
*               register structure.
*
* @return Returns 0 for success or negative error code.
*******************************************************************************/
int32_t AD7124::ReadRegister(ad7124_st_reg* pReg)
{
    int32_t ret;

    if (pReg->addr != ERR_REG && check_ready) {
        ret = WaitForSpiReady(spi_rdy_poll_cnt);
        if (ret < 0)
            return ret;
    }
    ret = NoCheckReadRegister(pReg);

    return ret;
}

/***************************************************************************//**
* @brief Writes the value of the specified register only when the device is
*        ready to accept user requests. If the device ready flag is deactivated
*        the write operation will be executed without checking the device state.
*
* @param device - The handler of the instance of the driver.
* @param reg - Register structure holding info about the register to be written
*
* @return Returns 0 for success or negative error code.
*******************************************************************************/
int32_t AD7124::WriteRegister(ad7124_st_reg pReg)
{
    int32_t ret;

    if (check_ready) {
        ret = WaitForSpiReady(spi_rdy_poll_cnt);
        if (ret < 0)
            return ret;
    }
    ret = NoCheckWriteRegister(pReg);

    return ret;
}

/***************************************************************************//**
* @brief Reads and returns the value of a device register. The read value is
*        also stored in software register list of the device.
*
* @param device - The handler of the instance of the driver.
* @param reg - Which register to read from.
* @param pError - Pointer to the location where to store the error code if an
*                 error occurs. Stores 0 for success or negative error code.
*                 Does not store anything if pErorr = NULL;
*
* @return Returns the value read from the specified register.
*******************************************************************************/
uint32_t AD7124::ReadDeviceRegister(enum ad7124_registers reg)
{
    ReadRegister(&regs[reg]);
    return (regs[reg].value);
}

/***************************************************************************//**
* @brief Writes the specified value to a device register. The value to be
*        written is also stored in the software register list of the device.
*
* @param device - The handler of the instance of the driver.
* @param reg - Which register to write to.
* @param value - The value to be written to the reigster of the device.
*
* @return Returns 0 for success or negative error code.
*******************************************************************************/
int32_t AD7124::WriteDeviceRegister(enum ad7124_registers reg, uint32_t value)
{
    regs[reg].value = value;
    return(WriteRegister(regs[reg]));
}

/***************************************************************************//**
* @brief Resets the device.
*
* @param device - The handler of the instance of the driver.
*
* @return Returns 0 for success or negative error code.
*******************************************************************************/
int32_t AD7124::Reset()
{
    int32_t ret = 0;
    uint8_t wrBuf[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};

    ret = SPI_Write( wrBuf, 8);


    return ret;
}

/***************************************************************************//**
* @brief Waits until the device can accept read and write user actions.
*
* @param device - The handler of the instance of the driver.
* @param timeout - Count representing the number of polls to be done until the
*                  function returns.
*
* @return Returns 0 for success or negative error code.
*******************************************************************************/
int32_t AD7124::WaitForSpiReady(uint32_t timeout)
{
    ad7124_st_reg *regs;
    int32_t ret;
    int8_t ready = 0;

    regs = this->regs;

    while(!ready && --timeout) {
        /* Read the value of the Error Register */
        ret = ReadRegister(&regs[AD7124_Error]);
        if(ret < 0)
            return ret;

        /* Check the SPI IGNORE Error bit in the Error Register */
        ready = (regs[AD7124_Error].value &
                 AD7124_ERR_REG_SPI_IGNORE_ERR) == 0;
    }

    return timeout ? 0 : TIMEOUT;
}

/***************************************************************************//**
* @brief Waits until a new conversion result is available.
*
* @param device - The handler of the instance of the driver.
* @param timeout - Count representing the number of polls to be done until the
*                  function returns if no new data is available.
*
* @return Returns 0 for success or negative error code.
*******************************************************************************/
int32_t AD7124::WaitForConvReady(uint32_t timeout)
{
    ad7124_st_reg *regs;
    int32_t ret;
    int8_t ready = 0;

    regs = this->regs;

    while(!ready && --timeout) {
        /* Read the value of the Status Register */
        ret = ReadRegister(&regs[AD7124_Status]);
        if(ret < 0)
            return ret;

        /* Check the RDY bit in the Status Register */
        ready = (regs[AD7124_Status].value &
                 AD7124_STATUS_REG_RDY) == 0;
        wait_ms(1);
    }

    return timeout ? 0 : TIMEOUT;
}

bool AD7124::get_miso()
{
    return miso.read();
}

/***************************************************************************//**
* @brief Reads the conversion result from the device.
*
* @param device - The handler of the instance of the driver.
* @param pData - Pointer to store the read data.
*
* @return Returns 0 for success or negative error code.
*******************************************************************************/
int32_t AD7124::ReadData( int32_t* pData)
{
    int32_t ret       = 0;
    uint8_t check8    = 0;
    uint8_t buffer[8] = {0, 0, 0, 0, 0, 0, 0, 0};
    uint8_t i         = 0;
    uint8_t msgBuf[8] = {0, 0, 0, 0, 0, 0, 0, 0};
    ad7124_st_reg *pReg;

    if( !pData)
        return INVALID_VAL;

    pReg = &regs[AD7124_Data];

    /* Build the Command word */
    buffer[0] = AD7124_COMM_REG_WEN | AD7124_COMM_REG_RD |
                AD7124_COMM_REG_RA(pReg->addr);


    /* Read data from the device */
    ret = SPI_Read(buffer,
                   ((useCRC != AD7124_DISABLE_CRC) ? pReg->size + 1
                    : pReg->size) + 2);

    if(ret < 0)
        return ret;

    /* Check the CRC */
    if(check8 == AD7124_USE_CRC) {
        msgBuf[0] = AD7124_COMM_REG_WEN | AD7124_COMM_REG_RD |
                    AD7124_COMM_REG_RA(pReg->addr);
        for(i = 1; i < pReg->size + 2; ++i) {
            msgBuf[i] = buffer[i];
        }
        check8 = ComputeCRC8(msgBuf, pReg->size + 3);
    }

    if(check8 != 0) {
        /* ReadRegister checksum failed. */
        return COMM_ERR;
    }

    /* Build the result */
    *pData = 0;
    for(i = 1; i < pReg->size + 2; i++) {
        *pData <<= 8;
        *pData += buffer[i];
    }
    wait_ms(500);
    return ret;
}

/***************************************************************************//**
* @brief Computes the CRC checksum for a data buffer.
*
* @param pBuf - Data buffer
* @param bufSize - Data buffer size in bytes
*
* @return Returns the computed CRC checksum.
*******************************************************************************/
uint8_t AD7124::ComputeCRC8(uint8_t * pBuf, uint8_t bufSize)
{
    uint8_t i   = 0;
    uint8_t crc = 0;

    while(bufSize) {
        for(i = 0x80; i != 0; i >>= 1) {
            if(((crc & 0x80) != 0) != ((*pBuf & i) != 0)) { /* MSB of CRC register XOR input Bit from Data */
                crc <<= 1;
                crc ^= AD7124_CRC8_POLYNOMIAL_REPRESENTATION;
            } else {
                crc <<= 1;
            }
        }
        pBuf++;
        bufSize--;
    }
    return crc;
}


/***************************************************************************//**
* @brief Updates the device SPI interface settings.
*
* @param device - The handler of the instance of the driver.
*
* @return None.
*******************************************************************************/
void AD7124::UpdateDevSpiSettings()
{
    ad7124_st_reg *regs;

    regs = this->regs;

    if (regs[AD7124_Error_En].value & AD7124_ERREN_REG_SPI_IGNORE_ERR_EN) {
        check_ready = 1;
    } else {
        check_ready = 0;
    }
}

/***************************************************************************//**
* @brief Initializes the AD7124.
*
* @param device - The handler of the instance of the driver.
* @param slave_select - The Slave Chip Select Id to be passed to the SPI calls.
* @param regs - The list of registers of the device (initialized or not) to be
*               added to the instance of the driver.
*
* @return Returns 0 for success or negative error code.
*******************************************************************************/
int32_t AD7124::Setup()
{
    int32_t ret;
    uint8_t regNr;


    spi_rdy_poll_cnt = 25000;

    /* Initialize the SPI communication. */
    /*ret = SPI_Init(0, 2500000, 1, 0);
    if (ret < 0)
        return ret;*/

    /*  Reset the device interface.*/
    ret = Reset();
    if (ret < 0)
        return ret;

    check_ready = 1;

    /* Initialize registers AD7124_ADC_Control through AD7124_Filter_7. */
    for(regNr = static_cast<uint8_t>(AD7124_Status); (regNr < static_cast<uint8_t>(AD7124_Offset_0)) && !(ret < 0);
            regNr++) {
        if (regs[regNr].rw == AD7124_RW) {
            ret = WriteRegister(regs[regNr]);
            if (ret < 0)
                break;
        }

        /* Get CRC State and device SPI interface settings */
        if (regNr == AD7124_Error_En) {
            UpdateDevSpiSettings();
        }
    }

    return ret;
}

uint8_t AD7124::SPI_Read(uint8_t *data, uint8_t bytes_number)
{
    cs = false;
    for(uint8_t byte = 0; byte < bytes_number; byte++) {
        data[byte] = ad7124.write(data[byte]);
    }
    cs = true;
    return bytes_number;
}

uint8_t AD7124::SPI_Write(uint8_t *data, uint8_t bytes_number)
{
    cs = false;
    for(uint8_t byte = 0; byte < bytes_number; byte++) {
        ad7124.write(data[byte]);
    }

    cs = true;
    return bytes_number;

}