Compare commits

...

6 Commits

Author SHA1 Message Date
Astro 34c2da4484 ad7172: fix calibration 2020-05-17 02:11:53 +02:00
Astro ba860e52ac control i_set dac in volts 2020-05-17 01:27:47 +02:00
Astro 38a220ce4e fix tec_i calculation, add units::{Amps, Ohms} 2020-05-17 00:54:37 +02:00
Astro fb5c7a84e9 add units::Volts, use for stm32f4 adc 2020-05-17 00:13:52 +02:00
Astro 7f8dd62a36 add itec_pin 2020-05-16 23:59:31 +02:00
Astro a317eb60fd rename ref_adc to dac_loopback 2020-05-16 19:36:00 +02:00
10 changed files with 218 additions and 83 deletions

View File

@ -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,

View File

@ -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(&regs::AdcMode, &mut adc_mode)?; adc.write_reg(&regs::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(&regs::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(&regs::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(&regs::AdcMode, |adc_mode| {
adc_mode.set_mode(Mode::InternalOffsetCalibration);
})?;
while ! self.read_reg(&regs::Status)?.ready() {}
// system offset calibration
self.update_reg(&regs::AdcMode, |adc_mode| { self.update_reg(&regs::AdcMode, |adc_mode| {
adc_mode.set_mode(Mode::SystemOffsetCalibration); adc_mode.set_mode(Mode::SystemOffsetCalibration);
})?; })?;
while ! self.read_reg(&regs::Status)?.ready() {} while ! self.read_reg(&regs::Status)?.ready() {}
// system gain calibration
self.update_reg(&regs::AdcMode, |adc_mode| { self.update_reg(&regs::AdcMode, |adc_mode| {
adc_mode.set_mode(Mode::ContinuousConversion); adc_mode.set_mode(Mode::SystemGainCalibration);
})?; })?;
while ! self.read_reg(&regs::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(&regs::AdcMode, &mut adc_mode)?;
Ok(()) Ok(())
} }

View File

@ -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,
} }
} }
} }

View File

@ -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
} }
} }

View File

@ -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(
stm32f4xx_hal::adc::config::SampleTime::Cycles_480 &self.channel0.dac_loopback_pin,
), stm32f4xx_hal::adc::config::SampleTime::Cycles_480
1 => self.channel1.ref_adc.convert( );
&self.channel1.ref_pin, let mv = self.channel0.adc.sample_to_millivolts(sample);
stm32f4xx_hal::adc::config::SampleTime::Cycles_480 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
);
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!(),
} }
} }

View File

@ -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)
} }

View File

@ -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);

View File

@ -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,
} }

View File

@ -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 {

37
src/units.rs Normal file
View File

@ -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)
}
}