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.tInstances; 015import edu.wpi.first.wpilibj.hal.FRCNetComm.tResourceType; 016import edu.wpi.first.wpilibj.hal.HAL; 017import edu.wpi.first.wpilibj.interfaces.Accelerometer; 018import edu.wpi.first.wpilibj.smartdashboard.SendableBuilder; 019 020/** 021 * ADXL345 SPI Accelerometer. 022 */ 023@SuppressWarnings({"TypeName", "PMD.UnusedPrivateField"}) 024public class ADXL345_SPI extends SensorBase implements Accelerometer, Sendable { 025 private static final int kPowerCtlRegister = 0x2D; 026 private static final int kDataFormatRegister = 0x31; 027 private static final int kDataRegister = 0x32; 028 private static final double kGsPerLSB = 0.00390625; 029 030 private static final int kAddress_Read = 0x80; 031 private static final int kAddress_MultiByte = 0x40; 032 033 private static final int kPowerCtl_Link = 0x20; 034 private static final int kPowerCtl_AutoSleep = 0x10; 035 private static final int kPowerCtl_Measure = 0x08; 036 private static final int kPowerCtl_Sleep = 0x04; 037 038 private static final int kDataFormat_SelfTest = 0x80; 039 private static final int kDataFormat_SPI = 0x40; 040 private static final int kDataFormat_IntInvert = 0x20; 041 private static final int kDataFormat_FullRes = 0x08; 042 private static final int kDataFormat_Justify = 0x04; 043 044 public enum Axes { 045 kX((byte) 0x00), 046 kY((byte) 0x02), 047 kZ((byte) 0x04); 048 049 /** 050 * The integer value representing this enumeration. 051 */ 052 @SuppressWarnings("MemberName") 053 public final byte value; 054 055 Axes(byte value) { 056 this.value = value; 057 } 058 } 059 060 @SuppressWarnings("MemberName") 061 public static class AllAxes { 062 public double XAxis; 063 public double YAxis; 064 public double ZAxis; 065 } 066 067 protected SPI m_spi; 068 069 /** 070 * Constructor. 071 * 072 * @param port The SPI port that the accelerometer is connected to 073 * @param range The range (+ or -) that the accelerometer will measure. 074 */ 075 public ADXL345_SPI(SPI.Port port, Range range) { 076 m_spi = new SPI(port); 077 init(range); 078 setName("ADXL345_SPI", port.value); 079 } 080 081 @Override 082 public void free() { 083 super.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.allocate(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.allocate(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 void initSendable(SendableBuilder builder) { 192 builder.setSmartDashboardType("3AxisAccelerometer"); 193 NetworkTableEntry entryX = builder.getEntry("X"); 194 NetworkTableEntry entryY = builder.getEntry("Y"); 195 NetworkTableEntry entryZ = builder.getEntry("Z"); 196 builder.setUpdateTable(() -> { 197 AllAxes data = getAccelerations(); 198 entryX.setDouble(data.XAxis); 199 entryY.setDouble(data.YAxis); 200 entryZ.setDouble(data.ZAxis); 201 }); 202 } 203}