Brian Silverman | 26e4e52 | 2015-12-17 01:56:40 -0500 | [diff] [blame] | 1 | /*----------------------------------------------------------------------------*/ |
Brian Silverman | 1a67511 | 2016-02-20 20:42:49 -0500 | [diff] [blame^] | 2 | /* Copyright (c) FIRST 2008-2016. All Rights Reserved. */ |
Brian Silverman | 26e4e52 | 2015-12-17 01:56:40 -0500 | [diff] [blame] | 3 | /* Open Source Software - may be modified and shared by FRC teams. The code */ |
Brian Silverman | 1a67511 | 2016-02-20 20:42:49 -0500 | [diff] [blame^] | 4 | /* must be accompanied by the FIRST BSD license file in the root directory of */ |
| 5 | /* the project. */ |
Brian Silverman | 26e4e52 | 2015-12-17 01:56:40 -0500 | [diff] [blame] | 6 | /*----------------------------------------------------------------------------*/ |
| 7 | |
| 8 | #include "ADXL362.h" |
| 9 | #include "DigitalInput.h" |
| 10 | #include "DigitalOutput.h" |
| 11 | #include "DriverStation.h" |
| 12 | #include "LiveWindow/LiveWindow.h" |
| 13 | |
| 14 | static uint8_t kRegWrite = 0x0A; |
| 15 | static uint8_t kRegRead = 0x0B; |
| 16 | |
| 17 | static uint8_t kPartIdRegister = 0x02; |
| 18 | static uint8_t kDataRegister = 0x0E; |
| 19 | static uint8_t kFilterCtlRegister = 0x2C; |
| 20 | static uint8_t kPowerCtlRegister = 0x2D; |
| 21 | |
| 22 | //static uint8_t kFilterCtl_Range2G = 0x00; |
| 23 | //static uint8_t kFilterCtl_Range4G = 0x40; |
| 24 | //static uint8_t kFilterCtl_Range8G = 0x80; |
| 25 | static uint8_t kFilterCtl_ODR_100Hz = 0x03; |
| 26 | |
| 27 | static uint8_t kPowerCtl_UltraLowNoise = 0x20; |
| 28 | //static uint8_t kPowerCtl_AutoSleep = 0x04; |
| 29 | static uint8_t kPowerCtl_Measure = 0x02; |
| 30 | |
| 31 | /** |
| 32 | * Constructor. Uses the onboard CS1. |
| 33 | * |
| 34 | * @param range The range (+ or -) that the accelerometer will measure. |
| 35 | */ |
| 36 | ADXL362::ADXL362(Range range) : ADXL362(SPI::Port::kOnboardCS1, range) {} |
| 37 | |
| 38 | /** |
| 39 | * Constructor. |
| 40 | * |
| 41 | * @param port The SPI port the accelerometer is attached to |
| 42 | * @param range The range (+ or -) that the accelerometer will measure. |
| 43 | */ |
| 44 | ADXL362::ADXL362(SPI::Port port, Range range) : m_spi(port) { |
| 45 | m_spi.SetClockRate(3000000); |
| 46 | m_spi.SetMSBFirst(); |
| 47 | m_spi.SetSampleDataOnFalling(); |
| 48 | m_spi.SetClockActiveLow(); |
| 49 | m_spi.SetChipSelectActiveLow(); |
| 50 | |
| 51 | // Validate the part ID |
| 52 | uint8_t commands[3]; |
| 53 | commands[0] = kRegRead; |
| 54 | commands[1] = kPartIdRegister; |
| 55 | commands[2] = 0; |
| 56 | m_spi.Transaction(commands, commands, 3); |
| 57 | if (commands[2] != 0xF2) { |
| 58 | DriverStation::ReportError("could not find ADXL362"); |
| 59 | m_gsPerLSB = 0.0; |
| 60 | return; |
| 61 | } |
| 62 | |
| 63 | SetRange(range); |
| 64 | |
| 65 | // Turn on the measurements |
| 66 | commands[0] = kRegWrite; |
| 67 | commands[1] = kPowerCtlRegister; |
| 68 | commands[2] = kPowerCtl_Measure | kPowerCtl_UltraLowNoise; |
| 69 | m_spi.Write(commands, 3); |
| 70 | |
| 71 | HALReport(HALUsageReporting::kResourceType_ADXL362, port); |
| 72 | |
| 73 | LiveWindow::GetInstance()->AddSensor("ADXL362", port, this); |
| 74 | } |
| 75 | |
| 76 | /** {@inheritdoc} */ |
| 77 | void ADXL362::SetRange(Range range) { |
| 78 | if (m_gsPerLSB == 0.0) return; |
| 79 | |
| 80 | uint8_t commands[3]; |
| 81 | |
| 82 | switch (range) { |
| 83 | case kRange_2G: |
| 84 | m_gsPerLSB = 0.001; |
| 85 | break; |
| 86 | case kRange_4G: |
| 87 | m_gsPerLSB = 0.002; |
| 88 | break; |
| 89 | case kRange_8G: |
| 90 | case kRange_16G: // 16G not supported; treat as 8G |
| 91 | m_gsPerLSB = 0.004; |
| 92 | break; |
| 93 | } |
| 94 | |
| 95 | // Specify the data format to read |
| 96 | commands[0] = kRegWrite; |
| 97 | commands[1] = kFilterCtlRegister; |
| 98 | commands[2] = kFilterCtl_ODR_100Hz | (uint8_t)((range & 0x03) << 6); |
| 99 | m_spi.Write(commands, 3); |
| 100 | } |
| 101 | |
| 102 | /** {@inheritdoc} */ |
| 103 | double ADXL362::GetX() { return GetAcceleration(kAxis_X); } |
| 104 | |
| 105 | /** {@inheritdoc} */ |
| 106 | double ADXL362::GetY() { return GetAcceleration(kAxis_Y); } |
| 107 | |
| 108 | /** {@inheritdoc} */ |
| 109 | double ADXL362::GetZ() { return GetAcceleration(kAxis_Z); } |
| 110 | |
| 111 | /** |
| 112 | * Get the acceleration of one axis in Gs. |
| 113 | * |
| 114 | * @param axis The axis to read from. |
| 115 | * @return Acceleration of the ADXL362 in Gs. |
| 116 | */ |
| 117 | double ADXL362::GetAcceleration(ADXL362::Axes axis) { |
| 118 | if (m_gsPerLSB == 0.0) return 0.0; |
| 119 | |
| 120 | uint8_t buffer[4]; |
| 121 | uint8_t command[4] = {0, 0, 0, 0}; |
| 122 | command[0] = kRegRead; |
| 123 | command[1] = kDataRegister + (uint8_t)axis; |
| 124 | m_spi.Transaction(command, buffer, 4); |
| 125 | |
| 126 | // Sensor is little endian... swap bytes |
| 127 | int16_t rawAccel = buffer[3] << 8 | buffer[2]; |
| 128 | return rawAccel * m_gsPerLSB; |
| 129 | } |
| 130 | |
| 131 | /** |
| 132 | * Get the acceleration of all axes in Gs. |
| 133 | * |
| 134 | * @return An object containing the acceleration measured on each axis of the |
| 135 | * ADXL362 in Gs. |
| 136 | */ |
| 137 | ADXL362::AllAxes ADXL362::GetAccelerations() { |
| 138 | AllAxes data = AllAxes(); |
| 139 | if (m_gsPerLSB == 0.0) { |
| 140 | data.XAxis = data.YAxis = data.ZAxis = 0.0; |
| 141 | return data; |
| 142 | } |
| 143 | |
| 144 | uint8_t dataBuffer[8] = {0, 0, 0, 0, 0, 0, 0, 0}; |
| 145 | int16_t rawData[3]; |
| 146 | |
| 147 | // Select the data address. |
| 148 | dataBuffer[0] = kRegRead; |
| 149 | dataBuffer[1] = kDataRegister; |
| 150 | m_spi.Transaction(dataBuffer, dataBuffer, 8); |
| 151 | |
| 152 | for (int32_t i = 0; i < 3; i++) { |
| 153 | // Sensor is little endian... swap bytes |
| 154 | rawData[i] = dataBuffer[i * 2 + 3] << 8 | dataBuffer[i * 2 + 2]; |
| 155 | } |
| 156 | |
| 157 | data.XAxis = rawData[0] * m_gsPerLSB; |
| 158 | data.YAxis = rawData[1] * m_gsPerLSB; |
| 159 | data.ZAxis = rawData[2] * m_gsPerLSB; |
| 160 | |
| 161 | return data; |
| 162 | } |
| 163 | |
| 164 | std::string ADXL362::GetSmartDashboardType() const { |
| 165 | return "3AxisAccelerometer"; |
| 166 | } |
| 167 | |
| 168 | void ADXL362::InitTable(std::shared_ptr<ITable> subtable) { |
| 169 | m_table = subtable; |
| 170 | UpdateTable(); |
| 171 | } |
| 172 | |
| 173 | void ADXL362::UpdateTable() { |
| 174 | if (m_table != nullptr) { |
| 175 | m_table->PutNumber("X", GetX()); |
| 176 | m_table->PutNumber("Y", GetY()); |
| 177 | m_table->PutNumber("Z", GetZ()); |
| 178 | } |
| 179 | } |
| 180 | |
| 181 | std::shared_ptr<ITable> ADXL362::GetTable() const { return m_table; } |