eldruin / lsm303agr-rs

Platform agnostic Rust driver for the LSM303AGR ultra-compact high-performance eCompass module: ultra-low-power 3D accelerometer and 3D magnetometer
Apache License 2.0
18 stars 14 forks source link

Confusing readings with different accelerometer modes #3

Closed robyoung closed 3 years ago

robyoung commented 3 years ago

I am using this library on micro:bit (tested on a V1.5 and V2) and getting some confusing readings when experimenting with different AccelMode values.

When I change the AccelMode I get different values for the readings. My understanding of the resolution_factor used here is that it accounts for the different number of bits used in each operating mode (described in 4.2.1 of the datasheet).

It looks as though the HighResolution data is correct (Z is approximately equal to 1000mg). This is what I would expect from the datasheet. However, it looks as though the Normal and LowPower data is also being returned the same and then getting sent off by the reslution_factor.

Example code

The full example, working on the V1.5 or V2 micro:bit, can be found here. Run it with

cargo run --release --manifest-path ./examples/magnetometer/Cargo.toml --features v1 --target thumbv6m-none-eabi

The main logic of the example is also copied below for convenience.

let mut sensor = Lsm303agr::new_with_i2c(i2c);
match sensor.accelerometer_id() {
    Ok(0x33u8) => {},
    _ => defmt::panic!("accelerometer not found"),
}
timer.delay_ms(100_u32);
defmt::info!("before init");
sensor.init().unwrap();
defmt::info!("before set accel odr");
sensor.set_accel_odr(AccelOutputDataRate::Hz50).unwrap();
sensor.set_accel_mode(AccelMode::Normal).unwrap();
timer.delay_ms(1000_u32);
get_data(&mut sensor);
sensor.set_accel_mode(AccelMode::LowPower).unwrap();
timer.delay_ms(1000_u32);
get_data(&mut sensor);
sensor.set_accel_mode(AccelMode::HighResolution).unwrap();
timer.delay_ms(1000_u32);
get_data(&mut sensor);
fn get_data(sensor: &mut Lsm303agr<I2cInterface<twim::Twim<TWIM1>>, MagOneShot>) {
    let mut counter = 0;
    loop {
        if sensor.accel_status().unwrap().xyz_new_data {
            let data = sensor.accel_data().unwrap();
            defmt::info!("Acceleration: x {} y {} z {}", data.x, data.y, data.z);
            counter += 1;
        }
        if counter > 2 {
            return;
        }
    }
}

And the output I get (with the data being written to the registers)

 INFO  before init
 INFO  register: 0x23 data: 0b10000000
 INFO  before set accel odr
 INFO  register: 0x20 data: 0b1000111
 INFO  register: 0x20 data: 0b1000111
 INFO  register: 0x23 data: 0b10000000
 INFO  Acceleration: x -35 y -60 z -247
 INFO  Acceleration: x -36 y -60 z -247
 INFO  Acceleration: x -36 y -59 z -253
 INFO  register: 0x23 data: 0b10000000
 INFO  register: 0x20 data: 0b1001111
 INFO  Acceleration: x -8 y -15 z -62
 INFO  Acceleration: x -9 y -16 z -63
 INFO  Acceleration: x -9 y -15 z -62
 INFO  register: 0x20 data: 0b1000111
 INFO  register: 0x23 data: 0b10001000
 INFO  Acceleration: x -141 y -240 z -994
 INFO  Acceleration: x -142 y -242 z -995
 INFO  Acceleration: x -141 y -238 z -997
eldruin commented 3 years ago

Hmm, interesting. Could it be that the measurements were taken too quickly (i.e. before the mode change took effect)? Being in one-shot mode, I assume the delay should be the turn-on delay detailed in the 4.2.1 section of the datasheet. Could you add some delays and try again?

robyoung commented 3 years ago

I have done a bit of reading through other drivers to better understand things and I think I misunderstood the datasheet. This chunk of code from SodaqMoja/Sodaq_LSM303AGR scales even the +/-2g factor back up when in normal or low power modes. I now realise that the right most column of the table below is talking about the number after applying the resolution factor.

Operating mode selection table

So the raw result is divided by a higher resolution factor for the lower power modes to blank out the least significant bits because they are not significant for lower resolutions. But the number still has to be scaled back up again to get a valid mg value.

Given this I'm not sure how useful the unscaled value is. I think I have enough information now to have a stab at a PR though.

robyoung commented 3 years ago

Hmm, interesting. Could it be that the measurements were taken too quickly (i.e. before the mode change took effect)? Being in one-shot mode, I assume the delay should be the turn-on delay detailed in the 4.2.1 section of the datasheet. Could you add some delays and try again?

The example has 1s delays after each mode change and then takes three measurements to try and ensure the value is stable.

eldruin commented 3 years ago

Ah, thanks I missed that reading it now as well. As a hint, if you prepare a PR, I would suggest a macro for the tests to avoid repeating the code for all combinations.

robyoung commented 3 years ago

Scaling addressed in #5