Skip to content
Snippets Groups Projects
Commit f87be43a authored by Elisabetta Pianori's avatar Elisabetta Pianori
Browse files

Merge branch 'kk_pot' into 'devel'

Add CalibratedDevice and Potentiometer classes

See merge request berkeleylab/labRemote!180
parents 3323eae8 83720e0a
Branches
No related tags found
No related merge requests found
#include "AD5245.h"
#include "LinearCalibration.h"
#include "OutOfRangeException.h"
AD5245::AD5245(double RAB, std::shared_ptr<I2CCom> com)
: Potentiometer(std::make_shared<LinearCalibration>(RAB, 256, 100)),
m_com(com)
{ }
void AD5245::writeCount(int32_t pos, uint8_t ch)
{
if(pos<0 || 0xFF<pos)
throw OutOfRangeException(pos, 0, 0xFF);
if(ch!=0)
throw OutOfRangeException(ch, 0, 0);
m_com->write_reg16(0x0000|pos);
}
int32_t AD5245::readCount(uint8_t ch)
{
if(ch!=0)
throw OutOfRangeException(ch, 0, 0);
return m_com->read_reg8();
}
#ifndef AD5245_H
#define AD5245_H
#include <memory>
#include "I2CCom.h"
#include "Potentiometer.h"
/** \brief AD5245: 256-Position I2C-Compatible Digital Potentiometer
*
* [Datasheet](https://www.analog.com/media/en/technical-documentation/data-sheets/AD5245.pdf)
*/
class AD5245 : public Potentiometer
{
public:
/**
* \param RAB Maximum resistance in Ohm's (5k, 10k or 100k)
* \param com I2C communication
*/
AD5245(double RAB, std::shared_ptr<I2CCom> com);
virtual ~AD5245() =default;
virtual void writeCount(int32_t pos, uint8_t ch=0);
virtual int32_t readCount(uint8_t ch=0);
private:
std::shared_ptr<I2CCom> m_com;
};
#endif // AD5245_H
......@@ -19,6 +19,8 @@ target_sources(DevCom
ChecksumException.cpp
OutOfRangeException.cpp
CalibratedDevice.cpp
ClimateSensor.cpp
#BME280.cpp
HIH6130.cpp
......@@ -49,6 +51,9 @@ target_sources(DevCom
MuxDevice.cpp
PGA11x.cpp
Potentiometer.cpp
AD5245.cpp
AMAC.cpp
DeviceCalibration.cpp
......
#include "CalibratedDevice.h"
#include "NotSupportedException.h"
CalibratedDevice::CalibratedDevice(std::shared_ptr<DeviceCalibration> calibration)
: m_calibration(calibration)
{ }
void CalibratedDevice::setCalibration(std::shared_ptr<DeviceCalibration> calibration)
{ m_calibration=calibration; }
void CalibratedDevice::setCalibration(uint8_t ch, std::shared_ptr<DeviceCalibration> calibration)
{ m_channelCalibration[ch]=calibration; }
std::shared_ptr<DeviceCalibration> CalibratedDevice::findCalibration(uint8_t ch) const
{
return (m_channelCalibration.find(ch)==m_channelCalibration.end())?m_calibration:m_channelCalibration.at(ch);
}
double CalibratedDevice::read(uint8_t ch)
{ return findCalibration(ch)->calibrate(readCount(ch)); }
void CalibratedDevice::read(const std::vector<uint8_t>& chs, std::vector<double>& data)
{
std::vector<int32_t> counts(chs.size());
readCount(chs, counts);
data.clear();
for(uint i=0; i<chs.size(); i++)
data.push_back(findCalibration(chs[i])->calibrate(counts[i]));
}
int32_t CalibratedDevice::readCount(uint8_t)
{ throw NotSupportedException("Read operation not supported."); }
void CalibratedDevice::readCount(const std::vector<uint8_t>& chs, std::vector<int32_t>& data)
{ throw NotSupportedException("Bulk read operation not supported."); }
double CalibratedDevice::write(double value, uint8_t ch)
{
std::shared_ptr<DeviceCalibration> cal=findCalibration(ch);
int32_t calval=cal->uncalibrate(value);
writeCount(calval);
return cal->calibrate(calval);
}
void CalibratedDevice::write(const std::vector<uint8_t>& chs, const std::vector<double>& values, std::vector<double>& actvalues)
{
std::vector<int32_t> counts;
actvalues.clear();
for(uint32_t i=0;i<chs.size();i++)
{
std::shared_ptr<DeviceCalibration> cal=findCalibration(chs[i]);
int32_t calval=cal->uncalibrate(values[i]);
counts.push_back(calval);
actvalues.push_back(cal->calibrate(calval));
}
writeCount(chs, counts);
}
void CalibratedDevice::writeCount(int32_t value, uint8_t ch)
{ throw NotSupportedException("Write operation not supported."); }
void CalibratedDevice::writeCount(const std::vector<uint8_t>& chs, const std::vector<int32_t>& values)
{ throw NotSupportedException("Bulk write operation not supported."); }
#ifndef CALIBRATEDDEVICE_H
#define CALIBRATEDDEVICE_H
#include <cstdint>
#include <vector>
#include <memory>
#include <map>
#include "DeviceCalibration.h"
#include "DummyCalibration.h"
//! \brief Abstract implementation of a calibrated device.
/**
* A calibrated device is a device that converts between digital units and
* a physical quantity. For example, an ADC that converts an input analogue
* voltage to a digital quantity.
*
* This class provides a common base for such devices to handle storing of
* calibrations (via `DeviceCalibration`) and the application.
*
* ## Reading/Writing
*
* Controlling a calibrated device is reading or writing (or both) digital values,
* indepdenent of device type. The base `CalibratedDevice` class implements the
* `read`/`write` functions that deal with the calibration and call the digital
* `readCount`/`writeCount` functions.
*
* The default implementation of the digital read/write functions throws
* a `NotSupportedException` exception. The supported operations need to be
* implemented in the device subclass.
*
* The write function returns the real value set. This is needed as not all
* analogue values can be represented using the discrete representations of
* a device.
*
* ## Multi-Channel Devices
* For multi-channel ADC's, a per-channel calibration can be applied using the
* `setCalibration(ch,...)` function. If no per-channel calibration is set for
* a specific channel, then the global calibration (set via constructor or
* `setCalibration(...)`) is used.
*
* The calibration of a channel should be retrieved via the `findCalibration`
* function. This handles the case of per-channel settings as described above.
*/
class CalibratedDevice
{
public:
/**
* \param calibration Global calibration for all channels.
*/
CalibratedDevice(std::shared_ptr<DeviceCalibration> calibration=std::make_shared<DummyCalibration>());
virtual ~CalibratedDevice() =default;
//! Set global calibration
/**
* \param calibration Global calibration for all channels.
*/
void setCalibration(std::shared_ptr<DeviceCalibration> calibration);
//! \brief Set per-channel calibration
/**
* Override global calibration for specific channels.
*
* \param ch Channel to calibrate
* \param calibration Calibration for `ch`
*/
void setCalibration(uint8_t ch, std::shared_ptr<DeviceCalibration> calibration);
//! Read calibrated value of channel `ch`
/**
* \param ch Channel to read
*
* \return result of `readCount`, followed by calibration
*/
double read(uint8_t ch=0);
//! Bulk read calibrated values of channels `chs`
/**
* The `data` vector used to store the results does not
* need to be the same size as `chs`. It will be cleared
* and allocated to the right size by this function.
*
* \param chs List of channel numbers to read
* \param data Vector where readings will be stored
*
* \return result of `readCount`, followed by calibration.
*/
void read(const std::vector<uint8_t>& chs, std::vector<double>& data);
//! Read value of channel `ch` (counts)
/**
* Implementation of reading from the device goes here
*
* \param ch Channel to read
*/
virtual int32_t readCount(uint8_t ch=0);
//! Bulk read values of channels `chs` (counts)
/**
* Implementation of reading from the ADC goes here
*
* The `data` vector used to store the results does not
* need to be the same size as `chs`. The implementatino
* of `readCount` should clear and allocated the vector
* to the right size.
*
* \param chs List of channel numbers to read
* \param data Vector where readings will be stored
*/
virtual void readCount(const std::vector<uint8_t>& chs, std::vector<int32_t>& data);
//! Write calibrated value of channel `ch`
/**
* \param value Value to write
* \param ch Channel to write
*
* \return Actual value written
*/
double write(double value, uint8_t ch=0);
//! Bulk write calibrated values of channels `chs`
/**
* The `actvalues` vector used to store the results does not
* need to be the same size as `chs`. It will be cleared
* and allocated to the right size by this function.
*
* \param chs List of channel numbers to write
* \param values Vector with target values
* \param actvalues Vector where actual values will be stored
*/
void write(const std::vector<uint8_t>& chs, const std::vector<double>& values, std::vector<double>& actvalues);
//! Write value of channel `ch` (counts)
/**
* Implementation of writeing from the device goes here
*
* \param value Value to write to
* \param ch Channel to write
*/
virtual void writeCount(int32_t value, uint8_t ch=0);
//! Bulk write values of channels `chs` (counts)
/**
* Implementation of writing to the device goes here
*
* \param chs List of channel numbers to write
* \param values Vector where writeings will be stored
*/
virtual void writeCount(const std::vector<uint8_t>& chs, const std::vector<int32_t>& values);
protected:
//! \brief Find `DeviceCalibration` for given channel
/**
* The global calibration is returned if `ch` does not have a per-channel calibration
* set.
*
* \return The calibration to be used for the given channel. (per-channel or global)
*/
std::shared_ptr<DeviceCalibration> findCalibration(uint8_t ch=0) const;
private:
std::shared_ptr<DeviceCalibration> m_calibration;
std::map<uint8_t, std::shared_ptr<DeviceCalibration>> m_channelCalibration;
};
#endif // CALIBRATEDDEVICE_H
......@@ -2,15 +2,16 @@
#include <cmath>
LinearCalibration::LinearCalibration(double reference, uint32_t max)
: m_reference(reference), m_max(max)
{ }
LinearCalibration::~LinearCalibration()
LinearCalibration::LinearCalibration(double reference, uint32_t max, double offset)
: m_reference(reference), m_max(max), m_offset(offset)
{ }
double LinearCalibration::calibrate(int32_t counts)
{ return (double)counts/m_max*m_reference; }
{
return (double)counts/m_max*m_reference+m_offset;
}
int32_t LinearCalibration::uncalibrate(double value)
{ return round(value*m_max/m_reference); }
{
return round((value-m_offset)*m_max/m_reference);
}
......@@ -6,14 +6,15 @@
//! \brief Calibration that assumes a linear relationship between counts and the output value
/**
* The conversion uses the following formula to convert counts (signed integer) to a value:
*
* ```
* value = reference * counts / max
* value = reference * counts / max + offset
* ```
* The `counts` correspond to the counts at value `max`.
*
* For example, an 8 bit ADC with a 3.3V reference would use
* - `reference = 3.3`
* - `counts = 0xFF`
* - `max = 0xFF`
* - `offset = 0.0`
*
* The counts are assumed to represented by a signed integer. This allows support
* for differential devices with negative values represented as such.
......@@ -24,16 +25,19 @@ public:
/**
* \param reference Voltage at `max` counts
* \param max Counts at `reference` value
* \param offset Linear offset.
*/
LinearCalibration(double reference, uint32_t max);
virtual ~LinearCalibration();
LinearCalibration(double reference, uint32_t max, double offset=0);
virtual ~LinearCalibration() =default;
virtual double calibrate(int32_t counts);
virtual int32_t uncalibrate(double value);
private:
double m_reference;
uint32_t m_max;
int32_t m_max;
double m_offset =0;
};
#endif // LINEARCALIBRATION_H
......@@ -5,7 +5,7 @@
#include "ComException.h"
class NotSupportedException : ComException
class NotSupportedException : public ComException
{
public:
NotSupportedException(const std::string& msg);
......
......@@ -5,7 +5,7 @@
#include "ComException.h"
class OutOfRangeException : ComException
class OutOfRangeException : public ComException
{
public:
OutOfRangeException(uint32_t ch, uint32_t minCh, uint32_t maxCh);
......
#include "Potentiometer.h"
Potentiometer::Potentiometer(std::shared_ptr<DeviceCalibration> calibration)
: CalibratedDevice(calibration)
{ }
#ifndef POTENTIOMETER_H
#define POTENTIOMETER_H
#include <memory>
#include "CalibratedDevice.h"
#include "DummyCalibration.h"
//! \brief Abstract implementation of a potentiometer
/**
* The read and write functions correspond to setting the wiper-to-B
* resistance.
*/
class Potentiometer : public CalibratedDevice
{
public:
/**
* \param calibration Device calibration to convert resistances to tap positon
*/
Potentiometer(std::shared_ptr<DeviceCalibration> calibration=std::make_shared<DummyCalibration>());
virtual ~Potentiometer() =default;
};
#endif // POTENTIOMETER_H
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment