Merge branch '5-implement-temperature-computations' into 'dev'
Resolve "Implement temperature computations" See merge request Steins7/fan_monitor!4
This commit is contained in:
commit
891e3d03cd
@ -13,6 +13,7 @@ cortex-m-rt = "0.6.10"
|
|||||||
cortex-m-semihosting = "0.3.3"
|
cortex-m-semihosting = "0.3.3"
|
||||||
panic-halt = "0.2.0"
|
panic-halt = "0.2.0"
|
||||||
hd44780-driver = "0.3.0"
|
hd44780-driver = "0.3.0"
|
||||||
|
libm = "0.2.1"
|
||||||
|
|
||||||
[dependencies.stm32f1xx-hal]
|
[dependencies.stm32f1xx-hal]
|
||||||
version = "0.7.0"
|
version = "0.7.0"
|
||||||
|
|||||||
BIN
docs/Graphs.ods
BIN
docs/Graphs.ods
Binary file not shown.
44
src/config.rs
Normal file
44
src/config.rs
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
pub enum ConfigError {
|
||||||
|
LoadError,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum FanOutput {
|
||||||
|
P1,
|
||||||
|
P2,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum FanState {
|
||||||
|
ON,
|
||||||
|
OFF,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SystemConfig {
|
||||||
|
pub max_temp_diff: f32,
|
||||||
|
pub min_temp_diff: f32,
|
||||||
|
pub ext_offset: f32,
|
||||||
|
pub p1_offset: f32,
|
||||||
|
pub p2_offset: f32,
|
||||||
|
pub fan_output: FanOutput,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SystemConfig {
|
||||||
|
|
||||||
|
pub fn new() -> SystemConfig {
|
||||||
|
SystemConfig {
|
||||||
|
max_temp_diff: 8.0,
|
||||||
|
min_temp_diff: 4.0,
|
||||||
|
ext_offset: 0.0,
|
||||||
|
p1_offset: 0.0,
|
||||||
|
p2_offset: 0.0,
|
||||||
|
fan_output: FanOutput::P1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load() -> Result<ConfigError, SystemConfig> {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn store() -> SystemConfig {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,4 +1,7 @@
|
|||||||
use core::{sync::atomic::{AtomicBool, Ordering}};
|
use core::{
|
||||||
|
sync::atomic::{AtomicBool, Ordering},
|
||||||
|
fmt,
|
||||||
|
};
|
||||||
|
|
||||||
use embedded_hal::{
|
use embedded_hal::{
|
||||||
//digital::v1_compat::OldOutputPin,
|
//digital::v1_compat::OldOutputPin,
|
||||||
@ -13,6 +16,33 @@ use hd44780_driver::{
|
|||||||
bus::DataBus,
|
bus::DataBus,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
config::SystemConfig,
|
||||||
|
state::SystemState,
|
||||||
|
};
|
||||||
|
|
||||||
|
//---Temp Enum--------------------------------------------------------------------------------------
|
||||||
|
/// A simple enum to handle displaying temps on the GUI. Temperatures can be valid or not. An
|
||||||
|
/// invalid temperature is displayed as "inval°C" whereas a valid temperature is displayed as
|
||||||
|
/// XX.XX°C
|
||||||
|
pub enum Temp {
|
||||||
|
Valid (f32),
|
||||||
|
Invalid,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Temp {
|
||||||
|
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Temp::Valid (deg) => formatter.write_fmt(format_args!("{:.2}", deg)),
|
||||||
|
Temp::Invalid => formatter.write_str("inval"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//---LCDGui Struct----------------------------------------------------------------------------------
|
||||||
|
/// Manages the lcd screen and inputs (encoder + button) and display relevant information. Can also
|
||||||
|
/// be used to configure the system. The update() function should be called frequently to refresh
|
||||||
|
/// the GUI (every 0.2s is enough)
|
||||||
pub struct LCDGui<L, Q, B>
|
pub struct LCDGui<L, Q, B>
|
||||||
where
|
where
|
||||||
L: LCDScreen,
|
L: LCDScreen,
|
||||||
@ -24,6 +54,8 @@ where
|
|||||||
button: &'static AtomicBool,
|
button: &'static AtomicBool,
|
||||||
backlight: Option<B>,
|
backlight: Option<B>,
|
||||||
count: u16,
|
count: u16,
|
||||||
|
ext_deg: Temp,
|
||||||
|
p1_deg: Temp,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<L, Q, B> LCDGui<L, Q, B>
|
impl<L, Q, B> LCDGui<L, Q, B>
|
||||||
@ -38,7 +70,15 @@ where
|
|||||||
use hd44780_driver::{Cursor, CursorBlink, Display};
|
use hd44780_driver::{Cursor, CursorBlink, Display};
|
||||||
|
|
||||||
let count = qei.count();
|
let count = qei.count();
|
||||||
let mut gui = LCDGui {lcd, qei, button, backlight, count};
|
let mut gui = LCDGui {
|
||||||
|
lcd,
|
||||||
|
qei,
|
||||||
|
button,
|
||||||
|
backlight,
|
||||||
|
count,
|
||||||
|
ext_deg: Temp::Invalid,
|
||||||
|
p1_deg: Temp::Invalid,
|
||||||
|
};
|
||||||
|
|
||||||
if let Some(bl) = &mut gui.backlight { let _ = bl.set_high(); };
|
if let Some(bl) = &mut gui.backlight { let _ = bl.set_high(); };
|
||||||
|
|
||||||
@ -56,8 +96,18 @@ where
|
|||||||
gui
|
gui
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(&mut self) -> ! {
|
pub fn update<O: OutputPin>(&mut self, state: &mut SystemState<O>) {
|
||||||
loop {
|
|
||||||
|
self.ext_deg = match state.ext_temp() {
|
||||||
|
Some(deg) => Temp::Valid(deg),
|
||||||
|
None => Temp::Invalid,
|
||||||
|
};
|
||||||
|
|
||||||
|
self.p1_deg = match state.p1_temp() {
|
||||||
|
Some(deg) => Temp::Valid(deg),
|
||||||
|
None => Temp::Invalid,
|
||||||
|
};
|
||||||
|
|
||||||
//TODO deduplicate button detection
|
//TODO deduplicate button detection
|
||||||
if self.button.swap(false, Ordering::AcqRel) {
|
if self.button.swap(false, Ordering::AcqRel) {
|
||||||
self.lcd.write_str("paf").unwrap();
|
self.lcd.write_str("paf").unwrap();
|
||||||
@ -71,9 +121,15 @@ where
|
|||||||
write!(self.lcd, "{}", self.qei.count()/2).unwrap();
|
write!(self.lcd, "{}", self.qei.count()/2).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
// put device in sleep mode until next interrupt (button or timer)
|
self.lcd.set_cursor_pos(0);
|
||||||
cortex_m::asm::wfi();
|
self.lcd.write_str(" ").unwrap();
|
||||||
}
|
self.lcd.set_cursor_pos(0);
|
||||||
|
let _ = write!(self.lcd, "{}\u{00df}C", self.ext_deg);
|
||||||
|
|
||||||
|
self.lcd.set_cursor_pos(0x40);
|
||||||
|
self.lcd.write_str(" ").unwrap();
|
||||||
|
self.lcd.set_cursor_pos(0x40);
|
||||||
|
let _ = write!(self.lcd, "{}\u{00df}C", self.p1_deg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
71
src/main.rs
71
src/main.rs
@ -1,10 +1,6 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
#![no_main]
|
#![no_main]
|
||||||
|
|
||||||
mod lcd_gui;
|
|
||||||
|
|
||||||
use lcd_gui::LCDGui;
|
|
||||||
|
|
||||||
extern crate panic_halt;
|
extern crate panic_halt;
|
||||||
|
|
||||||
use core::{
|
use core::{
|
||||||
@ -22,6 +18,7 @@ use embedded_hal::digital::{
|
|||||||
|
|
||||||
use stm32f1xx_hal::{
|
use stm32f1xx_hal::{
|
||||||
gpio::*,
|
gpio::*,
|
||||||
|
adc::{Adc, SampleTime},
|
||||||
pac,
|
pac,
|
||||||
pac::{interrupt, Interrupt, EXTI},
|
pac::{interrupt, Interrupt, EXTI},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
@ -30,13 +27,27 @@ use stm32f1xx_hal::{
|
|||||||
rtc::Rtc,
|
rtc::Rtc,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
mod lcd_gui;
|
||||||
|
use lcd_gui::LCDGui;
|
||||||
|
|
||||||
|
mod utils;
|
||||||
|
use utils::{
|
||||||
|
TemperatureProbe,
|
||||||
|
};
|
||||||
|
|
||||||
|
mod config;
|
||||||
|
use config::SystemConfig;
|
||||||
|
|
||||||
|
mod state;
|
||||||
|
use state::SystemState;
|
||||||
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------------------
|
||||||
/* system config */
|
/* system config */
|
||||||
|
|
||||||
const GUI_TICK_SEC: f32 = 0.2;
|
const GUI_TICK_SEC: f32 = 0.2;
|
||||||
const HEARTBEAT_SEC: f32 = 1.0;
|
const HEARTBEAT_SEC: f32 = 1.0;
|
||||||
|
|
||||||
const TEMPS_TICK_SEC: u32 = 5;
|
const TEMPS_TICK_SEC: u32 = 2;
|
||||||
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------------------
|
||||||
/* interrupt variables */
|
/* interrupt variables */
|
||||||
@ -53,10 +64,9 @@ static BUTTON_PIN: Mutex<RefCell<Option<gpiob::PB8<Input<PullUp>>>>>
|
|||||||
static BUTTON_FLAG: AtomicBool = AtomicBool::new(false);
|
static BUTTON_FLAG: AtomicBool = AtomicBool::new(false);
|
||||||
|
|
||||||
// temps interrupt
|
// temps interrupt
|
||||||
static RED_LED: Mutex<RefCell<Option<gpiob::PB11<Output<PushPull>>>>>
|
|
||||||
= Mutex::new(RefCell::new(None));
|
|
||||||
static RTC: Mutex<RefCell<Option<Rtc>>> = Mutex::new(RefCell::new(None));
|
static RTC: Mutex<RefCell<Option<Rtc>>> = Mutex::new(RefCell::new(None));
|
||||||
static G_EXTI: Mutex<RefCell<Option<EXTI>>> = Mutex::new(RefCell::new(None));
|
static G_EXTI: Mutex<RefCell<Option<EXTI>>> = Mutex::new(RefCell::new(None));
|
||||||
|
static TEMP_FLAG: AtomicBool = AtomicBool::new(false);
|
||||||
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------------------
|
||||||
/* interrupt service routines */
|
/* interrupt service routines */
|
||||||
@ -101,9 +111,7 @@ fn RTCALARM() {
|
|||||||
rtc.as_mut().unwrap().set_alarm(time + TEMPS_TICK_SEC); //also clears the flag
|
rtc.as_mut().unwrap().set_alarm(time + TEMPS_TICK_SEC); //also clears the flag
|
||||||
});
|
});
|
||||||
|
|
||||||
cortex_m::interrupt::free(|cs| {
|
TEMP_FLAG.store(true, Ordering::Release);
|
||||||
let _ = RED_LED.borrow(cs).borrow_mut().as_mut().unwrap().toggle();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------------------
|
||||||
@ -146,14 +154,6 @@ fn main() -> ! {
|
|||||||
TICK_TIMER.borrow(cs).borrow_mut().replace(timer)
|
TICK_TIMER.borrow(cs).borrow_mut().replace(timer)
|
||||||
});
|
});
|
||||||
|
|
||||||
// setup LED
|
|
||||||
let mut red_led = gpiob.pb11.into_push_pull_output(&mut gpiob.crh);
|
|
||||||
let _ = red_led.set_high();
|
|
||||||
|
|
||||||
cortex_m::interrupt::free(|cs| {
|
|
||||||
RED_LED.borrow(cs).borrow_mut().replace(red_led)
|
|
||||||
});
|
|
||||||
|
|
||||||
// set EXTI 17 (see note in 18.4.2, in short : needed for rtc ISR to trigger)
|
// set EXTI 17 (see note in 18.4.2, in short : needed for rtc ISR to trigger)
|
||||||
dp.EXTI.ftsr.write(|w| w.tr17().set_bit());
|
dp.EXTI.ftsr.write(|w| w.tr17().set_bit());
|
||||||
dp.EXTI.imr.write(|w| w.mr17().set_bit());
|
dp.EXTI.imr.write(|w| w.mr17().set_bit());
|
||||||
@ -256,7 +256,40 @@ fn main() -> ! {
|
|||||||
//dp.PWR.cr.write(|w| w.lpds().set_bit());
|
//dp.PWR.cr.write(|w| w.lpds().set_bit());
|
||||||
cp.SCB.set_sleepdeep();
|
cp.SCB.set_sleepdeep();
|
||||||
|
|
||||||
|
// setup adc
|
||||||
|
let mut adc = Adc::adc1(dp.ADC1, &mut rcc.apb2, clocks);
|
||||||
|
adc.set_sample_time(SampleTime::T_71);
|
||||||
|
let adc = RefCell::new(adc);
|
||||||
|
let ext1 = gpioa.pa4.into_analog(&mut gpioa.crl);
|
||||||
|
let ext2 = gpioa.pa5.into_analog(&mut gpioa.crl);
|
||||||
|
let p1_1 = gpioa.pa2.into_analog(&mut gpioa.crl);
|
||||||
|
let p1_2 = gpioa.pa3.into_analog(&mut gpioa.crl);
|
||||||
|
|
||||||
|
let mut ext_probe = TemperatureProbe::new(&adc, ext1, ext2).unwrap();
|
||||||
|
let mut p1_probe = TemperatureProbe::new(&adc, p1_1, p1_2).unwrap();
|
||||||
|
|
||||||
|
// initialize system state
|
||||||
|
let config = SystemConfig::new();
|
||||||
|
let mut state = SystemState::new(config, gpiob.pb12.into_push_pull_output(&mut gpiob.crh));
|
||||||
|
|
||||||
/* run */
|
/* run */
|
||||||
gui.run();
|
let _ = TEMP_FLAG.swap(true, Ordering::Release);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
|
||||||
|
//compute temps
|
||||||
|
if TEMP_FLAG.swap(false, Ordering::AcqRel) {
|
||||||
|
let ext_temp = ext_probe.read().ok();
|
||||||
|
let p1_temp = p1_probe.read().ok();
|
||||||
|
|
||||||
|
state.update(ext_temp, p1_temp, None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gui.update(&mut state);
|
||||||
|
|
||||||
|
// put device in sleep mode until next interrupt (button or timer)
|
||||||
|
cortex_m::asm::wfi();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
127
src/state.rs
Normal file
127
src/state.rs
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
|
||||||
|
use embedded_hal::digital::v2::OutputPin;
|
||||||
|
|
||||||
|
use crate::config::{
|
||||||
|
SystemConfig,
|
||||||
|
FanOutput,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub enum FanState {
|
||||||
|
ON,
|
||||||
|
OFF,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Fan<O: OutputPin> {
|
||||||
|
state: FanState,
|
||||||
|
pin: O,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<O: OutputPin> Fan<O> {
|
||||||
|
|
||||||
|
pub fn new(mut pin: O) -> Fan<O> {
|
||||||
|
|
||||||
|
// erroring here means the pin is wrongly configured, there is nothing else to do...
|
||||||
|
pin.set_low().map_err(|_| "fan pin configuration").unwrap();
|
||||||
|
Fan {
|
||||||
|
state: FanState::OFF,
|
||||||
|
pin,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn on(&mut self) {
|
||||||
|
self.pin.set_high().map_err(|_| "fan pin configuration").unwrap();
|
||||||
|
self.state = FanState::ON;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn off(&mut self) {
|
||||||
|
self.pin.set_low().map_err(|_| "fan pin configuration").unwrap();
|
||||||
|
self.state = FanState::OFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn state(&self) -> &FanState { &self.state }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SystemState<O: OutputPin> {
|
||||||
|
config: SystemConfig,
|
||||||
|
ext_temp: Option<f32>,
|
||||||
|
p1_temp: Option<f32>,
|
||||||
|
p2_temp: Option<f32>,
|
||||||
|
fan: Fan<O>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<O: OutputPin> SystemState<O> {
|
||||||
|
|
||||||
|
pub fn new(config: SystemConfig, fan_control_pin: O) -> SystemState<O> {
|
||||||
|
|
||||||
|
SystemState {
|
||||||
|
config,
|
||||||
|
ext_temp: None,
|
||||||
|
p1_temp: None,
|
||||||
|
p2_temp: None,
|
||||||
|
fan: Fan::new(fan_control_pin),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn config(&self) -> &SystemConfig { &self.config }
|
||||||
|
pub fn ext_temp(&self) -> Option<f32> { self.ext_temp }
|
||||||
|
pub fn p1_temp(&self) -> Option<f32> { self.p1_temp }
|
||||||
|
pub fn p2_temp(&self) -> Option<f32> { self.p2_temp }
|
||||||
|
|
||||||
|
pub fn update_config(&mut self, config: SystemConfig) {
|
||||||
|
|
||||||
|
// remove offsets
|
||||||
|
let ext_temp = self.ext_temp.map(|temp| temp - self.config.ext_offset);
|
||||||
|
let p1_temp = self.p1_temp.map(|temp| temp - self.config.p1_offset);
|
||||||
|
let p2_temp = self.p2_temp.map(|temp| temp - self.config.p2_offset);
|
||||||
|
|
||||||
|
// replace config
|
||||||
|
self.config = config;
|
||||||
|
|
||||||
|
// reset fan state before recomputing fan state
|
||||||
|
self.fan.off();
|
||||||
|
|
||||||
|
// recompute offsets and fan state
|
||||||
|
self.update(ext_temp, p1_temp, p2_temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(&mut self, ext_temp: Option<f32>, p1_temp: Option<f32>, p2_temp: Option<f32>) {
|
||||||
|
|
||||||
|
// apply offsets
|
||||||
|
self.ext_temp = ext_temp.map(|temp| temp + self.config.ext_offset);
|
||||||
|
self.p1_temp = p1_temp.map(|temp| temp + self.config.p1_offset);
|
||||||
|
self.p2_temp = p2_temp.map(|temp| temp + self.config.p2_offset);
|
||||||
|
|
||||||
|
// select right probe and check if data is available
|
||||||
|
let p = match match self.config.fan_output {
|
||||||
|
FanOutput::P1 => self.p1_temp,
|
||||||
|
FanOutput::P2 => self.p2_temp,
|
||||||
|
} {
|
||||||
|
Some(temp) => temp,
|
||||||
|
None => {
|
||||||
|
self.fan.off();
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let ext = match self.ext_temp {
|
||||||
|
Some(temp) => temp,
|
||||||
|
None => {
|
||||||
|
self.fan.off();
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// compute fan state
|
||||||
|
match self.fan.state() {
|
||||||
|
FanState::ON => {
|
||||||
|
if (p - ext) < self.config.min_temp_diff {
|
||||||
|
self.fan.off();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
FanState::OFF => {
|
||||||
|
if (p - ext) > self.config.max_temp_diff {
|
||||||
|
self.fan.on();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
116
src/utils.rs
Normal file
116
src/utils.rs
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
use core::{
|
||||||
|
marker::PhantomData,
|
||||||
|
cell::RefCell,
|
||||||
|
};
|
||||||
|
|
||||||
|
use embedded_hal::adc::*;
|
||||||
|
|
||||||
|
use libm::*;
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
/* 2nd order linearisation factor for the temperature probe */
|
||||||
|
const A: f32 = -0.00058;
|
||||||
|
const B: f32 = 0.0677;
|
||||||
|
const C: f32 = -0.07;
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
/* error management */
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum ProbeError {
|
||||||
|
ReadError,
|
||||||
|
Initializing,
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
/* TemperatureProbe */
|
||||||
|
const FILTER_TIME_CONSTANT: f32 = 100.0;
|
||||||
|
|
||||||
|
pub struct TemperatureProbe<'a, ADC, O, PINP, PINN>
|
||||||
|
where
|
||||||
|
PINP: Channel<ADC>,
|
||||||
|
PINN: Channel<ADC>,
|
||||||
|
O: OneShot<ADC, u16, PINP> + OneShot<ADC, u16, PINN>,
|
||||||
|
{
|
||||||
|
adc: &'a RefCell<O>,
|
||||||
|
pos_pin: PINP,
|
||||||
|
neg_pin: PINN,
|
||||||
|
phantom: PhantomData<ADC>,
|
||||||
|
|
||||||
|
filtered_temp: f32,
|
||||||
|
stabilized: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_temp<'a, ADC, O, PINP, PINN>(adc: &'a RefCell<O>, pos: &mut PINP, neg: &mut PINN)
|
||||||
|
-> Result<f32, ProbeError>
|
||||||
|
where
|
||||||
|
PINP: Channel<ADC>,
|
||||||
|
PINN: Channel<ADC>,
|
||||||
|
O: OneShot<ADC, u16, PINP> + OneShot<ADC, u16, PINN>,
|
||||||
|
{
|
||||||
|
//first read is bugged, may be due to fake chip
|
||||||
|
let _ = adc.borrow_mut().read(pos);
|
||||||
|
let pos = adc.borrow_mut().read(pos)
|
||||||
|
.map_err(|_| ProbeError::ReadError)? as f32;
|
||||||
|
|
||||||
|
//first read is bugged, may be due to fake chip
|
||||||
|
let _ = adc.borrow_mut().read(neg);
|
||||||
|
let neg = adc.borrow_mut().read(neg)
|
||||||
|
.map_err(|_| ProbeError::ReadError)? as f32;
|
||||||
|
|
||||||
|
// compute back voltage, then reverse linearisation to compute temperature
|
||||||
|
let volt = (pos - neg) * 3.3 / 4095.0;
|
||||||
|
Ok((-B + sqrtf(B*B - 4.0*(C - volt)*A)) / (2.0*A))
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, ADC, O, PINP, PINN> TemperatureProbe<'a, ADC, O, PINP, PINN>
|
||||||
|
where
|
||||||
|
PINP: Channel<ADC>,
|
||||||
|
PINN: Channel<ADC>,
|
||||||
|
O: OneShot<ADC, u16, PINP> + OneShot<ADC, u16, PINN>,
|
||||||
|
{
|
||||||
|
pub fn new(adc: &'a RefCell<O>, pos: PINP, neg: PINN)
|
||||||
|
-> Result<TemperatureProbe<ADC, O, PINP, PINN>, ProbeError> {
|
||||||
|
|
||||||
|
let mut pos_pin = pos;
|
||||||
|
let mut neg_pin = neg;
|
||||||
|
// compute first temp approximation to speed up stabilization
|
||||||
|
let mut temp = 0.0;
|
||||||
|
for _ in 0..10 {
|
||||||
|
temp += read_temp(&adc, &mut pos_pin, &mut neg_pin)?;
|
||||||
|
}
|
||||||
|
temp = temp/10.0;
|
||||||
|
|
||||||
|
Ok(TemperatureProbe {
|
||||||
|
adc,
|
||||||
|
pos_pin,
|
||||||
|
neg_pin,
|
||||||
|
phantom: PhantomData,
|
||||||
|
filtered_temp: temp,
|
||||||
|
stabilized: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read(&mut self) -> Result<f32, ProbeError> {
|
||||||
|
|
||||||
|
let temp = read_temp(self.adc, &mut self.pos_pin, &mut self.neg_pin)?;
|
||||||
|
|
||||||
|
match self.stabilized {
|
||||||
|
true => {
|
||||||
|
self.filtered_temp += (temp - self.filtered_temp)/FILTER_TIME_CONSTANT;
|
||||||
|
Ok(self.filtered_temp)
|
||||||
|
},
|
||||||
|
false => { // filter is not yet stabilized
|
||||||
|
let old_temp = self.filtered_temp;
|
||||||
|
self.filtered_temp += (temp - self.filtered_temp)/FILTER_TIME_CONSTANT;
|
||||||
|
|
||||||
|
match fabsf(old_temp - self.filtered_temp) < 0.01 {
|
||||||
|
true => self.stabilized = true,
|
||||||
|
false => (),
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(ProbeError::Initializing)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Loading…
Reference in New Issue
Block a user