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