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