kirdy/src/thermostat/ad7172/mod.rs

351 lines
9.5 KiB
Rust
Raw Normal View History

use core::fmt;
use fugit::MegahertzU32;
use num_traits::float::Float;
use serde::{Deserialize, Serialize};
use stm32f4xx_hal::spi;
mod checksum;
pub mod regs;
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
}