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.tInstances;
014import edu.wpi.first.wpilibj.hal.FRCNetComm.tResourceType;
015import edu.wpi.first.wpilibj.hal.HAL;
016import edu.wpi.first.wpilibj.interfaces.Accelerometer;
017import edu.wpi.first.wpilibj.livewindow.LiveWindow;
018import edu.wpi.first.wpilibj.livewindow.LiveWindowSendable;
019import edu.wpi.first.wpilibj.tables.ITable;
020
021/**
022 * ADXL345 SPI Accelerometer.
023 */
024@SuppressWarnings({"TypeName", "PMD.UnusedPrivateField"})
025public class ADXL345_SPI extends SensorBase implements Accelerometer, LiveWindowSendable {
026  private static final int kPowerCtlRegister = 0x2D;
027  private static final int kDataFormatRegister = 0x31;
028  private static final int kDataRegister = 0x32;
029  private static final double kGsPerLSB = 0.00390625;
030
031  private static final int kAddress_Read = 0x80;
032  private static final int kAddress_MultiByte = 0x40;
033
034  private static final int kPowerCtl_Link = 0x20;
035  private static final int kPowerCtl_AutoSleep = 0x10;
036  private static final int kPowerCtl_Measure = 0x08;
037  private static final int kPowerCtl_Sleep = 0x04;
038
039  private static final int kDataFormat_SelfTest = 0x80;
040  private static final int kDataFormat_SPI = 0x40;
041  private static final int kDataFormat_IntInvert = 0x20;
042  private static final int kDataFormat_FullRes = 0x08;
043  private static final int kDataFormat_Justify = 0x04;
044
045  public enum Axes {
046    kX((byte) 0x00),
047    kY((byte) 0x02),
048    kZ((byte) 0x04);
049
050    /**
051     * The integer value representing this enumeration.
052     */
053    @SuppressWarnings("MemberName")
054    public final byte value;
055
056    private Axes(byte value) {
057      this.value = value;
058    }
059  }
060
061  @SuppressWarnings("MemberName")
062  public static class AllAxes {
063
064    public double XAxis;
065    public double YAxis;
066    public double ZAxis;
067  }
068
069  protected SPI m_spi;
070
071  /**
072   * Constructor.
073   *
074   * @param port  The SPI port that the accelerometer is connected to
075   * @param range The range (+ or -) that the accelerometer will measure.
076   */
077  public ADXL345_SPI(SPI.Port port, Range range) {
078    m_spi = new SPI(port);
079    init(range);
080    LiveWindow.addSensor("ADXL345_SPI", port.value, this);
081  }
082
083  public void free() {
084    m_spi.free();
085  }
086
087  /**
088   * Set SPI bus parameters, bring device out of sleep and set format.
089   *
090   * @param range The range (+ or -) that the accelerometer will measure.
091   */
092  private void init(Range range) {
093    m_spi.setClockRate(500000);
094    m_spi.setMSBFirst();
095    m_spi.setSampleDataOnFalling();
096    m_spi.setClockActiveLow();
097    m_spi.setChipSelectActiveHigh();
098
099    // Turn on the measurements
100    byte[] commands = new byte[2];
101    commands[0] = kPowerCtlRegister;
102    commands[1] = kPowerCtl_Measure;
103    m_spi.write(commands, 2);
104
105    setRange(range);
106
107    HAL.report(tResourceType.kResourceType_ADXL345, tInstances.kADXL345_SPI);
108  }
109
110  @Override
111  public void setRange(Range range) {
112    final byte value;
113
114    switch (range) {
115      case k2G:
116        value = 0;
117        break;
118      case k4G:
119        value = 1;
120        break;
121      case k8G:
122        value = 2;
123        break;
124      case k16G:
125        value = 3;
126        break;
127      default:
128        throw new IllegalArgumentException(range + " unsupported");
129    }
130
131    // Specify the data format to read
132    byte[] commands = new byte[]{kDataFormatRegister, (byte) (kDataFormat_FullRes | value)};
133    m_spi.write(commands, commands.length);
134  }
135
136  @Override
137  public double getX() {
138    return getAcceleration(Axes.kX);
139  }
140
141  @Override
142  public double getY() {
143    return getAcceleration(Axes.kY);
144  }
145
146  @Override
147  public double getZ() {
148    return getAcceleration(Axes.kZ);
149  }
150
151  /**
152   * Get the acceleration of one axis in Gs.
153   *
154   * @param axis The axis to read from.
155   * @return Acceleration of the ADXL345 in Gs.
156   */
157  public double getAcceleration(ADXL345_SPI.Axes axis) {
158    ByteBuffer transferBuffer = ByteBuffer.allocateDirect(3);
159    transferBuffer.put(0,
160        (byte) ((kAddress_Read | kAddress_MultiByte | kDataRegister) + axis.value));
161    m_spi.transaction(transferBuffer, transferBuffer, 3);
162    // Sensor is little endian
163    transferBuffer.order(ByteOrder.LITTLE_ENDIAN);
164
165    return transferBuffer.getShort(1) * kGsPerLSB;
166  }
167
168  /**
169   * Get the acceleration of all axes in Gs.
170   *
171   * @return An object containing the acceleration measured on each axis of the ADXL345 in Gs.
172   */
173  public ADXL345_SPI.AllAxes getAccelerations() {
174    ADXL345_SPI.AllAxes data = new ADXL345_SPI.AllAxes();
175    if (m_spi != null) {
176      ByteBuffer dataBuffer = ByteBuffer.allocateDirect(7);
177      // Select the data address.
178      dataBuffer.put(0, (byte) (kAddress_Read | kAddress_MultiByte | kDataRegister));
179      m_spi.transaction(dataBuffer, dataBuffer, 7);
180      // Sensor is little endian... swap bytes
181      dataBuffer.order(ByteOrder.LITTLE_ENDIAN);
182
183      data.XAxis = dataBuffer.getShort(1) * kGsPerLSB;
184      data.YAxis = dataBuffer.getShort(3) * kGsPerLSB;
185      data.ZAxis = dataBuffer.getShort(5) * kGsPerLSB;
186    }
187    return data;
188  }
189
190  @Override
191  public String getSmartDashboardType() {
192    return "3AxisAccelerometer";
193  }
194
195  private ITable m_table;
196
197  @Override
198  public void initTable(ITable subtable) {
199    m_table = subtable;
200    updateTable();
201  }
202
203  @Override
204  public void updateTable() {
205    if (m_table != null) {
206      m_table.putNumber("X", getX());
207      m_table.putNumber("Y", getY());
208      m_table.putNumber("Z", getZ());
209    }
210  }
211
212  @Override
213  public ITable getTable() {
214    return m_table;
215  }
216
217  @Override
218  public void startLiveWindowMode() {
219  }
220
221  @Override
222  public void stopLiveWindowMode() {
223  }
224}