2020-05-13 06:04:55 +08:00
|
|
|
use smoltcp::time::Instant;
|
2020-09-14 04:52:20 +08:00
|
|
|
use uom::si::{
|
|
|
|
f64::ElectricPotential,
|
|
|
|
electric_potential::{millivolt, volt},
|
|
|
|
};
|
2020-05-19 06:15:39 +08:00
|
|
|
use log::info;
|
2020-05-13 05:16:57 +08:00
|
|
|
use crate::{
|
2020-05-19 06:15:39 +08:00
|
|
|
ad5680,
|
2020-05-13 05:16:57 +08:00
|
|
|
ad7172,
|
|
|
|
channel::{Channel, Channel0, Channel1},
|
2020-05-13 06:04:55 +08:00
|
|
|
channel_state::ChannelState,
|
2020-05-13 05:16:57 +08:00
|
|
|
pins,
|
|
|
|
};
|
|
|
|
|
2020-05-13 06:04:55 +08:00
|
|
|
pub const CHANNELS: usize = 2;
|
|
|
|
|
2020-05-19 03:38:13 +08:00
|
|
|
// TODO: -pub
|
2020-05-13 05:16:57 +08:00
|
|
|
pub struct Channels {
|
2020-05-21 03:27:22 +08:00
|
|
|
channel0: Channel<Channel0>,
|
|
|
|
channel1: Channel<Channel1>,
|
2020-05-13 05:16:57 +08:00
|
|
|
pub adc: ad7172::Adc<pins::AdcSpi, pins::AdcNss>,
|
2020-05-28 08:01:55 +08:00
|
|
|
/// stm32f4 integrated adc
|
|
|
|
pins_adc: pins::PinsAdc,
|
2020-05-13 05:16:57 +08:00
|
|
|
pub pwm: pins::PwmPins,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Channels {
|
|
|
|
pub fn new(pins: pins::Pins) -> Self {
|
|
|
|
let channel0 = Channel::new(pins.channel0);
|
|
|
|
let channel1 = Channel::new(pins.channel1);
|
2020-05-28 08:01:55 +08:00
|
|
|
let pins_adc = pins.pins_adc;
|
2020-05-13 05:16:57 +08:00
|
|
|
let pwm = pins.pwm;
|
|
|
|
|
|
|
|
let mut adc = ad7172::Adc::new(pins.adc_spi, pins.adc_nss).unwrap();
|
|
|
|
// Feature not used
|
|
|
|
adc.set_sync_enable(false).unwrap();
|
2020-05-17 08:11:53 +08:00
|
|
|
|
|
|
|
// Setup channels and start ADC
|
2020-05-13 05:16:57 +08:00
|
|
|
adc.setup_channel(0, ad7172::Input::Ain0, ad7172::Input::Ain1).unwrap();
|
|
|
|
adc.setup_channel(1, ad7172::Input::Ain2, ad7172::Input::Ain3).unwrap();
|
2020-05-17 08:11:53 +08:00
|
|
|
adc.start_continuous_conversion().unwrap();
|
2020-05-13 05:16:57 +08:00
|
|
|
|
2020-09-10 05:10:33 +08:00
|
|
|
let mut channels = Channels { channel0, channel1, adc, pins_adc, pwm };
|
|
|
|
for channel in 0..CHANNELS {
|
|
|
|
channels.calibrate_dac_value(channel);
|
|
|
|
}
|
|
|
|
channels
|
2020-05-13 05:16:57 +08:00
|
|
|
}
|
2020-05-13 06:04:55 +08:00
|
|
|
|
|
|
|
pub fn channel_state<I: Into<usize>>(&mut self, channel: I) -> &mut ChannelState {
|
|
|
|
match channel.into() {
|
|
|
|
0 => &mut self.channel0.state,
|
|
|
|
1 => &mut self.channel1.state,
|
|
|
|
_ => unreachable!(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// ADC input + PID processing
|
|
|
|
pub fn poll_adc(&mut self, instant: Instant) -> Option<u8> {
|
|
|
|
self.adc.data_ready().unwrap().map(|channel| {
|
|
|
|
let data = self.adc.read_data().unwrap();
|
|
|
|
|
|
|
|
let dac_value = {
|
|
|
|
let state = self.channel_state(channel);
|
2020-05-14 04:11:25 +08:00
|
|
|
let pid_output = state.update_pid(instant, data);
|
2020-05-13 06:04:55 +08:00
|
|
|
|
|
|
|
if state.pid_engaged {
|
2020-05-14 04:11:25 +08:00
|
|
|
Some(pid_output)
|
2020-05-13 06:04:55 +08:00
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
};
|
|
|
|
if let Some(dac_value) = dac_value {
|
|
|
|
// Forward PID output to i_set DAC
|
2020-09-14 04:52:20 +08:00
|
|
|
self.set_dac(channel.into(), ElectricPotential::new::<volt>(dac_value));
|
2020-05-13 06:04:55 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
channel
|
|
|
|
})
|
|
|
|
}
|
2020-05-13 06:15:29 +08:00
|
|
|
|
2020-05-14 03:02:26 +08:00
|
|
|
/// i_set DAC
|
2020-09-14 04:52:20 +08:00
|
|
|
pub fn set_dac(&mut self, channel: usize, voltage: ElectricPotential) {
|
2020-05-19 06:15:39 +08:00
|
|
|
let dac_factor = match channel.into() {
|
|
|
|
0 => self.channel0.dac_factor,
|
|
|
|
1 => self.channel1.dac_factor,
|
|
|
|
_ => unreachable!(),
|
|
|
|
};
|
2020-09-14 04:52:20 +08:00
|
|
|
let value = (voltage.get::<volt>() * dac_factor) as u32;
|
2020-05-14 03:02:26 +08:00
|
|
|
match channel {
|
|
|
|
0 => {
|
2020-05-19 06:15:39 +08:00
|
|
|
self.channel0.dac.set(value).unwrap();
|
2020-05-17 07:23:35 +08:00
|
|
|
self.channel0.state.dac_value = voltage;
|
2020-05-14 03:02:26 +08:00
|
|
|
}
|
|
|
|
1 => {
|
2020-05-19 06:15:39 +08:00
|
|
|
self.channel1.dac.set(value).unwrap();
|
2020-05-17 07:23:35 +08:00
|
|
|
self.channel1.state.dac_value = voltage;
|
2020-05-14 03:02:26 +08:00
|
|
|
}
|
|
|
|
_ => unreachable!(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-14 04:52:20 +08:00
|
|
|
pub fn read_dac_feedback(&mut self, channel: usize) -> ElectricPotential {
|
2020-05-13 06:15:29 +08:00
|
|
|
match channel {
|
2020-05-17 06:13:52 +08:00
|
|
|
0 => {
|
2020-05-28 08:01:55 +08:00
|
|
|
let sample = self.pins_adc.convert(
|
2020-05-17 08:18:25 +08:00
|
|
|
&self.channel0.dac_feedback_pin,
|
2020-05-17 06:13:52 +08:00
|
|
|
stm32f4xx_hal::adc::config::SampleTime::Cycles_480
|
|
|
|
);
|
2020-05-28 08:01:55 +08:00
|
|
|
let mv = self.pins_adc.sample_to_millivolts(sample);
|
|
|
|
info!("dac0_fb: {}/{:03X}", mv, sample);
|
2020-09-14 04:52:20 +08:00
|
|
|
ElectricPotential::new::<millivolt>(mv as f64)
|
2020-05-17 06:13:52 +08:00
|
|
|
}
|
|
|
|
1 => {
|
2020-05-28 08:01:55 +08:00
|
|
|
let sample = self.pins_adc.convert(
|
2020-05-17 08:18:25 +08:00
|
|
|
&self.channel1.dac_feedback_pin,
|
2020-05-17 06:13:52 +08:00
|
|
|
stm32f4xx_hal::adc::config::SampleTime::Cycles_480
|
|
|
|
);
|
2020-05-28 08:01:55 +08:00
|
|
|
let mv = self.pins_adc.sample_to_millivolts(sample);
|
|
|
|
info!("dac1_fb: {}/{:03X}", mv, sample);
|
2020-09-14 04:52:20 +08:00
|
|
|
ElectricPotential::new::<millivolt>(mv as f64)
|
2020-05-17 06:13:52 +08:00
|
|
|
}
|
2020-05-13 06:15:29 +08:00
|
|
|
_ => unreachable!(),
|
|
|
|
}
|
|
|
|
}
|
2020-05-17 05:59:31 +08:00
|
|
|
|
2020-09-14 04:52:20 +08:00
|
|
|
pub fn read_dac_feedback_until_stable(&mut self, channel: usize, tolerance: ElectricPotential) -> ElectricPotential {
|
2020-05-21 05:07:31 +08:00
|
|
|
let mut prev = self.read_dac_feedback(channel);
|
|
|
|
loop {
|
|
|
|
let current = self.read_dac_feedback(channel);
|
|
|
|
use num_traits::float::Float;
|
2020-09-14 04:52:20 +08:00
|
|
|
if (current - prev).abs() < tolerance {
|
2020-05-21 05:07:31 +08:00
|
|
|
return current;
|
|
|
|
}
|
|
|
|
prev = current;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-14 04:52:20 +08:00
|
|
|
pub fn read_itec(&mut self, channel: usize) -> ElectricPotential {
|
2020-05-17 05:59:31 +08:00
|
|
|
match channel {
|
2020-05-17 06:13:52 +08:00
|
|
|
0 => {
|
2020-05-28 08:01:55 +08:00
|
|
|
let sample = self.pins_adc.convert(
|
2020-05-17 06:13:52 +08:00
|
|
|
&self.channel0.itec_pin,
|
|
|
|
stm32f4xx_hal::adc::config::SampleTime::Cycles_480
|
|
|
|
);
|
2020-05-28 08:01:55 +08:00
|
|
|
let mv = self.pins_adc.sample_to_millivolts(sample);
|
2020-09-14 04:52:20 +08:00
|
|
|
ElectricPotential::new::<millivolt>(mv as f64)
|
2020-05-17 06:13:52 +08:00
|
|
|
}
|
|
|
|
1 => {
|
2020-05-28 08:01:55 +08:00
|
|
|
let sample = self.pins_adc.convert(
|
2020-05-17 06:13:52 +08:00
|
|
|
&self.channel1.itec_pin,
|
|
|
|
stm32f4xx_hal::adc::config::SampleTime::Cycles_480
|
|
|
|
);
|
2020-05-28 08:01:55 +08:00
|
|
|
let mv = self.pins_adc.sample_to_millivolts(sample);
|
2020-09-14 04:52:20 +08:00
|
|
|
ElectricPotential::new::<millivolt>(mv as f64)
|
2020-05-19 04:43:44 +08:00
|
|
|
}
|
|
|
|
_ => unreachable!(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-19 06:15:39 +08:00
|
|
|
/// should be 1.5V
|
2020-09-14 04:52:20 +08:00
|
|
|
pub fn read_vref(&mut self, channel: usize) -> ElectricPotential {
|
2020-05-19 04:43:44 +08:00
|
|
|
match channel {
|
|
|
|
0 => {
|
2020-05-28 08:01:55 +08:00
|
|
|
let sample = self.pins_adc.convert(
|
2020-05-19 04:43:44 +08:00
|
|
|
&self.channel0.vref_pin,
|
|
|
|
stm32f4xx_hal::adc::config::SampleTime::Cycles_480
|
|
|
|
);
|
2020-05-28 08:01:55 +08:00
|
|
|
let mv = self.pins_adc.sample_to_millivolts(sample);
|
2020-09-14 04:52:20 +08:00
|
|
|
ElectricPotential::new::<volt>(mv as f64 / 1000.0)
|
2020-05-19 04:43:44 +08:00
|
|
|
}
|
|
|
|
1 => {
|
2020-05-28 08:01:55 +08:00
|
|
|
let sample = self.pins_adc.convert(
|
2020-05-19 04:43:44 +08:00
|
|
|
&self.channel1.vref_pin,
|
|
|
|
stm32f4xx_hal::adc::config::SampleTime::Cycles_480
|
|
|
|
);
|
2020-05-28 08:01:55 +08:00
|
|
|
let mv = self.pins_adc.sample_to_millivolts(sample);
|
2020-09-14 04:52:20 +08:00
|
|
|
ElectricPotential::new::<volt>(mv as f64 / 1000.0)
|
2020-05-17 06:13:52 +08:00
|
|
|
}
|
2020-05-17 05:59:31 +08:00
|
|
|
_ => unreachable!(),
|
|
|
|
}
|
|
|
|
}
|
2020-05-19 03:38:13 +08:00
|
|
|
|
2020-09-14 04:52:20 +08:00
|
|
|
pub fn read_tec_u_meas(&mut self, channel: usize) -> ElectricPotential {
|
2020-05-19 03:38:13 +08:00
|
|
|
match channel {
|
|
|
|
0 => {
|
2020-05-28 08:06:32 +08:00
|
|
|
let sample = self.pins_adc.convert(
|
2020-05-19 03:38:13 +08:00
|
|
|
&self.channel0.tec_u_meas_pin,
|
|
|
|
stm32f4xx_hal::adc::config::SampleTime::Cycles_480
|
|
|
|
);
|
2020-05-28 08:06:32 +08:00
|
|
|
let mv = self.pins_adc.sample_to_millivolts(sample);
|
2020-09-14 04:52:20 +08:00
|
|
|
ElectricPotential::new::<millivolt>(mv as f64)
|
2020-05-19 03:38:13 +08:00
|
|
|
}
|
|
|
|
1 => {
|
2020-05-28 08:06:32 +08:00
|
|
|
let sample = self.pins_adc.convert(
|
2020-05-19 03:38:13 +08:00
|
|
|
&self.channel1.tec_u_meas_pin,
|
|
|
|
stm32f4xx_hal::adc::config::SampleTime::Cycles_480
|
|
|
|
);
|
2020-05-28 08:06:32 +08:00
|
|
|
let mv = self.pins_adc.sample_to_millivolts(sample);
|
2020-09-14 04:52:20 +08:00
|
|
|
ElectricPotential::new::<millivolt>(mv as f64)
|
2020-05-19 03:38:13 +08:00
|
|
|
}
|
|
|
|
_ => unreachable!(),
|
|
|
|
}
|
|
|
|
}
|
2020-05-19 06:15:39 +08:00
|
|
|
|
2020-09-07 01:08:09 +08:00
|
|
|
/// Calibrate the I_SET DAC using the DAC_FB ADC pin.
|
|
|
|
///
|
|
|
|
/// These loops perform a width-first search for the DAC setting
|
|
|
|
/// that will produce a `target_voltage`.
|
2020-05-19 06:15:39 +08:00
|
|
|
pub fn calibrate_dac_value(&mut self, channel: usize) {
|
2020-09-14 04:52:20 +08:00
|
|
|
let target_voltage = ElectricPotential::new::<volt>(2.5);
|
2020-09-07 01:08:09 +08:00
|
|
|
let mut start_value = 1;
|
2020-09-14 04:52:20 +08:00
|
|
|
let mut best_error = ElectricPotential::new::<volt>(100.0);
|
2020-05-21 05:07:31 +08:00
|
|
|
|
2020-09-07 01:08:09 +08:00
|
|
|
for step in (0..18).rev() {
|
|
|
|
let mut prev_value = start_value;
|
|
|
|
for value in (start_value..=ad5680::MAX_VALUE).step_by(1 << step) {
|
2020-05-21 05:07:31 +08:00
|
|
|
match channel {
|
|
|
|
0 => {
|
|
|
|
self.channel0.dac.set(value).unwrap();
|
|
|
|
}
|
|
|
|
1 => {
|
|
|
|
self.channel1.dac.set(value).unwrap();
|
|
|
|
}
|
|
|
|
_ => unreachable!(),
|
2020-05-19 06:15:39 +08:00
|
|
|
}
|
|
|
|
|
2020-09-14 04:52:20 +08:00
|
|
|
let dac_feedback = self.read_dac_feedback_until_stable(channel, ElectricPotential::new::<volt>(0.001));
|
2020-09-07 01:08:09 +08:00
|
|
|
let error = target_voltage - dac_feedback;
|
2020-09-14 04:52:20 +08:00
|
|
|
if error < ElectricPotential::new::<volt>(0.0) {
|
2020-05-21 05:07:31 +08:00
|
|
|
break;
|
|
|
|
} else if error < best_error {
|
|
|
|
best_error = error;
|
2020-09-07 01:08:09 +08:00
|
|
|
start_value = prev_value;
|
|
|
|
|
2020-09-14 04:52:20 +08:00
|
|
|
let dac_factor = value as f64 / dac_feedback.get::<volt>();
|
2020-09-07 01:08:09 +08:00
|
|
|
match channel {
|
|
|
|
0 => self.channel0.dac_factor = dac_factor,
|
|
|
|
1 => self.channel1.dac_factor = dac_factor,
|
|
|
|
_ => unreachable!(),
|
|
|
|
}
|
2020-05-21 05:07:31 +08:00
|
|
|
}
|
2020-09-07 01:08:09 +08:00
|
|
|
|
|
|
|
prev_value = value;
|
2020-05-19 06:15:39 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-07 01:08:09 +08:00
|
|
|
// Reset
|
2020-09-14 04:52:20 +08:00
|
|
|
self.set_dac(channel, ElectricPotential::new::<volt>(0.0));
|
2020-05-19 06:15:39 +08:00
|
|
|
}
|
2020-05-13 05:16:57 +08:00
|
|
|
}
|