Driver for the AD7790 16 bit ADC
Dependents: ad7790-helloworld CN0357
For additional information check out the mbed page of the Analog Devices wiki: https://wiki.analog.com/resources/tools-software/mbed-drivers-all
Revision 0:c7cfa6877e1d, committed 2016-05-03
- Comitter:
- adisuciu
- Date:
- Tue May 03 12:56:17 2016 +0000
- Commit message:
- Initial revision
Changed in this revision
AD7790.cpp | Show annotated file Show diff for this revision Revisions of this file |
AD7790.h | Show annotated file Show diff for this revision Revisions of this file |
diff -r 000000000000 -r c7cfa6877e1d AD7790.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/AD7790.cpp Tue May 03 12:56:17 2016 +0000 @@ -0,0 +1,399 @@ +/** +* @file AD7790.cpp +* @brief Source file for AD7790 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/ad7790 +* 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 "AD7790.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 + */ +AD7790::AD7790(float reference_voltage, + PinName CS, + PinName MOSI, + PinName MISO, + PinName SCK) : + miso(MISO), ad7790(MOSI, MISO, SCK), cs(CS), _vref(reference_voltage), _PGA_gain(1) +{ + cs = true; // cs is active low + ad7790.format(8, _SPI_MODE); + _continous_conversion = true; + _channel = DIFFERENTIAL; +} + +/** + * @brief Set AD7790 SPI frequency + * @param hz - SPI bus frequency in hz + * @return none + */ +void AD7790::frequency(int hz) +{ + ad7790.frequency(hz); +} + +/** + * @brief Resets the AD7790 + * @return none + */ +void AD7790::reset() +{ + ad7790.format(8, _SPI_MODE); + cs = false; + wait_us(_DELAY_TIMING); + ad7790.write(_RESET); + ad7790.write(_RESET); + ad7790.write(_RESET); + ad7790.write(_RESET); + wait_us(_DELAY_TIMING); + cs = true; + _continous_conversion = true; +} + +/** + * Sets the mode register. Also sets continous mode and range based on the value + * written in reg_val + * @param reg_val + */ +void AD7790::write_mode_reg(uint8_t reg_val) +{ + write_reg(MODE_REG, reg_val); + uint8_t continous_mode = (reg_val & 0xC0); + if(continous_mode == 0x00) { + _continous_conversion = true; + } else { + _continous_conversion = false; + } + uint8_t range = (reg_val & 0x30); + _PGA_gain = 1 << (range >> 4); +} + +/** + * Reads the mode register and returns its value + * @return value of the mode register + */ +uint8_t AD7790::read_mode_reg() +{ + return read_reg(MODE_REG); +} + +/** + * Writes the filter register + * @param regValue value to be written. + */ +void AD7790::write_filter_reg(uint8_t reg_val) +{ + write_reg(FILTER_REG, reg_val); +} + +/** + * Reads the filter register and returns its value + * @return the value of the filter register + */ +uint8_t AD7790::read_filter_reg() +{ + return read_reg(FILTER_REG); +} + +/** + * Reads the data register and returns its value + * @return value of the data register + */ +uint16_t AD7790::read_data_reg() +{ + uint16_t data_result; + ad7790.format(8, _SPI_MODE); + cs = false; + ad7790.write(_DATA_READ | (static_cast<uint8_t>(_channel))); + data_result = ((ad7790.write(_DUMMY_BYTE)) << 8); + data_result |= (ad7790.write(_DUMMY_BYTE)); + cs = true; + return data_result; +} + +/** + * Reads the status register of the ADC and returns its value + * @return value of the status reg + */ +uint8_t AD7790::read_status_reg() +{ + return read_reg(STATUS_REG); +} + + +/** + * @brief Enables/disables continous_conversion mode + * In Single Conversion mode, read_u16 method will read the MODE register of the ADC, + * then write the Start single conversion bit and wait for the DOUT/RDY pin to go low, + * When the pin is driven low, data register is read back from the ADC. + * + * In Continous conversion mode, read_u16 method will poll the DOUT/RDY pin, if it is low, + * the data register is read back from the ADC. + * + * @param mode + * true - continous conversion mode enabled + * false - single conversion mode enabled + */ +void AD7790::set_conversion_mode(AD7790Mode_t mode) +{ + uint8_t mode_reg_val; + mode_reg_val = read_mode_reg() & 0x3F; + mode_reg_val = mode_reg_val | (static_cast<uint8_t>(mode)); + write_mode_reg(mode); +} + +/** + * - From mbed AnalogIn API - + * @brief Read the input voltage, represented as an unsigned short in the range [0x0, 0xFFFF] + * Depending on the conversion mode, this method will have different behavior. Conversion mode is set using + * set_continous_conversion_mode(bool). + * + * In Single Conversion mode, read_u16 method will read the MODE register of the ADC, + * then write the Start single conversion bit and wait for the DOUT/RDY pin to go low, + * When the pin is driven low, data register is read back from the ADC. + * + * In Continous conversion mode, read_u16 method will poll the DOUT/RDY pin, if it is low, + * the data register is read back from the ADC. + * + * @return 16-bit unsigned short representing the current input voltage, normalised to a 16-bit value + * returns -1 (0xFFFF) along with a debug message if conversion failed. + */ +uint16_t AD7790::read_u16(void) +{ + uint16_t data_result = 0; + ad7790.format(8, _SPI_MODE); + cs = false; + uint16_t timeout_cnt = 0; + if(_continous_conversion == false) { + + uint8_t mode_reg = read_mode_reg(); + wait_us(_DELAY_TIMING); + + cs = false; + mode_reg = (mode_reg & 0x3F) | MD1; // mask single conversion bits + ad7790.write((MODE_REG << 4) | (static_cast<uint8_t>(_channel))); // start single conversion + ad7790.write(mode_reg); + timeout_cnt = _SINGLE_CONVERSION_TIMEOUT; // starts timeout + } else { + timeout_cnt = _CONTINOUS_CONVERSION_TIMEOUT; // starts timeout + } + wait_us(1); + + while(miso) { // wait for the MISO pin to go low. + if(timeout_cnt) { + timeout_cnt--; + } else { + cs = true; +#ifdef AD7790_DEBUG_MODE + printf("timeout occurred reading the AD7790. "); // error, MISO line didn't toggle +#endif + return -1; // ERROR + } + wait_us(10); + } + + ad7790.write(_DATA_READ | (static_cast<uint8_t>(_channel))); + data_result = ((ad7790.write(_DUMMY_BYTE)) << 8); + data_result |= (ad7790.write(_DUMMY_BYTE)); + cs = true; + return data_result; +} + +/** + * @brief Reads a register of the AD7790 + * @param address - address of the register + * @return value of the register + */ +uint16_t AD7790::read_reg(AD7790Register_t address) +{ + uint16_t data = address << 12; + data |= _DUMMY_BYTE; + data |= _READ_FLAG; + data |= (static_cast<uint8_t>(_channel) << 8); + 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 AD7790::write_reg(AD7790Register_t address, uint8_t reg_val) +{ + uint16_t spi_data = address << 12; + spi_data |= reg_val; + spi_data |= (static_cast<uint8_t>(_channel) << 8); + 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 AD7790::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; + ad7790.format(8, _SPI_MODE); + cs = false; + data_result = (ad7790.write(upper_byte) << 8); + data_result |= ad7790.write(lower_byte); + cs = true; + return data_result; +} + + +/** + * Sets the AnalogInputRange to be used by the AD7790 + * @param range AnalogInputRange_t to be used in voltage computations + */ +void AD7790::set_range(AnalogInputRange_t range) +{ + + uint8_t mode_reg_val; + mode_reg_val = read_mode_reg() & 0xCF; + mode_reg_val = mode_reg_val | (range << 4); + write_mode_reg(mode_reg_val); +} + +/** + * Sets the reference voltage of the AD7790 + * @param ref reference voltage to be set + */ +void AD7790::set_reference_voltage(float ref) +{ + _vref = ref; +} + +/** + * Gets the reference voltage of the AD7790 + * @return reference voltage + */ +float AD7790::get_reference_voltage(void) +{ + return _vref; +} + +/** + * Reads the data register of the ADC and converts the result to volts + * Gain needs to be correctly set using set_gain in order to get accurate results + * @return voltage of the ADC input + */ +float AD7790::read_voltage(void) +{ + return data_to_voltage(read_u16()); +} + +/** + * Converts an uint16_t to voltage. + * Gain needs to be correctly set using set_gain in order to get accurate results + * @param data in uint16_t format + * @return float value of voltage (in V) + */ +float AD7790::data_to_voltage(uint16_t data) +{ + return ((data / static_cast<float>(_RESOLUTION / 2)) - 1) * (_vref / _PGA_gain); +} + +/** + * Converts voltage to an uint16_t. + * Gain needs to be correctly set using set_gain in order to get accurate results + * @param voltage to be converted + * @return data in uint16_t format + */ +uint16_t AD7790::voltage_to_data(float voltage) +{ + return (((voltage * _PGA_gain / _vref) + 1) * static_cast<float>(_RESOLUTION / 2)); +} + +/** + * Sets the conversion channel. + * @param channel + */ +void AD7790::set_channel(AD7790Channel_t channel) +{ + _channel = channel; +} + +/** + * - From mbed AnalogIn API - + * Read the input voltage, represented as a float in the range [0.0, 1.0] - uses the read_u16 method + * @returns A floating-point value representing the current input voltage, measured as a percentage + * returns 1.0 along with a debug message if the conversion failed + */ +float AD7790::read(void) +{ + float percent; + uint16_t data; + data = read_u16(); + percent = (data / static_cast<float>(_RESOLUTION) ); // translate bipolar conversion to [0.0, 1.0] domain + return percent; +} + +#ifdef MBED_OPERATORS + +/** + * - From mbed AnalogIn API - + * An operator shorthand for read() + * The float() operator can be used as a shorthand for read() to simplify common code sequences + */ +AD7790::operator float() +{ + return read(); +} + +#endif + + +
diff -r 000000000000 -r c7cfa6877e1d AD7790.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/AD7790.h Tue May 03 12:56:17 2016 +0000 @@ -0,0 +1,174 @@ +/** +* @file AD7790.h +* @brief Header file for AD7790 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/ad7790 +* 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. +* +********************************************************************************/ + +#ifndef AD7790_H +#define AD7790_H + +#include "mbed.h" + +/** + * Comment this line if you want to turn off the debug mode. + * The debug mode will send a message if an exception occurs within AD7790 driver + */ + +#define AD7790_DEBUG_MODE + +/** + * @brief Analog Devices AD7790 SPI 16-bit Buffered Sigma-Delta ADC + */ +class AD7790 +{ +public: + /// AD7790 registers + typedef enum { + COMMUNICATION_REG = 0, ///< Communication register + STATUS_REG = 0, ///< Status register + MODE_REG, ///< Mode register + FILTER_REG, ///< Filter Register + DATA_REG ///< Data register + } AD7790Register_t; + + /// AD7790 channel configuration + typedef enum { + DIFFERENTIAL = 0, ///< AIN(+)-AIN(-) + RESERVED, ///< reserved + SHORT, ///< AIN(-)-AIN(-) + VDDMONITOR ///< Monitor VDD + } AD7790Channel_t; + + typedef enum { + CONTINOUS_CONVERSION_MODE = 0, + SINGLE_CONVERSION_MODE = 0x80, + SHUTDOWN_MODE = 0xC0 + } AD7790Mode_t; + + typedef enum { + MD1 = 0x80, ///< Mode Select Bit 1 + MD0 = 0x40, ///< Mode Select Bit 0 + G1 = 0x20, ///< Range bit 1 + G0 = 0x10, ///< Range bit 0 + BO = 0x08, ///< Burnout Current Enable bit + BUF = 0x02, ///< Buffered mode bit + } ModeRegisterBits_t; + + typedef enum { + CLKDIV1 = 0x40, ///< Clock divider bit 1 + CLKDIV0 = 0x20, ///< Clock divider bit 0 + FS2 = 0x04, ///< Update rate bit 2 + FS1 = 0x02, ///< Update rate bit 1 + FS0 = 0x01, ///< Update rate bit 0 + } FilterRegisterBits_t; + + typedef enum { + RANGE_VREF = 0, + RANGE_VREF_DIV_2, + RANGE_VREF_DIV_4, + RANGE_VREF_DIV_8, + } AnalogInputRange_t; + + /** SPI configuration & constructor */ + AD7790( float reference_voltage, PinName CS = SPI_CS, PinName MOSI = SPI_MOSI, PinName MISO = SPI_MISO, PinName SCK = SPI_SCK); + void frequency(int hz); + + /** Low level SPI bus comm methods */ + void reset(void); + + /** Register access methods*/ + void set_channel(AD7790Channel_t channel); + void set_conversion_mode(AD7790Mode_t mode); + uint16_t read_data_reg(); + uint8_t read_status_reg(void); + void write_filter_reg(uint8_t regVal); + uint8_t read_filter_reg(void); + void write_mode_reg(uint8_t regVal); + uint8_t read_mode_reg(void); + void set_range(AnalogInputRange_t range); + AnalogInputRange_t get_range(void); + + /** Reference voltage methods */ + void set_reference_voltage(float ref); + float get_reference_voltage(void); + + /** Voltage read methods */ + float read_voltage(void); + float data_to_voltage(uint16_t data); + uint16_t voltage_to_data(float voltage); + + /** AnalogIn API */ + float read(void); + uint16_t read_u16(void); + +#ifdef MBED_OPERATORS + operator float(); +#endif + +private: + DigitalIn miso;///< DigitalIn must be initialized before SPI to prevent pin MUX overwrite + SPI ad7790; ///< SPI instance of the AD7790 + DigitalOut cs; ///< DigitalOut instance for the chipselect of the AD7790 + + float _vref; + uint8_t _PGA_gain; + bool _continous_conversion; + AD7790Channel_t _channel; + + void write_reg(AD7790Register_t regAddress, uint8_t regValue); + uint16_t write_spi(uint16_t data); + uint16_t read_reg (AD7790Register_t regAddress); + + const static uint16_t _SINGLE_CONVERSION_TIMEOUT = 0xFFFF; // in 10us = 100ms + const static uint16_t _CONTINOUS_CONVERSION_TIMEOUT = 0xFFFF; + const static uint16_t _RESOLUTION = 0xFFFF; + const static uint8_t _RESET = 0xFF; + const static uint8_t _DUMMY_BYTE = 0xFF; + const static uint16_t _READ_FLAG = 0x0800; + const static uint8_t _DATA_READ = 0x38; // Read from the Data Register + const static uint8_t _DELAY_TIMING = 0x02; + const static uint8_t _SPI_MODE = 3; +}; + +#endif