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 miniconf::Tree;
use num_traits::Zero;
use serde::{Deserialize, Serialize};
use stm32f4xx_hal::{pac::{ADC3, TIM2},
timer::CounterUs};
@ -42,6 +43,7 @@ struct Settings {
pd_mon_params: pd_mon_params::Parameters,
ld_pwr_limit: Power,
ld_terms_short: bool,
incoming_pd_mon_params: pd_mon_params::Parameters,
}
impl Default for Settings {
@ -51,6 +53,7 @@ impl Default for Settings {
default_pwr_on: false,
ld_drive_current: ElectricCurrent::new::<milliampere>(0.0),
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_terms_short: false,
}
@ -181,24 +184,51 @@ impl LdDrive {
}
pub fn set_pd_responsitivity(&mut self, responsitivity: pd_mon_params::ResponsitivityUnit) {
self.settings.pd_mon_params.set(responsitivity);
self.set_ld_power_limit(self.settings.ld_pwr_limit)
self.settings.incoming_pd_mon_params.set_responsitivity(responsitivity);
}
pub fn set_pd_dark_current(&mut self, i_dark: ElectricCurrent) {
self.settings.pd_mon_params.set_i_dark(i_dark);
self.set_ld_power_limit(self.settings.ld_pwr_limit)
self.settings.incoming_pd_mon_params.set_i_dark(i_dark);
}
pub fn set_ld_power_limit(&mut self, pwr_limit: Power) {
LdPwrExcProtector::set_trigger_threshold_v(
pub fn apply_pd_params(&mut self) -> bool {
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,
);
if is_legal {
self.settings.ld_pwr_limit = pwr_limit;
}
is_legal
}
pub fn set_pd_i_limit(&mut self, i: ElectricCurrent) {
LdPwrExcProtector::set_trigger_threshold_v(i / self.settings.pd_mon_params.transconductance);
pub fn get_ld_power_limit_range(&mut self) -> Power {
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) {
@ -234,7 +264,10 @@ impl LdDrive {
max: Settings::LD_CURRENT_MAX,
},
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,
}
}
@ -243,10 +276,10 @@ impl LdDrive {
self.power_down();
self.settings.ld_drive_current = settings.ld_drive_current.value;
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.set_ld_power_limit(settings.ld_pwr_limit);
self.set_ld_power_limit(self.settings.ld_pwr_limit);
if self.settings.ld_terms_short {
self.ld_short();
@ -267,7 +300,7 @@ pub struct LdSettingsSummary {
default_pwr_on: bool,
ld_drive_current: LdSettingsSummaryField<ElectricCurrent>,
pd_mon_params: pd_mon_params::Parameters,
ld_pwr_limit: Power,
ld_pwr_limit: LdSettingsSummaryField<Power>,
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},
Adc},
gpio::{gpioa::PA3, gpiod::PD9, Analog, Output, PushPull},
@ -167,15 +167,19 @@ impl LdPwrExcProtector {
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() {
let code: u32 = ((((htr / (ElectricPotential::new::<millivolt>(wdg.calibrated_vdda as f32))).get::<ratio>()
* (MAX_SAMPLE as f32)) as u32)
+ wdg.offset)
.min(MAX_SAMPLE as u32);
let code: u32 = (((htr / (ElectricPotential::new::<millivolt>(wdg.calibrated_vdda as f32))).get::<ratio>()
* (MAX_SAMPLE as f32))
.ceil() as u32)
+ wdg.offset;
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) {
if let Some(ref mut wdg) = LdPwrExcProtector::get() {
@ -209,6 +213,15 @@ impl LdPwrExcProtector {
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() {
if let Some(ref mut wdg) = LdPwrExcProtector::get() {
if !wdg.alarm_status.pwr_excursion {

View File

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

View File

@ -29,6 +29,7 @@ pub enum ResponseEnum {
HwRev,
Acknowledge,
InvalidDatatype,
InvalidSettings,
InvalidCmd,
HardReset,
Dfu,
@ -89,6 +90,7 @@ enum LdCmdEnum {
// PD Mon Related
SetPdResponsitivity,
SetPdDarkCurrent,
ApplyPdParams,
SetLdPwrLimit,
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_POSTFILTER: &str = "Required field \"PostFilter\" 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)]
pub struct CmdJsonObj {
@ -453,12 +457,12 @@ pub fn execute_cmd(
},
Some(LdCmdEnum::SetPdResponsitivity) => match cmd.json.data_f32 {
Some(val) => {
send_response(buffer, ResponseEnum::Acknowledge, None, socket);
laser.set_pd_responsitivity(ResponsitivityUnit {
dimension: PhantomData,
units: PhantomData,
value: val,
})
});
send_response(buffer, ResponseEnum::Acknowledge, None, socket);
}
None => {
send_response(
@ -471,8 +475,8 @@ pub fn execute_cmd(
},
Some(LdCmdEnum::SetPdDarkCurrent) => match cmd.json.data_f32 {
Some(val) => {
laser.set_pd_dark_current(ElectricCurrent::new::<ampere>(val));
send_response(buffer, ResponseEnum::Acknowledge, None, socket);
laser.set_pd_dark_current(ElectricCurrent::new::<ampere>(val))
}
None => {
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(val) => {
let is_legal = laser.set_ld_power_limit(Power::new::<watt>(val));
if is_legal {
send_response(buffer, ResponseEnum::Acknowledge, None, socket);
laser.set_ld_power_limit(Power::new::<watt>(val))
} else {
send_response(
buffer,
ResponseEnum::InvalidSettings,
Some(ERR_MSG_INVALID_LD_PWR_LIMIT_SETTING),
socket,
)
}
}
None => {
send_response(