blob: eeacfce02e27a766c38581688348861ae055aa8f [file] [log] [blame]
#include "frc971/wpilib/gyro_interface.h"
#include <inttypes.h>
#include <chrono>
#include "aos/logging/logging.h"
#include "aos/time/time.h"
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
namespace frc971 {
namespace wpilib {
GyroInterface::GyroInterface() : gyro_(new frc::SPI(frc::SPI::kOnboardCS0)) {
// The gyro goes up to 8.08MHz.
// The myRIO goes up to 4MHz, so the roboRIO probably does too.
gyro_->SetClockRate(4e6);
gyro_->SetChipSelectActiveLow();
gyro_->SetClockActiveHigh();
gyro_->SetSampleDataOnRising();
gyro_->SetMSBFirst();
}
bool GyroInterface::InitializeGyro() {
uint32_t result;
if (!DoTransaction(0x20000003, &result)) {
AOS_LOG(WARNING, "failed to start a self-check\n");
return false;
}
if (result != 1) {
// We might have hit a parity error or something and are now retrying, so
// this isn't a very big deal.
AOS_LOG(INFO, "gyro unexpected initial response 0x%" PRIx32 "\n", result);
}
// Wait for it to assert the fault conditions before reading them.
::std::this_thread::sleep_for(::std::chrono::milliseconds(50));
if (!DoTransaction(0x20000000, &result)) {
AOS_LOG(WARNING, "failed to clear latched non-fault data\n");
return false;
}
AOS_LOG(DEBUG, "gyro dummy response is 0x%" PRIx32 "\n", result);
if (!DoTransaction(0x20000000, &result)) {
AOS_LOG(WARNING, "failed to read self-test data\n");
return false;
}
if (ExtractStatus(result) != 2) {
AOS_LOG(WARNING, "gyro first value 0x%" PRIx32 " not self-test data\n",
result);
return false;
}
if (ExtractErrors(result) != 0x7F) {
AOS_LOG(WARNING,
"gyro first value 0x%" PRIx32 " does not have all errors\n",
result);
return false;
}
if (!DoTransaction(0x20000000, &result)) {
AOS_LOG(WARNING, "failed to clear latched self-test data\n");
return false;
}
if (ExtractStatus(result) != 2) {
AOS_LOG(WARNING, "gyro second value 0x%" PRIx32 " not self-test data\n",
result);
return false;
}
return true;
}
bool GyroInterface::DoTransaction(uint32_t to_write, uint32_t *result) {
static const uint8_t kBytes = 4;
static_assert(kBytes == sizeof(to_write),
"need the same number of bytes as sizeof(the data)");
if (__builtin_parity(to_write & ~1) == 0) {
to_write |= 1;
} else {
to_write &= ~1;
}
uint8_t to_send[kBytes], to_receive[kBytes];
const uint32_t to_write_flipped = __builtin_bswap32(to_write);
memcpy(to_send, &to_write_flipped, kBytes);
switch (gyro_->Transaction(to_send, to_receive, kBytes)) {
case -1:
AOS_LOG(INFO, "SPI::Transaction failed\n");
return false;
case kBytes:
break;
default:
AOS_LOG(FATAL, "SPI::Transaction returned something weird\n");
}
memcpy(result, to_receive, kBytes);
if (__builtin_parity(*result & 0xFFFF) != 1) {
AOS_LOG(INFO, "high byte parity failure\n");
return false;
}
if (__builtin_parity(*result) != 1) {
AOS_LOG(INFO, "whole value parity failure\n");
return false;
}
*result = __builtin_bswap32(*result);
return true;
}
uint16_t GyroInterface::DoRead(uint8_t address) {
const uint32_t command = (0x8 << 28) | (address << 17);
uint32_t response;
while (true) {
if (!DoTransaction(command, &response)) {
AOS_LOG(WARNING, "reading 0x%" PRIx8 " failed\n", address);
continue;
}
if ((response & 0xEFE00000) != 0x4E000000) {
AOS_LOG(WARNING,
"gyro read from 0x%" PRIx8 " gave unexpected response 0x%" PRIx32
"\n",
address, response);
continue;
}
return (response >> 5) & 0xFFFF;
}
}
double GyroInterface::ExtractAngle(uint32_t value) {
const int16_t reading = -static_cast<int16_t>(value >> 10 & 0xFFFF);
return static_cast<double>(reading) * 2.0 * M_PI / 360.0 / 80.0;
}
uint32_t GyroInterface::ReadPartID() {
return (DoRead(0x0E) << 16) | DoRead(0x10);
}
uint32_t GyroInterface::GetReading() {
uint32_t result;
if (!DoTransaction(0x20000000, &result)) {
return 0;
}
return result;
}
} // namespace wpilib
} // namespace frc971