blob: 5365d9e1f90d46370fcdb4ffc166cb9668cca51f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2016 Herwig Eichler, www.conmeleon.org
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Herwig Eichler - initial API and implementation and initial documentation
*
* This class represents a 4 channel ADC from Texas Instruments Type ADS1018
* Please refer to the datasheet available at http://www.ti.com for details
* ADS1018 is derived from the CSpiDevice base class which uses the linux kernel spidev device driver.
*
* Remark on SCLK timing:
* According to the timing requirements of the ADS1018 the minimum SCLK period duration is 250ns, which means
* the maximum SCLK frequency is 4MHz.
*
* A SPI device (e.g. ADC) on bus X with chip select line Y will be available as /dev/spidevX.Y
* If you don't see this device in your file system, the spidev kernel module might not be loaded
* SPI devices have a limited userspace API, supporting basic half-duplex
* read() and write() access to SPI slave devices. Using ioctl() requests,
* full duplex transfers and device I/O configuration are also available.
*******************************************************************************/
#include "ads1018.h"
#include <unistd.h>
namespace CONMELEON {
/*! just call the constructor of the base class first and initialize own members afterwards
* 0x05, 0x8B are the default configuration register settings according to the ADS1018 datasheet
* SS MUX2 MUX1 MUX0 PGA2 PGA1 PGA0 MODE DR2 DR1 DR0 TS_MODE PULL_UP_EN NOP1 NOP0 NOT_USED
* 0 0 0 0 0 1 0 1 1 0 0 0 1 0 1 1
* -> MSB = 00000101 = 0x05, LSB = 10001011 = 0x8B
*
* AIN0 and AIN1 as differential input
* PGA full scale voltage 2.048V
* single shot conversion mode
* data rate 1600 samples
* analog input channel (internal temperature sensor disabled)
* internal pull up resistors enabled
* valid data, update config register
*/
CAds1018::CAds1018(const char* sDevice) : CSpiDevice(sDevice, 1000000, SPIMODE1) {
m_ConfigRegister[0] = 0x05; // MSB
m_ConfigRegister[1] = 0x8B; // LSB
m_DataRegister[0] = 0x00; // MSB
m_DataRegister[1] = 0x00; // LSB
m_ReferenceVoltage = 3.3;
m_VoltageDivider = 1.0;
// default data rate is 1600 samples per second, so a single conversion takes around 625 microseconds
m_ConversionDuration = 625;
}
CAds1018::CAds1018(const char* sDevice, unsigned int nSpeed, CONMELEON::ESpiMode enMode) : CSpiDevice(sDevice, nSpeed, enMode) {
m_ConfigRegister[0] = 0x05; // MSB
m_ConfigRegister[1] = 0x8B; // LSB
m_DataRegister[0] = 0x00; // MSB
m_DataRegister[1] = 0x00; // LSB
m_ReferenceVoltage = 3.3;
m_VoltageDivider = 1.0;
m_ConversionDuration = 625;
}
void CAds1018::setChannelConfig (EChannelConfig enConfig) {
unsigned char cBitMask = 0b01110000; // channel configuration is stored in bits [14:12] in the configuration register
// most significant byte of configuration register is on index 0
// reset bit [14:12] to "0" with the bitmask first and than "or" in the new bits
m_ConfigRegister[0] = (m_ConfigRegister[0] & (~cBitMask)) | (enConfig << 4);
}
void CAds1018::setConversionMode (EConversionMode enMode) {
unsigned char cBitMask = 0b00000001; // conversion mode is stored in bit [8] in the configuration register, which is the first bit in the MSB
// most significant byte of configuration register is on index 0
// reset bit [8] to "0" with the bitmask first and than "or" in the new bit
m_ConfigRegister[0] = (m_ConfigRegister[0] & (~cBitMask)) | (enMode);
}
void CAds1018::setPGA (EPgaSetting enPga) {
unsigned char cBitMask = 0b00001110; // pga setting is stored in bits [11:9] in the configuration register
// most significant byte of configuration register is on index 0
// reset bit [11:9] to "000" with the bitmask first and than "or" in the new bits
m_ConfigRegister[0] = (m_ConfigRegister[0] & (~cBitMask)) | (enPga << 1);
}
void CAds1018::setSampleSpeed (EDataRate enSpeed) {
unsigned char cBitMask = 0b11100000; // data rate speed setting is stored in bits [7:5] in the configuration register
// least significant byte of configuration register is on index 1
// reset bit [7:5] to "000" with the bitmask first and than "or" in the new bits
m_ConfigRegister[1] = (m_ConfigRegister[1] & (~cBitMask)) | (enSpeed << 5);
switch (enSpeed) {
case SPS128:
m_ConversionDuration = 7813;
break;
case SPS250:
m_ConversionDuration = 4000;
break;
case SPS490:
m_ConversionDuration = 2041;
break;
case SPS920:
m_ConversionDuration = 1087;
break;
case SPS1600:
m_ConversionDuration = 625;
break;
case SPS2400:
m_ConversionDuration = 417;
break;
case SPS3300:
m_ConversionDuration = 303;
break;
}
}
void CAds1018::setDataSource (EDataSource enSrc) {
unsigned char cBitMask = 0b00010000; // setting for temperature readout is stored in bit [4] of the configuration register
// least significant byte of configuration register is on index 1
// reset bit [4] to "0" with the bitmask first and than "or" in the new bit
m_ConfigRegister[1] = (m_ConfigRegister[1] & (~cBitMask)) | (enSrc << 4);
}
void CAds1018::setReferenceVoltage (const float fRefVoltage) {
// check if fRevVoltage is within the range of the allowed supply voltages
if (fRefVoltage < 2.0) {
m_ReferenceVoltage = 2.0;
}
else if (fRefVoltage > 5.5) {
m_ReferenceVoltage = 5.5;
}
else {
m_ReferenceVoltage = fRefVoltage;
}
}
void CAds1018::setVoltageDivider (const float fDivider) {
// divider voltages must not be smaller than 1.0
if (fDivider < 1.0) {
m_VoltageDivider = 1.0;
}
else {
m_VoltageDivider = fDivider;
}
}
unsigned int CAds1018::readRawValue() {
// first check if the ADS1018 is ready
if (this->isOpen()) {
// buffer to hold configuration, we operate in 32bit mode, initialize with the current configuration register settings
unsigned char TX[4] = {m_ConfigRegister[0], m_ConfigRegister[1], m_ConfigRegister[0], m_ConfigRegister[1]};
// buffer to hold received data, initialize with "0"
unsigned char RX[4] = {0, };
// set the single shot conversion start bit, if conversion mode is set to single shot
TX[0] = TX[0] | (this->getConversionMode() << 7);
TX[2] = TX[0];
// we write the configuration register first without reading back anything, to trigger a single shot conversion if necessary
if (CSpiDevice::write(TX, 4)) {
// wait until the conversion is finished
usleep(m_ConversionDuration);
// reset the single shot conversion trigger bit again , because we don't want to trigger a new conversion
TX[0] = TX[0] & 0b01111111;
TX[2] = TX[0];
// now we can read something back, data will be in the first two bytes MSB first
if (CSpiDevice::transfer(TX, RX, 4)) {
m_DataRegister[0] = RX[0];
m_DataRegister[1] = RX[1];
unsigned int nRawVoltage = 0; // used for bit shifting
// TODO: handle negative voltages in case of differential inputs
// copy data register bytes to 16 bit integer
nRawVoltage = (RX[0] << 8) | RX[1];
// now shift integer 4 bits to the right, because the 12 bit voltage value of the ADS1018 is left aligned
nRawVoltage >>= 4;
return nRawVoltage;
}
}
}
return 0;
}
int CAds1018::readVoltage(const unsigned int iChannelNr) {
// iChannelNr is unsigned and valid channel numbers are 0..3
// TODO: check for differential inputs as well, only single ended is handled now
if (iChannelNr < 4) {
// all channels are configured in single ended mode
// TODO: also implement differential channels
this->setChannelConfig(static_cast<EChannelConfig>(iChannelNr + 4));
// set the data source to normal input channel first, just to be sure
this->setDataSource(AnalogInput);
unsigned int nRawVoltage = this->readRawValue();
double fFullscaleVoltage = 0.0;
// get current PGA setting to read current full scale voltage
switch (this->getPgaSetting()) {
case FS6144:
fFullscaleVoltage = 6.144;
break;
case FS4096:
fFullscaleVoltage = 4.096;
break;
case FS2048:
fFullscaleVoltage = 2.048;
break;
case FS1024:
fFullscaleVoltage = 1.024;
break;
case FS0512:
fFullscaleVoltage = 0.512;
break;
case FS0256:
fFullscaleVoltage = 0.256;
break;
}
// we return the voltage in mV, that's why we use an additional factor of 1000.0
return static_cast<int>(static_cast<double>(nRawVoltage)*m_VoltageDivider*fFullscaleVoltage*1000.0/2048.0);
}
else {
// TODO: add logging or error handling
}
return 0.0;
}
int CAds1018::readTemperature() {
// read internal temperature sensor of the ADS1018
/*!
* Temperature readout of the ADS1018 internal temperature sensor:
* least significant bit equals 0.125 °C (temperature resolution)
* temperature is sampled in the same resolution (12bit) as the input channels
* if the most significant bit is "1" the temperature value is negative
* e.g. read value from the ADS1018 is 0x0F38 -> 111100111000
* 1. check MSB bit [11] = 1 -> negative temperature
* 2. generate the twos complement of 0x0F38 000011000111 + 000000000001 = 000011001000 -> 0x00C8
* readTemperature() will return 0x00C8 * (-0.125) = -25°C
*
* e.g. read value from the ADS1018 is 0x0258 -> 001001011000
* 1. check MSB bit [11] = 0 -> positive temperature
* 2. no twos complement necessary
* readTemperature() will return 0x0258 * 0.125 = +75°C
*/
// set the data source to internal temperature sensor
this->setDataSource(Temperature);
unsigned int nRawTemperature = this->readRawValue();
// check if MSB bit is "1", which means we have a negative temperature readout
if (nRawTemperature & 0x8000) {
// negative temperature, do a twos complement first, according to ADS1018 datasheet page 14
nRawTemperature = ~nRawTemperature;
nRawTemperature += 1;
}
// return 10th of degree, that's why we use a factor of 10.0
return static_cast<int>(static_cast<double>(nRawTemperature) * scm_TemperatureFactor * 10.0);
}
} // namespace CONMELEON