copterust/mpu9250

With after pulls/#11 `mpu.all()` transaction taking 10ns on my device

little-arhat opened this issue · 4 comments

When I updated my mpu version, I saw a second peak on the SPI clock bus, and when looking into it, I found this PR.
As you can see at the bottom of the screen capture, it eats into my time budget.

20190405122322
20190405122547
this is captured with this program:

#[allow(non_snake_case)]
    #[interrupt(resources = [LED1, EXTI, MPU, FILTER], priority = 2)]
    fn EXTI15_10() {
        resources.LED1.set_high();
        resources.EXTI.pr.modify(|_, w| w.pr15().set_bit());

        let b = resources.MPU.all();
        resources.LED1.set_low();
    }

the purple trace is the LED1

here is another run with unscaled_all:

   #[allow(non_snake_case)]
    #[interrupt(resources = [LED1, EXTI, MPU, FILTER], priority = 2)]
    fn EXTI15_10() {
        resources.LED1.set_high();
        resources.EXTI.pr.modify(|_, w| w.pr15().set_bit());

        let b = resources.MPU.unscaled_all();
        resources.LED1.set_low();
    }

20190405163233

Originally posted by @nraynaud in #11 (comment)

Previously, we've been reading acc, gyro and mag data in one go, like this:

pub fn all(&mut self) -> Result<MargMeasurements, E> {
         let buffer = self.dev.read_many::<U21>(Register::ACCEL_XOUT_H)?;
...

with #11 merged, we do it slightly differently:

    pub fn all(&mut self) -> Result<MargMeasurements, E> {
        let atg_buffer = Device::read_many::<U15>(&mut self.dev, Register::ACCEL_XOUT_H)?;
let mag_buffer = AK8963::read_xyz(&mut self.dev)?;
...

where, AK8963::read_xyz is needed to allow different implementations for spi and imu. However, as read_xyz for spi device is implemented like another transaction, we introduce extra overhead.

We have to come up with different implementation, that doesn't incur penalty.

it's getting complicated, what do you think of exposing an API for so that the user does their communication themselve?

Sorry for the trouble; I neglected to highlight that change in #11.

Since the I2C implementation is using I2C passthrough, we perform a direct query of the AK8963. Given the existing abstractions in this crate, this turned into the double transaction, as @little-arhat noted. It was the most straight-forward implementation when I put it together, but there is the overhead of a needless transaction for SPI-based communication.

A resolution could be to move the full 9-DOF query down into the device level. A SPI device can perform the whole 6DOF + temp + mag reading in one transaction; the I2C device would perform the two calls. I could put this together if we're interested, but I welcome any other approaches!

@nraynaud, what might an API for manual communication look like? To take it to the extreme: users are free to steal the register numbers from this crate and implement their own I2C / SPI calls. What could the middle-ground look like?

@mciantyre I guess we can add SixDOFDevice and then users can implement those two however they like.