Read several registers

Reading the IRA_REG_M register was a good test of our understanding of the I2C protocol but thatregister contains uninteresting information.

This time, we’ll read the registers of the magnetometer that actually expose the sensor readings.Six contiguous registers are involved and they start with OUT_X_H_M at address 0x03.

We’ll modify our previous program to read these six registers. Only a few modifications are needed.

We’ll need to change the address we request from the magnetometer from IRA_REG_M to OUT_X_H_M.

  1. // Send the address of the register that we want to read: OUT_X_H_M
  2. i2c1.txdr.write(|w| w.txdata().bits(OUT_X_H_M));

We’ll have to request the slave for six bytes rather than just one.

  1. // Broadcast RESTART
  2. // Broadcast the MAGNETOMETER address with the R/W bit set to Read
  3. i2c1.cr2.modify(|_, w| {
  4. w.start().set_bit();
  5. w.nbytes().bits(6);
  6. w.rd_wrn().set_bit();
  7. w.autoend().set_bit()
  8. });

And fill a buffer rather than read just one byte:

  1. let mut buffer = [0u8; 6];
  2. for byte in &mut buffer {
  3. // Wait until we have received the contents of the register
  4. while i2c1.isr.read().rxne().bit_is_clear() {}
  5. *byte = i2c1.rxdr.read().rxdata().bits();
  6. }
  7. // Broadcast STOP (automatic because of `AUTOEND = 1`)

Putting it all together inside a loop alongside a delay to reduce the data throughput:

  1. #![deny(unsafe_code)]
  2. #![no_main]
  3. #![no_std]
  4. #[allow(unused_imports)]
  5. use aux14::{entry, iprint, iprintln, prelude::*};
  6. // Slave address
  7. const MAGNETOMETER: u8 = 0b001_1110;
  8. // Addresses of the magnetometer's registers
  9. const OUT_X_H_M: u8 = 0x03;
  10. const IRA_REG_M: u8 = 0x0A;
  11. #[entry]
  12. fn main() -> ! {
  13. let (i2c1, mut delay, mut itm) = aux14::init();
  14. loop {
  15. // Broadcast START
  16. // Broadcast the MAGNETOMETER address with the R/W bit set to Write
  17. i2c1.cr2.write(|w| {
  18. w.start().set_bit();
  19. w.sadd1().bits(MAGNETOMETER);
  20. w.rd_wrn().clear_bit();
  21. w.nbytes().bits(1);
  22. w.autoend().clear_bit()
  23. });
  24. // Wait until we can send more data
  25. while i2c1.isr.read().txis().bit_is_clear() {}
  26. // Send the address of the register that we want to read: IRA_REG_M
  27. i2c1.txdr.write(|w| w.txdata().bits(OUT_X_H_M));
  28. // Wait until the previous byte has been transmitted
  29. while i2c1.isr.read().tc().bit_is_clear() {}
  30. // Broadcast RESTART
  31. // Broadcast the MAGNETOMETER address with the R/W bit set to Read
  32. i2c1.cr2.modify(|_, w| {
  33. w.start().set_bit();
  34. w.nbytes().bits(6);
  35. w.rd_wrn().set_bit();
  36. w.autoend().set_bit()
  37. });
  38. let mut buffer = [0u8; 6];
  39. for byte in &mut buffer {
  40. // Wait until we have received something
  41. while i2c1.isr.read().rxne().bit_is_clear() {}
  42. *byte = i2c1.rxdr.read().rxdata().bits();
  43. }
  44. // Broadcast STOP (automatic because of `AUTOEND = 1`)
  45. iprintln!(&mut itm.stim[0], "{:?}", buffer);
  46. delay.delay_ms(1_000_u16);
  47. }
  48. }

If you run this, you should printed in the itmdump‘s console a new array of six bytes everysecond. The values within the array should change if you move around the board.

  1. $ # itmdump terminal
  2. (..)
  3. [0, 45, 255, 251, 0, 193]
  4. [0, 44, 255, 249, 0, 193]
  5. [0, 49, 255, 250, 0, 195]

But these bytes don’t make much sense like that. Let’s turn them into actual readings:

  1. let x_h = u16::from(buffer[0]);
  2. let x_l = u16::from(buffer[1]);
  3. let z_h = u16::from(buffer[2]);
  4. let z_l = u16::from(buffer[3]);
  5. let y_h = u16::from(buffer[4]);
  6. let y_l = u16::from(buffer[5]);
  7. let x = ((x_h << 8) + x_l) as i16;
  8. let y = ((y_h << 8) + y_l) as i16;
  9. let z = ((z_h << 8) + z_l) as i16;
  10. iprintln!(&mut itm.stim[0], "{:?}", (x, y, z));

Now it should look better:

  1. $ # `itmdump terminal
  2. (..)
  3. (44, 196, -7)
  4. (45, 195, -6)
  5. (46, 196, -9)

This is the Earth’s magnetic field decomposed alongside the XYZ axis of the magnetometer.

In the next section, we’ll learn how to make sense of these numbers.