Compare commits
10 Commits
07ea733b34
...
f6677d874c
Author | SHA1 | Date |
---|---|---|
linuswck | f6677d874c | |
linuswck | d391e3a1fb | |
linuswck | edd30e94a0 | |
linuswck | ad731c2f15 | |
linuswck | 9d8a553669 | |
linuswck | e22f424531 | |
linuswck | 6af0f992d5 | |
linuswck | f50505feaf | |
linuswck | 9ae867cd88 | |
linuswck | 85b50bf824 |
|
@ -5,8 +5,4 @@ rustflags = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[build]
|
[build]
|
||||||
# Pick ONE of these compilation targets
|
|
||||||
# target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+
|
|
||||||
# target = "thumbv7m-none-eabi" # Cortex-M3
|
|
||||||
# target = "thumbv7em-none-eabi" # Cortex-M4 and Cortex-M7 (no FPU)
|
|
||||||
target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU)
|
target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU)
|
||||||
|
|
|
@ -13,6 +13,7 @@ use stm32f4xx_hal::{
|
||||||
watchdog::IndependentWatchdog,
|
watchdog::IndependentWatchdog,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use uom::si::electric_current::milliampere;
|
||||||
use uom::si::{electric_current::ampere, f64::ElectricCurrent};
|
use uom::si::{electric_current::ampere, f64::ElectricCurrent};
|
||||||
|
|
||||||
#[cfg(not(feature = "semihosting"))]
|
#[cfg(not(feature = "semihosting"))]
|
||||||
|
@ -65,6 +66,7 @@ pub fn bootup(
|
||||||
laser.ld_open();
|
laser.ld_open();
|
||||||
laser.set_ld_drive_current_limit(ElectricCurrent::new::<ampere>(0.2));
|
laser.set_ld_drive_current_limit(ElectricCurrent::new::<ampere>(0.2));
|
||||||
laser.ld_set_i(ElectricCurrent::new::<ampere>(0.15));
|
laser.ld_set_i(ElectricCurrent::new::<ampere>(0.15));
|
||||||
|
laser.set_pd_i_limit(ElectricCurrent::new::<milliampere>(2.5));
|
||||||
laser.power_up();
|
laser.power_up();
|
||||||
|
|
||||||
let tec_driver = MAX1968::new(max1968_phy, perif.ADC1);
|
let tec_driver = MAX1968::new(max1968_phy, perif.ADC1);
|
||||||
|
@ -76,6 +78,8 @@ pub fn bootup(
|
||||||
thermostat.calibrate_dac_value();
|
thermostat.calibrate_dac_value();
|
||||||
thermostat.set_i(ElectricCurrent::new::<ampere>(1.0));
|
thermostat.set_i(ElectricCurrent::new::<ampere>(1.0));
|
||||||
|
|
||||||
|
laser.set_pd_mon_calibrated_vdda(thermostat.get_calibrated_vdda());
|
||||||
|
|
||||||
let flash_store = flash_store::store(perif.FLASH);
|
let flash_store = flash_store::store(perif.FLASH);
|
||||||
|
|
||||||
let mut wd = IndependentWatchdog::new(perif.IWDG);
|
let mut wd = IndependentWatchdog::new(perif.IWDG);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::laser_diode::ld_ctrl::{self, LdCtrlPhy};
|
use crate::laser_diode::ld_ctrl::{self, LdCtrlPhy};
|
||||||
use crate::laser_diode::max5719;
|
use crate::laser_diode::max5719;
|
||||||
use crate::laser_diode::analog_wdg::LdAnalogWdgPhy;
|
use crate::laser_diode::ld_pwr_exc_protector::LdPwrExcProtectorPhy;
|
||||||
use crate::thermostat::ad5680;
|
use crate::thermostat::ad5680;
|
||||||
use crate::thermostat::max1968::{self, MAX1968PinSet, MAX1968Phy, PWM_FREQ_KHZ};
|
use crate::thermostat::max1968::{self, MAX1968PinSet, MAX1968Phy, PWM_FREQ_KHZ};
|
||||||
use crate::thermostat::ad7172;
|
use crate::thermostat::ad7172;
|
||||||
|
@ -40,7 +40,7 @@ pub fn setup(
|
||||||
LdCtrlPhy<ld_ctrl::Channel0>,
|
LdCtrlPhy<ld_ctrl::Channel0>,
|
||||||
ad7172::AdcPhy,
|
ad7172::AdcPhy,
|
||||||
MAX1968Phy<max1968::Channel0>,
|
MAX1968Phy<max1968::Channel0>,
|
||||||
LdAnalogWdgPhy
|
LdPwrExcProtectorPhy
|
||||||
) {
|
) {
|
||||||
let gpioa = gpioa.split();
|
let gpioa = gpioa.split();
|
||||||
let gpiob = gpiob.split();
|
let gpiob = gpiob.split();
|
||||||
|
@ -84,7 +84,7 @@ pub fn setup(
|
||||||
current_source_short_pin: gpioa.pa4.into_push_pull_output(),
|
current_source_short_pin: gpioa.pa4.into_push_pull_output(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let pd_mon_phy = LdAnalogWdgPhy {
|
let pd_mon_phy = LdPwrExcProtectorPhy {
|
||||||
_pd_mon_ch0: gpioa.pa3.into_analog(),
|
_pd_mon_ch0: gpioa.pa3.into_analog(),
|
||||||
pwr_en_ch0: gpiod.pd9.into_push_pull_output(),
|
pwr_en_ch0: gpiod.pd9.into_push_pull_output(),
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,208 +0,0 @@
|
||||||
// stm32f4xx_hal does not provide config and driver for analog watchdog yet
|
|
||||||
use stm32f4xx_hal::{
|
|
||||||
pac::{ADC2, NVIC},
|
|
||||||
gpio::{Analog, Output, PushPull, gpioa::PA3, gpiod::PD9},
|
|
||||||
interrupt,
|
|
||||||
};
|
|
||||||
use uom::si::electric_potential::millivolt;
|
|
||||||
use uom::si::f64::ElectricPotential;
|
|
||||||
|
|
||||||
// 12 bit Resolution
|
|
||||||
const MAX_SAMPLE: u16 = 4095;
|
|
||||||
|
|
||||||
pub type LdPwrEnPinType = PD9<Output<PushPull>>;
|
|
||||||
pub type PdMonAdcPinType = PA3<Analog>;
|
|
||||||
|
|
||||||
static mut ANALOG_WDG: Option<LdAnalogWdg> = None;
|
|
||||||
|
|
||||||
pub struct LdAnalogWdgPhy {
|
|
||||||
// To make sure PA3 is configured to Analog mode
|
|
||||||
pub _pd_mon_ch0: PdMonAdcPinType,
|
|
||||||
pub pwr_en_ch0: LdPwrEnPinType,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct AlarmStatus {
|
|
||||||
alarm: bool,
|
|
||||||
val: u16,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct LdAnalogWdg {
|
|
||||||
pac: ADC2,
|
|
||||||
phy: LdAnalogWdgPhy,
|
|
||||||
alarm_status: AlarmStatus,
|
|
||||||
//Calibrated VDDA in millivolt from Adc<ADC1>.calibrate()
|
|
||||||
calibrated_vdda: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LdAnalogWdg {
|
|
||||||
/// ADC interrupt is disabled and continuous conversion is started by default
|
|
||||||
pub fn setup(pac: ADC2, phy: LdAnalogWdgPhy){
|
|
||||||
unsafe { ANALOG_WDG = Some(LdAnalogWdg{
|
|
||||||
pac: pac,
|
|
||||||
phy: phy,
|
|
||||||
alarm_status: AlarmStatus {
|
|
||||||
alarm: false,
|
|
||||||
val: 0x0000,
|
|
||||||
},
|
|
||||||
calibrated_vdda: 3300,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
if let Some(ref mut wdg ) = LdAnalogWdg::get() {
|
|
||||||
wdg.pac.cr1.write(|w| w
|
|
||||||
// 12 Bit Resolution
|
|
||||||
.res().twelve_bit()
|
|
||||||
// Enable Analog Watchdog on Single Regular Channel
|
|
||||||
.awden().enabled()
|
|
||||||
.awdsgl().single_channel()
|
|
||||||
.jawden().disabled()
|
|
||||||
// Disable Analog Watchdog Interrupt
|
|
||||||
.awdie().disabled()
|
|
||||||
// Select Analog Watchdog Channel 3 (PA3) on ADC2
|
|
||||||
.awdch().bits(0x03)
|
|
||||||
);
|
|
||||||
wdg.pac.cr2.write(|w| w
|
|
||||||
// Continous Conversion mode
|
|
||||||
.cont().set_bit()
|
|
||||||
// Enable ADC
|
|
||||||
.adon().set_bit()
|
|
||||||
// Start ADC Conversion
|
|
||||||
.swstart().set_bit()
|
|
||||||
);
|
|
||||||
// Set Sampling Time for Channel 3 to 480 Cycle
|
|
||||||
wdg.pac.smpr2.write(|w| w
|
|
||||||
.smp3().cycles480()
|
|
||||||
);
|
|
||||||
// Set the high threshold to be max value initially
|
|
||||||
wdg.pac.htr.write(|w| w.bits(0xFFFF_FFFF));
|
|
||||||
// Set the high threshold to be min value initially
|
|
||||||
wdg.pac.ltr.write(|w| w.bits(0x0000_0000));
|
|
||||||
// Set the Conversion Sequence to only have Channel 3 (PA3)
|
|
||||||
wdg.pac.sqr3.write(|w| w
|
|
||||||
.sq1().bits(0x03)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get() -> Option<&'static mut Self> {
|
|
||||||
unsafe { ANALOG_WDG.as_mut() }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This fn accepts the calibrated vdda value from Adc<ADC1>.calibrate()
|
|
||||||
pub fn set_calibrated_vdda(val: u32) {
|
|
||||||
if let Some(ref mut wdg ) = LdAnalogWdg::get() {
|
|
||||||
wdg.calibrated_vdda = val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_pd_v() -> ElectricPotential {
|
|
||||||
if let Some(ref mut wdg ) = LdAnalogWdg::get() {
|
|
||||||
let sample = wdg.pac.dr.read().data().bits();
|
|
||||||
|
|
||||||
return ElectricPotential::new::<millivolt>(((u32::from(sample) * wdg.calibrated_vdda) / u32::from(MAX_SAMPLE)) as f64)
|
|
||||||
}
|
|
||||||
ElectricPotential::new::<millivolt>(0.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_alarm(){
|
|
||||||
if let Some(ref mut wdg ) = LdAnalogWdg::get() {
|
|
||||||
wdg.alarm_status = AlarmStatus {
|
|
||||||
alarm: true,
|
|
||||||
val: wdg.pac.dr.read().data().bits(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_alarm_status() -> AlarmStatus {
|
|
||||||
if let Some(ref mut wdg ) = LdAnalogWdg::get() {
|
|
||||||
return wdg.alarm_status.clone()
|
|
||||||
}
|
|
||||||
AlarmStatus {
|
|
||||||
alarm: false,
|
|
||||||
val: 0x0000,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn clear_alarm_status(){
|
|
||||||
if let Some(ref mut wdg ) = LdAnalogWdg::get() {
|
|
||||||
wdg.alarm_status = AlarmStatus {
|
|
||||||
alarm: false,
|
|
||||||
val: 0x0000,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set ADC Watchdog Higher threshold register
|
|
||||||
/// Interrupt is triggered when ADC value is ABOVE the value set
|
|
||||||
pub fn set_htr(htr: u32){
|
|
||||||
if let Some(ref mut wdg ) = LdAnalogWdg::get() {
|
|
||||||
wdg.pac.htr.write(|w| unsafe {w.bits(htr)});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set ADC Watchdog Lower threshold register
|
|
||||||
/// Interrupt is triggered when ADC value is BELOW the value set
|
|
||||||
#[allow(unused)]
|
|
||||||
pub fn set_ltr(ltr:u32){
|
|
||||||
if let Some(ref mut wdg ) = LdAnalogWdg::get() {
|
|
||||||
wdg.pac.ltr.write(|w| unsafe {w.bits(ltr)});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn enable_watchdog_interrupt(){
|
|
||||||
if let Some(ref mut wdg ) = LdAnalogWdg::get() {
|
|
||||||
wdg.pac.cr1.modify(|_, w| w
|
|
||||||
.awdie().set_bit()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn disable_watchdog_interrupt(){
|
|
||||||
if let Some(ref mut wdg ) = LdAnalogWdg::get() {
|
|
||||||
wdg.pac.cr1.modify(|_, w| w
|
|
||||||
.awdie().clear_bit()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn clear_interrupt_bit(){
|
|
||||||
unsafe{
|
|
||||||
NVIC::unmask(interrupt::ADC);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_pwr_engaged() -> bool{
|
|
||||||
if let Some(ref mut wdg ) = LdAnalogWdg::get() {
|
|
||||||
return wdg.phy.pwr_en_ch0.is_set_high()
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn pwr_engage(){
|
|
||||||
if let Some(ref mut wdg ) = LdAnalogWdg::get() {
|
|
||||||
wdg.phy.pwr_en_ch0.set_high()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn pwr_disengage(){
|
|
||||||
if let Some(ref mut wdg ) = LdAnalogWdg::get() {
|
|
||||||
wdg.phy.pwr_en_ch0.set_low()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[interrupt]
|
|
||||||
fn ADC(){
|
|
||||||
cortex_m::interrupt::free(|_| {
|
|
||||||
LdAnalogWdg::set_alarm();
|
|
||||||
LdAnalogWdg::pwr_disengage();
|
|
||||||
// Disable interrupt to avoid getting stuck in infinite interrupt loop
|
|
||||||
LdAnalogWdg::disable_watchdog_interrupt();
|
|
||||||
LdAnalogWdg::clear_interrupt_bit();
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -1,22 +1,24 @@
|
||||||
use miniconf::Miniconf;
|
use miniconf::Miniconf;
|
||||||
use stm32f4xx_hal::pac::ADC2;
|
use stm32f4xx_hal::pac::ADC2;
|
||||||
|
use uom::si::electric_current::ampere;
|
||||||
use crate::laser_diode::ld_ctrl::LdCtrl;
|
use crate::laser_diode::ld_ctrl::LdCtrl;
|
||||||
use crate::laser_diode::analog_wdg::{LdAnalogWdg, self};
|
use crate::laser_diode::ld_pwr_exc_protector::{LdPwrExcProtector, self};
|
||||||
use core::{marker::PhantomData, f64::NAN};
|
use crate::laser_diode::pd_responsitivity;
|
||||||
|
use core::marker::PhantomData;
|
||||||
|
use crate::device::sys_timer::sleep;
|
||||||
|
|
||||||
use uom::si::{
|
use uom::si::{
|
||||||
electric_current::milliampere,
|
electric_current::milliampere,
|
||||||
f64::{ElectricPotential, ElectricCurrent, Power},
|
f64::{ElectricPotential, ElectricCurrent, Power},
|
||||||
};
|
};
|
||||||
|
use num_traits::Float;
|
||||||
|
|
||||||
use uom::{si::{ISQ, SI, Quantity}, typenum::*};
|
use uom::{si::{ISQ, SI, Quantity, ratio::ratio}, typenum::*};
|
||||||
|
|
||||||
// Volt / Ampere
|
// Volt / Ampere
|
||||||
pub type TransimpedanceUnit = Quantity<ISQ<P2, P1, N3, N2, Z0, Z0, Z0>, SI<f64>, f64>;
|
pub type TransimpedanceUnit = Quantity<ISQ<P2, P1, N3, N2, Z0, Z0, Z0>, SI<f64>, f64>;
|
||||||
// Ampere / Volt
|
// Ampere / Volt
|
||||||
type TransconductanceUnit = Quantity<ISQ<N2, N1, P3, P2, Z0, Z0, Z0>, SI<f64>, f64>;
|
type TransconductanceUnit = Quantity<ISQ<N2, N1, P3, P2, Z0, Z0, Z0>, SI<f64>, f64>;
|
||||||
// Watt / Ampere
|
|
||||||
pub type IToPowerUnit = Quantity<ISQ<P2, P1, N3, N1, Z0, Z0, Z0>, SI<f64>, f64>;
|
|
||||||
|
|
||||||
impl Settings{
|
impl Settings{
|
||||||
pub const DAC_OUT_V_MAX: ElectricPotential = ElectricPotential {
|
pub const DAC_OUT_V_MAX: ElectricPotential = ElectricPotential {
|
||||||
|
@ -37,13 +39,22 @@ impl Settings{
|
||||||
units: PhantomData,
|
units: PhantomData,
|
||||||
value: 10.0 / 0.75,
|
value: 10.0 / 0.75,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const LD_CURRENT_STEP_SIZE: ElectricCurrent = ElectricCurrent {
|
||||||
|
dimension: PhantomData,
|
||||||
|
units: PhantomData,
|
||||||
|
value: 0.0001,
|
||||||
|
};
|
||||||
|
|
||||||
|
const LD_CURRENT_TIME_STEP_MS: u32 = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Miniconf)]
|
#[derive(Clone, Debug, Miniconf)]
|
||||||
pub struct Settings {
|
pub struct Settings {
|
||||||
ld_drive_current: ElectricCurrent,
|
ld_drive_current: ElectricCurrent,
|
||||||
ld_drive_current_limit: ElectricCurrent,
|
ld_drive_current_limit: ElectricCurrent,
|
||||||
pd_i_to_out_pwr: IToPowerUnit,
|
#[miniconf(defer)]
|
||||||
|
pd_responsitivity: pd_responsitivity::Parameters,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Settings {
|
impl Default for Settings {
|
||||||
|
@ -51,7 +62,7 @@ impl Default for Settings {
|
||||||
Self {
|
Self {
|
||||||
ld_drive_current: ElectricCurrent::new::<milliampere>(0.0),
|
ld_drive_current: ElectricCurrent::new::<milliampere>(0.0),
|
||||||
ld_drive_current_limit: ElectricCurrent::new::<milliampere>(0.0),
|
ld_drive_current_limit: ElectricCurrent::new::<milliampere>(0.0),
|
||||||
pd_i_to_out_pwr: IToPowerUnit {dimension: PhantomData, units: PhantomData, value: NAN}
|
pd_responsitivity: pd_responsitivity::Parameters::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -62,8 +73,8 @@ pub struct LdDrive{
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LdDrive{
|
impl LdDrive{
|
||||||
pub fn new(current_source: LdCtrl, pins_adc: ADC2, phy: analog_wdg::LdAnalogWdgPhy)-> Self {
|
pub fn new(current_source: LdCtrl, pins_adc: ADC2, phy: ld_pwr_exc_protector::LdPwrExcProtectorPhy)-> Self {
|
||||||
LdAnalogWdg::setup(pins_adc, phy);
|
LdPwrExcProtector::setup(pins_adc, phy);
|
||||||
|
|
||||||
LdDrive {
|
LdDrive {
|
||||||
ctrl: current_source,
|
ctrl: current_source,
|
||||||
|
@ -72,7 +83,7 @@ impl LdDrive{
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setup(&mut self) {
|
pub fn setup(&mut self) {
|
||||||
LdAnalogWdg::pwr_disengage();
|
LdPwrExcProtector::pwr_off();
|
||||||
self.ld_set_i(ElectricCurrent::new::<milliampere>(0.0));
|
self.ld_set_i(ElectricCurrent::new::<milliampere>(0.0));
|
||||||
self.ld_short();
|
self.ld_short();
|
||||||
}
|
}
|
||||||
|
@ -94,47 +105,73 @@ impl LdDrive{
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn power_up(&mut self){
|
pub fn power_up(&mut self){
|
||||||
LdAnalogWdg::pwr_engage();
|
let _ = self.ctrl.set_i(ElectricCurrent::new::<milliampere>(0.0), Settings::LD_DRIVE_TRANSIMPEDANCE, Settings::DAC_OUT_V_MAX);
|
||||||
|
LdPwrExcProtector::pwr_on_and_arm_protection();
|
||||||
|
// Wait for LD Power Supply to start up before driving current to laser diode
|
||||||
|
sleep(30);
|
||||||
|
self.ld_set_i(self.settings.ld_drive_current);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn power_down(&mut self){
|
pub fn power_down(&mut self){
|
||||||
LdAnalogWdg::pwr_disengage();
|
LdPwrExcProtector::pwr_off();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_pd_i(&mut self) -> ElectricCurrent {
|
pub fn get_pd_i(&mut self) -> ElectricCurrent {
|
||||||
LdAnalogWdg::get_pd_v() * Settings::PD_MON_TRANSCONDUCTANCE
|
LdPwrExcProtector::get_status().v * Settings::PD_MON_TRANSCONDUCTANCE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ramping up or down laser diode current according to preset current step size and time step.
|
||||||
pub fn ld_set_i(&mut self, i: ElectricCurrent) -> ElectricCurrent {
|
pub fn ld_set_i(&mut self, i: ElectricCurrent) -> ElectricCurrent {
|
||||||
let ld_i_set = i.min(self.settings.ld_drive_current_limit);
|
let mut prev_i_set = self.settings.ld_drive_current;
|
||||||
let ld_i_set = self.ctrl.set_i(ld_i_set, Settings::LD_DRIVE_TRANSIMPEDANCE, Settings::DAC_OUT_V_MAX);
|
let final_i_set = i.min(self.settings.ld_drive_current_limit).max(ElectricCurrent::new::<ampere>(0.0));
|
||||||
self.settings.ld_drive_current = ld_i_set;
|
|
||||||
ld_i_set
|
let num_of_step = ((final_i_set - prev_i_set)/Settings::LD_CURRENT_STEP_SIZE).get::<ratio>().floor() as i32;
|
||||||
|
|
||||||
|
let current_step = if num_of_step.is_positive() {
|
||||||
|
Settings::LD_CURRENT_STEP_SIZE
|
||||||
|
} else {
|
||||||
|
-Settings::LD_CURRENT_STEP_SIZE
|
||||||
|
};
|
||||||
|
|
||||||
|
for _ in (0..num_of_step.abs()).rev() {
|
||||||
|
prev_i_set = prev_i_set + current_step;
|
||||||
|
let _ = self.ctrl.set_i(prev_i_set, Settings::LD_DRIVE_TRANSIMPEDANCE, Settings::DAC_OUT_V_MAX);
|
||||||
|
sleep(Settings::LD_CURRENT_TIME_STEP_MS);
|
||||||
|
}
|
||||||
|
let prev_i_set = self.ctrl.set_i(final_i_set, Settings::LD_DRIVE_TRANSIMPEDANCE, Settings::DAC_OUT_V_MAX);
|
||||||
|
|
||||||
|
self.settings.ld_drive_current = prev_i_set;
|
||||||
|
prev_i_set
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the calibrated VDDA value obtained from ADC1 calibration
|
// Set the calibrated VDDA value obtained from ADC1 calibration
|
||||||
pub fn set_pd_mon_calibrated_vdda(val_cal: u32) {
|
pub fn set_pd_mon_calibrated_vdda(&mut self, val_cal: u32) {
|
||||||
LdAnalogWdg::set_calibrated_vdda(val_cal)
|
LdPwrExcProtector::set_calibrated_vdda(val_cal)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pd_mon_status() -> analog_wdg::AlarmStatus {
|
pub fn pd_mon_status(&mut self) -> ld_pwr_exc_protector::Status {
|
||||||
LdAnalogWdg::get_alarm_status()
|
LdPwrExcProtector::get_status()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pd_mon_clear_alarm(&mut self) {
|
pub fn pd_mon_clear_alarm(&mut self) {
|
||||||
LdAnalogWdg::clear_alarm_status();
|
LdPwrExcProtector::clear_alarm_status();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pd_mon_engage(){
|
pub fn set_pd_responsitivity(&mut self, responsitivity: pd_responsitivity::ResponsitivityUnit){
|
||||||
LdAnalogWdg::enable_watchdog_interrupt()
|
self.settings.pd_responsitivity.responsitivity = responsitivity;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pd_mon_disengage(){
|
pub fn set_pd_dark_current(&mut self, i_dark: ElectricCurrent){
|
||||||
LdAnalogWdg::disable_watchdog_interrupt()
|
self.settings.pd_responsitivity.i_dark = i_dark;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_ld_power_limit(pwr_limit: Power){
|
pub fn set_ld_power_limit(&mut self, pwr_limit: Power){
|
||||||
// LdAnalogWdg::set_htr(convert pwr_limit to raw adc code)
|
LdPwrExcProtector::set_trigger_threshold_v(self.settings.pd_responsitivity
|
||||||
unimplemented!()
|
.get_ld_i_from_ld_pwr(pwr_limit) / Settings::PD_MON_TRANSCONDUCTANCE
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_pd_i_limit(&mut self, i: ElectricCurrent){
|
||||||
|
LdPwrExcProtector::set_trigger_threshold_v(i / Settings::PD_MON_TRANSCONDUCTANCE);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,237 @@
|
||||||
|
use stm32f4xx_hal::pac;
|
||||||
|
use stm32f4xx_hal::rcc::Enable;
|
||||||
|
use stm32f4xx_hal::{
|
||||||
|
pac::{ADC2, NVIC},
|
||||||
|
gpio::{Analog, Output, PushPull, gpioa::PA3, gpiod::PD9},
|
||||||
|
interrupt,
|
||||||
|
};
|
||||||
|
use uom::si::{
|
||||||
|
electric_potential::millivolt,
|
||||||
|
f64::ElectricPotential,
|
||||||
|
ratio::ratio
|
||||||
|
};
|
||||||
|
|
||||||
|
// 12 bit Resolution
|
||||||
|
const MAX_SAMPLE: u16 = 4095;
|
||||||
|
pub type LdPwrEnPinType = PD9<Output<PushPull>>;
|
||||||
|
pub type PdMonAdcPinType = PA3<Analog>;
|
||||||
|
const PD_MON_ADC_CH_ID: u8 = 0x03;
|
||||||
|
|
||||||
|
static mut LD_PWR_EXC_PROTECTOR: Option<LdPwrExcProtector> = None;
|
||||||
|
|
||||||
|
pub struct LdPwrExcProtectorPhy {
|
||||||
|
// To make sure Pd Mon Pin is configured to Analog mode
|
||||||
|
pub _pd_mon_ch0: PdMonAdcPinType,
|
||||||
|
pub pwr_en_ch0: LdPwrEnPinType,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Status {
|
||||||
|
pub pwr_excursion: bool,
|
||||||
|
pub v_tripped: ElectricPotential,
|
||||||
|
pub pwr_engaged: bool,
|
||||||
|
pub v: ElectricPotential,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Status {
|
||||||
|
fn default() -> Self {
|
||||||
|
Status {
|
||||||
|
pwr_excursion: false,
|
||||||
|
v_tripped: ElectricPotential::new::<millivolt>(0.0),
|
||||||
|
pwr_engaged: false,
|
||||||
|
v: ElectricPotential::new::<millivolt>(0.0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct LdPwrExcProtector {
|
||||||
|
pac: ADC2,
|
||||||
|
phy: LdPwrExcProtectorPhy,
|
||||||
|
alarm_status: Status,
|
||||||
|
calibrated_vdda: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LdPwrExcProtector {
|
||||||
|
/// ADC Analog Watchdog is configured to guard a single regular Adc channel on Pd Mon Pin.
|
||||||
|
/// ADC is configured to start continuous conversion without using DMA immediately.
|
||||||
|
/// Interrupt is disabled by default.
|
||||||
|
pub fn setup(pac_adc: ADC2, mut phy: LdPwrExcProtectorPhy){
|
||||||
|
unsafe {
|
||||||
|
// All ADCs share the same reset interface.
|
||||||
|
// NOTE(unsafe) this reference will only be used for atomic writes with no side effects.
|
||||||
|
let rcc = &(*pac::RCC::ptr());
|
||||||
|
// Enable the ADC2 Clock
|
||||||
|
pac::ADC2::enable(rcc);
|
||||||
|
// Enable ADC Interrupt
|
||||||
|
NVIC::unmask(interrupt::ADC);
|
||||||
|
}
|
||||||
|
|
||||||
|
pac_adc.cr1.reset();
|
||||||
|
pac_adc.cr2.reset();
|
||||||
|
pac_adc.sqr1.reset();
|
||||||
|
pac_adc.sqr2.reset();
|
||||||
|
pac_adc.sqr3.reset();
|
||||||
|
|
||||||
|
pac_adc.cr1.write(|w| w
|
||||||
|
// 12 Bit Resolution
|
||||||
|
.res().twelve_bit()
|
||||||
|
// Set Analog Watchdog to guard Single Regular Channel
|
||||||
|
.awden().enabled()
|
||||||
|
.awdsgl().single_channel()
|
||||||
|
.jawden().disabled()
|
||||||
|
// Disable Analog Watchdog Interrupt
|
||||||
|
.awdie().disabled()
|
||||||
|
// Set Analog Watchdog to monitor Pd Mon Pin
|
||||||
|
.awdch().variant(PD_MON_ADC_CH_ID)
|
||||||
|
);
|
||||||
|
pac_adc.cr2.write(|w| w
|
||||||
|
// Continous Conversion Mode
|
||||||
|
.cont().set_bit()
|
||||||
|
// Power up ADC
|
||||||
|
.adon().set_bit()
|
||||||
|
// Set data alignment to the right
|
||||||
|
.align().right()
|
||||||
|
// End of conversion selection: Each Sequence
|
||||||
|
.eocs().each_sequence()
|
||||||
|
.exten().disabled()
|
||||||
|
.extsel().tim1cc1()
|
||||||
|
);
|
||||||
|
// Set the Conversion Sequence to include Pd Mon Pin
|
||||||
|
pac_adc.sqr3.write(|w| w
|
||||||
|
.sq1().variant(PD_MON_ADC_CH_ID)
|
||||||
|
);
|
||||||
|
// Set all sampling channels to have fastest sampling interval
|
||||||
|
pac_adc.smpr1.reset();
|
||||||
|
pac_adc.smpr2.reset();
|
||||||
|
|
||||||
|
// Set the higher threshold to be max value initially
|
||||||
|
pac_adc.htr.write(|w| w.ht().variant(MAX_SAMPLE));
|
||||||
|
// Set the lower threshold to be min value initially
|
||||||
|
pac_adc.ltr.write(|w| w.lt().variant(0));
|
||||||
|
|
||||||
|
// SWStart should only be set when ADON = 1. Otherwise no conversion is launched.
|
||||||
|
pac_adc.cr2.modify(|_, w| w
|
||||||
|
.swstart().set_bit()
|
||||||
|
);
|
||||||
|
|
||||||
|
phy.pwr_en_ch0.set_low();
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
LD_PWR_EXC_PROTECTOR = Some(
|
||||||
|
LdPwrExcProtector {
|
||||||
|
pac: pac_adc,
|
||||||
|
phy: phy,
|
||||||
|
alarm_status: Status::default(),
|
||||||
|
calibrated_vdda: 3300,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get() -> Option<&'static mut Self> {
|
||||||
|
unsafe { LD_PWR_EXC_PROTECTOR.as_mut() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn convert_sample_to_volt(sample :u16) -> ElectricPotential {
|
||||||
|
if let Some(ref mut wdg ) = LdPwrExcProtector::get() {
|
||||||
|
return ElectricPotential::new::<millivolt>(((u32::from(sample) * wdg.calibrated_vdda) / u32::from(MAX_SAMPLE)) as f64)
|
||||||
|
}
|
||||||
|
ElectricPotential::new::<millivolt>(0.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_trigger_threshold_v(htr: ElectricPotential){
|
||||||
|
if let Some(ref mut wdg ) = LdPwrExcProtector::get() {
|
||||||
|
let code: u32 = ((htr / (ElectricPotential::new::<millivolt>(wdg.calibrated_vdda as f64))).get::<ratio>() * (MAX_SAMPLE as f64)) as u32;
|
||||||
|
wdg.pac.htr.write(|w| unsafe {w.bits(code)});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_calibrated_vdda(val: u32) {
|
||||||
|
if let Some(ref mut wdg ) = LdPwrExcProtector::get() {
|
||||||
|
wdg.calibrated_vdda = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_status() -> Status {
|
||||||
|
if let Some(ref mut wdg ) = LdPwrExcProtector::get() {
|
||||||
|
wdg.alarm_status.v = LdPwrExcProtector::convert_sample_to_volt(wdg.pac.dr.read().data().bits());
|
||||||
|
return wdg.alarm_status.clone()
|
||||||
|
}
|
||||||
|
Status::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pwr_on_and_arm_protection(){
|
||||||
|
if let Some(ref mut wdg ) = LdPwrExcProtector::get() {
|
||||||
|
wdg.alarm_status = Status::default();
|
||||||
|
LdPwrExcProtector::pwr_on();
|
||||||
|
// Interrupt should be enabled after power on to tackle the following edge case:
|
||||||
|
// Pd_Mon pin voltage has already exceed threshold before LD Power is on.
|
||||||
|
LdPwrExcProtector::enable_watchdog_interrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear_alarm_status(){
|
||||||
|
if let Some(ref mut wdg ) = LdPwrExcProtector::get() {
|
||||||
|
wdg.alarm_status.pwr_excursion = false;
|
||||||
|
wdg.alarm_status.v_tripped = ElectricPotential::new::<millivolt>(0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enable_watchdog_interrupt(){
|
||||||
|
if let Some(ref mut wdg ) = LdPwrExcProtector::get() {
|
||||||
|
wdg.pac.cr1.modify(|_, w| w
|
||||||
|
.awdie().set_bit()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn disable_watchdog_interrupt(){
|
||||||
|
if let Some(ref mut wdg ) = LdPwrExcProtector::get() {
|
||||||
|
wdg.pac.cr1.modify(|_, w| w
|
||||||
|
.awdie().clear_bit()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clear_interrupt_bit(){
|
||||||
|
if let Some(ref mut wdg ) = LdPwrExcProtector::get() {
|
||||||
|
wdg.pac.sr.modify(|_, w| w
|
||||||
|
.awd().clear_bit()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pwr_on(){
|
||||||
|
if let Some(ref mut wdg ) = LdPwrExcProtector::get() {
|
||||||
|
wdg.alarm_status.pwr_engaged = true;
|
||||||
|
wdg.phy.pwr_en_ch0.set_high()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pwr_off(){
|
||||||
|
if let Some(ref mut wdg ) = LdPwrExcProtector::get() {
|
||||||
|
wdg.alarm_status.pwr_engaged = false;
|
||||||
|
wdg.phy.pwr_en_ch0.set_low()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pwr_excursion_handler(){
|
||||||
|
if let Some(ref mut wdg ) = LdPwrExcProtector::get() {
|
||||||
|
let sample = wdg.pac.dr.read().data().bits();
|
||||||
|
LdPwrExcProtector::pwr_off();
|
||||||
|
wdg.alarm_status.pwr_excursion = true;
|
||||||
|
wdg.alarm_status.v_tripped = LdPwrExcProtector::convert_sample_to_volt(sample);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[interrupt]
|
||||||
|
fn ADC(){
|
||||||
|
cortex_m::interrupt::free(|_| {
|
||||||
|
LdPwrExcProtector::pwr_excursion_handler();
|
||||||
|
// Disable interrupt to avoid getting stuck in infinite loop
|
||||||
|
LdPwrExcProtector::disable_watchdog_interrupt();
|
||||||
|
LdPwrExcProtector::clear_interrupt_bit();
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
pub mod ld_ctrl;
|
pub mod ld_ctrl;
|
||||||
pub mod max5719;
|
pub mod max5719;
|
||||||
pub mod laser_diode;
|
pub mod laser_diode;
|
||||||
pub mod pd_mon;
|
pub mod pd_responsitivity;
|
||||||
pub mod analog_wdg;
|
pub mod ld_pwr_exc_protector;
|
|
@ -12,19 +12,22 @@ use miniconf::Miniconf;
|
||||||
// Ampere / Watt
|
// Ampere / Watt
|
||||||
pub type ResponsitivityUnit = Quantity<ISQ<N2, N1, P3, P1, Z0, Z0, Z0>, SI<f64>, f64>;
|
pub type ResponsitivityUnit = Quantity<ISQ<N2, N1, P3, P1, Z0, Z0, Z0>, SI<f64>, f64>;
|
||||||
|
|
||||||
/// Steinhart-Hart equation Photodiode
|
|
||||||
#[derive(Clone, Debug, PartialEq, Miniconf)]
|
#[derive(Clone, Debug, PartialEq, Miniconf)]
|
||||||
pub struct Parameters {
|
pub struct Parameters {
|
||||||
/// Responsitivity
|
|
||||||
pub responsitivity: ResponsitivityUnit,
|
pub responsitivity: ResponsitivityUnit,
|
||||||
pub i_dark: ElectricCurrent,
|
pub i_dark: ElectricCurrent,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parameters {
|
impl Parameters {
|
||||||
pub fn get_ld_output_power(&self, i: ElectricCurrent) -> Power {
|
pub fn get_ld_pwr_from_ld_i(&self, i: ElectricCurrent) -> Power {
|
||||||
let ld_power = (i - self.i_dark) / self.responsitivity;
|
let ld_power = (i - self.i_dark) / self.responsitivity;
|
||||||
ld_power
|
ld_power
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_ld_i_from_ld_pwr(&self, pwr: Power) -> ElectricCurrent {
|
||||||
|
let ld_i = pwr * self.responsitivity + self.i_dark;
|
||||||
|
ld_i
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Parameters {
|
impl Default for Parameters {
|
|
@ -81,6 +81,9 @@ fn main() -> ! {
|
||||||
info!("curr_tec_i: {:?}", amp_fmt.with(thermostat.get_tec_i()));
|
info!("curr_tec_i: {:?}", amp_fmt.with(thermostat.get_tec_i()));
|
||||||
info!("curr_tec_v: {:?}", volt_fmt.with(thermostat.get_tec_v()));
|
info!("curr_tec_v: {:?}", volt_fmt.with(thermostat.get_tec_v()));
|
||||||
|
|
||||||
sys_timer::sleep(10);
|
info!("pd_mon_v: {:?}", volt_fmt.with(laser.pd_mon_status().v));
|
||||||
|
info!("power_excursion: {:?}", laser.pd_mon_status().pwr_excursion);
|
||||||
|
|
||||||
|
sys_timer::sleep(500);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -113,18 +113,13 @@ impl<C: ChannelPins> MAX1968Phy<C> {
|
||||||
|
|
||||||
impl MAX1968 {
|
impl MAX1968 {
|
||||||
pub fn new(phy_ch0: MAX1968Phy<Channel0>, adc1: ADC1) -> Self {
|
pub fn new(phy_ch0: MAX1968Phy<Channel0>, adc1: ADC1) -> Self {
|
||||||
// Set ADC to a slowest sampling interval for more accurate calibration
|
|
||||||
let config = AdcConfig::default()
|
let config = AdcConfig::default()
|
||||||
.clock(config::Clock::Pclk2_div_8)
|
.clock(config::Clock::Pclk2_div_8)
|
||||||
.default_sample_time(config::SampleTime::Cycles_480);
|
.default_sample_time(config::SampleTime::Cycles_480);
|
||||||
let mut pins_adc = Adc::adc1(adc1, true, config);
|
// Do not set reset RCCs as it causes other ADCs' clock to be disabled
|
||||||
|
let mut pins_adc = Adc::adc1(adc1, false, config);
|
||||||
pins_adc.calibrate();
|
pins_adc.calibrate();
|
||||||
|
|
||||||
let config = AdcConfig::default()
|
|
||||||
.clock(config::Clock::Pclk2_div_8)
|
|
||||||
.default_sample_time(config::SampleTime::Cycles_480);
|
|
||||||
pins_adc.apply_config(config);
|
|
||||||
|
|
||||||
MAX1968 {
|
MAX1968 {
|
||||||
phy: phy_ch0,
|
phy: phy_ch0,
|
||||||
pins_adc: pins_adc,
|
pins_adc: pins_adc,
|
||||||
|
|
Loading…
Reference in New Issue