1use super::dmp_constants::*;
2use super::get_i2c_bus_path;
3use super::{
4 AccelDlpf, AccelFsr, FifoReadError, GyroDlpf, GyroFsr, MPU9250_SLAVE_ADDR, Mpu9250,
5 Mpu9250Sample,
6};
7use ak8963::{
8 Ak8963, Ak8963Sample, SampleRate as Ak8963SampleRate, Sensitivity as Ak8963Sensitivity,
9};
10use byteorder::{BigEndian, ByteOrder, WriteBytesExt};
11use i2cdev::core::*;
12use i2cdev::linux::{LinuxI2CDevice, LinuxI2CError};
13use ndarray::prelude::*;
14use std::cmp;
15
16const GYRO_SF: u32 = 46850825;
17
18const QUAT_ERROR_THRESH: i32 = (1 << 16);
19const QUAT_MAG_SQ_NORMALIZED: i32 = (1 << 28);
20const QUAT_MAG_SQ_MIN: i32 = (QUAT_MAG_SQ_NORMALIZED - QUAT_ERROR_THRESH);
21const QUAT_MAG_SQ_MAX: i32 = (QUAT_MAG_SQ_NORMALIZED + QUAT_ERROR_THRESH);
22
23#[derive(Clone, Copy)]
24enum Mpu6500Reg {
25 BankSel = 0x6d,
26 MemRw = 0x6f,
27 PrgmStartH = 0x70,
28}
29
30impl Mpu6500Reg {
31 pub fn addr(&self) -> u8 {
32 *self as u8
33 }
34}
35
36const MPU6500_BANK_SIZE: usize = 256;
37
38pub enum DmpInterruptMode {
39 Continuous,
41 Gesture,
43}
44
45pub struct DigitalMotionProcessor {
46 i2c_dev: LinuxI2CDevice,
47 pub mpu: Mpu9250,
48 pub mag: Ak8963,
49 pub debug: bool,
50}
51
52impl DigitalMotionProcessor {
53 pub fn new(
54 i2c_bus: i32,
55 mpu_addr: Option<u16>,
56 mpu_whoami: Option<u8>,
57 ak_addr: Option<u16>,
58 ) -> Result<DigitalMotionProcessor, LinuxI2CError> {
59 let i2c_dev = LinuxI2CDevice::new(get_i2c_bus_path(i2c_bus), MPU9250_SLAVE_ADDR)?;
60
61 let mut mpu = Mpu9250::new(i2c_bus, mpu_addr, mpu_whoami, None)?;
62
63 mpu.enable_bypass()?;
64
65 let mag = Ak8963::new(
66 i2c_bus,
67 ak_addr,
68 Ak8963Sensitivity::Opt16bit,
69 Ak8963SampleRate::Opt100Hz,
70 ).unwrap();
71
72 mpu.disable_bypass()?;
73
74 Ok(DigitalMotionProcessor {
75 i2c_dev,
76 mpu,
77 mag,
78 debug: false,
79 })
80 }
81
82 pub fn set_debug(&mut self, debug: bool) {
83 self.debug = debug;
84 self.mpu.set_debug(debug);
85 }
86
87 pub fn initialize(&mut self, sample_rate: u16) -> Result<(), LinuxI2CError> {
88 self.mpu.reset()?;
89 self.mpu.set_sample_rate(sample_rate)?;
90 self.mpu.write_accel_fsr(AccelFsr::Opt2G)?;
91 self.mpu.write_gyro_fsr(GyroFsr::Opt250)?;
92 self.mpu.write_accel_dlpf(AccelDlpf::Opt41)?;
93 self.mpu.write_gyro_dlpf(GyroDlpf::Opt41)?;
94
95 self.load_motion_driver_firmware()?;
96
97 self.write_fifo_rate(200)?;
99
100 self.enable_quaternion_accel_gyro_in_fifo()?;
101
102 self.set_interrupt_mode(DmpInterruptMode::Continuous)?;
103
104 self.mpu.dmp_enable()?;
105
106 self.mpu.setup_mag_in_fifo()?;
107 Ok(())
108 }
109
110 pub fn write_memory(&mut self, mem_addr: u16, data: &[u8]) -> Result<(), LinuxI2CError> {
111 let length = data.len();
112 if (((mem_addr & 0xFF) as usize) + length) > MPU6500_BANK_SIZE {
113 panic!("Exceeds bank size");
114 }
115
116 self.i2c_dev.write(&[
119 Mpu6500Reg::BankSel.addr(),
120 (mem_addr >> 8) as u8,
121 (mem_addr & 0xFF) as u8,
122 ])?;
123
124 let mut data2 = vec![Mpu6500Reg::MemRw.addr()];
126 for x in data.iter() {
127 data2.push(*x);
128 }
129 self.i2c_dev.write(&data2)?;
130 Ok(())
131 }
132
133 pub fn read_memory(&mut self, mem_addr: u16, length: u8) -> Result<Vec<u8>, LinuxI2CError> {
134 if (((mem_addr & 0xFF) + (length as u16)) as usize) > MPU6500_BANK_SIZE {
135 panic!("Exceeds bank size");
136 }
137
138 self.i2c_dev.write(&[
141 Mpu6500Reg::BankSel.addr(),
142 (mem_addr >> 8) as u8,
143 (mem_addr & 0xFF) as u8,
144 ])?;
145
146 let mut data = vec![0; length as usize];
147 self.i2c_dev.write(&[Mpu6500Reg::MemRw.addr()])?;
148 self.i2c_dev.read(&mut data)?;
149 Ok(data)
150 }
151
152 fn load_motion_driver_firmware(&mut self) -> Result<(), LinuxI2CError> {
153 let mut write_offset: u16 = 0; while write_offset < DMP_CODE_SIZE {
155 let bytes_to_write = cmp::min(DMP_LOAD_CHUNK, DMP_CODE_SIZE - write_offset);
156 let firmware_slice =
157 &DMP_FIRMWARE[write_offset as usize..(write_offset + bytes_to_write) as usize];
158 self.write_memory(write_offset, firmware_slice)?;
159 write_offset += bytes_to_write;
160 }
162
163 self.i2c_dev
164 .write(&[Mpu6500Reg::PrgmStartH.addr(), 0x04, 0x00])?;
165 Ok(())
166 }
167
168 fn set_interrupt_mode(&mut self, mode: DmpInterruptMode) -> Result<(), LinuxI2CError> {
169 match mode {
170 DmpInterruptMode::Continuous => {
171 self.write_memory(
172 CFG_FIFO_ON_EVENT,
173 &[
174 0xd8, 0xb1, 0xb9, 0xf3, 0x8b, 0xa3, 0x91, 0xb6, 0x09, 0xb4, 0xd9,
175 ],
176 )?;
177 }
178 DmpInterruptMode::Gesture => {
179 self.write_memory(
180 CFG_FIFO_ON_EVENT,
181 &[
182 0xda, 0xb1, 0xb9, 0xf3, 0x8b, 0xa3, 0x91, 0xb6, 0xda, 0xb4, 0xda,
183 ],
184 )?;
185 }
186 };
187 Ok(())
188 }
189
190 fn write_fifo_rate(&mut self, rate: u16) -> Result<(), LinuxI2CError> {
191 let regs_end: [u8; 12] = [
195 DINAFE, DINAF2, DINAAB, 0xc4, DINAAA, DINAF1, DINADF, DINADF, 0xbb, 0xaf, DINADF,
196 DINADF,
197 ];
198 let div = ((DMP_MAX_RATE / rate) - 1) as u16;
199
200 self.write_memory(D_0_22, &[(div >> 8) as u8, (div & 0xff) as u8])?;
201 self.write_memory(CFG_6, ®s_end)?;
202 Ok(())
203 }
204
205 pub fn enable_6x_lp_quat(&mut self) -> Result<(), LinuxI2CError> {
206 self.write_memory(CFG_8, &[DINA20, DINA28, DINA30, DINA38])?;
207 Ok(())
208 }
209
210 pub fn disable_6x_lp_quat(&mut self) -> Result<(), LinuxI2CError> {
211 self.write_memory(CFG_8, &[0xA3; 4])?;
212 Ok(())
213 }
214
215 fn enable_quaternion_accel_gyro_in_fifo(&mut self) -> Result<(), LinuxI2CError> {
216 let mut tmp = vec![];
218 tmp.write_u32::<BigEndian>(GYRO_SF).unwrap();
219 self.write_memory(D_0_104, &tmp)?;
220
221 let mut buf10: [u8; 10] = [0; 10];
223
224 buf10[0] = 0xA3;
225 buf10[1] = 0xC0;
227 buf10[2] = 0xC8;
228 buf10[3] = 0xC2;
229 buf10[4] = 0xC4;
231 buf10[5] = 0xCC;
232 buf10[6] = 0xC6;
233 buf10[7] = 0xA3;
235 buf10[8] = 0xA3;
236 buf10[9] = 0xA3;
237 self.write_memory(CFG_15, &buf10)?;
238
239 self.write_memory(CFG_27, &[0xD8])?;
241 self.write_memory(CFG_20, &[0xD8])?;
243 self.write_memory(CFG_ANDROID_ORIENT_INT, &[0xD8])?;
245
246 self.enable_6x_lp_quat()?;
247 self.mpu.reset_fifo()?;
248
249 Ok(())
250 }
251
252 pub fn read_fifo(&mut self) -> Result<DmpSample, DmpFifoReadError> {
253 let data = self
254 .mpu
255 .read_fifo_strict(35)
256 .map_err(|e| DmpFifoReadError::Read(e))?;
257
258 if self.debug {
259 println!("FIFO packet data (mag+mpu): {:?}", &data[..]);
260 }
261 let mag_output = self.mag.parse_sample_data(&data[0..7]);
262 let quaternion = Self::parse_quaternion(&data[7..23]).ok_or(DmpFifoReadError::Parse)?;
263 let taitbryan = Self::quaternion_to_taitbryan(&quaternion);
264 let mpu_output = Mpu9250::parse_accel_and_gyro(
265 &data[23..35],
266 self.mpu.accel_fsr.get_sensitivity_mss(),
267 self.mpu.gyro_fsr.get_scalar(),
268 );
269
270 Ok(DmpSample {
271 mag: mag_output,
272 mpu: mpu_output,
273 quaternion,
274 taitbryan,
275 })
276 }
277
278 fn parse_quaternion(data: &[u8]) -> Option<Array1<f32>> {
279 let q_data = array![
280 BigEndian::read_i32(&data[0..4]),
281 BigEndian::read_i32(&data[4..8]),
282 BigEndian::read_i32(&data[8..12]),
283 BigEndian::read_i32(&data[12..16]),
284 ];
285
286 let mut q: Array1<f64> = array![
287 q_data[0] as f64,
288 q_data[1] as f64,
289 q_data[2] as f64,
290 q_data[3] as f64,
291 ];
292 let norm = (q[0].powi(2) + q[1].powi(2) + q[2].powi(2) + q[3].powi(2)).sqrt();
293 q[0] /= norm;
294 q[1] /= norm;
295 q[2] /= norm;
296 q[3] /= norm;
297
298 let mut q_verify: Vec<i32> = vec![
301 q_data[0] >> 16,
302 q_data[1] >> 16,
303 q_data[2] >> 16,
304 q_data[3] >> 16,
305 ];
306 for v in &mut q_verify {
307 match (*v).checked_mul(*v) {
308 None => return None,
309 Some(sq_v) => *v = sq_v,
310 }
311 }
312 let quat_mag_sq = q_verify[0].checked_add(q_verify[1]).and_then(|v| {
314 v.checked_add(q_verify[2])
315 .and_then(|v2| v2.checked_add(q_verify[3]))
316 });
317 match quat_mag_sq {
318 None => return None,
319 Some(quat_mag_sq) => {
320 if (quat_mag_sq < QUAT_MAG_SQ_MIN) || (quat_mag_sq > QUAT_MAG_SQ_MAX) {
321 return None;
322 }
323 }
324 }
325 Some(array![q[0] as f32, q[1] as f32, q[2] as f32, q[3] as f32])
326 }
327
328 pub fn quaternion_to_taitbryan(q: &Array1<f32>) -> Array1<f32> {
329 let tb1 = 2.0 * (q[0] * q[2] - q[1] * q[3]).asin();
330 let tb0 =
331 (2.0 * (q[2] * q[3] + q[0] * q[1])).atan2(1.0 - 2.0 * (q[1] * q[1] + q[2] * q[2]));
332 let tb2 =
333 (2.0 * (q[1] * q[2] + q[0] * q[3])).atan2(1.0 - 2.0 * (q[2] * q[2] + q[3] * q[3]));
334 array![tb0, tb1, tb2]
335 }
336}
337
338#[derive(Clone, Debug)]
339pub struct DmpSample {
340 pub mag: Option<Ak8963Sample>,
341 pub mpu: Mpu9250Sample,
342 pub quaternion: Array1<f32>,
343 pub taitbryan: Array1<f32>,
344}
345
346#[derive(Debug)]
347pub enum DmpFifoReadError {
348 Read(FifoReadError),
349 Parse,
350}