diff --git a/cpp-rpi/README.md b/cpp-rpi/README.md new file mode 100644 index 0000000..0d37522 --- /dev/null +++ b/cpp-rpi/README.md @@ -0,0 +1,33 @@ +# ADS1220 + +### C++ example (for Raspberry Pi) + +##### Prerequisites +- Install [WiringPi](http://wiringpi.com); +- Install [spidev-lib++](https://github.com/milekium/spidev-lib). + +##### Wiring + +All pins numbers are referring to the physical number. + +| Raspberry Pi | ADS1220 breakout | Note +|----------------|------------------|- +| Pin 13 | DRDY | Or other GPIO pin +| Pin 21 (MISO) | MISO | +| Pin 19 (MOSI) | MOSI | +| Pin 23 (SCLK) | SCLK | +| Pin 18 | CS | Or other GPIO pin +| GND | CLK | +| 3.3V | DVDD | +| GND | DGND | +| 3.3V | AVDD | +| GND | AGND | +| N/A | REFN0 / REFP0 | Floating +| N/A | AIN0-3 | Connect your inputs (e.g. pot) + +##### Compile & run + +``` +g++ -std=c++20 -o ads example.cpp ads1220.cpp -lspidev-lib++ -lwiringPi +./ads +``` \ No newline at end of file diff --git a/cpp-rpi/ads1220.cpp b/cpp-rpi/ads1220.cpp new file mode 100644 index 0000000..d153e1b --- /dev/null +++ b/cpp-rpi/ads1220.cpp @@ -0,0 +1,178 @@ +#include "ads1220.h" + +#include +#include + +#include +#include + +using namespace std; + +Ads1220::Ads1220(int csPin, int drdyPin) : _csPin(csPin), _drdyPin(drdyPin) { + pinMode(csPin, OUTPUT); + pinMode(drdyPin, INPUT); + + string spidevPath = "/dev/spidev0.1"; + spi = new SPI(spidevPath.c_str()); + if (!spi->begin()) { + cerr << "Could't init SPI on path " << spidevPath << endl; + exit(1); + } + spi->setMode(1); + spi->setSpeed(50000); + spi->setBitPerWord(8); + + sendSpiCommand(SpiCommand::RESET, 0); + usleep(_sleepBetweenCommandsInMicroSeconds); + digitalWrite(csPin, LOW); + + writeRegister(REG0_ADDR, _configReg0); + writeRegister(REG1_ADDR, _configReg1); + writeRegister(REG2_ADDR, _configReg2); + writeRegister(REG3_ADDR, _configReg3); + + setPgaBypass(true); + setDataRate(DataRate::SPS_1000); + setOperatingMode(OperatingMode::TURBO); + setConversionMode(ConversionMode::CONTINUOUS); + setVoltageReferenceSource(VoltageReferenceSource::ANALOG_SUPPLY_AVDD_AVSS); + + usleep(_sleepBetweenCommandsInMicroSeconds); + cout << "Initialised SPI on " << spidevPath << " with config: " + << int(readRegister(REG0_ADDR)) << ", " << int(readRegister(REG1_ADDR)) << ", " + << int(readRegister(REG2_ADDR)) << ", " << int(readRegister(REG3_ADDR)) << endl; + + digitalWrite(csPin, HIGH); + + usleep(300000); +} + +Ads1220::Ads1220() : Ads1220(DEFAULT_CS_PIN, DEFAULT_DRDY_PIN) {} + +Ads1220::~Ads1220() { + delete spi; +} + +uint32_t Ads1220::readValue(MultiplexerConfiguration configuration) { + selectMultiplexerConfiguration(configuration); + usleep(_sleepBetweenCommandsInMicroSeconds); + + if (_singleShotMode) { + sendSpiCommand(SpiCommand::START, 0); + usleep(_sleepBetweenCommandsInMicroSeconds); + } + + while (digitalRead(_drdyPin) != LOW) { + usleep(_sleepBetweenCommandsInMicroSeconds); + } + + digitalWrite(_csPin, LOW); + usleep(_sleepBetweenCommandsInMicroSeconds); + + spi->read(_rxBuffer + 0, 1); + spi->read(_rxBuffer + 1, 1); + spi->read(_rxBuffer + 2, 1); + + uint32_t value = (_rxBuffer[0] << 16) | (_rxBuffer[1] << 8) | _rxBuffer[2]; + + digitalWrite(_csPin, HIGH); + usleep(_sleepBetweenCommandsInMicroSeconds); + + return value; +} + +uint32_t Ads1220::readUntilNewValue(MultiplexerConfiguration configuration, int32_t previousValue) { + while (true) { + uint32_t value = readValue(configuration) >> _shiftResultBy; + int32_t diff = value - previousValue; + if (value <= _maxResult && abs(diff) > _detectionThreshold) { + return value; + } + } +} + +void Ads1220::setPgaGain(PgaGain gain) { + _configReg0 &= ~MASK_CONFIG_0_PGA_GAIN; + _configReg0 |= int(gain) << 1; + writeRegister(REG0_ADDR, _configReg0); +} + +void Ads1220::setPgaBypass(bool bypass) { + _configReg0 &= ~MASK_CONFIG_0_PGA_BYPASS; + _configReg0 |= bypass; + writeRegister(REG0_ADDR, _configReg0); +} + +void Ads1220::setDataRate(DataRate rate) { + _configReg1 &= ~MASK_CONFIG_1_DATA_RATE; + _configReg1 |= int(rate) << 5; + writeRegister(REG1_ADDR, _configReg1); +} + +void Ads1220::setOperatingMode(OperatingMode mode) { + _configReg1 &= ~MASK_CONFIG_1_OPERATING_MODE; + _configReg1 |= int(mode) << 3; + writeRegister(REG1_ADDR, _configReg1); +} + +void Ads1220::setConversionMode(ConversionMode mode) { + _configReg1 &= ~MASK_CONFIG_1_CONVERSION_MODE; + _configReg1 |= int(mode) << 2; + writeRegister(REG1_ADDR, _configReg1); + _singleShotMode = mode == ConversionMode::SINGLE_SHOT; +} + +void Ads1220::setVoltageReferenceSource(VoltageReferenceSource source) { + _configReg2 &= ~MASK_CONFIG_2_VOLTAGE_REFERENCE; + _configReg2 |= int(source) << 6; + writeRegister(REG2_ADDR, _configReg2); +} + +void Ads1220::sendSpiCommand(SpiCommand command, uint8_t payload) { + digitalWrite(_csPin, LOW); + usleep(_sleepBetweenCommandsInMicroSeconds); + digitalWrite(_csPin, HIGH); + usleep(_sleepBetweenCommandsInMicroSeconds); + digitalWrite(_csPin, LOW); + usleep(_sleepBetweenCommandsInMicroSeconds); + + _txBuffer[0] = int(command) | payload; + spi->write(_txBuffer, 1); + + usleep(_sleepBetweenCommandsInMicroSeconds); + digitalWrite(_csPin, HIGH); +} + +void Ads1220::writeRegister(uint8_t addr, uint8_t data) { + digitalWrite(_csPin, LOW); + usleep(_sleepBetweenCommandsInMicroSeconds); + + // 0100rrnn - rr=register address; nn=number bytes to write + _txBuffer[0] = int(SpiCommand::WREG) | (addr << 2); + _txBuffer[1] = data; + spi->write(_txBuffer, 2); + + usleep(_sleepBetweenCommandsInMicroSeconds); + digitalWrite(_csPin, HIGH); +} + +uint8_t Ads1220::readRegister(uint8_t addr) { + digitalWrite(_csPin, LOW); + usleep(_sleepBetweenCommandsInMicroSeconds); + + // 0010rrnn - rr=register address; nn=number bytes to read + _txBuffer[0] = int(SpiCommand::RREG) | (addr << 2); + spi->write(_txBuffer, 1); + spi->read(_rxBuffer, 1); + + usleep(_sleepBetweenCommandsInMicroSeconds); + digitalWrite(_csPin, HIGH); + + return _rxBuffer[0]; +} + +void Ads1220::selectMultiplexerConfiguration(MultiplexerConfiguration configuration) { + _configReg0 &= ~MASK_CONFIG_0_MUX; + _configReg0 |= int(configuration) << 4; + writeRegister(REG0_ADDR, _configReg0); +} diff --git a/cpp-rpi/ads1220.h b/cpp-rpi/ads1220.h new file mode 100644 index 0000000..fde89c0 --- /dev/null +++ b/cpp-rpi/ads1220.h @@ -0,0 +1,139 @@ +#pragma once + +#include + +class SPI; + +class Ads1220 { +public: + Ads1220(int csPin, int drdyPin); + Ads1220(); + ~Ads1220(); + + enum MultiplexerConfiguration { + AIN0_SINGLE_ENDED = 0b1000, + AIN1_SINGLE_ENDED = 0b1001, + AIN2_SINGLE_ENDED = 0b1010, + AIN3_SINGLE_ENDED = 0b1011, + // Check the datasheet (section 8.6.1.1) for more configurations + }; + + enum PgaGain { + GAIN_1 = 0b000, // default + GAIN_2 = 0b001, + GAIN_4 = 0b010, + GAIN_8 = 0b011, + GAIN_16 = 0b100, + GAIN_32 = 0b101, + GAIN_64 = 0b110, + GAIN_128 = 0b111, + }; + + enum DataRate { + // SPS values are for the normal operating mode + SPS_20 = 0b000, // default + SPS_45 = 0b001, + SPS_90 = 0b010, + SPS_175 = 0b011, + SPS_330 = 0b100, + SPS_600 = 0b101, + SPS_1000 = 0b110, + }; + + enum OperatingMode { + NORMAL = 0b00, // default + DUTY_CYCLE = 0b01, + TURBO = 0b10, + }; + + enum ConversionMode { + SINGLE_SHOT = 0b0, // default + CONTINUOUS = 0b1, + }; + + enum VoltageReferenceSource { + INTERNAL_2048_mV = 0b00, // default + EXTERNAL_REFP0_REFN0 = 0b01, + EXTERNAL_AIN0_REFP1_AIN3_REFN1 = 0b10, + ANALOG_SUPPLY_AVDD_AVSS = 0b11, + }; + + uint32_t readValue(MultiplexerConfiguration configuration); + uint32_t readUntilNewValue(MultiplexerConfiguration configuration, int32_t previousValue); + + void setPgaGain(PgaGain gain); + void setPgaBypass(bool bypass); // default: false (enabled) + void setDataRate(DataRate rate); + void setOperatingMode(OperatingMode mode); + void setConversionMode(ConversionMode mode); + void setVoltageReferenceSource(VoltageReferenceSource source); + + void setShiftResultBy(int value) { _shiftResultBy = value; } + void setMaxResult(int value) { _maxResult = value; } + void setDetectionThreshold(int value) { _detectionThreshold = value; } + void setSleepBetweenCommandsInMicroSeconds(int value) { _sleepBetweenCommandsInMicroSeconds = value; } + +private: + SPI* spi = nullptr; + + int _csPin; + int _drdyPin; + + bool _singleShotMode = true; + + uint8_t _configReg0 = 0; + uint8_t _configReg1 = 0; + uint8_t _configReg2 = 0; + uint8_t _configReg3 = 0; + + uint8_t _txBuffer[2]; + uint8_t _rxBuffer[3]; + + int _shiftResultBy = 10; + int _maxResult = 8192; + int _detectionThreshold = 3; + + int _sleepBetweenCommandsInMicroSeconds = 500; + + enum SpiCommand { + RESET = 0b00000110, + START = 0b00001000, + POWERDOWN = 0b00000010, + RDATA = 0b00010000, + RREG = 0b00100000, + WREG = 0b01000000, + }; + + void sendSpiCommand(SpiCommand command, uint8_t payload); + void writeRegister(uint8_t addr, uint8_t data); + uint8_t readRegister(uint8_t addr); + + void selectMultiplexerConfiguration(MultiplexerConfiguration configuration); + + static constexpr const int DEFAULT_CS_PIN = 18; + static constexpr const int DEFAULT_DRDY_PIN = 13; + + static constexpr const uint8_t REG0_ADDR = 0x00; + static constexpr const uint8_t REG1_ADDR = 0x01; + static constexpr const uint8_t REG2_ADDR = 0x02; + static constexpr const uint8_t REG3_ADDR = 0x03; + + static constexpr const uint8_t MASK_CONFIG_0_MUX = 0b11110000; + static constexpr const uint8_t MASK_CONFIG_0_PGA_GAIN = 0b00001110; + static constexpr const uint8_t MASK_CONFIG_0_PGA_BYPASS = 0b00000001; + + static constexpr const uint8_t MASK_CONFIG_1_DATA_RATE = 0b11100000; + static constexpr const uint8_t MASK_CONFIG_1_OPERATING_MODE = 0b00011000; + static constexpr const uint8_t MASK_CONFIG_1_CONVERSION_MODE = 0b00000100; + static constexpr const uint8_t MASK_CONFIG_1_TEMPERATURE_SENSOR_MODE = 0b00000010; + static constexpr const uint8_t MASK_CONFIG_1_BURNOUT_CURRENT_SOURCES = 0b00000001; + + static constexpr const uint8_t MASK_CONFIG_2_VOLTAGE_REFERENCE = 0b11000000; + static constexpr const uint8_t MASK_CONFIG_2_FIR_FILTER = 0b00110000; + static constexpr const uint8_t MASK_CONFIG_2_LOW_SIDE_SWITCH = 0b00001000; + static constexpr const uint8_t MASK_CONFIG_2_IDAC_CURRENT = 0b00000111; + + static constexpr const uint8_t MASK_CONFIG_3_IDAC1_ROUTING = 0b11100000; + static constexpr const uint8_t MASK_CONFIG_3_IDAC2_ROUTING = 0b00011100; + static constexpr const uint8_t MASK_CONFIG_3_DRDY_MODE = 0b00000010; +}; \ No newline at end of file diff --git a/cpp-rpi/example.cpp b/cpp-rpi/example.cpp new file mode 100644 index 0000000..f08bb13 --- /dev/null +++ b/cpp-rpi/example.cpp @@ -0,0 +1,27 @@ +#include + +#include + +#include "ads1220.h" + +#define CS_PIN 18 +#define DRDY_PIN 13 + +using namespace std; + +int main(void) { + int err = wiringPiSetupPhys(); + if (err != 0) { + cerr << "Couldn't setup wiringPi: " << err << endl; + exit(err); + } + + Ads1220 ads(CS_PIN, DRDY_PIN); + + uint32_t previousValue = 0; + while (true) { + uint32_t value = ads.readUntilNewValue(Ads1220::MultiplexerConfiguration::AIN0_SINGLE_ENDED, previousValue); + cout << "AIN0: " << value << endl; + previousValue = value; + } +} diff --git a/cpp.tar b/cpp.tar new file mode 100644 index 0000000..48e9331 Binary files /dev/null and b/cpp.tar differ