0.1.1 MCP3424

This commit is contained in:
Rob Tillaart 2024-09-19 16:38:26 +02:00
parent 188019a4fb
commit 3336a0dced
9 changed files with 264 additions and 82 deletions

View File

@ -6,6 +6,14 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).
## [0.1.1] - 2024-09-18
- add derived classes for MCP3421/2/3/6/7/8
- extend unit tests.
- fixed **getMode()** to return 1 (continuous) or 0 (single shot).
- fixed math of **floats readVolts()** et al.
- update readme.md
- minor edits
## [0.1.0] - 2024-09-15
- initial version

View File

@ -1,7 +1,7 @@
//
// FILE: MCP3424.cpp
// AUTHOR: Rob Tillaart
// VERSION: 0.1.0
// VERSION: 0.1.1
// PURPOSE: Arduino library for 18 bit ADC I2C MCP3424 and compatibles.
// URL: https://github.com/RobTillaart/MCP3424
@ -23,8 +23,7 @@ MCP3424::MCP3424(uint8_t address, TwoWire *wire)
_gain = 1;
_bits = 12;
_mode = 0; // CONTINUOUS
_config = 0x10;
_config = 0x10; // default
}
@ -60,47 +59,52 @@ int32_t MCP3424::read()
}
// shift is to calculate the LSB factor.
// 18 bits => 1, LSB == 15.625 uV
// 16 bits => 4, LSB == 62.5 uV
// 14 bits => 16, LSB == 250 uV
// 12 bits => 64 LSB == 1000 uV = 1 mV
// must be multiplied to float first to prevent
// losing bits due to integer division
float MCP3424::readVolts()
{
// TODO
// pre calculate float _factor = 15.625e-6 * pow(2, (_bits - 12))/ _gain;
return read() * 15.625e-6 * pow(2, (_bits - 12))/ _gain;
return read() * (15.625e-6 * (1L << (18 - _bits))) / _gain;
}
float MCP3424::readMilliVolts()
{
return read() * 15.625e-3 * pow(2, (_bits - 12))/ _gain;
return read() * (15.625e-3 * (1L << (18 - _bits))) / _gain;
}
float MCP3424::readMicroVolts()
{
return read() * 15.625e0 * pow(2, (_bits - 12))/ _gain;
return read() * (15.625e0 * (1L << (18 - _bits))) / _gain;
}
// TODO move to derived class with more than one channel?
bool MCP3424::setChannel(uint8_t channel)
{
if (channel >= _maxChannels)
{
return false;
}
// only update if changed
if (_channel != channel)
{
_channel = channel;
_config &= 0x1F; // channel = 0
if (channel == 1) _config |= 0x20;
if (channel == 2) _config |= 0x40;
if (channel == 3) _config |= 0x60;
// if (channel > 0) => _config |= (channel << 4)
if (channel == 1) _config |= 0x20;
else if (channel == 2) _config |= 0x40;
else if (channel == 3) _config |= 0x60;
}
writeConfig();
return true;
}
// TODO move to derived class with more than one channel?
uint8_t MCP3424::getChannel()
{
return _channel;
@ -113,13 +117,14 @@ bool MCP3424::setGain(uint8_t gain)
{
return false;
}
// only update if changed.
if (_gain != gain)
{
_gain = gain;
_config &= 0xFC; // gain == 1
if (_gain == 2) _config |= 0x01;
if (_gain == 4) _config |= 0x02;
if (_gain == 8) _config |= 0x03;
if (_gain == 2) _config |= 0x01;
else if (_gain == 4) _config |= 0x02;
else if (_gain == 8) _config |= 0x03;
writeConfig();
}
return true;
@ -138,13 +143,14 @@ bool MCP3424::setResolution(uint8_t bits)
{
return false;
}
// only update if changed.
if (_bits != bits)
{
_bits = bits;
_config &= 0xF3; // bits == 12
if (_bits == 14) _config |= 0x04;
if (_bits == 16) _config |= 0x08;
if (_bits == 18) _config |= 0x0C;
if (_bits == 14) _config |= 0x04;
else if (_bits == 16) _config |= 0x08;
else if (_bits == 18) _config |= 0x0C;
writeConfig();
}
return true;
@ -153,27 +159,34 @@ bool MCP3424::setResolution(uint8_t bits)
uint8_t MCP3424::getResolution()
{
// return 12 + 2 * ((_config >> 2) & 0x03);
return _bits;
}
void MCP3424::setContinuousMode()
{
_config &= ~0x10;
writeConfig();
if (getMode() != 1)
{
_config |= 0x10;
writeConfig();
}
}
void MCP3424::setSingleShotMode()
{
_config |= 0x10;
writeConfig();
if (getMode() != 0)
{
_config &= ~0x10;
writeConfig();
}
}
uint8_t MCP3424::getMode()
{
return (_config & 0x10);
return (_config & 0x10) ? 1 : 0;
}
@ -226,5 +239,42 @@ int32_t MCP3424::readRaw()
}
/////////////////////////////////////////////////////////////////////////////
//
// DERIVED CLASSES
//
MCP3421::MCP3421(uint8_t address, TwoWire *wire) : MCP3424(address, wire)
{
_maxChannels = 1;
}
MCP3422::MCP3422(uint8_t address, TwoWire *wire) : MCP3424(address, wire)
{
_maxChannels = 2;
}
MCP3423::MCP3423(uint8_t address, TwoWire *wire) : MCP3424(address, wire)
{
_maxChannels = 2;
}
MCP3426::MCP3426(uint8_t address, TwoWire *wire) : MCP3424(address, wire)
{
_maxChannels = 2;
}
MCP3427::MCP3427(uint8_t address, TwoWire *wire) : MCP3424(address, wire)
{
_maxChannels = 2;
}
MCP3428::MCP3428(uint8_t address, TwoWire *wire) : MCP3424(address, wire)
{
_maxChannels = 4;
}
// -- END OF FILE --

View File

@ -2,7 +2,7 @@
//
// FILE: MCP3424.h
// AUTHOR: Rob Tillaart
// VERSION: 0.1.0
// VERSION: 0.1.1
// PURPOSE: Arduino library for 18 bit ADC I2C MCP3424 and compatibles.
// URL: https://github.com/RobTillaart/MCP3424
@ -10,15 +10,9 @@
#include "Arduino.h"
#include "Wire.h"
#define MCP3424_LIB_VERSION (F("0.1.0"))
#define MCP3424_LIB_VERSION (F("0.1.1"))
// TODO
// - ERROR HANDLING
// - READ STATUS
// - CACHE LAST READ
//
class MCP3424
{
public:
@ -50,12 +44,11 @@ public:
uint8_t getMode();
private:
protected:
uint8_t _maxChannels;
uint8_t _channel;
uint8_t _gain;
uint8_t _bits;
uint8_t _mode;
uint8_t _config;
uint8_t _address;
@ -66,5 +59,49 @@ private:
};
/////////////////////////////////////////////////////////////////////////////
//
// DERIVED CLASSES
//
class MCP3421 : public MCP3424
{
public:
MCP3421(uint8_t address = 0x68, TwoWire *wire = &Wire);
};
class MCP3422 : public MCP3424
{
public:
MCP3422(uint8_t address = 0x68, TwoWire *wire = &Wire);
};
class MCP3423 : public MCP3424
{
public:
MCP3423(uint8_t address = 0x68, TwoWire *wire = &Wire);
};
//
// max 16 bit
//
class MCP3426 : public MCP3424
{
public:
MCP3426(uint8_t address = 0x68, TwoWire *wire = &Wire);
};
class MCP3427 : public MCP3424
{
public:
MCP3427(uint8_t address = 0x68, TwoWire *wire = &Wire);
};
class MCP3428 : public MCP3424
{
public:
MCP3428(uint8_t address = 0x68, TwoWire *wire = &Wire);
};
// -- END OF FILE --

View File

@ -22,22 +22,25 @@ Library not tested with hardware yet.
This library is to be used to configure and read the 18 bit MCP4324 4 channel ADC et al.
The MCP3424 is not a fast ADC, however with 18 bit it has at least a very high
The MCP3424 is not a fast ADC, however with 18 bit it has at least a very high
resolution. What the effects of the long sampling time means is to be investigated.
The high resolution combined with an optional gain of 8x means one could
measure voltage in steps of about 2 µV
The high resolution combined with an optional gain of 8x means one could
measure voltage in steps of about 2 µV.
The library has three functions that return a reading in volts, millivolts or
microvolts to match the need of the user.
The library cannot check yet if a conversion is ready.
Need hardware to check how this works in detail.
The library has three functions that return a reading in Volts, milliVolts or
microVolts to match the need of the user.
The user has to configure the ADC device (bits, gain) and can call
**read()** (et al) without parameters to keep usage straightforward.
Current implementation will probably change slightly in the future
Current implementation will probably change slightly in the future
when related devices will be supported. (See future section).
Alt-230 = µ
Alt-230 = µ
### Resolution
@ -45,23 +48,33 @@ Alt-230 =
| Bits | LSB (gain=1) | SPS | Raw range | Notes |
|:------:|---------------:|:------:|:-------------------:|:-------:|
| 12 | 1 mV | 240 | -2048 .. 2047 |
| 14 | 250 µV | 60 | -8192 .. 8191 |
| 16 | 62.5 µV | 15 | -32768 .. 32767 |
| 18 | 15.625 µV | 3.75 | -131072 .. 131071 | not for 3426/27/28.
| 14 | 250 µV | 60 | -8192 .. 8191 |
| 16 | 62.5 µV | 15 | -32768 .. 32767 |
| 18 | 15.625 µV | 3.75 | -131072 .. 131071 | not for 3426/27/28.
The effective resolution also depends on the gain set.
In theory with a gain of 8 the LSB of the 18 bit resolution represents
1/8 of 15.625 µV == 1.95 µV.
If this is feasible in practice is to be seen.
In theory with a gain of 8 the LSB of the 18 bit resolution represents
1/8 of 15.625 µV == 1.95 µV.
If this is feasible in practice is to be seen.
### I2C Address
The MCP3421 has a fixed address 0x68, one can order different addresses at factory (how?).
The MCP3421 and MCP3426 have a fixed address 0x68, one can order different
addresses at the factory (how?).
TODO Table of addresses?
The other devices have two address pins to set 8 addresses. The trick is
to leave address pins floating. See datasheet table 5.3 for details.
Max speed ? 400 KHz.
### I2C Speed
The MCP342x devices support 100 KHz, 400 KHz and 3.4 MHz.
The latter is not (yet) supported by the library.
The sketch **MCP3424_performance.ino** can be used to get some insight
in the performance. It will check up to 800 kHz.
TODO verify with hardware.
### I2C multiplexing
@ -99,14 +112,14 @@ too if they are behind the multiplexer.
### Constructor
TODO other constructors.
- **MCP3424(uint8_t address = 0x68, TwoWire \*wire = &Wire)**
- **MCP3424(uint8_t address = 0x68, TwoWire \*wire = &Wire)**
- **bool begin()** initializes the device. POR?
- **bool isConnected()** checks if the device address can be seen on I2C bus.
- **uint8_t getAddress()** idem, convenience function.
- **uint8_t getMaxChannels()** idem, convenience function.
The MCP3421/2/3/6/7/8 constructors have the same parameters.
### Read
@ -115,8 +128,8 @@ TODO other constructors.
- **float readMilliVolts()** converts the raw reading to millivolts value (wrapper).
This is useful for small ranges.
- **float readMicroVolts()** converts the raw reading to microvolts value (wrapper).
This is useful for very small ranges (especially with a gain of 8 one has a
resolution of about 2 microvolts.
This is useful for very small ranges (especially with a gain of 8 one has a
resolution of about 2 microvolts.
### Configuration
@ -128,53 +141,67 @@ Correct settings will be written to the device immediately, but be aware of the
that it will take some time before the conversion with new settings is done.
- **bool setChannel(uint8_t channel = 0)** not to be used for the MCP3421 as
it has only one channel. Default is channel 0, parameter should be less than the
it has only one channel. Default is channel 0, parameter should be less than the
value of **getMaxChannels()**.
- **uint8_t getChannel()** returns chosen channel, 0 based. e
- **bool setGain(uint8_t gain = 1)** set gain to 1,2,4, or 8.
- **uint8_t getChannel()** returns chosen channel (default 0).
- **bool setGain(uint8_t gain = 1)** set gain to 1,2,4, or 8.
Other values will return false and not change the setting.
- **uint8_t getGain()** returns the set gain (default 1).
- **bool setResolution(uint8_t bits = 12)** set the bit resolution 12,14,16 or 18.
Other values will return false and not change the setting.
- **uint8_t getResolution()** returns the set resolution.
- **uint8_t getResolution()** returns the set resolution (default 12).
- **void setContinuousMode()** idem.
- **void setSingleShotMode()** idem.
- **uint8_t getMode()** idem.
- **uint8_t getMode()** returns 0 for singleShot and 1 for continuous.
The set function write their changes directly to the device. It might be better
to have one function to set all parameters in one call. To be investigated.
The library caches the last configuration, it is not read back from the device.
This might be added in the future.
## Future
#### Must
- investigate reading of ready flag
- get hardware to test.
- redo interface for MCP3424 if needed.
- investigate ready flag
- investigate continuous vs single shot.
- investigate continuous vs single shot mode.
- improve documentation
- Table of addresses.
#### Should
- implement support for (separate classes ?)
- 18 bit, MCP3421/MCP3422/MCP3423
- 16 bit: MCP3426/MCP3427/MCP3428 (no 18 bit so not 100% compatible)
- test on different boards.
- optimize performance (a lot of same math in conversion to voltage)
- optimize performance if possible
- check performance I2C with HW
- optimize setting all configuration in one function call.
- PowerOnReset function for configuration
- **setConfig(channel, resolution, gain, mode)** ?
- getter needed?
- implement PowerOnReset function for configuration
#### Could
- implement maxResolution (combine with maxChannels? in one "maxValue" byte)
- check range in **setResolution()**.
- extract gain and resolution from the config byte to reduce storage.
- like **getMode()**.
- extend examples
- array of ADC's
- mcp3424_plotter
- add error handling
- add error variable
- check return value **writeConfig()**.
- check read() process.
#### Wont
- cache last read value. (difficult for wrappers)
## Support

View File

@ -1,7 +1,7 @@
//
// FILE: MCP3424_test.ino
// FILE: MCP3424_performance.ino
// AUTHOR: Rob Tillaart
// PURPOSE: basic test API calls.
// PURPOSE: basic performance test API calls.
// URL: https://github.com/RobTillaart/MCP3424
//
// needs a device connected to be able to test.
@ -38,6 +38,8 @@ void setup()
Serial.println(mcp.getGain());
Serial.print("Bits:\t");
Serial.println(mcp.getResolution());
Serial.print("Mode:\t");
Serial.println(mcp.getMode());
Serial.println();
delay(100);

View File

@ -7,11 +7,9 @@
#include "MCP3424.h"
MCP3424 mcp;
void setup()
{
Serial.begin(115200);
@ -36,6 +34,8 @@ void setup()
Serial.println(mcp.getGain());
Serial.print("Bits:\t");
Serial.println(mcp.getResolution());
Serial.print("Mode:\t");
Serial.println(mcp.getMode());
Serial.println();
Serial.println("GAIN 1");

View File

@ -1,6 +1,6 @@
{
"name": "MCP3424",
"keywords": "",
"keywords": "MCP3421,MCP3422,MCP3423,MCP3426,MCP3427,MCP3428",
"description": "Arduino library for 18 bit ADC I2C MCP3424 et al.",
"authors":
[
@ -15,7 +15,7 @@
"type": "git",
"url": "https://github.com/RobTillaart/MCP3424.git"
},
"version": "0.1.0",
"version": "0.1.1",
"license": "MIT",
"frameworks": "*",
"platforms": "*",

View File

@ -1,9 +1,9 @@
name=MCP3424
version=0.1.0
version=0.1.1
author=Rob Tillaart <rob.tillaart@gmail.com>
maintainer=Rob Tillaart <rob.tillaart@gmail.com>
sentence=Arduino library for 18 bit ADC I2C MCP3424 et al.
paragraph=
paragraph=MCP3421,MCP3422,MCP3423,MCP3426,MCP3427,MCP3428
category=Sensors
url=https://github.com/RobTillaart/MCP3424
architectures=*

View File

@ -29,7 +29,7 @@
unittest_setup()
{
fprintf(stderr, "MCP3424_LIB_VERSION: %s\n", (char *) MCP3424_LIB_VERSION);
Wire.begin();
// Wire.setTimeout(10000);
}
@ -45,15 +45,55 @@ unittest(test_constants)
}
unittest(test_constructor)
unittest(test_constructors)
{
MCP3421 mcp1;
MCP3422 mcp2;
MCP3423 mcp3;
MCP3424 mcp4;
MCP3426 mcp6;
MCP3427 mcp7;
MCP3428 mcp8;
// base class
assertEqual(0, mcp4.getChannel());
assertEqual(4, mcp4.getMaxChannels());
assertEqual(1, mcp4.getGain());
assertEqual(12, mcp4.getResolution());
assertEqual(1, mcp4.getMode());
// check MAX channels derived classes
assertEqual(1, mcp1.getMaxChannels());
assertEqual(2, mcp2.getMaxChannels());
assertEqual(2, mcp3.getMaxChannels());
assertEqual(4, mcp4.getMaxChannels());
assertEqual(2, mcp6.getMaxChannels());
assertEqual(2, mcp7.getMaxChannels());
assertEqual(4, mcp8.getMaxChannels());
}
unittest(test_channel)
{
MCP3424 mcp;
assertEqual(0, mcp.getChannel());
assertEqual(4, mcp.getMaxChannels());
assertEqual(1, mcp.getGain());
assertEqual(12, mcp.getResolution());
// mode
assertTrue(mcp.setChannel(1));
assertEqual(1, mcp.getChannel());
assertTrue(mcp.setChannel(2));
assertEqual(2, mcp.getChannel());
assertTrue(mcp.setChannel(3));
assertEqual(3, mcp.getChannel());
// default
assertTrue(mcp.setChannel());
assertEqual(0, mcp.getChannel());
// out of range
assertFalse(mcp.setChannel(5));
}
@ -72,9 +112,11 @@ unittest(test_gain)
assertTrue(mcp.setGain(8));
assertEqual(8, mcp.getGain());
// default
assertTrue(mcp.setGain());
assertEqual(1, mcp.getGain());
// out of range
assertFalse(mcp.setGain(0));
}
@ -94,13 +136,29 @@ unittest(test_resolution)
assertTrue(mcp.setResolution(18));
assertEqual(18, mcp.getResolution());
// default
assertTrue(mcp.setResolution());
assertEqual(12, mcp.getResolution());
// out of range
assertFalse(mcp.setResolution(13));
}
unittest(test_mode)
{
MCP3424 mcp;
assertEqual(1, mcp.getMode());
mcp.setSingleShotMode();
assertEqual(0, mcp.getMode());
mcp.setContinuousMode();
assertEqual(1, mcp.getMode());
}
unittest_main()