Files
allwpilib/wpilibc/src/main/native/cpp/ADXL362.cpp
Thad House 17f504f548 [hal,wpilib] Fix SPI Mode Setting (#4434)
SPI Mode setting was very broken. MSB and LSB sets did not work (MSB is the only one supported)
and if LSB was set (which was the default) the ioct to set clock phase would fail. This
deprecates all the individual functions, the LSB/MSB functions, and adds an SPI mode selection
function. This is usually more understandable, and shows up in a lot more documentation
2022-09-24 00:11:37 -07:00

195 lines
5.1 KiB
C++

// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include "frc/ADXL362.h"
#include <hal/FRCUsageReporting.h>
#include <networktables/NTSendableBuilder.h>
#include <wpi/sendable/SendableRegistry.h>
#include "frc/Errors.h"
using namespace frc;
static constexpr int kRegWrite = 0x0A;
static constexpr int kRegRead = 0x0B;
static constexpr int kPartIdRegister = 0x02;
static constexpr int kDataRegister = 0x0E;
static constexpr int kFilterCtlRegister = 0x2C;
static constexpr int kPowerCtlRegister = 0x2D;
// static constexpr int kFilterCtl_Range2G = 0x00;
// static constexpr int kFilterCtl_Range4G = 0x40;
// static constexpr int kFilterCtl_Range8G = 0x80;
static constexpr int kFilterCtl_ODR_100Hz = 0x03;
static constexpr int kPowerCtl_UltraLowNoise = 0x20;
// static constexpr int kPowerCtl_AutoSleep = 0x04;
static constexpr int kPowerCtl_Measure = 0x02;
ADXL362::ADXL362(Range range) : ADXL362(SPI::Port::kOnboardCS1, range) {}
ADXL362::ADXL362(SPI::Port port, Range range)
: m_spi(port), m_simDevice("Accel:ADXL362", port) {
if (m_simDevice) {
m_simRange = m_simDevice.CreateEnumDouble("range", hal::SimDevice::kOutput,
{"2G", "4G", "8G", "16G"},
{2.0, 4.0, 8.0, 16.0}, 0);
m_simX = m_simDevice.CreateDouble("x", hal::SimDevice::kInput, 0.0);
m_simY = m_simDevice.CreateDouble("y", hal::SimDevice::kInput, 0.0);
m_simZ = m_simDevice.CreateDouble("z", hal::SimDevice::kInput, 0.0);
}
m_spi.SetClockRate(3000000);
m_spi.SetMode(frc::SPI::Mode::kMode3);
m_spi.SetChipSelectActiveLow();
uint8_t commands[3];
if (!m_simDevice) {
// Validate the part ID
commands[0] = kRegRead;
commands[1] = kPartIdRegister;
commands[2] = 0;
m_spi.Transaction(commands, commands, 3);
if (commands[2] != 0xF2) {
FRC_ReportError(err::Error, "{}", "could not find ADXL362");
m_gsPerLSB = 0.0;
return;
}
}
SetRange(range);
// Turn on the measurements
commands[0] = kRegWrite;
commands[1] = kPowerCtlRegister;
commands[2] = kPowerCtl_Measure | kPowerCtl_UltraLowNoise;
m_spi.Write(commands, 3);
HAL_Report(HALUsageReporting::kResourceType_ADXL362, port + 1);
wpi::SendableRegistry::AddLW(this, "ADXL362", port);
}
SPI::Port ADXL362::GetSpiPort() const {
return m_spi.GetPort();
}
void ADXL362::SetRange(Range range) {
if (m_gsPerLSB == 0.0) {
return;
}
uint8_t commands[3];
switch (range) {
case kRange_2G:
m_gsPerLSB = 0.001;
break;
case kRange_4G:
m_gsPerLSB = 0.002;
break;
case kRange_8G:
case kRange_16G: // 16G not supported; treat as 8G
m_gsPerLSB = 0.004;
break;
}
// Specify the data format to read
commands[0] = kRegWrite;
commands[1] = kFilterCtlRegister;
commands[2] =
kFilterCtl_ODR_100Hz | static_cast<uint8_t>((range & 0x03) << 6);
m_spi.Write(commands, 3);
if (m_simRange) {
m_simRange.Set(range);
}
}
double ADXL362::GetX() {
return GetAcceleration(kAxis_X);
}
double ADXL362::GetY() {
return GetAcceleration(kAxis_Y);
}
double ADXL362::GetZ() {
return GetAcceleration(kAxis_Z);
}
double ADXL362::GetAcceleration(ADXL362::Axes axis) {
if (m_gsPerLSB == 0.0) {
return 0.0;
}
if (axis == kAxis_X && m_simX) {
return m_simX.Get();
}
if (axis == kAxis_Y && m_simY) {
return m_simY.Get();
}
if (axis == kAxis_Z && m_simZ) {
return m_simZ.Get();
}
uint8_t buffer[4];
uint8_t command[4] = {0, 0, 0, 0};
command[0] = kRegRead;
command[1] = kDataRegister + static_cast<uint8_t>(axis);
m_spi.Transaction(command, buffer, 4);
// Sensor is little endian... swap bytes
int16_t rawAccel = buffer[3] << 8 | buffer[2];
return rawAccel * m_gsPerLSB;
}
ADXL362::AllAxes ADXL362::GetAccelerations() {
AllAxes data;
if (m_gsPerLSB == 0.0) {
data.XAxis = data.YAxis = data.ZAxis = 0.0;
return data;
}
if (m_simX && m_simY && m_simZ) {
data.XAxis = m_simX.Get();
data.YAxis = m_simY.Get();
data.ZAxis = m_simZ.Get();
return data;
}
uint8_t dataBuffer[8] = {0, 0, 0, 0, 0, 0, 0, 0};
int16_t rawData[3];
// Select the data address.
dataBuffer[0] = kRegRead;
dataBuffer[1] = kDataRegister;
m_spi.Transaction(dataBuffer, dataBuffer, 8);
for (int i = 0; i < 3; i++) {
// Sensor is little endian... swap bytes
rawData[i] = dataBuffer[i * 2 + 3] << 8 | dataBuffer[i * 2 + 2];
}
data.XAxis = rawData[0] * m_gsPerLSB;
data.YAxis = rawData[1] * m_gsPerLSB;
data.ZAxis = rawData[2] * m_gsPerLSB;
return data;
}
void ADXL362::InitSendable(nt::NTSendableBuilder& builder) {
builder.SetSmartDashboardType("3AxisAccelerometer");
auto x = builder.GetEntry("X").GetHandle();
auto y = builder.GetEntry("Y").GetHandle();
auto z = builder.GetEntry("Z").GetHandle();
builder.SetUpdateTable([=] {
auto data = GetAccelerations();
nt::NetworkTableEntry(x).SetDouble(data.XAxis);
nt::NetworkTableEntry(y).SetDouble(data.YAxis);
nt::NetworkTableEntry(z).SetDouble(data.ZAxis);
});
}