laser: check pd_mon params & pwr limit before set

- Design to prevent Issue #47 on hardware kirdy repo from happening
- responsitivity, i_dark & transconductance determine the pwr limit range
- when "ApplyPdParams" and "SetLdPwrLimit" are called, pwr limit range
    is checked before applying new parameter
- Send out a "InvalidSettings" response with error message if settings
    cannot be applied
- Add settable power range to settings json object
This commit is contained in:
linuswck 2024-09-09 17:38:02 +08:00
parent 1111967a7b
commit 86e6d3764e
4 changed files with 107 additions and 27 deletions

View File

@ -2,6 +2,7 @@ use core::marker::PhantomData;
use log::info; use log::info;
use miniconf::Tree; use miniconf::Tree;
use num_traits::Zero;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use stm32f4xx_hal::{pac::{ADC3, TIM2}, use stm32f4xx_hal::{pac::{ADC3, TIM2},
timer::CounterUs}; timer::CounterUs};
@ -42,6 +43,7 @@ struct Settings {
pd_mon_params: pd_mon_params::Parameters, pd_mon_params: pd_mon_params::Parameters,
ld_pwr_limit: Power, ld_pwr_limit: Power,
ld_terms_short: bool, ld_terms_short: bool,
incoming_pd_mon_params: pd_mon_params::Parameters,
} }
impl Default for Settings { impl Default for Settings {
@ -51,6 +53,7 @@ impl Default for Settings {
default_pwr_on: false, default_pwr_on: false,
ld_drive_current: ElectricCurrent::new::<milliampere>(0.0), ld_drive_current: ElectricCurrent::new::<milliampere>(0.0),
pd_mon_params: pd_mon_params::Parameters::default(), pd_mon_params: pd_mon_params::Parameters::default(),
incoming_pd_mon_params: pd_mon_params::Parameters::default(),
ld_pwr_limit: Power::new::<milliwatt>(0.0), ld_pwr_limit: Power::new::<milliwatt>(0.0),
ld_terms_short: false, ld_terms_short: false,
} }
@ -181,24 +184,51 @@ impl LdDrive {
} }
pub fn set_pd_responsitivity(&mut self, responsitivity: pd_mon_params::ResponsitivityUnit) { pub fn set_pd_responsitivity(&mut self, responsitivity: pd_mon_params::ResponsitivityUnit) {
self.settings.pd_mon_params.set(responsitivity); self.settings.incoming_pd_mon_params.set_responsitivity(responsitivity);
self.set_ld_power_limit(self.settings.ld_pwr_limit)
} }
pub fn set_pd_dark_current(&mut self, i_dark: ElectricCurrent) { pub fn set_pd_dark_current(&mut self, i_dark: ElectricCurrent) {
self.settings.pd_mon_params.set_i_dark(i_dark); self.settings.incoming_pd_mon_params.set_i_dark(i_dark);
self.set_ld_power_limit(self.settings.ld_pwr_limit)
} }
pub fn set_ld_power_limit(&mut self, pwr_limit: Power) { pub fn apply_pd_params(&mut self) -> bool {
LdPwrExcProtector::set_trigger_threshold_v( let prev_pd_params = self.settings.pd_mon_params;
self.settings.incoming_pd_mon_params.transconductance = self.settings.pd_mon_params.transconductance;
self.settings.pd_mon_params = self.settings.incoming_pd_mon_params;
let max_settable_pwr = self.get_ld_power_limit_range();
let is_legal = self.settings.ld_pwr_limit <= max_settable_pwr;
if is_legal {
self.set_ld_power_limit(self.settings.ld_pwr_limit);
} else {
self.settings.pd_mon_params = prev_pd_params;
}
is_legal
}
pub fn set_ld_power_limit(&mut self, pwr_limit: Power) -> bool {
let is_legal = LdPwrExcProtector::set_trigger_threshold_v(
self.settings.pd_mon_params.get_pd_i_from_ld_pwr(pwr_limit) / self.settings.pd_mon_params.transconductance, self.settings.pd_mon_params.get_pd_i_from_ld_pwr(pwr_limit) / self.settings.pd_mon_params.transconductance,
); );
self.settings.ld_pwr_limit = pwr_limit; if is_legal {
self.settings.ld_pwr_limit = pwr_limit;
}
is_legal
} }
pub fn set_pd_i_limit(&mut self, i: ElectricCurrent) { pub fn get_ld_power_limit_range(&mut self) -> Power {
LdPwrExcProtector::set_trigger_threshold_v(i / self.settings.pd_mon_params.transconductance); let v_range = LdPwrExcProtector::get_settable_volt_range();
let i_range = self.settings.pd_mon_params.get_pd_i_from_pd_v(v_range);
let max_settable_pwr = self.settings.pd_mon_params.get_ld_pwr_from_ld_i(i_range);
if max_settable_pwr.is_nan() || max_settable_pwr.is_infinite() {
Power::zero()
} else {
max_settable_pwr
}
}
pub fn set_pd_i_limit(&mut self, i: ElectricCurrent) -> bool {
return LdPwrExcProtector::set_trigger_threshold_v(i / self.settings.pd_mon_params.transconductance);
} }
pub fn set_default_pwr_on(&mut self, pwr_on: bool) { pub fn set_default_pwr_on(&mut self, pwr_on: bool) {
@ -234,7 +264,10 @@ impl LdDrive {
max: Settings::LD_CURRENT_MAX, max: Settings::LD_CURRENT_MAX,
}, },
pd_mon_params: settings.pd_mon_params, pd_mon_params: settings.pd_mon_params,
ld_pwr_limit: settings.ld_pwr_limit, ld_pwr_limit: LdSettingsSummaryField {
value: settings.ld_pwr_limit,
max: self.get_ld_power_limit_range(),
},
ld_terms_short: settings.ld_terms_short, ld_terms_short: settings.ld_terms_short,
} }
} }
@ -243,10 +276,10 @@ impl LdDrive {
self.power_down(); self.power_down();
self.settings.ld_drive_current = settings.ld_drive_current.value; self.settings.ld_drive_current = settings.ld_drive_current.value;
self.settings.pd_mon_params = settings.pd_mon_params; self.settings.pd_mon_params = settings.pd_mon_params;
self.settings.ld_pwr_limit = settings.ld_pwr_limit; let max_pwr_limit = self.get_ld_power_limit_range();
self.settings.ld_pwr_limit = settings.ld_pwr_limit.value.min(max_pwr_limit);
self.settings.default_pwr_on = settings.default_pwr_on; self.settings.default_pwr_on = settings.default_pwr_on;
self.set_ld_power_limit(self.settings.ld_pwr_limit);
self.set_ld_power_limit(settings.ld_pwr_limit);
if self.settings.ld_terms_short { if self.settings.ld_terms_short {
self.ld_short(); self.ld_short();
@ -267,7 +300,7 @@ pub struct LdSettingsSummary {
default_pwr_on: bool, default_pwr_on: bool,
ld_drive_current: LdSettingsSummaryField<ElectricCurrent>, ld_drive_current: LdSettingsSummaryField<ElectricCurrent>,
pd_mon_params: pd_mon_params::Parameters, pd_mon_params: pd_mon_params::Parameters,
ld_pwr_limit: Power, ld_pwr_limit: LdSettingsSummaryField<Power>,
ld_terms_short: bool, ld_terms_short: bool,
} }

View File

@ -1,4 +1,4 @@
use num_traits::Zero; use num_traits::{Float, Zero};
use stm32f4xx_hal::{adc::{config::{self, AdcConfig}, use stm32f4xx_hal::{adc::{config::{self, AdcConfig},
Adc}, Adc},
gpio::{gpioa::PA3, gpiod::PD9, Analog, Output, PushPull}, gpio::{gpioa::PA3, gpiod::PD9, Analog, Output, PushPull},
@ -167,14 +167,18 @@ impl LdPwrExcProtector {
ElectricPotential::new::<millivolt>(0.0) ElectricPotential::new::<millivolt>(0.0)
} }
pub fn set_trigger_threshold_v(htr: ElectricPotential) { pub fn set_trigger_threshold_v(htr: ElectricPotential) -> bool {
if let Some(ref mut wdg) = LdPwrExcProtector::get() { if let Some(ref mut wdg) = LdPwrExcProtector::get() {
let code: u32 = ((((htr / (ElectricPotential::new::<millivolt>(wdg.calibrated_vdda as f32))).get::<ratio>() let code: u32 = (((htr / (ElectricPotential::new::<millivolt>(wdg.calibrated_vdda as f32))).get::<ratio>()
* (MAX_SAMPLE as f32)) as u32) * (MAX_SAMPLE as f32))
+ wdg.offset) .ceil() as u32)
.min(MAX_SAMPLE as u32); + wdg.offset;
wdg.pac.htr.write(|w| unsafe { w.bits(code) }); if code <= MAX_SAMPLE as u32 {
wdg.pac.htr.write(|w| unsafe { w.bits(code) });
return true;
}
} }
false
} }
pub fn set_calibrated_vdda(val: u32) { pub fn set_calibrated_vdda(val: u32) {
@ -209,6 +213,15 @@ impl LdPwrExcProtector {
Status::default() Status::default()
} }
pub fn get_settable_volt_range() -> ElectricPotential {
if let Some(ref mut wdg) = LdPwrExcProtector::get() {
return ElectricPotential::new::<millivolt>(wdg.calibrated_vdda as f32)
* ((MAX_SAMPLE as u32 - wdg.offset) as f32)
/ MAX_SAMPLE as f32;
}
ElectricPotential::zero()
}
pub fn pwr_on_and_arm_protection() { pub fn pwr_on_and_arm_protection() {
if let Some(ref mut wdg) = LdPwrExcProtector::get() { if let Some(ref mut wdg) = LdPwrExcProtector::get() {
if !wdg.alarm_status.pwr_excursion { if !wdg.alarm_status.pwr_excursion {

View File

@ -33,14 +33,22 @@ impl Parameters {
(v * self.transconductance).max(ElectricCurrent::zero()) (v * self.transconductance).max(ElectricCurrent::zero())
} }
pub fn set(&mut self, responsitivity: ResponsitivityUnit) { pub fn set_responsitivity(&mut self, responsitivity: ResponsitivityUnit) {
self.responsitivity = responsitivity; self.responsitivity = responsitivity;
} }
pub fn get_responsitivity(&mut self) -> ResponsitivityUnit {
self.responsitivity
}
pub fn set_i_dark(&mut self, i_dark: ElectricCurrent) { pub fn set_i_dark(&mut self, i_dark: ElectricCurrent) {
self.i_dark = i_dark; self.i_dark = i_dark;
} }
pub fn get_i_dark(&mut self) -> ElectricCurrent {
self.i_dark
}
pub fn set_transconductance(&mut self, transconductance: ElectricalConductance) { pub fn set_transconductance(&mut self, transconductance: ElectricalConductance) {
self.transconductance = transconductance; self.transconductance = transconductance;
} }

View File

@ -29,6 +29,7 @@ pub enum ResponseEnum {
HwRev, HwRev,
Acknowledge, Acknowledge,
InvalidDatatype, InvalidDatatype,
InvalidSettings,
InvalidCmd, InvalidCmd,
HardReset, HardReset,
Dfu, Dfu,
@ -89,6 +90,7 @@ enum LdCmdEnum {
// PD Mon Related // PD Mon Related
SetPdResponsitivity, SetPdResponsitivity,
SetPdDarkCurrent, SetPdDarkCurrent,
ApplyPdParams,
SetLdPwrLimit, SetLdPwrLimit,
ClearAlarm, ClearAlarm,
} }
@ -135,6 +137,8 @@ const ERR_MSG_MISSING_SINC5SINC1ODR: &str = "Required field \"sinc5sinc1odr\" do
const ERR_MSG_MISSING_SINC3ODR: &str = "Required field \"sinc3odr\" does not exist"; const ERR_MSG_MISSING_SINC3ODR: &str = "Required field \"sinc3odr\" does not exist";
const ERR_MSG_MISSING_POSTFILTER: &str = "Required field \"PostFilter\" does not exist"; const ERR_MSG_MISSING_POSTFILTER: &str = "Required field \"PostFilter\" does not exist";
const ERR_MSG_MISSING_SINC3FINEODR: &str = "Required field \"sinc3fineodr\" does not exist"; const ERR_MSG_MISSING_SINC3FINEODR: &str = "Required field \"sinc3fineodr\" does not exist";
const ERR_MSG_INVALID_PDMON_SETTINGS: &str = "Invalid PD Mon Parameter Setting(s)";
const ERR_MSG_INVALID_LD_PWR_LIMIT_SETTING: &str = "Invalid LD Power Limit Setting";
#[derive(Deserialize, Serialize, Copy, Clone, Debug, Default, Tree)] #[derive(Deserialize, Serialize, Copy, Clone, Debug, Default, Tree)]
pub struct CmdJsonObj { pub struct CmdJsonObj {
@ -453,12 +457,12 @@ pub fn execute_cmd(
}, },
Some(LdCmdEnum::SetPdResponsitivity) => match cmd.json.data_f32 { Some(LdCmdEnum::SetPdResponsitivity) => match cmd.json.data_f32 {
Some(val) => { Some(val) => {
send_response(buffer, ResponseEnum::Acknowledge, None, socket);
laser.set_pd_responsitivity(ResponsitivityUnit { laser.set_pd_responsitivity(ResponsitivityUnit {
dimension: PhantomData, dimension: PhantomData,
units: PhantomData, units: PhantomData,
value: val, value: val,
}) });
send_response(buffer, ResponseEnum::Acknowledge, None, socket);
} }
None => { None => {
send_response( send_response(
@ -471,8 +475,8 @@ pub fn execute_cmd(
}, },
Some(LdCmdEnum::SetPdDarkCurrent) => match cmd.json.data_f32 { Some(LdCmdEnum::SetPdDarkCurrent) => match cmd.json.data_f32 {
Some(val) => { Some(val) => {
laser.set_pd_dark_current(ElectricCurrent::new::<ampere>(val));
send_response(buffer, ResponseEnum::Acknowledge, None, socket); send_response(buffer, ResponseEnum::Acknowledge, None, socket);
laser.set_pd_dark_current(ElectricCurrent::new::<ampere>(val))
} }
None => { None => {
send_response( send_response(
@ -483,10 +487,32 @@ pub fn execute_cmd(
); );
} }
}, },
Some(LdCmdEnum::ApplyPdParams) => {
let is_legal = laser.apply_pd_params();
if is_legal {
send_response(buffer, ResponseEnum::Acknowledge, None, socket);
} else {
send_response(
buffer,
ResponseEnum::InvalidSettings,
Some(ERR_MSG_INVALID_PDMON_SETTINGS),
socket,
)
}
}
Some(LdCmdEnum::SetLdPwrLimit) => match cmd.json.data_f32 { Some(LdCmdEnum::SetLdPwrLimit) => match cmd.json.data_f32 {
Some(val) => { Some(val) => {
send_response(buffer, ResponseEnum::Acknowledge, None, socket); let is_legal = laser.set_ld_power_limit(Power::new::<watt>(val));
laser.set_ld_power_limit(Power::new::<watt>(val)) if is_legal {
send_response(buffer, ResponseEnum::Acknowledge, None, socket);
} else {
send_response(
buffer,
ResponseEnum::InvalidSettings,
Some(ERR_MSG_INVALID_LD_PWR_LIMIT_SETTING),
socket,
)
}
} }
None => { None => {
send_response( send_response(