001/*----------------------------------------------------------------------------*/ 002/* Copyright (c) 2008-2018 FIRST. 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.networktables.NetworkTableEntry; 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.smartdashboard.SendableBuilder; 018 019/** 020 * ADXL362 SPI Accelerometer. 021 * 022 * <p>This class allows access to an Analog Devices ADXL362 3-axis accelerometer. 023 */ 024@SuppressWarnings("PMD.UnusedPrivateField") 025public class ADXL362 extends SensorBase implements Accelerometer, Sendable { 026 private static final byte kRegWrite = 0x0A; 027 private static final byte kRegRead = 0x0B; 028 029 private static final byte kPartIdRegister = 0x02; 030 private static final byte kDataRegister = 0x0E; 031 private static final byte kFilterCtlRegister = 0x2C; 032 private static final byte kPowerCtlRegister = 0x2D; 033 034 private static final byte kFilterCtl_Range2G = 0x00; 035 private static final byte kFilterCtl_Range4G = 0x40; 036 private static final byte kFilterCtl_Range8G = (byte) 0x80; 037 private static final byte kFilterCtl_ODR_100Hz = 0x03; 038 039 private static final byte kPowerCtl_UltraLowNoise = 0x20; 040 private static final byte kPowerCtl_AutoSleep = 0x04; 041 private static final byte kPowerCtl_Measure = 0x02; 042 043 public enum Axes { 044 kX((byte) 0x00), 045 kY((byte) 0x02), 046 kZ((byte) 0x04); 047 048 @SuppressWarnings("MemberName") 049 public final byte value; 050 051 Axes(byte value) { 052 this.value = value; 053 } 054 } 055 056 @SuppressWarnings("MemberName") 057 public static class AllAxes { 058 public double XAxis; 059 public double YAxis; 060 public double ZAxis; 061 } 062 063 private SPI m_spi; 064 private double m_gsPerLSB; 065 066 /** 067 * Constructor. Uses the onboard CS1. 068 * 069 * @param range The range (+ or -) that the accelerometer will measure. 070 */ 071 public ADXL362(Range range) { 072 this(SPI.Port.kOnboardCS1, range); 073 } 074 075 /** 076 * Constructor. 077 * 078 * @param port The SPI port that the accelerometer is connected to 079 * @param range The range (+ or -) that the accelerometer will measure. 080 */ 081 public ADXL362(SPI.Port port, Range range) { 082 m_spi = new SPI(port); 083 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.allocate(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 setName("ADXL362", port.value); 112 } 113 114 @Override 115 public void free() { 116 super.free(); 117 if (m_spi != null) { 118 m_spi.free(); 119 m_spi = null; 120 } 121 } 122 123 @Override 124 public void setRange(Range range) { 125 if (m_spi == null) { 126 return; 127 } 128 129 final byte value; 130 switch (range) { 131 case k2G: 132 value = kFilterCtl_Range2G; 133 m_gsPerLSB = 0.001; 134 break; 135 case k4G: 136 value = kFilterCtl_Range4G; 137 m_gsPerLSB = 0.002; 138 break; 139 case k8G: 140 case k16G: // 16G not supported; treat as 8G 141 value = kFilterCtl_Range8G; 142 m_gsPerLSB = 0.004; 143 break; 144 default: 145 throw new IllegalArgumentException(range + " unsupported"); 146 147 } 148 149 // Specify the data format to read 150 byte[] commands = new byte[]{kRegWrite, kFilterCtlRegister, (byte) (kFilterCtl_ODR_100Hz 151 | value)}; 152 m_spi.write(commands, commands.length); 153 } 154 155 156 @Override 157 public double getX() { 158 return getAcceleration(Axes.kX); 159 } 160 161 @Override 162 public double getY() { 163 return getAcceleration(Axes.kY); 164 } 165 166 @Override 167 public double getZ() { 168 return getAcceleration(Axes.kZ); 169 } 170 171 /** 172 * Get the acceleration of one axis in Gs. 173 * 174 * @param axis The axis to read from. 175 * @return Acceleration of the ADXL362 in Gs. 176 */ 177 public double getAcceleration(ADXL362.Axes axis) { 178 if (m_spi == null) { 179 return 0.0; 180 } 181 ByteBuffer transferBuffer = ByteBuffer.allocate(4); 182 transferBuffer.put(0, kRegRead); 183 transferBuffer.put(1, (byte) (kDataRegister + axis.value)); 184 m_spi.transaction(transferBuffer, transferBuffer, 4); 185 // Sensor is little endian 186 transferBuffer.order(ByteOrder.LITTLE_ENDIAN); 187 188 return transferBuffer.getShort(2) * m_gsPerLSB; 189 } 190 191 /** 192 * Get the acceleration of all axes in Gs. 193 * 194 * @return An object containing the acceleration measured on each axis of the ADXL362 in Gs. 195 */ 196 public ADXL362.AllAxes getAccelerations() { 197 ADXL362.AllAxes data = new ADXL362.AllAxes(); 198 if (m_spi != null) { 199 ByteBuffer dataBuffer = ByteBuffer.allocate(8); 200 // Select the data address. 201 dataBuffer.put(0, kRegRead); 202 dataBuffer.put(1, kDataRegister); 203 m_spi.transaction(dataBuffer, dataBuffer, 8); 204 // Sensor is little endian... swap bytes 205 dataBuffer.order(ByteOrder.LITTLE_ENDIAN); 206 207 data.XAxis = dataBuffer.getShort(2) * m_gsPerLSB; 208 data.YAxis = dataBuffer.getShort(4) * m_gsPerLSB; 209 data.ZAxis = dataBuffer.getShort(6) * m_gsPerLSB; 210 } 211 return data; 212 } 213 214 @Override 215 public void initSendable(SendableBuilder builder) { 216 builder.setSmartDashboardType("3AxisAccelerometer"); 217 NetworkTableEntry entryX = builder.getEntry("X"); 218 NetworkTableEntry entryY = builder.getEntry("Y"); 219 NetworkTableEntry entryZ = builder.getEntry("Z"); 220 builder.setUpdateTable(() -> { 221 AllAxes data = getAccelerations(); 222 entryX.setDouble(data.XAxis); 223 entryY.setDouble(data.YAxis); 224 entryZ.setDouble(data.ZAxis); 225 }); 226 } 227}