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}