#![no_std]
#![deny(missing_docs)]
#![allow(clippy::trivially_copy_pass_by_ref, clippy::new_ret_no_self)]
use core::mem;
use cast::{i16, i32, u16, u32};
use generic_array::typenum::consts::*;
use generic_array::{ArrayLength, GenericArray};
use hal::blocking::delay::DelayUs;
use hal::blocking::i2c::{Write, WriteRead};
#[allow(clippy::unreadable_literal)]
const ADDRESS: u8 = 0b1110111;
#[allow(non_camel_case_types)]
#[derive(Copy, Clone)]
enum Register {
COEFF_AC1 = 0xAA,
COEFF_AC2 = 0xAC,
COEFF_AC3 = 0xAE,
COEFF_AC4 = 0xB0,
COEFF_AC5 = 0xB2,
COEFF_AC6 = 0xB4,
COEFF_B1 = 0xB6,
COEFF_B2 = 0xB8,
COEFF_MB = 0xBA,
COEFF_MC = 0xBC,
COEFF_MD = 0xBE,
CONTROL_REG = 0xF4,
VALUE_REG = 0xF6,
}
impl Register {
pub fn addr(&self) -> u8 {
*self as u8
}
}
#[allow(non_camel_case_types)]
#[derive(Copy, Clone)]
enum ControlRegisterValue {
CONTROL_VALUE_TEMPERATURE = 0x2E, CONTROL_VALUE_PRESSURE_OSRS_0 = 0x34, CONTROL_VALUE_PRESSURE_OSRS_1 = 0x74, CONTROL_VALUE_PRESSURE_OSRS_2 = 0xB4, CONTROL_VALUE_PRESSURE_OSRS_3 = 0xF4, }
impl ControlRegisterValue {
pub fn addr(&self) -> u8 {
*self as u8
}
}
impl From<Oversampling> for ControlRegisterValue {
fn from(oss: Oversampling) -> Self {
match oss {
Oversampling::UltraLowPower => ControlRegisterValue::CONTROL_VALUE_PRESSURE_OSRS_0,
Oversampling::Standard => ControlRegisterValue::CONTROL_VALUE_PRESSURE_OSRS_1,
Oversampling::HighResolution => ControlRegisterValue::CONTROL_VALUE_PRESSURE_OSRS_2,
Oversampling::UltraHighResolution => {
ControlRegisterValue::CONTROL_VALUE_PRESSURE_OSRS_3
}
}
}
}
struct Coefficients {
pub ac1: i16,
pub ac2: i16,
pub ac3: i16,
pub ac4: u16,
pub ac5: u16,
pub ac6: u16,
pub b1: i16,
pub b2: i16,
pub mb: i16,
pub mc: i16,
pub md: i16,
}
impl Coefficients {
fn new() -> Self {
Coefficients {
ac1: 0,
ac2: 0,
ac3: 0,
ac4: 0,
ac5: 0,
ac6: 0,
b1: 0,
b2: 0,
mb: 0,
mc: 0,
md: 0,
}
}
}
#[derive(Copy, Clone)]
pub enum Oversampling {
UltraLowPower = 0,
Standard = 1,
HighResolution = 2,
UltraHighResolution = 3,
}
impl Oversampling {
fn value(&self) -> u8 {
*self as u8
}
}
pub struct Bmp085<I2C, TIMER: DelayUs<u16>> {
i2c: I2C,
timer: TIMER,
coeff: Coefficients,
oss: Oversampling,
}
pub type DeciCelcius = i32;
pub type Pascal = i32;
pub struct PT {
pub temperature: DeciCelcius,
pub pressure: Pascal,
}
impl<I2C, TIMER, E> Bmp085<I2C, TIMER>
where
I2C: WriteRead<Error = E> + Write<Error = E>,
TIMER: DelayUs<u16>,
{
pub fn new(i2c: I2C, timer: TIMER, oss: Oversampling) -> Result<Self, E> {
let mut bmp085 = Bmp085 {
i2c,
timer,
coeff: Coefficients::new(),
oss,
};
bmp085.coeff.ac1 = bmp085.read_i16(Register::COEFF_AC1)?;
bmp085.coeff.ac2 = bmp085.read_i16(Register::COEFF_AC2)?;
bmp085.coeff.ac3 = bmp085.read_i16(Register::COEFF_AC3)?;
bmp085.coeff.ac4 = bmp085.read_u16(Register::COEFF_AC4)?;
bmp085.coeff.ac5 = bmp085.read_u16(Register::COEFF_AC5)?;
bmp085.coeff.ac6 = bmp085.read_u16(Register::COEFF_AC6)?;
bmp085.coeff.b1 = bmp085.read_i16(Register::COEFF_B1)?;
bmp085.coeff.b2 = bmp085.read_i16(Register::COEFF_B2)?;
bmp085.coeff.mb = bmp085.read_i16(Register::COEFF_MB)?;
bmp085.coeff.mc = bmp085.read_i16(Register::COEFF_MC)?;
bmp085.coeff.md = bmp085.read_i16(Register::COEFF_MD)?;
Ok(bmp085)
}
fn read_u16(&mut self, reg: Register) -> Result<u16, E> {
let buf: GenericArray<u8, U2> = self.read_register(reg)?;
Ok((u16(buf[0]) << 8) + u16(buf[1]))
}
fn read_i16(&mut self, reg: Register) -> Result<i16, E> {
let buf: GenericArray<u8, U2> = self.read_register(reg)?;
Ok((i16(buf[0]) << 8) + i16(buf[1]))
}
fn read_register<N>(&mut self, reg: Register) -> Result<GenericArray<u8, N>, E>
where
N: ArrayLength<u8>,
{
let mut buffer: GenericArray<u8, N> = unsafe { mem::uninitialized() };
{
let buffer: &mut [u8] = &mut buffer;
self.i2c.write_read(ADDRESS, &[reg.addr()], buffer)?;
}
Ok(buffer)
}
fn write_register(&mut self, reg: Register, byte: u8) -> Result<(), E> {
self.i2c.write(ADDRESS, &[reg.addr(), byte])
}
pub fn read(&mut self) -> Result<PT, E> {
self.write_register(
Register::CONTROL_REG,
ControlRegisterValue::CONTROL_VALUE_TEMPERATURE.addr(),
)?;
self.timer.delay_us(4500);
let ut = self.read_i16(Register::VALUE_REG)?;
let (temperature, b5) = calculate_temperature(i32(ut), &self.coeff);
let press_reg_value = ControlRegisterValue::from(self.oss);
self.write_register(Register::CONTROL_REG, press_reg_value.addr())?;
self.timer.delay_us(1000 * (2 + (3 << self.oss.value())));
let up: GenericArray<u8, U3> = self.read_register(Register::VALUE_REG)?;
let up: i32 =
((i32(up[0]) << 16) + (i32(up[1]) << 8) + i32(up[2])) >> (8 - self.oss.value());
let pressure: Pascal = calculate_true_pressure(up, b5, self.oss, &self.coeff);
Ok(PT {
temperature,
pressure,
})
}
}
fn calculate_temperature(ut: i32, coeff: &Coefficients) -> (DeciCelcius, i32) {
let x1: i32 = ((ut - i32(coeff.ac6)) * i32(coeff.ac5)) >> 15;
let x2: i32 = (i32(coeff.mc) << 11) / (x1 + i32(coeff.md));
let b5: i32 = x1 + x2; let t: DeciCelcius = (b5 + 8) >> 4;
(t, b5)
}
fn calculate_true_pressure(up: i32, b5: i32, oss: Oversampling, coeff: &Coefficients) -> Pascal {
let b6: i32 = b5 - 4000;
let x1: i32 = ((i32(coeff.b2) * (b6 * b6)) >> 12) >> 11;
let x2: i32 = (i32(coeff.ac2) * b6) >> 11;
let x3: i32 = x1 + x2;
let b3: i32 = (((i32(coeff.ac1) * 4 + x3) << oss.value()) + 2) >> 2;
let x1: i32 = (i32(coeff.ac3) * b6) >> 13;
let x2: i32 = (i32(coeff.b1) * ((b6 * b6) >> 12)) >> 16;
let x3: i32 = (x1 + x2 + 2) >> 2;
let b4: u32 = (u32(coeff.ac4) * ((x3 as u32) + 32768)) >> 15;
let b7: u32 = ((up - b3) as u32) * (50000u32 >> oss.value());
let p: i32 = if b7 < 0x8000_0000 {
b7 * 2 / b4
} else {
b7 / b4 * 2
} as i32;
let x1: i32 = (p >> 8) * (p >> 8);
let x1: i32 = (x1 * 3038) >> 16;
let x2: i32 = (-7357 * p) >> 16;
p + ((x1 + x2 + 3791) >> 4)
}
#[cfg(feature = "default")]
pub fn pressure_to_normal_null(p: Pascal, altitude: u16) -> u16 {
let z = (p as f32) / libm::powf(1f32 - (f32::from(altitude) / 44330f32), 5.255f32);
libm::roundf(z / 100f32) as u16
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn calculate_temp_pressure() {
let coeff = Coefficients {
ac1: 408,
ac2: -72,
ac3: -14383,
ac4: 32741,
ac5: 32757,
ac6: 23153,
b1: 6190,
b2: 4,
mb: -32767,
mc: -8711,
md: 2868,
};
let ut: i32 = 27898; let up: i32 = 23843; let oss = Oversampling::UltraLowPower;
assert_eq!(oss.value(), 0);
let (deci_c, b5) = calculate_temperature(ut, &coeff);
assert_eq!(deci_c, 150);
assert_eq!(b5, 2400);
let pressure: Pascal = calculate_true_pressure(up, b5, oss, &coeff);
assert_eq!(pressure, 69964); }
#[test]
#[cfg(feature = "default")]
fn calculate_pressure_to_normal_null() {
let p: Pascal = 93728;
let alt: u16 = 691; let pnn = pressure_to_normal_null(p, alt);
assert_eq!(1018, pnn);
}
}