diff --git a/src/thermostat/ad7172/adc.rs b/src/thermostat/ad7172/adc.rs index e3c9faf..2946eb4 100644 --- a/src/thermostat/ad7172/adc.rs +++ b/src/thermostat/ad7172/adc.rs @@ -15,9 +15,7 @@ use uom::si::{ electric_potential::volt, }; use super::{ - regs::{self, Register, RegisterData}, - checksum::{ChecksumMode, Checksum}, - Mode, Input, RefSource, PostFilter, DigitalFilterOrder, + checksum::{Checksum, ChecksumMode}, regs::{self, Register, RegisterData}, sinc3_fine_odr_closest, sinc3_fine_odr_output_rate, DigitalFilterOrder, FilterType, Input, Mode, PostFilter, RefSource, SingleChODR }; /// AD7172-2 implementation @@ -57,6 +55,7 @@ impl, NSS: OutputPin, E: fmt::Debug> Adc let mut adc_mode = ::Data::empty(); adc_mode.set_ref_en(true); + adc_mode.set_sing_cyc(false); adc_mode.set_mode(Mode::Standby); adc.write_reg(®s::AdcMode, &mut adc_mode)?; @@ -96,13 +95,7 @@ impl, NSS: OutputPin, E: fmt::Debug> Adc data.set_ainbuf_neg(true); data.set_ref_sel(RefSource::External); })?; - self.update_reg(®s::FiltCon { index }, |data| { - data.set_enh_filt_en(true); - data.set_enh_filt(PostFilter::F16SPS); - data.set_order(DigitalFilterOrder::Sinc5Sinc1); - // output data rate: 10 Hz - data.set_odr(0b10011); - })?; + self.set_sinc5_sinc1_with_50hz_60hz_rejection(index, PostFilter::F16SPS)?; self.update_reg(®s::Channel { index }, |data| { data.set_setup(index); data.set_enabled(true); @@ -122,35 +115,85 @@ impl, NSS: OutputPin, E: fmt::Debug> Adc pub fn start_continuous_conversion(&mut self) -> Result<(), SPI::Error> { let mut adc_mode = ::Data::empty(); adc_mode.set_ref_en(true); + // Set SING_CYC = 0 to maximize sampling rate with lowest noise for single channel of temperature measurement + adc_mode.set_sing_cyc(false); adc_mode.set_mode(Mode::ContinuousConversion); self.write_reg(®s::AdcMode, &mut adc_mode)?; Ok(()) } - pub fn get_postfilter(&mut self, index: u8) -> Result, SPI::Error> { + /// Rate is only valid with single channel enabled + pub fn get_filter_type_and_rate(&mut self, index: u8) -> Result<(FilterType, f32), SPI::Error> { + let mut filter_type: FilterType = FilterType::Sinc5Sinc1With50hz60HzRejection; + let mut rate: f32 = -1.0; self.read_reg(®s::FiltCon { index }) - .map(|data| { - if data.enh_filt_en() { - Some(data.enh_filt()) - } else { - None + .map(|data| { + if data.sinc3_map() { + filter_type = FilterType::Sinc3WithFineODR; + let odr : u16 = (data.sinc3_map_fine_odr_msb() as u16) << 8 | data.sinc3_map_fine_odr_lsb() as u16; + rate = sinc3_fine_odr_output_rate(odr); + } else if data.enh_filt_en() { + filter_type = FilterType::Sinc5Sinc1With50hz60HzRejection; + match data.enh_filt().output_rate(){ + Some(val) => { rate = val; } + None => { rate = -1.0; } + }; + } else if data.order() == DigitalFilterOrder::Sinc5Sinc1 { + filter_type = FilterType::Sinc5Sinc1; + match data.odr().output_rate(){ + Some(val) => { rate = val; } + None => { rate = -1.0; } } - }) - } - - pub fn set_postfilter(&mut self, index: u8, filter: Option) -> Result<(), SPI::Error> { - self.update_reg(®s::FiltCon { index }, |data| { - match filter { - None => data.set_enh_filt_en(false), - Some(filter) => { - data.set_enh_filt_en(true); - data.set_enh_filt(filter); + } else { + filter_type = FilterType::Sinc3; + match data.odr().output_rate(){ + Some(val) => { rate = val; } + None => { rate = -1.0; } } } + })?; + + Ok((filter_type, rate)) + } + + pub fn set_sinc5_sinc1_with_50hz_60hz_rejection(&mut self, index: u8, rate: PostFilter) -> Result<(), SPI::Error> { + self.update_reg(®s::FiltCon { index }, |data| { + data.set_sinc3_map(false); + data.set_enh_filt_en(true); + data.set_order(DigitalFilterOrder::Sinc5Sinc1); + data.set_enh_filt(rate); }) } + pub fn set_sinc5_sinc1_filter(&mut self, index: u8, rate: SingleChODR) -> Result<(), SPI::Error> { + self.update_reg(®s::FiltCon { index }, |data| { + data.set_sinc3_map(false); + data.set_enh_filt_en(false); + data.set_order(DigitalFilterOrder::Sinc5Sinc1); + data.set_odr(rate); + }) + } + + pub fn set_sinc3_filter(&mut self, index: u8, rate: SingleChODR) -> Result<(), SPI::Error> { + self.update_reg(®s::FiltCon { index }, |data| { + data.set_sinc3_map(false); + data.set_enh_filt_en(false); + data.set_order(DigitalFilterOrder::Sinc3); + data.set_odr(rate); + }) + } + + pub fn set_sinc3_fine_filter(&mut self, index: u8, rate: f32) -> Result { + let sinc3_fine_odr_u16 = sinc3_fine_odr_closest(rate); + + self.update_reg(®s::FiltCon { index }, |data| { + data.set_sinc3_map(true); + data.set_sinc3_map_fine_odr_msb((sinc3_fine_odr_u16 >> 8 & 0xFF) as u8); + data.set_sinc3_map_fine_odr_lsb((sinc3_fine_odr_u16 & 0xFF) as u8); + }).map(|_| sinc3_fine_odr_output_rate(sinc3_fine_odr_u16)) + } + /// Returns the channel the data is from pub fn data_ready(&mut self) -> Result, SPI::Error> { self.read_reg(®s::Status) diff --git a/src/thermostat/ad7172/mod.rs b/src/thermostat/ad7172/mod.rs index 173dadb..1578e20 100644 --- a/src/thermostat/ad7172/mod.rs +++ b/src/thermostat/ad7172/mod.rs @@ -203,6 +203,16 @@ impl From for PostFilter { } } +#[derive(Deserialize, Serialize, Copy, Clone, Debug, Default)] +pub enum FilterType { + #[default] + Sinc5Sinc1With50hz60HzRejection, + Sinc5Sinc1, + Sinc3, + Sinc3WithFineODR, +} + +#[derive(PartialEq)] #[repr(u8)] pub enum DigitalFilterOrder { Sinc5Sinc1 = 0b00, @@ -219,3 +229,124 @@ impl From for DigitalFilterOrder { } } } + +#[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 { + 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 { + 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 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 +} diff --git a/src/thermostat/ad7172/regs.rs b/src/thermostat/ad7172/regs.rs index 5b8c9d5..b992ab1 100644 --- a/src/thermostat/ad7172/regs.rs +++ b/src/thermostat/ad7172/regs.rs @@ -227,11 +227,13 @@ impl setup_con::Data { def_reg!(FiltCon, u8, filt_con, 0x28, 2); impl filt_con::Data { - reg_bit!(sinc3_map, 0, 7, "If set, mapping of filter register changes to directly program the decimation rate of the sinc3 filter"); + reg_bit!(sinc3_map, set_sinc3_map, 0, 7, "If set, Sinc3 Filter's notch frequency rejection position can be fine tuned with FiltCon[14:0]. Best to be used with Single Channel Enabled"); reg_bit!(enh_filt_en, set_enh_filt_en, 0, 3, "Enable postfilters for enhanced 50Hz and 60Hz rejection"); - reg_bits!(enh_filt, set_enh_filt, 0, 0..=2, PostFilter, "Select postfilters for enhanced 50Hz and 60Hz rejection"); + reg_bits!(enh_filt, set_enh_filt, 0, 0..=2, PostFilter, "Select postfilters output data rate for enhanced 50Hz and 60Hz rejection"); reg_bits!(order, set_order, 1, 5..=6, DigitalFilterOrder, "order of the digital filter that processes the modulator data"); - reg_bits!(odr, set_odr, 1, 0..=4, "Output data rate"); + reg_bits!(odr, set_odr, 1, 0..=4, SingleChODR, "Output data rate for normal Sin5c + Sinc1 and Sinc3 filter with SING_CYC = 0 and Single Channel Enabled"); + reg_bits!(sinc3_map_fine_odr_msb, set_sinc3_map_fine_odr_msb, 0, 0..=6, "MSB Byte Sinc3 Fine Output Config"); + reg_bits!(sinc3_map_fine_odr_lsb, set_sinc3_map_fine_odr_lsb, 1, 0..=7, "LSB Byte Sinc3 Fine Output Config"); } def_reg!(Offset, u8, offset, 0x30, 3); diff --git a/src/thermostat/thermostat.rs b/src/thermostat/thermostat.rs index a80ca40..d3505c7 100644 --- a/src/thermostat/thermostat.rs +++ b/src/thermostat/thermostat.rs @@ -379,6 +379,22 @@ impl Thermostat{ self.temp_mon.set_lower_limit(t); } + pub fn set_temp_adc_sinc5_sinc1_filter(&mut self, index: u8, odr: ad7172::SingleChODR) { + self.ad7172.set_sinc5_sinc1_filter(index, odr).unwrap(); + } + + pub fn set_temp_adc_sinc3_filter(&mut self, index: u8, odr: ad7172::SingleChODR) { + self.ad7172.set_sinc3_filter(index, odr).unwrap(); + } + + pub fn set_temp_adc_sinc5_sinc1_with_postfilter(&mut self, index: u8, odr: ad7172::PostFilter) { + self.ad7172.set_sinc5_sinc1_with_50hz_60hz_rejection(index, odr).unwrap(); + } + + pub fn set_temp_adc_sinc3_fine_filter(&mut self, index: u8, rate: f64) { + self.ad7172.set_sinc3_fine_filter(index, rate as f32).unwrap(); + } + pub fn clear_temp_mon_alarm(&mut self) { self.temp_mon.clear_alarm(); }