Compare commits
4 Commits
b1b6d1ea94
...
fc0ca8b581
Author | SHA1 | Date |
---|---|---|
Astro | fc0ca8b581 | |
Astro | 8c80062da8 | |
Astro | 7d45d5ad32 | |
Astro | dda1f2f0b4 |
|
@ -52,10 +52,10 @@ The scope of this setting is per TCP session.
|
||||||
| `report mode` | Show current report mode |
|
| `report mode` | Show current report mode |
|
||||||
| `report mode <off/on>` | Set report mode |
|
| `report mode <off/on>` | Set report mode |
|
||||||
| `pwm` | Show current PWM settings |
|
| `pwm` | Show current PWM settings |
|
||||||
| `pwm <0/1> max_i_pos <ratio>` | Set PWM duty cycle for **max_i_pos** to *ratio* |
|
| `pwm <0/1> max_i_pos <ratio>` | Set PWM duty cycle for **max_i_pos** to *ampere* |
|
||||||
| `pwm <0/1> max_i_neg <ratio>` | Set PWM duty cycle for **max_i_neg** to *ratio* |
|
| `pwm <0/1> max_i_neg <ratio>` | Set PWM duty cycle for **max_i_neg** to *ampere* |
|
||||||
| `pwm <0/1> max_v <ratio>` | Set PWM duty cycle for **max_v** to *ratio* |
|
| `pwm <0/1> max_v <ratio>` | Set PWM duty cycle for **max_v** to *volt* |
|
||||||
| `pwm <0/1> <volts>` | Disengage PID, set **i_set** DAC to *volts* |
|
| `pwm <0/1> <volts>` | Disengage PID, set **i_set** DAC to *ampere* |
|
||||||
| `pwm <0/1> pid` | Set PWM to be controlled by PID |
|
| `pwm <0/1> pid` | Set PWM to be controlled by PID |
|
||||||
| `pid` | Show PID configuration |
|
| `pid` | Show PID configuration |
|
||||||
| `pid <0/1> target <value>` | Set the PID controller target |
|
| `pid <0/1> target <value>` | Set the PID controller target |
|
||||||
|
|
|
@ -45,13 +45,14 @@ impl<SPI: Transfer<u8>, S: OutputPin> Dac<SPI, S> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set(&mut self, value: u32) -> Result<(), SPI::Error> {
|
pub fn set(&mut self, value: u32) -> Result<u32, SPI::Error> {
|
||||||
let value = value.min(MAX_VALUE);
|
let value = value.min(MAX_VALUE);
|
||||||
let mut buf = [
|
let mut buf = [
|
||||||
(value >> 14) as u8,
|
(value >> 14) as u8,
|
||||||
(value >> 6) as u8,
|
(value >> 6) as u8,
|
||||||
(value << 2) as u8,
|
(value << 2) as u8,
|
||||||
];
|
];
|
||||||
self.write(&mut buf)
|
self.write(&mut buf)?;
|
||||||
|
Ok(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,10 @@ use stm32f4xx_hal::hal::{
|
||||||
blocking::spi::Transfer,
|
blocking::spi::Transfer,
|
||||||
digital::v2::OutputPin,
|
digital::v2::OutputPin,
|
||||||
};
|
};
|
||||||
|
use uom::si::{
|
||||||
|
f64::ElectricPotential,
|
||||||
|
electric_potential::volt,
|
||||||
|
};
|
||||||
use super::{
|
use super::{
|
||||||
regs::{self, Register, RegisterData},
|
regs::{self, Register, RegisterData},
|
||||||
checksum::{ChecksumMode, Checksum},
|
checksum::{ChecksumMode, Checksum},
|
||||||
|
@ -272,7 +276,7 @@ pub struct ChannelCalibration {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ChannelCalibration {
|
impl ChannelCalibration {
|
||||||
pub fn convert_data(&self, data: u32) -> f64 {
|
pub fn convert_data(&self, data: u32) -> ElectricPotential {
|
||||||
let data = if self.bipolar {
|
let data = if self.bipolar {
|
||||||
(data as i32 - 0x80_0000) as f64
|
(data as i32 - 0x80_0000) as f64
|
||||||
} else {
|
} else {
|
||||||
|
@ -282,7 +286,7 @@ impl ChannelCalibration {
|
||||||
let data = data + (self.offset as i32 - 0x80_0000) as f64;
|
let data = data + (self.offset as i32 - 0x80_0000) as f64;
|
||||||
let data = data / (2 << 23) as f64;
|
let data = data / (2 << 23) as f64;
|
||||||
|
|
||||||
const V_REF: f64 = 3.0;
|
const V_REF: f64 = 3.3;
|
||||||
data * V_REF / 0.75
|
ElectricPotential::new::<volt>(data * V_REF / 0.75)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,13 @@
|
||||||
use smoltcp::time::Instant;
|
use smoltcp::time::Instant;
|
||||||
use uom::si::{
|
use uom::si::{
|
||||||
f64::ElectricPotential,
|
f64::{
|
||||||
|
ElectricPotential,
|
||||||
|
ElectricalResistance,
|
||||||
|
ThermodynamicTemperature,
|
||||||
|
},
|
||||||
electric_potential::volt,
|
electric_potential::volt,
|
||||||
|
electrical_resistance::ohm,
|
||||||
|
temperature_interval::kelvin,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
ad7172,
|
ad7172,
|
||||||
|
@ -9,11 +15,13 @@ use crate::{
|
||||||
steinhart_hart as sh,
|
steinhart_hart as sh,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const R_INNER: f64 = 2.0 * 5100.0;
|
||||||
|
|
||||||
pub struct ChannelState {
|
pub struct ChannelState {
|
||||||
pub adc_data: Option<u32>,
|
pub adc_data: Option<u32>,
|
||||||
pub adc_calibration: ad7172::ChannelCalibration,
|
pub adc_calibration: ad7172::ChannelCalibration,
|
||||||
pub adc_time: Instant,
|
pub adc_time: Instant,
|
||||||
|
pub vref: ElectricPotential,
|
||||||
pub dac_value: ElectricPotential,
|
pub dac_value: ElectricPotential,
|
||||||
pub pid_engaged: bool,
|
pub pid_engaged: bool,
|
||||||
pub pid: pid::Controller,
|
pub pid: pid::Controller,
|
||||||
|
@ -26,6 +34,8 @@ impl ChannelState {
|
||||||
adc_data: None,
|
adc_data: None,
|
||||||
adc_calibration,
|
adc_calibration,
|
||||||
adc_time: Instant::from_secs(0),
|
adc_time: Instant::from_secs(0),
|
||||||
|
// can be initialized later with Channels.read_vref()
|
||||||
|
vref: ElectricPotential::new::<volt>(3.3 / 2.0),
|
||||||
dac_value: ElectricPotential::new::<volt>(0.0),
|
dac_value: ElectricPotential::new::<volt>(0.0),
|
||||||
pid_engaged: false,
|
pid_engaged: false,
|
||||||
pid: pid::Controller::new(pid::Parameters::default()),
|
pid: pid::Controller::new(pid::Parameters::default()),
|
||||||
|
@ -39,18 +49,26 @@ impl ChannelState {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update PID state on ADC input, calculate new DAC output
|
/// Update PID state on ADC input, calculate new DAC output
|
||||||
pub fn update_pid(&mut self) -> f64 {
|
pub fn update_pid(&mut self) {
|
||||||
// Update PID controller
|
// Update PID controller
|
||||||
self.pid.update(self.get_temperature().unwrap())
|
// self.pid.update(self.get_temperature().unwrap().get::<kelvin>())
|
||||||
|
// TODO: add output field
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_adc(&self) -> Option<ElectricPotential> {
|
pub fn get_adc(&self) -> Option<ElectricPotential> {
|
||||||
let volts = self.adc_calibration.convert_data(self.adc_data?);
|
Some(self.adc_calibration.convert_data(self.adc_data?))
|
||||||
Some(ElectricPotential::new::<volt>(volts))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_temperature(&self) -> Option<f64> {
|
/// Get `SENS[01]` input resistance
|
||||||
let r = self.get_adc()?.get::<volt>();
|
pub fn get_sens(&self) -> Option<ElectricalResistance> {
|
||||||
|
let r_inner = ElectricalResistance::new::<ohm>(R_INNER);
|
||||||
|
let adc_input = self.get_adc()?;
|
||||||
|
let r = r_inner * adc_input / (self.vref - adc_input);
|
||||||
|
Some(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_temperature(&self) -> Option<ThermodynamicTemperature> {
|
||||||
|
let r = self.get_sens()?;
|
||||||
let temperature = self.sh.get_temperature(r);
|
let temperature = self.sh.get_temperature(r);
|
||||||
Some(temperature)
|
Some(temperature)
|
||||||
}
|
}
|
||||||
|
|
162
src/channels.rs
162
src/channels.rs
|
@ -1,7 +1,11 @@
|
||||||
|
use stm32f4xx_hal::hal;
|
||||||
use smoltcp::time::Instant;
|
use smoltcp::time::Instant;
|
||||||
use uom::si::{
|
use uom::si::{
|
||||||
f64::ElectricPotential,
|
f64::{ElectricCurrent, ElectricPotential, ElectricalResistance},
|
||||||
electric_potential::{millivolt, volt},
|
electric_potential::{millivolt, volt},
|
||||||
|
electric_current::ampere,
|
||||||
|
electrical_resistance::ohm,
|
||||||
|
ratio::ratio,
|
||||||
};
|
};
|
||||||
use log::info;
|
use log::info;
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -9,10 +13,12 @@ use crate::{
|
||||||
ad7172,
|
ad7172,
|
||||||
channel::{Channel, Channel0, Channel1},
|
channel::{Channel, Channel0, Channel1},
|
||||||
channel_state::ChannelState,
|
channel_state::ChannelState,
|
||||||
|
command_parser::PwmPin,
|
||||||
pins,
|
pins,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const CHANNELS: usize = 2;
|
pub const CHANNELS: usize = 2;
|
||||||
|
pub const R_SENSE: f64 = 0.05;
|
||||||
|
|
||||||
// TODO: -pub
|
// TODO: -pub
|
||||||
pub struct Channels {
|
pub struct Channels {
|
||||||
|
@ -45,6 +51,8 @@ impl Channels {
|
||||||
let pwm = pins.pwm;
|
let pwm = pins.pwm;
|
||||||
let mut channels = Channels { channel0, channel1, adc, pins_adc, pwm };
|
let mut channels = Channels { channel0, channel1, adc, pins_adc, pwm };
|
||||||
for channel in 0..CHANNELS {
|
for channel in 0..CHANNELS {
|
||||||
|
// FIXME: this reads 1.5 V instead of the expected 1.65 V.
|
||||||
|
// channels.channel_state(channel).vref = channels.read_vref(channel);
|
||||||
channels.calibrate_dac_value(channel);
|
channels.calibrate_dac_value(channel);
|
||||||
}
|
}
|
||||||
channels
|
channels
|
||||||
|
@ -76,7 +84,8 @@ impl Channels {
|
||||||
};
|
};
|
||||||
if let Some(dac_value) = dac_value {
|
if let Some(dac_value) = dac_value {
|
||||||
// Forward PID output to i_set DAC
|
// Forward PID output to i_set DAC
|
||||||
self.set_dac(channel.into(), ElectricPotential::new::<volt>(dac_value));
|
// TODO:
|
||||||
|
// self.set_dac(channel.into(), ElectricPotential::new::<volt>(dac_value));
|
||||||
}
|
}
|
||||||
|
|
||||||
channel
|
channel
|
||||||
|
@ -84,24 +93,53 @@ impl Channels {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// i_set DAC
|
/// i_set DAC
|
||||||
pub fn set_dac(&mut self, channel: usize, voltage: ElectricPotential) {
|
fn get_dac(&mut self, channel: usize) -> (ElectricPotential, ElectricPotential) {
|
||||||
|
let dac_factor = match channel.into() {
|
||||||
|
0 => self.channel0.dac_factor,
|
||||||
|
1 => self.channel1.dac_factor,
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
let voltage = self.channel_state(channel).dac_value;
|
||||||
|
let max = ElectricPotential::new::<volt>(ad5680::MAX_VALUE as f64 / dac_factor);
|
||||||
|
(voltage, max)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_i(&mut self, channel: usize) -> (ElectricCurrent, ElectricCurrent) {
|
||||||
|
let vref = self.channel_state(channel).vref;
|
||||||
|
let r_sense = ElectricalResistance::new::<ohm>(R_SENSE);
|
||||||
|
let (voltage, max) = self.get_dac(channel);
|
||||||
|
let i_tec = (voltage - vref) / (10.0 * r_sense);
|
||||||
|
let max = (max - vref) / (10.0 * r_sense);
|
||||||
|
(i_tec, max)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// i_set DAC
|
||||||
|
fn set_dac(&mut self, channel: usize, voltage: ElectricPotential) -> (ElectricPotential, ElectricPotential) {
|
||||||
let dac_factor = match channel.into() {
|
let dac_factor = match channel.into() {
|
||||||
0 => self.channel0.dac_factor,
|
0 => self.channel0.dac_factor,
|
||||||
1 => self.channel1.dac_factor,
|
1 => self.channel1.dac_factor,
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
let value = (voltage.get::<volt>() * dac_factor) as u32;
|
let value = (voltage.get::<volt>() * dac_factor) as u32;
|
||||||
match channel {
|
let value = match channel {
|
||||||
0 => {
|
0 => self.channel0.dac.set(value).unwrap(),
|
||||||
self.channel0.dac.set(value).unwrap();
|
1 => self.channel1.dac.set(value).unwrap(),
|
||||||
self.channel0.state.dac_value = voltage;
|
|
||||||
}
|
|
||||||
1 => {
|
|
||||||
self.channel1.dac.set(value).unwrap();
|
|
||||||
self.channel1.state.dac_value = voltage;
|
|
||||||
}
|
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
let voltage = ElectricPotential::new::<volt>(value as f64 / dac_factor);
|
||||||
|
self.channel_state(channel).dac_value = voltage;
|
||||||
|
let max = ElectricPotential::new::<volt>(ad5680::MAX_VALUE as f64 / dac_factor);
|
||||||
|
(voltage, max)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_i(&mut self, channel: usize, i_tec: ElectricCurrent) -> (ElectricCurrent, ElectricCurrent) {
|
||||||
|
let vref = self.channel_state(channel).vref;
|
||||||
|
let r_sense = ElectricalResistance::new::<ohm>(R_SENSE);
|
||||||
|
let voltage = i_tec * 10.0 * r_sense + vref;
|
||||||
|
let (voltage, max) = self.set_dac(channel, voltage);
|
||||||
|
let i_tec = (voltage - vref) / (10.0 * r_sense);
|
||||||
|
let max = (max - vref) / (10.0 * r_sense);
|
||||||
|
(i_tec, max)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_dac_feedback(&mut self, channel: usize) -> ElectricPotential {
|
pub fn read_dac_feedback(&mut self, channel: usize) -> ElectricPotential {
|
||||||
|
@ -171,7 +209,7 @@ impl Channels {
|
||||||
stm32f4xx_hal::adc::config::SampleTime::Cycles_480
|
stm32f4xx_hal::adc::config::SampleTime::Cycles_480
|
||||||
);
|
);
|
||||||
let mv = self.pins_adc.sample_to_millivolts(sample);
|
let mv = self.pins_adc.sample_to_millivolts(sample);
|
||||||
ElectricPotential::new::<volt>(mv as f64 / 1000.0)
|
ElectricPotential::new::<millivolt>(mv as f64)
|
||||||
}
|
}
|
||||||
1 => {
|
1 => {
|
||||||
let sample = self.pins_adc.convert(
|
let sample = self.pins_adc.convert(
|
||||||
|
@ -179,7 +217,7 @@ impl Channels {
|
||||||
stm32f4xx_hal::adc::config::SampleTime::Cycles_480
|
stm32f4xx_hal::adc::config::SampleTime::Cycles_480
|
||||||
);
|
);
|
||||||
let mv = self.pins_adc.sample_to_millivolts(sample);
|
let mv = self.pins_adc.sample_to_millivolts(sample);
|
||||||
ElectricPotential::new::<volt>(mv as f64 / 1000.0)
|
ElectricPotential::new::<millivolt>(mv as f64)
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
|
@ -252,4 +290,100 @@ impl Channels {
|
||||||
// Reset
|
// Reset
|
||||||
self.set_dac(channel, ElectricPotential::new::<volt>(0.0));
|
self.set_dac(channel, ElectricPotential::new::<volt>(0.0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_pwm(&self, channel: usize, pin: PwmPin) -> f64 {
|
||||||
|
fn get<P: hal::PwmPin<Duty=u16>>(pin: &P) -> f64 {
|
||||||
|
let duty = pin.get_duty();
|
||||||
|
let max = pin.get_max_duty();
|
||||||
|
duty as f64 / (max as f64)
|
||||||
|
}
|
||||||
|
match (channel, pin) {
|
||||||
|
(_, PwmPin::ISet) =>
|
||||||
|
panic!("i_set is no pwm pin"),
|
||||||
|
(0, PwmPin::MaxIPos) =>
|
||||||
|
get(&self.pwm.max_i_pos0),
|
||||||
|
(0, PwmPin::MaxINeg) =>
|
||||||
|
get(&self.pwm.max_i_neg0),
|
||||||
|
(0, PwmPin::MaxV) =>
|
||||||
|
get(&self.pwm.max_v0),
|
||||||
|
(1, PwmPin::MaxIPos) =>
|
||||||
|
get(&self.pwm.max_i_pos1),
|
||||||
|
(1, PwmPin::MaxINeg) =>
|
||||||
|
get(&self.pwm.max_i_neg1),
|
||||||
|
(1, PwmPin::MaxV) =>
|
||||||
|
get(&self.pwm.max_v1),
|
||||||
|
_ =>
|
||||||
|
unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_max_v(&mut self, channel: usize) -> (ElectricPotential, ElectricPotential) {
|
||||||
|
let vref = self.channel_state(channel).vref;
|
||||||
|
let duty = self.get_pwm(channel, PwmPin::MaxV);
|
||||||
|
(duty * 4.0 * vref, 4.0 * vref)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_max_i_pos(&mut self, channel: usize) -> (ElectricCurrent, ElectricCurrent) {
|
||||||
|
let vref = self.channel_state(channel).vref;
|
||||||
|
let scale = vref / ElectricPotential::new::<volt>(3.0) / ElectricCurrent::new::<ampere>(1.0);
|
||||||
|
let duty = self.get_pwm(channel, PwmPin::MaxIPos);
|
||||||
|
(duty / scale, 1.0 / scale)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_max_i_neg(&mut self, channel: usize) -> (ElectricCurrent, ElectricCurrent) {
|
||||||
|
let vref = self.channel_state(channel).vref;
|
||||||
|
let scale = vref / ElectricPotential::new::<volt>(3.0) / ElectricCurrent::new::<ampere>(1.0);
|
||||||
|
let duty = self.get_pwm(channel, PwmPin::MaxINeg);
|
||||||
|
(duty / scale, 1.0 / scale)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_pwm(&mut self, channel: usize, pin: PwmPin, duty: f64) -> f64 {
|
||||||
|
fn set<P: hal::PwmPin<Duty=u16>>(pin: &mut P, duty: f64) -> f64 {
|
||||||
|
let max = pin.get_max_duty();
|
||||||
|
let value = ((duty * (max as f64)) as u16).min(max);
|
||||||
|
pin.set_duty(value);
|
||||||
|
value as f64 / (max as f64)
|
||||||
|
}
|
||||||
|
match (channel, pin) {
|
||||||
|
(_, PwmPin::ISet) =>
|
||||||
|
panic!("i_set is no pwm pin"),
|
||||||
|
(0, PwmPin::MaxIPos) =>
|
||||||
|
set(&mut self.pwm.max_i_pos0, duty),
|
||||||
|
(0, PwmPin::MaxINeg) =>
|
||||||
|
set(&mut self.pwm.max_i_neg0, duty),
|
||||||
|
(0, PwmPin::MaxV) =>
|
||||||
|
set(&mut self.pwm.max_v0, duty),
|
||||||
|
(1, PwmPin::MaxIPos) =>
|
||||||
|
set(&mut self.pwm.max_i_pos1, duty),
|
||||||
|
(1, PwmPin::MaxINeg) =>
|
||||||
|
set(&mut self.pwm.max_i_neg1, duty),
|
||||||
|
(1, PwmPin::MaxV) =>
|
||||||
|
set(&mut self.pwm.max_v1, duty),
|
||||||
|
_ =>
|
||||||
|
unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_max_v(&mut self, channel: usize, max_v: ElectricPotential) -> (ElectricPotential, ElectricPotential) {
|
||||||
|
let vref = self.channel_state(channel).vref;
|
||||||
|
let duty = (max_v / 4.0 / vref).get::<ratio>();
|
||||||
|
let duty = self.set_pwm(channel, PwmPin::MaxV, duty);
|
||||||
|
(duty * 4.0 * vref, 4.0 * vref)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_max_i_pos(&mut self, channel: usize, max_i_pos: ElectricCurrent) -> (ElectricCurrent, ElectricCurrent) {
|
||||||
|
let vref = self.channel_state(channel).vref;
|
||||||
|
let scale = vref / ElectricPotential::new::<volt>(3.0) / ElectricCurrent::new::<ampere>(1.0);
|
||||||
|
let duty = (max_i_pos * scale).get::<ratio>();
|
||||||
|
let duty = self.set_pwm(channel, PwmPin::MaxIPos, duty);
|
||||||
|
(duty / scale, 1.0 / scale)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_max_i_neg(&mut self, channel: usize, max_i_neg: ElectricCurrent) -> (ElectricCurrent, ElectricCurrent) {
|
||||||
|
let vref = self.channel_state(channel).vref;
|
||||||
|
let scale = vref / ElectricPotential::new::<volt>(3.0) / ElectricCurrent::new::<ampere>(1.0);
|
||||||
|
let duty = (max_i_neg * scale).get::<ratio>();
|
||||||
|
let duty = self.set_pwm(channel, PwmPin::MaxINeg, duty);
|
||||||
|
(duty / scale, 1.0 / scale)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -142,7 +142,7 @@ pub enum Command {
|
||||||
Pwm {
|
Pwm {
|
||||||
channel: usize,
|
channel: usize,
|
||||||
pin: PwmPin,
|
pin: PwmPin,
|
||||||
duty: f64,
|
value: f64,
|
||||||
},
|
},
|
||||||
/// Enable PID control for `i_set`
|
/// Enable PID control for `i_set`
|
||||||
PwmPid {
|
PwmPid {
|
||||||
|
@ -242,7 +242,7 @@ fn report(input: &[u8]) -> IResult<&[u8], Command> {
|
||||||
fn pwm_setup(input: &[u8]) -> IResult<&[u8], Result<(PwmPin, f64), Error>> {
|
fn pwm_setup(input: &[u8]) -> IResult<&[u8], Result<(PwmPin, f64), Error>> {
|
||||||
let result_with_pin = |pin: PwmPin|
|
let result_with_pin = |pin: PwmPin|
|
||||||
move |result: Result<f64, Error>|
|
move |result: Result<f64, Error>|
|
||||||
result.map(|duty| (pin, duty));
|
result.map(|value| (pin, value));
|
||||||
|
|
||||||
alt((
|
alt((
|
||||||
map(
|
map(
|
||||||
|
@ -300,8 +300,8 @@ fn pwm(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
|
||||||
|input| {
|
|input| {
|
||||||
let (input, config) = pwm_setup(input)?;
|
let (input, config) = pwm_setup(input)?;
|
||||||
match config {
|
match config {
|
||||||
Ok((pin, duty)) =>
|
Ok((pin, value)) =>
|
||||||
Ok((input, Ok(Command::Pwm { channel, pin, duty }))),
|
Ok((input, Ok(Command::Pwm { channel, pin, value }))),
|
||||||
Err(e) =>
|
Err(e) =>
|
||||||
Ok((input, Err(e))),
|
Ok((input, Err(e))),
|
||||||
}
|
}
|
||||||
|
@ -461,7 +461,7 @@ mod test {
|
||||||
assert_eq!(command, Ok(Command::Pwm {
|
assert_eq!(command, Ok(Command::Pwm {
|
||||||
channel: 1,
|
channel: 1,
|
||||||
pin: PwmPin::ISet,
|
pin: PwmPin::ISet,
|
||||||
duty: 16383,
|
value: 16383,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -480,7 +480,7 @@ mod test {
|
||||||
assert_eq!(command, Ok(Command::Pwm {
|
assert_eq!(command, Ok(Command::Pwm {
|
||||||
channel: 0,
|
channel: 0,
|
||||||
pin: PwmPin::MaxIPos,
|
pin: PwmPin::MaxIPos,
|
||||||
duty: 7,
|
value: 7,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -490,7 +490,7 @@ mod test {
|
||||||
assert_eq!(command, Ok(Command::Pwm {
|
assert_eq!(command, Ok(Command::Pwm {
|
||||||
channel: 0,
|
channel: 0,
|
||||||
pin: PwmPin::MaxINeg,
|
pin: PwmPin::MaxINeg,
|
||||||
duty: 128,
|
value: 128,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -500,7 +500,7 @@ mod test {
|
||||||
assert_eq!(command, Ok(Command::Pwm {
|
assert_eq!(command, Ok(Command::Pwm {
|
||||||
channel: 0,
|
channel: 0,
|
||||||
pin: PwmPin::MaxV,
|
pin: PwmPin::MaxV,
|
||||||
duty: 32768,
|
value: 32768,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
148
src/main.rs
148
src/main.rs
|
@ -32,12 +32,15 @@ use uom::{
|
||||||
fmt::DisplayStyle::Abbreviation,
|
fmt::DisplayStyle::Abbreviation,
|
||||||
si::{
|
si::{
|
||||||
f64::{
|
f64::{
|
||||||
|
ElectricCurrent,
|
||||||
ElectricPotential,
|
ElectricPotential,
|
||||||
ElectricalResistance,
|
ElectricalResistance,
|
||||||
|
ThermodynamicTemperature,
|
||||||
},
|
},
|
||||||
electric_current::ampere,
|
electric_current::ampere,
|
||||||
electric_potential::volt,
|
electric_potential::volt,
|
||||||
electrical_resistance::ohm,
|
electrical_resistance::ohm,
|
||||||
|
thermodynamic_temperature::degree_celsius,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -166,7 +169,7 @@ fn main() -> ! {
|
||||||
}
|
}
|
||||||
Command::Show(ShowCommand::Input) => {
|
Command::Show(ShowCommand::Input) => {
|
||||||
for channel in 0..CHANNELS {
|
for channel in 0..CHANNELS {
|
||||||
if let Some(adc_data) = channels.channel_state(channel).adc_data {
|
if let Some(adc_input) = channels.channel_state(channel).get_adc() {
|
||||||
let vref = channels.read_vref(channel);
|
let vref = channels.read_vref(channel);
|
||||||
let dac_feedback = channels.read_dac_feedback(channel);
|
let dac_feedback = channels.read_dac_feedback(channel);
|
||||||
|
|
||||||
|
@ -177,9 +180,10 @@ fn main() -> ! {
|
||||||
|
|
||||||
let state = channels.channel_state(channel);
|
let state = channels.channel_state(channel);
|
||||||
let _ = writeln!(
|
let _ = writeln!(
|
||||||
socket, "channel {}: t={} adc{}={} vref={} dac_feedback={} itec={} tec={} tec_u_meas={} r={:03}",
|
socket, "channel {}: t={} adc{}={} adc_r={} vref={} dac_feedback={} itec={} tec={} tec_u_meas={} r={:03}",
|
||||||
channel,
|
channel, state.adc_time,
|
||||||
state.adc_time, channel, adc_data,
|
channel, adc_input.into_format_args(volt, Abbreviation),
|
||||||
|
state.get_sens().unwrap().into_format_args(ohm, Abbreviation),
|
||||||
vref.into_format_args(volt, Abbreviation), dac_feedback.into_format_args(volt, Abbreviation),
|
vref.into_format_args(volt, Abbreviation), dac_feedback.into_format_args(volt, Abbreviation),
|
||||||
itec.into_format_args(volt, Abbreviation), tec_i.into_format_args(ampere, Abbreviation),
|
itec.into_format_args(volt, Abbreviation), tec_i.into_format_args(ampere, Abbreviation),
|
||||||
tec_u_meas.into_format_args(volt, Abbreviation),
|
tec_u_meas.into_format_args(volt, Abbreviation),
|
||||||
|
@ -225,34 +229,33 @@ fn main() -> ! {
|
||||||
channel,
|
channel,
|
||||||
if state.pid_engaged { "engaged" } else { "disengaged" }
|
if state.pid_engaged { "engaged" } else { "disengaged" }
|
||||||
);
|
);
|
||||||
let _ = writeln!(socket, "- i_set={}", state.dac_value.into_format_args(volt, Abbreviation));
|
let i_set = channels.get_i(channel);
|
||||||
fn show_pwm_channel<S, P>(mut socket: S, name: &str, pin: &P)
|
|
||||||
where
|
|
||||||
S: core::fmt::Write,
|
|
||||||
P: hal::PwmPin<Duty=u16>,
|
|
||||||
{
|
|
||||||
let _ = writeln!(
|
let _ = writeln!(
|
||||||
socket,
|
socket, "- i_set={:.3} / {:.3}",
|
||||||
"- {}={}/{}",
|
i_set.0.into_format_args(ampere, Abbreviation),
|
||||||
name, pin.get_duty(), pin.get_max_duty()
|
i_set.1.into_format_args(ampere, Abbreviation),
|
||||||
|
);
|
||||||
|
let max_v = channels.get_max_v(channel);
|
||||||
|
let _ = writeln!(
|
||||||
|
socket, "- max_v={:.3} / {:.3}",
|
||||||
|
max_v.0.into_format_args(volt, Abbreviation),
|
||||||
|
max_v.1.into_format_args(volt, Abbreviation),
|
||||||
|
);
|
||||||
|
let max_i_pos = channels.get_max_i_pos(channel);
|
||||||
|
let _ = writeln!(
|
||||||
|
socket, "- max_i_pos={:.3} / {:.3}",
|
||||||
|
max_i_pos.0.into_format_args(ampere, Abbreviation),
|
||||||
|
max_i_pos.1.into_format_args(ampere, Abbreviation),
|
||||||
|
);
|
||||||
|
let max_i_neg = channels.get_max_i_neg(channel);
|
||||||
|
let _ = writeln!(
|
||||||
|
socket, "- max_i_neg={:.3} / {:.3}",
|
||||||
|
max_i_neg.0.into_format_args(ampere, Abbreviation),
|
||||||
|
max_i_neg.1.into_format_args(ampere, Abbreviation),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
match channel {
|
|
||||||
0 => {
|
|
||||||
show_pwm_channel(socket.deref_mut(), "max_v", &channels.pwm.max_v0);
|
|
||||||
show_pwm_channel(socket.deref_mut(), "max_i_pos", &channels.pwm.max_i_pos0);
|
|
||||||
show_pwm_channel(socket.deref_mut(), "max_i_neg", &channels.pwm.max_i_neg0);
|
|
||||||
}
|
|
||||||
1 => {
|
|
||||||
show_pwm_channel(socket.deref_mut(), "max_v", &channels.pwm.max_v1);
|
|
||||||
show_pwm_channel(socket.deref_mut(), "max_i_pos", &channels.pwm.max_i_pos1);
|
|
||||||
show_pwm_channel(socket.deref_mut(), "max_i_neg", &channels.pwm.max_i_neg1);
|
|
||||||
}
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
let _ = writeln!(socket, "");
|
let _ = writeln!(socket, "");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
Command::Show(ShowCommand::SteinhartHart) => {
|
Command::Show(ShowCommand::SteinhartHart) => {
|
||||||
for channel in 0..CHANNELS {
|
for channel in 0..CHANNELS {
|
||||||
let state = channels.channel_state(channel);
|
let state = channels.channel_state(channel);
|
||||||
|
@ -260,9 +263,20 @@ fn main() -> ! {
|
||||||
socket, "channel {}: Steinhart-Hart equation parameters",
|
socket, "channel {}: Steinhart-Hart equation parameters",
|
||||||
channel,
|
channel,
|
||||||
);
|
);
|
||||||
let _ = writeln!(socket, "- t0={}", state.sh.t0);
|
let _ = writeln!(socket, "- t0={}", state.sh.t0.into_format_args(degree_celsius, Abbreviation));
|
||||||
let _ = writeln!(socket, "- b={}", state.sh.b);
|
let _ = writeln!(socket, "- b={}", state.sh.b);
|
||||||
let _ = writeln!(socket, "- r0={}", state.sh.r0);
|
let _ = writeln!(socket, "- r0={}", state.sh.r0.into_format_args(ohm, Abbreviation));
|
||||||
|
match (state.get_adc(), state.get_sens(), state.get_temperature()) {
|
||||||
|
(Some(adc), Some(sens), Some(temp)) => {
|
||||||
|
let _ = writeln!(
|
||||||
|
socket, "- adc={} r={} temp={:.3}K",
|
||||||
|
adc.into_format_args(volt, Abbreviation),
|
||||||
|
sens.into_format_args(ohm, Abbreviation),
|
||||||
|
temp.into_format_args(degree_celsius, Abbreviation),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
let _ = writeln!(socket, "");
|
let _ = writeln!(socket, "");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -290,46 +304,56 @@ fn main() -> ! {
|
||||||
let _ = writeln!(socket, "channel {}: PID enabled to control PWM", channel
|
let _ = writeln!(socket, "channel {}: PID enabled to control PWM", channel
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Command::Pwm { channel, pin: PwmPin::ISet, duty } => {
|
Command::Pwm { channel, pin: PwmPin::ISet, value } => {
|
||||||
channels.channel_state(channel).pid_engaged = false;
|
channels.channel_state(channel).pid_engaged = false;
|
||||||
leds.g3.off();
|
leds.g3.off();
|
||||||
let voltage = ElectricPotential::new::<volt>(duty);
|
let current = ElectricCurrent::new::<ampere>(value);
|
||||||
channels.set_dac(channel, voltage);
|
let (current, max) = channels.set_i(channel, current);
|
||||||
let _ = writeln!(
|
let _ = writeln!(
|
||||||
socket, "channel {}: PWM duty cycle manually set to {}",
|
socket, "channel {}: i_set DAC output set to {:.3} / {:.3}",
|
||||||
channel, voltage.into_format_args(volt, Abbreviation),
|
channel,
|
||||||
|
current.into_format_args(ampere, Abbreviation),
|
||||||
|
max.into_format_args(ampere, Abbreviation),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Command::Pwm { channel, pin, duty } => {
|
Command::Pwm { channel, pin, value } => {
|
||||||
fn set_pwm_channel<P: hal::PwmPin<Duty=u16>>(pin: &mut P, duty: f64) -> (u16, u16) {
|
match pin {
|
||||||
let max = pin.get_max_duty();
|
PwmPin::ISet =>
|
||||||
let value = (duty * (max as f64)) as u16;
|
|
||||||
pin.set_duty(value);
|
|
||||||
(value, max)
|
|
||||||
}
|
|
||||||
let (value, max) = match (channel, pin) {
|
|
||||||
(_, PwmPin::ISet) =>
|
|
||||||
// Handled above
|
// Handled above
|
||||||
unreachable!(),
|
unreachable!(),
|
||||||
(0, PwmPin::MaxIPos) =>
|
PwmPin::MaxV => {
|
||||||
set_pwm_channel(&mut channels.pwm.max_i_pos0, duty),
|
let voltage = ElectricPotential::new::<volt>(value);
|
||||||
(0, PwmPin::MaxINeg) =>
|
let (voltage, max) = channels.set_max_v(channel, voltage);
|
||||||
set_pwm_channel(&mut channels.pwm.max_i_neg0, duty),
|
let _ = writeln!(
|
||||||
(0, PwmPin::MaxV) =>
|
socket, "channel {:.3}: max_v set to {:.3} / {:.3}",
|
||||||
set_pwm_channel(&mut channels.pwm.max_v0, duty),
|
channel,
|
||||||
(1, PwmPin::MaxIPos) =>
|
voltage.into_format_args(volt, Abbreviation),
|
||||||
set_pwm_channel(&mut channels.pwm.max_i_pos1, duty),
|
max.into_format_args(volt, Abbreviation),
|
||||||
(1, PwmPin::MaxINeg) =>
|
);
|
||||||
set_pwm_channel(&mut channels.pwm.max_i_neg1, duty),
|
}
|
||||||
(1, PwmPin::MaxV) =>
|
PwmPin::MaxIPos => {
|
||||||
set_pwm_channel(&mut channels.pwm.max_v1, duty),
|
let current = ElectricCurrent::new::<ampere>(value);
|
||||||
|
let (current, max) = channels.set_max_i_pos(channel, current);
|
||||||
|
let _ = writeln!(
|
||||||
|
socket, "channel {:.3}: max_i_pos set to {:.3} / {:.3}",
|
||||||
|
channel,
|
||||||
|
current.into_format_args(ampere, Abbreviation),
|
||||||
|
max.into_format_args(ampere, Abbreviation),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
PwmPin::MaxINeg => {
|
||||||
|
let current = ElectricCurrent::new::<ampere>(value);
|
||||||
|
let (current, max) = channels.set_max_i_neg(channel, current);
|
||||||
|
let _ = writeln!(
|
||||||
|
socket, "channel {:.3}: max_i_neg set to {:.3} / {:.3}",
|
||||||
|
channel,
|
||||||
|
current.into_format_args(ampere, Abbreviation),
|
||||||
|
max.into_format_args(ampere, Abbreviation),
|
||||||
|
);
|
||||||
|
}
|
||||||
_ =>
|
_ =>
|
||||||
unreachable!(),
|
unreachable!(),
|
||||||
};
|
}
|
||||||
let _ = writeln!(
|
|
||||||
socket, "channel {}: PWM {} reconfigured to {}/{}",
|
|
||||||
channel, pin.name(), value, max
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
Command::Pid { channel, parameter, value } => {
|
Command::Pid { channel, parameter, value } => {
|
||||||
let pid = &mut channels.channel_state(channel).pid;
|
let pid = &mut channels.channel_state(channel).pid;
|
||||||
|
@ -361,9 +385,9 @@ fn main() -> ! {
|
||||||
let sh = &mut channels.channel_state(channel).sh;
|
let sh = &mut channels.channel_state(channel).sh;
|
||||||
use command_parser::ShParameter::*;
|
use command_parser::ShParameter::*;
|
||||||
match parameter {
|
match parameter {
|
||||||
T0 => sh.t0 = value,
|
T0 => sh.t0 = ThermodynamicTemperature::new::<degree_celsius>(value),
|
||||||
B => sh.b = value,
|
B => sh.b = value,
|
||||||
R0 => sh.r0 = value,
|
R0 => sh.r0 = ElectricalResistance::new::<ohm>(value),
|
||||||
}
|
}
|
||||||
let _ = writeln!(socket, "Steinhart-Hart equation parameter updated");
|
let _ = writeln!(socket, "Steinhart-Hart equation parameter updated");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,23 @@
|
||||||
use num_traits::float::Float;
|
use num_traits::float::Float;
|
||||||
|
use uom::si::{
|
||||||
|
f64::{
|
||||||
|
ElectricalResistance,
|
||||||
|
ThermodynamicTemperature,
|
||||||
|
},
|
||||||
|
electrical_resistance::ohm,
|
||||||
|
ratio::ratio,
|
||||||
|
thermodynamic_temperature::{degree_celsius, kelvin},
|
||||||
|
};
|
||||||
|
|
||||||
/// Steinhart-Hart equation parameters
|
/// Steinhart-Hart equation parameters
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Parameters {
|
pub struct Parameters {
|
||||||
pub t0: f64,
|
/// Base temperature
|
||||||
|
pub t0: ThermodynamicTemperature,
|
||||||
|
/// Base resistance
|
||||||
|
pub r0: ElectricalResistance,
|
||||||
|
/// Beta
|
||||||
pub b: f64,
|
pub b: f64,
|
||||||
pub r0: f64,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parameters {
|
impl Parameters {
|
||||||
|
@ -14,18 +26,18 @@ impl Parameters {
|
||||||
/// Result unit: Kelvin
|
/// Result unit: Kelvin
|
||||||
///
|
///
|
||||||
/// TODO: verify
|
/// TODO: verify
|
||||||
pub fn get_temperature(&self, r: f64) -> f64 {
|
pub fn get_temperature(&self, r: ElectricalResistance) -> ThermodynamicTemperature {
|
||||||
let inv_temp = 1.0 / self.t0 + (r / self.r0).ln() / self.b;
|
let inv_temp = 1.0 / self.t0.get::<kelvin>() + (r / self.r0).get::<ratio>().ln() / self.b;
|
||||||
1.0 / inv_temp
|
ThermodynamicTemperature::new::<kelvin>(1.0 / inv_temp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Parameters {
|
impl Default for Parameters {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Parameters {
|
Parameters {
|
||||||
t0: 0.001_4,
|
t0: ThermodynamicTemperature::new::<degree_celsius>(25.0),
|
||||||
b: 0.000_000_099,
|
r0: ElectricalResistance::new::<ohm>(10_000.0),
|
||||||
r0: 5_110.0,
|
b: 3800.0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue