001/*----------------------------------------------------------------------------*/
002/* Copyright (c) FIRST 2008-2017. All Rights Reserved.                        */
003/* Open Source Software - may be modified and shared by FRC teams. The code   */
004/* must be accompanied by the FIRST BSD license file in the root directory of */
005/* the project.                                                               */
006/*----------------------------------------------------------------------------*/
007
008package edu.wpi.first.wpilibj;
009
010import java.nio.ByteBuffer;
011import java.nio.ByteOrder;
012
013import edu.wpi.first.wpilibj.hal.FRCNetComm.tResourceType;
014import edu.wpi.first.wpilibj.hal.HAL;
015import edu.wpi.first.wpilibj.interfaces.Accelerometer;
016import edu.wpi.first.wpilibj.livewindow.LiveWindow;
017import edu.wpi.first.wpilibj.livewindow.LiveWindowSendable;
018import edu.wpi.first.wpilibj.tables.ITable;
019
020/**
021 * ADXL362 SPI Accelerometer.
022 *
023 * <p>This class allows access to an Analog Devices ADXL362 3-axis accelerometer.
024 */
025@SuppressWarnings("PMD.UnusedPrivateField")
026public class ADXL362 extends SensorBase implements Accelerometer, LiveWindowSendable {
027  private static final byte kRegWrite = 0x0A;
028  private static final byte kRegRead = 0x0B;
029
030  private static final byte kPartIdRegister = 0x02;
031  private static final byte kDataRegister = 0x0E;
032  private static final byte kFilterCtlRegister = 0x2C;
033  private static final byte kPowerCtlRegister = 0x2D;
034
035  private static final byte kFilterCtl_Range2G = 0x00;
036  private static final byte kFilterCtl_Range4G = 0x40;
037  private static final byte kFilterCtl_Range8G = (byte) 0x80;
038  private static final byte kFilterCtl_ODR_100Hz = 0x03;
039
040  private static final byte kPowerCtl_UltraLowNoise = 0x20;
041  private static final byte kPowerCtl_AutoSleep = 0x04;
042  private static final byte kPowerCtl_Measure = 0x02;
043
044  public enum Axes {
045    kX((byte) 0x00),
046    kY((byte) 0x02),
047    kZ((byte) 0x04);
048
049    @SuppressWarnings("MemberName")
050    public final byte value;
051
052    private Axes(byte value) {
053      this.value = value;
054    }
055  }
056
057  @SuppressWarnings("MemberName")
058  public static class AllAxes {
059    public double XAxis;
060    public double YAxis;
061    public double ZAxis;
062  }
063
064  private SPI m_spi;
065  private double m_gsPerLSB;
066
067  /**
068   * Constructor.  Uses the onboard CS1.
069   *
070   * @param range The range (+ or -) that the accelerometer will measure.
071   */
072  public ADXL362(Range range) {
073    this(SPI.Port.kOnboardCS1, range);
074  }
075
076  /**
077   * Constructor.
078   *
079   * @param port  The SPI port that the accelerometer is connected to
080   * @param range The range (+ or -) that the accelerometer will measure.
081   */
082  public ADXL362(SPI.Port port, Range range) {
083    m_spi = new SPI(port);
084    m_spi.setClockRate(3000000);
085    m_spi.setMSBFirst();
086    m_spi.setSampleDataOnFalling();
087    m_spi.setClockActiveLow();
088    m_spi.setChipSelectActiveLow();
089
090    // Validate the part ID
091    ByteBuffer transferBuffer = ByteBuffer.allocateDirect(3);
092    transferBuffer.put(0, kRegRead);
093    transferBuffer.put(1, kPartIdRegister);
094    m_spi.transaction(transferBuffer, transferBuffer, 3);
095    if (transferBuffer.get(2) != (byte) 0xF2) {
096      m_spi.free();
097      m_spi = null;
098      DriverStation.reportError("could not find ADXL362 on SPI port " + port.value, false);
099      return;
100    }
101
102    setRange(range);
103
104    // Turn on the measurements
105    transferBuffer.put(0, kRegWrite);
106    transferBuffer.put(1, kPowerCtlRegister);
107    transferBuffer.put(2, (byte) (kPowerCtl_Measure | kPowerCtl_UltraLowNoise));
108    m_spi.write(transferBuffer, 3);
109
110    HAL.report(tResourceType.kResourceType_ADXL362, port.value);
111    LiveWindow.addSensor("ADXL362", port.value, this);
112  }
113
114  public void free() {
115    m_spi.free();
116  }
117
118  @Override
119  public void setRange(Range range) {
120    final byte value;
121
122    switch (range) {
123      case k2G:
124        value = kFilterCtl_Range2G;
125        m_gsPerLSB = 0.001;
126        break;
127      case k4G:
128        value = kFilterCtl_Range4G;
129        m_gsPerLSB = 0.002;
130        break;
131      case k8G:
132      case k16G:  // 16G not supported; treat as 8G
133        value = kFilterCtl_Range8G;
134        m_gsPerLSB = 0.004;
135        break;
136      default:
137        throw new IllegalArgumentException(range + " unsupported");
138
139    }
140
141    // Specify the data format to read
142    byte[] commands = new byte[]{kRegWrite, kFilterCtlRegister, (byte) (kFilterCtl_ODR_100Hz
143        | value)};
144    m_spi.write(commands, commands.length);
145  }
146
147
148  @Override
149  public double getX() {
150    return getAcceleration(Axes.kX);
151  }
152
153  @Override
154  public double getY() {
155    return getAcceleration(Axes.kY);
156  }
157
158  @Override
159  public double getZ() {
160    return getAcceleration(Axes.kZ);
161  }
162
163  /**
164   * Get the acceleration of one axis in Gs.
165   *
166   * @param axis The axis to read from.
167   * @return Acceleration of the ADXL362 in Gs.
168   */
169  public double getAcceleration(ADXL362.Axes axis) {
170    if (m_spi == null) {
171      return 0.0;
172    }
173    ByteBuffer transferBuffer = ByteBuffer.allocateDirect(4);
174    transferBuffer.put(0, kRegRead);
175    transferBuffer.put(1, (byte) (kDataRegister + axis.value));
176    m_spi.transaction(transferBuffer, transferBuffer, 4);
177    // Sensor is little endian
178    transferBuffer.order(ByteOrder.LITTLE_ENDIAN);
179
180    return transferBuffer.getShort(2) * m_gsPerLSB;
181  }
182
183  /**
184   * Get the acceleration of all axes in Gs.
185   *
186   * @return An object containing the acceleration measured on each axis of the ADXL362 in Gs.
187   */
188  public ADXL362.AllAxes getAccelerations() {
189    ADXL362.AllAxes data = new ADXL362.AllAxes();
190    if (m_spi != null) {
191      ByteBuffer dataBuffer = ByteBuffer.allocateDirect(8);
192      // Select the data address.
193      dataBuffer.put(0, kRegRead);
194      dataBuffer.put(1, kDataRegister);
195      m_spi.transaction(dataBuffer, dataBuffer, 8);
196      // Sensor is little endian... swap bytes
197      dataBuffer.order(ByteOrder.LITTLE_ENDIAN);
198
199      data.XAxis = dataBuffer.getShort(2) * m_gsPerLSB;
200      data.YAxis = dataBuffer.getShort(4) * m_gsPerLSB;
201      data.ZAxis = dataBuffer.getShort(6) * m_gsPerLSB;
202    }
203    return data;
204  }
205
206  @Override
207  public String getSmartDashboardType() {
208    return "3AxisAccelerometer";
209  }
210
211  private ITable m_table;
212
213  @Override
214  public void initTable(ITable subtable) {
215    m_table = subtable;
216    updateTable();
217  }
218
219  @Override
220  public void updateTable() {
221    if (m_table != null) {
222      m_table.putNumber("X", getX());
223      m_table.putNumber("Y", getY());
224      m_table.putNumber("Z", getZ());
225    }
226  }
227
228  @Override
229  public ITable getTable() {
230    return m_table;
231  }
232
233  @Override
234  public void startLiveWindowMode() {
235  }
236
237  @Override
238  public void stopLiveWindowMode() {
239  }
240}