#![no_std]
#[cfg(feature = "rttdebug")]
use panic_rtt_core::rprintln;
use crate::interface::SensorInterface;
use embedded_hal as hal;
use hal::blocking::delay::DelayMs;
pub mod interface;
#[derive(Debug)]
pub enum Error<CommE, PinE> {
Comm(CommE),
Pin(PinE),
OutOfRange,
Configuration,
UnknownChipId,
}
#[repr(u8)]
pub enum GainSetting {
Gain1370 = 0b00000000,
Gain1090 = 0b00100000,
Gain0820 = 0b01000000,
Gain0660 = 0b01100000,
Gain0440 = 0b10000000,
Gain0390 = 0b10100000,
Gain0330 = 0b11000000,
Gain0230 = 0b11100000,
}
#[repr(u8)]
pub enum OdrSetting {
Odr0_75Hz = 0b000,
Odr1_5Hz = 0b001,
Odr3_0Hz = 0b010,
Odr7_5Hz = 0b011,
Odr15_0Hz = 0b100,
Odr30_0Hz = 0b110,
Odr220_0Hz = 0b111,
}
#[repr(u8)]
pub enum SampleAvgSetting {
AvgSamples1 = 0b00,
AvgSamples2 = 0b01,
AvgSamples4 = 0b10,
AvgSamples8 = 0b11,
}
#[repr(u8)]
pub enum MeasurementModeSetting {
NormalMode = 0b00,
PositiveBias = 0b01,
NegativeBias = 0b10,
TemperatureOnly = 0b11,
}
pub struct HMC5983<SI> {
pub(crate) sensor_interface: SI,
block_buf: [u8; BLOCK_BUF_LEN],
}
impl<SI, CommE, PinE> HMC5983<SI>
where
SI: SensorInterface<InterfaceError = crate::Error<CommE, PinE>>,
{
pub fn new_with_interface(sensor_interface: SI) -> Self {
Self {
sensor_interface,
block_buf: [0; BLOCK_BUF_LEN],
}
}
pub fn init(
&mut self,
delay_source: &mut impl DelayMs<u8>,
) -> Result<(), crate::Error<CommE, PinE>> {
self.reset(delay_source)
}
fn reset(
&mut self,
delay_source: &mut impl DelayMs<u8>,
) -> Result<(), crate::Error<CommE, PinE>> {
for reg in 0x00..0x0D {
let _val = self.read_reg(reg)?;
#[cfg(feature = "rttdebug")]
rprintln!("0x{:0x} : {} ", reg, _val);
}
const EXPECTED_PROD_ID_A: u8 = 72; const EXPECTED_PROD_ID_B: u8 = 52; const EXPECTED_PROD_ID_C: u8 = 51; self.sensor_interface
.read_block(REG_ID_A, &mut self.block_buf[..3])?;
if self.block_buf[0] != EXPECTED_PROD_ID_A
|| self.block_buf[1] != EXPECTED_PROD_ID_B
|| self.block_buf[2] != EXPECTED_PROD_ID_C
{
#[cfg(feature = "rttdebug")]
rprintln!(
"bad ID block: {},{},{}",
self.block_buf[0],
self.block_buf[1],
self.block_buf[2]
);
return Err(Error::UnknownChipId);
}
self.set_all_config_a(
MeasurementModeSetting::NormalMode,
OdrSetting::Odr30_0Hz,
SampleAvgSetting::AvgSamples8,
true,
)?;
self.set_gain(GainSetting::Gain0820)?;
self.sensor_interface.write_reg(
REG_CONFIG_C,
MeasurementModeSetting::NormalMode as u8,
)?;
delay_source.delay_ms(100);
Ok(())
}
pub fn set_gain(
&mut self,
gain: GainSetting,
) -> Result<(), crate::Error<CommE, PinE>> {
let gain_val: u8 = gain as u8;
self.sensor_interface.write_reg(REG_CONFIG_B, gain_val)?;
let confirm_val = self.read_reg(REG_CONFIG_B)?;
if confirm_val != gain_val {
#[cfg(feature = "rttdebug")]
rprintln!("gain bad: expected {} got {}", gain_val, confirm_val);
return Err(Error::Configuration);
}
Ok(())
}
pub fn set_all_config_a(
&mut self,
mode: MeasurementModeSetting,
odr: OdrSetting,
averaging: SampleAvgSetting,
temp_enabled: bool,
) -> Result<(), crate::Error<CommE, PinE>> {
let new_val = (if temp_enabled { (1 << 7) } else { 0 })
& ((averaging as u8) << 6)
& ((odr as u8) << 4)
& ((mode as u8) << 2);
self.sensor_interface.write_reg(REG_CONFIG_A, new_val)
}
fn read_reg(&mut self, reg: u8) -> Result<u8, crate::Error<CommE, PinE>> {
self.sensor_interface
.read_block(reg, &mut self.block_buf[..1])?;
Ok(self.block_buf[0])
}
fn raw_reading_to_i16(buf: &[u8], idx: usize) -> i16 {
let val: i16 = (buf[idx] as i16) | ((buf[idx + 1] as i16) << 8);
val
}
pub fn get_mag_vector(
&mut self,
) -> Result<[i16; 3], crate::Error<CommE, PinE>> {
const XYZ_DATA_LEN: usize = 6;
self.sensor_interface.read_block(
REG_MAG_DATA_START,
&mut self.block_buf[..XYZ_DATA_LEN],
)?;
let sample_i16 = [
Self::raw_reading_to_i16(&self.block_buf, 0),
Self::raw_reading_to_i16(&self.block_buf, 2),
Self::raw_reading_to_i16(&self.block_buf, 4),
];
Ok(sample_i16)
}
pub fn get_temperature(
&mut self,
) -> Result<i16, crate::Error<CommE, PinE>> {
const TEMP_DATA_LEN: usize = 2;
self.sensor_interface.read_block(
REG_TEMP_OUTPUT_MSB,
&mut self.block_buf[..TEMP_DATA_LEN],
)?;
let celsius = (((self.block_buf[0] as i16) * 256)
+ (self.block_buf[1] as i16))
/ 128
+ 25;
Ok(celsius)
}
}
const REG_CONFIG_A: u8 = 0x00;
const REG_CONFIG_B: u8 = 0x01;
const REG_CONFIG_C: u8 = 0x02;
const REG_DATA_X: u8 = 0x03;
const REG_MAG_DATA_START: u8 = REG_DATA_X;
const REG_ID_A: u8 = 0x0A;
const REG_TEMP_OUTPUT_MSB: u8 = 0x31;
const BLOCK_BUF_LEN: usize = 32;