Microchip MCP342x ADC library

mcp342x.h

Committer:
SomeRandomBloke
Date:
2020-09-24
Revision:
7:82a309e53326
Parent:
5:0ca445d2d2bc

File content as of revision 7:82a309e53326:

/**
 * Author: Antonio Gonzalez <antgon@cantab.net>
 * Copyright (c) 2016 The Francis Crick Institute.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef MCP342x_h
#define MCP342x_h

#include "mbed.h"

#define COMMAND_N_BYTES 5
#define MCP342X_FREQ 100000

/**
 * @brief Microchip analog-to-digital converter MCP3422/3/4
 *
 * Library for using Microchip's family of analog-to-digital converters
 * MCP3422/3/4. Some features of these include:
 * - 12-, 14-, 16-, or 18-bit ADC.
 * - Two-wire I2C interface.
 * - Sampling rate, 3.75 (18-bit), 15 (16-bit), 60 (14-bit), or 240
 *   (12-bit) SPS.
 * - Two (MCP3422/3) or four (MCP3424) channels, differential input.
 * - Internal voltage reference, 2.048 V.
 * - Selectable PGA, x1, x2, x4, or x8.
 * - Conversion modes: one-shot or continuous.
 *
 * TODO:
 * - Implement 'conversion mode one-shot'
 *
 * Example 1: Minimum functionallity.
 * @code
 * #include "mbed.h"
 * #include "mcp342x.h"
 *
 * I2C i2c(p9, p10);
 * MCP342x mcp_adc(&i2c);
 *
 * int main(){
 *     mcp_adc.set_channel(MCP342x::CHANNEL_2);
 *     mcp_adc.set_pga(MCP342x::PGA_4);
 *     mcp_adc.set_resolution(MCP342x::RESOLUTION_12);
 *
 *     while(1){
 *         // Channel 2 was selected above. Read data from this channel.
 *         float chan2_val = mcp_adc.read_volts();
 *
 *         // Select now channel 1 and read it.
 *         mcp_adc.set_channel(MCP342x::CHANNEL_1);
 *         float chan1_val = mcp_adc.read_volts();
 *
 *         printf("CH1 %.3f; CH2 %.3f\r\n", chan1_val, chan2_val);
 *         wait(5);
 *     }
 * }
 * @endcode
 *
 * Example 2: Compare MPC342x with mbed analog input.
 *
 * In this example a voltage is applied on pin 18 (AnalogOut), and this
 * is read by both one of the mbed's analog inputs and by the MCP342x.
 * It is useful for checking that the chip is properly connected and
 * this library is working as expected, etc.
 * @code
 * // Connect:
 * //     - MCP342x CH1- to ground.
 * //     - MCP342x CH1+ to mbed p18.
 * //     - mbed ANALOG_IN (defined below) to mbed p18.
 * // Then open a terminal.
 * #include "mbed.h"
 * #include "mcp342x.h"
 *
 * #define SDA_PIN p28
 * #define SCL_PIN p27
 * #define ANALOG_IN p20
 * #define ANALOG_OUT p18
 *
 * I2C i2c(SDA_PIN, SCL_PIN);
 * MCP342x mcp_adc(&i2c);
 *
 * DigitalOut led1(LED1);
 * AnalogOut dac(ANALOG_OUT);
 * AnalogIn ain(ANALOG_IN);
 *
 * float mbed_Vin;
 * float Vout = 0.1/3.3;
 * float mcp_Vin;
 *
 * int main(){
 *     i2c.frequency(400000);
 *     mcp_adc.set_resolution(MCP342x::RESOLUTION_16);
 *
 *     while(1){
 *         // Set the output voltage.
 *         dac = Vout;
 *         wait(2);
 *
 *         // Read voltage using both the mbed and the MCP342x.
 *         led1 = 1;
 *         mcp_Vin = mcp_adc.read_volts();
 *         mbed_Vin = ain.read() * 3.3;
 *
 *         // Display both values.
 *         printf("\r\nmcp Vin = %.2f\r\n", mcp_Vin);
 *         printf("mbed Vin = %.2f\r\n", mbed_Vin);
 *         led1 = 0;
 *
 *         // Increment output voltage by 0.1 V.
 *         Vout += 0.1/3.3;
 *
 *         // If the output voltage goes above 2.5 V, reset to 0.5 V.
 *         if (Vout >= 2.5/3.3){
 *             Vout = 0.5/3.3;
 *         }
 * }
 * }
 * @endcode
 */
class MCP342x
{
public:
    // Registers.
    // The device has only one register, which is the configuration
    // byte. It consists of these bits:
    //   7    ~ready
    //   6-5  channel selection
    //   4    conversion mode (one-shot, continuous)
    //   3-2  sample rate selection (12, 14, 16, or 18)
    //   1-0  PGA gain selection (1x, 2x, 4x, or 8x)
    typedef enum {
        REG_PGA_Pos = 0,
        REG_RESOLUTION_Pos = 2,
        REG_MODE_Pos = 4,
        REG_CHANNEL_Pos = 5,
        REG_RDY_Pos = 7,

        REG_PGA_Clear = ~(0x3 << REG_PGA_Pos),
        REG_RESOLUTION_Clear = ~(0x3 << REG_RESOLUTION_Pos),
        REG_MODE_Clear = ~(0x1 << REG_MODE_Pos),
        REG_CHANNEL_Clear = ~(0x3 << REG_CHANNEL_Pos),
        REG_RDY_Clear = ~(0x1 << REG_RDY_Pos)
    } mcp342x_reg_t;

    // Programmable gain.
    typedef enum {
        PGA_1 = 0,
        PGA_2 = 1,
        PGA_4 = 2,
        PGA_8 = 3
    } mcp342x_pga_t;

    // Resolution.
    typedef enum {
        RESOLUTION_12 = 0,
        RESOLUTION_14 = 1,
        RESOLUTION_16 = 2,
        RESOLUTION_18 = 3
    } mcp342x_resolution_t;

    // Conversion mode.
    typedef enum {
        CONVERSION_MODE_ONESHOT = 0,
        CONVERSION_MODE_CONTINUOUS = 1
    } mcp342x_conversion_mode_t;

    // Channels.
    typedef enum {
        CHANNEL_1 = 0,
        CHANNEL_2 = 1,
        CHANNEL_3 = 2,
        CHANNEL_4 = 3
    } mcp342x_channel_t;

    /**
    * MCP342x constructor
    *
    * @param i2c Pointer to I2C
    * @param device_address Address for this sensor
    */
    MCP342x(I2C *i2c, uint8_t device_address = 0b000);
    
    /**
    * Set channel to read from.
    *
    * @param channel Channel number
    */
    void set_channel(mcp342x_channel_t channel);

    /**
    * Set conversion mode.
    *
    * @param mode Conversion mode. Options are
    * MCP342x::CONVERSION_MODE_ONESHOT or
    * MCP342x::CONVERSION_MODE_CONTINUOUS.
    */
    void set_conversion_mode(mcp342x_conversion_mode_t mode);

    /**
    * Set resolution.
    *
    * @param resolution Resolution, one of
    * MCP342x::RESOLUTION_12, MCP342x::RESOLUTION_14,
    * MCP342x::RESOLUTION_16 or MCP342x::RESOLUTION_18.
    */
    void set_resolution(mcp342x_resolution_t resolution);

    /**
    * Set programable gain amplifier. Options are
    * MCP342x::PGA_1, MCP342x::PGA_2, MCP342x::PGA_4, or MCP342x::PGA_8.
    */
    void set_pga(mcp342x_pga_t pga);

    /**
    * Read the ADC value. The value will be that read from whatever
    * channel was set previously (`set_channel`).
    *
    * @return Analog measurement in raw data (integer)
    */
    uint32_t read();

    /**
    * Read the ADC value in volts. The value will be that read from
    * whatever channel was set previously (`set_channel`).
    *
    * The data are coded in two's complements format, and the final
    * voltage is also a function of the resolution and gain settings.
    * This function follows the equations presented in Section 4.9 of
    * the device's datasheet (Microchip DS22088C).
    *
    * @return Analog measurement in volts (float)
    */
    float read_volts();

    //void start_conversion(void); For one-shot mode.

private:
    // Variables and functions for I2C communication.
    I2C *_i2c;
    uint8_t _address;
    char _i2c_command[COMMAND_N_BYTES];
    uint8_t _configuration;
    static const uint8_t _device_code = 0b1101; // Hardcoded in factory
    void _write_configuration();
    void _startRead();

    // Variables needed for converting digital output codes to real
    // values (i.e. voltage).
    mcp342x_resolution_t _resolution;
    uint8_t _pga;
    float _lsb;
    uint32_t _max_code;
};

#endif