load_switch/
lib.rs

1mod gtsr_gpio;
2
3use std::marker::PhantomData;
4
5use tm4c123x_hal as hal;
6
7use self::hal::prelude;
8
9pub const LS_OVERCURRENT_DISABLE: f64 = 99999.9;
10pub const LS_MIN_EXPECT_CURRENT_DISABLE: f64 = -99999.9;
11pub const RATIO_DISNST_DT: f64 = 0.011;
12pub const TEMP_CONVERSION_OFFSET: f64 = -0.85 / RATIO_DISNST_DT + 25.0;
13pub const OVERCURRENT_RECLOSES: u8 = 3;
14
15#[derive(Copy, Clone, Debug, PartialEq, Eq)]
16pub enum LoadSwitchChannel {
17    LoadSwitchCh1 = 0,
18    LoadSwitchCh2 = 1,
19}
20
21#[derive(Copy, Clone, Debug, PartialEq, Eq)]
22pub enum LoadSwitchChannelStatus {
23    LsStatusNominalOn = 1,
24    LsStatusNominalOff = 0,
25    LsStatusWarningLowCurrent = -1,
26    LsStatusFaultOpenCircuit = -2,
27    LsStatusFaultOverCurrent = -3,
28}
29
30#[derive(Copy, Clone, Debug, PartialEq, Eq)]
31pub enum SensorEnablingStatus {
32    Chan1Sensing = 1, // sel[0] and sel[1] are not connected. Only channel 1 current sensing is available.
33    Chan12Sensing = 2, // sel[0] is not connected: Ch1,2 current sense enabled, temperature sensing not enabled
34    TempSensing = 3,   // sel[0] is connected, temperature sensing is enabled
35}
36
37#[allow(deprecated)]
38pub struct LoadSwitch<
39    'a,
40    L: gtsr_gpio::Pin
41        + prelude::_embedded_hal_digital_OutputPin
42        + prelude::_embedded_hal_digital_ToggleableOutputPin,
43    S1: gtsr_gpio::Pin
44        + prelude::_embedded_hal_digital_OutputPin
45        + prelude::_embedded_hal_digital_ToggleableOutputPin,
46    S2: gtsr_gpio::Pin
47        + prelude::_embedded_hal_digital_OutputPin
48        + prelude::_embedded_hal_digital_ToggleableOutputPin,
49    EN1: gtsr_gpio::Pin
50        + prelude::_embedded_hal_digital_OutputPin
51        + prelude::_embedded_hal_digital_ToggleableOutputPin,
52    EN2: gtsr_gpio::Pin
53        + prelude::_embedded_hal_digital_OutputPin
54        + prelude::_embedded_hal_digital_ToggleableOutputPin,
55    DIA: gtsr_gpio::Pin
56        + prelude::_embedded_hal_digital_OutputPin
57        + prelude::_embedded_hal_digital_ToggleableOutputPin,
58> where
59    &'a mut L: Default,
60    &'a mut S1: Default,
61    &'a mut S2: Default,
62    &'a mut EN1: Default,
63    &'a mut EN2: Default,
64    &'a mut DIA: Default,
65{
66    latch: gtsr_gpio::Signal<'a, L>,
67    sel1: gtsr_gpio::Signal<'a, S1>,
68    sel2: gtsr_gpio::Signal<'a, S2>,
69    en1: gtsr_gpio::Signal<'a, EN1>,
70    en2: gtsr_gpio::Signal<'a, EN2>,
71    dia_en: gtsr_gpio::Signal<'a, DIA>,
72    channel_state: SensorEnablingStatus,
73
74    overcurrent_threshold: [f64; 2],
75    min_expected_current: [f64; 2],
76    current: [f64; 2],
77    status: [LoadSwitchChannelStatus; 2],
78    latching: bool,
79    overcurrent_retries: [u8; 2],
80    temperature: f64,
81    current_scale_factor: f64,
82    temperature_scale_factor: f64,
83
84    _latch_pin: PhantomData<L>,
85    _sel1_pin: PhantomData<S1>,
86    _sel2_pin: PhantomData<S2>,
87    _en1_pin: PhantomData<EN1>,
88    _en2_pin: PhantomData<EN2>,
89    _dia_en_pin: PhantomData<DIA>,
90}
91
92#[allow(deprecated)]
93impl<
94        L: gtsr_gpio::Pin
95            + prelude::_embedded_hal_digital_OutputPin
96            + prelude::_embedded_hal_digital_ToggleableOutputPin,
97        S1: gtsr_gpio::Pin
98            + prelude::_embedded_hal_digital_OutputPin
99            + prelude::_embedded_hal_digital_ToggleableOutputPin,
100        S2: gtsr_gpio::Pin
101            + prelude::_embedded_hal_digital_OutputPin
102            + prelude::_embedded_hal_digital_ToggleableOutputPin,
103        EN1: gtsr_gpio::Pin
104            + prelude::_embedded_hal_digital_OutputPin
105            + prelude::_embedded_hal_digital_ToggleableOutputPin,
106        EN2: gtsr_gpio::Pin
107            + prelude::_embedded_hal_digital_OutputPin
108            + prelude::_embedded_hal_digital_ToggleableOutputPin,
109        DIA: gtsr_gpio::Pin
110            + prelude::_embedded_hal_digital_OutputPin
111            + prelude::_embedded_hal_digital_ToggleableOutputPin,
112    > LoadSwitch<'static, L, S1, S2, EN1, EN2, DIA>
113where
114    &'static mut L: Default,
115    &'static mut S1: Default,
116    &'static mut S2: Default,
117    &'static mut EN1: Default,
118    &'static mut EN2: Default,
119    &'static mut DIA: Default,
120{
121    pub fn new(
122        latch_pin: &'static mut L,
123        sel1_pin: Option<&'static mut S1>,
124        sel2_pin: Option<&'static mut S2>,
125        en1_pin: &'static mut EN1,
126        en2_pin: &'static mut EN2,
127        dia_pin: &'static mut DIA,
128        latching: bool,
129        k_sns_value: f64,
130        r_sns_value: f64,
131        overcurrent_threshold: [f64; 2],
132        min_expected_current: [f64; 2],
133    ) -> Self {
134        let channel_state = if sel1_pin.is_some() && sel2_pin.is_some() {
135            SensorEnablingStatus::TempSensing
136        } else if sel1_pin.is_some() && sel2_pin.is_none() {
137            SensorEnablingStatus::Chan12Sensing
138        } else {
139            SensorEnablingStatus::Chan1Sensing
140        };
141
142        let mut load_switch = LoadSwitch {
143            latch: gtsr_gpio::Signal::new(latch_pin),
144            sel1: gtsr_gpio::Signal::new(sel1_pin.unwrap_or_default()),
145            sel2: gtsr_gpio::Signal::new(sel2_pin.unwrap_or_default()),
146            en1: gtsr_gpio::Signal::new(en1_pin),
147            en2: gtsr_gpio::Signal::new(en2_pin),
148            dia_en: gtsr_gpio::Signal::new(dia_pin),
149            channel_state,
150            overcurrent_threshold: [
151                overcurrent_threshold[LoadSwitchChannel::LoadSwitchCh1 as usize],
152                overcurrent_threshold[LoadSwitchChannel::LoadSwitchCh2 as usize],
153            ],
154            min_expected_current: [
155                min_expected_current[LoadSwitchChannel::LoadSwitchCh1 as usize],
156                min_expected_current[LoadSwitchChannel::LoadSwitchCh2 as usize],
157            ],
158            current: [0.0, 0.0],
159            status: [
160                LoadSwitchChannelStatus::LsStatusNominalOff,
161                LoadSwitchChannelStatus::LsStatusNominalOff,
162            ],
163            latching,
164            overcurrent_retries: [0, 0],
165            temperature: 0.0,
166            current_scale_factor: k_sns_value / r_sns_value,
167            temperature_scale_factor: 1000.0 / (r_sns_value * RATIO_DISNST_DT),
168            _latch_pin: PhantomData,
169            _sel1_pin: PhantomData,
170            _sel2_pin: PhantomData,
171            _en1_pin: PhantomData,
172            _en2_pin: PhantomData,
173            _dia_en_pin: PhantomData,
174        };
175
176        load_switch.latch.set(latching);
177
178        load_switch.channel_off(LoadSwitchChannel::LoadSwitchCh1);
179        load_switch.channel_off(LoadSwitchChannel::LoadSwitchCh2);
180
181        load_switch.dia_en.set_high();
182
183        return load_switch;
184    }
185
186    pub fn channel_on(&mut self, channel: LoadSwitchChannel) {
187        match channel {
188            LoadSwitchChannel::LoadSwitchCh1 => {
189                self.en1.set_high();
190                self.status[LoadSwitchChannel::LoadSwitchCh1 as usize] =
191                    LoadSwitchChannelStatus::LsStatusNominalOn;
192            }
193            LoadSwitchChannel::LoadSwitchCh2 => {
194                self.en2.set_high();
195                self.status[LoadSwitchChannel::LoadSwitchCh2 as usize] =
196                    LoadSwitchChannelStatus::LsStatusNominalOn;
197            }
198        }
199    }
200
201    pub fn channel_off(&mut self, channel: LoadSwitchChannel) {
202        match channel {
203            LoadSwitchChannel::LoadSwitchCh1 => {
204                self.en1.set_low();
205                self.status[LoadSwitchChannel::LoadSwitchCh1 as usize] =
206                    LoadSwitchChannelStatus::LsStatusNominalOff;
207            }
208            LoadSwitchChannel::LoadSwitchCh2 => {
209                self.en2.set_low();
210                self.status[LoadSwitchChannel::LoadSwitchCh2 as usize] =
211                    LoadSwitchChannelStatus::LsStatusNominalOff;
212            }
213        }
214    }
215
216    fn set_sel(&mut self, sel: i8) -> i8 {
217        match sel {
218            0 | 1 | 2 => {
219                if self.channel_state == SensorEnablingStatus::TempSensing {
220                    self.sel1.set((sel & 0x3) >> 1 != 0);
221                    self.sel2.set(sel & 0x1 != 0);
222                } else if self.channel_state == SensorEnablingStatus::Chan12Sensing {
223                    self.sel2.set(sel & 0x1 != 0);
224                }
225            }
226            _ => return -1,
227        }
228
229        return 0;
230    }
231
232    pub fn update_sns(&mut self, sns_voltage: f64, step_sel: bool) {
233        let sel = self.sel2.is_set() as u8 | (self.sel1.is_set() as u8) << 1;
234        if !self.dia_en.is_set() {
235            match sel {
236                0 => {
237                    self.current[0] = self.current_scale_factor * sns_voltage;
238
239                    if self.current[0] > self.overcurrent_threshold[0] {
240                        match self.en1.get_state() {
241                            gtsr_gpio::SignalState::Low => {
242                                self.status[0] = LoadSwitchChannelStatus::LsStatusFaultOpenCircuit;
243                            }
244                            gtsr_gpio::SignalState::High => {
245                                self.channel_off(LoadSwitchChannel::LoadSwitchCh1);
246                                self.status[0] = LoadSwitchChannelStatus::LsStatusFaultOverCurrent;
247                            }
248                        }
249                    } else if self.en1.is_set() && self.current[0] < self.min_expected_current[0] {
250                        self.status[0] = LoadSwitchChannelStatus::LsStatusWarningLowCurrent;
251                    } else if self.status[0] == LoadSwitchChannelStatus::LsStatusFaultOverCurrent
252                        && !self.latching
253                        && self.current[0] < self.overcurrent_threshold[0]
254                        && self.overcurrent_retries[0] < OVERCURRENT_RECLOSES
255                    {
256                        self.channel_on(LoadSwitchChannel::LoadSwitchCh1);
257                    } else if self.status[0] != LoadSwitchChannelStatus::LsStatusFaultOverCurrent {
258                        self.status[0] = if self.en1.is_set() {
259                            LoadSwitchChannelStatus::LsStatusNominalOn
260                        } else {
261                            LoadSwitchChannelStatus::LsStatusNominalOff
262                        };
263                    }
264                }
265                1 => {
266                    self.current[1] = self.current_scale_factor * sns_voltage;
267
268                    if self.current[1] > self.overcurrent_threshold[1] {
269                        match self.en2.get_state() {
270                            gtsr_gpio::SignalState::Low => {
271                                self.status[1] = LoadSwitchChannelStatus::LsStatusFaultOpenCircuit;
272                            }
273                            gtsr_gpio::SignalState::High => {
274                                self.channel_off(LoadSwitchChannel::LoadSwitchCh2);
275                                self.status[1] = LoadSwitchChannelStatus::LsStatusFaultOverCurrent;
276                            }
277                        }
278                    } else if self.en2.is_set() && self.current[1] < self.min_expected_current[1] {
279                        self.status[1] = LoadSwitchChannelStatus::LsStatusWarningLowCurrent;
280                    } else if self.status[1] == LoadSwitchChannelStatus::LsStatusFaultOverCurrent
281                        && !self.latching
282                        && self.current[1] < self.overcurrent_threshold[1]
283                        && self.overcurrent_retries[1] < OVERCURRENT_RECLOSES
284                    {
285                        self.channel_on(LoadSwitchChannel::LoadSwitchCh2);
286                    } else if self.status[1] != LoadSwitchChannelStatus::LsStatusFaultOverCurrent {
287                        self.status[1] = if self.en2.is_set() {
288                            LoadSwitchChannelStatus::LsStatusNominalOn
289                        } else {
290                            LoadSwitchChannelStatus::LsStatusNominalOff
291                        };
292                    }
293                }
294                2 => {
295                    self.temperature =
296                        self.temperature_scale_factor * sns_voltage + TEMP_CONVERSION_OFFSET;
297                }
298                _ => {
299                    self.current[LoadSwitchChannel::LoadSwitchCh1 as usize] =
300                        self.current_scale_factor * sns_voltage;
301                    self.current[LoadSwitchChannel::LoadSwitchCh2 as usize] =
302                        self.current_scale_factor * sns_voltage;
303                }
304            }
305        }
306
307        if step_sel {
308            self.set_sel((sel as i8 + 1) % self.channel_state as i8);
309        }
310    }
311}