blob: 839b82b76cd8b7e24fb6d739d67a0050da7c6504 [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).
162constexpr double kAccelerometerLsbG = 1.0 / 54'428'800.0;
163// 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);
199
200 event_loop_->OnRun([this]() { BeginInitialization(); });
201}
202
203void ADIS16470::DoReads() {
204 if (state_ != State::kRunning) {
205 // Not sure how to interpret data received now, so ignore it.
206 return;
207 }
208
209 int amount_to_read =
210 spi_->ReadAutoReceivedData(to_read_.data(), 0, 0 /* don't block */);
211 while (true) {
212 if (amount_to_read == 0) break;
213 CHECK(!to_read_.empty());
214 const int amount_read_now = std::min<int>(amount_to_read, to_read_.size());
215 CHECK_GT(amount_read_now, 0) << "amount_to_read: " << amount_to_read
216 << ", to_read_.size(): " << to_read_.size();
217 spi_->ReadAutoReceivedData(to_read_.data(), amount_read_now,
218 0 /* don't block */);
219 to_read_ = to_read_.subspan(amount_read_now);
220 amount_to_read -= amount_read_now;
221
222 if (to_read_.empty()) {
223 ProcessReading();
224
225 // Reset for the next reading.
226 to_read_ = absl::MakeSpan(read_data_);
227 } else {
228 CHECK_EQ(amount_to_read, 0);
229 break;
230 }
231 }
232}
233
234void ADIS16470::DoInitializeStep() {
235 switch (state_) {
236 case State::kUninitialized: {
237 to_read_ = absl::MakeSpan(read_data_);
238
239 // First, set the SPI to normal mode so it stops trying to talk
240 // automatically.
241 spi_->StopAuto();
242
243 reset_->Set(false);
244 // Datasheet says it needs a 1 us pulse, so make sure we do something in
245 // between asserting and deasserting.
246 std::this_thread::sleep_for(::std::chrono::milliseconds(1));
247 reset_->Set(true);
248
249 state_ = State::kWaitForReset;
250 // Datasheet says it takes 193 ms to come out of reset, so give it some
251 // margin on top of that.
252 initialize_timer_->Setup(event_loop_->monotonic_now() +
253 std::chrono::milliseconds(250));
254 }
255 break;
256
257 case State::kWaitForReset: {
258 flatbuffers::Offset<ADIS16470DiagStat> start_diag_stat;
259 flatbuffers::Offset<ADIS16470DiagStat> self_test_diag_stat;
260 bool success = false;
261 auto builder = imu_values_sender_.MakeBuilder();
262
263 // Configure the IMU the way we want it.
264 const uint16_t product_id = ReadRegister(registers::PROD_ID, 0);
265 if (product_id == kExpectedProductId ||
266 product_id == kObservedProductId) {
267 const uint16_t start_diag_stat_value =
268 ReadRegister(registers::DIAG_STAT, 0);
269 start_diag_stat = PackDiagStat(builder.fbb(), start_diag_stat_value);
270 if (!DiagStatHasError(
271 *GetTemporaryPointer(*builder.fbb(), start_diag_stat))) {
272 WriteRegister(registers::FILT_CTRL, 0 /* no filtering */);
273 WriteRegister(
274 registers::MSC_CTRL,
275 (1 << 7) /* enable gyro linear g compensation */ |
276 (1 << 6) /* enable point of percussion alignment */ |
277 (0 << 2) /* internal clock mode */ |
278 (0 << 1) /* sync polarity, doesn't matter */ |
279 (1 << 0) /* data ready is active high */);
280 WriteRegister(registers::DEC_RATE,
281 0 /* no internal decimation (averaging) */);
282
283 // Start a sensor self test.
284 WriteRegister(registers::GLOB_CMD, 1 << 2);
285 // Datasheet says it takes 14ms, so give it some margin.
286 std::this_thread::sleep_for(std::chrono::milliseconds(25));
287 // Read DIAG_STAT again, and queue up a read of the first part of the
288 // autospi data packet.
289 const uint16_t self_test_diag_stat_value =
290 ReadRegister(registers::DIAG_STAT, kAutospiPacket[0]);
291 self_test_diag_stat =
292 PackDiagStat(builder.fbb(), self_test_diag_stat_value);
293 if (!DiagStatHasError(
294 *GetTemporaryPointer(*builder.fbb(), self_test_diag_stat))) {
295 // Initialize automatic mode, but don't start it yet.
296 spi_->InitAuto(kAutospiDataSize * 100);
297 spi_->SetAutoTransmitData(kAutospiPacket,
298 0 /* no extra 0s at the end */);
299 // No idea what units the "stall period" is in. This value is just
300 // bigger than the 16us min from the datasheet. It does not appear
301 // to scale with SPICLK frequency. Empirically, this value comes out
302 // to 16.7us.
303 spi_->ConfigureAutoStall(
304 HAL_SPI_kOnboardCS0,
305 0 /* the minimum CS delay is enough for this IMU */, 670,
306 1 /* toggle CS every 2 8-bit bytes */);
307
308 // Read any data queued up by the FPGA.
309 while (true){
310 uint32_t buffer;
311 if (spi_->ReadAutoReceivedData(&buffer, 1, 0 /* don't block */) ==
312 0) {
313 break;
314 }
315 }
316
317 // Finally, enable automatic mode so it starts reading data.
318 spi_->StartAutoTrigger(*data_ready_, true, false);
319 success = true;
320 }
321 }
322 }
323
324 IMUValues::Builder imu_builder = builder.MakeBuilder<IMUValues>();
325 imu_builder.add_product_id(product_id);
326 if (!start_diag_stat.IsNull()) {
327 imu_builder.add_start_diag_stat(start_diag_stat);
328 }
329 if (!self_test_diag_stat.IsNull()) {
330 imu_builder.add_self_test_diag_stat(self_test_diag_stat);
331 }
332 builder.Send(imu_builder.Finish());
333 if (success) {
334 state_ = State::kRunning;
335 } else {
336 BeginInitialization();
337 }
338 }
339 break;
340
341 case State::kRunning:
342 LOG(FATAL) << "Not a reset state";
343 }
344}
345
346void ADIS16470::ProcessReading() {
347 // If we ever see this, we'll need to decide how to handle it. Probably reset
348 // everything and try again.
349 CHECK_EQ(0, spi_->GetAutoDroppedCount());
350
351 auto builder = imu_values_sender_.MakeBuilder();
352
353 absl::Span<const uint32_t> to_process = read_data_;
354 hal::fpga_clock::time_point fpga_time;
355 {
356 int32_t status = 0;
357 const uint64_t fpga_expanded = HAL_ExpandFPGATime(to_process[0], &status);
358 CHECK_EQ(0, status);
359 fpga_time =
360 hal::fpga_clock::time_point(hal::fpga_clock::duration(fpga_expanded));
361 }
362 to_process = to_process.subspan(1);
363
364 const uint16_t diag_stat_value = (static_cast<uint16_t>(to_process[0]) << 8) |
365 static_cast<uint16_t>(to_process[1]);
366 const auto diag_stat = PackDiagStat(builder.fbb(), diag_stat_value);
367 to_process = to_process.subspan(2);
368
369 IMUValues::Builder imu_builder = builder.MakeBuilder<IMUValues>();
370 imu_builder.add_fpga_timestamp(
371 aos::time::DurationInSeconds(fpga_time.time_since_epoch()));
372 imu_builder.add_monotonic_timestamp_ns(
373 time_converter_.FpgaToMonotonic(fpga_time).time_since_epoch().count());
374 imu_builder.add_previous_reading_diag_stat(diag_stat);
375
376 imu_builder.add_gyro_x(ConvertValue16(to_process, kGyroLsbRadianSecond));
377 to_process = to_process.subspan(2);
378 imu_builder.add_accelerometer_x(
379 ConvertValue32(to_process, kAccelerometerLsbG));
380 to_process = to_process.subspan(4);
381 imu_builder.add_gyro_y(ConvertValue16(to_process, kGyroLsbRadianSecond));
382 to_process = to_process.subspan(2);
383 imu_builder.add_accelerometer_y(
384 ConvertValue32(to_process, kAccelerometerLsbG));
385 to_process = to_process.subspan(4);
386 imu_builder.add_gyro_z(ConvertValue16(to_process, kGyroLsbRadianSecond));
387 to_process = to_process.subspan(2);
388 imu_builder.add_accelerometer_z(
389 ConvertValue32(to_process, kAccelerometerLsbG));
390 to_process = to_process.subspan(4);
391
392 imu_builder.add_temperature(
393 ConvertValue16(to_process, kTemperatureLsbDegree));
394 to_process = to_process.subspan(2);
395
396 CHECK(to_process.empty()) << "Have leftover bytes: " << to_process.size();
397
398 builder.Send(imu_builder.Finish());
399}
400
401double ADIS16470::ConvertValue32(absl::Span<const uint32_t> data,
402 double lsb_per_output) {
403 const uint32_t unsigned_value = (static_cast<uint32_t>(data[0]) << 24) |
404 (static_cast<uint32_t>(data[1]) << 16) |
405 (static_cast<uint32_t>(data[2]) << 8) |
406 static_cast<uint32_t>(data[3]);
407 int32_t signed_value;
408 memcpy(&signed_value, &unsigned_value, sizeof(unsigned_value));
409 return static_cast<double>(signed_value) * lsb_per_output;
410}
411
412double ADIS16470::ConvertValue16(absl::Span<const uint32_t> data,
413 double lsb_per_output) {
414 const uint16_t unsigned_value =
415 (static_cast<uint16_t>(data[0]) << 8) | static_cast<uint16_t>(data[1]);
416 int16_t signed_value;
417 memcpy(&signed_value, &unsigned_value, sizeof(unsigned_value));
418 return static_cast<double>(signed_value) * lsb_per_output;
419}
420
421flatbuffers::Offset<ADIS16470DiagStat> ADIS16470::PackDiagStat(
422 flatbuffers::FlatBufferBuilder *fbb, uint16_t value) {
423 ADIS16470DiagStat::Builder diag_stat_builder(*fbb);
424 diag_stat_builder.add_clock_error(value & (1 << 7));
425 diag_stat_builder.add_memory_failure(value & (1 << 6));
426 diag_stat_builder.add_sensor_failure(value & (1 << 5));
427 diag_stat_builder.add_standby_mode(value & (1 << 4));
428 diag_stat_builder.add_spi_communication_error(value & (1 << 3));
429 diag_stat_builder.add_flash_memory_update_error(value & (1 << 2));
430 diag_stat_builder.add_data_path_overrun(value & (1 << 1));
431 return diag_stat_builder.Finish();
432}
433
434bool ADIS16470::DiagStatHasError(const ADIS16470DiagStat &diag_stat) {
435 return diag_stat.clock_error() || diag_stat.memory_failure() ||
436 diag_stat.sensor_failure() || diag_stat.standby_mode() ||
437 diag_stat.spi_communication_error() ||
438 diag_stat.flash_memory_update_error() || diag_stat.data_path_overrun();
439}
440
441uint16_t ADIS16470::ReadRegister(uint8_t register_address,
442 uint8_t next_register_address) {
443 uint8_t send_buffer[2] = {static_cast<uint8_t>(register_address & 0x7f), 0};
444 uint8_t dummy[2];
445 spi_->Transaction(send_buffer, dummy, sizeof(send_buffer));
446 uint8_t receive_buffer[2];
447 uint8_t next_send_buffer[2] = {
448 static_cast<uint8_t>(next_register_address & 0x7f), 0};
449 spi_->Transaction(next_send_buffer, receive_buffer, sizeof(receive_buffer));
450 return (static_cast<uint16_t>(receive_buffer[0]) << 8) |
451 static_cast<uint16_t>(receive_buffer[1]);
452}
453
454void ADIS16470::WriteRegister(uint8_t register_address, uint16_t value) {
455 uint8_t buffer1[2] = {static_cast<uint8_t>(register_address | 0x80),
456 static_cast<uint8_t>(value & 0xff)};
457 uint8_t buffer2[2] = {static_cast<uint8_t>(register_address | 0x81),
458 static_cast<uint8_t>(value >> 8)};
459 spi_->Write(buffer1, sizeof(buffer1));
460 spi_->Write(buffer2, sizeof(buffer2));
461}
462
463} // namespace wpilib
464} // namespace frc971