From 8c80062da86ae36914ae4ab44d297f7a40dfae67 Mon Sep 17 00:00:00 2001 From: Astro Date: Wed, 16 Sep 2020 22:05:31 +0200 Subject: [PATCH] use proper units for pwm pins --- README.md | 6 +-- src/ad5680.rs | 5 +- src/channels.rs | 122 +++++++++++++++++++++++++++++++++++++----- src/command_parser.rs | 16 +++--- src/main.rs | 119 ++++++++++++++++++++-------------------- 5 files changed, 186 insertions(+), 82 deletions(-) diff --git a/README.md b/README.md index c507035..f9576ed 100644 --- a/README.md +++ b/README.md @@ -52,9 +52,9 @@ The scope of this setting is per TCP session. | `report mode` | Show current report mode | | `report mode ` | Set report mode | | `pwm` | Show current PWM settings | -| `pwm <0/1> max_i_pos ` | Set PWM duty cycle for **max_i_pos** to *ratio* | -| `pwm <0/1> max_i_neg ` | Set PWM duty cycle for **max_i_neg** to *ratio* | -| `pwm <0/1> max_v ` | Set PWM duty cycle for **max_v** to *ratio* | +| `pwm <0/1> max_i_pos ` | Set PWM duty cycle for **max_i_pos** to *ampere* | +| `pwm <0/1> max_i_neg ` | Set PWM duty cycle for **max_i_neg** to *ampere* | +| `pwm <0/1> max_v ` | Set PWM duty cycle for **max_v** to *volt* | | `pwm <0/1> ` | Disengage PID, set **i_set** DAC to *volts* | | `pwm <0/1> pid` | Set PWM to be controlled by PID | | `pid` | Show PID configuration | diff --git a/src/ad5680.rs b/src/ad5680.rs index 24217f3..9af3bd4 100644 --- a/src/ad5680.rs +++ b/src/ad5680.rs @@ -45,13 +45,14 @@ impl, S: OutputPin> Dac { Ok(()) } - pub fn set(&mut self, value: u32) -> Result<(), SPI::Error> { + pub fn set(&mut self, value: u32) -> Result { let value = value.min(MAX_VALUE); let mut buf = [ (value >> 14) as u8, (value >> 6) as u8, (value << 2) as u8, ]; - self.write(&mut buf) + self.write(&mut buf)?; + Ok(value) } } diff --git a/src/channels.rs b/src/channels.rs index 9720c6b..b0a9e2d 100644 --- a/src/channels.rs +++ b/src/channels.rs @@ -1,7 +1,10 @@ +use stm32f4xx_hal::hal; use smoltcp::time::Instant; use uom::si::{ - f64::ElectricPotential, + f64::{ElectricCurrent, ElectricPotential}, electric_potential::{millivolt, volt}, + electric_current::ampere, + ratio::ratio, }; use log::info; use crate::{ @@ -9,6 +12,7 @@ use crate::{ ad7172, channel::{Channel, Channel0, Channel1}, channel_state::ChannelState, + command_parser::PwmPin, pins, }; @@ -87,24 +91,22 @@ impl Channels { } /// i_set DAC - pub fn set_dac(&mut self, channel: usize, voltage: ElectricPotential) { + pub fn set_dac(&mut self, channel: usize, voltage: ElectricPotential) -> (ElectricPotential, ElectricPotential) { let dac_factor = match channel.into() { 0 => self.channel0.dac_factor, 1 => self.channel1.dac_factor, _ => unreachable!(), }; let value = (voltage.get::() * dac_factor) as u32; - match channel { - 0 => { - 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; - } + let value = match channel { + 0 => self.channel0.dac.set(value).unwrap(), + 1 => self.channel1.dac.set(value).unwrap(), _ => unreachable!(), - } + }; + let voltage = ElectricPotential::new::(value as f64 / dac_factor); + self.channel_state(channel).dac_value = voltage; + let max = ElectricPotential::new::(ad5680::MAX_VALUE as f64 / dac_factor); + (voltage, max) } pub fn read_dac_feedback(&mut self, channel: usize) -> ElectricPotential { @@ -255,4 +257,100 @@ impl Channels { // Reset self.set_dac(channel, ElectricPotential::new::(0.0)); } + + fn get_pwm(&self, channel: usize, pin: PwmPin) -> f64 { + fn get>(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::(3.0) / ElectricCurrent::new::(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::(3.0) / ElectricCurrent::new::(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>(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::(); + 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::(3.0) / ElectricCurrent::new::(1.0); + let duty = (max_i_pos * scale).get::(); + 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::(3.0) / ElectricCurrent::new::(1.0); + let duty = (max_i_neg * scale).get::(); + let duty = self.set_pwm(channel, PwmPin::MaxINeg, duty); + (duty / scale, 1.0 / scale) + } } diff --git a/src/command_parser.rs b/src/command_parser.rs index ee043b2..17a4077 100644 --- a/src/command_parser.rs +++ b/src/command_parser.rs @@ -142,7 +142,7 @@ pub enum Command { Pwm { channel: usize, pin: PwmPin, - duty: f64, + value: f64, }, /// Enable PID control for `i_set` PwmPid { @@ -242,7 +242,7 @@ fn report(input: &[u8]) -> IResult<&[u8], Command> { fn pwm_setup(input: &[u8]) -> IResult<&[u8], Result<(PwmPin, f64), Error>> { let result_with_pin = |pin: PwmPin| move |result: Result| - result.map(|duty| (pin, duty)); + result.map(|value| (pin, value)); alt(( map( @@ -300,8 +300,8 @@ fn pwm(input: &[u8]) -> IResult<&[u8], Result> { |input| { let (input, config) = pwm_setup(input)?; match config { - Ok((pin, duty)) => - Ok((input, Ok(Command::Pwm { channel, pin, duty }))), + Ok((pin, value)) => + Ok((input, Ok(Command::Pwm { channel, pin, value }))), Err(e) => Ok((input, Err(e))), } @@ -461,7 +461,7 @@ mod test { assert_eq!(command, Ok(Command::Pwm { channel: 1, pin: PwmPin::ISet, - duty: 16383, + value: 16383, })); } @@ -480,7 +480,7 @@ mod test { assert_eq!(command, Ok(Command::Pwm { channel: 0, pin: PwmPin::MaxIPos, - duty: 7, + value: 7, })); } @@ -490,7 +490,7 @@ mod test { assert_eq!(command, Ok(Command::Pwm { channel: 0, pin: PwmPin::MaxINeg, - duty: 128, + value: 128, })); } @@ -500,7 +500,7 @@ mod test { assert_eq!(command, Ok(Command::Pwm { channel: 0, pin: PwmPin::MaxV, - duty: 32768, + value: 32768, })); } diff --git a/src/main.rs b/src/main.rs index ff421be..8bb7c7d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -32,6 +32,7 @@ use uom::{ fmt::DisplayStyle::Abbreviation, si::{ f64::{ + ElectricCurrent, ElectricPotential, ElectricalResistance, ThermodynamicTemperature, @@ -228,33 +229,27 @@ fn main() -> ! { channel, if state.pid_engaged { "engaged" } else { "disengaged" } ); - let _ = writeln!(socket, "- i_set={}", state.dac_value.into_format_args(volt, Abbreviation)); - fn show_pwm_channel(mut socket: S, name: &str, pin: &P) - where - S: core::fmt::Write, - P: hal::PwmPin, - { - let _ = writeln!( - socket, - "- {}={}/{}", - name, pin.get_duty(), pin.get_max_duty() - ); - } - 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, "- i_set={:.3}", state.dac_value.into_format_args(volt, 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), + ); } + let _ = writeln!(socket, ""); } Command::Show(ShowCommand::SteinhartHart) => { for channel in 0..CHANNELS { @@ -304,46 +299,56 @@ fn main() -> ! { 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; leds.g3.off(); - let voltage = ElectricPotential::new::(duty); - channels.set_dac(channel, voltage); + let voltage = ElectricPotential::new::(value); + let (voltage, max) = channels.set_dac(channel, voltage); let _ = writeln!( - socket, "channel {}: PWM duty cycle manually set to {}", - channel, voltage.into_format_args(volt, Abbreviation), + socket, "channel {}: i_set DAC output set to {:.3} / {:.3}", + channel, + voltage.into_format_args(volt, Abbreviation), + max.into_format_args(volt, Abbreviation), ); } - Command::Pwm { channel, pin, duty } => { - fn set_pwm_channel>(pin: &mut P, duty: f64) -> (u16, u16) { - 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) => + Command::Pwm { channel, pin, value } => { + match pin { + PwmPin::ISet => // Handled above unreachable!(), - (0, PwmPin::MaxIPos) => - set_pwm_channel(&mut channels.pwm.max_i_pos0, duty), - (0, PwmPin::MaxINeg) => - set_pwm_channel(&mut channels.pwm.max_i_neg0, duty), - (0, PwmPin::MaxV) => - set_pwm_channel(&mut channels.pwm.max_v0, duty), - (1, PwmPin::MaxIPos) => - set_pwm_channel(&mut channels.pwm.max_i_pos1, duty), - (1, PwmPin::MaxINeg) => - set_pwm_channel(&mut channels.pwm.max_i_neg1, duty), - (1, PwmPin::MaxV) => - set_pwm_channel(&mut channels.pwm.max_v1, duty), + PwmPin::MaxV => { + let voltage = ElectricPotential::new::(value); + let (voltage, max) = channels.set_max_v(channel, voltage); + let _ = writeln!( + socket, "channel {:.3}: max_v set to {:.3} / {:.3}", + channel, + voltage.into_format_args(volt, Abbreviation), + max.into_format_args(volt, Abbreviation), + ); + } + PwmPin::MaxIPos => { + let current = ElectricCurrent::new::(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::(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!(), - }; - let _ = writeln!( - socket, "channel {}: PWM {} reconfigured to {}/{}", - channel, pin.name(), value, max - ); + } } Command::Pid { channel, parameter, value } => { let pid = &mut channels.channel_state(channel).pid;