blob: 151f3e05d7b1c3d23409b5c2a2fb2f6228fdd20e [file] [log] [blame]
Brian Silverman7be68ba2020-01-08 22:08:40 -08001#include "frc971/wpilib/ADIS16470.h"
2
Tyler Chatowbf0609c2021-07-31 16:13:27 -07003#include <cinttypes>
Brian Silverman7be68ba2020-01-08 22:08:40 -08004
Austin Schuhac17fba2020-03-28 15:55:33 -07005#include "aos/containers/sized_array.h"
Brian Silverman7be68ba2020-01-08 22:08:40 -08006#include "aos/time/time.h"
Tyler Chatowbf0609c2021-07-31 16:13:27 -07007#include "glog/logging.h"
Brian Silverman7be68ba2020-01-08 22:08:40 -08008#include "hal/HAL.h"
9
10namespace frc971 {
11namespace wpilib {
12namespace {
Austin Schuhb51692d2021-04-10 15:27:25 -070013namespace chrono = std::chrono;
Brian Silverman7be68ba2020-01-08 22:08:40 -080014namespace registers {
15
16// Flash memory write count
17constexpr uint8_t FLASH_CNT = 0x00;
18// Diagnostic and operational status
19constexpr uint8_t DIAG_STAT = 0x02;
20// X-axis gyroscope output, lower word
21constexpr uint8_t X_GYRO_LOW = 0x04;
22// X-axis gyroscope output, upper word
23constexpr uint8_t X_GYRO_OUT = 0x06;
24// Y-axis gyroscope output, lower word
25constexpr uint8_t Y_GYRO_LOW = 0x08;
26// Y-axis gyroscope output, upper word
27constexpr uint8_t Y_GYRO_OUT = 0x0A;
28// Z-axis gyroscope output, lower word
29constexpr uint8_t Z_GYRO_LOW = 0x0C;
30// Z-axis gyroscope output, upper word
31constexpr uint8_t Z_GYRO_OUT = 0x0E;
32// X-axis accelerometer output, lower word
33constexpr uint8_t X_ACCL_LOW = 0x10;
34// X-axis accelerometer output, upper word
35constexpr uint8_t X_ACCL_OUT = 0x12;
36// Y-axis accelerometer output, lower word
37constexpr uint8_t Y_ACCL_OUT = 0x16;
38// Y-axis accelerometer output, upper word
39constexpr uint8_t Z_ACCL_LOW = 0x18;
40// Z-axis accelerometer output, lower word
41constexpr uint8_t Z_ACCL_OUT = 0x1A;
42// Z-axis accelerometer output, upper word
43constexpr uint8_t TEMP_OUT = 0x1C;
44// Temperature output (internal, not calibrated)
45constexpr uint8_t TIME_STAMP = 0x1E;
46// PPS mode time stamp
47constexpr uint8_t X_DELTANG_LOW = 0x24;
48// X-axis delta angle output, lower word
49constexpr uint8_t X_DELTANG_OUT = 0x26;
50// X-axis delta angle output, upper word
51constexpr uint8_t Y_DELTANG_LOW = 0x28;
52// Y-axis delta angle output, lower word
53constexpr uint8_t Y_DELTANG_OUT = 0x2A;
54// Y-axis delta angle output, upper word
55constexpr uint8_t Z_DELTANG_LOW = 0x2C;
56// Z-axis delta angle output, lower word
57constexpr uint8_t Z_DELTANG_OUT = 0x2E;
58// Z-axis delta angle output, upper word
59constexpr uint8_t X_DELTVEL_LOW = 0x30;
60// X-axis delta velocity output, lower word
61constexpr uint8_t X_DELTVEL_OUT = 0x32;
62// X-axis delta velocity output, upper word
63constexpr uint8_t Y_DELTVEL_LOW = 0x34;
64// Y-axis delta velocity output, lower word
65constexpr uint8_t Y_DELTVEL_OUT = 0x36;
66// Y-axis delta velocity output, upper word
67constexpr uint8_t Z_DELTVEL_LOW = 0x38;
68// Z-axis delta velocity output, lower word
69constexpr uint8_t Z_DELTVEL_OUT = 0x3A;
70// Z-axis delta velocity output, upper word
71constexpr uint8_t XG_BIAS_LOW = 0x40;
72// X-axis gyroscope bias offset correction, lower word
73constexpr uint8_t XG_BIAS_HIGH = 0x42;
74// X-axis gyroscope bias offset correction, upper word
75constexpr uint8_t YG_BIAS_LOW = 0x44;
76// Y-axis gyroscope bias offset correction, lower word
77constexpr uint8_t YG_BIAS_HIGH = 0x46;
78// Y-axis gyroscope bias offset correction, upper word
79constexpr uint8_t ZG_BIAS_LOW = 0x48;
80// Z-axis gyroscope bias offset correction, lower word
81constexpr uint8_t ZG_BIAS_HIGH = 0x4A;
82// Z-axis gyroscope bias offset correction, upper word
83constexpr uint8_t XA_BIAS_LOW = 0x4C;
84// X-axis accelerometer bias offset correction, lower word
85constexpr uint8_t XA_BIAS_HIGH = 0x4E;
86// X-axis accelerometer bias offset correction, upper word
87constexpr uint8_t YA_BIAS_LOW = 0x50;
88// Y-axis accelerometer bias offset correction, lower word
89constexpr uint8_t YA_BIAS_HIGH = 0x52;
90// Y-axis accelerometer bias offset correction, upper word
91constexpr uint8_t ZA_BIAS_LOW = 0x54;
92// Z-axis accelerometer bias offset correction, lower word
93constexpr uint8_t ZA_BIAS_HIGH = 0x56;
94// Z-axis accelerometer bias offset correction, upper word
95constexpr uint8_t FILT_CTRL = 0x5C;
96// Filter control
97constexpr uint8_t MSC_CTRL = 0x60;
98// Miscellaneous control
99constexpr uint8_t UP_SCALE = 0x62;
100// Clock scale factor, PPS mode
101constexpr uint8_t DEC_RATE = 0x64;
102// Decimation rate control (output data rate)
103constexpr uint8_t NULL_CNFG = 0x66;
104// Auto-null configuration control
105constexpr uint8_t GLOB_CMD = 0x68;
106// Global commands
107constexpr uint8_t FIRM_REV = 0x6C;
108// Firmware revision
109constexpr uint8_t FIRM_DM = 0x6E;
110// Firmware revision date, month and day
111constexpr uint8_t FIRM_Y = 0x70;
112// Firmware revision date, year
113constexpr uint8_t PROD_ID = 0x72;
114// Product identification
115constexpr uint8_t SERIAL_NUM = 0x74;
116// Serial number (relative to assembly lot)
117constexpr uint8_t USER_SCR1 = 0x76;
118// User scratch register 1
119constexpr uint8_t USER_SCR2 = 0x78;
120// User scratch register 2
121constexpr uint8_t USER_SCR3 = 0x7A;
122// User scratch register 3
123constexpr uint8_t FLSHCNT_LOW = 0x7C;
124// Flash update count, lower word
125constexpr uint8_t FLSHCNT_HIGH = 0x7E;
126// Flash update count, upper word
127constexpr uint8_t Y_ACCL_LOW = 0x14;
128
129} // namespace registers
130
131// The complete automatic packet we will send. This needs to include the dummy 0
132// bytes making up full 16-bit frames.
133// Note that in addition to the 24-byte limit from the FPGA, this is also
134// limited to 12 16-bit register reads by the IMU itself given that we're
135// reading at the full 2kHz rate.
136// We rotate the registers here by 1, such that the first thing we read is the
137// last thing triggered by the previous reading. We put DIAG_STAT in this
138// position because we don't care if it's one cycle stale.
139constexpr uint8_t kAutospiPacket[] = {
140 // X
Tyler Chatowbf0609c2021-07-31 16:13:27 -0700141 registers::X_GYRO_OUT,
142 0,
143 registers::X_ACCL_OUT,
144 0,
145 registers::X_ACCL_LOW,
146 0,
Brian Silverman7be68ba2020-01-08 22:08:40 -0800147 // Y
Tyler Chatowbf0609c2021-07-31 16:13:27 -0700148 registers::Y_GYRO_OUT,
149 0,
150 registers::Y_ACCL_OUT,
151 0,
152 registers::Y_ACCL_LOW,
153 0,
Brian Silverman7be68ba2020-01-08 22:08:40 -0800154 // Z
Tyler Chatowbf0609c2021-07-31 16:13:27 -0700155 registers::Z_GYRO_OUT,
156 0,
157 registers::Z_ACCL_OUT,
158 0,
159 registers::Z_ACCL_LOW,
160 0,
Brian Silverman7be68ba2020-01-08 22:08:40 -0800161 // Other
Tyler Chatowbf0609c2021-07-31 16:13:27 -0700162 registers::TEMP_OUT,
163 0,
164 registers::DIAG_STAT,
165 0,
Brian Silverman7be68ba2020-01-08 22:08:40 -0800166};
167// clang-format on
168
169static_assert((sizeof(kAutospiPacket) % 2) == 0,
170 "Need a whole number of register reads");
171
Tyler Chatowbf0609c2021-07-31 16:13:27 -0700172static constexpr size_t kAutospiDataSize =
173 sizeof(kAutospiPacket) + 1 /* timestamp */;
Brian Silverman7be68ba2020-01-08 22:08:40 -0800174
175// radian/second/LSB for the gyros (for just the 16-bit value).
176constexpr double kGyroLsbRadianSecond =
177 1.0 / 10.0 * (2.0 * M_PI / 360.0) /* degrees -> radians */;
178// G/LSB for the accelerometers (for the full 32-bit value).
James Kuszmaulb145b322020-01-22 22:31:52 -0800179constexpr double kAccelerometerLsbG = 1.0 / 52'428'800.0;
Brian Silverman7be68ba2020-01-08 22:08:40 -0800180// C/LSB for the temperature.
181constexpr double kTemperatureLsbDegree = 0.1;
182
183// This is what the datasheet says PROD_ID should be.
184constexpr uint16_t kExpectedProductId = 0x4056;
185// This is the PROD_ID we observe.
186constexpr uint16_t kObservedProductId = 0x4256;
187
188} // namespace
189
190ADIS16470::ADIS16470(aos::EventLoop *event_loop, frc::SPI *spi,
191 frc::DigitalInput *data_ready, frc::DigitalOutput *reset)
192 : event_loop_(event_loop),
193 imu_values_sender_(
Austin Schuhac17fba2020-03-28 15:55:33 -0700194 event_loop_->MakeSender<::frc971::IMUValuesBatch>("/drivetrain")),
Brian Silverman7be68ba2020-01-08 22:08:40 -0800195 initialize_timer_(
196 event_loop_->AddTimer([this]() { DoInitializeStep(); })),
197 spi_(spi),
198 data_ready_(data_ready),
199 reset_(reset) {
200 // Rather than put the entire data packet into the header, just put a size
201 // there and verify it matches here.
202 CHECK_EQ(kAutospiDataSize, read_data_.size());
203
204 // We're not doing burst mode, so this is the IMU's rated speed.
205 spi_->SetClockRate(2'000'000);
206 spi_->SetChipSelectActiveLow();
207 spi_->SetClockActiveLow();
208 spi_->SetSampleDataOnTrailingEdge();
209 spi_->SetMSBFirst();
210
211 // NI's SPI driver defaults to SCHED_OTHER. Find it's PID with ps, and change
212 // it to a RT priority of 33.
Tyler Chatowbf0609c2021-07-31 16:13:27 -0700213 PCHECK(system("busybox ps -ef | grep '\\[spi0\\]' | awk '{print $1}' | xargs "
214 "chrt -f -p "
215 "33") == 0);
216 PCHECK(system("busybox ps -ef | grep '\\[spi1\\]' | awk '{print $1}' | xargs "
217 "chrt -f -p "
218 "33") == 0);
Brian Silverman7be68ba2020-01-08 22:08:40 -0800219
220 event_loop_->OnRun([this]() { BeginInitialization(); });
221}
222
223void ADIS16470::DoReads() {
224 if (state_ != State::kRunning) {
225 // Not sure how to interpret data received now, so ignore it.
226 return;
227 }
228
Austin Schuhac17fba2020-03-28 15:55:33 -0700229 auto builder = imu_values_sender_.MakeBuilder();
230
Brian Silverman7be68ba2020-01-08 22:08:40 -0800231 int amount_to_read =
232 spi_->ReadAutoReceivedData(to_read_.data(), 0, 0 /* don't block */);
Austin Schuhac17fba2020-03-28 15:55:33 -0700233
234 aos::SizedArray<flatbuffers::Offset<IMUValues>, 50> readings_offsets;
Brian Silverman7be68ba2020-01-08 22:08:40 -0800235 while (true) {
236 if (amount_to_read == 0) break;
237 CHECK(!to_read_.empty());
238 const int amount_read_now = std::min<int>(amount_to_read, to_read_.size());
239 CHECK_GT(amount_read_now, 0) << "amount_to_read: " << amount_to_read
240 << ", to_read_.size(): " << to_read_.size();
241 spi_->ReadAutoReceivedData(to_read_.data(), amount_read_now,
242 0 /* don't block */);
243 to_read_ = to_read_.subspan(amount_read_now);
244 amount_to_read -= amount_read_now;
245
246 if (to_read_.empty()) {
Austin Schuhac17fba2020-03-28 15:55:33 -0700247 flatbuffers::Offset<IMUValues> reading_offset =
248 ProcessReading(builder.fbb());
249 readings_offsets.push_back(reading_offset);
Brian Silverman7be68ba2020-01-08 22:08:40 -0800250
251 // Reset for the next reading.
252 to_read_ = absl::MakeSpan(read_data_);
253 } else {
254 CHECK_EQ(amount_to_read, 0);
255 break;
256 }
257 }
Austin Schuhac17fba2020-03-28 15:55:33 -0700258
259 flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<IMUValues>>>
260 readings_offset = builder.fbb()->CreateVector(readings_offsets.data(),
261 readings_offsets.size());
262
263 IMUValuesBatch::Builder imu_values_batch_builder =
264 builder.MakeBuilder<IMUValuesBatch>();
265 imu_values_batch_builder.add_readings(readings_offset);
266 builder.Send(imu_values_batch_builder.Finish());
Brian Silverman7be68ba2020-01-08 22:08:40 -0800267}
268
269void ADIS16470::DoInitializeStep() {
270 switch (state_) {
271 case State::kUninitialized: {
272 to_read_ = absl::MakeSpan(read_data_);
273
274 // First, set the SPI to normal mode so it stops trying to talk
275 // automatically.
276 spi_->StopAuto();
277
278 reset_->Set(false);
279 // Datasheet says it needs a 1 us pulse, so make sure we do something in
280 // between asserting and deasserting.
Austin Schuhb51692d2021-04-10 15:27:25 -0700281 std::this_thread::sleep_for(chrono::milliseconds(1));
Brian Silverman7be68ba2020-01-08 22:08:40 -0800282 reset_->Set(true);
283
284 state_ = State::kWaitForReset;
285 // Datasheet says it takes 193 ms to come out of reset, so give it some
286 // margin on top of that.
287 initialize_timer_->Setup(event_loop_->monotonic_now() +
Austin Schuhb51692d2021-04-10 15:27:25 -0700288 chrono::milliseconds(250));
Tyler Chatowbf0609c2021-07-31 16:13:27 -0700289 } break;
Brian Silverman7be68ba2020-01-08 22:08:40 -0800290
291 case State::kWaitForReset: {
292 flatbuffers::Offset<ADIS16470DiagStat> start_diag_stat;
293 flatbuffers::Offset<ADIS16470DiagStat> self_test_diag_stat;
294 bool success = false;
295 auto builder = imu_values_sender_.MakeBuilder();
296
297 // Configure the IMU the way we want it.
298 const uint16_t product_id = ReadRegister(registers::PROD_ID, 0);
299 if (product_id == kExpectedProductId ||
300 product_id == kObservedProductId) {
301 const uint16_t start_diag_stat_value =
302 ReadRegister(registers::DIAG_STAT, 0);
303 start_diag_stat = PackDiagStat(builder.fbb(), start_diag_stat_value);
304 if (!DiagStatHasError(
305 *GetTemporaryPointer(*builder.fbb(), start_diag_stat))) {
306 WriteRegister(registers::FILT_CTRL, 0 /* no filtering */);
307 WriteRegister(
308 registers::MSC_CTRL,
309 (1 << 7) /* enable gyro linear g compensation */ |
310 (1 << 6) /* enable point of percussion alignment */ |
311 (0 << 2) /* internal clock mode */ |
312 (0 << 1) /* sync polarity, doesn't matter */ |
313 (1 << 0) /* data ready is active high */);
James Kuszmaul26dbd0d2020-09-28 20:08:08 -0700314 // Rate of the output will be 2000 / (DEC_RATE + 1) Hz.
Brian Silverman7be68ba2020-01-08 22:08:40 -0800315 WriteRegister(registers::DEC_RATE,
James Kuszmaul26dbd0d2020-09-28 20:08:08 -0700316 1 /* Average every pair of values. */);
Brian Silverman7be68ba2020-01-08 22:08:40 -0800317
318 // Start a sensor self test.
319 WriteRegister(registers::GLOB_CMD, 1 << 2);
320 // Datasheet says it takes 14ms, so give it some margin.
Austin Schuhb51692d2021-04-10 15:27:25 -0700321 std::this_thread::sleep_for(chrono::milliseconds(25));
Brian Silverman7be68ba2020-01-08 22:08:40 -0800322 // Read DIAG_STAT again, and queue up a read of the first part of the
323 // autospi data packet.
324 const uint16_t self_test_diag_stat_value =
325 ReadRegister(registers::DIAG_STAT, kAutospiPacket[0]);
326 self_test_diag_stat =
327 PackDiagStat(builder.fbb(), self_test_diag_stat_value);
328 if (!DiagStatHasError(
329 *GetTemporaryPointer(*builder.fbb(), self_test_diag_stat))) {
330 // Initialize automatic mode, but don't start it yet.
331 spi_->InitAuto(kAutospiDataSize * 100);
332 spi_->SetAutoTransmitData(kAutospiPacket,
333 0 /* no extra 0s at the end */);
334 // No idea what units the "stall period" is in. This value is just
335 // bigger than the 16us min from the datasheet. It does not appear
336 // to scale with SPICLK frequency. Empirically, this value comes out
337 // to 16.7us.
338 spi_->ConfigureAutoStall(
Brian Silverman7be68ba2020-01-08 22:08:40 -0800339 0 /* the minimum CS delay is enough for this IMU */, 670,
340 1 /* toggle CS every 2 8-bit bytes */);
341
342 // Read any data queued up by the FPGA.
Tyler Chatowbf0609c2021-07-31 16:13:27 -0700343 while (true) {
Brian Silverman7be68ba2020-01-08 22:08:40 -0800344 uint32_t buffer;
345 if (spi_->ReadAutoReceivedData(&buffer, 1, 0 /* don't block */) ==
346 0) {
347 break;
348 }
349 }
350
351 // Finally, enable automatic mode so it starts reading data.
352 spi_->StartAutoTrigger(*data_ready_, true, false);
Austin Schuhb51692d2021-04-10 15:27:25 -0700353
354 // We need a bit of time for the auto trigger to start up so we have
355 // something to throw out. 1 khz trigger, so 2 ms gives us 2 cycles
356 // to hit it worst case.
357 std::this_thread::sleep_for(chrono::milliseconds(2));
358
359 // Throw out the first sample. It is almost always faulted due to
360 // how we start up, and it isn't worth tracking for downstream users
361 // to look at.
362 to_read_ = absl::MakeSpan(read_data_);
363 CHECK_EQ(spi_->ReadAutoReceivedData(
364 to_read_.data(), to_read_.size(),
365 1000.0 /* block for up to 1 second */),
366 static_cast<int>(to_read_.size()))
367 << ": Failed to read first sample.";
Brian Silverman7be68ba2020-01-08 22:08:40 -0800368 success = true;
369 }
370 }
371 }
372
373 IMUValues::Builder imu_builder = builder.MakeBuilder<IMUValues>();
374 imu_builder.add_product_id(product_id);
375 if (!start_diag_stat.IsNull()) {
376 imu_builder.add_start_diag_stat(start_diag_stat);
377 }
378 if (!self_test_diag_stat.IsNull()) {
379 imu_builder.add_self_test_diag_stat(self_test_diag_stat);
380 }
Austin Schuhac17fba2020-03-28 15:55:33 -0700381
382 const flatbuffers::Offset<IMUValues> readings_offsets =
383 imu_builder.Finish();
384 const flatbuffers::Offset<
385 flatbuffers::Vector<flatbuffers::Offset<IMUValues>>>
386 readings_offset = builder.fbb()->CreateVector(&readings_offsets, 1);
387
388 IMUValuesBatch::Builder imu_batch_builder =
389 builder.MakeBuilder<IMUValuesBatch>();
390 imu_batch_builder.add_readings(readings_offset);
391 builder.Send(imu_batch_builder.Finish());
Brian Silverman7be68ba2020-01-08 22:08:40 -0800392 if (success) {
393 state_ = State::kRunning;
394 } else {
395 BeginInitialization();
396 }
Tyler Chatowbf0609c2021-07-31 16:13:27 -0700397 } break;
Brian Silverman7be68ba2020-01-08 22:08:40 -0800398
399 case State::kRunning:
400 LOG(FATAL) << "Not a reset state";
401 }
402}
403
Austin Schuhac17fba2020-03-28 15:55:33 -0700404flatbuffers::Offset<IMUValues> ADIS16470::ProcessReading(
405 flatbuffers::FlatBufferBuilder *fbb) {
Brian Silverman7be68ba2020-01-08 22:08:40 -0800406 // If we ever see this, we'll need to decide how to handle it. Probably reset
407 // everything and try again.
408 CHECK_EQ(0, spi_->GetAutoDroppedCount());
409
Brian Silverman7be68ba2020-01-08 22:08:40 -0800410 absl::Span<const uint32_t> to_process = read_data_;
411 hal::fpga_clock::time_point fpga_time;
412 {
413 int32_t status = 0;
414 const uint64_t fpga_expanded = HAL_ExpandFPGATime(to_process[0], &status);
415 CHECK_EQ(0, status);
416 fpga_time =
417 hal::fpga_clock::time_point(hal::fpga_clock::duration(fpga_expanded));
418 }
419 to_process = to_process.subspan(1);
420
421 const uint16_t diag_stat_value = (static_cast<uint16_t>(to_process[0]) << 8) |
422 static_cast<uint16_t>(to_process[1]);
Austin Schuhac17fba2020-03-28 15:55:33 -0700423 const auto diag_stat = PackDiagStat(fbb, diag_stat_value);
Brian Silverman7be68ba2020-01-08 22:08:40 -0800424 to_process = to_process.subspan(2);
425
Austin Schuhac17fba2020-03-28 15:55:33 -0700426 IMUValues::Builder imu_builder(*fbb);
Brian Silverman7be68ba2020-01-08 22:08:40 -0800427 imu_builder.add_fpga_timestamp(
428 aos::time::DurationInSeconds(fpga_time.time_since_epoch()));
429 imu_builder.add_monotonic_timestamp_ns(
430 time_converter_.FpgaToMonotonic(fpga_time).time_since_epoch().count());
431 imu_builder.add_previous_reading_diag_stat(diag_stat);
432
433 imu_builder.add_gyro_x(ConvertValue16(to_process, kGyroLsbRadianSecond));
434 to_process = to_process.subspan(2);
435 imu_builder.add_accelerometer_x(
436 ConvertValue32(to_process, kAccelerometerLsbG));
437 to_process = to_process.subspan(4);
438 imu_builder.add_gyro_y(ConvertValue16(to_process, kGyroLsbRadianSecond));
439 to_process = to_process.subspan(2);
440 imu_builder.add_accelerometer_y(
441 ConvertValue32(to_process, kAccelerometerLsbG));
442 to_process = to_process.subspan(4);
443 imu_builder.add_gyro_z(ConvertValue16(to_process, kGyroLsbRadianSecond));
444 to_process = to_process.subspan(2);
445 imu_builder.add_accelerometer_z(
446 ConvertValue32(to_process, kAccelerometerLsbG));
447 to_process = to_process.subspan(4);
448
449 imu_builder.add_temperature(
450 ConvertValue16(to_process, kTemperatureLsbDegree));
451 to_process = to_process.subspan(2);
452
453 CHECK(to_process.empty()) << "Have leftover bytes: " << to_process.size();
454
Austin Schuhac17fba2020-03-28 15:55:33 -0700455 return imu_builder.Finish();
Brian Silverman7be68ba2020-01-08 22:08:40 -0800456}
457
458double ADIS16470::ConvertValue32(absl::Span<const uint32_t> data,
459 double lsb_per_output) {
460 const uint32_t unsigned_value = (static_cast<uint32_t>(data[0]) << 24) |
461 (static_cast<uint32_t>(data[1]) << 16) |
462 (static_cast<uint32_t>(data[2]) << 8) |
463 static_cast<uint32_t>(data[3]);
464 int32_t signed_value;
465 memcpy(&signed_value, &unsigned_value, sizeof(unsigned_value));
466 return static_cast<double>(signed_value) * lsb_per_output;
467}
468
469double ADIS16470::ConvertValue16(absl::Span<const uint32_t> data,
470 double lsb_per_output) {
471 const uint16_t unsigned_value =
472 (static_cast<uint16_t>(data[0]) << 8) | static_cast<uint16_t>(data[1]);
473 int16_t signed_value;
474 memcpy(&signed_value, &unsigned_value, sizeof(unsigned_value));
475 return static_cast<double>(signed_value) * lsb_per_output;
476}
477
478flatbuffers::Offset<ADIS16470DiagStat> ADIS16470::PackDiagStat(
479 flatbuffers::FlatBufferBuilder *fbb, uint16_t value) {
480 ADIS16470DiagStat::Builder diag_stat_builder(*fbb);
481 diag_stat_builder.add_clock_error(value & (1 << 7));
482 diag_stat_builder.add_memory_failure(value & (1 << 6));
483 diag_stat_builder.add_sensor_failure(value & (1 << 5));
484 diag_stat_builder.add_standby_mode(value & (1 << 4));
485 diag_stat_builder.add_spi_communication_error(value & (1 << 3));
486 diag_stat_builder.add_flash_memory_update_error(value & (1 << 2));
487 diag_stat_builder.add_data_path_overrun(value & (1 << 1));
488 return diag_stat_builder.Finish();
489}
490
491bool ADIS16470::DiagStatHasError(const ADIS16470DiagStat &diag_stat) {
492 return diag_stat.clock_error() || diag_stat.memory_failure() ||
493 diag_stat.sensor_failure() || diag_stat.standby_mode() ||
494 diag_stat.spi_communication_error() ||
495 diag_stat.flash_memory_update_error() || diag_stat.data_path_overrun();
496}
497
498uint16_t ADIS16470::ReadRegister(uint8_t register_address,
499 uint8_t next_register_address) {
500 uint8_t send_buffer[2] = {static_cast<uint8_t>(register_address & 0x7f), 0};
501 uint8_t dummy[2];
502 spi_->Transaction(send_buffer, dummy, sizeof(send_buffer));
503 uint8_t receive_buffer[2];
504 uint8_t next_send_buffer[2] = {
505 static_cast<uint8_t>(next_register_address & 0x7f), 0};
506 spi_->Transaction(next_send_buffer, receive_buffer, sizeof(receive_buffer));
507 return (static_cast<uint16_t>(receive_buffer[0]) << 8) |
508 static_cast<uint16_t>(receive_buffer[1]);
509}
510
511void ADIS16470::WriteRegister(uint8_t register_address, uint16_t value) {
512 uint8_t buffer1[2] = {static_cast<uint8_t>(register_address | 0x80),
513 static_cast<uint8_t>(value & 0xff)};
514 uint8_t buffer2[2] = {static_cast<uint8_t>(register_address | 0x81),
515 static_cast<uint8_t>(value >> 8)};
516 spi_->Write(buffer1, sizeof(buffer1));
517 spi_->Write(buffer2, sizeof(buffer2));
518}
519
520} // namespace wpilib
521} // namespace frc971