Compare commits
6 Commits
feae22b277
...
34c2da4484
Author | SHA1 | Date |
---|---|---|
Astro | 34c2da4484 | |
Astro | ba860e52ac | |
Astro | 38a220ce4e | |
Astro | fb5c7a84e9 | |
Astro | 7f8dd62a36 | |
Astro | a317eb60fd |
|
@ -6,6 +6,7 @@ use stm32f4xx_hal::{
|
||||||
time::MegaHertz,
|
time::MegaHertz,
|
||||||
spi,
|
spi,
|
||||||
};
|
};
|
||||||
|
use crate::units::Volts;
|
||||||
|
|
||||||
/// SPI Mode 1
|
/// SPI Mode 1
|
||||||
pub const SPI_MODE: spi::Mode = spi::Mode {
|
pub const SPI_MODE: spi::Mode = spi::Mode {
|
||||||
|
@ -44,8 +45,9 @@ impl<SPI: Transfer<u8>, S: OutputPin> Dac<SPI, S> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// value: `0..0x20_000`
|
pub fn set(&mut self, voltage: Volts) -> Result<(), SPI::Error> {
|
||||||
pub fn set(&mut self, value: u32) -> Result<(), SPI::Error> {
|
let value = ((voltage.0 * (MAX_VALUE as f64) / 5.0) as u32)
|
||||||
|
.min(MAX_VALUE);
|
||||||
let buf = [
|
let buf = [
|
||||||
(value >> 14) as u8,
|
(value >> 14) as u8,
|
||||||
(value >> 6) as u8,
|
(value >> 6) as u8,
|
||||||
|
|
|
@ -43,7 +43,7 @@ impl<SPI: Transfer<u8, Error = E>, NSS: OutputPin, E: fmt::Debug> Adc<SPI, NSS>
|
||||||
|
|
||||||
let mut adc_mode = <regs::AdcMode as Register>::Data::empty();
|
let mut adc_mode = <regs::AdcMode as Register>::Data::empty();
|
||||||
adc_mode.set_ref_en(true);
|
adc_mode.set_ref_en(true);
|
||||||
adc_mode.set_mode(Mode::ContinuousConversion);
|
adc_mode.set_mode(Mode::Standby);
|
||||||
adc.write_reg(®s::AdcMode, &mut adc_mode)?;
|
adc.write_reg(®s::AdcMode, &mut adc_mode)?;
|
||||||
|
|
||||||
Ok(adc)
|
Ok(adc)
|
||||||
|
@ -96,16 +96,52 @@ impl<SPI: Transfer<u8, Error = E>, NSS: OutputPin, E: fmt::Debug> Adc<SPI, NSS>
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn disable_channel(
|
||||||
|
&mut self, index: u8
|
||||||
|
) -> Result<(), SPI::Error> {
|
||||||
|
self.update_reg(®s::Channel { index }, |data| {
|
||||||
|
data.set_enabled(false);
|
||||||
|
})?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn disable_all_channels(&mut self) -> Result<(), SPI::Error> {
|
||||||
|
for index in 0..4 {
|
||||||
|
self.update_reg(®s::Channel { index }, |data| {
|
||||||
|
data.set_enabled(false);
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Calibrates offset registers
|
/// Calibrates offset registers
|
||||||
pub fn calibrate_offset(&mut self) -> Result<(), SPI::Error> {
|
pub fn calibrate(&mut self) -> Result<(), SPI::Error> {
|
||||||
|
// internal offset calibration
|
||||||
|
self.update_reg(®s::AdcMode, |adc_mode| {
|
||||||
|
adc_mode.set_mode(Mode::InternalOffsetCalibration);
|
||||||
|
})?;
|
||||||
|
while ! self.read_reg(®s::Status)?.ready() {}
|
||||||
|
|
||||||
|
// system offset calibration
|
||||||
self.update_reg(®s::AdcMode, |adc_mode| {
|
self.update_reg(®s::AdcMode, |adc_mode| {
|
||||||
adc_mode.set_mode(Mode::SystemOffsetCalibration);
|
adc_mode.set_mode(Mode::SystemOffsetCalibration);
|
||||||
})?;
|
})?;
|
||||||
while ! self.read_reg(®s::Status)?.ready() {}
|
while ! self.read_reg(®s::Status)?.ready() {}
|
||||||
|
|
||||||
|
// system gain calibration
|
||||||
self.update_reg(®s::AdcMode, |adc_mode| {
|
self.update_reg(®s::AdcMode, |adc_mode| {
|
||||||
adc_mode.set_mode(Mode::ContinuousConversion);
|
adc_mode.set_mode(Mode::SystemGainCalibration);
|
||||||
})?;
|
})?;
|
||||||
|
while ! self.read_reg(®s::Status)?.ready() {}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn start_continuous_conversion(&mut self) -> Result<(), SPI::Error> {
|
||||||
|
let mut adc_mode = <regs::AdcMode as Register>::Data::empty();
|
||||||
|
adc_mode.set_ref_en(true);
|
||||||
|
adc_mode.set_mode(Mode::ContinuousConversion);
|
||||||
|
self.write_reg(®s::AdcMode, &mut adc_mode)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
use crate::pins::{ChannelPins, ChannelPinSet};
|
use crate::{
|
||||||
use crate::channel_state::ChannelState;
|
ad5680,
|
||||||
use crate::ad5680;
|
channel_state::ChannelState,
|
||||||
|
pins::{ChannelPins, ChannelPinSet},
|
||||||
|
units::Volts,
|
||||||
|
};
|
||||||
|
|
||||||
/// Marker type for the first channel
|
/// Marker type for the first channel
|
||||||
pub struct Channel0;
|
pub struct Channel0;
|
||||||
|
@ -11,24 +14,29 @@ pub struct Channel1;
|
||||||
|
|
||||||
pub struct Channel<C: ChannelPins> {
|
pub struct Channel<C: ChannelPins> {
|
||||||
pub state: ChannelState,
|
pub state: ChannelState,
|
||||||
|
/// for `i_set`
|
||||||
pub dac: ad5680::Dac<C::DacSpi, C::DacSync>,
|
pub dac: ad5680::Dac<C::DacSpi, C::DacSync>,
|
||||||
pub shdn: C::Shdn,
|
pub shdn: C::Shdn,
|
||||||
pub ref_adc: C::RefAdc,
|
/// stm32f4 integrated adc
|
||||||
pub ref_pin: C::RefPin,
|
pub adc: C::Adc,
|
||||||
|
pub itec_pin: C::ItecPin,
|
||||||
|
/// feedback from `dac` output
|
||||||
|
pub dac_loopback_pin: C::DacLoopbackPin,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C: ChannelPins> Channel<C> {
|
impl<C: ChannelPins> Channel<C> {
|
||||||
pub fn new(pins: ChannelPinSet<C>) -> Self {
|
pub fn new(pins: ChannelPinSet<C>) -> Self {
|
||||||
let state = ChannelState::default();
|
let state = ChannelState::default();
|
||||||
let mut dac = ad5680::Dac::new(pins.dac_spi, pins.dac_sync);
|
let mut dac = ad5680::Dac::new(pins.dac_spi, pins.dac_sync);
|
||||||
let _ = dac.set(0);
|
let _ = dac.set(Volts(0.0));
|
||||||
|
|
||||||
Channel {
|
Channel {
|
||||||
state,
|
state,
|
||||||
dac,
|
dac,
|
||||||
shdn: pins.shdn,
|
shdn: pins.shdn,
|
||||||
ref_adc: pins.ref_adc,
|
adc: pins.adc,
|
||||||
ref_pin: pins.ref_pin,
|
itec_pin: pins.itec_pin,
|
||||||
|
dac_loopback_pin: pins.dac_loopback_pin,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,16 @@
|
||||||
use smoltcp::time::Instant;
|
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 struct ChannelState {
|
||||||
pub adc_data: Option<u32>,
|
pub adc_data: Option<u32>,
|
||||||
pub adc_time: Instant,
|
pub adc_time: Instant,
|
||||||
pub dac_value: u32,
|
pub dac_value: Volts,
|
||||||
pub pid_engaged: bool,
|
pub pid_engaged: bool,
|
||||||
pub pid: pid::Controller,
|
pub pid: pid::Controller,
|
||||||
pub sh: sh::Parameters,
|
pub sh: sh::Parameters,
|
||||||
|
@ -16,7 +21,7 @@ impl Default for ChannelState {
|
||||||
ChannelState {
|
ChannelState {
|
||||||
adc_data: None,
|
adc_data: None,
|
||||||
adc_time: Instant::from_secs(0),
|
adc_time: Instant::from_secs(0),
|
||||||
dac_value: 0,
|
dac_value: Volts(0.0),
|
||||||
pid_engaged: false,
|
pid_engaged: false,
|
||||||
pid: pid::Controller::new(pid::Parameters::default()),
|
pid: pid::Controller::new(pid::Parameters::default()),
|
||||||
sh: sh::Parameters::default(),
|
sh: sh::Parameters::default(),
|
||||||
|
@ -26,14 +31,13 @@ impl Default for ChannelState {
|
||||||
|
|
||||||
impl ChannelState {
|
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, 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_data = Some(adc_data);
|
||||||
self.adc_time = now;
|
self.adc_time = now;
|
||||||
|
|
||||||
// Update PID controller
|
// Update PID controller
|
||||||
let input = (adc_data as f64) / (ad7172::MAX_VALUE as f64);
|
let input = (adc_data as f64) / (ad7172::MAX_VALUE as f64);
|
||||||
let temperature = self.sh.get_temperature(input);
|
let temperature = self.sh.get_temperature(input);
|
||||||
let output = self.pid.update(temperature);
|
self.pid.update(temperature)
|
||||||
(output * (ad5680::MAX_VALUE as f64)) as u32
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ use crate::{
|
||||||
channel::{Channel, Channel0, Channel1},
|
channel::{Channel, Channel0, Channel1},
|
||||||
channel_state::ChannelState,
|
channel_state::ChannelState,
|
||||||
pins,
|
pins,
|
||||||
|
units::Volts,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const CHANNELS: usize = 2;
|
pub const CHANNELS: usize = 2;
|
||||||
|
@ -25,10 +26,22 @@ impl Channels {
|
||||||
let mut adc = ad7172::Adc::new(pins.adc_spi, pins.adc_nss).unwrap();
|
let mut adc = ad7172::Adc::new(pins.adc_spi, pins.adc_nss).unwrap();
|
||||||
// Feature not used
|
// Feature not used
|
||||||
adc.set_sync_enable(false).unwrap();
|
adc.set_sync_enable(false).unwrap();
|
||||||
// Setup channels
|
|
||||||
|
// Calibrate ADC channels individually
|
||||||
|
adc.disable_all_channels().unwrap();
|
||||||
|
|
||||||
|
adc.setup_channel(0, ad7172::Input::Ain0, ad7172::Input::Ain1).unwrap();
|
||||||
|
adc.calibrate().unwrap();
|
||||||
|
adc.disable_channel(0).unwrap();
|
||||||
|
|
||||||
|
adc.setup_channel(1, ad7172::Input::Ain2, ad7172::Input::Ain3).unwrap();
|
||||||
|
adc.calibrate().unwrap();
|
||||||
|
adc.disable_channel(1).unwrap();
|
||||||
|
|
||||||
|
// Setup channels and start ADC
|
||||||
adc.setup_channel(0, ad7172::Input::Ain0, ad7172::Input::Ain1).unwrap();
|
adc.setup_channel(0, ad7172::Input::Ain0, ad7172::Input::Ain1).unwrap();
|
||||||
adc.setup_channel(1, ad7172::Input::Ain2, ad7172::Input::Ain3).unwrap();
|
adc.setup_channel(1, ad7172::Input::Ain2, ad7172::Input::Ain3).unwrap();
|
||||||
adc.calibrate_offset().unwrap();
|
adc.start_continuous_conversion().unwrap();
|
||||||
|
|
||||||
Channels { channel0, channel1, adc, pwm }
|
Channels { channel0, channel1, adc, pwm }
|
||||||
}
|
}
|
||||||
|
@ -58,7 +71,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
|
||||||
self.set_dac(channel.into(), dac_value);
|
self.set_dac(channel.into(), Volts(dac_value));
|
||||||
}
|
}
|
||||||
|
|
||||||
channel
|
channel
|
||||||
|
@ -66,32 +79,62 @@ impl Channels {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// i_set DAC
|
/// 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 {
|
match channel {
|
||||||
0 => {
|
0 => {
|
||||||
self.channel0.dac.set(duty).unwrap();
|
self.channel0.dac.set(voltage).unwrap();
|
||||||
self.channel0.state.dac_value = duty;
|
self.channel0.state.dac_value = voltage;
|
||||||
self.channel0.shdn.set_high().unwrap();
|
self.channel0.shdn.set_high().unwrap();
|
||||||
}
|
}
|
||||||
1 => {
|
1 => {
|
||||||
self.channel1.dac.set(duty).unwrap();
|
self.channel1.dac.set(voltage).unwrap();
|
||||||
self.channel1.state.dac_value = duty;
|
self.channel1.state.dac_value = voltage;
|
||||||
self.channel1.shdn.set_high().unwrap();
|
self.channel1.shdn.set_high().unwrap();
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_ref_adc(&mut self, channel: usize) -> u16 {
|
pub fn read_dac_loopback(&mut self, channel: usize) -> Volts {
|
||||||
match channel {
|
match channel {
|
||||||
0 => self.channel0.ref_adc.convert(
|
0 => {
|
||||||
&self.channel0.ref_pin,
|
let sample = self.channel0.adc.convert(
|
||||||
|
&self.channel0.dac_loopback_pin,
|
||||||
stm32f4xx_hal::adc::config::SampleTime::Cycles_480
|
stm32f4xx_hal::adc::config::SampleTime::Cycles_480
|
||||||
),
|
);
|
||||||
1 => self.channel1.ref_adc.convert(
|
let mv = self.channel0.adc.sample_to_millivolts(sample);
|
||||||
&self.channel1.ref_pin,
|
Volts(mv as f64 / 1000.0)
|
||||||
|
}
|
||||||
|
1 => {
|
||||||
|
let sample = self.channel1.adc.convert(
|
||||||
|
&self.channel1.dac_loopback_pin,
|
||||||
stm32f4xx_hal::adc::config::SampleTime::Cycles_480
|
stm32f4xx_hal::adc::config::SampleTime::Cycles_480
|
||||||
),
|
);
|
||||||
|
let mv = self.channel1.adc.sample_to_millivolts(sample);
|
||||||
|
Volts(mv as f64 / 1000.0)
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_itec(&mut self, channel: usize) -> Volts {
|
||||||
|
match channel {
|
||||||
|
0 => {
|
||||||
|
let sample = self.channel0.adc.convert(
|
||||||
|
&self.channel0.itec_pin,
|
||||||
|
stm32f4xx_hal::adc::config::SampleTime::Cycles_480
|
||||||
|
);
|
||||||
|
let mv = self.channel0.adc.sample_to_millivolts(sample);
|
||||||
|
Volts(mv as f64 / 1000.0)
|
||||||
|
}
|
||||||
|
1 => {
|
||||||
|
let sample = self.channel1.adc.convert(
|
||||||
|
&self.channel1.itec_pin,
|
||||||
|
stm32f4xx_hal::adc::config::SampleTime::Cycles_480
|
||||||
|
);
|
||||||
|
let mv = self.channel1.adc.sample_to_millivolts(sample);
|
||||||
|
Volts(mv as f64 / 1000.0)
|
||||||
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -142,7 +142,7 @@ pub enum Command {
|
||||||
Pwm {
|
Pwm {
|
||||||
channel: usize,
|
channel: usize,
|
||||||
pin: PwmPin,
|
pin: PwmPin,
|
||||||
duty: u32,
|
duty: f64,
|
||||||
},
|
},
|
||||||
/// Enable PID control for `i_set`
|
/// Enable PID control for `i_set`
|
||||||
PwmPid {
|
PwmPid {
|
||||||
|
@ -239,9 +239,9 @@ fn report(input: &[u8]) -> IResult<&[u8], Command> {
|
||||||
)(input)
|
)(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|
|
let result_with_pin = |pin: PwmPin|
|
||||||
move |result: Result<u32, Error>|
|
move |result: Result<f64, Error>|
|
||||||
result.map(|duty| (pin, duty));
|
result.map(|duty| (pin, duty));
|
||||||
|
|
||||||
alt((
|
alt((
|
||||||
|
@ -250,7 +250,7 @@ fn pwm_setup(input: &[u8]) -> IResult<&[u8], Result<(PwmPin, u32), Error>> {
|
||||||
tag("max_i_pos"),
|
tag("max_i_pos"),
|
||||||
preceded(
|
preceded(
|
||||||
whitespace,
|
whitespace,
|
||||||
unsigned
|
float
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
result_with_pin(PwmPin::MaxIPos)
|
result_with_pin(PwmPin::MaxIPos)
|
||||||
|
@ -260,7 +260,7 @@ fn pwm_setup(input: &[u8]) -> IResult<&[u8], Result<(PwmPin, u32), Error>> {
|
||||||
tag("max_i_neg"),
|
tag("max_i_neg"),
|
||||||
preceded(
|
preceded(
|
||||||
whitespace,
|
whitespace,
|
||||||
unsigned
|
float
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
result_with_pin(PwmPin::MaxINeg)
|
result_with_pin(PwmPin::MaxINeg)
|
||||||
|
@ -270,12 +270,12 @@ fn pwm_setup(input: &[u8]) -> IResult<&[u8], Result<(PwmPin, u32), Error>> {
|
||||||
tag("max_v"),
|
tag("max_v"),
|
||||||
preceded(
|
preceded(
|
||||||
whitespace,
|
whitespace,
|
||||||
unsigned
|
float
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
result_with_pin(PwmPin::MaxV)
|
result_with_pin(PwmPin::MaxV)
|
||||||
),
|
),
|
||||||
map(unsigned, result_with_pin(PwmPin::ISet)
|
map(float, result_with_pin(PwmPin::ISet)
|
||||||
))
|
))
|
||||||
)(input)
|
)(input)
|
||||||
}
|
}
|
||||||
|
|
39
src/main.rs
39
src/main.rs
|
@ -42,6 +42,8 @@ use session::{Session, SessionOutput};
|
||||||
mod command_parser;
|
mod command_parser;
|
||||||
use command_parser::{Command, ShowCommand, PwmPin};
|
use command_parser::{Command, ShowCommand, PwmPin};
|
||||||
mod timer;
|
mod timer;
|
||||||
|
mod units;
|
||||||
|
use units::{Amps, Ohms, Volts};
|
||||||
mod pid;
|
mod pid;
|
||||||
mod steinhart_hart;
|
mod steinhart_hart;
|
||||||
mod channels;
|
mod channels;
|
||||||
|
@ -141,12 +143,18 @@ 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_data) = channels.channel_state(channel).adc_data {
|
||||||
let ref_adc_data = channels.read_ref_adc(channel);
|
let dac_loopback = channels.read_dac_loopback(channel);
|
||||||
|
let dac_i = dac_loopback / Ohms(5.0);
|
||||||
|
|
||||||
|
let itec = channels.read_itec(channel);
|
||||||
|
let tec_i = Amps((itec.0 - 1.5) / 8.0);
|
||||||
|
|
||||||
let state = channels.channel_state(channel);
|
let state = channels.channel_state(channel);
|
||||||
let _ = writeln!(
|
let _ = writeln!(
|
||||||
socket, "t={} raw{}=0x{:06X} ref_adc=0x{:X}",
|
socket, "t={} raw{}=0x{:06X} dac_loopback={}/{} itec={} tec={}",
|
||||||
state.adc_time, channel, adc_data,
|
state.adc_time, channel, adc_data,
|
||||||
ref_adc_data
|
dac_loopback, dac_i,
|
||||||
|
itec, tec_i,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -186,7 +194,7 @@ 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, ad5680::MAX_VALUE);
|
let _ = writeln!(socket, "- i_set={}", state.dac_value);
|
||||||
fn show_pwm_channel<S, P>(mut socket: S, name: &str, pin: &P)
|
fn show_pwm_channel<S, P>(mut socket: S, name: &str, pin: &P)
|
||||||
where
|
where
|
||||||
S: core::fmt::Write,
|
S: core::fmt::Write,
|
||||||
|
@ -250,21 +258,16 @@ 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 } if duty <= ad5680::MAX_VALUE => {
|
Command::Pwm { channel, pin: PwmPin::ISet, duty } => {
|
||||||
channels.channel_state(channel).pid_engaged = false;
|
channels.channel_state(channel).pid_engaged = false;
|
||||||
channels.set_dac(channel, duty);
|
let voltage = Volts(duty);
|
||||||
|
channels.set_dac(channel, voltage);
|
||||||
let _ = writeln!(
|
let _ = writeln!(
|
||||||
socket, "channel {}: PWM duty cycle manually set to {}/{}",
|
socket, "channel {}: PWM duty cycle manually set to {}",
|
||||||
channel, duty, ad5680::MAX_VALUE
|
channel, voltage
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Command::Pwm { pin: PwmPin::ISet, duty, .. } if duty > ad5680::MAX_VALUE => {
|
Command::Pwm { channel, pin, duty } => {
|
||||||
let _ = writeln!(
|
|
||||||
socket, "error: PWM duty range must not exceed {}",
|
|
||||||
ad5680::MAX_VALUE
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Command::Pwm { channel, pin, duty } if duty <= 0xFFFF => {
|
|
||||||
let duty = duty as u16;
|
let duty = duty as u16;
|
||||||
|
|
||||||
fn set_pwm_channel<P: hal::PwmPin<Duty=u16>>(pin: &mut P, duty: u16) -> u16 {
|
fn set_pwm_channel<P: hal::PwmPin<Duty=u16>>(pin: &mut P, duty: u16) -> u16 {
|
||||||
|
@ -295,9 +298,6 @@ fn main() -> ! {
|
||||||
channel, pin.name(), duty, max
|
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 } => {
|
Command::Pid { channel, parameter, value } => {
|
||||||
let pid = &mut channels.channel_state(channel).pid;
|
let pid = &mut channels.channel_state(channel).pid;
|
||||||
use command_parser::PidParameter::*;
|
use command_parser::PidParameter::*;
|
||||||
|
@ -349,9 +349,6 @@ fn main() -> ! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cmd => {
|
|
||||||
let _ = writeln!(socket, "Not yet implemented: {:?}", cmd);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Ok(SessionOutput::Error(e)) => {
|
Ok(SessionOutput::Error(e)) => {
|
||||||
let _ = writeln!(socket, "Command error: {:?}", e);
|
let _ = writeln!(socket, "Command error: {:?}", e);
|
||||||
|
|
|
@ -16,7 +16,7 @@ impl Default for Parameters {
|
||||||
ki: 0.05,
|
ki: 0.05,
|
||||||
kd: 0.45,
|
kd: 0.45,
|
||||||
output_min: 0.0,
|
output_min: 0.0,
|
||||||
output_max: 1.0,
|
output_max: 5.0,
|
||||||
integral_min: 0.0,
|
integral_min: 0.0,
|
||||||
integral_max: 1.0,
|
integral_max: 1.0,
|
||||||
}
|
}
|
||||||
|
|
44
src/pins.rs
44
src/pins.rs
|
@ -26,24 +26,27 @@ pub trait ChannelPins {
|
||||||
type DacSpi: Transfer<u8>;
|
type DacSpi: Transfer<u8>;
|
||||||
type DacSync: OutputPin;
|
type DacSync: OutputPin;
|
||||||
type Shdn: OutputPin;
|
type Shdn: OutputPin;
|
||||||
type RefAdc;
|
type Adc;
|
||||||
type RefPin;
|
type ItecPin;
|
||||||
|
type DacLoopbackPin;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ChannelPins for Channel0 {
|
impl ChannelPins for Channel0 {
|
||||||
type DacSpi = Dac0Spi;
|
type DacSpi = Dac0Spi;
|
||||||
type DacSync = PE4<Output<PushPull>>;
|
type DacSync = PE4<Output<PushPull>>;
|
||||||
type Shdn = PE10<Output<PushPull>>;
|
type Shdn = PE10<Output<PushPull>>;
|
||||||
type RefAdc = Adc<ADC1>;
|
type Adc = Adc<ADC1>;
|
||||||
type RefPin = PA4<Analog>;
|
type ItecPin = PA6<Analog>;
|
||||||
|
type DacLoopbackPin = PA4<Analog>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ChannelPins for Channel1 {
|
impl ChannelPins for Channel1 {
|
||||||
type DacSpi = Dac1Spi;
|
type DacSpi = Dac1Spi;
|
||||||
type DacSync = PF6<Output<PushPull>>;
|
type DacSync = PF6<Output<PushPull>>;
|
||||||
type Shdn = PE15<Output<PushPull>>;
|
type Shdn = PE15<Output<PushPull>>;
|
||||||
type RefAdc = Adc<ADC2>;
|
type Adc = Adc<ADC2>;
|
||||||
type RefPin = PA5<Analog>;
|
type ItecPin = PB0<Analog>;
|
||||||
|
type DacLoopbackPin = PA5<Analog>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// SPI peripheral used for communication with the ADC
|
/// SPI peripheral used for communication with the ADC
|
||||||
|
@ -57,8 +60,9 @@ pub struct ChannelPinSet<C: ChannelPins> {
|
||||||
pub dac_spi: C::DacSpi,
|
pub dac_spi: C::DacSpi,
|
||||||
pub dac_sync: C::DacSync,
|
pub dac_sync: C::DacSync,
|
||||||
pub shdn: C::Shdn,
|
pub shdn: C::Shdn,
|
||||||
pub ref_adc: C::RefAdc,
|
pub adc: C::Adc,
|
||||||
pub ref_pin: C::RefPin,
|
pub itec_pin: C::ItecPin,
|
||||||
|
pub dac_loopback_pin: C::DacLoopbackPin,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Pins {
|
pub struct Pins {
|
||||||
|
@ -106,15 +110,17 @@ impl Pins {
|
||||||
);
|
);
|
||||||
let mut shdn0 = gpioe.pe10.into_push_pull_output();
|
let mut shdn0 = gpioe.pe10.into_push_pull_output();
|
||||||
let _ = shdn0.set_low();
|
let _ = shdn0.set_low();
|
||||||
let mut ref0_adc = Adc::adc1(adc1, true, Default::default());
|
let mut adc0 = Adc::adc1(adc1, true, Default::default());
|
||||||
ref0_adc.enable();
|
adc0.enable();
|
||||||
let ref0_pin = gpioa.pa4.into_analog();
|
let itec0_pin = gpioa.pa6.into_analog();
|
||||||
|
let dac_loopback0_pin = gpioa.pa4.into_analog();
|
||||||
let channel0 = ChannelPinSet {
|
let channel0 = ChannelPinSet {
|
||||||
dac_spi: dac0_spi,
|
dac_spi: dac0_spi,
|
||||||
dac_sync: dac0_sync,
|
dac_sync: dac0_sync,
|
||||||
shdn: shdn0,
|
shdn: shdn0,
|
||||||
ref_adc: ref0_adc,
|
adc: adc0,
|
||||||
ref_pin: ref0_pin,
|
itec_pin: itec0_pin,
|
||||||
|
dac_loopback_pin: dac_loopback0_pin,
|
||||||
};
|
};
|
||||||
|
|
||||||
let (dac1_spi, dac1_sync) = Self::setup_dac1(
|
let (dac1_spi, dac1_sync) = Self::setup_dac1(
|
||||||
|
@ -123,15 +129,17 @@ impl Pins {
|
||||||
);
|
);
|
||||||
let mut shdn1 = gpioe.pe15.into_push_pull_output();
|
let mut shdn1 = gpioe.pe15.into_push_pull_output();
|
||||||
let _ = shdn1.set_low();
|
let _ = shdn1.set_low();
|
||||||
let mut ref1_adc = Adc::adc2(adc2, true, Default::default());
|
let mut adc1 = Adc::adc2(adc2, true, Default::default());
|
||||||
ref1_adc.enable();
|
adc1.enable();
|
||||||
let ref1_pin = gpioa.pa5.into_analog();
|
let itec1_pin = gpiob.pb0.into_analog();
|
||||||
|
let dac_loopback1_pin = gpioa.pa5.into_analog();
|
||||||
let channel1 = ChannelPinSet {
|
let channel1 = ChannelPinSet {
|
||||||
dac_spi: dac1_spi,
|
dac_spi: dac1_spi,
|
||||||
dac_sync: dac1_sync,
|
dac_sync: dac1_sync,
|
||||||
shdn: shdn1,
|
shdn: shdn1,
|
||||||
ref_adc: ref1_adc,
|
adc: adc1,
|
||||||
ref_pin: ref1_pin,
|
itec_pin: itec1_pin,
|
||||||
|
dac_loopback_pin: dac_loopback1_pin,
|
||||||
};
|
};
|
||||||
|
|
||||||
Pins {
|
Pins {
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
use core::{
|
||||||
|
fmt,
|
||||||
|
ops::Div,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
|
||||||
|
pub struct Volts(pub f64);
|
||||||
|
|
||||||
|
impl fmt::Display for Volts {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{:.3}V", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Div<Ohms> for Volts {
|
||||||
|
type Output = Amps;
|
||||||
|
fn div(self, rhs: Ohms) -> Amps {
|
||||||
|
Amps(self.0 / rhs.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
|
||||||
|
pub struct Amps(pub f64);
|
||||||
|
|
||||||
|
impl fmt::Display for Amps {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{:.3}A", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
|
||||||
|
pub struct Ohms(pub f64);
|
||||||
|
|
||||||
|
impl fmt::Display for Ohms {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{:.3}Ω", self.0)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue