MCP3427 16-Bit Analog-to-Digital Converter with I2C Interface

Fork of MCP3425 by Kenji Arai

MCP3427.cpp

Committer:
kenjiArai
Date:
2018-09-21
Revision:
2:bb3efa8c5b23
Parent:
MCP3425.cpp@ 1:223248a79e87

File content as of revision 2:bb3efa8c5b23:

/*
 * mbed library program
 *  2 channels 16-Bit(available 14/12bit mode) Analog-to-Digital Converter
 *      with I2C Interface ---- MCP3427 by Microchip Technology Inc.
 *
 * Copyright (c) 2018 Kenji Arai / JH1PJL
 *  http://www.page.sannet.ne.jp/kenjia/index.html
 *  http://mbed.org/users/kenjiArai/
 *      Created:    June       7th, 2018
 *      Revised:    July      13th, 2018
 */

#include "MCP3427.h"

extern Serial pc;

// BIT DEFINITION
#define RDY_BIT                     (1UL << 7)
#define CONVERSION_MODE_BIT         (1UL << 4)
#define SAMPLE_RATE_BIT             (3UL << 2)
#define PGA_GAIN_BIT                (3UL << 0)

#if (MBED_MAJOR_VERSION == 2)
#define WAIT_MS(x)              wait_ms(x)
#elif (MBED_MAJOR_VERSION == 5)
#define WAIT_MS(x)              Thread::wait(x)
#else
#warning "Cannot control wait_ms()!!"
#endif

MCP3427::MCP3427 (PinName p_sda, PinName p_scl, uint8_t addr)
    : _i2c_p(new I2C(p_sda, p_scl)), _i2c(*_i2c_p)
{
    mcp3427_addr = addr;
    init();
}

MCP3427::MCP3427 (I2C& p_i2c, uint8_t addr)
    : _i2c(p_i2c)
{
    mcp3427_addr = addr;
    init();
}

void MCP3427::init()
{
    offset_volt[0] = 0.0f;          // none offset
    offset_volt[1] = 0.0f;          // none offset
    compensation_ref = 1.0f;        // no compensation
    pga_gain[0] = PGA_GAIN_1;       // Gain x1
    pga_gain[1] = PGA_GAIN_1;       // Gain x1
    sample_rate[0] = SAMPLE_RATE_15SPS_16BIT;   // 16bit resolution
    sample_rate[1] = SAMPLE_RATE_15SPS_16BIT;   // 16bit resolution
    conversion_mode[0] = CONV_MODE_CONTINUOUS;  // Continuous conversion
    conversion_mode[1] = CONV_MODE_CONTINUOUS;  // Continuous conversion
    convert_config2byte(); // make one byte
    buf[0] = config_byte[0];
    _i2c.write((int)mcp3427_addr, (char *)buf, 1);  // write into config reg.
    current_ch = 0;
}

int16_t MCP3427::read_16bit(uint8_t ch)
{
    _ch = ch - 1;
    if (_ch != current_ch) {
        current_ch = _ch;
        _set_ch_and_config(current_ch);
        buf[0] = config_byte[current_ch] + RDY_BIT;
        _i2c.write((int)mcp3427_addr, (char *)buf, 1);
    }
    if (conversion_mode[current_ch] == CONV_MODE_ONE_SHOT) {
        // trigger for starting conversion
        buf[0] = config_byte[current_ch] + RDY_BIT;
        _i2c.write((int)mcp3427_addr, (char *)buf, 1);
    }
    return _read_16bit(); // internal channel 0 & 1
    
}

int16_t MCP3427::_read_16bit(void)
{
    uint32_t timeout = 1000U;   // timeout
    do {
        _i2c.read( (int)mcp3427_addr, (char *)buf, 3);
        if ((buf[2] & RDY_BIT) == 0) {  // check Config. reg. Ready bit
            break;      // end of conversion (RDY = 0)
        }
        if (--timeout == 0) {
            return -1;  // timeout then error
        }
        uint8_t spd = (buf[2] >> 2) & 0x03; // get current sampling rate
        if (spd == SAMPLE_RATE_60SPS_14BIT) {   // wait next conversion period
            WAIT_MS(6);     // conversion time = 16.7ms
        } else if (spd == SAMPLE_RATE_15SPS_16BIT) {
            WAIT_MS(24);    // conversion time = 66.7ms
        } else {  // == SAMPLE_RATE_240SPS_12BIT
            WAIT_MS(2);     // conversion time = 4.2ms
        }
    } while(true);
    //printf("[%d,0x%02x,0x%02x,0x%02x]", _ch, buf[0], buf[1], buf[2]);
    dt_adc16 = (uint16_t)buf[0] << 8;   // High byte
    dt_adc16 += (uint16_t)buf[1];       // Low byte
    return dt_adc16;
}

void MCP3427::start_conversion(uint8_t ch)
{
    _ch = ch - 1;
    if (_ch != current_ch) {
        current_ch = _ch;
        _set_ch_and_config(current_ch);
        buf[0] = config_byte[current_ch] + RDY_BIT;
        _i2c.write((int)mcp3427_addr, (char *)buf, 1);
    }
    if (conversion_mode[current_ch] == CONV_MODE_ONE_SHOT) {
        // trigger for starting conversion
        buf[0] = config_byte[current_ch] + RDY_BIT;
        _i2c.write((int)mcp3427_addr, (char *)buf, 1);
    }
}

float MCP3427::read_voltage_from_current_ch(void)
{
    float dt16 = (float)_read_16bit();
    return _read_voltage(dt16);
}

float MCP3427::read_voltage(uint8_t ch)
{
    float dt16 = (float)read_16bit(ch);
    return _read_voltage(dt16);
}

float MCP3427::_read_voltage(float dt16)
{
    switch(sample_rate[current_ch]) {
        case SAMPLE_RATE_240SPS_12BIT:
            dt16 /=  2048.0f; // 11bit (0x7ff +1)
            break;
        case SAMPLE_RATE_60SPS_14BIT:
            dt16 /= 16384.0f; // 14bit (0x3fff +1)
            break;
        case SAMPLE_RATE_15SPS_16BIT:
            dt16 /= 32768.0f; // 15bit (0x7fff +1)
            break;
        default:
            return -1;      // error
    }
    switch(pga_gain[current_ch]) {
        case PGA_GAIN_1:
            dt16 /= 1.0f;
            break;
        case PGA_GAIN_2:
            dt16 /= 2.0f;
            break;
        case PGA_GAIN_4:
            dt16 /= 4.0f;
            break;
        case PGA_GAIN_8:
            dt16 /= 8.0f;
            break;
        default:
            return -1;      // error
    }
    dt_adc_f = dt16 * 2.048f;           // Vref = 2.048V +/- 0.05%
    dt_adc_f -= offset_volt[current_ch];// adjust offset voltage
    dt_adc_f *= compensation_ref;       // compensate Vref deviation
    return dt_adc_f;
}

void MCP3427::set_offset_volt(uint8_t ch, float offset)
{
   offset_volt[ch - 1] = offset;    // internal channel 0 & 1
}

void MCP3427::set_vref_compensation(float compensation)
{
    compensation_ref = compensation;
}

void MCP3427::set_config(mcp3427_config_t *parameter)
{
    pga_gain[0] = parameter->pga_gain1 & 0x03;
    pga_gain[1] = parameter->pga_gain2 & 0x03;
    sample_rate[0] = parameter->sample_rate1 & 0x03;
    sample_rate[1] = parameter->sample_rate2 & 0x03;
    conversion_mode[0] = parameter->conversion_mode1 & 0x01;
    conversion_mode[1] = parameter->conversion_mode2 & 0x01;
    convert_config2byte();
    _set_ch_and_config(0);   // channel inf is dummy at this moment
}

void MCP3427::_set_ch_and_config(uint8_t n)
{
    buf[0] = config_byte[n] + n << 5;
    _i2c.write((int)mcp3427_addr, (char *)buf, 1);
}

void MCP3427::frequency(uint32_t hz)
{
    _i2c.frequency(hz);
}

void MCP3427::convert_config2byte()
{
    uint8_t byte = pga_gain[0];
    byte |= sample_rate[0] << 2;
    byte |= conversion_mode[0] << 4;
    config_byte[0] = byte;
    byte = pga_gain[1];
    byte |= sample_rate[1] << 2;
    byte |= conversion_mode[1] << 4;
    byte |= 0x20;   // Ch 2
    config_byte[1] = byte;
}