forked from M-Labs/thermostat
control i_set dac in volts
This commit is contained in:
parent
38a220ce4e
commit
ba860e52ac
|
@ -6,6 +6,7 @@ use stm32f4xx_hal::{
|
|||
time::MegaHertz,
|
||||
spi,
|
||||
};
|
||||
use crate::units::Volts;
|
||||
|
||||
/// SPI Mode 1
|
||||
pub const SPI_MODE: spi::Mode = spi::Mode {
|
||||
|
@ -44,8 +45,9 @@ impl<SPI: Transfer<u8>, S: OutputPin> Dac<SPI, S> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// value: `0..0x20_000`
|
||||
pub fn set(&mut self, value: u32) -> Result<(), SPI::Error> {
|
||||
pub fn set(&mut self, voltage: Volts) -> Result<(), SPI::Error> {
|
||||
let value = ((voltage.0 * (MAX_VALUE as f64) / 5.0) as u32)
|
||||
.min(MAX_VALUE);
|
||||
let buf = [
|
||||
(value >> 14) as u8,
|
||||
(value >> 6) as u8,
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
use crate::pins::{ChannelPins, ChannelPinSet};
|
||||
use crate::channel_state::ChannelState;
|
||||
use crate::ad5680;
|
||||
use crate::{
|
||||
ad5680,
|
||||
channel_state::ChannelState,
|
||||
pins::{ChannelPins, ChannelPinSet},
|
||||
units::Volts,
|
||||
};
|
||||
|
||||
/// Marker type for the first channel
|
||||
pub struct Channel0;
|
||||
|
@ -25,7 +28,7 @@ impl<C: ChannelPins> Channel<C> {
|
|||
pub fn new(pins: ChannelPinSet<C>) -> Self {
|
||||
let state = ChannelState::default();
|
||||
let mut dac = ad5680::Dac::new(pins.dac_spi, pins.dac_sync);
|
||||
let _ = dac.set(0);
|
||||
let _ = dac.set(Volts(0.0));
|
||||
|
||||
Channel {
|
||||
state,
|
||||
|
|
|
@ -1,11 +1,16 @@
|
|||
use smoltcp::time::Instant;
|
||||
use crate::{ad5680, ad7172, pid, steinhart_hart as sh};
|
||||
use crate::{
|
||||
ad7172,
|
||||
pid,
|
||||
steinhart_hart as sh,
|
||||
units::Volts,
|
||||
};
|
||||
|
||||
|
||||
pub struct ChannelState {
|
||||
pub adc_data: Option<u32>,
|
||||
pub adc_time: Instant,
|
||||
pub dac_value: u32,
|
||||
pub dac_value: Volts,
|
||||
pub pid_engaged: bool,
|
||||
pub pid: pid::Controller,
|
||||
pub sh: sh::Parameters,
|
||||
|
@ -16,7 +21,7 @@ impl Default for ChannelState {
|
|||
ChannelState {
|
||||
adc_data: None,
|
||||
adc_time: Instant::from_secs(0),
|
||||
dac_value: 0,
|
||||
dac_value: Volts(0.0),
|
||||
pid_engaged: false,
|
||||
pid: pid::Controller::new(pid::Parameters::default()),
|
||||
sh: sh::Parameters::default(),
|
||||
|
@ -26,14 +31,13 @@ impl Default for ChannelState {
|
|||
|
||||
impl ChannelState {
|
||||
/// Update PID state on ADC input, calculate new DAC output
|
||||
pub fn update_pid(&mut self, now: Instant, adc_data: u32) -> u32 {
|
||||
pub fn update_pid(&mut self, now: Instant, adc_data: u32) -> f64 {
|
||||
self.adc_data = Some(adc_data);
|
||||
self.adc_time = now;
|
||||
|
||||
// Update PID controller
|
||||
let input = (adc_data as f64) / (ad7172::MAX_VALUE as f64);
|
||||
let temperature = self.sh.get_temperature(input);
|
||||
let output = self.pid.update(temperature);
|
||||
(output * (ad5680::MAX_VALUE as f64)) as u32
|
||||
self.pid.update(temperature)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,7 +59,7 @@ impl Channels {
|
|||
};
|
||||
if let Some(dac_value) = dac_value {
|
||||
// Forward PID output to i_set DAC
|
||||
self.set_dac(channel.into(), dac_value);
|
||||
self.set_dac(channel.into(), Volts(dac_value));
|
||||
}
|
||||
|
||||
channel
|
||||
|
@ -67,16 +67,16 @@ impl Channels {
|
|||
}
|
||||
|
||||
/// i_set DAC
|
||||
pub fn set_dac(&mut self, channel: usize, duty: u32) {
|
||||
pub fn set_dac(&mut self, channel: usize, voltage: Volts) {
|
||||
match channel {
|
||||
0 => {
|
||||
self.channel0.dac.set(duty).unwrap();
|
||||
self.channel0.state.dac_value = duty;
|
||||
self.channel0.dac.set(voltage).unwrap();
|
||||
self.channel0.state.dac_value = voltage;
|
||||
self.channel0.shdn.set_high().unwrap();
|
||||
}
|
||||
1 => {
|
||||
self.channel1.dac.set(duty).unwrap();
|
||||
self.channel1.state.dac_value = duty;
|
||||
self.channel1.dac.set(voltage).unwrap();
|
||||
self.channel1.state.dac_value = voltage;
|
||||
self.channel1.shdn.set_high().unwrap();
|
||||
}
|
||||
_ => unreachable!(),
|
||||
|
|
|
@ -142,7 +142,7 @@ pub enum Command {
|
|||
Pwm {
|
||||
channel: usize,
|
||||
pin: PwmPin,
|
||||
duty: u32,
|
||||
duty: f64,
|
||||
},
|
||||
/// Enable PID control for `i_set`
|
||||
PwmPid {
|
||||
|
@ -239,9 +239,9 @@ fn report(input: &[u8]) -> IResult<&[u8], Command> {
|
|||
)(input)
|
||||
}
|
||||
|
||||
fn pwm_setup(input: &[u8]) -> IResult<&[u8], Result<(PwmPin, u32), Error>> {
|
||||
fn pwm_setup(input: &[u8]) -> IResult<&[u8], Result<(PwmPin, f64), Error>> {
|
||||
let result_with_pin = |pin: PwmPin|
|
||||
move |result: Result<u32, Error>|
|
||||
move |result: Result<f64, Error>|
|
||||
result.map(|duty| (pin, duty));
|
||||
|
||||
alt((
|
||||
|
@ -250,7 +250,7 @@ fn pwm_setup(input: &[u8]) -> IResult<&[u8], Result<(PwmPin, u32), Error>> {
|
|||
tag("max_i_pos"),
|
||||
preceded(
|
||||
whitespace,
|
||||
unsigned
|
||||
float
|
||||
)
|
||||
),
|
||||
result_with_pin(PwmPin::MaxIPos)
|
||||
|
@ -260,7 +260,7 @@ fn pwm_setup(input: &[u8]) -> IResult<&[u8], Result<(PwmPin, u32), Error>> {
|
|||
tag("max_i_neg"),
|
||||
preceded(
|
||||
whitespace,
|
||||
unsigned
|
||||
float
|
||||
)
|
||||
),
|
||||
result_with_pin(PwmPin::MaxINeg)
|
||||
|
@ -270,12 +270,12 @@ fn pwm_setup(input: &[u8]) -> IResult<&[u8], Result<(PwmPin, u32), Error>> {
|
|||
tag("max_v"),
|
||||
preceded(
|
||||
whitespace,
|
||||
unsigned
|
||||
float
|
||||
)
|
||||
),
|
||||
result_with_pin(PwmPin::MaxV)
|
||||
),
|
||||
map(unsigned, result_with_pin(PwmPin::ISet)
|
||||
map(float, result_with_pin(PwmPin::ISet)
|
||||
))
|
||||
)(input)
|
||||
}
|
||||
|
|
29
src/main.rs
29
src/main.rs
|
@ -43,7 +43,7 @@ mod command_parser;
|
|||
use command_parser::{Command, ShowCommand, PwmPin};
|
||||
mod timer;
|
||||
mod units;
|
||||
use units::{Amps, Ohms};
|
||||
use units::{Amps, Ohms, Volts};
|
||||
mod pid;
|
||||
mod steinhart_hart;
|
||||
mod channels;
|
||||
|
@ -144,7 +144,7 @@ fn main() -> ! {
|
|||
for channel in 0..CHANNELS {
|
||||
if let Some(adc_data) = channels.channel_state(channel).adc_data {
|
||||
let dac_loopback = channels.read_dac_loopback(channel);
|
||||
let dac_i = dac_loopback.clone() / Ohms(5.0);
|
||||
let dac_i = dac_loopback / Ohms(5.0);
|
||||
|
||||
let itec = channels.read_itec(channel);
|
||||
let tec_i = Amps((itec.0 - 1.5) / 8.0);
|
||||
|
@ -194,7 +194,7 @@ fn main() -> ! {
|
|||
channel,
|
||||
if state.pid_engaged { "engaged" } else { "disengaged" }
|
||||
);
|
||||
let _ = writeln!(socket, "- i_set={}/{}", state.dac_value, ad5680::MAX_VALUE);
|
||||
let _ = writeln!(socket, "- i_set={}", state.dac_value);
|
||||
fn show_pwm_channel<S, P>(mut socket: S, name: &str, pin: &P)
|
||||
where
|
||||
S: core::fmt::Write,
|
||||
|
@ -258,21 +258,16 @@ fn main() -> ! {
|
|||
let _ = writeln!(socket, "channel {}: PID enabled to control PWM", channel
|
||||
);
|
||||
}
|
||||
Command::Pwm { channel, pin: PwmPin::ISet, duty } if duty <= ad5680::MAX_VALUE => {
|
||||
Command::Pwm { channel, pin: PwmPin::ISet, duty } => {
|
||||
channels.channel_state(channel).pid_engaged = false;
|
||||
channels.set_dac(channel, duty);
|
||||
let voltage = Volts(duty);
|
||||
channels.set_dac(channel, voltage);
|
||||
let _ = writeln!(
|
||||
socket, "channel {}: PWM duty cycle manually set to {}/{}",
|
||||
channel, duty, ad5680::MAX_VALUE
|
||||
socket, "channel {}: PWM duty cycle manually set to {}",
|
||||
channel, voltage
|
||||
);
|
||||
}
|
||||
Command::Pwm { pin: PwmPin::ISet, duty, .. } if duty > ad5680::MAX_VALUE => {
|
||||
let _ = writeln!(
|
||||
socket, "error: PWM duty range must not exceed {}",
|
||||
ad5680::MAX_VALUE
|
||||
);
|
||||
}
|
||||
Command::Pwm { channel, pin, duty } if duty <= 0xFFFF => {
|
||||
Command::Pwm { channel, pin, duty } => {
|
||||
let duty = duty as u16;
|
||||
|
||||
fn set_pwm_channel<P: hal::PwmPin<Duty=u16>>(pin: &mut P, duty: u16) -> u16 {
|
||||
|
@ -303,9 +298,6 @@ fn main() -> ! {
|
|||
channel, pin.name(), duty, max
|
||||
);
|
||||
}
|
||||
Command::Pwm { duty, .. } if duty > 0xFFFF => {
|
||||
let _ = writeln!(socket, "error: PWM duty range must fit 16 bits");
|
||||
}
|
||||
Command::Pid { channel, parameter, value } => {
|
||||
let pid = &mut channels.channel_state(channel).pid;
|
||||
use command_parser::PidParameter::*;
|
||||
|
@ -357,9 +349,6 @@ fn main() -> ! {
|
|||
}
|
||||
}
|
||||
}
|
||||
cmd => {
|
||||
let _ = writeln!(socket, "Not yet implemented: {:?}", cmd);
|
||||
}
|
||||
}
|
||||
Ok(SessionOutput::Error(e)) => {
|
||||
let _ = writeln!(socket, "Command error: {:?}", e);
|
||||
|
|
|
@ -16,7 +16,7 @@ impl Default for Parameters {
|
|||
ki: 0.05,
|
||||
kd: 0.45,
|
||||
output_min: 0.0,
|
||||
output_max: 1.0,
|
||||
output_max: 5.0,
|
||||
integral_min: 0.0,
|
||||
integral_max: 1.0,
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ use core::{
|
|||
ops::Div,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, PartialOrd)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
|
||||
pub struct Volts(pub f64);
|
||||
|
||||
impl fmt::Display for Volts {
|
||||
|
@ -19,7 +19,7 @@ impl Div<Ohms> for Volts {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, PartialOrd)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
|
||||
pub struct Amps(pub f64);
|
||||
|
||||
impl fmt::Display for Amps {
|
||||
|
@ -27,7 +27,7 @@ impl fmt::Display for Amps {
|
|||
write!(f, "{:.3}A", self.0)
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Clone, PartialEq, PartialOrd)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
|
||||
pub struct Ohms(pub f64);
|
||||
|
||||
impl fmt::Display for Ohms {
|
||||
|
|
Loading…
Reference in New Issue