mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-19 00:41:43 +00:00
This was not done to stdint.h for brevity in type names. Also removed "using namespace std;".
255 lines
6.6 KiB
C++
255 lines
6.6 KiB
C++
/*----------------------------------------------------------------------------*/
|
|
/* Copyright (c) FIRST 2016. All Rights Reserved. */
|
|
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
|
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
|
/* the project. */
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
#include "HAL/Accelerometer.h"
|
|
|
|
#include <cassert>
|
|
#include <cstdio>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include "ChipObject.h"
|
|
|
|
// The 7-bit I2C address with a 0 "send" bit
|
|
static const uint8_t kSendAddress = (0x1c << 1) | 0;
|
|
|
|
// The 7-bit I2C address with a 1 "receive" bit
|
|
static const uint8_t kReceiveAddress = (0x1c << 1) | 1;
|
|
|
|
static const uint8_t kControlTxRx = 1;
|
|
static const uint8_t kControlStart = 2;
|
|
static const uint8_t kControlStop = 4;
|
|
|
|
static tAccel* accel = 0;
|
|
static AccelerometerRange accelerometerRange;
|
|
|
|
// Register addresses
|
|
enum Register {
|
|
kReg_Status = 0x00,
|
|
kReg_OutXMSB = 0x01,
|
|
kReg_OutXLSB = 0x02,
|
|
kReg_OutYMSB = 0x03,
|
|
kReg_OutYLSB = 0x04,
|
|
kReg_OutZMSB = 0x05,
|
|
kReg_OutZLSB = 0x06,
|
|
kReg_Sysmod = 0x0B,
|
|
kReg_IntSource = 0x0C,
|
|
kReg_WhoAmI = 0x0D,
|
|
kReg_XYZDataCfg = 0x0E,
|
|
kReg_HPFilterCutoff = 0x0F,
|
|
kReg_PLStatus = 0x10,
|
|
kReg_PLCfg = 0x11,
|
|
kReg_PLCount = 0x12,
|
|
kReg_PLBfZcomp = 0x13,
|
|
kReg_PLThsReg = 0x14,
|
|
kReg_FFMtCfg = 0x15,
|
|
kReg_FFMtSrc = 0x16,
|
|
kReg_FFMtThs = 0x17,
|
|
kReg_FFMtCount = 0x18,
|
|
kReg_TransientCfg = 0x1D,
|
|
kReg_TransientSrc = 0x1E,
|
|
kReg_TransientThs = 0x1F,
|
|
kReg_TransientCount = 0x20,
|
|
kReg_PulseCfg = 0x21,
|
|
kReg_PulseSrc = 0x22,
|
|
kReg_PulseThsx = 0x23,
|
|
kReg_PulseThsy = 0x24,
|
|
kReg_PulseThsz = 0x25,
|
|
kReg_PulseTmlt = 0x26,
|
|
kReg_PulseLtcy = 0x27,
|
|
kReg_PulseWind = 0x28,
|
|
kReg_ASlpCount = 0x29,
|
|
kReg_CtrlReg1 = 0x2A,
|
|
kReg_CtrlReg2 = 0x2B,
|
|
kReg_CtrlReg3 = 0x2C,
|
|
kReg_CtrlReg4 = 0x2D,
|
|
kReg_CtrlReg5 = 0x2E,
|
|
kReg_OffX = 0x2F,
|
|
kReg_OffY = 0x30,
|
|
kReg_OffZ = 0x31
|
|
};
|
|
|
|
extern "C" uint32_t getFPGATime(int32_t* status);
|
|
|
|
static void writeRegister(Register reg, uint8_t data);
|
|
static uint8_t readRegister(Register reg);
|
|
|
|
/**
|
|
* Initialize the accelerometer.
|
|
*/
|
|
static void initializeAccelerometer() {
|
|
int32_t status;
|
|
|
|
if (!accel) {
|
|
accel = tAccel::create(&status);
|
|
|
|
// Enable I2C
|
|
accel->writeCNFG(1, &status);
|
|
|
|
// Set the counter to 100 kbps
|
|
accel->writeCNTR(213, &status);
|
|
|
|
// The device identification number should be 0x2a
|
|
assert(readRegister(kReg_WhoAmI) == 0x2a);
|
|
}
|
|
}
|
|
|
|
static void writeRegister(Register reg, uint8_t data) {
|
|
int32_t status = 0;
|
|
uint32_t initialTime;
|
|
|
|
accel->writeADDR(kSendAddress, &status);
|
|
|
|
// Send a start transmit/receive message with the register address
|
|
accel->writeCNTL(kControlStart | kControlTxRx, &status);
|
|
accel->writeDATO(reg, &status);
|
|
accel->strobeGO(&status);
|
|
|
|
// Execute and wait until it's done (up to a millisecond)
|
|
initialTime = getFPGATime(&status);
|
|
while (accel->readSTAT(&status) & 1) {
|
|
if (getFPGATime(&status) > initialTime + 1000) break;
|
|
}
|
|
|
|
// Send a stop transmit/receive message with the data
|
|
accel->writeCNTL(kControlStop | kControlTxRx, &status);
|
|
accel->writeDATO(data, &status);
|
|
accel->strobeGO(&status);
|
|
|
|
// Execute and wait until it's done (up to a millisecond)
|
|
initialTime = getFPGATime(&status);
|
|
while (accel->readSTAT(&status) & 1) {
|
|
if (getFPGATime(&status) > initialTime + 1000) break;
|
|
}
|
|
|
|
std::fflush(stdout);
|
|
}
|
|
|
|
static uint8_t readRegister(Register reg) {
|
|
int32_t status = 0;
|
|
uint32_t initialTime;
|
|
|
|
// Send a start transmit/receive message with the register address
|
|
accel->writeADDR(kSendAddress, &status);
|
|
accel->writeCNTL(kControlStart | kControlTxRx, &status);
|
|
accel->writeDATO(reg, &status);
|
|
accel->strobeGO(&status);
|
|
|
|
// Execute and wait until it's done (up to a millisecond)
|
|
initialTime = getFPGATime(&status);
|
|
while (accel->readSTAT(&status) & 1) {
|
|
if (getFPGATime(&status) > initialTime + 1000) break;
|
|
}
|
|
|
|
// Receive a message with the data and stop
|
|
accel->writeADDR(kReceiveAddress, &status);
|
|
accel->writeCNTL(kControlStart | kControlStop | kControlTxRx, &status);
|
|
accel->strobeGO(&status);
|
|
|
|
// Execute and wait until it's done (up to a millisecond)
|
|
initialTime = getFPGATime(&status);
|
|
while (accel->readSTAT(&status) & 1) {
|
|
if (getFPGATime(&status) > initialTime + 1000) break;
|
|
}
|
|
|
|
std::fflush(stdout);
|
|
|
|
return accel->readDATI(&status);
|
|
}
|
|
|
|
/**
|
|
* Convert a 12-bit raw acceleration value into a scaled double in units of
|
|
* 1 g-force, taking into account the accelerometer range.
|
|
*/
|
|
static double unpackAxis(int16_t raw) {
|
|
// The raw value is actually 12 bits, not 16, so we need to propogate the
|
|
// 2's complement sign bit to the unused 4 bits for this to work with
|
|
// negative numbers.
|
|
raw <<= 4;
|
|
raw >>= 4;
|
|
|
|
switch (accelerometerRange) {
|
|
case kRange_2G:
|
|
return raw / 1024.0;
|
|
case kRange_4G:
|
|
return raw / 512.0;
|
|
case kRange_8G:
|
|
return raw / 256.0;
|
|
default:
|
|
return 0.0;
|
|
}
|
|
}
|
|
|
|
extern "C" {
|
|
|
|
/**
|
|
* Set the accelerometer to active or standby mode. It must be in standby
|
|
* mode to change any configuration.
|
|
*/
|
|
void setAccelerometerActive(bool active) {
|
|
initializeAccelerometer();
|
|
|
|
uint8_t ctrlReg1 = readRegister(kReg_CtrlReg1);
|
|
ctrlReg1 &= ~1; // Clear the existing active bit
|
|
writeRegister(kReg_CtrlReg1, ctrlReg1 | (active ? 1 : 0));
|
|
}
|
|
|
|
/**
|
|
* Set the range of values that can be measured (either 2, 4, or 8 g-forces).
|
|
* The accelerometer should be in standby mode when this is called.
|
|
*/
|
|
void setAccelerometerRange(AccelerometerRange range) {
|
|
initializeAccelerometer();
|
|
|
|
accelerometerRange = range;
|
|
|
|
uint8_t xyzDataCfg = readRegister(kReg_XYZDataCfg);
|
|
xyzDataCfg &= ~3; // Clear the existing two range bits
|
|
writeRegister(kReg_XYZDataCfg, xyzDataCfg | range);
|
|
}
|
|
|
|
/**
|
|
* Get the x-axis acceleration
|
|
*
|
|
* This is a floating point value in units of 1 g-force
|
|
*/
|
|
double getAccelerometerX() {
|
|
initializeAccelerometer();
|
|
|
|
int raw =
|
|
(readRegister(kReg_OutXMSB) << 4) | (readRegister(kReg_OutXLSB) >> 4);
|
|
return unpackAxis(raw);
|
|
}
|
|
|
|
/**
|
|
* Get the y-axis acceleration
|
|
*
|
|
* This is a floating point value in units of 1 g-force
|
|
*/
|
|
double getAccelerometerY() {
|
|
initializeAccelerometer();
|
|
|
|
int raw =
|
|
(readRegister(kReg_OutYMSB) << 4) | (readRegister(kReg_OutYLSB) >> 4);
|
|
return unpackAxis(raw);
|
|
}
|
|
|
|
/**
|
|
* Get the z-axis acceleration
|
|
*
|
|
* This is a floating point value in units of 1 g-force
|
|
*/
|
|
double getAccelerometerZ() {
|
|
initializeAccelerometer();
|
|
|
|
int raw =
|
|
(readRegister(kReg_OutZMSB) << 4) | (readRegister(kReg_OutZLSB) >> 4);
|
|
return unpackAxis(raw);
|
|
}
|
|
|
|
} // extern "C"
|