Austin Schuh | 812d0d1 | 2021-11-04 20:16:48 -0700 | [diff] [blame] | 1 | // Copyright (c) FIRST and other WPILib contributors. |
| 2 | // Open Source Software; you can modify and/or share it under the terms of |
| 3 | // the WPILib BSD license file in the root directory of this project. |
Brian Silverman | 8fce748 | 2020-01-05 13:18:21 -0800 | [diff] [blame] | 4 | |
| 5 | #include "frc/ADXL362.h" |
| 6 | |
| 7 | #include <hal/FRCUsageReporting.h> |
James Kuszmaul | cf32412 | 2023-01-14 14:07:17 -0800 | [diff] [blame^] | 8 | #include <networktables/DoubleTopic.h> |
Austin Schuh | 812d0d1 | 2021-11-04 20:16:48 -0700 | [diff] [blame] | 9 | #include <networktables/NTSendableBuilder.h> |
| 10 | #include <wpi/sendable/SendableRegistry.h> |
Brian Silverman | 8fce748 | 2020-01-05 13:18:21 -0800 | [diff] [blame] | 11 | |
Austin Schuh | 812d0d1 | 2021-11-04 20:16:48 -0700 | [diff] [blame] | 12 | #include "frc/Errors.h" |
Brian Silverman | 8fce748 | 2020-01-05 13:18:21 -0800 | [diff] [blame] | 13 | |
| 14 | using namespace frc; |
| 15 | |
| 16 | static constexpr int kRegWrite = 0x0A; |
| 17 | static constexpr int kRegRead = 0x0B; |
| 18 | |
| 19 | static constexpr int kPartIdRegister = 0x02; |
| 20 | static constexpr int kDataRegister = 0x0E; |
| 21 | static constexpr int kFilterCtlRegister = 0x2C; |
| 22 | static constexpr int kPowerCtlRegister = 0x2D; |
| 23 | |
| 24 | // static constexpr int kFilterCtl_Range2G = 0x00; |
| 25 | // static constexpr int kFilterCtl_Range4G = 0x40; |
| 26 | // static constexpr int kFilterCtl_Range8G = 0x80; |
| 27 | static constexpr int kFilterCtl_ODR_100Hz = 0x03; |
| 28 | |
| 29 | static constexpr int kPowerCtl_UltraLowNoise = 0x20; |
| 30 | // static constexpr int kPowerCtl_AutoSleep = 0x04; |
| 31 | static constexpr int kPowerCtl_Measure = 0x02; |
| 32 | |
| 33 | ADXL362::ADXL362(Range range) : ADXL362(SPI::Port::kOnboardCS1, range) {} |
| 34 | |
| 35 | ADXL362::ADXL362(SPI::Port port, Range range) |
Austin Schuh | 812d0d1 | 2021-11-04 20:16:48 -0700 | [diff] [blame] | 36 | : m_spi(port), m_simDevice("Accel:ADXL362", port) { |
Brian Silverman | 8fce748 | 2020-01-05 13:18:21 -0800 | [diff] [blame] | 37 | if (m_simDevice) { |
Austin Schuh | 812d0d1 | 2021-11-04 20:16:48 -0700 | [diff] [blame] | 38 | m_simRange = m_simDevice.CreateEnumDouble("range", hal::SimDevice::kOutput, |
| 39 | {"2G", "4G", "8G", "16G"}, |
| 40 | {2.0, 4.0, 8.0, 16.0}, 0); |
| 41 | m_simX = m_simDevice.CreateDouble("x", hal::SimDevice::kInput, 0.0); |
| 42 | m_simY = m_simDevice.CreateDouble("y", hal::SimDevice::kInput, 0.0); |
| 43 | m_simZ = m_simDevice.CreateDouble("z", hal::SimDevice::kInput, 0.0); |
Brian Silverman | 8fce748 | 2020-01-05 13:18:21 -0800 | [diff] [blame] | 44 | } |
| 45 | |
| 46 | m_spi.SetClockRate(3000000); |
James Kuszmaul | cf32412 | 2023-01-14 14:07:17 -0800 | [diff] [blame^] | 47 | m_spi.SetMode(frc::SPI::Mode::kMode3); |
Brian Silverman | 8fce748 | 2020-01-05 13:18:21 -0800 | [diff] [blame] | 48 | m_spi.SetChipSelectActiveLow(); |
| 49 | |
| 50 | uint8_t commands[3]; |
| 51 | if (!m_simDevice) { |
| 52 | // Validate the part ID |
| 53 | commands[0] = kRegRead; |
| 54 | commands[1] = kPartIdRegister; |
| 55 | commands[2] = 0; |
| 56 | m_spi.Transaction(commands, commands, 3); |
| 57 | if (commands[2] != 0xF2) { |
James Kuszmaul | cf32412 | 2023-01-14 14:07:17 -0800 | [diff] [blame^] | 58 | FRC_ReportError(err::Error, "could not find ADXL362"); |
Brian Silverman | 8fce748 | 2020-01-05 13:18:21 -0800 | [diff] [blame] | 59 | m_gsPerLSB = 0.0; |
| 60 | return; |
| 61 | } |
| 62 | } |
| 63 | |
| 64 | SetRange(range); |
| 65 | |
| 66 | // Turn on the measurements |
| 67 | commands[0] = kRegWrite; |
| 68 | commands[1] = kPowerCtlRegister; |
| 69 | commands[2] = kPowerCtl_Measure | kPowerCtl_UltraLowNoise; |
| 70 | m_spi.Write(commands, 3); |
| 71 | |
| 72 | HAL_Report(HALUsageReporting::kResourceType_ADXL362, port + 1); |
| 73 | |
Austin Schuh | 812d0d1 | 2021-11-04 20:16:48 -0700 | [diff] [blame] | 74 | wpi::SendableRegistry::AddLW(this, "ADXL362", port); |
| 75 | } |
| 76 | |
| 77 | SPI::Port ADXL362::GetSpiPort() const { |
| 78 | return m_spi.GetPort(); |
Brian Silverman | 8fce748 | 2020-01-05 13:18:21 -0800 | [diff] [blame] | 79 | } |
| 80 | |
| 81 | void ADXL362::SetRange(Range range) { |
Austin Schuh | 812d0d1 | 2021-11-04 20:16:48 -0700 | [diff] [blame] | 82 | if (m_gsPerLSB == 0.0) { |
| 83 | return; |
| 84 | } |
Brian Silverman | 8fce748 | 2020-01-05 13:18:21 -0800 | [diff] [blame] | 85 | |
| 86 | uint8_t commands[3]; |
| 87 | |
| 88 | switch (range) { |
| 89 | case kRange_2G: |
| 90 | m_gsPerLSB = 0.001; |
| 91 | break; |
| 92 | case kRange_4G: |
| 93 | m_gsPerLSB = 0.002; |
| 94 | break; |
| 95 | case kRange_8G: |
| 96 | case kRange_16G: // 16G not supported; treat as 8G |
| 97 | m_gsPerLSB = 0.004; |
| 98 | break; |
| 99 | } |
| 100 | |
| 101 | // Specify the data format to read |
| 102 | commands[0] = kRegWrite; |
| 103 | commands[1] = kFilterCtlRegister; |
| 104 | commands[2] = |
| 105 | kFilterCtl_ODR_100Hz | static_cast<uint8_t>((range & 0x03) << 6); |
| 106 | m_spi.Write(commands, 3); |
| 107 | |
Austin Schuh | 812d0d1 | 2021-11-04 20:16:48 -0700 | [diff] [blame] | 108 | if (m_simRange) { |
| 109 | m_simRange.Set(range); |
| 110 | } |
Brian Silverman | 8fce748 | 2020-01-05 13:18:21 -0800 | [diff] [blame] | 111 | } |
| 112 | |
Austin Schuh | 812d0d1 | 2021-11-04 20:16:48 -0700 | [diff] [blame] | 113 | double ADXL362::GetX() { |
| 114 | return GetAcceleration(kAxis_X); |
| 115 | } |
Brian Silverman | 8fce748 | 2020-01-05 13:18:21 -0800 | [diff] [blame] | 116 | |
Austin Schuh | 812d0d1 | 2021-11-04 20:16:48 -0700 | [diff] [blame] | 117 | double ADXL362::GetY() { |
| 118 | return GetAcceleration(kAxis_Y); |
| 119 | } |
Brian Silverman | 8fce748 | 2020-01-05 13:18:21 -0800 | [diff] [blame] | 120 | |
Austin Schuh | 812d0d1 | 2021-11-04 20:16:48 -0700 | [diff] [blame] | 121 | double ADXL362::GetZ() { |
| 122 | return GetAcceleration(kAxis_Z); |
| 123 | } |
Brian Silverman | 8fce748 | 2020-01-05 13:18:21 -0800 | [diff] [blame] | 124 | |
| 125 | double ADXL362::GetAcceleration(ADXL362::Axes axis) { |
Austin Schuh | 812d0d1 | 2021-11-04 20:16:48 -0700 | [diff] [blame] | 126 | if (m_gsPerLSB == 0.0) { |
| 127 | return 0.0; |
| 128 | } |
Brian Silverman | 8fce748 | 2020-01-05 13:18:21 -0800 | [diff] [blame] | 129 | |
Austin Schuh | 812d0d1 | 2021-11-04 20:16:48 -0700 | [diff] [blame] | 130 | if (axis == kAxis_X && m_simX) { |
| 131 | return m_simX.Get(); |
| 132 | } |
| 133 | if (axis == kAxis_Y && m_simY) { |
| 134 | return m_simY.Get(); |
| 135 | } |
| 136 | if (axis == kAxis_Z && m_simZ) { |
| 137 | return m_simZ.Get(); |
| 138 | } |
Brian Silverman | 8fce748 | 2020-01-05 13:18:21 -0800 | [diff] [blame] | 139 | |
| 140 | uint8_t buffer[4]; |
| 141 | uint8_t command[4] = {0, 0, 0, 0}; |
| 142 | command[0] = kRegRead; |
| 143 | command[1] = kDataRegister + static_cast<uint8_t>(axis); |
| 144 | m_spi.Transaction(command, buffer, 4); |
| 145 | |
| 146 | // Sensor is little endian... swap bytes |
| 147 | int16_t rawAccel = buffer[3] << 8 | buffer[2]; |
| 148 | return rawAccel * m_gsPerLSB; |
| 149 | } |
| 150 | |
| 151 | ADXL362::AllAxes ADXL362::GetAccelerations() { |
| 152 | AllAxes data; |
| 153 | if (m_gsPerLSB == 0.0) { |
| 154 | data.XAxis = data.YAxis = data.ZAxis = 0.0; |
| 155 | return data; |
| 156 | } |
| 157 | if (m_simX && m_simY && m_simZ) { |
| 158 | data.XAxis = m_simX.Get(); |
| 159 | data.YAxis = m_simY.Get(); |
| 160 | data.ZAxis = m_simZ.Get(); |
| 161 | return data; |
| 162 | } |
| 163 | |
| 164 | uint8_t dataBuffer[8] = {0, 0, 0, 0, 0, 0, 0, 0}; |
| 165 | int16_t rawData[3]; |
| 166 | |
| 167 | // Select the data address. |
| 168 | dataBuffer[0] = kRegRead; |
| 169 | dataBuffer[1] = kDataRegister; |
| 170 | m_spi.Transaction(dataBuffer, dataBuffer, 8); |
| 171 | |
| 172 | for (int i = 0; i < 3; i++) { |
| 173 | // Sensor is little endian... swap bytes |
| 174 | rawData[i] = dataBuffer[i * 2 + 3] << 8 | dataBuffer[i * 2 + 2]; |
| 175 | } |
| 176 | |
| 177 | data.XAxis = rawData[0] * m_gsPerLSB; |
| 178 | data.YAxis = rawData[1] * m_gsPerLSB; |
| 179 | data.ZAxis = rawData[2] * m_gsPerLSB; |
| 180 | |
| 181 | return data; |
| 182 | } |
| 183 | |
Austin Schuh | 812d0d1 | 2021-11-04 20:16:48 -0700 | [diff] [blame] | 184 | void ADXL362::InitSendable(nt::NTSendableBuilder& builder) { |
Brian Silverman | 8fce748 | 2020-01-05 13:18:21 -0800 | [diff] [blame] | 185 | builder.SetSmartDashboardType("3AxisAccelerometer"); |
James Kuszmaul | cf32412 | 2023-01-14 14:07:17 -0800 | [diff] [blame^] | 186 | builder.SetUpdateTable( |
| 187 | [this, x = nt::DoubleTopic{builder.GetTopic("X")}.Publish(), |
| 188 | y = nt::DoubleTopic{builder.GetTopic("Y")}.Publish(), |
| 189 | z = nt::DoubleTopic{builder.GetTopic("Z")}.Publish()]() mutable { |
| 190 | auto data = GetAccelerations(); |
| 191 | x.Set(data.XAxis); |
| 192 | y.Set(data.YAxis); |
| 193 | z.Set(data.ZAxis); |
| 194 | }); |
Brian Silverman | 8fce748 | 2020-01-05 13:18:21 -0800 | [diff] [blame] | 195 | } |