Compare commits

..

No commits in common. "fc0ca8b581d6f8f9635c26f63764729210382d45" and "b1b6d1ea9416df9aca6b0051ccd0282c56b42376" have entirely different histories.

8 changed files with 112 additions and 305 deletions

View File

@ -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 *ampere* | | `pwm <0/1> max_i_pos <ratio>` | Set PWM duty cycle for **max_i_pos** to *ratio* |
| `pwm <0/1> max_i_neg <ratio>` | Set PWM duty cycle for **max_i_neg** to *ampere* | | `pwm <0/1> max_i_neg <ratio>` | Set PWM duty cycle for **max_i_neg** to *ratio* |
| `pwm <0/1> max_v <ratio>` | Set PWM duty cycle for **max_v** to *volt* | | `pwm <0/1> max_v <ratio>` | Set PWM duty cycle for **max_v** to *ratio* |
| `pwm <0/1> <volts>` | Disengage PID, set **i_set** DAC to *ampere* | | `pwm <0/1> <volts>` | Disengage PID, set **i_set** DAC to *volts* |
| `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 |

View File

@ -45,14 +45,13 @@ impl<SPI: Transfer<u8>, S: OutputPin> Dac<SPI, S> {
Ok(()) Ok(())
} }
pub fn set(&mut self, value: u32) -> Result<u32, SPI::Error> { pub fn set(&mut self, value: u32) -> Result<(), 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)
} }
} }

View File

@ -4,10 +4,6 @@ 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},
@ -276,7 +272,7 @@ pub struct ChannelCalibration {
} }
impl ChannelCalibration { impl ChannelCalibration {
pub fn convert_data(&self, data: u32) -> ElectricPotential { pub fn convert_data(&self, data: u32) -> f64 {
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 {
@ -286,7 +282,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.3; const V_REF: f64 = 3.0;
ElectricPotential::new::<volt>(data * V_REF / 0.75) data * V_REF / 0.75
} }
} }

View File

@ -1,13 +1,7 @@
use smoltcp::time::Instant; use smoltcp::time::Instant;
use uom::si::{ use uom::si::{
f64::{ f64::ElectricPotential,
ElectricPotential,
ElectricalResistance,
ThermodynamicTemperature,
},
electric_potential::volt, electric_potential::volt,
electrical_resistance::ohm,
temperature_interval::kelvin,
}; };
use crate::{ use crate::{
ad7172, ad7172,
@ -15,13 +9,11 @@ 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,
@ -34,8 +26,6 @@ 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()),
@ -49,26 +39,18 @@ 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) { pub fn update_pid(&mut self) -> f64 {
// Update PID controller // Update PID controller
// self.pid.update(self.get_temperature().unwrap().get::<kelvin>()) self.pid.update(self.get_temperature().unwrap())
// TODO: add output field
} }
pub fn get_adc(&self) -> Option<ElectricPotential> { pub fn get_adc(&self) -> Option<ElectricPotential> {
Some(self.adc_calibration.convert_data(self.adc_data?)) let volts = self.adc_calibration.convert_data(self.adc_data?);
Some(ElectricPotential::new::<volt>(volts))
} }
/// Get `SENS[01]` input resistance pub fn get_temperature(&self) -> Option<f64> {
pub fn get_sens(&self) -> Option<ElectricalResistance> { let r = self.get_adc()?.get::<volt>();
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)
} }

View File

@ -1,11 +1,7 @@
use stm32f4xx_hal::hal;
use smoltcp::time::Instant; use smoltcp::time::Instant;
use uom::si::{ use uom::si::{
f64::{ElectricCurrent, ElectricPotential, ElectricalResistance}, f64::ElectricPotential,
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::{
@ -13,12 +9,10 @@ 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 {
@ -51,8 +45,6 @@ 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
@ -84,8 +76,7 @@ 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
// TODO: self.set_dac(channel.into(), ElectricPotential::new::<volt>(dac_value));
// self.set_dac(channel.into(), ElectricPotential::new::<volt>(dac_value));
} }
channel channel
@ -93,53 +84,24 @@ impl Channels {
} }
/// i_set DAC /// i_set DAC
fn get_dac(&mut self, channel: usize) -> (ElectricPotential, ElectricPotential) { pub fn set_dac(&mut self, channel: usize, voltage: 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;
let value = match channel { match channel {
0 => self.channel0.dac.set(value).unwrap(), 0 => {
1 => self.channel1.dac.set(value).unwrap(), self.channel0.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 {
@ -209,7 +171,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::<millivolt>(mv as f64) ElectricPotential::new::<volt>(mv as f64 / 1000.0)
} }
1 => { 1 => {
let sample = self.pins_adc.convert( let sample = self.pins_adc.convert(
@ -217,7 +179,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::<millivolt>(mv as f64) ElectricPotential::new::<volt>(mv as f64 / 1000.0)
} }
_ => unreachable!(), _ => unreachable!(),
} }
@ -290,100 +252,4 @@ 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)
}
} }

View File

@ -142,7 +142,7 @@ pub enum Command {
Pwm { Pwm {
channel: usize, channel: usize,
pin: PwmPin, pin: PwmPin,
value: f64, duty: 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(|value| (pin, value)); result.map(|duty| (pin, duty));
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, value)) => Ok((pin, duty)) =>
Ok((input, Ok(Command::Pwm { channel, pin, value }))), Ok((input, Ok(Command::Pwm { channel, pin, duty }))),
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,
value: 16383, duty: 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,
value: 7, duty: 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,
value: 128, duty: 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,
value: 32768, duty: 32768,
})); }));
} }

View File

@ -32,15 +32,12 @@ 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,
}, },
}; };
@ -169,7 +166,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_input) = channels.channel_state(channel).get_adc() { if let Some(adc_data) = channels.channel_state(channel).adc_data {
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);
@ -180,10 +177,9 @@ fn main() -> ! {
let state = channels.channel_state(channel); let state = channels.channel_state(channel);
let _ = writeln!( let _ = writeln!(
socket, "channel {}: t={} adc{}={} adc_r={} vref={} dac_feedback={} itec={} tec={} tec_u_meas={} r={:03}", socket, "channel {}: t={} adc{}={} vref={} dac_feedback={} itec={} tec={} tec_u_meas={} r={:03}",
channel, state.adc_time, channel,
channel, adc_input.into_format_args(volt, Abbreviation), state.adc_time, channel, adc_data,
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),
@ -229,32 +225,33 @@ fn main() -> ! {
channel, channel,
if state.pid_engaged { "engaged" } else { "disengaged" } if state.pid_engaged { "engaged" } else { "disengaged" }
); );
let i_set = channels.get_i(channel); let _ = writeln!(socket, "- i_set={}", state.dac_value.into_format_args(volt, Abbreviation));
let _ = writeln!( fn show_pwm_channel<S, P>(mut socket: S, name: &str, pin: &P)
socket, "- i_set={:.3} / {:.3}", where
i_set.0.into_format_args(ampere, Abbreviation), S: core::fmt::Write,
i_set.1.into_format_args(ampere, Abbreviation), P: hal::PwmPin<Duty=u16>,
); {
let max_v = channels.get_max_v(channel); let _ = writeln!(
let _ = writeln!( socket,
socket, "- max_v={:.3} / {:.3}", "- {}={}/{}",
max_v.0.into_format_args(volt, Abbreviation), name, pin.get_duty(), pin.get_max_duty()
max_v.1.into_format_args(volt, Abbreviation), );
); }
let max_i_pos = channels.get_max_i_pos(channel); match channel {
let _ = writeln!( 0 => {
socket, "- max_i_pos={:.3} / {:.3}", show_pwm_channel(socket.deref_mut(), "max_v", &channels.pwm.max_v0);
max_i_pos.0.into_format_args(ampere, Abbreviation), show_pwm_channel(socket.deref_mut(), "max_i_pos", &channels.pwm.max_i_pos0);
max_i_pos.1.into_format_args(ampere, Abbreviation), show_pwm_channel(socket.deref_mut(), "max_i_neg", &channels.pwm.max_i_neg0);
); }
let max_i_neg = channels.get_max_i_neg(channel); 1 => {
let _ = writeln!( show_pwm_channel(socket.deref_mut(), "max_v", &channels.pwm.max_v1);
socket, "- max_i_neg={:.3} / {:.3}", show_pwm_channel(socket.deref_mut(), "max_i_pos", &channels.pwm.max_i_pos1);
max_i_neg.0.into_format_args(ampere, Abbreviation), show_pwm_channel(socket.deref_mut(), "max_i_neg", &channels.pwm.max_i_neg1);
max_i_neg.1.into_format_args(ampere, Abbreviation), }
); _ => 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 {
@ -263,20 +260,9 @@ fn main() -> ! {
socket, "channel {}: Steinhart-Hart equation parameters", socket, "channel {}: Steinhart-Hart equation parameters",
channel, channel,
); );
let _ = writeln!(socket, "- t0={}", state.sh.t0.into_format_args(degree_celsius, Abbreviation)); let _ = writeln!(socket, "- t0={}", state.sh.t0);
let _ = writeln!(socket, "- b={}", state.sh.b); let _ = writeln!(socket, "- b={}", state.sh.b);
let _ = writeln!(socket, "- r0={}", state.sh.r0.into_format_args(ohm, Abbreviation)); let _ = writeln!(socket, "- r0={}", state.sh.r0);
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, "");
} }
} }
@ -304,56 +290,46 @@ 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, value } => { Command::Pwm { channel, pin: PwmPin::ISet, duty } => {
channels.channel_state(channel).pid_engaged = false; channels.channel_state(channel).pid_engaged = false;
leds.g3.off(); leds.g3.off();
let current = ElectricCurrent::new::<ampere>(value); let voltage = ElectricPotential::new::<volt>(duty);
let (current, max) = channels.set_i(channel, current); channels.set_dac(channel, voltage);
let _ = writeln!( let _ = writeln!(
socket, "channel {}: i_set DAC output set to {:.3} / {:.3}", socket, "channel {}: PWM duty cycle manually set to {}",
channel, channel, voltage.into_format_args(volt, Abbreviation),
current.into_format_args(ampere, Abbreviation),
max.into_format_args(ampere, Abbreviation),
); );
} }
Command::Pwm { channel, pin, value } => { Command::Pwm { channel, pin, duty } => {
match pin { fn set_pwm_channel<P: hal::PwmPin<Duty=u16>>(pin: &mut P, duty: f64) -> (u16, u16) {
PwmPin::ISet => let max = pin.get_max_duty();
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!(),
PwmPin::MaxV => { (0, PwmPin::MaxIPos) =>
let voltage = ElectricPotential::new::<volt>(value); set_pwm_channel(&mut channels.pwm.max_i_pos0, duty),
let (voltage, max) = channels.set_max_v(channel, voltage); (0, PwmPin::MaxINeg) =>
let _ = writeln!( set_pwm_channel(&mut channels.pwm.max_i_neg0, duty),
socket, "channel {:.3}: max_v set to {:.3} / {:.3}", (0, PwmPin::MaxV) =>
channel, set_pwm_channel(&mut channels.pwm.max_v0, duty),
voltage.into_format_args(volt, Abbreviation), (1, PwmPin::MaxIPos) =>
max.into_format_args(volt, Abbreviation), set_pwm_channel(&mut channels.pwm.max_i_pos1, duty),
); (1, PwmPin::MaxINeg) =>
} set_pwm_channel(&mut channels.pwm.max_i_neg1, duty),
PwmPin::MaxIPos => { (1, PwmPin::MaxV) =>
let current = ElectricCurrent::new::<ampere>(value); set_pwm_channel(&mut channels.pwm.max_v1, duty),
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;
@ -385,9 +361,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 = ThermodynamicTemperature::new::<degree_celsius>(value), T0 => sh.t0 = value,
B => sh.b = value, B => sh.b = value,
R0 => sh.r0 = ElectricalResistance::new::<ohm>(value), R0 => sh.r0 = value,
} }
let _ = writeln!(socket, "Steinhart-Hart equation parameter updated"); let _ = writeln!(socket, "Steinhart-Hart equation parameter updated");
} }

View File

@ -1,23 +1,11 @@
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 {
/// Base temperature pub t0: f64,
pub t0: ThermodynamicTemperature,
/// Base resistance
pub r0: ElectricalResistance,
/// Beta
pub b: f64, pub b: f64,
pub r0: f64,
} }
impl Parameters { impl Parameters {
@ -26,18 +14,18 @@ impl Parameters {
/// Result unit: Kelvin /// Result unit: Kelvin
/// ///
/// TODO: verify /// TODO: verify
pub fn get_temperature(&self, r: ElectricalResistance) -> ThermodynamicTemperature { pub fn get_temperature(&self, r: f64) -> f64 {
let inv_temp = 1.0 / self.t0.get::<kelvin>() + (r / self.r0).get::<ratio>().ln() / self.b; let inv_temp = 1.0 / self.t0 + (r / self.r0).ln() / self.b;
ThermodynamicTemperature::new::<kelvin>(1.0 / inv_temp) 1.0 / inv_temp
} }
} }
impl Default for Parameters { impl Default for Parameters {
fn default() -> Self { fn default() -> Self {
Parameters { Parameters {
t0: ThermodynamicTemperature::new::<degree_celsius>(25.0), t0: 0.001_4,
r0: ElectricalResistance::new::<ohm>(10_000.0), b: 0.000_000_099,
b: 3800.0, r0: 5_110.0,
} }
} }
} }