forked from M-Labs/thermostat
use proper units for pwm pins
This commit is contained in:
parent
7d45d5ad32
commit
8c80062da8
|
@ -52,9 +52,9 @@ The scope of this setting is per TCP session.
|
|||
| `report mode` | Show current report mode |
|
||||
| `report mode <off/on>` | Set report mode |
|
||||
| `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_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 *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 *ampere* |
|
||||
| `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> pid` | Set PWM to be controlled by PID |
|
||||
| `pid` | Show PID configuration |
|
||||
|
|
|
@ -45,13 +45,14 @@ impl<SPI: Transfer<u8>, S: OutputPin> Dac<SPI, S> {
|
|||
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 mut buf = [
|
||||
(value >> 14) as u8,
|
||||
(value >> 6) as u8,
|
||||
(value << 2) as u8,
|
||||
];
|
||||
self.write(&mut buf)
|
||||
self.write(&mut buf)?;
|
||||
Ok(value)
|
||||
}
|
||||
}
|
||||
|
|
122
src/channels.rs
122
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::<volt>() * 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::<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 read_dac_feedback(&mut self, channel: usize) -> ElectricPotential {
|
||||
|
@ -255,4 +257,100 @@ impl Channels {
|
|||
// Reset
|
||||
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 {
|
||||
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<f64, Error>|
|
||||
result.map(|duty| (pin, duty));
|
||||
result.map(|value| (pin, value));
|
||||
|
||||
alt((
|
||||
map(
|
||||
|
@ -300,8 +300,8 @@ fn pwm(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
|
|||
|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,
|
||||
}));
|
||||
}
|
||||
|
||||
|
|
113
src/main.rs
113
src/main.rs
|
@ -32,6 +32,7 @@ use uom::{
|
|||
fmt::DisplayStyle::Abbreviation,
|
||||
si::{
|
||||
f64::{
|
||||
ElectricCurrent,
|
||||
ElectricPotential,
|
||||
ElectricalResistance,
|
||||
ThermodynamicTemperature,
|
||||
|
@ -228,34 +229,28 @@ 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<S, P>(mut socket: S, name: &str, pin: &P)
|
||||
where
|
||||
S: core::fmt::Write,
|
||||
P: hal::PwmPin<Duty=u16>,
|
||||
{
|
||||
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,
|
||||
"- {}={}/{}",
|
||||
name, pin.get_duty(), pin.get_max_duty()
|
||||
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, "");
|
||||
}
|
||||
}
|
||||
Command::Show(ShowCommand::SteinhartHart) => {
|
||||
for channel in 0..CHANNELS {
|
||||
let state = channels.channel_state(channel);
|
||||
|
@ -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::<volt>(duty);
|
||||
channels.set_dac(channel, voltage);
|
||||
let voltage = ElectricPotential::new::<volt>(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<P: hal::PwmPin<Duty=u16>>(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::<volt>(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::<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!(),
|
||||
};
|
||||
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;
|
||||
|
|
Loading…
Reference in New Issue