Support fan PWM settings #73

Merged
sb10q merged 16 commits from esavkin/thermostat:69-fan_pwm into master 2023-03-22 17:15:49 +08:00
2 changed files with 75 additions and 8 deletions
Showing only changes of commit ea2eb51b27 - Show all commits

View File

@ -26,9 +26,10 @@ const MAX_TEC_I: f64 = 3.0;
const MAX_FAN_PWM: f64 = 100.0; const MAX_FAN_PWM: f64 = 100.0;
Review

Those naive values will need proper testing and determination at some point.

Those naive values will need proper testing and determination at some point.
const MIN_FAN_PWM: f64 = 1.0; const MIN_FAN_PWM: f64 = 1.0;
const TACHO_MEASURE_MS: i64 = 2500; const TACHO_MEASURE_MS: i64 = 2500;
const TACHO_LOW_THRESHOLD: u32 = 100;
const DEFAULT_K_A: f64 = 1.0; const DEFAULT_K_A: f64 = 1.0;
const DEFAULT_K_B: f64 = 0.0; const DEFAULT_K_B: f64 = 0.0;
const DEFAULT_K_C: f64 = 0.0; const DEFAULT_K_C: f64 = 0.04;
#[derive(Serialize, Copy, Clone)] #[derive(Serialize, Copy, Clone)]
pub struct HWRev { pub struct HWRev {
@ -36,11 +37,20 @@ pub struct HWRev {
pub minor: u8, pub minor: u8,
} }
#[derive(Serialize, Clone, Copy, PartialEq)]
pub enum FanStatus {
OK,
NotAvailable,
Stalled,
LowSignal,
}
struct TachoCtrl { struct TachoCtrl {
tacho: TachoPin, tacho: TachoPin,
tacho_cnt: u32, tacho_cnt: u32,
tacho_value: Option<u32>, tacho_value: Option<u32>,
prev_epoch: i64, prev_epoch: i64,
past_record: u64,
} }
pub struct FanCtrl<'a> { pub struct FanCtrl<'a> {
@ -52,6 +62,7 @@ pub struct FanCtrl<'a> {
k_b: f64, k_b: f64,
k_c: f64, k_c: f64,
channels: &'a mut Channels, channels: &'a mut Channels,
last_status: FanStatus
} }
Review

inline

inline
impl<'a> FanCtrl<'a> { impl<'a> FanCtrl<'a> {
@ -74,14 +85,22 @@ impl<'a> FanCtrl<'a> {
k_b: DEFAULT_K_B, k_b: DEFAULT_K_B,
k_c: DEFAULT_K_C, k_c: DEFAULT_K_C,
Review

this doesn't need to be pub

this doesn't need to be ``pub``
channels, channels,
last_status: FanStatus::OK,
} }
} }
pub fn cycle(&mut self) { pub fn cycle(&mut self) -> Result<(), FanStatus>{
if self.available { if self.available {
self.tacho.cycle(); self.tacho.cycle();
} }
self.adjust_speed(); self.adjust_speed();
let diagnose = self.diagnose();
if diagnose != self.last_status {
self.last_status = diagnose;
Err(diagnose)
} else {
Ok(())
}
} }
pub fn summary(&mut self) -> Result<JsonBuffer, serde_json_core::ser::Error> { pub fn summary(&mut self) -> Result<JsonBuffer, serde_json_core::ser::Error> {
@ -91,6 +110,7 @@ impl<'a> FanCtrl<'a> {
tacho: self.tacho.get(), tacho: self.tacho.get(),
abs_max_tec_i: self.channels.current_abs_max_tec_i(), abs_max_tec_i: self.channels.current_abs_max_tec_i(),
auto_mode: self.fan_auto, auto_mode: self.fan_auto,
status: self.diagnose(),
k_a: self.k_a, k_a: self.k_a,
k_b: self.k_b, k_b: self.k_b,
k_c: self.k_c, k_c: self.k_c,
@ -139,6 +159,13 @@ impl<'a> FanCtrl<'a> {
value as f64 / (max as f64) value as f64 / (max as f64)
} }
fn diagnose(&mut self) -> FanStatus {
if !self.available {
return FanStatus::NotAvailable;
}
self.tacho.diagnose()
}
fn get_pwm(&self) -> u32 { fn get_pwm(&self) -> u32 {
let duty = self.fan.get_duty(); let duty = self.fan.get_duty();
let max = self.fan.get_max_duty(); let max = self.fan.get_max_duty();
@ -147,16 +174,17 @@ impl<'a> FanCtrl<'a> {
} }
impl TachoCtrl { impl TachoCtrl {
pub fn new(tacho: TachoPin) -> Self { fn new(tacho: TachoPin) -> Self {
TachoCtrl { TachoCtrl {
tacho, tacho,
tacho_cnt: 0, tacho_cnt: 0,
tacho_value: None, tacho_value: None,
prev_epoch: 0, prev_epoch: 0,
past_record: 0,
} }
} }
pub fn init(&mut self, exti: &mut EXTI, syscfg: &mut SysCfg) { fn init(&mut self, exti: &mut EXTI, syscfg: &mut SysCfg) {
// These lines do not cause NVIC to run the ISR, // These lines do not cause NVIC to run the ISR,
// since the interrupt should be unmasked in the cortex_m::peripheral::NVIC. // since the interrupt should be unmasked in the cortex_m::peripheral::NVIC.
// Also using interrupt-related workaround is the best // Also using interrupt-related workaround is the best
@ -169,7 +197,17 @@ impl TachoCtrl {
self.tacho.enable_interrupt(exti); self.tacho.enable_interrupt(exti);
} }
pub fn cycle(&mut self) { #[inline]
fn add_record(&mut self, value: u32) {
self.past_record = self.past_record << 2;
if value >= TACHO_LOW_THRESHOLD {
self.past_record += 0b11;
} else if value > 0 && self.tacho_cnt < TACHO_LOW_THRESHOLD {
self.past_record += 0b10;
}
}
fn cycle(&mut self) {
let tacho_input = self.tacho.check_interrupt(); let tacho_input = self.tacho.check_interrupt();
if tacho_input { if tacho_input {
self.tacho.clear_interrupt_pending_bit(); self.tacho.clear_interrupt_pending_bit();
@ -179,14 +217,25 @@ impl TachoCtrl {
let instant = Instant::from_millis(i64::from(timer::now())); let instant = Instant::from_millis(i64::from(timer::now()));
if instant.millis - self.prev_epoch >= TACHO_MEASURE_MS { if instant.millis - self.prev_epoch >= TACHO_MEASURE_MS {
self.tacho_value = Some(self.tacho_cnt); self.tacho_value = Some(self.tacho_cnt);
self.add_record(self.tacho_cnt);
self.tacho_cnt = 0; self.tacho_cnt = 0;
self.prev_epoch = instant.millis; self.prev_epoch = instant.millis;
} }
} }
pub fn get(&self) -> u32 { fn get(&self) -> u32 {
self.tacho_value.unwrap_or(u32::MAX) self.tacho_value.unwrap_or(u32::MAX)
} }
fn diagnose(&mut self) -> FanStatus {
if self.past_record & 0b11 == 0b11 {
FanStatus::OK
} else if self.past_record & 0xAAAAAAAAAAAAAAAA > 0 {
FanStatus::LowSignal
} else {
FanStatus::Stalled
}
}
} }
impl HWRev { impl HWRev {
@ -212,7 +261,19 @@ pub struct FanSummary {
tacho: u32, tacho: u32,
abs_max_tec_i: f64, abs_max_tec_i: f64,
auto_mode: bool, auto_mode: bool,
status: FanStatus,
k_a: f64, k_a: f64,
k_b: f64, k_b: f64,
k_c: f64, k_c: f64,
} }
impl FanStatus {
pub fn fmt_u8(&self) -> &'static [u8] {
match *self {
FanStatus::OK => "Fan is OK".as_bytes(),
FanStatus::NotAvailable => "Fan is not available".as_bytes(),
FanStatus::Stalled => "Fan is stalled".as_bytes(),
FanStatus::LowSignal => "Fan is low signal".as_bytes(),
}
}
}

View File

@ -187,10 +187,10 @@ fn main() -> ! {
if let Some(channel) = updated_channel { if let Some(channel) = updated_channel {
server.for_each(|_, session| session.set_report_pending(channel.into())); server.for_each(|_, session| session.set_report_pending(channel.into()));
} }
fan_ctrl.cycle();
let fan_status = fan_ctrl.cycle();
let instant = Instant::from_millis(i64::from(timer::now())); let instant = Instant::from_millis(i64::from(timer::now()));
cortex_m::interrupt::free(net::clear_pending); cortex_m::interrupt::free(net::clear_pending);
server.poll(instant) server.poll(instant)
.unwrap_or_else(|e| { .unwrap_or_else(|e| {
@ -241,6 +241,12 @@ fn main() -> ! {
} }
} }
} }
match fan_status {
Ok(_) => {}
Err(status) => {
send_line(&mut socket, status.fmt_u8());
}
};
} }
}); });
} else { } else {