use crate::chip::i2c::Peer;
use crate::chip::{Timeline, round};
use crate::snapshot::store;
use crate::time::VirtualNanos;
const PRS_B0: u8 = 0x02;
const TMP_B2: u8 = 0x03;
const TMP_B0: u8 = 0x05;
const PRS_CFG: u8 = 0x06;
const TMP_CFG: u8 = 0x07;
const MEAS_CFG: u8 = 0x08;
const CFG_REG: u8 = 0x09;
const RESET: u8 = 0x0C;
const ID: u8 = 0x0D;
const COEF: u8 = 0x10;
const COEF_END: u8 = 0x21;
const COEF_SRCE: u8 = 0x28;
const PRODUCT_ID: u8 = 0x10;
const COEF_RDY: u8 = 0x80;
const SENSOR_RDY: u8 = 0x40;
const TMP_RDY: u8 = 0x20;
const PRS_RDY: u8 = 0x10;
const MEAS_CTRL: u8 = 0x07;
const BACKGROUND: u8 = 0x04;
const MEASURE_TEMPERATURE: u8 = 0x02;
const MEASURE_PRESSURE: u8 = 0x01;
const RATE: u8 = 0x70;
const RATE_SHIFT: u32 = 4;
const OVERSAMPLING: u8 = 0x0F;
const RESERVED_OVERSAMPLING: u8 = 0x08;
const SOFT_RST: u8 = 0x0F;
const SOFT_RESET: u8 = 0b1001;
const TMP_COEF_SRCE: u8 = 0x80;
const ANSWERS_FROM: u64 = 2_500_000;
const SENSOR_READY_AT: u64 = 12_000_000;
const COEFFICIENTS_READY_AT: u64 = 40_000_000;
const MEASUREMENT_NANOS: [u64; 8] = [
3_600_000,
5_200_000,
8_400_000,
14_800_000,
27_600_000,
53_200_000,
104_400_000,
206_800_000,
];
const SCALE_FACTORS: [i64; 8] = [
524_288, 1_572_864, 3_670_016, 7_864_320, 253_952, 516_096, 1_040_384, 2_088_960,
];
const SECOND_NANOS: u64 = 1_000_000_000;
const C0: i64 = 204;
const C1: i64 = -258;
const C00: i64 = 75_000;
const C10: i64 = -52_000;
const C01: i64 = -2_000;
const C11: i64 = 300;
const C20: i64 = 200;
const C21: i64 = -20;
const C30: i64 = -5;
const TWELVE_BITS: u64 = 0xFFF;
const TWENTY_BITS: u64 = 0xF_FFFF;
const SIXTEEN_BITS: u64 = 0xFFFF;
const WORD_MIN: i64 = -0x80_0000;
const WORD_MAX: i64 = 0x7F_FFFF;
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum Sdo {
High,
Low,
}
impl Sdo {
#[must_use]
pub fn address(self) -> u8 {
match self {
Self::High => 0x77,
Self::Low => 0x76,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
enum Kind {
Pressure,
Temperature,
}
impl Kind {
fn index(self) -> usize {
match self {
Self::Pressure => 0,
Self::Temperature => 1,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
enum Phase {
Idle,
Register,
Data,
}
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct Dps310 {
address: u8,
started_at: VirtualNanos,
prs_cfg: u8,
tmp_cfg: u8,
cfg_reg: u8,
meas_ctrl: u8,
pressure: i32,
temperature: i32,
pressure_ready: bool,
temperature_ready: bool,
running: Option<(Kind, VirtualNanos)>,
free_at: VirtualNanos,
due: [VirtualNanos; 2],
pascals: u32,
centi_celsius: i32,
pointer: u8,
phase: Phase,
}
impl Dps310 {
#[must_use]
pub fn new(sdo: Sdo) -> Self {
Self {
address: sdo.address(),
started_at: VirtualNanos(0),
prs_cfg: 0,
tmp_cfg: 0,
cfg_reg: 0,
meas_ctrl: 0,
pressure: 0,
temperature: 0,
pressure_ready: false,
temperature_ready: false,
running: None,
free_at: VirtualNanos(0),
due: [VirtualNanos(0); 2],
pascals: 101_325,
centi_celsius: 2_500,
pointer: 0,
phase: Phase::Idle,
}
}
pub fn set_pressure(&mut self, pascals: u32) {
self.pascals = pascals;
}
pub fn set_temperature(&mut self, centi_celsius: i32) {
self.centi_celsius = centi_celsius;
}
fn reset(&mut self, now: VirtualNanos) {
*self = Self {
address: self.address,
started_at: now,
free_at: now,
due: [now; 2],
pascals: self.pascals,
centi_celsius: self.centi_celsius,
pointer: self.pointer,
phase: self.phase,
..Self::new(Sdo::High)
};
}
fn since_start(&self, now: VirtualNanos) -> u64 {
now.0.saturating_sub(self.started_at.0)
}
fn coefficients_ready(&self, now: VirtualNanos) -> bool {
self.since_start(now) >= COEFFICIENTS_READY_AT
}
fn oversampling(&self, kind: Kind) -> u8 {
match kind {
Kind::Pressure => self.prs_cfg & OVERSAMPLING,
Kind::Temperature => self.tmp_cfg & OVERSAMPLING,
}
}
fn duration(&self, kind: Kind) -> Option<u64> {
let code = self.oversampling(kind);
(code & RESERVED_OVERSAMPLING == 0).then(|| MEASUREMENT_NANOS[usize::from(code)])
}
fn period(&self, kind: Kind) -> u64 {
let register = match kind {
Kind::Pressure => self.prs_cfg,
Kind::Temperature => self.tmp_cfg,
};
SECOND_NANOS >> ((register & RATE) >> RATE_SHIFT)
}
fn scale(&self, kind: Kind) -> i64 {
SCALE_FACTORS[usize::from(self.oversampling(kind) & !RESERVED_OVERSAMPLING)]
}
fn temperature_word(&self) -> i32 {
let scale = self.scale(Kind::Temperature);
let numerator = (i64::from(self.centi_celsius) - 50 * C0) * scale;
let word = round(numerator, 100 * C1).clamp(WORD_MIN, WORD_MAX);
i32::try_from(word).expect("held to twenty-four bits")
}
fn pressure_word(&self) -> i32 {
let temperature = i128::from(self.temperature_word());
let (pressure_scale, temperature_scale) = (
i128::from(self.scale(Kind::Pressure)),
i128::from(self.scale(Kind::Temperature)),
);
let rising =
|word: i128| -compensated(word, temperature, pressure_scale, temperature_scale);
let target =
-i128::from(self.pascals) * compensation_scale(pressure_scale, temperature_scale);
let (mut low, mut high) = (i128::from(WORD_MIN), i128::from(WORD_MAX));
while low < high {
let middle = (low + high).div_euclid(2);
if rising(middle) < target {
low = middle + 1;
} else {
high = middle;
}
}
let below = (low - 1).max(i128::from(WORD_MIN));
let nearest = if (target - rising(below)).abs() < (rising(low) - target).abs() {
below
} else {
low
};
i32::try_from(nearest).expect("held to twenty-four bits")
}
fn begin(&mut self, kind: Kind, start: VirtualNanos) {
let Some(duration) = self.duration(kind) else {
return;
};
self.running = Some((kind, VirtualNanos(start.0 + duration)));
self.due[kind.index()] = VirtualNanos(start.0 + self.period(kind));
}
fn next(&self) -> Option<Kind> {
[
(Kind::Pressure, self.meas_ctrl & MEASURE_PRESSURE != 0),
(Kind::Temperature, self.meas_ctrl & MEASURE_TEMPERATURE != 0),
]
.into_iter()
.filter(|&(kind, measured)| measured && self.duration(kind).is_some())
.map(|(kind, _)| kind)
.min_by_key(|kind| self.due[kind.index()])
}
fn advance(&mut self, now: VirtualNanos) {
let mut finished = [false; 2];
loop {
if let Some((kind, ends_at)) = self.running {
if ends_at > now {
break;
}
finished[kind.index()] = true;
self.running = None;
self.free_at = ends_at;
if self.meas_ctrl & BACKGROUND == 0 {
self.meas_ctrl = 0;
break;
}
}
if self.meas_ctrl & BACKGROUND == 0 {
break;
}
let Some(kind) = self.next() else {
break;
};
let start = self.due[kind.index()].max(self.free_at);
if start > now {
break;
}
self.begin(kind, start);
}
if finished[Kind::Pressure.index()] {
self.pressure = self.pressure_word();
self.pressure_ready = true;
}
if finished[Kind::Temperature.index()] {
self.temperature = self.temperature_word();
self.temperature_ready = true;
}
}
fn set_mode(&mut self, control: u8, now: VirtualNanos) {
if control == self.meas_ctrl {
return;
}
self.meas_ctrl = control;
self.running = None;
self.free_at = now;
self.due = [now; 2];
match control {
MEASURE_PRESSURE => self.begin(Kind::Pressure, now),
MEASURE_TEMPERATURE => self.begin(Kind::Temperature, now),
_ => {}
}
}
fn meas_cfg(&self, now: VirtualNanos) -> u8 {
let mut flags = 0;
if self.since_start(now) >= SENSOR_READY_AT {
flags |= SENSOR_RDY;
}
if self.coefficients_ready(now) {
flags |= COEF_RDY;
}
if self.temperature_ready {
flags |= TMP_RDY;
}
if self.pressure_ready {
flags |= PRS_RDY;
}
flags | self.meas_ctrl
}
fn read_register(&mut self, register: u8, now: VirtualNanos) -> u8 {
match register {
0..=PRS_B0 => {
let bytes = self.pressure.to_be_bytes();
if register == PRS_B0 {
self.pressure_ready = false;
}
bytes[usize::from(register) + 1]
}
TMP_B2..=TMP_B0 => {
let bytes = self.temperature.to_be_bytes();
if register == TMP_B0 {
self.temperature_ready = false;
}
bytes[usize::from(register - TMP_B2) + 1]
}
PRS_CFG => self.prs_cfg,
TMP_CFG => self.tmp_cfg,
MEAS_CFG => self.meas_cfg(now),
CFG_REG => self.cfg_reg,
ID => PRODUCT_ID,
COEF..=COEF_END if self.coefficients_ready(now) => {
coefficient_bytes()[usize::from(register - COEF)]
}
COEF_SRCE if self.coefficients_ready(now) => TMP_COEF_SRCE,
_ => 0,
}
}
fn write_register(&mut self, register: u8, byte: u8, now: VirtualNanos) {
match register {
PRS_CFG => self.prs_cfg = byte,
TMP_CFG => self.tmp_cfg = byte,
CFG_REG => self.cfg_reg = byte,
MEAS_CFG => self.set_mode(byte & MEAS_CTRL, now),
RESET if byte & SOFT_RST == SOFT_RESET => self.reset(now),
_ => {}
}
}
}
fn coefficient_bytes() -> [u8; 18] {
let packed = |value: i64, mask: u64| value.cast_unsigned() & mask;
let mut bytes = [0; 18];
let c0_c1 = (packed(C0, TWELVE_BITS) << 12) | packed(C1, TWELVE_BITS);
bytes[0..3].copy_from_slice(&c0_c1.to_be_bytes()[5..8]);
let c00_c10 = (packed(C00, TWENTY_BITS) << 20) | packed(C10, TWENTY_BITS);
bytes[3..8].copy_from_slice(&c00_c10.to_be_bytes()[3..8]);
for (pair, value) in [C01, C11, C20, C21, C30].into_iter().enumerate() {
let start = 8 + 2 * pair;
bytes[start..start + 2].copy_from_slice(&packed(value, SIXTEEN_BITS).to_be_bytes()[6..8]);
}
bytes
}
fn compensation_scale(pressure_scale: i128, temperature_scale: i128) -> i128 {
temperature_scale * pressure_scale.pow(3)
}
fn compensated(word: i128, temperature: i128, kp: i128, kt: i128) -> i128 {
let c = [C00, C10, C20, C30, C01, C11, C21].map(i128::from);
c[0] * kt * kp.pow(3)
+ word * (c[1] * kt * kp.pow(2) + word * (c[2] * kt * kp + word * c[3] * kt))
+ temperature * (c[4] * kp.pow(3) + word * (c[5] * kp.pow(2) + word * c[6] * kp))
}
impl Peer for Dps310 {
fn start(&mut self, address: u8, read: bool, context: &mut Timeline<'_, '_>) -> bool {
let now = context.now();
self.advance(now);
if address != self.address || self.since_start(now) < ANSWERS_FROM {
self.phase = Phase::Idle;
return false;
}
self.phase = if read { Phase::Data } else { Phase::Register };
true
}
fn write(&mut self, byte: u8, context: &mut Timeline<'_, '_>) -> bool {
let now = context.now();
self.advance(now);
match self.phase {
Phase::Register => {
self.pointer = byte;
self.phase = Phase::Data;
}
Phase::Data => {
self.write_register(self.pointer, byte, now);
self.pointer = self.pointer.wrapping_add(1);
}
Phase::Idle => return false,
}
true
}
fn read(&mut self, _last: bool, context: &mut Timeline<'_, '_>) -> u8 {
let now = context.now();
self.advance(now);
match self.phase {
Phase::Data => {
let register = self.pointer;
self.pointer = self.pointer.wrapping_add(1);
self.read_register(register, now)
}
Phase::Register | Phase::Idle => 0,
}
}
fn stop(&mut self, _context: &mut Timeline<'_, '_>) {
self.phase = Phase::Idle;
}
}
store!(Dps310);