All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.diozero.devices.Ads112C04 Maven / Gradle / Ivy

There is a newer version: 1.4.1
Show newest version
package com.diozero.devices;

/*-
 * #%L
 * Organisation: diozero
 * Project:      Device I/O Zero - Core
 * Filename:     Ads112C04.java  
 * 
 * This file is part of the diozero project. More information about this project
 * can be found at http://www.diozero.com/
 * %%
 * Copyright (C) 2016 - 2021 diozero
 * %%
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 * #L%
 */

/*-
 * Wiring:
 * # Con Name | Name Con  #
 * -----------+-------------
 * 1 GND   A0 | SCL  SCL  16 **
 * 2 GND   A1 | SDA  SDA  15 **
 * 3 3v3  RST | DRDY GP#X 14 *
 * 4 GND DGND | DVdd 3v3  13
 * 5 GND AVss | AVdd 3v3  12
 * 6     AIN3 | AIN0      11
 * 7     AIN2 | AIN1      10
 * 8     REFN | REFP      9
 *
 * * Active low therefore the GPIO must have the pull-up resistor enabled.
 * Otherwise connect to DVdd using a "weak" pull-up resistor (1 kOhm).
 *
 * ** The datasheet states 1 kOhm pull-up resistors for SCL and SDA
 * for standard and fast modes, and 350 Ohm for fast-mode plus.
 * Most boards have built-in pull-up resistors for SCL and SDA.
 */
import java.io.Closeable;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import org.tinylog.Logger;

import com.diozero.api.I2CDevice;
import com.diozero.api.I2CDeviceInterface;
import com.diozero.api.I2CDeviceInterface.I2CMessage;
import com.diozero.util.Crc;
import com.diozero.util.Hex;
import com.diozero.util.PropertyUtil;
import com.diozero.util.SleepUtil;

public class Ads112C04 implements Closeable {
	private static final int NUM_ADC_CHANNELS = 4;

	/**
	 * The ADS112C04 has two address pins: A0 and A1. Each address pin can be tied
	 * to either DGND, DVDD, SDA, or SCL, providing 16 possible unique addresses.
	 * This configuration allows up to 16 different ADS112C04 devices to be present
	 * on the same I2C bus. Name format is A1_A0
	 */
	public enum Address {
		GND_GND(0b01000000), GND_VDD(0b01000001), GND_SDA(0b01000010), GND_SCL(0b01000011), //
		VDD_GND(0b01000100), VDD_VDD(0b01000101), VDD_SDA(0b01000110), VDD_SCL(0b01000111), //
		SDA_GND(0b01001000), SDA_VDD(0b01001001), SDA_SDA(0b01001010), SDA_SCL(0b01001011), //
		SCL_GND(0b01001100), SCL_VDD(0b01001101), SCL_SDA(0b01001110), SCL_SCL(0b01001111);

		private int value;

		private Address(int value) {
			this.value = value;
		}

		public int getValue() {
			return value;
		}
	}

	public enum GainConfig {
		_1(1, 0b000), _2(2, 0b001), _4(4, 0b010), _8(8, 0b011), _16(16, 0b100), _32(32, 0b101), _64(64, 0b110),
		_128(128, 0b111);

		private int gain;
		private byte mask;

		private GainConfig(int gain, int mask) {
			this.gain = gain;
			this.mask = (byte) (mask << C0_GAIN_BIT_START);
		}

		public int getGain() {
			return gain;
		}

		byte getMask() {
			return mask;
		}
	}

	public enum Pga {
		ENABLED(0), DISABLED(1);

		private byte mask;

		private Pga(int mask) {
			this.mask = (byte) (mask << C0_PGA_BYPASS_BIT_START);
		}

		byte getMask() {
			return mask;
		}

		public boolean isEnabled() {
			return this == ENABLED;
		}
	}

	public enum DataRate {
		_20HZ(20, 0b000), _45HZ(45, 0b001), _90HZ(90, 0b010), _175HZ(175, 0b011), _330HZ(330, 0b100),
		_600HZ(600, 0b101), _1000HZ(1000, 0b110);

		private int dateRate;
		private byte mask;

		private DataRate(int dateRate, int mask) {
			this.dateRate = dateRate;
			this.mask = (byte) (mask << C1_DATA_RATE_BIT_START);
		}

		public int getDataRate() {
			return dateRate;
		}

		byte getMask() {
			return mask;
		}
	}

	public enum OperatingMode {
		NORMAL(1, 0b0), TURBO(2, 0b1);

		private int multiplier;
		private byte mask;

		private OperatingMode(int multiplier, int mask) {
			this.multiplier = multiplier;
			this.mask = (byte) (mask << C1_OP_MODE_BIT_START);
		}

		public int getMultiplier() {
			return multiplier;
		}

		byte getMask() {
			return mask;
		}
	}

	public enum ConversionMode {
		SINGLE_SHOT(0b0), CONTINUOUS(0b1);

		private byte mask;

		private ConversionMode(int mask) {
			this.mask = (byte) (mask << C1_CONV_MODE_BIT_START);
		}

		byte getMask() {
			return mask;
		}
	}

	public enum VRef {
		INTERNAL(0b00), EXTERNAL(0b01), ANALOG_SUPPLY(0b10);

		private byte mask;

		private VRef(int mask) {
			this.mask = (byte) (mask << C1_VREF_BIT_START);
		}

		public int getMask() {
			return mask;
		}
	}

	public enum TemperatureSensorMode {
		ENABLED(1), DISABLED(0);

		private byte mask;

		private TemperatureSensorMode(int mask) {
			this.mask = (byte) (mask << C1_TEMP_SENSOR_BIT_START);
		}

		byte getMask() {
			return mask;
		}

		public boolean isEnabled() {
			return this == ENABLED;
		}
	}

	public enum DataCounter {
		ENABLED(1), DISABLED(0);

		private byte mask;

		private DataCounter(int mask) {
			this.mask = (byte) (mask << C2_DATA_CNT_EN_BIT_START);
		}

		byte getMask() {
			return mask;
		}

		public boolean isEnabled() {
			return this == ENABLED;
		}
	}

	public enum CrcConfig {
		DISABLED(0b00), INVERTED_DATA_OUTPUT(0b01), CRC16(0b10);

		private byte mask;

		private CrcConfig(int mask) {
			this.mask = (byte) (mask << C2_CRC_EN_BIT_START);
		}

		byte getMask() {
			return mask;
		}
	}

	public enum BurnoutCurrentSources {
		ENABLED(1), DISABLED(0);

		private byte mask;

		private BurnoutCurrentSources(int mask) {
			this.mask = (byte) (mask << C2_BCS_BIT_START);
		}

		byte getMask() {
			return mask;
		}

		public boolean isEnabled() {
			return this == ENABLED;
		}
	}

	public enum IdacCurrent {
		OFF(0, 0b000), _10UA(10, 0b001), _50UA(50, 0b010), _100UA(100, 0b011), _250UA(250, 0b100), _500UA(500, 0b101),
		_1000UA(1000, 0b110), _1500UA(1500, 0b111);

		private int microAmps;
		private byte mask;

		private IdacCurrent(int microAmps, int mask) {
			this.microAmps = microAmps;
			this.mask = (byte) (mask << C2_IDAC_CRNT_BIT_START);
		}

		public int getMicroAmps() {
			return microAmps;
		}

		byte getMask() {
			return mask;
		}
	}

	public enum Idac1RoutingConfig {
		DISABLED(0b000), AIN0(0b001), AIN1(0b010), AIN2(0b011), AIN3(0b100), REFP(0b101), REFN(0b110);

		private byte mask;

		private Idac1RoutingConfig(int mask) {
			this.mask = (byte) (mask << C3_I1MUX_BIT_START);
		}

		byte getMask() {
			return mask;
		}
	}

	public enum Idac2RoutingConfig {
		DISABLED(0b000), AIN0(0b001), AIN1(0b010), AIN2(0b011), AIN3(0b100), REFP(0b101), REFN(0b110);

		private byte mask;

		private Idac2RoutingConfig(int mask) {
			this.mask = (byte) (mask << C3_I2MUX_BIT_START);
		}

		byte getMask() {
			return mask;
		}
	}

	/*
	 * The device has four 8-bit configuration registers that are accessible through
	 * the I2C interface using the RREG and WREG commands. After power-up or reset,
	 * all registers are set to the default values (which are all 0). All register
	 * values are retained during power-down mode.
	 */
	public enum ConfigRegister {
		_0(0b00), _1(0b01), _2(0b10), _3(0b11);

		private byte mask;

		private ConfigRegister(int mask) {
			this.mask = (byte) (mask << 2);
		}

		byte getMask() {
			return mask;
		}
	}

	/*- Config register 0
	 * MUX 7:4 (R/W), Gain 3:1 (R/W), PGA Bypass 0 (R/W)
	 */
	private static final int C0_MUX_BIT_START = 4;
	private static final int C0_GAIN_BIT_START = 1;
	private static final int C0_PGA_BYPASS_BIT_START = 0;
	/*- Config register 1
	 * Data Rate 7:5 (R/W), Operating Mode 4 (R/W), Conversion Mode 3 (R/W),
	 * VRef 2:1 (R/W), Temp. Sensor Mode 0 (R/W)
	 */
	private static final int C1_DATA_RATE_BIT_START = 5;
	private static final int C1_OP_MODE_BIT_START = 4;
	private static final int C1_CONV_MODE_BIT_START = 3;
	private static final int C1_VREF_BIT_START = 1;
	private static final int C1_TEMP_SENSOR_BIT_START = 0;
	/*- Config register 2
	 * Data Ready 7 (R), Data Counter Enable 6 (R/W), CRC Enable 5:4 (R/W),
	 * Burn-out Current Sources 3 (R/W), IDAC Current Setting 0 (R/W)
	 */
	private static final int C2_DATA_RDY_BIT_START = 7;
	private static final int C2_DATA_RDY_MASK = 1 << C2_DATA_RDY_BIT_START;
	private static final int C2_DATA_CNT_EN_BIT_START = 6;
	private static final int C2_CRC_EN_BIT_START = 4;
	private static final int C2_BCS_BIT_START = 3;
	private static final int C2_IDAC_CRNT_BIT_START = 0;
	/*- Config register 3
	 * I1MUX 7:5 (R/W), I2MUX 4:2 (R/W), Reserved 1:0 (R) Always 0
	 */
	private static final int C3_I1MUX_BIT_START = 5;
	private static final int C3_I2MUX_BIT_START = 2;

	private static final byte COMMAND_RESET = (byte) 0b00000110;
	private static final byte COMMAND_START = (byte) 0b00001000;
	private static final byte COMMAND_POWER_DOWN = (byte) 0b00000010;
	private static final byte COMMAND_RDATA = (byte) 0b00010000;
	private static final byte COMMAND_READ_REG = (byte) 0b00100000;
	private static final byte COMMAND_WRITE_REG = (byte) 0b01000000;

	// The CRC is based on the CRC-16-CCITT polynomial: x16 + x12 + x5 + 1 with an
	// initial value of FFFFh.
	private static final Crc.Params CRC_PARAMS = new Crc.Params(0b10001000000100001, 0xffff, false, false, 0x0000);

	public static class Builder {
		private int controller;
		private Address address;
		private GainConfig gainConfig = GainConfig._1;
		private Pga pga = Pga.ENABLED;
		private DataRate dataRate = DataRate._20HZ;
		private OperatingMode operatingMode = OperatingMode.NORMAL;
		private VRef vRef = VRef.INTERNAL;
		private TemperatureSensorMode tsMode = TemperatureSensorMode.DISABLED;
		private DataCounter dataCounter = DataCounter.DISABLED;
		private CrcConfig crcConfig = CrcConfig.DISABLED;
		private BurnoutCurrentSources burnoutCurrentSources = BurnoutCurrentSources.DISABLED;
		private IdacCurrent idacCurrent = IdacCurrent.OFF;
		private Idac1RoutingConfig idac1RoutingConfig = Idac1RoutingConfig.DISABLED;
		private Idac2RoutingConfig idac2RoutingConfig = Idac2RoutingConfig.DISABLED;

		protected Builder(Address address) {
			this.address = address;
		}

		public Builder setController(int controller) {
			this.controller = controller;
			return this;
		}

		public Builder setGainConfig(GainConfig gainConfig) {
			this.gainConfig = gainConfig;
			return this;
		}

		public Builder setPga(Pga pga) {
			this.pga = pga;
			return this;
		}

		public Builder setPgaEnabled(boolean pgaEnabled) {
			this.pga = pgaEnabled ? Pga.ENABLED : Pga.DISABLED;
			return this;
		}

		public Builder setDataRate(DataRate dataRate) {
			this.dataRate = dataRate;
			return this;
		}

		public Builder setOperatingMode(OperatingMode operatingMode) {
			this.operatingMode = operatingMode;
			return this;
		}

		public Builder setTurboModeEnabled(boolean turboModeEnabled) {
			this.operatingMode = turboModeEnabled ? OperatingMode.TURBO : OperatingMode.NORMAL;
			return this;
		}

		public Builder setVRef(VRef vRef) {
			this.vRef = vRef;
			return this;
		}

		public Builder setTemperatureSensorMode(TemperatureSensorMode tsMode) {
			this.tsMode = tsMode;
			return this;
		}

		public Builder setTemperatureSensorEnabled(boolean tsEnabled) {
			this.tsMode = tsEnabled ? TemperatureSensorMode.ENABLED : TemperatureSensorMode.DISABLED;
			return this;
		}

		public Builder setDataCounter(DataCounter dataCounter) {
			this.dataCounter = dataCounter;
			return this;
		}

		public Builder setDataCounterEnabled(boolean dcEnabled) {
			this.dataCounter = dcEnabled ? DataCounter.ENABLED : DataCounter.DISABLED;
			return this;
		}

		public Builder setCrcConfig(CrcConfig crcConfig) {
			this.crcConfig = crcConfig;
			return this;
		}

		public Builder setBurnoutCurrentSources(BurnoutCurrentSources burnoutCurrentSources) {
			this.burnoutCurrentSources = burnoutCurrentSources;
			return this;
		}

		public Builder setBurnoutCurrentSourcesEnabled(boolean burnoutCurrentSourcesEnabled) {
			this.burnoutCurrentSources = burnoutCurrentSourcesEnabled ? BurnoutCurrentSources.ENABLED
					: BurnoutCurrentSources.DISABLED;
			return this;
		}

		public Builder setIdacCurrent(IdacCurrent idacCurrent) {
			this.idacCurrent = idacCurrent;
			return this;
		}

		public Builder setIdac1RoutingConfig(Idac1RoutingConfig idac1RoutingConfig) {
			this.idac1RoutingConfig = idac1RoutingConfig;
			return this;
		}

		public Builder setIdac2RoutingConfig(Idac2RoutingConfig idac2RoutingConfig) {
			this.idac2RoutingConfig = idac2RoutingConfig;
			return this;
		}

		public Ads112C04 build() {
			// Validation
			/*-
			 * 1. Input Multiplexer
			 * For settings where AINN = AVSS, the PGA must be disabled (PGA_BYPASS = 1) and only gains 1, 2, and 4 can be used.
			 */
			/*-
			 * 2. Gain
			 * The PGA can only be disabled for gains 1, 2, and 4.
			 * The PGA is always enabled for gain settings 8 to 128, regardless of the PGA_BYPASS setting.
			 */
			if (!pga.isEnabled() && gainConfig.getGain() > GainConfig._4.getGain()) {
				throw new IllegalArgumentException(
						"The PGA can only be disabled for gains 1, 2, and 4 (requested gain: " + gainConfig + ")");
			}

			return new Ads112C04(controller, address, gainConfig, pga, dataRate, operatingMode, vRef, tsMode,
					dataCounter, crcConfig, burnoutCurrentSources, idacCurrent, idac1RoutingConfig, idac2RoutingConfig);
		}
	}

	public static Builder builder(Address address) {
		return new Builder(address);
	}

	private I2CDevice device;
	private GainConfig gainConfig;
	private Pga pga;
	private DataRate dataRate;
	private OperatingMode operatingMode;
	private ConversionMode conversionMode;
	private VRef vRef;
	private TemperatureSensorMode tsMode;
	private DataCounter dataCounter;
	private CrcConfig crcConfig;
	private BurnoutCurrentSources burnoutCurrentSources;
	private IdacCurrent idacCurrent;
	private Idac1RoutingConfig idac1RoutingConfig;
	private Idac2RoutingConfig idac2RoutingConfig;
	private byte inputMultiplexer;
	private int lastDataCounter;

	// Testing
	private boolean repeatedStart;
	private boolean method1;

	protected Ads112C04(int controller, Address address, GainConfig gainConfig, Pga pga, DataRate dataRate,
			OperatingMode operatingMode, VRef vRef, TemperatureSensorMode tsMode, DataCounter dataCounter,
			CrcConfig crcConfig, BurnoutCurrentSources burnoutCurrentSources, IdacCurrent idacCurrent,
			Idac1RoutingConfig idac1RoutingConfig, Idac2RoutingConfig idac2RoutingConfig) {
		this.gainConfig = gainConfig;
		this.pga = pga;
		this.dataRate = dataRate;
		this.operatingMode = operatingMode;
		this.vRef = vRef;
		this.tsMode = tsMode;
		this.dataCounter = dataCounter;
		this.crcConfig = crcConfig;
		this.burnoutCurrentSources = burnoutCurrentSources;
		this.idacCurrent = idacCurrent;
		this.idac1RoutingConfig = idac1RoutingConfig;
		this.idac2RoutingConfig = idac2RoutingConfig;

		inputMultiplexer = (byte) ((0b1000) << C0_MUX_BIT_START);

		conversionMode = ConversionMode.SINGLE_SHOT;
		lastDataCounter = -1;

		repeatedStart = PropertyUtil.getBooleanProperty("diozero.ads112c04.repeatedStart", true);
		Logger.debug("repeatedStart: {}", Boolean.valueOf(repeatedStart));
		method1 = PropertyUtil.getBooleanProperty("diozero.ads112c04.method1", true);
		Logger.debug("method1: {}", Boolean.valueOf(method1));

		device = I2CDevice.builder(address.getValue()).setController(controller).setByteOrder(ByteOrder.BIG_ENDIAN)
				.build();

		reset();

		setConfig0();
		setConfig1();
		setConfig2();
		setConfig3();
	}

	@Override
	public void close() {
		device.close();
	}

	public void reset() {
		Logger.debug("reset");
		device.writeByte(COMMAND_RESET);
		// TODO Check delays
		SleepUtil.sleepMillis(1);
	}

	public void start() {
		Logger.debug("start");
		device.writeByte(COMMAND_START);
		// TODO Check delays
		SleepUtil.busySleep(10_000);
	}

	public void powerDown() {
		Logger.debug("powerDown");
		device.writeByte(COMMAND_POWER_DOWN);
		// TODO Check delays
		SleepUtil.sleepMillis(1);
	}

	public byte readConfigRegister(ConfigRegister register) {
		// TODO Also read CRC data if enabled!
		if (crcConfig == CrcConfig.DISABLED) {
			return device.readByteData(COMMAND_READ_REG | register.getMask());
		}

		byte[] buffer = new byte[crcConfig == CrcConfig.CRC16 ? 3 : 2];
		device.readI2CBlockData(COMMAND_READ_REG, buffer);
		if (Logger.isTraceEnabled()) {
			Hex.dumpByteArray(buffer);
		}
		if (crcConfig == CrcConfig.CRC16) {
			int calc_crc = Crc.crc16(CRC_PARAMS, buffer[0]);
			int crc = (buffer[1] & 0xff) << 8 | (buffer[2] & 0xff);
			if (crc != calc_crc) {
				Logger.warn("CRC-16 error calculated {}, got {} for value {}", Integer.valueOf(calc_crc),
						Integer.valueOf(crc), Byte.valueOf(buffer[0]));
			}
		} else {
			byte calc_val_inverted = (byte) (~buffer[0]);
			if (buffer[1] != calc_val_inverted) {
				Logger.warn("Data Integrity error calculated {}, got {} for value {}",
						Integer.valueOf(calc_val_inverted), Integer.valueOf(buffer[1]), Byte.valueOf(buffer[0]));
			}
		}

		return buffer[0];
	}

	private void writeConfigRegister(ConfigRegister register, byte value) {
		device.writeByteData(COMMAND_WRITE_REG | register.getMask(), value);
		// TODO Check delays
	}

	private void setConfig0() {
		// Logger.debug("setConfig0");
		writeConfigRegister(ConfigRegister._0, (byte) (inputMultiplexer | gainConfig.getMask() | pga.getMask()));
	}

	private void setConfig1() {
		// Logger.debug("setConfig1");
		writeConfigRegister(ConfigRegister._1, (byte) (dataRate.getMask() | operatingMode.getMask()
				| conversionMode.getMask() | vRef.getMask() | tsMode.getMask()));
	}

	private void setConfig2() {
		// Logger.debug("setConfig2");
		writeConfigRegister(ConfigRegister._2, (byte) (dataCounter.getMask() | crcConfig.getMask()
				| burnoutCurrentSources.getMask() | idacCurrent.getMask()));
	}

	private void setConfig3() {
		// Logger.debug("setConfig3");
		writeConfigRegister(ConfigRegister._3, (byte) (idac1RoutingConfig.getMask() | idac2RoutingConfig.getMask()));
	}

	public int getInputMultiplexer() {
		return inputMultiplexer;
	}

	/**
	 * Set the input multiplexer configuration
	 * 
	 * For settings where AINN = AVSS, the PGA must be disabled (PGA_BYPASS = 1) and
	 * only gains 1, 2, and 4 can be used.
	 * 
	 * 
	 * 0000 : AINP = AIN0, AINN = AIN1 (default)
	 * 0001 : AINP = AIN0, AINN = AIN2
	 * 0010 : AINP = AIN0, AINN = AIN3
	 * 0011 : AINP = AIN1, AINN = AIN0
	 * 0100 : AINP = AIN1, AINN = AIN2
	 * 0101 : AINP = AIN1, AINN = AIN3
	 * 0110 : AINP = AIN2, AINN = AIN3
	 * 0111 : AINP = AIN3, AINN = AIN2
	 * 1000 : AINP = AIN0, AINN = AVSS
	 * 1001 : AINP = AIN1, AINN = AVSS
	 * 1010 : AINP = AIN2, AINN = AVSS
	 * 1011 : AINP = AIN3, AINN = AVSS
	 * 1100 : (V(REFP) – V(REFN)) / 4 monitor (PGA bypassed)
	 * 1101 : (AVDD – AVSS) / 4 monitor (PGA bypassed)
	 * 1110 : AINP and AINN shorted to (AVDD + AVSS) / 2
	 * 
* * @param inputMultiplexer the input multiplexer */ public void setInputMultiplexer(int inputMultiplexer) { if (inputMultiplexer < 0 || inputMultiplexer > 0b1110) { throw new IllegalArgumentException( "Invalid input multiplexer value: " + inputMultiplexer + " must be 0..14"); } this.inputMultiplexer = (byte) (inputMultiplexer & 0xf); setConfig0(); } public GainConfig getGainConfig() { return gainConfig; } public void setGainConfig(GainConfig gainConfig) { this.gainConfig = gainConfig; setConfig0(); } public Pga getPga() { return pga; } public void setPga(Pga pga) { this.pga = pga; setConfig0(); } public DataRate getDataRate() { return dataRate; } public void setDataRate(DataRate dataRate) { this.dataRate = dataRate; setConfig1(); } public boolean isTurboModeEnabled() { return operatingMode == OperatingMode.TURBO; } public void setTurboModeEnabled(boolean enabled) { this.operatingMode = enabled ? OperatingMode.TURBO : OperatingMode.NORMAL; setConfig1(); } public int getDataRateFrequency() { return dataRate.getDataRate() * operatingMode.getMultiplier(); } public VRef getVRef() { return vRef; } public void setVRef(VRef vRef) { this.vRef = vRef; setConfig1(); } public boolean isTemperatureSensorModeEnabled() { return tsMode.isEnabled(); } public void setTemperatureSensorModeEnabled(boolean enabled) { this.tsMode = enabled ? TemperatureSensorMode.ENABLED : TemperatureSensorMode.DISABLED; setConfig1(); } public boolean isDataCounterEnabled() { return dataCounter.isEnabled(); } public void setDataCounterEnabled(boolean enabled) { this.dataCounter = enabled ? DataCounter.ENABLED : DataCounter.DISABLED; setConfig2(); } public CrcConfig getCrcConfig() { return crcConfig; } public void setCrcConfig(CrcConfig crcConfig) { this.crcConfig = crcConfig; setConfig2(); } public BurnoutCurrentSources getBurnoutCurrentSources() { return burnoutCurrentSources; } public void setBurnoutCurrentSources(BurnoutCurrentSources burnoutCurrentSources) { this.burnoutCurrentSources = burnoutCurrentSources; setConfig2(); } public IdacCurrent getIdacCurrent() { return idacCurrent; } public void setIdacCurrent(IdacCurrent idacCurrent) { this.idacCurrent = idacCurrent; setConfig2(); } public Idac1RoutingConfig getIdac1RoutingConfig() { return idac1RoutingConfig; } public void setIdac1RoutingConfig(Idac1RoutingConfig idac1RoutingConfig) { this.idac1RoutingConfig = idac1RoutingConfig; setConfig3(); } public Idac2RoutingConfig getIdac2RoutingConfig() { return idac2RoutingConfig; } public void setIdac2RoutingConfig(Idac2RoutingConfig idac2RoutingConfig) { this.idac2RoutingConfig = idac2RoutingConfig; setConfig3(); } public void setConfig0(GainConfig gainConfig, Pga pga) { this.gainConfig = gainConfig; this.pga = pga; setConfig0(); } public void setConfig1(DataRate dataRate, boolean turboModeEnabled, VRef vRef, boolean temperatureSensorEnabled) { this.dataRate = dataRate; this.operatingMode = turboModeEnabled ? OperatingMode.TURBO : OperatingMode.NORMAL; this.vRef = vRef; this.tsMode = temperatureSensorEnabled ? TemperatureSensorMode.ENABLED : TemperatureSensorMode.DISABLED; setConfig1(); } public void setConfig2(boolean dataCounterEnabled, CrcConfig crcConfig, BurnoutCurrentSources burnoutCurrentSources, IdacCurrent idacCurrent) { this.dataCounter = dataCounterEnabled ? DataCounter.ENABLED : DataCounter.DISABLED; this.crcConfig = crcConfig; this.burnoutCurrentSources = burnoutCurrentSources; this.idacCurrent = idacCurrent; setConfig2(); } public void setConfig3(Idac1RoutingConfig idac1RoutingConfig, Idac2RoutingConfig idac2RoutingConfig) { this.idac1RoutingConfig = idac1RoutingConfig; this.idac2RoutingConfig = idac2RoutingConfig; setConfig3(); } public void setSingleShotMode() { // System.out.println("getValueSingle"); conversionMode = ConversionMode.SINGLE_SHOT; setConfig1(); // Start command must be issued each time the CM bit is changed start(); } public short getSingleShotReading(int adcNumber) { if (adcNumber < 0 || adcNumber >= NUM_ADC_CHANNELS) { throw new IllegalArgumentException("Invalid input channel number - " + adcNumber); } inputMultiplexer = (byte) ((0b1000 + adcNumber) << C0_MUX_BIT_START); setConfig0(); // Must issue a start command to trigger a new reading start(); return method1 ? getReadingOnDataReadyBit() : getReadingOnDataReadyBit2(); } public void setContinuousMode(int adcNumber) { inputMultiplexer = (byte) ((0b1000 + adcNumber) << C0_MUX_BIT_START); setConfig0(); conversionMode = ConversionMode.CONTINUOUS; setConfig1(); start(); } /** * Read data whenever the data read bit is set in Config Register #2 * * @return the raw analog data reading in signed short format */ public short getReadingOnDataReadyBit() { /*- * DC enabled and CRC disabled is 3 bytes (1 DC, 2 data) * DC enabled and CRC enabled is 5 bytes (1 DC, 2 data, 2 CRC). * DC disabled and CRC enabled is 4 bytes (2 data, 2 CRC). * DC enabled and CRC inverted is 6 bytes (1 DC, 2 data, 1 DC inv. and 2 data inv.). * DC disabled and CRC inverted is 4 bytes (2 data, 2 data inv.). */ int bytes_to_read = 2; if (dataCounter.isEnabled()) { bytes_to_read++; } if (crcConfig != CrcConfig.DISABLED) { bytes_to_read += 2; if (dataCounter.isEnabled() && crcConfig == CrcConfig.INVERTED_DATA_OUTPUT) { bytes_to_read++; } } // Logger.debug("Waiting for data to be available..."); // Wait for the Data Ready bit to be set in config register #2 while (true) { if ((readConfigRegister(ConfigRegister._2) & C2_DATA_RDY_MASK) != 0) { break; } // 100 nS SleepUtil.busySleep(100); } // Logger.debug("Data available"); // SleepUtil.sleepMillis(2); /*- byte[] buffer = device.readI2CBlockDataByteArray(COMMAND_RDATA, bytes_to_read); Logger.debug("Read {} bytes:", Integer.valueOf(buffer.length)); */ byte[] buffer = new byte[1 + bytes_to_read]; // device.readNoStop(COMMAND_RDATA, bytes_to_read, buffer, repeatedStart); buffer[0] = COMMAND_RDATA; I2CMessage messages[] = new I2CMessage[2]; messages[0] = new I2CMessage(I2CMessage.I2C_M_WR, 1); messages[1] = new I2CMessage(I2CMessage.I2C_M_RD, bytes_to_read); device.readWrite(messages, buffer); if (Logger.isTraceEnabled()) { Hex.dumpByteArray(buffer); } ByteBuffer bb = ByteBuffer.wrap(buffer, 1, bytes_to_read); bb.order(ByteOrder.BIG_ENDIAN); int counter = -1; if (dataCounter.isEnabled()) { counter = bb.get() & 0xff; Logger.debug("Conversion counter: {}", Integer.valueOf(counter)); } short value = bb.getShort(); if (crcConfig != CrcConfig.DISABLED) { // Validate the CRC value if (crcConfig == CrcConfig.INVERTED_DATA_OUTPUT) { // A bitwise-inverted version of the data if (dataCounter.isEnabled()) { int counter_inverted = bb.get() & 0xff; int calc_counter_inverted = ~counter & 0xff; if (calc_counter_inverted != counter_inverted) { Logger.warn("Inversion error for counter {}, calculated {}, got {}", Integer.valueOf(counter), Integer.valueOf(calc_counter_inverted), Integer.valueOf(counter_inverted)); } } short value_inverted = bb.getShort(); short calc_val_inverted = (short) (~value); if (calc_val_inverted != value_inverted) { Logger.warn("Inversion error for data {}, calculated {}, got {}. DC Enabled: {}{}", Short.valueOf(value), Short.valueOf(calc_val_inverted), Short.valueOf(value_inverted), Boolean.valueOf(dataCounter.isEnabled()), dataCounter.isEnabled() ? " - " + counter : ""); } } else if (crcConfig == CrcConfig.CRC16) { int crc_val = bb.getShort() & 0xffff; // In CRC mode, the checksum bytes are the 16-bit remainder of the bitwise // exclusive-OR (XOR) of the data bytes with a CRC polynomial /*- * The CRC is "for the entire data being returned" * i.e. includes the data counter if present * https://e2e.ti.com/support/data-converters/f/73/t/758829 * From the datasheet: * The optional data counter word that precedes conversion data is covered by both data * integrity options. */ int calc_crc_val; if (dataCounter.isEnabled()) { calc_crc_val = Crc.crc16(CRC_PARAMS, (byte) counter, (byte) (value >> 8), (byte) value); } else { calc_crc_val = Crc.crc16Short(CRC_PARAMS, value); } if (calc_crc_val != crc_val) { Logger.warn("CRC error for value {}, calculated {}, got {}. DC Enabled: {}{}", Short.valueOf(value), Integer.valueOf((calc_crc_val)), Integer.valueOf(crc_val), Boolean.valueOf(dataCounter.isEnabled()), dataCounter.isEnabled() ? " - " + counter : ""); } } } return value; } /** * Read data whenever the data read bit is set in Config Register #2 * * @return the raw analog data reading in signed short format */ public short getReadingOnDataReadyBit2() { /*- * DC disabled and CRC disabled is 2 bytes (2 data) * DC enabled and CRC disabled is 3 bytes (1 DC, 2 data) * DC enabled and CRC enabled is 5 bytes (1 DC, 2 data, 2 CRC). * DC disabled and CRC enabled is 4 bytes (2 data, 2 CRC). * DC enabled and CRC inverted is 6 bytes (1 DC, 2 data, 1 DC inv. and 2 data inv.). * DC disabled and CRC inverted is 4 bytes (2 data, 2 data inv.). */ int bytes_to_read = 2; if (dataCounter.isEnabled()) { // The data counter prefix bytes_to_read++; } if (crcConfig != CrcConfig.DISABLED) { // The data integrity check for data bytes_to_read += 2; if (dataCounter.isEnabled() && crcConfig == CrcConfig.INVERTED_DATA_OUTPUT) { // The inverted data counter bytes_to_read++; } } I2CDeviceInterface.I2CMessage[] messages = { // new I2CDeviceInterface.I2CMessage(I2CDeviceInterface.I2CMessage.I2C_M_WR, 1), // Write config register 2 new I2CDeviceInterface.I2CMessage(I2CDeviceInterface.I2CMessage.I2C_M_RD, 1), // Read the value new I2CDeviceInterface.I2CMessage(I2CDeviceInterface.I2CMessage.I2C_M_WR, 1), // Write data register new I2CDeviceInterface.I2CMessage(I2CDeviceInterface.I2CMessage.I2C_M_RD, bytes_to_read) // Read the // value }; // WRITE Config Reg 2 Addr, READ Config Reg 2 val, WRITE RDATA Addr, READ RDATA // value byte[] buffer = new byte[3 + bytes_to_read]; // Write Config Register #2 buffer[0] = (byte) (COMMAND_READ_REG | ConfigRegister._2.getMask()); // Placeholder for Config Register #2 data buffer[1] = 0; // Write Data Register buffer[2] = COMMAND_RDATA; // Buffer 3..end RDATA value // Wait for the Data Ready bit to be set in config register #2 // Logger.debug("Waiting for data to be available..."); while (true) { device.readWrite(messages, buffer); if ((buffer[1] & C2_DATA_RDY_MASK) != 0) { break; } // 100 nS before trying again SleepUtil.busySleep(100); } // Logger.debug("Data available"); if (Logger.isTraceEnabled()) { Hex.dumpByteArray(buffer); } ByteBuffer bb = ByteBuffer.wrap(buffer, 3, bytes_to_read); bb.order(ByteOrder.BIG_ENDIAN); int counter = -1; if (dataCounter.isEnabled()) { counter = bb.get() & 0xff; Logger.debug("Conversion counter: {}", Integer.valueOf(counter)); } short value = bb.getShort(); if (crcConfig != CrcConfig.DISABLED) { // Validate the CRC value if (crcConfig == CrcConfig.INVERTED_DATA_OUTPUT) { // A bitwise-inverted version of the data if (dataCounter.isEnabled()) { int counter_inverted = bb.get() & 0xff; int calc_counter_inverted = ~counter & 0xff; if (calc_counter_inverted != counter_inverted) { Logger.warn("Inversion error for counter {}, calculated {}, got {}", Integer.valueOf(counter), Integer.valueOf(calc_counter_inverted), Integer.valueOf(counter_inverted)); } } short value_inverted = bb.getShort(); short calc_val_inverted = (short) (~value); if (calc_val_inverted != value_inverted) { Logger.warn("Inversion error for data {}, calculated {}, got {}. DC Enabled: {}{}", Short.valueOf(value), Short.valueOf(calc_val_inverted), Short.valueOf(value_inverted), Boolean.valueOf(dataCounter.isEnabled()), dataCounter.isEnabled() ? " - " + counter : ""); } } else if (crcConfig == CrcConfig.CRC16) { int crc_val = bb.getShort() & 0xffff; // In CRC mode, the checksum bytes are the 16-bit remainder of the bitwise // exclusive-OR (XOR) of the data bytes with a CRC polynomial /*- * The CRC is "for the entire data being returned" * i.e. includes the data counter if present * https://e2e.ti.com/support/data-converters/f/73/t/758829 * From the datasheet: * The optional data counter word that precedes conversion data is covered by both data * integrity options. */ int calc_crc_val; if (dataCounter.isEnabled()) { calc_crc_val = Crc.crc16(CRC_PARAMS, (byte) counter, (byte) (value >> 8), (byte) value); } else { calc_crc_val = Crc.crc16Short(CRC_PARAMS, value); } if (calc_crc_val != crc_val) { Logger.warn("CRC error for value {}, calculated {}, got {}. DC Enabled: {}{}", Short.valueOf(value), Integer.valueOf((calc_crc_val)), Integer.valueOf(crc_val), Boolean.valueOf(dataCounter.isEnabled()), dataCounter.isEnabled() ? " - " + counter : ""); } } } return value; } public short getReadingOnDataCounterChange() { // Data counter must be available for this method to work if (dataCounter == DataCounter.DISABLED) { throw new IllegalArgumentException("Data counter must be enabled"); } byte[] buffer; switch (crcConfig) { case CRC16: buffer = new byte[5]; break; case INVERTED_DATA_OUTPUT: buffer = new byte[6]; break; case DISABLED: default: buffer = new byte[3]; } short value; while (true) { device.readI2CBlockData(COMMAND_RDATA, buffer); int new_dc = buffer[0] & 0xff; if (new_dc != lastDataCounter) { if (lastDataCounter != -1 && (new_dc != lastDataCounter + 1)) { Logger.info("Missed a reading - last DC: {}, new DC: {}", Integer.valueOf(lastDataCounter), Integer.valueOf(new_dc)); } lastDataCounter = new_dc; // TODO If DI is set to inverted, buffer[1] is the inversion of the DC value = (short) ((buffer[1] << 8) | (buffer[2] & 0xff)); // TODO Data Integrity validation break; } SleepUtil.busySleep(100); } return value; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy