discovery
discovery copied to clipboard
Newer revisions use LSM303AGR instead of LSM303DLHC
Newer revisions of the discovery board use the LSM303AGR
instead of the LSM303DLHC
. The I2C address of the LSM303AGR
chip is still the same but the registers to read are different. This makes it very confusing, as the code doesn't end up panicking, only returning zeros or garbage data.
Thanks for noticing. I have a WIP driver for the LSM303AGR so you can at least play around with that. It is actually the same accelerometer as the micro:bit board has, for which this book is being rewritten for (a deprecation/removal of the current book version is still uncertain since the F3 disco board seems available for purchase again)
That's great news! Even with the discrepancies the book has, I found this book extremely helpful with dipping my toes into the embedded rust world, and updating the book will help countless more people.
Additionally, to make the LSM303AGR
works like in the section 14.5 of the discovery book, you have to change its system mode from default idle
to continuous
mode by modifying the first two bits of the register CFG_REG_A_M
via the I2C as well. Otherwise, your (x, y, z)
value would probably stuck at some values! Hope this help someone.
This is for anyone working through the book and you're stuck at the I2C
section because the returned value from the IRA_REG_M
register is 0
.
Here is the link for the LSM303AGR data sheet. As noted above, newer board revisions use this IC
instead of the LSM303DLHC
.
For a quick test the WHO_AM_I_M
register (address 0x4F
) of the magnetometer can be used to quickly query the device via I2C.
If successfully you should see 64
or 0b01000000
So the code from the book becomes:
const MAGNETOMETER: u8 = 0b001_1110;
// Addresses of the magnetometer's registers.
const WHO_AM_I_M: u8 = 0x4F;
#[entry]
fn main() -> ! {
let (i2c1, _delay, mut itm) = aux14::init();
// Stage 1: Send the address of the register we want to read to the
// magnetometer
{
// Broadcast START
// Broadcast the MAGNETOMETER address with the R/W bit set to Write
i2c1.cr2.write(|w| {
w.start().set_bit();
w.sadd1().bits(MAGNETOMETER);
w.rd_wrn().clear_bit();
w.nbytes().bits(1);
w.autoend().clear_bit()
});
// Wait until we can send more data
while i2c1.isr.read().txis().bit_is_clear() {}
// Send the address of the register that we want to read: WHO_AM_I_M
i2c1.txdr.write(|w| w.txdata().bits(WHO_AM_I_M));
// Wait until the previous byte has been transmitted
while i2c1.isr.read().tc().bit_is_clear() {}
}
// Stage 2: Receive the contents of the register we asked for
let byte = {
// Broadcast RESTART
// Broadcast the MAGNETOMETER address with the R/W bit set to Read.
i2c1.cr2.modify(|_, w| {
w.start().set_bit();
w.nbytes().bits(1);
w.rd_wrn().set_bit();
w.autoend().set_bit()
});
// Wait until we have received the contents of the register
while i2c1.isr.read().rxne().bit_is_clear() {}
// Broadcast STOP (automatic because of `AUTOEND = 1`)
i2c1.rxdr.read().rxdata().bits()
};
// Expected output: 0b01000000(64)
iprintln!(&mut itm.stim[0], "0x{:02X} - 0b{:08b}", WHO_AM_I_M, byte);
loop {}
}
Until there is a rewrite, is it possible to flag this divergence (more?) prominently in the book?
Akin to how the missing solder bridge is nice and big and hard to miss (which I still managed to miss on first read).
@waalge Sure thing. We would be glad about PRs doing that.
Happy to do more with instruction/ pointers.
When does the book get rebuilt so that changes appear?
The one hosted at https://rust-embedded.github.io/discovery should be rebuilt shortly after each PR merge. The one hosted at https://docs.rust-embedded.org/discovery is a bit outdated because our CI for it is currently broken. We recently had some discussion about solving this and only publishing one version.
Additionally, to make the
LSM303AGR
works like in the section 14.5 of the discovery book, you have to change its system mode from defaultidle
tocontinuous
mode by modifying the first two bits of the registerCFG_REG_A_M
via the I2C as well. Otherwise, your(x, y, z)
value would probably stuck at some values! Hope this help someone.
Indeed this helped me get to a solution for the LSM303AGR
.
Here's my implementation for reading the magnetometer continuously.
#![deny(unsafe_code)]
#![no_main]
#![no_std]
#[allow(unused_imports)]
use aux14::{entry, iprint, iprintln, prelude::*};
use aux14::i2c1::RegisterBlock;
// Slave address
const MAGNETOMETER: u8 = 0b001_1110;
// Addresses of the magnetometer's registers
const OUT_X_H_M: u8 = 0x03;
const IRA_REG_M: u8 = 0x0A;
const WHO_AM_I_M: u8 = 0x4F;
const CFG_REG_A_M: u8 = 0x60;
const OUTX_L_REG_M: u8 = 0x068;
const OUT_X_ACC: u8 = 0x28;
fn set_mode_continuous__LSM303AGR(i2c1: &RegisterBlock) -> (u8) {
{
// Broadcast START
// Broadcast the MAGNETOMETER address with the R/W bit set to Write
i2c1.cr2.write(|w| {
w.start().set_bit();
w.sadd1().bits(MAGNETOMETER);
w.rd_wrn().clear_bit();
w.nbytes().bits(2);
w.autoend().clear_bit()
});
// Wait until we can send more data
while i2c1.isr.read().txis().bit_is_clear() {}
// Send the address of the register that we want to read: WHO_AM_I_M
i2c1.txdr.write(|w| w.txdata().bits(CFG_REG_A_M));
i2c1.txdr.write(|w| w.txdata().bits(0x0));
// Wait until the previous byte has been transmitted
while i2c1.isr.read().tc().bit_is_clear() {}
}
let cfg_reg_a_m_byte = {
// Broadcast RESTART
// Broadcast the MAGNETOMETER address with the R/W bit set to Read.
i2c1.cr2.modify(|_, w| {
w.start().set_bit();
w.nbytes().bits(1);
w.rd_wrn().set_bit();
w.autoend().set_bit()
});
// Wait until we have received the contents of the register
while i2c1.isr.read().rxne().bit_is_clear() {}
// Broadcast STOP (automatic because of `AUTOEND = 1`)
i2c1.rxdr.read().rxdata().bits()
};
cfg_reg_a_m_byte
}
fn who_am_i__LSM303AGR(i2c1: &RegisterBlock) -> u8 {
// Stage 1: Send the address of the register we want to read to the
// magnetometer
{
// Broadcast START
// Broadcast the MAGNETOMETER address with the R/W bit set to Write
i2c1.cr2.write(|w| {
w.start().set_bit();
w.sadd1().bits(MAGNETOMETER);
w.rd_wrn().clear_bit();
w.nbytes().bits(1);
w.autoend().clear_bit()
});
// Wait until we can send more data
while i2c1.isr.read().txis().bit_is_clear() {}
// Send the address of the register that we want to read: WHO_AM_I_M
i2c1.txdr.write(|w| w.txdata().bits(WHO_AM_I_M));
// Wait until the previous byte has been transmitted
while i2c1.isr.read().tc().bit_is_clear() {}
}
// Stage 2: Receive the contents of the register we asked for
let byte = {
// Broadcast RESTART
// Broadcast the MAGNETOMETER address with the R/W bit set to Read.
i2c1.cr2.modify(|_, w| {
w.start().set_bit();
w.nbytes().bits(1);
w.rd_wrn().set_bit();
w.autoend().set_bit()
});
// Wait until we have received the contents of the register
while i2c1.isr.read().rxne().bit_is_clear() {}
// Broadcast STOP (automatic because of `AUTOEND = 1`)
i2c1.rxdr.read().rxdata().bits()
};
byte
}
#[entry]
fn main() -> ! {
let (i2c1, _delay, mut itm) = aux14::init();
let cfg_reg_a_m_byte: u8 = set_mode_continuous__LSM303AGR(i2c1);
// Expected output: 0x60 - 0b00000000
iprintln!(&mut itm.stim[0], "0x{:02X} - 0b{:08b}", CFG_REG_A_M, cfg_reg_a_m_byte);
let whoami: u8 = who_am_i__LSM303AGR(i2c1);
// Expected output: 0x4F - 0b01000000
iprintln!(&mut itm.stim[0], "0x{:02X} - 0b{:08b}", WHO_AM_I_M, whoami);
// sample magnetometer
loop{
// ask for an array of 6 register values starting at OUTX_L_REG_M (0x68)
{
// Broadcast START
// Broadcast the MAGNETOMETER address with the R/W bit set to Write
i2c1.cr2.write(|w| {
w.start().set_bit();
w.sadd1().bits(MAGNETOMETER);
w.rd_wrn().clear_bit();
w.nbytes().bits(1);
w.autoend().clear_bit()
});
// Wait until we can send more data
while i2c1.isr.read().txis().bit_is_clear() {}
// Send the address of the register that we want to read: WHO_AM_I_M
i2c1.txdr.write(|w| w.txdata().bits(OUTX_L_REG_M));
// Wait until the previous byte has been transmitted
while i2c1.isr.read().tc().bit_is_clear() {}
}
i2c1.cr2.modify(|_, w| {
w.start().set_bit();
w.nbytes().bits(6);
w.rd_wrn().set_bit();
w.autoend().set_bit()
});
let mut buffer = [0u8; 6];
for byte in &mut buffer {
// Wait until we have received something
while i2c1.isr.read().rxne().bit_is_clear() {}
*byte = i2c1.rxdr.read().rxdata().bits();
}
// Broadcast STOP (automatic because of `AUTOEND = 1`)
iprintln!(&mut itm.stim[0], "{:?}", buffer);
}
}
Also note that the code to convert from the 6 register readings to 3 x i16 have the LSB/MSB order mixed up it should be LSB first :
let x_l = u16::from(buffer[0]);
let x_h = u16::from(buffer[1]);
let z_l = u16::from(buffer[2]);
let z_h = u16::from(buffer[3]);
let y_l = u16::from(buffer[4]);
let y_h = u16::from(buffer[5]);
let x = ((x_h << 8) + x_l) as i16;
let y = ((y_h << 8) + y_l) as i16;
let z = ((z_h << 8) + z_l) as i16;
@folknology thanks!
I'm seeing that the registers are in order x, then y, then z. This is giving me the results I'm getting from using the lsm303agr
crate in ch 15.
Hi, I suspect that I have a board with LSM303AGR
. I copy-pasted the 14.4 solution and am still getting panics in itmdump
, and it is related to Lsm303dlhc::new
(error message below).
panicked at 'called `Result::unwrap()` on an `Err` value: Arbitration', src/14-i2c/auxiliary/src/lib.rs:37:26
The book points to this thread but there don't seem to be any solutions to this problem here. What should be modified? Looking at the lsm303agr
crate it looks like there will need to be pretty big changes? Sorry, I have no experience with embedded programming so I am pretty clueless. Thanks.
Edit: Ok, took a while but I managed to figure something out. Not sure if this is the appropriate place to post this, but hopefully it helps someone. I was using the lsm303agr
crate but that gave Comm(Arbitration)
errors. I rewrote everything and am now able to get data from the magnetometer.
In Cargo.toml
:
[dependencies]
cortex-m = "0.7"
cortex-m-rt = { version = "0.7", features = ["device"] }
panic-itm = "0.4"
stm32f3xx-hal = { version = "0.8", features = ["ld", "rt", "stm32f303xc"] }
lsm303agr = "0.2"
In src/main.rs
:
#![deny(unsafe_code)]
#![no_std]
#![no_main]
use core::convert::TryInto;
use cortex_m::{asm, iprintln};
use cortex_m_rt::entry;
use lsm303agr::Lsm303agr;
use panic_itm as _;
use stm32f3xx_hal::{self as hal, pac, prelude::*};
#[entry]
fn main() -> ! {
let cp = cortex_m::Peripherals::take().unwrap();
let mut itm = cp.ITM;
let dp = pac::Peripherals::take().unwrap();
let mut flash = dp.FLASH.constrain();
let mut rcc = dp.RCC.constrain();
let clocks = rcc.cfgr.freeze(&mut flash.acr);
let mut gpiob = dp.GPIOB.split(&mut rcc.ahb);
let mut scl =
gpiob
.pb6
.into_af4_open_drain(&mut gpiob.moder, &mut gpiob.otyper, &mut gpiob.afrl);
let mut sda =
gpiob
.pb7
.into_af4_open_drain(&mut gpiob.moder, &mut gpiob.otyper, &mut gpiob.afrl);
scl.internal_pull_up(&mut gpiob.pupdr, true);
sda.internal_pull_up(&mut gpiob.pupdr, true);
let i2c = hal::i2c::I2c::new(
dp.I2C1,
(scl, sda),
400.kHz().try_into().unwrap(),
clocks,
&mut rcc.apb1,
);
let mut sensor = Lsm303agr::new_with_i2c(i2c);
sensor
.set_mag_odr(lsm303agr::MagOutputDataRate::Hz10)
.unwrap();
loop {
match sensor.mag_data() {
Ok(val) => iprintln!(&mut itm.stim[0], "{:?}", val),
Err(_) => asm::delay(1_000_000),
}
}
}
Hello. I was working through this and got stuck here. I followed @wbuck's example above but there isn't a sadd1
anymore, just a sadd
and its bits
method takes a u16
. I tried changing the MAGNETOMETER
to a u16
but it just hangs when I run and do continue
in gdb.
@davemo88 From f3's reference manual, page: 87

// For 7 bit address, we populate bits 1 through 7 of SADD
// This gives me output that matches `lsm303agr` crate
w.sadd().bits((MAGNETOMETER << 1).into());
In the latest version the magnetometer data can be obtained correctly using the following code.
#![deny(unsafe_code)]
#![no_main]
#![no_std]
use aux14::i2c1::RegisterBlock;
#[allow(unused_imports)]
use aux14::{entry, iprint, iprintln, prelude::*};
// Slave address
const MAGNETOMETER: u16 = 0b0011_1100;
// Addresses of the magnetometer's registers
const WHO_AM_I_M: u8 = 0x4F;
const CFG_REG_A_M: u8 = 0x60;
const OUTX_L_REG_M: u8 = 0x068;
#[entry]
fn main() -> ! {
let (i2c1, mut delay, mut itm) = aux14::init();
let cfg_reg_a_m_byte: u8 = set_mode_continuous__LSM303AGR(i2c1);
// Expected output: 0x60 - 0b00000000
iprintln!(
&mut itm.stim[0],
"0x{:02X} - 0b{:08b}",
CFG_REG_A_M,
cfg_reg_a_m_byte
);
let whoami: u8 = who_am_i__LSM303AGR(i2c1);
// Expected output: 0x4F - 0b01000000
iprintln!(&mut itm.stim[0], "0x{:02X} - 0b{:08b}", WHO_AM_I_M, whoami);
loop {
// ask for an array of 6 register values starting at OUTX_L_REG_M (0x68)
{
// Broadcast START
// Broadcast the MAGNETOMETER address with the R/W bit set to Write
i2c1.cr2.write(|w| {
w.start().set_bit();
w.sadd().bits(MAGNETOMETER);
w.rd_wrn().clear_bit();
w.nbytes().bits(1);
w.autoend().clear_bit()
});
// Wait until we can send more data
while i2c1.isr.read().txis().bit_is_clear() {}
// Send the address of the register that we want to read: WHO_AM_I_M
i2c1.txdr.write(|w| w.txdata().bits(OUTX_L_REG_M));
// Wait until the previous byte has been transmitted
while i2c1.isr.read().tc().bit_is_clear() {}
}
i2c1.cr2.modify(|_, w| {
w.start().set_bit();
w.nbytes().bits(6);
w.rd_wrn().set_bit();
w.autoend().set_bit()
});
let mut buffer = [0u8; 6];
for byte in &mut buffer {
// Wait until we have received something
while i2c1.isr.read().rxne().bit_is_clear() {}
*byte = i2c1.rxdr.read().rxdata().bits();
}
// Broadcast STOP (automatic because of `AUTOEND = 1`)
iprintln!(&mut itm.stim[0], "{:?}", buffer);
let x_l = u16::from(buffer[0]);
let x_h = u16::from(buffer[1]);
let y_l = u16::from(buffer[2]);
let y_h = u16::from(buffer[3]);
let z_l = u16::from(buffer[4]);
let z_h = u16::from(buffer[5]);
let x = ((x_h << 8) + x_l) as i16;
let y = ((y_h << 8) + y_l) as i16;
let z = ((z_h << 8) + z_l) as i16;
iprintln!(&mut itm.stim[0], "{:?}", (x, y, z));
delay.delay_ms(1_000_u16);
}
}
fn who_am_i__LSM303AGR(i2c1: &RegisterBlock) -> u8 {
// Stage 1: Send the address of the register we want to read to the
// magnetometer
{
// Broadcast START
// Broadcast the MAGNETOMETER address with the R/W bit set to Write
i2c1.cr2.write(|w| {
w.start().set_bit();
w.sadd().bits(MAGNETOMETER);
w.rd_wrn().clear_bit();
w.nbytes().bits(1);
w.autoend().clear_bit()
});
// Wait until we can send more data
while i2c1.isr.read().txis().bit_is_clear() {}
// Send the address of the register that we want to read: WHO_AM_I_M
i2c1.txdr.write(|w| w.txdata().bits(WHO_AM_I_M));
// Wait until the previous byte has been transmitted
while i2c1.isr.read().tc().bit_is_clear() {}
}
// Stage 2: Receive the contents of the register we asked for
let byte = {
// Broadcast RESTART
// Broadcast the MAGNETOMETER address with the R/W bit set to Read.
i2c1.cr2.modify(|_, w| {
w.start().set_bit();
w.nbytes().bits(1);
w.rd_wrn().set_bit();
w.autoend().set_bit()
});
// Wait until we have received the contents of the register
while i2c1.isr.read().rxne().bit_is_clear() {}
// Broadcast STOP (automatic because of `AUTOEND = 1`)
i2c1.rxdr.read().rxdata().bits()
};
byte
}
fn set_mode_continuous__LSM303AGR(i2c1: &RegisterBlock) -> (u8) {
{
// Broadcast START
// Broadcast the MAGNETOMETER address with the R/W bit set to Write
i2c1.cr2.write(|w| {
w.start().set_bit();
w.sadd().bits(MAGNETOMETER);
w.rd_wrn().clear_bit();
w.nbytes().bits(2);
w.autoend().clear_bit()
});
// Wait until we can send more data
while i2c1.isr.read().txis().bit_is_clear() {}
// Send the address of the register that we want to read: WHO_AM_I_M
i2c1.txdr.write(|w| w.txdata().bits(CFG_REG_A_M));
i2c1.txdr.write(|w| w.txdata().bits(0x0));
// Wait until the previous byte has been transmitted
while i2c1.isr.read().tc().bit_is_clear() {}
}
let cfg_reg_a_m_byte = {
// Broadcast RESTART
// Broadcast the MAGNETOMETER address with the R/W bit set to Read.
i2c1.cr2.modify(|_, w| {
w.start().set_bit();
w.nbytes().bits(1);
w.rd_wrn().set_bit();
w.autoend().set_bit()
});
// Wait until we have received the contents of the register
while i2c1.isr.read().rxne().bit_is_clear() {}
// Broadcast STOP (automatic because of `AUTOEND = 1`)
i2c1.rxdr.read().rxdata().bits()
};
cfg_reg_a_m_byte
}
@al-jshen Just another datapoint: I had the same problem that you describe, but for me it was fixed simply by restarting everything (board & openocd). 🤷