initial commit
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target
|
||||||
80
Cargo.lock
generated
Normal file
80
Cargo.lock
generated
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "2.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "embedded-hal"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "embedded-hal-async"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0c4c685bbef7fe13c3c6dd4da26841ed3980ef33e841cddfa15ce8a8fb3f1884"
|
||||||
|
dependencies = [
|
||||||
|
"embedded-hal",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "maybe-async"
|
||||||
|
version = "0.2.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5cf92c10c7e361d6b99666ec1c6f9805b0bea2c3bd8c78dc6fe98ac5bd78db11"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mmc56x3"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"embedded-hal",
|
||||||
|
"embedded-hal-async",
|
||||||
|
"maybe-async",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.106"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.44"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.114"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.23"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "537dd038a89878be9b64dd4bd1b260315c1bb94f4d784956b81e27a088d9a09e"
|
||||||
15
Cargo.toml
Normal file
15
Cargo.toml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
[package]
|
||||||
|
name = "mmc56x3"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
embedded-hal-async = { version = "1.0.0" }
|
||||||
|
maybe-async = { version = "0.2.10" }
|
||||||
|
embedded-hal = { version = "1.0.0", optional = true }
|
||||||
|
bitflags = { version = "2.10.0", default-features = false }
|
||||||
|
|
||||||
|
|
||||||
|
[features]
|
||||||
|
sync = ["embedded-hal", "maybe-async/is_sync"]
|
||||||
|
|
||||||
7
README.md
Normal file
7
README.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
A Rust driver for the [MMC56X3](https://www.adafruit.com/product/5579) Triple-axis Magnetometer.
|
||||||
|
|
||||||
|
Based on the [arduino driver](https://github.com/adafruit/Adafruit_MMC56x3).
|
||||||
|
|
||||||
|
# Usage
|
||||||
|
|
||||||
|
See [examples](./example). Also make sure to check the [datasheet](https://cdn-learn.adafruit.com/assets/assets/000/113/957/original/MMC5603NJ_RevB_7-12-18.pdf).
|
||||||
8
example/rp2040_embassy/.cargo/config.toml
Normal file
8
example/rp2040_embassy/.cargo/config.toml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||||
|
runner = "picotool load --update --verify --execute -t elf"
|
||||||
|
|
||||||
|
[build]
|
||||||
|
target = "thumbv6m-none-eabi"
|
||||||
|
|
||||||
|
[env]
|
||||||
|
DEFMT_LOG = "debug"
|
||||||
1
example/rp2040_embassy/.gitignore
vendored
Normal file
1
example/rp2040_embassy/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target
|
||||||
1437
example/rp2040_embassy/Cargo.lock
generated
Normal file
1437
example/rp2040_embassy/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
23
example/rp2040_embassy/Cargo.toml
Normal file
23
example/rp2040_embassy/Cargo.toml
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
[package]
|
||||||
|
name = "rp2040_embassy"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
embassy-embedded-hal = { version = "0.5.0" }
|
||||||
|
embassy-sync = { version = "0.7.2", features = ["log"] }
|
||||||
|
embassy-executor = { version = "0.9.1" , features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "log"] }
|
||||||
|
embassy-time = { version = "0.5.0", features = ["log"] }
|
||||||
|
embassy-rp = { version = "0.9.0" , features = ["log", "time-driver", "critical-section-impl", "rp2040"] }
|
||||||
|
embassy-futures = { version = "0.1.2" }
|
||||||
|
embassy-usb-logger = { version = "0.5.1" }
|
||||||
|
embassy-usb = { version = "0.5.1", features = ["log"] }
|
||||||
|
|
||||||
|
cortex-m = { version = "0.7.7", features = ["inline-asm"] }
|
||||||
|
cortex-m-rt = "0.7.5"
|
||||||
|
critical-section = "1.2.0"
|
||||||
|
heapless = "0.9.2"
|
||||||
|
|
||||||
|
log = "0.4"
|
||||||
|
|
||||||
|
mmc56x3 = { path = "../../" }
|
||||||
21
example/rp2040_embassy/build.rs
Normal file
21
example/rp2040_embassy/build.rs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
use std::env;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::Write;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// Put `memory.x` in our output directory and ensure it's
|
||||||
|
// on the linker search path.
|
||||||
|
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
|
||||||
|
File::create(out.join("memory.x"))
|
||||||
|
.unwrap()
|
||||||
|
.write_all(include_bytes!("memory.x"))
|
||||||
|
.unwrap();
|
||||||
|
println!("cargo:rustc-link-search={}", out.display());
|
||||||
|
|
||||||
|
println!("cargo:rerun-if-changed=memory.x");
|
||||||
|
|
||||||
|
println!("cargo:rustc-link-arg-bins=--nmagic");
|
||||||
|
println!("cargo:rustc-link-arg-bins=-Tlink.x");
|
||||||
|
println!("cargo:rustc-link-arg-bins=-Tlink-rp.x");
|
||||||
|
}
|
||||||
5
example/rp2040_embassy/memory.x
Normal file
5
example/rp2040_embassy/memory.x
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
MEMORY {
|
||||||
|
BOOT2 : ORIGIN = 0x10000000, LENGTH = 0x100
|
||||||
|
FLASH : ORIGIN = 0x10000100, LENGTH = 2048K - 0x100
|
||||||
|
RAM : ORIGIN = 0x20000000, LENGTH = 264K
|
||||||
|
}
|
||||||
76
example/rp2040_embassy/src/main.rs
Normal file
76
example/rp2040_embassy/src/main.rs
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use embassy_executor::Spawner;
|
||||||
|
use embassy_rp::{
|
||||||
|
bind_interrupts,
|
||||||
|
i2c::{self, I2c},
|
||||||
|
peripherals::{self, USB},
|
||||||
|
usb::Driver,
|
||||||
|
};
|
||||||
|
use embassy_time::{Delay, Timer};
|
||||||
|
use log::{error, info};
|
||||||
|
use mmc56x3::MMC56X3;
|
||||||
|
|
||||||
|
#[panic_handler]
|
||||||
|
fn panic(info: &core::panic::PanicInfo) -> ! {
|
||||||
|
for _ in 0..20 {
|
||||||
|
error!("PANIC: {info}");
|
||||||
|
}
|
||||||
|
info!("Doing cold boot from panic");
|
||||||
|
embassy_rp::rom_data::reset_to_usb_boot(0, 0);
|
||||||
|
|
||||||
|
loop {}
|
||||||
|
}
|
||||||
|
|
||||||
|
bind_interrupts!(struct Irqs {
|
||||||
|
USBCTRL_IRQ => embassy_rp::usb::InterruptHandler<USB>;
|
||||||
|
I2C0_IRQ => i2c::InterruptHandler<peripherals::I2C0>;
|
||||||
|
});
|
||||||
|
|
||||||
|
#[embassy_executor::task]
|
||||||
|
async fn logger_task(driver: Driver<'static, USB>) {
|
||||||
|
embassy_usb_logger::run!(1024, log::LevelFilter::Info, driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[embassy_executor::main]
|
||||||
|
async fn main(spawner: Spawner) {
|
||||||
|
let p = embassy_rp::init(Default::default());
|
||||||
|
|
||||||
|
let driver = Driver::new(p.USB, Irqs);
|
||||||
|
spawner.must_spawn(logger_task(driver));
|
||||||
|
|
||||||
|
let sda = p.PIN_4;
|
||||||
|
let scl = p.PIN_5;
|
||||||
|
|
||||||
|
let i2c = I2c::new_async(p.I2C0, scl, sda, Irqs, i2c::Config::default());
|
||||||
|
|
||||||
|
let mut device = MMC56X3::new(i2c, Delay);
|
||||||
|
device.init().await.expect("Failed to init");
|
||||||
|
|
||||||
|
device
|
||||||
|
.set_data_rate(mmc56x3::DataRate::Hz(100))
|
||||||
|
.await
|
||||||
|
.expect("Failed to set data rate");
|
||||||
|
|
||||||
|
device
|
||||||
|
.set_continuous_mode(true)
|
||||||
|
.await
|
||||||
|
.expect("Failed to set continuous mode");
|
||||||
|
|
||||||
|
for _ in 0..20 {
|
||||||
|
Timer::after_secs(1).await;
|
||||||
|
|
||||||
|
// let result = device.read_temperature().await;
|
||||||
|
// device.trigger_messurement().await.expect("Failed to trigger trigger_messurement");
|
||||||
|
|
||||||
|
match device.read_messurement().await {
|
||||||
|
Ok(d) => info!("Got: {:?}", d),
|
||||||
|
Err(e) => error!("Error: {:?}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
info!("Doing cold boot");
|
||||||
|
Timer::after_secs(1).await;
|
||||||
|
embassy_rp::rom_data::reset_to_usb_boot(0, 0);
|
||||||
|
}
|
||||||
405
src/lib.rs
Normal file
405
src/lib.rs
Normal file
@@ -0,0 +1,405 @@
|
|||||||
|
#![no_std]
|
||||||
|
|
||||||
|
#[cfg(feature = "sync")]
|
||||||
|
use embedded_hal::i2c::{self};
|
||||||
|
|
||||||
|
use embedded_hal_async::delay::DelayNs;
|
||||||
|
#[cfg(not(feature = "sync"))]
|
||||||
|
use embedded_hal_async::i2c::{self};
|
||||||
|
|
||||||
|
use bitflags::bitflags;
|
||||||
|
|
||||||
|
const REG_XOUT_0: u8 = 0x00;
|
||||||
|
const REG_XOUT_1: u8 = 0x01;
|
||||||
|
const REG_YOUT_0: u8 = 0x02;
|
||||||
|
const REG_YOUT_1: u8 = 0x03;
|
||||||
|
const REG_ZOUT_0: u8 = 0x04;
|
||||||
|
const REG_ZOUT_1: u8 = 0x05;
|
||||||
|
const REG_XOUT_2: u8 = 0x06;
|
||||||
|
const REG_YOUT_2: u8 = 0x07;
|
||||||
|
const REG_ZOUT_2: u8 = 0x08;
|
||||||
|
const REG_TOUT: u8 = 0x09;
|
||||||
|
const REG_STATUS1: u8 = 0x18;
|
||||||
|
const REG_ODR: u8 = 0x1A;
|
||||||
|
const REG_CONTROL0: u8 = 0x1B;
|
||||||
|
const REG_CONTROL1: u8 = 0x1C;
|
||||||
|
const REG_CONTROL2: u8 = 0x1D;
|
||||||
|
const REG_PRODUCT_ID: u8 = 0x39;
|
||||||
|
|
||||||
|
const DEFAULT_ADDRESS: u8 = 0x30;
|
||||||
|
|
||||||
|
const DEVICE_ID: u8 = 0x10;
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
/// Flags for status_1 register
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
struct StatusRegisterFlags: u8 {
|
||||||
|
/// This bit is an indicator of successfully reading its OTP memory either as part of its power up
|
||||||
|
/// sequence, or after an I2C command that reloads the OTP memory, such as resetting the chip
|
||||||
|
/// and refreshing the OTP registers
|
||||||
|
const OTP_READ_DONE = 0b0001_0000;
|
||||||
|
|
||||||
|
/// This bit is an indicator of the selftest signal, it keeps low once the device PASS selftest.
|
||||||
|
const SAT_SENSOR = 0b0010_0000;
|
||||||
|
|
||||||
|
/// This bit indicates that a measurement of magnetic field is done and the data is ready to be
|
||||||
|
/// read. This bit is reset only when any of the magnetic data registers is read.
|
||||||
|
const MESSUREMENT_M_DONE = 0b0100_0000;
|
||||||
|
|
||||||
|
/// This bit indicates that a measurement of temperature is done and the data is ready to be read.
|
||||||
|
/// This bit is reset only when the temperature register is read.
|
||||||
|
const MESSUREMENT_T_DONE = 0b1000_0000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
/// Flags for the control 0 register
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
struct Control0RegisterFlags: u8 {
|
||||||
|
/// Take Measure of Magnetic field, or TM_M bit. Writing a 1 into this location causes the chip to
|
||||||
|
/// perform a magnetic measurement. This bit is self-clearing at the end of each measurement.
|
||||||
|
const TAKE_MESSUREMENT_M = 0b0000_0001;
|
||||||
|
|
||||||
|
/// Take Measure of Temperature, or TM_T bit. Writing a 1 into this location causes the chip to
|
||||||
|
/// perform a temperature measurement. This bit is self-clearing at the end of each measurement.
|
||||||
|
const TAKE_MESSUREMENT_T = 0b0000_0010;
|
||||||
|
|
||||||
|
/// Writing a 1 into this location will cause the chip to do the Set operation, which will allow large set
|
||||||
|
/// current to flow through the sensor coils for 375ns. This bit is self-cleared at the end of Set operation.
|
||||||
|
const DO_SET = 0b0000_1000;
|
||||||
|
|
||||||
|
/// Writing a 1 into this location will cause the chip to do the Reset operation, which will allow large
|
||||||
|
/// reset current to flow through the sensor coils for 375ns. This bit is self-cleared at the end of Reset operation.
|
||||||
|
const DO_RESET = 0b0001_0000;
|
||||||
|
|
||||||
|
/// Writing a 1 into this location will enable the function of automatic set/reset. This function applies
|
||||||
|
/// to both on-demand and continuous-time measurements. This bit must be set to 1 in order to
|
||||||
|
/// activate the feature of periodic set. This bit is recommended to set to “1” in the application.
|
||||||
|
const AUTO_SET_RESET_EN = 0b0010_0000;
|
||||||
|
|
||||||
|
/// Writing a 1 into this location will enable the function of automatic self-test. The threshold in
|
||||||
|
/// register 1EH, 1FH, 20H should be set before this bit is set to 1. This bit clears itself after the
|
||||||
|
/// operation is completed.
|
||||||
|
const AUTO_SELF_TEST_EN = 0b0100_0000;
|
||||||
|
|
||||||
|
/// Writing a 1 into this location will start the calculation of the measurement period according to the
|
||||||
|
/// ODR. This bit should be set before continuous-mode measurements are started. This bit is self-
|
||||||
|
/// cleared after the measurement period is calculated by internal circuits.
|
||||||
|
const CMM_FRE_EN = 0b1000_0000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
/// Flags for the control 1 register
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
struct Control1RegisterFlags: u8 {
|
||||||
|
const BANDWIDTH_0 = 0b0000_0001;
|
||||||
|
const BANDWIDTH_1 = 0b0000_0010;
|
||||||
|
|
||||||
|
/// Writing “1” will disable this channel, and reduce Measurement Time and total charge per
|
||||||
|
/// measurement.When a channel is disabled it is simply skipped during Take Measure routine. Its
|
||||||
|
/// output register is not reset and will maintain the last value written to it when this channel was
|
||||||
|
/// active.
|
||||||
|
const X_INHIBI = 0b0000_0100;
|
||||||
|
|
||||||
|
/// Writing “1” will disable this channel, and reduce Measurement Time and total charge per
|
||||||
|
/// measurement.When a channel is disabled it is simply skipped during Take Measure routine. Its
|
||||||
|
/// output register is not reset and will maintain the last value written to it when this channel was
|
||||||
|
/// active. Note: Y/Z needs to be inhibited the same time in case needed.
|
||||||
|
const Y_INHIBIT = 0b0000_1000;
|
||||||
|
|
||||||
|
/// Writing “1” will disable this channel, and reduce Measurement Time and total charge per
|
||||||
|
/// measurement.When a channel is disabled it is simply skipped during Take Measure routine. Its
|
||||||
|
/// output register is not reset and will maintain the last value written to it when this channel was
|
||||||
|
/// active. Note: Y/Z needs to be inhibited the same time in case needed.
|
||||||
|
const Z_INHIBIT = 0b0001_0000;
|
||||||
|
|
||||||
|
/// Writing 1 into this location will bring a DC current through the self-test coil of the sensor. This
|
||||||
|
/// current will cause an offset of the magnetic field. This function is used to check whether the
|
||||||
|
/// sensor has been saturated.
|
||||||
|
const ST_ENP = 0b0010_0000;
|
||||||
|
|
||||||
|
/// The function of this bit is similar to ST_ENP, but the offset of the magnetic field is of opposite
|
||||||
|
/// polarity.
|
||||||
|
const ST_ENM = 0b0100_0000;
|
||||||
|
|
||||||
|
/// Software Reset. Writing “1”will cause the part to reset, similar to power-up. It will clear all registers
|
||||||
|
/// and also re-read OTP as part of its startup routine. The power on time is 20mS
|
||||||
|
const SW_RESET = 0b1000_0000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
/// Flags for the control 2 register
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
struct Control2RegisterFlags: u8 {
|
||||||
|
const PDR_0 = 0b0000_0001;
|
||||||
|
const PDR_1 = 0b0000_0010;
|
||||||
|
const PDR_2 = 0b0000_0100;
|
||||||
|
|
||||||
|
/// Writing 1 into this location will enable the function of periodical set.
|
||||||
|
const EN_PRD_SET = 0b0000_1000;
|
||||||
|
|
||||||
|
/// The device will enter continuous mode, if ODR has been set to a non-zero value and a 1 has
|
||||||
|
/// been written into Cmm_freq_en. The internal counter will start counting as well since this bit
|
||||||
|
/// is set.
|
||||||
|
const CMM_EN = 0b0001_0000;
|
||||||
|
|
||||||
|
/// If this bit is set to 1 to achieve 1000Hz ODR.
|
||||||
|
const HPOWER = 0b1000_0000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct MagneticMessurement {
|
||||||
|
/// X-Axis in µT
|
||||||
|
pub x: f32,
|
||||||
|
|
||||||
|
/// Y-Axis in µT
|
||||||
|
pub y: f32,
|
||||||
|
|
||||||
|
/// Z-Axis in µT
|
||||||
|
pub z: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// At what rate
|
||||||
|
pub enum DataRate {
|
||||||
|
Hz(u8),
|
||||||
|
Max1000Hz,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Error<E> {
|
||||||
|
I2c(E),
|
||||||
|
Timeout,
|
||||||
|
NotAvailableInContinuousMode,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E> From<E> for Error<E> {
|
||||||
|
fn from(e: E) -> Self {
|
||||||
|
Error::I2c(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The MMC56X3 sensor
|
||||||
|
pub struct MMC56X3<I, D>
|
||||||
|
where
|
||||||
|
I: i2c::I2c,
|
||||||
|
D: DelayNs,
|
||||||
|
{
|
||||||
|
i2c: I,
|
||||||
|
delay: D,
|
||||||
|
is_continuous_mode: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I, D> MMC56X3<I, D>
|
||||||
|
where
|
||||||
|
I: i2c::I2c,
|
||||||
|
D: DelayNs,
|
||||||
|
{
|
||||||
|
pub fn new(i2c: I, delay: D) -> Self {
|
||||||
|
Self {
|
||||||
|
i2c,
|
||||||
|
delay,
|
||||||
|
is_continuous_mode: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn init(&mut self) -> Result<(), Error<I::Error>> {
|
||||||
|
self.reset().await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resets the sensor to an initial state
|
||||||
|
pub async fn reset(&mut self) -> Result<(), Error<I::Error>> {
|
||||||
|
self.write_reg_controll_1(Control1RegisterFlags::SW_RESET)
|
||||||
|
.await?;
|
||||||
|
self.delay.delay_ms(20).await; // According to the datasheet power on time is 20ms
|
||||||
|
|
||||||
|
self.magnet_set_reset().await?;
|
||||||
|
self.set_continuous_mode(false).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pulse large currents through the sense coils to clear any offset
|
||||||
|
pub async fn magnet_set_reset(&mut self) -> Result<(), Error<I::Error>> {
|
||||||
|
self.write_reg_controll_0(Control0RegisterFlags::DO_SET)
|
||||||
|
.await?;
|
||||||
|
self.delay.delay_ns(375).await; // According to the datasheet this is how long it takes.
|
||||||
|
self.write_reg_controll_0(Control0RegisterFlags::empty())
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn set_continuous_mode(&mut self, enable: bool) -> Result<(), Error<I::Error>> {
|
||||||
|
if enable {
|
||||||
|
self.write_reg_controll_0(Control0RegisterFlags::CMM_FRE_EN)
|
||||||
|
.await?;
|
||||||
|
self.write_reg_controll_2(Control2RegisterFlags::CMM_EN)
|
||||||
|
.await?;
|
||||||
|
self.is_continuous_mode = true;
|
||||||
|
} else {
|
||||||
|
self.write_reg_controll_2(Control2RegisterFlags::empty())
|
||||||
|
.await?;
|
||||||
|
self.is_continuous_mode = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn set_data_rate(&mut self, rate: DataRate) -> Result<(), Error<I::Error>> {
|
||||||
|
match rate {
|
||||||
|
DataRate::Hz(hz) => {
|
||||||
|
self.write_reg_odr(hz).await?;
|
||||||
|
self.write_reg_controll_2(Control2RegisterFlags::empty())
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
DataRate::Max1000Hz => {
|
||||||
|
self.write_reg_odr(255).await?;
|
||||||
|
self.write_reg_controll_2(Control2RegisterFlags::HPOWER)
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read temperature in Celcius with steps of 0.8 C
|
||||||
|
pub async fn read_temperature(&mut self) -> Result<f32, Error<I::Error>> {
|
||||||
|
if self.is_continuous_mode {
|
||||||
|
return Err(Error::NotAvailableInContinuousMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.write_reg_controll_0(Control0RegisterFlags::TAKE_MESSUREMENT_T)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
self.wait_for_status_flag(StatusRegisterFlags::MESSUREMENT_T_DONE)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let t_out = self.read_reg_temperature().await?;
|
||||||
|
|
||||||
|
// The start of the temperature range starts a -75c
|
||||||
|
let temperature = -75.0 + (t_out as f32 * 0.8);
|
||||||
|
|
||||||
|
Ok(temperature)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn read_messurement(&mut self) -> Result<MagneticMessurement, Error<I::Error>> {
|
||||||
|
let mut data = [0u8; 9];
|
||||||
|
self.read_registers(REG_XOUT_0, &mut data).await?;
|
||||||
|
|
||||||
|
let x = ((data[0] as u32) << 12) | ((data[1] as u32) << 4) | ((data[6] as u32) >> 4);
|
||||||
|
let y = ((data[2] as u32) << 12) | ((data[3] as u32) << 4) | ((data[7] as u32) >> 4);
|
||||||
|
let z = ((data[4] as u32) << 12) | ((data[5] as u32) << 4) | ((data[8] as u32) >> 4);
|
||||||
|
|
||||||
|
const OFFSET: u32 = 1 << 19;
|
||||||
|
|
||||||
|
let x = x.wrapping_sub(OFFSET) as i32;
|
||||||
|
let y = y.wrapping_sub(OFFSET) as i32;
|
||||||
|
let z = z.wrapping_sub(OFFSET) as i32;
|
||||||
|
|
||||||
|
// Apply resolution. At 20 Bit mode.
|
||||||
|
const RESOLUTION: f32 = 0.00625;
|
||||||
|
|
||||||
|
let fx: f32 = x as f32 * RESOLUTION;
|
||||||
|
let fy: f32 = y as f32 * RESOLUTION;
|
||||||
|
let fz: f32 = z as f32 * RESOLUTION;
|
||||||
|
|
||||||
|
Ok(MagneticMessurement {
|
||||||
|
x: fx,
|
||||||
|
y: fy,
|
||||||
|
z: fz,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn trigger_messurement(&mut self) -> Result<(), Error<I::Error>> {
|
||||||
|
self.write_reg_controll_0(Control0RegisterFlags::TAKE_MESSUREMENT_M)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
self.wait_for_status_flag(StatusRegisterFlags::MESSUREMENT_M_DONE)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub async fn read_product_id(&mut self) -> Result<u8, Error<I::Error>> {
|
||||||
|
self.read_register(REG_PRODUCT_ID).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn wait_for_status_flag(
|
||||||
|
&mut self,
|
||||||
|
flag: StatusRegisterFlags,
|
||||||
|
) -> Result<(), Error<I::Error>> {
|
||||||
|
for _ in 0..300 {
|
||||||
|
let status = self.read_reg_status().await?;
|
||||||
|
if status.contains(flag) {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
self.delay.delay_ms(5).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(Error::Timeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
async fn read_reg_temperature(&mut self) -> Result<u8, Error<I::Error>> {
|
||||||
|
self.read_register(REG_TOUT).await
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
async fn read_reg_status(&mut self) -> Result<StatusRegisterFlags, Error<I::Error>> {
|
||||||
|
Ok(StatusRegisterFlags::from_bits_truncate(
|
||||||
|
self.read_register(REG_STATUS1).await?,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
async fn write_reg_odr(&mut self, data: u8) -> Result<(), Error<I::Error>> {
|
||||||
|
self.write_register(REG_ODR, data).await
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
async fn write_reg_controll_0(
|
||||||
|
&mut self,
|
||||||
|
value: Control0RegisterFlags,
|
||||||
|
) -> Result<(), Error<I::Error>> {
|
||||||
|
self.write_register(REG_CONTROL0, value.bits()).await
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
async fn write_reg_controll_1(
|
||||||
|
&mut self,
|
||||||
|
value: Control1RegisterFlags,
|
||||||
|
) -> Result<(), Error<I::Error>> {
|
||||||
|
self.write_register(REG_CONTROL1, value.bits()).await
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
async fn write_reg_controll_2(
|
||||||
|
&mut self,
|
||||||
|
value: Control2RegisterFlags,
|
||||||
|
) -> Result<(), Error<I::Error>> {
|
||||||
|
self.write_register(REG_CONTROL2, value.bits()).await
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
async fn write_register(&mut self, reg: u8, value: u8) -> Result<(), Error<I::Error>> {
|
||||||
|
self.i2c.write(DEFAULT_ADDRESS, &[reg, value]).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn read_register(&mut self, reg: u8) -> Result<u8, Error<I::Error>> {
|
||||||
|
let mut data = [0u8; 1];
|
||||||
|
self.i2c
|
||||||
|
.write_read(DEFAULT_ADDRESS, &[reg], &mut data)
|
||||||
|
.await?;
|
||||||
|
Ok(data[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
async fn read_registers(&mut self, reg: u8, buffer: &mut [u8]) -> Result<(), Error<I::Error>> {
|
||||||
|
self.i2c.write_read(DEFAULT_ADDRESS, &[reg], buffer).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user