kirdy/src/thermostat/ad7172/mod.rs

353 lines
9.5 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use core::fmt;
use num_traits::float::Float;
use serde::{Serialize, Deserialize};
use fugit::MegahertzU32;
use stm32f4xx_hal::spi;
pub mod regs;
mod checksum;
pub use checksum::ChecksumMode;
mod adc;
pub use adc::*;
/// SPI Mode 3
pub const SPI_MODE: spi::Mode = spi::Mode {
polarity: spi::Polarity::IdleHigh,
phase: spi::Phase::CaptureOnSecondTransition,
};
/// AD7172 Max Frequency: 40MHz | SPI3 Max Frequency: 21MHz
pub const SPI_CLOCK_MHZ: MegahertzU32 = MegahertzU32::from_raw(21);
pub const MAX_VALUE: u32 = 0xFF_FFFF;
#[derive(Clone, Copy, Debug)]
#[repr(u8)]
pub enum Mode {
ContinuousConversion = 0b000,
SingleConversion = 0b001,
Standby = 0b010,
PowerDown = 0b011,
InternalOffsetCalibration = 0b100,
Invalid,
SystemOffsetCalibration = 0b110,
SystemGainCalibration = 0b111,
}
impl From<u8> for Mode {
fn from(x: u8) -> Self {
use Mode::*;
match x {
0b000 => ContinuousConversion,
0b001 => SingleConversion,
0b010 => Standby,
0b011 => PowerDown,
0b100 => InternalOffsetCalibration,
0b110 => SystemOffsetCalibration,
0b111 => SystemGainCalibration,
_ => Invalid,
}
}
}
#[derive(Clone, Copy, Debug)]
#[repr(u8)]
pub enum Input {
Ain0 = 0,
Ain1 = 1,
Ain2 = 2,
Ain3 = 3,
Ain4 = 4,
TemperaturePos = 17,
TemperatureNeg = 18,
AnalogSupplyPos = 19,
AnalogSupplyNeg = 20,
RefPos = 21,
RefNeg = 22,
Invalid = 0b11111,
}
impl From<u8> for Input {
fn from(x: u8) -> Self {
match x {
0 => Input::Ain0,
1 => Input::Ain1,
2 => Input::Ain2,
3 => Input::Ain3,
4 => Input::Ain4,
17 => Input::TemperaturePos,
18 => Input::TemperatureNeg,
19 => Input::AnalogSupplyPos,
20 => Input::AnalogSupplyNeg,
21 => Input::RefPos,
22 => Input::RefNeg,
_ => Input::Invalid,
}
}
}
impl fmt::Display for Input {
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
use Input::*;
match self {
Ain0 => "ain0",
Ain1 => "ain1",
Ain2 => "ain2",
Ain3 => "ain3",
Ain4 => "ain4",
TemperaturePos => "temperature+",
TemperatureNeg => "temperature-",
AnalogSupplyPos => "analogsupply+",
AnalogSupplyNeg => "analogsupply-",
RefPos => "ref+",
RefNeg => "ref-",
_ => "<INVALID>",
}.fmt(fmt)
}
}
/// Reference source for ADC conversion
#[repr(u8)]
pub enum RefSource {
/// External reference
External = 0b00,
/// Internal 2.5V reference
Internal = 0b10,
/// AVDD1 AVSS
Avdd1MinusAvss = 0b11,
Invalid = 0b01,
}
impl From<u8> for RefSource {
fn from(x: u8) -> Self {
match x {
0 => RefSource::External,
1 => RefSource::Internal,
2 => RefSource::Avdd1MinusAvss,
_ => RefSource::Invalid,
}
}
}
impl fmt::Display for RefSource {
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
use RefSource::*;
match self {
External => "external",
Internal => "internal",
Avdd1MinusAvss => "avdd1-avss",
_ => "<INVALID>",
}.fmt(fmt)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
#[repr(u8)]
pub enum PostFilter {
/// 27 SPS, 47 dB rejection, 36.7 ms settling
F27SPS = 0b010,
/// 21.25 SPS, 62 dB rejection, 40 ms settling
F21SPS = 0b011,
/// 20 SPS, 86 dB rejection, 50 ms settling
F20SPS = 0b101,
/// 16.67 SPS, 92 dB rejection, 60 ms settling
F16SPS = 0b110,
Invalid = 0b111,
}
impl PostFilter {
pub const VALID_VALUES: &'static [Self] = &[
PostFilter::F27SPS,
PostFilter::F21SPS,
PostFilter::F20SPS,
PostFilter::F16SPS,
];
pub fn closest(rate: f32) -> Option<Self> {
let mut best: Option<(f32, Self)> = None;
for value in Self::VALID_VALUES {
let error = (rate - value.output_rate().unwrap()).abs();
let better = best
.map(|(best_error, _)| error < best_error)
.unwrap_or(true);
if better {
best = Some((error, *value));
}
}
best.map(|(_, best)| best)
}
/// Samples per Second
pub fn output_rate(&self) -> Option<f32> {
match self {
PostFilter::F27SPS => Some(27.0),
PostFilter::F21SPS => Some(21.25),
PostFilter::F20SPS => Some(20.0),
PostFilter::F16SPS => Some(16.67),
PostFilter::Invalid => None,
}
}
}
impl From<u8> for PostFilter {
fn from(x: u8) -> Self {
match x {
0b010 => PostFilter::F27SPS,
0b011 => PostFilter::F21SPS,
0b101 => PostFilter::F20SPS,
0b110 => PostFilter::F16SPS,
_ => PostFilter::Invalid,
}
}
}
#[derive(Deserialize, Serialize, Copy, Clone, Debug, Default)]
pub enum FilterType {
#[default]
Sinc5Sinc1With50hz60HzRejection,
Sinc5Sinc1,
Sinc3,
Sinc3WithFineODR,
}
#[derive(PartialEq)]
#[repr(u8)]
pub enum DigitalFilterOrder {
Sinc5Sinc1 = 0b00,
Sinc3 = 0b11,
Invalid = 0b10,
}
impl From<u8> for DigitalFilterOrder {
fn from(x: u8) -> Self {
match x {
0b00 => DigitalFilterOrder::Sinc5Sinc1,
0b11 => DigitalFilterOrder::Sinc3,
_ => DigitalFilterOrder::Invalid,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
#[allow(unused)]
#[repr(u8)]
pub enum SingleChODR {
F31250_0SPS = 0b00101,
F15625_0SPS = 0b00110,
F10417_0SPS = 0b00111,
F5208_0SPS = 0b01000,
F2597_0SPS = 0b01001,
F1007_0SPS = 0b01010,
F503_8SPS = 0b01011,
F381_0SPS = 0b01100,
F200_3SPS = 0b01101,
F100_2SPS = 0b01110,
F59_52SPS = 0b01111,
F49_68SPS = 0b10000,
F20_01SPS = 0b10001,
F16_63SPS = 0b10010,
F10_0SPS = 0b10011,
F5_0SPS = 0b10100,
F2_5SPS = 0b10101,
F1_25SPS = 0b10110,
Invalid = 0b11111,
}
impl SingleChODR {
pub const VALID_VALUES: &'static [Self] = &[
SingleChODR::F31250_0SPS,
SingleChODR::F15625_0SPS,
SingleChODR::F10417_0SPS,
SingleChODR::F5208_0SPS,
SingleChODR::F2597_0SPS,
SingleChODR::F1007_0SPS,
SingleChODR::F503_8SPS,
SingleChODR::F381_0SPS,
SingleChODR::F200_3SPS,
SingleChODR::F100_2SPS,
SingleChODR::F59_52SPS,
SingleChODR::F49_68SPS,
SingleChODR::F20_01SPS,
SingleChODR::F16_63SPS,
SingleChODR::F10_0SPS,
SingleChODR::F5_0SPS,
SingleChODR::F2_5SPS,
SingleChODR::F1_25SPS,
];
pub fn closest(rate: f32) -> Option<Self> {
let mut best: Option<(f32, Self)> = None;
for value in Self::VALID_VALUES {
let error = (rate - value.output_rate().unwrap()).abs();
let better = best
.map(|(best_error, _)| error < best_error)
.unwrap_or(true);
if better {
best = Some((error, *value));
}
}
best.map(|(_, best)| best)
}
/// Samples per Second
pub fn output_rate(&self) -> Option<f32> {
match self {
SingleChODR::F31250_0SPS => Some(31250.0),
SingleChODR::F15625_0SPS => Some(15625.0),
SingleChODR::F10417_0SPS => Some(10417.0),
SingleChODR::F5208_0SPS => Some(5208.0),
SingleChODR::F2597_0SPS => Some(2597.0),
SingleChODR::F1007_0SPS => Some(1007.0),
SingleChODR::F503_8SPS => Some(503.8),
SingleChODR::F381_0SPS => Some(381.0),
SingleChODR::F200_3SPS => Some(200.3),
SingleChODR::F100_2SPS => Some(100.2),
SingleChODR::F59_52SPS => Some(59.52),
SingleChODR::F49_68SPS => Some(49.68),
SingleChODR::F20_01SPS => Some(20.01),
SingleChODR::F16_63SPS => Some(16.63),
SingleChODR::F10_0SPS => Some(10.0),
SingleChODR::F5_0SPS => Some(5.0),
SingleChODR::F2_5SPS => Some(2.5),
SingleChODR::F1_25SPS => Some(1.25),
SingleChODR::Invalid => None,
}
}
}
impl From<u8> for SingleChODR {
fn from(x: u8) -> Self {
match x {
0b00101 => SingleChODR::F31250_0SPS,
0b00110 => SingleChODR::F15625_0SPS,
0b00111 => SingleChODR::F10417_0SPS,
0b01000 => SingleChODR::F5208_0SPS,
0b01001 => SingleChODR::F2597_0SPS,
0b01010 => SingleChODR::F1007_0SPS,
0b01011 => SingleChODR::F503_8SPS,
0b01100 => SingleChODR::F381_0SPS,
0b01101 => SingleChODR::F200_3SPS,
0b01110 => SingleChODR::F100_2SPS,
0b01111 => SingleChODR::F59_52SPS,
0b10000 => SingleChODR::F49_68SPS,
0b10001 => SingleChODR::F20_01SPS,
0b10010 => SingleChODR::F16_63SPS,
0b10011 => SingleChODR::F10_0SPS,
0b10100 => SingleChODR::F5_0SPS,
0b10101 => SingleChODR::F2_5SPS,
0b10110 => SingleChODR::F1_25SPS,
_ => SingleChODR::Invalid,
}
}
}
pub fn sinc3_fine_odr_output_rate(odr: u16) -> f32 {
1.0 * 1e6 / (32.0 * odr as f32)
}
pub fn sinc3_fine_odr_closest(rate: f32) -> u16 {
(1.0e6 / ( 32.0 * rate )).max(1.0 as f32).min(0x7FFF as f32) as u16
}