Compare commits

..

No commits in common. "3a8e0bddeca069770ed0342c7f5053021a62b53d" and "ae3d8b51d4e79eec77d862e6e6a8941221a966eb" have entirely different histories.

28 changed files with 1021 additions and 1191 deletions

View File

@ -45,7 +45,7 @@ There are several options for flashing Thermostat. DFU requires only a micro-USB
### dfu-util on Linux ### dfu-util on Linux
* Install the DFU USB tool (dfu-util). * Install the DFU USB tool (dfu-util).
* Convert firmware from ELF to BIN: `arm-none-eabi-objcopy -O binary target/thumbv7em-none-eabihf/release/thermostat thermostat.bin` (you can skip this step if using the BIN from Hydra) * Convert firmware from ELF to BIN: `arm-none-eabi-objcopy -O binary thermostat thermostat.bin` (you can skip this step if using the BIN from Hydra)
* Connect to the Micro USB connector to Thermostat below the RJ45. * Connect to the Micro USB connector to Thermostat below the RJ45.
* Add jumper to Thermostat v2.0 across 2-pin jumper adjacent to JTAG connector. * Add jumper to Thermostat v2.0 across 2-pin jumper adjacent to JTAG connector.
* Cycle board power to put it in DFU update mode * Cycle board power to put it in DFU update mode
@ -255,18 +255,21 @@ Use the bare `report` command to obtain a single report. Enable
continuous reporting with `report mode on`. Reports are JSON objects continuous reporting with `report mode on`. Reports are JSON objects
with the following keys. with the following keys.
| Key | Unit | Description | | Key | Unit | Description |
| --- | :---: | --- | | --- | :---: | --- |
| `channel` | Integer | Channel `0`, or `1` | | `channel` | Integer | Channel `0`, or `1` |
| `time` | Seconds | Report timestamp (since thermostat reset) | | `time` | Seconds | Temperature measurement time |
| `interval` | Seconds | ADC Sampling Interval | | `adc` | Volts | AD7172 input |
| `sens` | Ohms | Thermistor resistance | | `sens` | Ohms | Thermistor resistance derived from `adc` |
| `temperature` | Degrees Celsius | Temperature derived from thermistor resistance | | `temperature` | Degrees Celsius | Steinhart-Hart conversion result derived from `sens` |
| `pid_engaged` | Boolean | `true` if in closed-loop mode | | `pid_engaged` | Boolean | `true` if in closed-loop mode |
| `pid_output` | Amperes | PID control output | | `i_set` | Amperes | TEC output current |
| `i_set` | Amperes | Set TEC output current | | `dac_value` | Volts | AD5680 output derived from `i_set` |
| `i_measured` | Amperes | Measured current passing through TEC | | `dac_feedback` | Volts | ADC measurement of the AD5680 output |
| `v_measured` | Volts | Measured voltage across TEC | | `i_tec` | Volts | MAX1968 TEC current monitor |
| `tec_i` | Amperes | TEC output current feedback derived from `i_tec` |
| `tec_u_meas` | Volts | Measurement of the voltage across the TEC |
| `pid_output` | Amperes | PID control output |
Note: With Thermostat v2 and below, the voltage and current readouts `i_tec` and `tec_i` are disabled and null due to faulty hardware that introduces a lot of noise in the signal. Note: With Thermostat v2 and below, the voltage and current readouts `i_tec` and `tec_i` are disabled and null due to faulty hardware that introduces a lot of noise in the signal.
@ -277,10 +280,10 @@ The thermostat implements a PID control loop for each of the TEC channels, more
## Fan control ## Fan control
Fan control commands are available for thermostat revisions with an integrated fan system: Fan control commands are available for thermostat revisions with an integrated fan system:
1. `fan` - show fan stats: `fan_pwm`, `max_abs_i_measured`, `auto_mode`, `k_a`, `k_b`, `k_c`. 1. `fan` - show fan stats: `fan_pwm`, `abs_max_tec_i`, `auto_mode`, `k_a`, `k_b`, `k_c`.
2. `fan auto` - enable auto speed controller mode, where fan speed is controlled by the fan curve `fcurve`. 2. `fan auto` - enable auto speed controller mode, where fan speed is controlled by the fan curve `fcurve`.
3. `fan <value>` - set the fan power with the value from `1` to `100` and disable auto mode. There is no way to completely disable the fan. 3. `fan <value>` - set the fan power with the value from `1` to `100` and disable auto mode. There is no way to completely disable the fan.
Please note that power doesn't correlate with the actual speed linearly. Please note that power doesn't correlate with the actual speed linearly.
4. `fcurve <a> <b> <c>` - set coefficients of the controlling curve `a*x^2 + b*x + c`, where `x` is `max_abs_i_measured/MAX_I`, a normalized value in range [0,1], 4. `fcurve <a> <b> <c>` - set coefficients of the controlling curve `a*x^2 + b*x + c`, where `x` is `abs_max_tec_i/MAX_TEC_I`, a normalized value in range [0,1],
i.e. the (linear) proportion of current output capacity used, on the channel with the largest current flow. The controlling curve is also clamped to [0,1]. i.e. the (linear) proportion of current output capacity used, on the channel with the largest current flow. The controlling curve is also clamped to [0,1].
5. `fcurve default` - restore fan curve coefficients to defaults: `a = 1.0, b = 0.0, c = 0.0`. 5. `fcurve default` - restore fan curve coefficients to defaults: `a = 1.0, b = 0.0, c = 0.0`.

View File

@ -67,8 +67,7 @@
devShell.x86_64-linux = pkgs.mkShell { devShell.x86_64-linux = pkgs.mkShell {
name = "thermostat-dev-shell"; name = "thermostat-dev-shell";
buildInputs = with pkgs; [ buildInputs = with pkgs; [
rust llvm rust openocd dfu-util
openocd dfu-util
] ++ (with python3Packages; [ ] ++ (with python3Packages; [
numpy matplotlib numpy matplotlib
]); ]);

View File

@ -1,9 +1,12 @@
use crate::timer::sleep;
use stm32f4xx_hal::{ use stm32f4xx_hal::{
hal::{blocking::spi::Transfer, digital::v2::OutputPin}, hal::{
spi, blocking::spi::Transfer,
digital::v2::OutputPin,
},
time::MegaHertz, time::MegaHertz,
spi,
}; };
use crate::timer::sleep;
/// SPI Mode 1 /// SPI Mode 1
pub const SPI_MODE: spi::Mode = spi::Mode { pub const SPI_MODE: spi::Mode = spi::Mode {
@ -24,8 +27,11 @@ pub struct Dac<SPI: Transfer<u8>, S: OutputPin> {
impl<SPI: Transfer<u8>, S: OutputPin> Dac<SPI, S> { impl<SPI: Transfer<u8>, S: OutputPin> Dac<SPI, S> {
pub fn new(spi: SPI, mut sync: S) -> Self { pub fn new(spi: SPI, mut sync: S) -> Self {
let _ = sync.set_low(); let _ = sync.set_low();
Dac { spi, sync } Dac {
spi,
sync,
}
} }
fn write(&mut self, buf: &mut [u8]) -> Result<(), SPI::Error> { fn write(&mut self, buf: &mut [u8]) -> Result<(), SPI::Error> {
@ -41,7 +47,11 @@ impl<SPI: Transfer<u8>, S: OutputPin> Dac<SPI, S> {
pub fn set(&mut self, value: u32) -> Result<u32, SPI::Error> { pub fn set(&mut self, value: u32) -> Result<u32, SPI::Error> {
let value = value.min(MAX_VALUE); let value = value.min(MAX_VALUE);
let mut buf = [(value >> 14) as u8, (value >> 6) as u8, (value << 2) as u8]; 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) Ok(value)
} }

View File

@ -1,12 +1,18 @@
use super::{
checksum::{Checksum, ChecksumMode},
regs::{self, Register, RegisterData},
DigitalFilterOrder, Input, Mode, PostFilter, RefSource,
};
use core::fmt; use core::fmt;
use log::{info, warn}; use log::{info, warn};
use stm32f4xx_hal::hal::{blocking::spi::Transfer, digital::v2::OutputPin}; use stm32f4xx_hal::hal::{
use uom::si::{electric_potential::volt, f64::ElectricPotential}; blocking::spi::Transfer,
digital::v2::OutputPin,
};
use uom::si::{
f64::ElectricPotential,
electric_potential::volt,
};
use super::{
regs::{self, Register, RegisterData},
checksum::{ChecksumMode, Checksum},
Mode, Input, RefSource, PostFilter, DigitalFilterOrder,
};
/// AD7172-2 implementation /// AD7172-2 implementation
/// ///
@ -21,8 +27,7 @@ impl<SPI: Transfer<u8, Error = E>, NSS: OutputPin, E: fmt::Debug> Adc<SPI, NSS>
pub fn new(spi: SPI, mut nss: NSS) -> Result<Self, SPI::Error> { pub fn new(spi: SPI, mut nss: NSS) -> Result<Self, SPI::Error> {
let _ = nss.set_high(); let _ = nss.set_high();
let mut adc = Adc { let mut adc = Adc {
spi, spi, nss,
nss,
checksum_mode: ChecksumMode::Off, checksum_mode: ChecksumMode::Off,
}; };
adc.reset()?; adc.reset()?;
@ -50,7 +55,8 @@ impl<SPI: Transfer<u8, Error = E>, NSS: OutputPin, E: fmt::Debug> Adc<SPI, NSS>
/// `0x00DX` for AD7172-2 /// `0x00DX` for AD7172-2
pub fn identify(&mut self) -> Result<u16, SPI::Error> { pub fn identify(&mut self) -> Result<u16, SPI::Error> {
self.read_reg(&regs::Id).map(|id| id.id()) self.read_reg(&regs::Id)
.map(|id| id.id())
} }
pub fn set_checksum_mode(&mut self, mode: ChecksumMode) -> Result<(), SPI::Error> { pub fn set_checksum_mode(&mut self, mode: ChecksumMode) -> Result<(), SPI::Error> {
@ -70,10 +76,7 @@ impl<SPI: Transfer<u8, Error = E>, NSS: OutputPin, E: fmt::Debug> Adc<SPI, NSS>
} }
pub fn setup_channel( pub fn setup_channel(
&mut self, &mut self, index: u8, in_pos: Input, in_neg: Input
index: u8,
in_pos: Input,
in_neg: Input,
) -> Result<(), SPI::Error> { ) -> Result<(), SPI::Error> {
self.update_reg(&regs::SetupCon { index }, |data| { self.update_reg(&regs::SetupCon { index }, |data| {
data.set_bipolar(false); data.set_bipolar(false);
@ -103,11 +106,7 @@ impl<SPI: Transfer<u8, Error = E>, NSS: OutputPin, E: fmt::Debug> Adc<SPI, NSS>
let offset = self.read_reg(&regs::Offset { index })?.offset(); let offset = self.read_reg(&regs::Offset { index })?.offset();
let gain = self.read_reg(&regs::Gain { index })?.gain(); let gain = self.read_reg(&regs::Gain { index })?.gain();
let bipolar = self.read_reg(&regs::SetupCon { index })?.bipolar(); let bipolar = self.read_reg(&regs::SetupCon { index })?.bipolar();
Ok(ChannelCalibration { Ok(ChannelCalibration { offset, gain, bipolar })
offset,
gain,
bipolar,
})
} }
pub fn start_continuous_conversion(&mut self) -> Result<(), SPI::Error> { pub fn start_continuous_conversion(&mut self) -> Result<(), SPI::Error> {
@ -120,43 +119,44 @@ impl<SPI: Transfer<u8, Error = E>, NSS: OutputPin, E: fmt::Debug> Adc<SPI, NSS>
} }
pub fn get_postfilter(&mut self, index: u8) -> Result<Option<PostFilter>, SPI::Error> { pub fn get_postfilter(&mut self, index: u8) -> Result<Option<PostFilter>, SPI::Error> {
self.read_reg(&regs::FiltCon { index }).map(|data| { self.read_reg(&regs::FiltCon { index })
if data.enh_filt_en() { .map(|data| {
Some(data.enh_filt()) if data.enh_filt_en() {
} else { Some(data.enh_filt())
None } else {
} None
}) }
})
} }
pub fn set_postfilter( pub fn set_postfilter(&mut self, index: u8, filter: Option<PostFilter>) -> Result<(), SPI::Error> {
&mut self, self.update_reg(&regs::FiltCon { index }, |data| {
index: u8, match filter {
filter: Option<PostFilter>, None => data.set_enh_filt_en(false),
) -> Result<(), SPI::Error> { Some(filter) => {
self.update_reg(&regs::FiltCon { index }, |data| match filter { data.set_enh_filt_en(true);
None => data.set_enh_filt_en(false), data.set_enh_filt(filter);
Some(filter) => { }
data.set_enh_filt_en(true);
data.set_enh_filt(filter);
} }
}) })
} }
/// Returns the channel the data is from /// Returns the channel the data is from
pub fn data_ready(&mut self) -> Result<Option<u8>, SPI::Error> { pub fn data_ready(&mut self) -> Result<Option<u8>, SPI::Error> {
self.read_reg(&regs::Status).map(|status| { self.read_reg(&regs::Status)
if status.ready() { .map(|status| {
Some(status.channel()) if status.ready() {
} else { Some(status.channel())
None } else {
} None
}) }
})
} }
/// Get data /// Get data
pub fn read_data(&mut self) -> Result<u32, SPI::Error> { pub fn read_data(&mut self) -> Result<u32, SPI::Error> {
self.read_reg(&regs::Data).map(|data| data.data()) self.read_reg(&regs::Data)
.map(|data| data.data())
} }
fn read_reg<R: regs::Register>(&mut self, reg: &R) -> Result<R::Data, SPI::Error> { fn read_reg<R: regs::Register>(&mut self, reg: &R) -> Result<R::Data, SPI::Error> {
@ -175,21 +175,12 @@ impl<SPI: Transfer<u8, Error = E>, NSS: OutputPin, E: fmt::Debug> Adc<SPI, NSS>
break; break;
} }
// Retry // Retry
warn!( warn!("read_reg {:02X}: checksum error: {:?}!={:?}, retrying", reg.address(), checksum_expected, checksum_in);
"read_reg {:02X}: checksum error: {:?}!={:?}, retrying",
reg.address(),
checksum_expected,
checksum_in
);
} }
Ok(reg_data) Ok(reg_data)
} }
fn write_reg<R: regs::Register>( fn write_reg<R: regs::Register>(&mut self, reg: &R, reg_data: &mut R::Data) -> Result<(), SPI::Error> {
&mut self,
reg: &R,
reg_data: &mut R::Data,
) -> Result<(), SPI::Error> {
loop { loop {
let address = reg.address(); let address = reg.address();
let mut checksum = Checksum::new(match self.checksum_mode { let mut checksum = Checksum::new(match self.checksum_mode {
@ -199,7 +190,7 @@ impl<SPI: Transfer<u8, Error = E>, NSS: OutputPin, E: fmt::Debug> Adc<SPI, NSS>
ChecksumMode::Crc => ChecksumMode::Crc, ChecksumMode::Crc => ChecksumMode::Crc,
}); });
checksum.feed(&[address]); checksum.feed(&[address]);
checksum.feed(reg_data); checksum.feed(&reg_data);
let checksum_out = checksum.result(); let checksum_out = checksum.result();
let mut data = reg_data.clone(); let mut data = reg_data.clone();
@ -210,10 +201,7 @@ impl<SPI: Transfer<u8, Error = E>, NSS: OutputPin, E: fmt::Debug> Adc<SPI, NSS>
if *readback_data == **reg_data { if *readback_data == **reg_data {
return Ok(()); return Ok(());
} }
warn!( warn!("write_reg {:02X}: readback error, {:?}!={:?}, retrying", address, &*readback_data, &**reg_data);
"write_reg {:02X}: readback error, {:?}!={:?}, retrying",
address, &*readback_data, &**reg_data
);
} }
} }
@ -237,12 +225,7 @@ impl<SPI: Transfer<u8, Error = E>, NSS: OutputPin, E: fmt::Debug> Adc<SPI, NSS>
Ok(()) Ok(())
} }
fn transfer<'w>( fn transfer<'w>(&mut self, addr: u8, reg_data: &'w mut [u8], checksum: Option<u8>) -> Result<Option<u8>, SPI::Error> {
&mut self,
addr: u8,
reg_data: &'w mut [u8],
checksum: Option<u8>,
) -> Result<Option<u8>, SPI::Error> {
let mut addr_buf = [addr]; let mut addr_buf = [addr];
let _ = self.nss.set_low(); let _ = self.nss.set_low();
@ -251,7 +234,8 @@ impl<SPI: Transfer<u8, Error = E>, NSS: OutputPin, E: fmt::Debug> Adc<SPI, NSS>
Err(e) => Err(e), Err(e) => Err(e),
}; };
let result = match (result, checksum) { let result = match (result, checksum) {
(Ok(_), None) => Ok(None), (Ok(_), None) =>
Ok(None),
(Ok(_), Some(checksum_out)) => { (Ok(_), Some(checksum_out)) => {
let mut checksum_buf = [checksum_out; 1]; let mut checksum_buf = [checksum_out; 1];
match self.spi.transfer(&mut checksum_buf) { match self.spi.transfer(&mut checksum_buf) {
@ -259,7 +243,8 @@ impl<SPI: Transfer<u8, Error = E>, NSS: OutputPin, E: fmt::Debug> Adc<SPI, NSS>
Err(e) => Err(e), Err(e) => Err(e),
} }
} }
(Err(e), _) => Err(e), (Err(e), _) =>
Err(e),
}; };
let _ = self.nss.set_high(); let _ = self.nss.set_high();

View File

@ -29,13 +29,13 @@ impl Checksum {
fn feed_byte(&mut self, input: u8) { fn feed_byte(&mut self, input: u8) {
match self.mode { match self.mode {
ChecksumMode::Off => {} ChecksumMode::Off => {},
ChecksumMode::Xor => self.state ^= input, ChecksumMode::Xor => self.state ^= input,
ChecksumMode::Crc => { ChecksumMode::Crc => {
for i in 0..8 { for i in 0..8 {
let input_mask = 0x80 >> i; let input_mask = 0x80 >> i;
self.state = (self.state << 1) self.state = (self.state << 1) ^
^ if ((self.state & 0x80) != 0) != ((input & input_mask) != 0) { if ((self.state & 0x80) != 0) != ((input & input_mask) != 0) {
0x07 /* x8 + x2 + x + 1 */ 0x07 /* x8 + x2 + x + 1 */
} else { } else {
0 0
@ -54,7 +54,7 @@ impl Checksum {
pub fn result(&self) -> Option<u8> { pub fn result(&self) -> Option<u8> {
match self.mode { match self.mode {
ChecksumMode::Off => None, ChecksumMode::Off => None,
_ => Some(self.state), _ => Some(self.state)
} }
} }
} }

View File

@ -1,10 +1,13 @@
use core::fmt; use core::fmt;
use num_traits::float::Float; use num_traits::float::Float;
use serde::{Deserialize, Serialize}; use serde::{Serialize, Deserialize};
use stm32f4xx_hal::{spi, time::MegaHertz}; use stm32f4xx_hal::{
time::MegaHertz,
spi,
};
mod checksum;
pub mod regs; pub mod regs;
mod checksum;
pub use checksum::ChecksumMode; pub use checksum::ChecksumMode;
mod adc; mod adc;
pub use adc::*; pub use adc::*;
@ -19,6 +22,7 @@ pub const SPI_CLOCK: MegaHertz = MegaHertz(2);
pub const MAX_VALUE: u32 = 0xFF_FFFF; pub const MAX_VALUE: u32 = 0xFF_FFFF;
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
#[repr(u8)] #[repr(u8)]
pub enum Mode { pub enum Mode {
@ -101,8 +105,7 @@ impl fmt::Display for Input {
RefPos => "ref+", RefPos => "ref+",
RefNeg => "ref-", RefNeg => "ref-",
_ => "<INVALID>", _ => "<INVALID>",
} }.fmt(fmt)
.fmt(fmt)
} }
} }
@ -138,8 +141,7 @@ impl fmt::Display for RefSource {
Internal => "internal", Internal => "internal",
Avdd1MinusAvss => "avdd1-avss", Avdd1MinusAvss => "avdd1-avss",
_ => "<INVALID>", _ => "<INVALID>",
} }.fmt(fmt)
.fmt(fmt)
} }
} }

View File

@ -1,6 +1,6 @@
use bit_field::BitField;
use byteorder::{BigEndian, ByteOrder};
use core::ops::{Deref, DerefMut}; use core::ops::{Deref, DerefMut};
use byteorder::{BigEndian, ByteOrder};
use bit_field::BitField;
use super::*; use super::*;
@ -9,7 +9,7 @@ pub trait Register {
fn address(&self) -> u8; fn address(&self) -> u8;
} }
pub trait RegisterData: Clone + Deref<Target = [u8]> + DerefMut { pub trait RegisterData: Clone + Deref<Target=[u8]> + DerefMut {
fn empty() -> Self; fn empty() -> Self;
} }
@ -49,9 +49,7 @@ macro_rules! def_reg {
} }
}; };
($Reg: ident, u8, $reg: ident, $addr: expr, $size: expr) => { ($Reg: ident, u8, $reg: ident, $addr: expr, $size: expr) => {
pub struct $Reg { pub struct $Reg { pub index: u8, }
pub index: u8,
}
impl Register for $Reg { impl Register for $Reg {
type Data = $reg::Data; type Data = $reg::Data;
fn address(&self) -> u8 { fn address(&self) -> u8 {
@ -78,7 +76,7 @@ macro_rules! def_reg {
} }
} }
} }
}; }
} }
macro_rules! reg_bit { macro_rules! reg_bit {
@ -148,7 +146,7 @@ def_reg!(Status, status, 0x00, 1);
impl status::Data { impl status::Data {
/// Is there new data to read? /// Is there new data to read?
pub fn ready(&self) -> bool { pub fn ready(&self) -> bool {
!self.not_ready() ! self.not_ready()
} }
reg_bit!(not_ready, 0, 7, "No data ready indicator"); reg_bit!(not_ready, 0, 7, "No data ready indicator");
@ -161,21 +159,9 @@ impl status::Data {
def_reg!(AdcMode, adc_mode, 0x01, 2); def_reg!(AdcMode, adc_mode, 0x01, 2);
impl adc_mode::Data { impl adc_mode::Data {
reg_bits!(delay, set_delay, 0, 0..=2, "Delay after channel switch"); reg_bits!(delay, set_delay, 0, 0..=2, "Delay after channel switch");
reg_bit!( reg_bit!(sing_cyc, set_sing_cyc, 0, 5, "Can only used with single channel");
sing_cyc,
set_sing_cyc,
0,
5,
"Can only used with single channel"
);
reg_bit!(hide_delay, set_hide_delay, 0, 6, "Hide delay"); reg_bit!(hide_delay, set_hide_delay, 0, 6, "Hide delay");
reg_bit!( reg_bit!(ref_en, set_ref_en, 0, 7, "Enable internal reference, output buffered 2.5 V to REFOUT");
ref_en,
set_ref_en,
0,
7,
"Enable internal reference, output buffered 2.5 V to REFOUT"
);
reg_bits!(clockset, set_clocksel, 1, 2..=3, "Clock source"); reg_bits!(clockset, set_clocksel, 1, 2..=3, "Clock source");
reg_bits!(mode, set_mode, 1, 4..=6, Mode, "Operating mode"); reg_bits!(mode, set_mode, 1, 4..=6, Mode, "Operating mode");
} }
@ -188,19 +174,15 @@ impl if_mode::Data {
def_reg!(Data, data, 0x04, 3); def_reg!(Data, data, 0x04, 3);
impl data::Data { impl data::Data {
pub fn data(&self) -> u32 { pub fn data(&self) -> u32 {
(u32::from(self.0[0]) << 16) | (u32::from(self.0[1]) << 8) | u32::from(self.0[2]) (u32::from(self.0[0]) << 16) |
(u32::from(self.0[1]) << 8) |
u32::from(self.0[2])
} }
} }
def_reg!(GpioCon, gpio_con, 0x06, 2); def_reg!(GpioCon, gpio_con, 0x06, 2);
impl gpio_con::Data { impl gpio_con::Data {
reg_bit!( reg_bit!(sync_en, set_sync_en, 0, 3, "Enables the SYNC/ERROR pin as a sync input");
sync_en,
set_sync_en,
0,
3,
"Enables the SYNC/ERROR pin as a sync input"
);
} }
def_reg!(Id, id, 0x07, 2); def_reg!(Id, id, 0x07, 2);
@ -218,7 +200,8 @@ impl channel::Data {
/// Which input is connected to positive input of this channel /// Which input is connected to positive input of this channel
#[allow(unused)] #[allow(unused)]
pub fn a_in_pos(&self) -> Input { pub fn a_in_pos(&self) -> Input {
((self.0[0].get_bits(0..=1) << 3) | self.0[1].get_bits(5..=7)).into() ((self.0[0].get_bits(0..=1) << 3) |
self.0[1].get_bits(5..=7)).into()
} }
/// Set which input is connected to positive input of this channel /// Set which input is connected to positive input of this channel
#[allow(unused)] #[allow(unused)]
@ -227,66 +210,27 @@ impl channel::Data {
self.0[0].set_bits(0..=1, value >> 3); self.0[0].set_bits(0..=1, value >> 3);
self.0[1].set_bits(5..=7, value & 0x7); self.0[1].set_bits(5..=7, value & 0x7);
} }
reg_bits!( reg_bits!(a_in_neg, set_a_in_neg, 1, 0..=4, Input,
a_in_neg, "Which input is connected to negative input of this channel");
set_a_in_neg,
1,
0..=4,
Input,
"Which input is connected to negative input of this channel"
);
} }
def_reg!(SetupCon, u8, setup_con, 0x20, 2); def_reg!(SetupCon, u8, setup_con, 0x20, 2);
impl setup_con::Data { impl setup_con::Data {
reg_bit!( reg_bit!(bipolar, set_bipolar, 0, 4, "Unipolar (`false`) or bipolar (`true`) coded output");
bipolar,
set_bipolar,
0,
4,
"Unipolar (`false`) or bipolar (`true`) coded output"
);
reg_bit!(refbuf_pos, set_refbuf_pos, 0, 3, "Enable REF+ input buffer"); reg_bit!(refbuf_pos, set_refbuf_pos, 0, 3, "Enable REF+ input buffer");
reg_bit!(refbuf_neg, set_refbuf_neg, 0, 2, "Enable REF- input buffer"); reg_bit!(refbuf_neg, set_refbuf_neg, 0, 2, "Enable REF- input buffer");
reg_bit!(ainbuf_pos, set_ainbuf_pos, 0, 1, "Enable AIN+ input buffer"); reg_bit!(ainbuf_pos, set_ainbuf_pos, 0, 1, "Enable AIN+ input buffer");
reg_bit!(ainbuf_neg, set_ainbuf_neg, 0, 0, "Enable AIN- input buffer"); reg_bit!(ainbuf_neg, set_ainbuf_neg, 0, 0, "Enable AIN- input buffer");
reg_bit!(burnout_en, 1, 7, "enables a 10 µA current source on the positive analog input selected and a 10 µA current sink on the negative analog input selected"); reg_bit!(burnout_en, 1, 7, "enables a 10 µA current source on the positive analog input selected and a 10 µA current sink on the negative analog input selected");
reg_bits!( reg_bits!(ref_sel, set_ref_sel, 1, 4..=5, RefSource, "Select reference source for conversion");
ref_sel,
set_ref_sel,
1,
4..=5,
RefSource,
"Select reference source for conversion"
);
} }
def_reg!(FiltCon, u8, filt_con, 0x28, 2); def_reg!(FiltCon, u8, filt_con, 0x28, 2);
impl filt_con::Data { impl filt_con::Data {
reg_bit!(sinc3_map, 0, 7, "If set, mapping of filter register changes to directly program the decimation rate of the sinc3 filter"); reg_bit!(sinc3_map, 0, 7, "If set, mapping of filter register changes to directly program the decimation rate of the sinc3 filter");
reg_bit!( reg_bit!(enh_filt_en, set_enh_filt_en, 0, 3, "Enable postfilters for enhanced 50Hz and 60Hz rejection");
enh_filt_en, reg_bits!(enh_filt, set_enh_filt, 0, 0..=2, PostFilter, "Select postfilters for enhanced 50Hz and 60Hz rejection");
set_enh_filt_en, reg_bits!(order, set_order, 1, 5..=6, DigitalFilterOrder, "order of the digital filter that processes the modulator data");
0,
3,
"Enable postfilters for enhanced 50Hz and 60Hz rejection"
);
reg_bits!(
enh_filt,
set_enh_filt,
0,
0..=2,
PostFilter,
"Select postfilters for enhanced 50Hz and 60Hz rejection"
);
reg_bits!(
order,
set_order,
1,
5..=6,
DigitalFilterOrder,
"order of the digital filter that processes the modulator data"
);
reg_bits!(odr, set_odr, 1, 0..=4, "Output data rate"); reg_bits!(odr, set_odr, 1, 0..=4, "Output data rate");
} }
@ -294,7 +238,9 @@ def_reg!(Offset, u8, offset, 0x30, 3);
impl offset::Data { impl offset::Data {
#[allow(unused)] #[allow(unused)]
pub fn offset(&self) -> u32 { pub fn offset(&self) -> u32 {
(u32::from(self.0[0]) << 16) | (u32::from(self.0[1]) << 8) | u32::from(self.0[2]) (u32::from(self.0[0]) << 16) |
(u32::from(self.0[1]) << 8) |
u32::from(self.0[2])
} }
#[allow(unused)] #[allow(unused)]
pub fn set_offset(&mut self, value: u32) { pub fn set_offset(&mut self, value: u32) {
@ -308,7 +254,9 @@ def_reg!(Gain, u8, gain, 0x38, 3);
impl gain::Data { impl gain::Data {
#[allow(unused)] #[allow(unused)]
pub fn gain(&self) -> u32 { pub fn gain(&self) -> u32 {
(u32::from(self.0[0]) << 16) | (u32::from(self.0[1]) << 8) | u32::from(self.0[2]) (u32::from(self.0[0]) << 16) |
(u32::from(self.0[1]) << 8) |
u32::from(self.0[2])
} }
#[allow(unused)] #[allow(unused)]
pub fn set_gain(&mut self, value: u32) { pub fn set_gain(&mut self, value: u32) {

View File

@ -1,10 +1,14 @@
use crate::{
ad5680, ad7172,
channel_state::ChannelState,
pins::{ChannelPinSet, ChannelPins},
};
use stm32f4xx_hal::hal::digital::v2::OutputPin; use stm32f4xx_hal::hal::digital::v2::OutputPin;
use uom::si::{electric_potential::volt, f64::ElectricPotential}; use uom::si::{
f64::ElectricPotential,
electric_potential::volt,
};
use crate::{
ad5680,
ad7172,
channel_state::ChannelState,
pins::{ChannelPins, ChannelPinSet},
};
/// Marker type for the first channel /// Marker type for the first channel
pub struct Channel0; pub struct Channel0;
@ -36,8 +40,7 @@ impl<C: ChannelPins> Channel<C> {
Channel { Channel {
state, state,
dac, dac, vref_meas,
vref_meas,
shdn: pins.shdn, shdn: pins.shdn,
vref_pin: pins.vref_pin, vref_pin: pins.vref_pin,
itec_pin: pins.itec_pin, itec_pin: pins.itec_pin,

View File

@ -1,12 +1,22 @@
use crate::{ad7172, command_parser::CenterPoint, pid, steinhart_hart as sh};
use smoltcp::time::{Duration, Instant}; use smoltcp::time::{Duration, Instant};
use uom::si::{ use uom::si::{
f64::{
ElectricPotential,
ElectricalResistance,
ThermodynamicTemperature,
Time,
},
electric_potential::volt, electric_potential::volt,
electrical_resistance::ohm, electrical_resistance::ohm,
f64::{ElectricPotential, ElectricalResistance, ThermodynamicTemperature, Time},
thermodynamic_temperature::degree_celsius, thermodynamic_temperature::degree_celsius,
time::millisecond, time::millisecond,
}; };
use crate::{
ad7172,
pid,
steinhart_hart as sh,
command_parser::CenterPoint,
};
const R_INNER: f64 = 2.0 * 5100.0; const R_INNER: f64 = 2.0 * 5100.0;
const VREF_SENS: f64 = 3.3 / 2.0; const VREF_SENS: f64 = 3.3 / 2.0;
@ -53,7 +63,8 @@ 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) -> Option<f64> { pub fn update_pid(&mut self) -> Option<f64> {
let temperature = self.get_temperature()?.get::<degree_celsius>(); let temperature = self.get_temperature()?
.get::<degree_celsius>();
let pid_output = self.pid.update(temperature); let pid_output = self.pid.update(temperature);
Some(pid_output) Some(pid_output)
} }

View File

@ -1,27 +1,30 @@
use crate::{
ad5680, ad7172,
channel::{Channel, Channel0, Channel1},
channel_state::ChannelState,
command_handler::JsonBuffer,
command_parser::{CenterPoint, PwmPin},
hw_rev, pins, steinhart_hart,
};
use core::cmp::max_by; use core::cmp::max_by;
use heapless::{consts::U2, Vec}; use heapless::{consts::U2, Vec};
use serde::{Serialize, Serializer}; use serde::{Serialize, Serializer};
use smoltcp::time::Instant; use smoltcp::time::Instant;
use stm32f4xx_hal::hal; use stm32f4xx_hal::hal;
use uom::si::{ use uom::si::{
electric_current::ampere,
electric_potential::{millivolt, volt},
electrical_resistance::ohm,
f64::{ElectricCurrent, ElectricPotential, ElectricalResistance, Time}, f64::{ElectricCurrent, ElectricPotential, ElectricalResistance, Time},
electric_potential::{millivolt, volt},
electric_current::ampere,
electrical_resistance::ohm,
ratio::ratio, ratio::ratio,
thermodynamic_temperature::degree_celsius, thermodynamic_temperature::degree_celsius,
}; };
use crate::{
ad5680,
ad7172,
channel::{Channel, Channel0, Channel1},
channel_state::ChannelState,
command_parser::{CenterPoint, PwmPin},
command_handler::JsonBuffer,
pins,
steinhart_hart,
hw_rev,
};
pub const CHANNELS: usize = 2; pub const CHANNELS: usize = 2;
const R_SENSE: f64 = 0.05; pub const R_SENSE: f64 = 0.05;
// DAC chip outputs 0-5v, which is then passed through a resistor dividor to provide 0-3v range // DAC chip outputs 0-5v, which is then passed through a resistor dividor to provide 0-3v range
const DAC_OUT_V_MAX: f64 = 3.0; const DAC_OUT_V_MAX: f64 = 3.0;
@ -32,7 +35,7 @@ pub struct Channels<'a> {
pub adc: ad7172::Adc<pins::AdcSpi, pins::AdcNss>, pub adc: ad7172::Adc<pins::AdcSpi, pins::AdcNss>,
/// stm32f4 integrated adc /// stm32f4 integrated adc
pins_adc: pins::PinsAdc, pins_adc: pins::PinsAdc,
pwm: pins::PwmPins, pub pwm: pins::PwmPins,
hwrev: &'a hw_rev::HWRev, hwrev: &'a hw_rev::HWRev,
} }
@ -43,26 +46,19 @@ impl<'a> Channels<'a> {
adc.set_sync_enable(false).unwrap(); adc.set_sync_enable(false).unwrap();
// Setup channels and start ADC // Setup channels and start ADC
adc.setup_channel(0, ad7172::Input::Ain2, ad7172::Input::Ain3) adc.setup_channel(0, ad7172::Input::Ain2, ad7172::Input::Ain3).unwrap();
.unwrap(); let adc_calibration0 = adc.get_calibration(0)
let adc_calibration0 = adc.get_calibration(0).expect("adc_calibration0"); .expect("adc_calibration0");
adc.setup_channel(1, ad7172::Input::Ain0, ad7172::Input::Ain1) adc.setup_channel(1, ad7172::Input::Ain0, ad7172::Input::Ain1).unwrap();
.unwrap(); let adc_calibration1 = adc.get_calibration(1)
let adc_calibration1 = adc.get_calibration(1).expect("adc_calibration1"); .expect("adc_calibration1");
adc.start_continuous_conversion().unwrap(); adc.start_continuous_conversion().unwrap();
let channel0 = Channel::new(pins.channel0, adc_calibration0); let channel0 = Channel::new(pins.channel0, adc_calibration0);
let channel1 = Channel::new(pins.channel1, adc_calibration1); let channel1 = Channel::new(pins.channel1, adc_calibration1);
let pins_adc = pins.pins_adc; let pins_adc = pins.pins_adc;
let pwm = pins.pwm; let pwm = pins.pwm;
let mut channels = Channels { let mut channels = Channels { channel0, channel1, adc, pins_adc, pwm, hwrev };
channel0,
channel1,
adc,
pins_adc,
pwm,
hwrev,
};
for channel in 0..CHANNELS { for channel in 0..CHANNELS {
channels.calibrate_dac_value(channel); channels.calibrate_dac_value(channel);
channels.set_i(channel, ElectricCurrent::new::<ampere>(0.0)); channels.set_i(channel, ElectricCurrent::new::<ampere>(0.0));
@ -103,10 +99,10 @@ impl<'a> Channels<'a> {
/// calculate the TEC i_set centerpoint /// calculate the TEC i_set centerpoint
pub fn get_center(&mut self, channel: usize) -> ElectricPotential { pub fn get_center(&mut self, channel: usize) -> ElectricPotential {
match self.channel_state(channel).center { match self.channel_state(channel).center {
CenterPoint::Vref => self.read_vref(channel), CenterPoint::Vref =>
CenterPoint::Override(center_point) => { self.read_vref(channel),
ElectricPotential::new::<volt>(center_point.into()) CenterPoint::Override(center_point) =>
} ElectricPotential::new::<volt>(center_point.into()),
} }
} }
@ -116,17 +112,17 @@ impl<'a> Channels<'a> {
voltage voltage
} }
pub fn get_i_set(&mut self, channel: usize) -> ElectricCurrent { pub fn get_i(&mut self, channel: usize) -> ElectricCurrent {
let center_point = self.get_center(channel); let center_point = self.get_center(channel);
let r_sense = ElectricalResistance::new::<ohm>(R_SENSE); let r_sense = ElectricalResistance::new::<ohm>(R_SENSE);
let voltage = self.get_dac(channel); let voltage = self.get_dac(channel);
(voltage - center_point) / (10.0 * r_sense) let i_tec = (voltage - center_point) / (10.0 * r_sense);
i_tec
} }
/// i_set DAC /// i_set DAC
fn set_dac(&mut self, channel: usize, voltage: ElectricPotential) -> ElectricPotential { fn set_dac(&mut self, channel: usize, voltage: ElectricPotential) -> ElectricPotential {
let value = ((voltage / ElectricPotential::new::<volt>(DAC_OUT_V_MAX)).get::<ratio>() let value = ((voltage / ElectricPotential::new::<volt>(DAC_OUT_V_MAX)).get::<ratio>() * (ad5680::MAX_VALUE as f64)) as u32 ;
* (ad5680::MAX_VALUE as f64)) as u32;
match channel { match channel {
0 => self.channel0.dac.set(value).unwrap(), 0 => self.channel0.dac.set(value).unwrap(),
1 => self.channel1.dac.set(value).unwrap(), 1 => self.channel1.dac.set(value).unwrap(),
@ -136,17 +132,18 @@ impl<'a> Channels<'a> {
voltage voltage
} }
pub fn set_i(&mut self, channel: usize, i_set: ElectricCurrent) -> ElectricCurrent { pub fn set_i(&mut self, channel: usize, i_tec: ElectricCurrent) -> ElectricCurrent {
let vref_meas = match channel { let vref_meas = match channel.into() {
0 => self.channel0.vref_meas, 0 => self.channel0.vref_meas,
1 => self.channel1.vref_meas, 1 => self.channel1.vref_meas,
_ => unreachable!(), _ => unreachable!(),
}; };
let center_point = vref_meas; let center_point = vref_meas;
let r_sense = ElectricalResistance::new::<ohm>(R_SENSE); let r_sense = ElectricalResistance::new::<ohm>(R_SENSE);
let voltage = i_set * 10.0 * r_sense + center_point; let voltage = i_tec * 10.0 * r_sense + center_point;
let voltage = self.set_dac(channel, voltage); let voltage = self.set_dac(channel, voltage);
(voltage - center_point) / (10.0 * r_sense) let i_tec = (voltage - center_point) / (10.0 * r_sense);
i_tec
} }
pub fn read_dac_feedback(&mut self, channel: usize) -> ElectricPotential { pub fn read_dac_feedback(&mut self, channel: usize) -> ElectricPotential {
@ -154,7 +151,7 @@ impl<'a> Channels<'a> {
0 => { 0 => {
let sample = self.pins_adc.convert( let sample = self.pins_adc.convert(
&self.channel0.dac_feedback_pin, &self.channel0.dac_feedback_pin,
stm32f4xx_hal::adc::config::SampleTime::Cycles_480, stm32f4xx_hal::adc::config::SampleTime::Cycles_480
); );
let mv = self.pins_adc.sample_to_millivolts(sample); let mv = self.pins_adc.sample_to_millivolts(sample);
ElectricPotential::new::<millivolt>(mv as f64) ElectricPotential::new::<millivolt>(mv as f64)
@ -162,7 +159,7 @@ impl<'a> Channels<'a> {
1 => { 1 => {
let sample = self.pins_adc.convert( let sample = self.pins_adc.convert(
&self.channel1.dac_feedback_pin, &self.channel1.dac_feedback_pin,
stm32f4xx_hal::adc::config::SampleTime::Cycles_480, stm32f4xx_hal::adc::config::SampleTime::Cycles_480
); );
let mv = self.pins_adc.sample_to_millivolts(sample); let mv = self.pins_adc.sample_to_millivolts(sample);
ElectricPotential::new::<millivolt>(mv as f64) ElectricPotential::new::<millivolt>(mv as f64)
@ -171,11 +168,7 @@ impl<'a> Channels<'a> {
} }
} }
pub fn read_dac_feedback_until_stable( pub fn read_dac_feedback_until_stable(&mut self, channel: usize, tolerance: ElectricPotential) -> ElectricPotential {
&mut self,
channel: usize,
tolerance: ElectricPotential,
) -> ElectricPotential {
let mut prev = self.read_dac_feedback(channel); let mut prev = self.read_dac_feedback(channel);
loop { loop {
let current = self.read_dac_feedback(channel); let current = self.read_dac_feedback(channel);
@ -191,7 +184,7 @@ impl<'a> Channels<'a> {
0 => { 0 => {
let sample = self.pins_adc.convert( let sample = self.pins_adc.convert(
&self.channel0.itec_pin, &self.channel0.itec_pin,
stm32f4xx_hal::adc::config::SampleTime::Cycles_480, stm32f4xx_hal::adc::config::SampleTime::Cycles_480
); );
let mv = self.pins_adc.sample_to_millivolts(sample); let mv = self.pins_adc.sample_to_millivolts(sample);
ElectricPotential::new::<millivolt>(mv as f64) ElectricPotential::new::<millivolt>(mv as f64)
@ -199,7 +192,7 @@ impl<'a> Channels<'a> {
1 => { 1 => {
let sample = self.pins_adc.convert( let sample = self.pins_adc.convert(
&self.channel1.itec_pin, &self.channel1.itec_pin,
stm32f4xx_hal::adc::config::SampleTime::Cycles_480, stm32f4xx_hal::adc::config::SampleTime::Cycles_480
); );
let mv = self.pins_adc.sample_to_millivolts(sample); let mv = self.pins_adc.sample_to_millivolts(sample);
ElectricPotential::new::<millivolt>(mv as f64) ElectricPotential::new::<millivolt>(mv as f64)
@ -214,7 +207,7 @@ impl<'a> Channels<'a> {
0 => { 0 => {
let sample = self.pins_adc.convert( let sample = self.pins_adc.convert(
&self.channel0.vref_pin, &self.channel0.vref_pin,
stm32f4xx_hal::adc::config::SampleTime::Cycles_480, stm32f4xx_hal::adc::config::SampleTime::Cycles_480
); );
let mv = self.pins_adc.sample_to_millivolts(sample); let mv = self.pins_adc.sample_to_millivolts(sample);
ElectricPotential::new::<millivolt>(mv as f64) ElectricPotential::new::<millivolt>(mv as f64)
@ -222,7 +215,7 @@ impl<'a> Channels<'a> {
1 => { 1 => {
let sample = self.pins_adc.convert( let sample = self.pins_adc.convert(
&self.channel1.vref_pin, &self.channel1.vref_pin,
stm32f4xx_hal::adc::config::SampleTime::Cycles_480, stm32f4xx_hal::adc::config::SampleTime::Cycles_480
); );
let mv = self.pins_adc.sample_to_millivolts(sample); let mv = self.pins_adc.sample_to_millivolts(sample);
ElectricPotential::new::<millivolt>(mv as f64) ElectricPotential::new::<millivolt>(mv as f64)
@ -236,7 +229,7 @@ impl<'a> Channels<'a> {
0 => { 0 => {
let sample = self.pins_adc.convert( let sample = self.pins_adc.convert(
&self.channel0.tec_u_meas_pin, &self.channel0.tec_u_meas_pin,
stm32f4xx_hal::adc::config::SampleTime::Cycles_480, stm32f4xx_hal::adc::config::SampleTime::Cycles_480
); );
let mv = self.pins_adc.sample_to_millivolts(sample); let mv = self.pins_adc.sample_to_millivolts(sample);
ElectricPotential::new::<millivolt>(mv as f64) ElectricPotential::new::<millivolt>(mv as f64)
@ -244,7 +237,7 @@ impl<'a> Channels<'a> {
1 => { 1 => {
let sample = self.pins_adc.convert( let sample = self.pins_adc.convert(
&self.channel1.tec_u_meas_pin, &self.channel1.tec_u_meas_pin,
stm32f4xx_hal::adc::config::SampleTime::Cycles_480, stm32f4xx_hal::adc::config::SampleTime::Cycles_480
); );
let mv = self.pins_adc.sample_to_millivolts(sample); let mv = self.pins_adc.sample_to_millivolts(sample);
ElectricPotential::new::<millivolt>(mv as f64) ElectricPotential::new::<millivolt>(mv as f64)
@ -257,25 +250,25 @@ impl<'a> Channels<'a> {
/// ///
/// The thermostat DAC applies a control voltage signal to the CTLI pin of MAX driver chip to control its output current. /// The thermostat DAC applies a control voltage signal to the CTLI pin of MAX driver chip to control its output current.
/// The CTLI input signal is centered around VREF of the MAX chip. Applying VREF to CTLI sets the output current to 0. /// The CTLI input signal is centered around VREF of the MAX chip. Applying VREF to CTLI sets the output current to 0.
/// ///
/// This calibration routine measures the VREF voltage and the DAC output with the STM32 ADC, and uses a breadth-first /// This calibration routine measures the VREF voltage and the DAC output with the STM32 ADC, and uses a breadth-first
/// search to find the DAC setting that will produce a DAC output voltage closest to VREF. This DAC output voltage will /// search to find the DAC setting that will produce a DAC output voltage closest to VREF. This DAC output voltage will
/// be stored and used in subsequent i_set routines to bias the current control signal to the measured VREF, reducing /// be stored and used in subsequent i_set routines to bias the current control signal to the measured VREF, reducing
/// the offset error of the current control signal. /// the offset error of the current control signal.
/// ///
/// The input offset of the STM32 ADC is eliminated by using the same ADC for the measurements, and by only using the /// The input offset of the STM32 ADC is eliminated by using the same ADC for the measurements, and by only using the
/// difference in VREF and DAC output for the calibration. /// difference in VREF and DAC output for the calibration.
/// ///
/// This routine should be called only once after boot, repeated reading of the vref signal and changing of the stored /// This routine should be called only once after boot, repeated reading of the vref signal and changing of the stored
/// VREF measurement can introduce significant noise at the current output, degrading the stabilily performance of the /// VREF measurement can introduce significant noise at the current output, degrading the stabilily performance of the
/// thermostat. /// thermostat.
pub fn calibrate_dac_value(&mut self, channel: usize) { pub fn calibrate_dac_value(&mut self, channel: usize) {
let samples = 50; let samples = 50;
let mut target_voltage = ElectricPotential::new::<volt>(0.0); let mut target_voltage = ElectricPotential::new::<volt>(0.0);
for _ in 0..samples { for _ in 0..samples {
target_voltage += self.get_center(channel); target_voltage = target_voltage + self.get_center(channel);
} }
target_voltage /= samples as f64; target_voltage = target_voltage / samples as f64;
let mut start_value = 1; let mut start_value = 1;
let mut best_error = ElectricPotential::new::<volt>(100.0); let mut best_error = ElectricPotential::new::<volt>(100.0);
@ -292,8 +285,7 @@ impl<'a> Channels<'a> {
_ => unreachable!(), _ => unreachable!(),
} }
let dac_feedback = self let dac_feedback = self.read_dac_feedback_until_stable(channel, ElectricPotential::new::<volt>(0.001));
.read_dac_feedback_until_stable(channel, ElectricPotential::new::<volt>(0.001));
let error = target_voltage - dac_feedback; let error = target_voltage - dac_feedback;
if error < ElectricPotential::new::<volt>(0.0) { if error < ElectricPotential::new::<volt>(0.0) {
break; break;
@ -301,8 +293,7 @@ impl<'a> Channels<'a> {
best_error = error; best_error = error;
start_value = prev_value; start_value = prev_value;
let vref = (value as f64 / ad5680::MAX_VALUE as f64) let vref = (value as f64 / ad5680::MAX_VALUE as f64) * ElectricPotential::new::<volt>(DAC_OUT_V_MAX);
* ElectricPotential::new::<volt>(DAC_OUT_V_MAX);
match channel { match channel {
0 => self.channel0.vref_meas = vref, 0 => self.channel0.vref_meas = vref,
1 => self.channel1.vref_meas = vref, 1 => self.channel1.vref_meas = vref,
@ -337,20 +328,28 @@ impl<'a> Channels<'a> {
} }
fn get_pwm(&self, channel: usize, pin: PwmPin) -> f64 { fn get_pwm(&self, channel: usize, pin: PwmPin) -> f64 {
fn get<P: hal::PwmPin<Duty = u16>>(pin: &P) -> f64 { fn get<P: hal::PwmPin<Duty=u16>>(pin: &P) -> f64 {
let duty = pin.get_duty(); let duty = pin.get_duty();
let max = pin.get_max_duty(); let max = pin.get_max_duty();
duty as f64 / (max as f64) duty as f64 / (max as f64)
} }
match (channel, pin) { match (channel, pin) {
(_, PwmPin::ISet) => panic!("i_set is no pwm pin"), (_, PwmPin::ISet) =>
(0, PwmPin::MaxIPos) => get(&self.pwm.max_i_pos0), panic!("i_set is no pwm pin"),
(0, PwmPin::MaxINeg) => get(&self.pwm.max_i_neg0), (0, PwmPin::MaxIPos) =>
(0, PwmPin::MaxV) => get(&self.pwm.max_v0), get(&self.pwm.max_i_pos0),
(1, PwmPin::MaxIPos) => get(&self.pwm.max_i_pos1), (0, PwmPin::MaxINeg) =>
(1, PwmPin::MaxINeg) => get(&self.pwm.max_i_neg1), get(&self.pwm.max_i_neg0),
(1, PwmPin::MaxV) => get(&self.pwm.max_v1), (0, PwmPin::MaxV) =>
_ => unreachable!(), 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!(),
} }
} }
@ -372,62 +371,58 @@ impl<'a> Channels<'a> {
(duty * max, max) (duty * max, max)
} }
// Measure current passing through TEC // Get current passing through TEC
pub fn get_i_measured(&mut self, channel: usize) -> ElectricCurrent { pub fn get_tec_i(&mut self, channel: usize) -> ElectricCurrent {
(self.read_itec(channel) - self.read_vref(channel)) / ElectricalResistance::new::<ohm>(0.4) (self.read_itec(channel) - self.read_vref(channel)) / ElectricalResistance::new::<ohm>(0.4)
} }
// Measure voltage across TEC // Get voltage across TEC
pub fn get_v_measured(&mut self, channel: usize) -> ElectricPotential { pub fn get_tec_v(&mut self, channel: usize) -> ElectricPotential {
(self.read_tec_u_meas(channel) - ElectricPotential::new::<volt>(1.5)) * 4.0 (self.read_tec_u_meas(channel) - ElectricPotential::new::<volt>(1.5)) * 4.0
} }
fn set_pwm(&mut self, channel: usize, pin: PwmPin, duty: f64) -> f64 { 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 { fn set<P: hal::PwmPin<Duty=u16>>(pin: &mut P, duty: f64) -> f64 {
let max = pin.get_max_duty(); let max = pin.get_max_duty();
let value = ((duty * (max as f64)) as u16).min(max); let value = ((duty * (max as f64)) as u16).min(max);
pin.set_duty(value); pin.set_duty(value);
value as f64 / (max as f64) value as f64 / (max as f64)
} }
match (channel, pin) { match (channel, pin) {
(_, PwmPin::ISet) => panic!("i_set is no pwm pin"), (_, PwmPin::ISet) =>
(0, PwmPin::MaxIPos) => set(&mut self.pwm.max_i_pos0, duty), panic!("i_set is no pwm pin"),
(0, PwmPin::MaxINeg) => set(&mut self.pwm.max_i_neg0, duty), (0, PwmPin::MaxIPos) =>
(0, PwmPin::MaxV) => set(&mut self.pwm.max_v0, duty), set(&mut self.pwm.max_i_pos0, duty),
(1, PwmPin::MaxIPos) => set(&mut self.pwm.max_i_pos1, duty), (0, PwmPin::MaxINeg) =>
(1, PwmPin::MaxINeg) => set(&mut self.pwm.max_i_neg1, duty), set(&mut self.pwm.max_i_neg0, duty),
(1, PwmPin::MaxV) => set(&mut self.pwm.max_v1, duty), (0, PwmPin::MaxV) =>
_ => unreachable!(), 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( pub fn set_max_v(&mut self, channel: usize, max_v: ElectricPotential) -> (ElectricPotential, ElectricPotential) {
&mut self,
channel: usize,
max_v: ElectricPotential,
) -> (ElectricPotential, ElectricPotential) {
let max = 4.0 * ElectricPotential::new::<volt>(3.3); let max = 4.0 * ElectricPotential::new::<volt>(3.3);
let duty = (max_v / max).get::<ratio>(); let duty = (max_v / max).get::<ratio>();
let duty = self.set_pwm(channel, PwmPin::MaxV, duty); let duty = self.set_pwm(channel, PwmPin::MaxV, duty);
(duty * max, max) (duty * max, max)
} }
pub fn set_max_i_pos( pub fn set_max_i_pos(&mut self, channel: usize, max_i_pos: ElectricCurrent) -> (ElectricCurrent, ElectricCurrent) {
&mut self,
channel: usize,
max_i_pos: ElectricCurrent,
) -> (ElectricCurrent, ElectricCurrent) {
let max = ElectricCurrent::new::<ampere>(3.0); let max = ElectricCurrent::new::<ampere>(3.0);
let duty = (max_i_pos / max).get::<ratio>(); let duty = (max_i_pos / max).get::<ratio>();
let duty = self.set_pwm(channel, PwmPin::MaxIPos, duty); let duty = self.set_pwm(channel, PwmPin::MaxIPos, duty);
(duty * max, max) (duty * max, max)
} }
pub fn set_max_i_neg( pub fn set_max_i_neg(&mut self, channel: usize, max_i_neg: ElectricCurrent) -> (ElectricCurrent, ElectricCurrent) {
&mut self,
channel: usize,
max_i_neg: ElectricCurrent,
) -> (ElectricCurrent, ElectricCurrent) {
let max = ElectricCurrent::new::<ampere>(3.0); let max = ElectricCurrent::new::<ampere>(3.0);
let duty = (max_i_neg / max).get::<ratio>(); let duty = (max_i_neg / max).get::<ratio>();
let duty = self.set_pwm(channel, PwmPin::MaxINeg, duty); let duty = self.set_pwm(channel, PwmPin::MaxINeg, duty);
@ -435,24 +430,28 @@ impl<'a> Channels<'a> {
} }
fn report(&mut self, channel: usize) -> Report { fn report(&mut self, channel: usize) -> Report {
let i_set = self.get_i(channel);
let i_tec = if self.hwrev.major > 2 {Some(self.read_itec(channel))} else {None};
let tec_i = if self.hwrev.major > 2 {Some(self.get_tec_i(channel))} else {None};
let dac_value = self.get_dac(channel);
let state = self.channel_state(channel); let state = self.channel_state(channel);
let pid_output = ElectricCurrent::new::<ampere>(state.pid.y1);
Report { Report {
channel, channel,
time: state.get_adc_time(), time: state.get_adc_time(),
interval: state.get_adc_interval(), interval: state.get_adc_interval(),
adc: state.get_adc(),
sens: state.get_sens(), sens: state.get_sens(),
temperature: state temperature: state.get_temperature()
.get_temperature()
.map(|temperature| temperature.get::<degree_celsius>()), .map(|temperature| temperature.get::<degree_celsius>()),
pid_engaged: state.pid_engaged, pid_engaged: state.pid_engaged,
pid_output: ElectricCurrent::new::<ampere>(state.pid.y1), i_set,
i_set: self.get_i_set(channel), dac_value,
i_measured: if self.hwrev.major > 2 { dac_feedback: self.read_dac_feedback(channel),
Some(self.get_i_measured(channel)) i_tec,
} else { tec_i,
None tec_u_meas: self.get_tec_v(channel),
}, pid_output,
v_measured: self.get_v_measured(channel),
} }
} }
@ -485,7 +484,7 @@ impl<'a> Channels<'a> {
PwmSummary { PwmSummary {
channel, channel,
center: CenterPointJson(self.channel_state(channel).center.clone()), center: CenterPointJson(self.channel_state(channel).center.clone()),
i_set: (self.get_i_set(channel), ElectricCurrent::new::<ampere>(3.0)).into(), i_set: (self.get_i(channel), ElectricCurrent::new::<ampere>(3.0)).into(),
max_v: (self.get_max_v(channel), ElectricPotential::new::<volt>(5.0)).into(), max_v: (self.get_max_v(channel), ElectricPotential::new::<volt>(5.0)).into(),
max_i_pos: self.get_max_i_pos(channel).into(), max_i_pos: self.get_max_i_pos(channel).into(),
max_i_neg: self.get_max_i_neg(channel).into(), max_i_neg: self.get_max_i_neg(channel).into(),
@ -501,10 +500,7 @@ impl<'a> Channels<'a> {
} }
fn postfilter_summary(&mut self, channel: usize) -> PostFilterSummary { fn postfilter_summary(&mut self, channel: usize) -> PostFilterSummary {
let rate = self let rate = self.adc.get_postfilter(channel as u8).unwrap()
.adc
.get_postfilter(channel as u8)
.unwrap()
.and_then(|filter| filter.output_rate()); .and_then(|filter| filter.output_rate());
PostFilterSummary { channel, rate } PostFilterSummary { channel, rate }
} }
@ -522,9 +518,7 @@ impl<'a> Channels<'a> {
SteinhartHartSummary { channel, params } SteinhartHartSummary { channel, params }
} }
pub fn steinhart_hart_summaries_json( pub fn steinhart_hart_summaries_json(&mut self) -> Result<JsonBuffer, serde_json_core::ser::Error> {
&mut self,
) -> Result<JsonBuffer, serde_json_core::ser::Error> {
let mut summaries = Vec::<_, U2>::new(); let mut summaries = Vec::<_, U2>::new();
for channel in 0..CHANNELS { for channel in 0..CHANNELS {
let _ = summaries.push(self.steinhart_hart_summary(channel)); let _ = summaries.push(self.steinhart_hart_summary(channel));
@ -532,12 +526,10 @@ impl<'a> Channels<'a> {
serde_json_core::to_vec(&summaries) serde_json_core::to_vec(&summaries)
} }
pub fn max_abs_i_measured(&mut self) -> ElectricCurrent { pub fn current_abs_max_tec_i(&mut self) -> ElectricCurrent {
max_by( max_by(self.get_tec_i(0).abs(),
self.get_i_measured(0).abs(), self.get_tec_i(1).abs(),
self.get_i_measured(1).abs(), |a, b| a.partial_cmp(b).unwrap_or(core::cmp::Ordering::Equal))
|a, b| a.partial_cmp(b).unwrap_or(core::cmp::Ordering::Equal),
)
} }
} }
@ -546,13 +538,17 @@ pub struct Report {
channel: usize, channel: usize,
time: Time, time: Time,
interval: Time, interval: Time,
adc: Option<ElectricPotential>,
sens: Option<ElectricalResistance>, sens: Option<ElectricalResistance>,
temperature: Option<f64>, temperature: Option<f64>,
pid_engaged: bool, pid_engaged: bool,
pid_output: ElectricCurrent,
i_set: ElectricCurrent, i_set: ElectricCurrent,
i_measured: Option<ElectricCurrent>, dac_value: ElectricPotential,
v_measured: ElectricPotential, dac_feedback: ElectricPotential,
i_tec: Option<ElectricPotential>,
tec_i: Option<ElectricCurrent>,
tec_u_meas: ElectricPotential,
pid_output: ElectricCurrent,
} }
pub struct CenterPointJson(CenterPoint); pub struct CenterPointJson(CenterPoint);
@ -564,8 +560,10 @@ impl Serialize for CenterPointJson {
S: Serializer, S: Serializer,
{ {
match self.0 { match self.0 {
CenterPoint::Vref => serializer.serialize_str("vref"), CenterPoint::Vref =>
CenterPoint::Override(vref) => serializer.serialize_f32(vref), serializer.serialize_str("vref"),
CenterPoint::Override(vref) =>
serializer.serialize_f32(vref),
} }
} }
} }

View File

@ -1,28 +1,45 @@
use smoltcp::socket::TcpSocket;
use log::{error, warn};
use core::fmt::Write;
use heapless::{consts::U1024, Vec};
use super::{ use super::{
ad7172, net,
channels::{Channels, CHANNELS},
command_parser::{ command_parser::{
CenterPoint, Command, Ipv4Config, PidParameter, PwmPin, ShParameter, ShowCommand, Ipv4Config,
Command,
ShowCommand,
CenterPoint,
PidParameter,
PwmPin,
ShParameter
},
ad7172,
CHANNEL_CONFIG_KEY,
channels::{
Channels,
CHANNELS
}, },
config::ChannelConfig, config::ChannelConfig,
dfu, dfu,
flash_store::FlashStore, flash_store::FlashStore,
hw_rev::HWRev,
net,
session::Session, session::Session,
FanCtrl, CHANNEL_CONFIG_KEY, FanCtrl,
hw_rev::HWRev,
}; };
use core::fmt::Write;
use heapless::{consts::U1024, Vec};
use log::{error, warn};
use smoltcp::socket::TcpSocket;
use uom::si::{ use uom::{
electric_current::ampere, si::{
electric_potential::volt, f64::{
electrical_resistance::ohm, ElectricCurrent,
f64::{ElectricCurrent, ElectricPotential, ElectricalResistance, ThermodynamicTemperature}, ElectricPotential,
thermodynamic_temperature::degree_celsius, ElectricalResistance,
ThermodynamicTemperature,
},
electric_current::ampere,
electric_potential::volt,
electrical_resistance::ohm,
thermodynamic_temperature::degree_celsius,
},
}; };
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
@ -35,9 +52,9 @@ pub enum Handler {
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum Error { pub enum Error {
Report, ReportError,
PostFilterRate, PostFilterRateError,
Flash, FlashError
} }
pub type JsonBuffer = Vec<u8, U1024>; pub type JsonBuffer = Vec<u8, U1024>;
@ -49,19 +66,19 @@ fn send_line(socket: &mut TcpSocket, data: &[u8]) -> bool {
// instead of sending incomplete line // instead of sending incomplete line
warn!( warn!(
"TCP socket has only {}/{} needed {}", "TCP socket has only {}/{} needed {}",
send_free + 1, send_free + 1, socket.send_capacity(), data.len(),
socket.send_capacity(),
data.len(),
); );
} else { } else {
match socket.send_slice(data) { match socket.send_slice(&data) {
Ok(sent) if sent == data.len() => { Ok(sent) if sent == data.len() => {
let _ = socket.send_slice(b"\n"); let _ = socket.send_slice(b"\n");
// success // success
return true; return true
} }
Ok(sent) => warn!("sent only {}/{} bytes", sent, data.len()), Ok(sent) =>
Err(e) => error!("error sending line: {:?}", e), warn!("sent only {}/{} bytes", sent, data.len()),
Err(e) =>
error!("error sending line: {:?}", e),
} }
} }
// not success // not success
@ -69,6 +86,7 @@ fn send_line(socket: &mut TcpSocket, data: &[u8]) -> bool {
} }
impl Handler { impl Handler {
fn reporting(socket: &mut TcpSocket) -> Result<Handler, Error> { fn reporting(socket: &mut TcpSocket) -> Result<Handler, Error> {
send_line(socket, b"{}"); send_line(socket, b"{}");
Ok(Handler::Handled) Ok(Handler::Handled)
@ -87,7 +105,7 @@ impl Handler {
Err(e) => { Err(e) => {
error!("unable to serialize report: {:?}", e); error!("unable to serialize report: {:?}", e);
let _ = writeln!(socket, "{{\"error\":\"{:?}\"}}", e); let _ = writeln!(socket, "{{\"error\":\"{:?}\"}}", e);
return Err(Error::Report); return Err(Error::ReportError);
} }
} }
Ok(Handler::Handled) Ok(Handler::Handled)
@ -101,7 +119,7 @@ impl Handler {
Err(e) => { Err(e) => {
error!("unable to serialize pid summary: {:?}", e); error!("unable to serialize pid summary: {:?}", e);
let _ = writeln!(socket, "{{\"error\":\"{:?}\"}}", e); let _ = writeln!(socket, "{{\"error\":\"{:?}\"}}", e);
return Err(Error::Report); return Err(Error::ReportError);
} }
} }
Ok(Handler::Handled) Ok(Handler::Handled)
@ -115,16 +133,13 @@ impl Handler {
Err(e) => { Err(e) => {
error!("unable to serialize pwm summary: {:?}", e); error!("unable to serialize pwm summary: {:?}", e);
let _ = writeln!(socket, "{{\"error\":\"{:?}\"}}", e); let _ = writeln!(socket, "{{\"error\":\"{:?}\"}}", e);
return Err(Error::Report); return Err(Error::ReportError);
} }
} }
Ok(Handler::Handled) Ok(Handler::Handled)
} }
fn show_steinhart_hart( fn show_steinhart_hart(socket: &mut TcpSocket, channels: &mut Channels) -> Result<Handler, Error> {
socket: &mut TcpSocket,
channels: &mut Channels,
) -> Result<Handler, Error> {
match channels.steinhart_hart_summaries_json() { match channels.steinhart_hart_summaries_json() {
Ok(buf) => { Ok(buf) => {
send_line(socket, &buf); send_line(socket, &buf);
@ -132,13 +147,13 @@ impl Handler {
Err(e) => { Err(e) => {
error!("unable to serialize steinhart-hart summaries: {:?}", e); error!("unable to serialize steinhart-hart summaries: {:?}", e);
let _ = writeln!(socket, "{{\"error\":\"{:?}\"}}", e); let _ = writeln!(socket, "{{\"error\":\"{:?}\"}}", e);
return Err(Error::Report); return Err(Error::ReportError);
} }
} }
Ok(Handler::Handled) Ok(Handler::Handled)
} }
fn show_post_filter(socket: &mut TcpSocket, channels: &mut Channels) -> Result<Handler, Error> { fn show_post_filter (socket: &mut TcpSocket, channels: &mut Channels) -> Result<Handler, Error> {
match channels.postfilter_summaries_json() { match channels.postfilter_summaries_json() {
Ok(buf) => { Ok(buf) => {
send_line(socket, &buf); send_line(socket, &buf);
@ -146,13 +161,13 @@ impl Handler {
Err(e) => { Err(e) => {
error!("unable to serialize postfilter summary: {:?}", e); error!("unable to serialize postfilter summary: {:?}", e);
let _ = writeln!(socket, "{{\"error\":\"{:?}\"}}", e); let _ = writeln!(socket, "{{\"error\":\"{:?}\"}}", e);
return Err(Error::Report); return Err(Error::ReportError);
} }
} }
Ok(Handler::Handled) Ok(Handler::Handled)
} }
fn show_ipv4(socket: &mut TcpSocket, ipv4_config: &mut Ipv4Config) -> Result<Handler, Error> { fn show_ipv4 (socket: &mut TcpSocket, ipv4_config: &mut Ipv4Config) -> Result<Handler, Error> {
let (cidr, gateway) = net::split_ipv4_config(ipv4_config.clone()); let (cidr, gateway) = net::split_ipv4_config(ipv4_config.clone());
let _ = write!(socket, "{{\"addr\":\"{}\"", cidr); let _ = write!(socket, "{{\"addr\":\"{}\"", cidr);
gateway.map(|gateway| write!(socket, ",\"gateway\":\"{}\"", gateway)); gateway.map(|gateway| write!(socket, ",\"gateway\":\"{}\"", gateway));
@ -160,23 +175,13 @@ impl Handler {
Ok(Handler::Handled) Ok(Handler::Handled)
} }
fn engage_pid( fn engage_pid (socket: &mut TcpSocket, channels: &mut Channels, channel: usize) -> Result<Handler, Error> {
socket: &mut TcpSocket,
channels: &mut Channels,
channel: usize,
) -> Result<Handler, Error> {
channels.channel_state(channel).pid_engaged = true; channels.channel_state(channel).pid_engaged = true;
send_line(socket, b"{}"); send_line(socket, b"{}");
Ok(Handler::Handled) Ok(Handler::Handled)
} }
fn set_pwm( fn set_pwm (socket: &mut TcpSocket, channels: &mut Channels, channel: usize, pin: PwmPin, value: f64) -> Result<Handler, Error> {
socket: &mut TcpSocket,
channels: &mut Channels,
channel: usize,
pin: PwmPin,
value: f64,
) -> Result<Handler, Error> {
match pin { match pin {
PwmPin::ISet => { PwmPin::ISet => {
channels.channel_state(channel).pid_engaged = false; channels.channel_state(channel).pid_engaged = false;
@ -201,13 +206,8 @@ impl Handler {
Ok(Handler::Handled) Ok(Handler::Handled)
} }
fn set_center_point( fn set_center_point(socket: &mut TcpSocket, channels: &mut Channels, channel: usize, center: CenterPoint) -> Result<Handler, Error> {
socket: &mut TcpSocket, let i_tec = channels.get_i(channel);
channels: &mut Channels,
channel: usize,
center: CenterPoint,
) -> Result<Handler, Error> {
let i_tec = channels.get_i_set(channel);
let state = channels.channel_state(channel); let state = channels.channel_state(channel);
state.center = center; state.center = center;
if !state.pid_engaged { if !state.pid_engaged {
@ -217,34 +217,28 @@ impl Handler {
Ok(Handler::Handled) Ok(Handler::Handled)
} }
fn set_pid( fn set_pid (socket: &mut TcpSocket, channels: &mut Channels, channel: usize, parameter: PidParameter, value: f64) -> Result<Handler, Error> {
socket: &mut TcpSocket,
channels: &mut Channels,
channel: usize,
parameter: PidParameter,
value: f64,
) -> Result<Handler, Error> {
let pid = &mut channels.channel_state(channel).pid; let pid = &mut channels.channel_state(channel).pid;
use super::command_parser::PidParameter::*; use super::command_parser::PidParameter::*;
match parameter { match parameter {
Target => pid.target = value, Target =>
KP => pid.parameters.kp = value as f32, pid.target = value,
KI => pid.update_ki(value as f32), KP =>
KD => pid.parameters.kd = value as f32, pid.parameters.kp = value as f32,
OutputMin => pid.parameters.output_min = value as f32, KI =>
OutputMax => pid.parameters.output_max = value as f32, pid.update_ki(value as f32),
KD =>
pid.parameters.kd = value as f32,
OutputMin =>
pid.parameters.output_min = value as f32,
OutputMax =>
pid.parameters.output_max = value as f32,
} }
send_line(socket, b"{}"); send_line(socket, b"{}");
Ok(Handler::Handled) Ok(Handler::Handled)
} }
fn set_steinhart_hart( fn set_steinhart_hart (socket: &mut TcpSocket, channels: &mut Channels, channel: usize, parameter: ShParameter, value: f64) -> Result<Handler, Error> {
socket: &mut TcpSocket,
channels: &mut Channels,
channel: usize,
parameter: ShParameter,
value: f64,
) -> Result<Handler, Error> {
let sh = &mut channels.channel_state(channel).sh; let sh = &mut channels.channel_state(channel).sh;
use super::command_parser::ShParameter::*; use super::command_parser::ShParameter::*;
match parameter { match parameter {
@ -256,49 +250,29 @@ impl Handler {
Ok(Handler::Handled) Ok(Handler::Handled)
} }
fn reset_post_filter( fn reset_post_filter (socket: &mut TcpSocket, channels: &mut Channels, channel: usize) -> Result<Handler, Error> {
socket: &mut TcpSocket,
channels: &mut Channels,
channel: usize,
) -> Result<Handler, Error> {
channels.adc.set_postfilter(channel as u8, None).unwrap(); channels.adc.set_postfilter(channel as u8, None).unwrap();
send_line(socket, b"{}"); send_line(socket, b"{}");
Ok(Handler::Handled) Ok(Handler::Handled)
} }
fn set_post_filter( fn set_post_filter (socket: &mut TcpSocket, channels: &mut Channels, channel: usize, rate: f32) -> Result<Handler, Error> {
socket: &mut TcpSocket,
channels: &mut Channels,
channel: usize,
rate: f32,
) -> Result<Handler, Error> {
let filter = ad7172::PostFilter::closest(rate); let filter = ad7172::PostFilter::closest(rate);
match filter { match filter {
Some(filter) => { Some(filter) => {
channels channels.adc.set_postfilter(channel as u8, Some(filter)).unwrap();
.adc
.set_postfilter(channel as u8, Some(filter))
.unwrap();
send_line(socket, b"{}"); send_line(socket, b"{}");
} }
None => { None => {
error!("unable to choose postfilter for rate {:.3}", rate); error!("unable to choose postfilter for rate {:.3}", rate);
send_line( send_line(socket, b"{{\"error\": \"unable to choose postfilter rate\"}}");
socket, return Err(Error::PostFilterRateError);
b"{{\"error\": \"unable to choose postfilter rate\"}}",
);
return Err(Error::PostFilterRate);
} }
} }
Ok(Handler::Handled) Ok(Handler::Handled)
} }
fn load_channel( fn load_channel (socket: &mut TcpSocket, channels: &mut Channels, store: &mut FlashStore, channel: Option<usize>) -> Result<Handler, Error> {
socket: &mut TcpSocket,
channels: &mut Channels,
store: &mut FlashStore,
channel: Option<usize>,
) -> Result<Handler, Error> {
for c in 0..CHANNELS { for c in 0..CHANNELS {
if channel.is_none() || channel == Some(c) { if channel.is_none() || channel == Some(c) {
match store.read_value::<ChannelConfig>(CHANNEL_CONFIG_KEY[c]) { match store.read_value::<ChannelConfig>(CHANNEL_CONFIG_KEY[c]) {
@ -313,7 +287,7 @@ impl Handler {
Err(e) => { Err(e) => {
error!("unable to load config from flash: {:?}", e); error!("unable to load config from flash: {:?}", e);
let _ = writeln!(socket, "{{\"error\":\"{:?}\"}}", e); let _ = writeln!(socket, "{{\"error\":\"{:?}\"}}", e);
return Err(Error::Flash); return Err(Error::FlashError);
} }
} }
} }
@ -321,12 +295,7 @@ impl Handler {
Ok(Handler::Handled) Ok(Handler::Handled)
} }
fn save_channel( fn save_channel (socket: &mut TcpSocket, channels: &mut Channels, channel: Option<usize>, store: &mut FlashStore) -> Result<Handler, Error> {
socket: &mut TcpSocket,
channels: &mut Channels,
channel: Option<usize>,
store: &mut FlashStore,
) -> Result<Handler, Error> {
for c in 0..CHANNELS { for c in 0..CHANNELS {
let mut store_value_buf = [0u8; 256]; let mut store_value_buf = [0u8; 256];
if channel.is_none() || channel == Some(c) { if channel.is_none() || channel == Some(c) {
@ -338,7 +307,7 @@ impl Handler {
Err(e) => { Err(e) => {
error!("unable to save channel {} config to flash: {:?}", c, e); error!("unable to save channel {} config to flash: {:?}", c, e);
let _ = writeln!(socket, "{{\"error\":\"{:?}\"}}", e); let _ = writeln!(socket, "{{\"error\":\"{:?}\"}}", e);
return Err(Error::Flash); return Err(Error::FlashError);
} }
} }
} }
@ -346,11 +315,7 @@ impl Handler {
Ok(Handler::Handled) Ok(Handler::Handled)
} }
fn set_ipv4( fn set_ipv4 (socket: &mut TcpSocket, store: &mut FlashStore, config: Ipv4Config) -> Result<Handler, Error> {
socket: &mut TcpSocket,
store: &mut FlashStore,
config: Ipv4Config,
) -> Result<Handler, Error> {
let _ = store let _ = store
.write_value("ipv4", &config, [0; 16]) .write_value("ipv4", &config, [0; 16])
.map_err(|e| error!("unable to save ipv4 config to flash: {:?}", e)); .map_err(|e| error!("unable to save ipv4 config to flash: {:?}", e));
@ -359,7 +324,7 @@ impl Handler {
Ok(Handler::NewIPV4(new_ipv4_config.unwrap())) Ok(Handler::NewIPV4(new_ipv4_config.unwrap()))
} }
fn reset(channels: &mut Channels) -> Result<Handler, Error> { fn reset (channels: &mut Channels) -> Result<Handler, Error> {
for i in 0..CHANNELS { for i in 0..CHANNELS {
channels.power_down(i); channels.power_down(i);
} }
@ -367,7 +332,7 @@ impl Handler {
Ok(Handler::Reset) Ok(Handler::Reset)
} }
fn dfu(channels: &mut Channels) -> Result<Handler, Error> { fn dfu (channels: &mut Channels) -> Result<Handler, Error> {
for i in 0..CHANNELS { for i in 0..CHANNELS {
channels.power_down(i); channels.power_down(i);
} }
@ -378,16 +343,9 @@ impl Handler {
Ok(Handler::Reset) Ok(Handler::Reset)
} }
fn set_fan( fn set_fan(socket: &mut TcpSocket, fan_pwm: u32, fan_ctrl: &mut FanCtrl) -> Result<Handler, Error> {
socket: &mut TcpSocket,
fan_pwm: u32,
fan_ctrl: &mut FanCtrl,
) -> Result<Handler, Error> {
if !fan_ctrl.fan_available() { if !fan_ctrl.fan_available() {
send_line( send_line(socket, b"{ \"warning\": \"this thermostat doesn't have fan!\" }");
socket,
b"{ \"warning\": \"this thermostat doesn't have a fan!\" }",
);
return Ok(Handler::Handled); return Ok(Handler::Handled);
} }
fan_ctrl.set_auto_mode(false); fan_ctrl.set_auto_mode(false);
@ -409,17 +367,14 @@ impl Handler {
Err(e) => { Err(e) => {
error!("unable to serialize fan summary: {:?}", e); error!("unable to serialize fan summary: {:?}", e);
let _ = writeln!(socket, "{{\"error\":\"{:?}\"}}", e); let _ = writeln!(socket, "{{\"error\":\"{:?}\"}}", e);
Err(Error::Report) Err(Error::ReportError)
} }
} }
} }
fn fan_auto(socket: &mut TcpSocket, fan_ctrl: &mut FanCtrl) -> Result<Handler, Error> { fn fan_auto(socket: &mut TcpSocket, fan_ctrl: &mut FanCtrl) -> Result<Handler, Error> {
if !fan_ctrl.fan_available() { if !fan_ctrl.fan_available() {
send_line( send_line(socket, b"{ \"warning\": \"this thermostat doesn't have fan!\" }");
socket,
b"{ \"warning\": \"this thermostat doesn't have fan!\" }",
);
return Ok(Handler::Handled); return Ok(Handler::Handled);
} }
fan_ctrl.set_auto_mode(true); fan_ctrl.set_auto_mode(true);
@ -431,13 +386,7 @@ impl Handler {
Ok(Handler::Handled) Ok(Handler::Handled)
} }
fn fan_curve( fn fan_curve(socket: &mut TcpSocket, fan_ctrl: &mut FanCtrl, k_a: f32, k_b: f32, k_c: f32) -> Result<Handler, Error> {
socket: &mut TcpSocket,
fan_ctrl: &mut FanCtrl,
k_a: f32,
k_b: f32,
k_c: f32,
) -> Result<Handler, Error> {
fan_ctrl.set_curve(k_a, k_b, k_c); fan_ctrl.set_curve(k_a, k_b, k_c);
send_line(socket, b"{}"); send_line(socket, b"{}");
Ok(Handler::Handled) Ok(Handler::Handled)
@ -458,73 +407,40 @@ impl Handler {
Err(e) => { Err(e) => {
error!("unable to serialize HWRev summary: {:?}", e); error!("unable to serialize HWRev summary: {:?}", e);
let _ = writeln!(socket, "{{\"error\":\"{:?}\"}}", e); let _ = writeln!(socket, "{{\"error\":\"{:?}\"}}", e);
Err(Error::Report) Err(Error::ReportError)
} }
} }
} }
pub fn handle_command( pub fn handle_command(command: Command, socket: &mut TcpSocket, channels: &mut Channels, session: &Session, store: &mut FlashStore, ipv4_config: &mut Ipv4Config, fan_ctrl: &mut FanCtrl, hwrev: HWRev) -> Result<Self, Error> {
command: Command,
socket: &mut TcpSocket,
channels: &mut Channels,
session: &Session,
store: &mut FlashStore,
ipv4_config: &mut Ipv4Config,
fan_ctrl: &mut FanCtrl,
hwrev: HWRev,
) -> Result<Self, Error> {
match command { match command {
Command::Quit => Ok(Handler::CloseSocket), Command::Quit => Ok(Handler::CloseSocket),
Command::Reporting(_reporting) => Handler::reporting(socket), Command::Reporting(_reporting) => Handler::reporting(socket),
Command::Show(ShowCommand::Reporting) => Handler::show_report_mode(socket, session), Command::Show(ShowCommand::Reporting) => Handler::show_report_mode(socket, session),
Command::Show(ShowCommand::Input) => Handler::show_report(socket, channels), Command::Show(ShowCommand::Input) => Handler::show_report(socket, channels),
Command::Show(ShowCommand::Pid) => Handler::show_pid(socket, channels), Command::Show(ShowCommand::Pid) => Handler::show_pid(socket, channels),
Command::Show(ShowCommand::Pwm) => Handler::show_pwm(socket, channels), Command::Show(ShowCommand::Pwm) => Handler::show_pwm(socket, channels),
Command::Show(ShowCommand::SteinhartHart) => { Command::Show(ShowCommand::SteinhartHart) => Handler::show_steinhart_hart(socket, channels),
Handler::show_steinhart_hart(socket, channels)
}
Command::Show(ShowCommand::PostFilter) => Handler::show_post_filter(socket, channels), Command::Show(ShowCommand::PostFilter) => Handler::show_post_filter(socket, channels),
Command::Show(ShowCommand::Ipv4) => Handler::show_ipv4(socket, ipv4_config), Command::Show(ShowCommand::Ipv4) => Handler::show_ipv4(socket, ipv4_config),
Command::PwmPid { channel } => Handler::engage_pid(socket, channels, channel), Command::PwmPid { channel } => Handler::engage_pid(socket, channels, channel),
Command::Pwm { Command::Pwm { channel, pin, value } => Handler::set_pwm(socket, channels, channel, pin, value),
channel, Command::CenterPoint { channel, center } => Handler::set_center_point(socket, channels, channel, center),
pin, Command::Pid { channel, parameter, value } => Handler::set_pid(socket, channels, channel, parameter, value),
value, Command::SteinhartHart { channel, parameter, value } => Handler::set_steinhart_hart(socket, channels, channel, parameter, value),
} => Handler::set_pwm(socket, channels, channel, pin, value), Command::PostFilter { channel, rate: None } => Handler::reset_post_filter(socket, channels, channel),
Command::CenterPoint { channel, center } => { Command::PostFilter { channel, rate: Some(rate) } => Handler::set_post_filter(socket, channels, channel, rate),
Handler::set_center_point(socket, channels, channel, center)
}
Command::Pid {
channel,
parameter,
value,
} => Handler::set_pid(socket, channels, channel, parameter, value),
Command::SteinhartHart {
channel,
parameter,
value,
} => Handler::set_steinhart_hart(socket, channels, channel, parameter, value),
Command::PostFilter {
channel,
rate: None,
} => Handler::reset_post_filter(socket, channels, channel),
Command::PostFilter {
channel,
rate: Some(rate),
} => Handler::set_post_filter(socket, channels, channel, rate),
Command::Load { channel } => Handler::load_channel(socket, channels, store, channel), Command::Load { channel } => Handler::load_channel(socket, channels, store, channel),
Command::Save { channel } => Handler::save_channel(socket, channels, channel, store), Command::Save { channel } => Handler::save_channel(socket, channels, channel, store),
Command::Ipv4(config) => Handler::set_ipv4(socket, store, config), Command::Ipv4(config) => Handler::set_ipv4(socket, store, config),
Command::Reset => Handler::reset(channels), Command::Reset => Handler::reset(channels),
Command::Dfu => Handler::dfu(channels), Command::Dfu => Handler::dfu(channels),
Command::FanSet { fan_pwm } => Handler::set_fan(socket, fan_pwm, fan_ctrl), Command::FanSet {fan_pwm} => Handler::set_fan(socket, fan_pwm, fan_ctrl),
Command::ShowFan => Handler::show_fan(socket, fan_ctrl), Command::ShowFan => Handler::show_fan(socket, fan_ctrl),
Command::FanAuto => Handler::fan_auto(socket, fan_ctrl), Command::FanAuto => Handler::fan_auto(socket, fan_ctrl),
Command::FanCurve { k_a, k_b, k_c } => { Command::FanCurve { k_a, k_b, k_c } => Handler::fan_curve(socket, fan_ctrl, k_a, k_b, k_c),
Handler::fan_curve(socket, fan_ctrl, k_a, k_b, k_c)
}
Command::FanCurveDefaults => Handler::fan_defaults(socket, fan_ctrl), Command::FanCurveDefaults => Handler::fan_defaults(socket, fan_ctrl),
Command::ShowHWRev => Handler::show_hwrev(socket, hwrev), Command::ShowHWRev => Handler::show_hwrev(socket, hwrev),
} }
} }
} }

View File

@ -2,20 +2,19 @@ use core::fmt;
use core::num::ParseIntError; use core::num::ParseIntError;
use core::str::{from_utf8, Utf8Error}; use core::str::{from_utf8, Utf8Error};
use nom::{ use nom::{
IResult,
branch::alt, branch::alt,
bytes::complete::{is_a, tag, take_while1}, bytes::complete::{is_a, tag, take_while1},
character::{ character::{is_digit, complete::{char, one_of}},
complete::{char, one_of},
is_digit,
},
combinator::{complete, map, opt, value}, combinator::{complete, map, opt, value},
error::ErrorKind,
multi::{fold_many0, fold_many1},
sequence::preceded, sequence::preceded,
IResult, Needed, multi::{fold_many0, fold_many1},
error::ErrorKind,
Needed,
}; };
use num_traits::{Num, ParseFloatError}; use num_traits::{Num, ParseFloatError};
use serde::{Deserialize, Serialize}; use serde::{Serialize, Deserialize};
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum Error { pub enum Error {
@ -31,9 +30,12 @@ pub enum Error {
impl<'t> From<nom::Err<(&'t [u8], ErrorKind)>> for Error { impl<'t> From<nom::Err<(&'t [u8], ErrorKind)>> for Error {
fn from(e: nom::Err<(&'t [u8], ErrorKind)>) -> Self { fn from(e: nom::Err<(&'t [u8], ErrorKind)>) -> Self {
match e { match e {
nom::Err::Incomplete(_) => Error::Incomplete, nom::Err::Incomplete(_) =>
nom::Err::Error((_, e)) => Error::Parser(e), Error::Incomplete,
nom::Err::Failure((_, e)) => Error::Parser(e), nom::Err::Error((_, e)) =>
Error::Parser(e),
nom::Err::Failure((_, e)) =>
Error::Parser(e),
} }
} }
} }
@ -59,7 +61,8 @@ impl From<ParseFloatError> for Error {
impl fmt::Display for Error { impl fmt::Display for Error {
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match self { match self {
Error::Incomplete => "incomplete input".fmt(fmt), Error::Incomplete =>
"incomplete input".fmt(fmt),
Error::UnexpectedInput(c) => { Error::UnexpectedInput(c) => {
"unexpected input: ".fmt(fmt)?; "unexpected input: ".fmt(fmt)?;
c.fmt(fmt) c.fmt(fmt)
@ -76,7 +79,9 @@ impl fmt::Display for Error {
"parsing int: ".fmt(fmt)?; "parsing int: ".fmt(fmt)?;
(e as &dyn core::fmt::Debug).fmt(fmt) (e as &dyn core::fmt::Debug).fmt(fmt)
} }
Error::ParseFloat => "parsing float".fmt(fmt), Error::ParseFloat => {
"parsing float".fmt(fmt)
}
} }
} }
} }
@ -175,7 +180,7 @@ pub enum Command {
}, },
Dfu, Dfu,
FanSet { FanSet {
fan_pwm: u32, fan_pwm: u32
}, },
FanAuto, FanAuto,
ShowFan, ShowFan,
@ -189,7 +194,12 @@ pub enum Command {
} }
fn end(input: &[u8]) -> IResult<&[u8], ()> { fn end(input: &[u8]) -> IResult<&[u8], ()> {
complete(fold_many0(one_of("\r\n\t "), (), |(), _| ()))(input) complete(
fold_many0(
one_of("\r\n\t "),
(), |(), _| ()
)
)(input)
} }
fn whitespace(input: &[u8]) -> IResult<&[u8], ()> { fn whitespace(input: &[u8]) -> IResult<&[u8], ()> {
@ -197,27 +207,36 @@ fn whitespace(input: &[u8]) -> IResult<&[u8], ()> {
} }
fn unsigned(input: &[u8]) -> IResult<&[u8], Result<u32, Error>> { fn unsigned(input: &[u8]) -> IResult<&[u8], Result<u32, Error>> {
take_while1(is_digit)(input).map(|(input, digits)| { take_while1(is_digit)(input)
let result = from_utf8(digits) .map(|(input, digits)| {
.map_err(|e| e.into()) let result =
.and_then(|digits| u32::from_str_radix(digits, 10).map_err(|e| e.into())); from_utf8(digits)
(input, result) .map_err(|e| e.into())
}) .and_then(|digits| u32::from_str_radix(digits, 10)
.map_err(|e| e.into())
);
(input, result)
})
} }
fn float(input: &[u8]) -> IResult<&[u8], Result<f64, Error>> { fn float(input: &[u8]) -> IResult<&[u8], Result<f64, Error>> {
let (input, sign) = opt(is_a("-"))(input)?; let (input, sign) = opt(is_a("-"))(input)?;
let negative = sign.is_some(); let negative = sign.is_some();
let (input, digits) = take_while1(|c| is_digit(c) || c == b'.')(input)?; let (input, digits) = take_while1(|c| is_digit(c) || c == '.' as u8)(input)?;
let result = from_utf8(digits) let result =
from_utf8(digits)
.map_err(|e| e.into()) .map_err(|e| e.into())
.and_then(|digits| f64::from_str_radix(digits, 10).map_err(|e| e.into())) .and_then(|digits| f64::from_str_radix(digits, 10)
.map_err(|e| e.into())
)
.map(|result: f64| if negative { -result } else { result }); .map(|result: f64| if negative { -result } else { result });
Ok((input, result)) Ok((input, result))
} }
fn off_on(input: &[u8]) -> IResult<&[u8], bool> { fn off_on(input: &[u8]) -> IResult<&[u8], bool> {
alt((value(false, tag("off")), value(true, tag("on"))))(input) alt((value(false, tag("off")),
value(true, tag("on"))
))(input)
} }
fn channel(input: &[u8]) -> IResult<&[u8], usize> { fn channel(input: &[u8]) -> IResult<&[u8], usize> {
@ -236,41 +255,65 @@ fn report(input: &[u8]) -> IResult<&[u8], Command> {
preceded( preceded(
whitespace, whitespace,
// `report mode <on | off>` - Switch repoting mode // `report mode <on | off>` - Switch repoting mode
map(off_on, Command::Reporting), map(off_on, Command::Reporting)
), ),
// `report mode` - Show current reporting state // `report mode` - Show current reporting state
value(Command::Show(ShowCommand::Reporting), end), value(Command::Show(ShowCommand::Reporting), end)
)), ))
), )),
),
// `report` - Report once // `report` - Report once
value(Command::Show(ShowCommand::Input), end), value(Command::Show(ShowCommand::Input), end)
)), ))
)(input) )(input)
} }
fn pwm_setup(input: &[u8]) -> IResult<&[u8], Result<(PwmPin, f64), Error>> { fn pwm_setup(input: &[u8]) -> IResult<&[u8], Result<(PwmPin, f64), Error>> {
let result_with_pin = let result_with_pin = |pin: PwmPin|
|pin: PwmPin| move |result: Result<f64, Error>| result.map(|value| (pin, value)); move |result: Result<f64, Error>|
result.map(|value| (pin, value));
alt(( alt((
map( map(
preceded(tag("i_set"), preceded(whitespace, float)), preceded(
result_with_pin(PwmPin::ISet), tag("i_set"),
preceded(
whitespace,
float
)
),
result_with_pin(PwmPin::ISet)
), ),
map( map(
preceded(tag("max_i_pos"), preceded(whitespace, float)), preceded(
result_with_pin(PwmPin::MaxIPos), tag("max_i_pos"),
preceded(
whitespace,
float
)
),
result_with_pin(PwmPin::MaxIPos)
), ),
map( map(
preceded(tag("max_i_neg"), preceded(whitespace, float)), preceded(
result_with_pin(PwmPin::MaxINeg), tag("max_i_neg"),
preceded(
whitespace,
float
)
),
result_with_pin(PwmPin::MaxINeg)
), ),
map( map(
preceded(tag("max_v"), preceded(whitespace, float)), preceded(
result_with_pin(PwmPin::MaxV), tag("max_v"),
), preceded(
))(input) whitespace,
float
)
),
result_with_pin(PwmPin::MaxV)
))
)(input)
} }
/// `pwm <0-1> pid` - Set PWM to be controlled by PID /// `pwm <0-1> pid` - Set PWM to be controlled by PID
@ -293,22 +336,17 @@ fn pwm(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
|input| { |input| {
let (input, config) = pwm_setup(input)?; let (input, config) = pwm_setup(input)?;
match config { match config {
Ok((pin, value)) => Ok(( Ok((pin, value)) =>
input, Ok((input, Ok(Command::Pwm { channel, pin, value }))),
Ok(Command::Pwm { Err(e) =>
channel, Ok((input, Err(e))),
pin,
value,
}),
)),
Err(e) => Ok((input, Err(e))),
} }
}, },
))(input)?; ))(input)?;
end(input)?; end(input)?;
Ok((input, result)) Ok((input, result))
}, },
value(Ok(Command::Show(ShowCommand::Pwm)), end), value(Ok(Command::Show(ShowCommand::Pwm)), end)
))(input) ))(input)
} }
@ -317,39 +355,36 @@ fn center_point(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
let (input, _) = whitespace(input)?; let (input, _) = whitespace(input)?;
let (input, channel) = channel(input)?; let (input, channel) = channel(input)?;
let (input, _) = whitespace(input)?; let (input, _) = whitespace(input)?;
let (input, center) = alt((value(Ok(CenterPoint::Vref), tag("vref")), |input| { let (input, center) = alt((
let (input, value) = float(input)?; value(Ok(CenterPoint::Vref), tag("vref")),
Ok(( |input| {
input, let (input, value) = float(input)?;
value.map(|value| CenterPoint::Override(value as f32)), Ok((input, value.map(|value| CenterPoint::Override(value as f32))))
)) }
}))(input)?; ))(input)?;
end(input)?; end(input)?;
Ok(( Ok((input, center.map(|center| Command::CenterPoint {
input, channel,
center.map(|center| Command::CenterPoint { channel, center }), center,
)) })))
} }
/// `pid <0-1> <parameter> <value>` /// `pid <0-1> <parameter> <value>`
fn pid_parameter(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> { fn pid_parameter(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
let (input, channel) = channel(input)?; let (input, channel) = channel(input)?;
let (input, _) = whitespace(input)?; let (input, _) = whitespace(input)?;
let (input, parameter) = alt(( let (input, parameter) =
value(PidParameter::Target, tag("target")), alt((value(PidParameter::Target, tag("target")),
value(PidParameter::KP, tag("kp")), value(PidParameter::KP, tag("kp")),
value(PidParameter::KI, tag("ki")), value(PidParameter::KI, tag("ki")),
value(PidParameter::KD, tag("kd")), value(PidParameter::KD, tag("kd")),
value(PidParameter::OutputMin, tag("output_min")), value(PidParameter::OutputMin, tag("output_min")),
value(PidParameter::OutputMax, tag("output_max")), value(PidParameter::OutputMax, tag("output_max")),
))(input)?; ))(input)?;
let (input, _) = whitespace(input)?; let (input, _) = whitespace(input)?;
let (input, value) = float(input)?; let (input, value) = float(input)?;
let result = value.map(|value| Command::Pid { let result = value
channel, .map(|value| Command::Pid { channel, parameter, value });
parameter,
value,
});
Ok((input, result)) Ok((input, result))
} }
@ -357,8 +392,11 @@ fn pid_parameter(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
fn pid(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> { fn pid(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
let (input, _) = tag("pid")(input)?; let (input, _) = tag("pid")(input)?;
alt(( alt((
preceded(whitespace, pid_parameter), preceded(
value(Ok(Command::Show(ShowCommand::Pid)), end), whitespace,
pid_parameter
),
value(Ok(Command::Show(ShowCommand::Pid)), end)
))(input) ))(input)
} }
@ -366,18 +404,15 @@ fn pid(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
fn steinhart_hart_parameter(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> { fn steinhart_hart_parameter(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
let (input, channel) = channel(input)?; let (input, channel) = channel(input)?;
let (input, _) = whitespace(input)?; let (input, _) = whitespace(input)?;
let (input, parameter) = alt(( let (input, parameter) =
value(ShParameter::T0, tag("t0")), alt((value(ShParameter::T0, tag("t0")),
value(ShParameter::B, tag("b")), value(ShParameter::B, tag("b")),
value(ShParameter::R0, tag("r0")), value(ShParameter::R0, tag("r0"))
))(input)?; ))(input)?;
let (input, _) = whitespace(input)?; let (input, _) = whitespace(input)?;
let (input, value) = float(input)?; let (input, value) = float(input)?;
let result = value.map(|value| Command::SteinhartHart { let result = value
channel, .map(|value| Command::SteinhartHart { channel, parameter, value });
parameter,
value,
});
Ok((input, result)) Ok((input, result))
} }
@ -385,38 +420,42 @@ fn steinhart_hart_parameter(input: &[u8]) -> IResult<&[u8], Result<Command, Erro
fn steinhart_hart(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> { fn steinhart_hart(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
let (input, _) = tag("s-h")(input)?; let (input, _) = tag("s-h")(input)?;
alt(( alt((
preceded(whitespace, steinhart_hart_parameter), preceded(
value(Ok(Command::Show(ShowCommand::SteinhartHart)), end), whitespace,
steinhart_hart_parameter
),
value(Ok(Command::Show(ShowCommand::SteinhartHart)), end)
))(input) ))(input)
} }
fn postfilter(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> { fn postfilter(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
let (input, _) = tag("postfilter")(input)?; let (input, _) = tag("postfilter")(input)?;
alt(( alt((
preceded(whitespace, |input| { preceded(
let (input, channel) = channel(input)?; whitespace,
let (input, _) = whitespace(input)?; |input| {
alt(( let (input, channel) = channel(input)?;
value( let (input, _) = whitespace(input)?;
Ok(Command::PostFilter { alt((
value(Ok(Command::PostFilter {
channel, channel,
rate: None, rate: None,
}), }), tag("off")),
tag("off"), move |input| {
), let (input, _) = tag("rate")(input)?;
move |input| { let (input, _) = whitespace(input)?;
let (input, _) = tag("rate")(input)?; let (input, rate) = float(input)?;
let (input, _) = whitespace(input)?; let result = rate
let (input, rate) = float(input)?; .map(|rate| Command::PostFilter {
let result = rate.map(|rate| Command::PostFilter { channel,
channel, rate: Some(rate as f32),
rate: Some(rate as f32), });
}); Ok((input, result))
Ok((input, result)) }
}, ))(input)
))(input) }
}), ),
value(Ok(Command::Show(ShowCommand::PostFilter)), end), value(Ok(Command::Show(ShowCommand::PostFilter)), end)
))(input) ))(input)
} }
@ -429,7 +468,7 @@ fn load(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
let (input, _) = end(input)?; let (input, _) = end(input)?;
Ok((input, Some(channel))) Ok((input, Some(channel)))
}, },
value(None, end), value(None, end)
))(input)?; ))(input)?;
let result = Ok(Command::Load { channel }); let result = Ok(Command::Load { channel });
@ -445,7 +484,7 @@ fn save(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
let (input, _) = end(input)?; let (input, _) = end(input)?;
Ok((input, Some(channel))) Ok((input, Some(channel)))
}, },
value(None, end), value(None, end)
))(input)?; ))(input)?;
let result = Ok(Command::Save { channel }); let result = Ok(Command::Save { channel });
@ -507,17 +546,12 @@ fn fan(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
}, },
|input| { |input| {
let (input, value) = unsigned(input)?; let (input, value) = unsigned(input)?;
Ok(( Ok((input, Ok(Command::FanSet { fan_pwm: value.unwrap_or(0)})))
input,
Ok(Command::FanSet {
fan_pwm: value.unwrap_or(0),
}),
))
}, },
))(input)?; ))(input)?;
Ok((input, result)) Ok((input, result))
}, },
value(Ok(Command::ShowFan), end), value(Ok(Command::ShowFan), end)
))(input) ))(input)
} }
@ -538,14 +572,7 @@ fn fan_curve(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
let (input, _) = whitespace(input)?; let (input, _) = whitespace(input)?;
let (input, k_c) = float(input)?; let (input, k_c) = float(input)?;
if k_a.is_ok() && k_b.is_ok() && k_c.is_ok() { if k_a.is_ok() && k_b.is_ok() && k_c.is_ok() {
Ok(( Ok((input, Ok(Command::FanCurve { k_a: k_a.unwrap() as f32, k_b: k_b.unwrap() as f32, k_c: k_c.unwrap() as f32 })))
input,
Ok(Command::FanCurve {
k_a: k_a.unwrap() as f32,
k_b: k_b.unwrap() as f32,
k_c: k_c.unwrap() as f32,
}),
))
} else { } else {
Err(nom::Err::Incomplete(Needed::Size(3))) Err(nom::Err::Incomplete(Needed::Size(3)))
} }
@ -553,36 +580,38 @@ fn fan_curve(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
))(input)?; ))(input)?;
Ok((input, result)) Ok((input, result))
}, },
value(Err(Error::Incomplete), end), value(Err(Error::Incomplete), end)
))(input) ))(input)
} }
fn command(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> { fn command(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
alt(( alt((value(Ok(Command::Quit), tag("quit")),
value(Ok(Command::Quit), tag("quit")), load,
load, save,
save, value(Ok(Command::Reset), tag("reset")),
value(Ok(Command::Reset), tag("reset")), ipv4,
ipv4, map(report, Ok),
map(report, Ok), pwm,
pwm, center_point,
center_point, pid,
pid, steinhart_hart,
steinhart_hart, postfilter,
postfilter, value(Ok(Command::Dfu), tag("dfu")),
value(Ok(Command::Dfu), tag("dfu")), fan,
fan, fan_curve,
fan_curve, value(Ok(Command::ShowHWRev), tag("hwrev")),
value(Ok(Command::ShowHWRev), tag("hwrev")),
))(input) ))(input)
} }
impl Command { impl Command {
pub fn parse(input: &[u8]) -> Result<Self, Error> { pub fn parse(input: &[u8]) -> Result<Self, Error> {
match command(input) { match command(input) {
Ok((input_remain, result)) if input_remain.is_empty() => result, Ok((input_remain, result)) if input_remain.len() == 0 =>
Ok((input_remain, _)) => Err(Error::UnexpectedInput(input_remain[0])), result,
Err(e) => Err(e.into()), Ok((input_remain, _)) =>
Err(Error::UnexpectedInput(input_remain[0])),
Err(e) =>
Err(e.into()),
} }
} }
} }
@ -630,27 +659,21 @@ mod test {
#[test] #[test]
fn parse_ipv4() { fn parse_ipv4() {
let command = Command::parse(b"ipv4 192.168.1.26/24"); let command = Command::parse(b"ipv4 192.168.1.26/24");
assert_eq!( assert_eq!(command, Ok(Command::Ipv4(Ipv4Config {
command, address: [192, 168, 1, 26],
Ok(Command::Ipv4(Ipv4Config { mask_len: 24,
address: [192, 168, 1, 26], gateway: None,
mask_len: 24, })));
gateway: None,
}))
);
} }
#[test] #[test]
fn parse_ipv4_and_gateway() { fn parse_ipv4_and_gateway() {
let command = Command::parse(b"ipv4 10.42.0.126/8 10.1.0.1"); let command = Command::parse(b"ipv4 10.42.0.126/8 10.1.0.1");
assert_eq!( assert_eq!(command, Ok(Command::Ipv4(Ipv4Config {
command, address: [10, 42, 0, 126],
Ok(Command::Ipv4(Ipv4Config { mask_len: 8,
address: [10, 42, 0, 126], gateway: Some([10, 1, 0, 1]),
mask_len: 8, })));
gateway: Some([10, 1, 0, 1]),
}))
);
} }
#[test] #[test]
@ -680,59 +703,49 @@ mod test {
#[test] #[test]
fn parse_pwm_i_set() { fn parse_pwm_i_set() {
let command = Command::parse(b"pwm 1 i_set 16383"); let command = Command::parse(b"pwm 1 i_set 16383");
assert_eq!( assert_eq!(command, Ok(Command::Pwm {
command, channel: 1,
Ok(Command::Pwm { pin: PwmPin::ISet,
channel: 1, value: 16383.0,
pin: PwmPin::ISet, }));
value: 16383.0,
})
);
} }
#[test] #[test]
fn parse_pwm_pid() { fn parse_pwm_pid() {
let command = Command::parse(b"pwm 0 pid"); let command = Command::parse(b"pwm 0 pid");
assert_eq!(command, Ok(Command::PwmPid { channel: 0 })); assert_eq!(command, Ok(Command::PwmPid {
channel: 0,
}));
} }
#[test] #[test]
fn parse_pwm_max_i_pos() { fn parse_pwm_max_i_pos() {
let command = Command::parse(b"pwm 0 max_i_pos 7"); let command = Command::parse(b"pwm 0 max_i_pos 7");
assert_eq!( assert_eq!(command, Ok(Command::Pwm {
command, channel: 0,
Ok(Command::Pwm { pin: PwmPin::MaxIPos,
channel: 0, value: 7.0,
pin: PwmPin::MaxIPos, }));
value: 7.0,
})
);
} }
#[test] #[test]
fn parse_pwm_max_i_neg() { fn parse_pwm_max_i_neg() {
let command = Command::parse(b"pwm 0 max_i_neg 128"); let command = Command::parse(b"pwm 0 max_i_neg 128");
assert_eq!( assert_eq!(command, Ok(Command::Pwm {
command, channel: 0,
Ok(Command::Pwm { pin: PwmPin::MaxINeg,
channel: 0, value: 128.0,
pin: PwmPin::MaxINeg, }));
value: 128.0,
})
);
} }
#[test] #[test]
fn parse_pwm_max_v() { fn parse_pwm_max_v() {
let command = Command::parse(b"pwm 0 max_v 32768"); let command = Command::parse(b"pwm 0 max_v 32768");
assert_eq!( assert_eq!(command, Ok(Command::Pwm {
command, channel: 0,
Ok(Command::Pwm { pin: PwmPin::MaxV,
channel: 0, value: 32768.0,
pin: PwmPin::MaxV, }));
value: 32768.0,
})
);
} }
#[test] #[test]
@ -744,14 +757,11 @@ mod test {
#[test] #[test]
fn parse_pid_target() { fn parse_pid_target() {
let command = Command::parse(b"pid 0 target 36.5"); let command = Command::parse(b"pid 0 target 36.5");
assert_eq!( assert_eq!(command, Ok(Command::Pid {
command, channel: 0,
Ok(Command::Pid { parameter: PidParameter::Target,
channel: 0, value: 36.5,
parameter: PidParameter::Target, }));
value: 36.5,
})
);
} }
#[test] #[test]
@ -763,14 +773,11 @@ mod test {
#[test] #[test]
fn parse_steinhart_hart_set() { fn parse_steinhart_hart_set() {
let command = Command::parse(b"s-h 1 t0 23.05"); let command = Command::parse(b"s-h 1 t0 23.05");
assert_eq!( assert_eq!(command, Ok(Command::SteinhartHart {
command, channel: 1,
Ok(Command::SteinhartHart { parameter: ShParameter::T0,
channel: 1, value: 23.05,
parameter: ShParameter::T0, }));
value: 23.05,
})
);
} }
#[test] #[test]
@ -782,49 +789,37 @@ mod test {
#[test] #[test]
fn parse_postfilter_off() { fn parse_postfilter_off() {
let command = Command::parse(b"postfilter 1 off"); let command = Command::parse(b"postfilter 1 off");
assert_eq!( assert_eq!(command, Ok(Command::PostFilter {
command, channel: 1,
Ok(Command::PostFilter { rate: None,
channel: 1, }));
rate: None,
})
);
} }
#[test] #[test]
fn parse_postfilter_rate() { fn parse_postfilter_rate() {
let command = Command::parse(b"postfilter 0 rate 21"); let command = Command::parse(b"postfilter 0 rate 21");
assert_eq!( assert_eq!(command, Ok(Command::PostFilter {
command, channel: 0,
Ok(Command::PostFilter { rate: Some(21.0),
channel: 0, }));
rate: Some(21.0),
})
);
} }
#[test] #[test]
fn parse_center_point() { fn parse_center_point() {
let command = Command::parse(b"center 0 1.5"); let command = Command::parse(b"center 0 1.5");
assert_eq!( assert_eq!(command, Ok(Command::CenterPoint {
command, channel: 0,
Ok(Command::CenterPoint { center: CenterPoint::Override(1.5),
channel: 0, }));
center: CenterPoint::Override(1.5),
})
);
} }
#[test] #[test]
fn parse_center_point_vref() { fn parse_center_point_vref() {
let command = Command::parse(b"center 1 vref"); let command = Command::parse(b"center 1 vref");
assert_eq!( assert_eq!(command, Ok(Command::CenterPoint {
command, channel: 1,
Ok(Command::CenterPoint { center: CenterPoint::Vref,
channel: 1, }));
center: CenterPoint::Vref,
})
);
} }
#[test] #[test]
@ -836,7 +831,7 @@ mod test {
#[test] #[test]
fn parse_fan_set() { fn parse_fan_set() {
let command = Command::parse(b"fan 42"); let command = Command::parse(b"fan 42");
assert_eq!(command, Ok(Command::FanSet { fan_pwm: 42 })); assert_eq!(command, Ok(Command::FanSet {fan_pwm: 42}));
} }
#[test] #[test]
@ -848,14 +843,11 @@ mod test {
#[test] #[test]
fn parse_fcurve_set() { fn parse_fcurve_set() {
let command = Command::parse(b"fcurve 1.2 3.4 5.6"); let command = Command::parse(b"fcurve 1.2 3.4 5.6");
assert_eq!( assert_eq!(command, Ok(Command::FanCurve {
command, k_a: 1.2,
Ok(Command::FanCurve { k_b: 3.4,
k_a: 1.2, k_c: 5.6
k_b: 3.4, }));
k_c: 5.6
})
);
} }
#[test] #[test]

View File

@ -1,12 +1,16 @@
use crate::{ use serde::{Serialize, Deserialize};
ad7172::PostFilter, channels::Channels, command_parser::CenterPoint, pid, steinhart_hart,
};
use serde::{Deserialize, Serialize};
use uom::si::{ use uom::si::{
electric_current::ampere,
electric_potential::volt, electric_potential::volt,
electric_current::ampere,
f64::{ElectricCurrent, ElectricPotential}, f64::{ElectricCurrent, ElectricPotential},
}; };
use crate::{
ad7172::PostFilter,
channels::Channels,
command_parser::CenterPoint,
pid,
steinhart_hart,
};
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct ChannelConfig { pub struct ChannelConfig {
@ -24,9 +28,7 @@ impl ChannelConfig {
pub fn new(channels: &mut Channels, channel: usize) -> Self { pub fn new(channels: &mut Channels, channel: usize) -> Self {
let pwm = PwmLimits::new(channels, channel); let pwm = PwmLimits::new(channels, channel);
let adc_postfilter = channels let adc_postfilter = channels.adc.get_postfilter(channel as u8)
.adc
.get_postfilter(channel as u8)
.unwrap() .unwrap()
.unwrap_or(PostFilter::Invalid); .unwrap_or(PostFilter::Invalid);

View File

@ -14,7 +14,7 @@ pub unsafe fn set_dfu_trigger() {
} }
/// Called by reset handler in lib.rs immediately after reset. /// Called by reset handler in lib.rs immediately after reset.
/// This function should not be called outside of reset handler as /// This function should not be called outside of reset handler as
/// bootloader expects MCU to be in reset state when called. /// bootloader expects MCU to be in reset state when called.
#[cfg(target_arch = "arm")] #[cfg(target_arch = "arm")]
#[pre_init] #[pre_init]
@ -27,13 +27,13 @@ unsafe fn __pre_init() {
rcc.apb2enr.modify(|_, w| w.syscfgen().set_bit()); rcc.apb2enr.modify(|_, w| w.syscfgen().set_bit());
// Bypass BOOT pins and remap bootloader to 0x00000000 // Bypass BOOT pins and remap bootloader to 0x00000000
let syscfg = &*SYSCFG::ptr(); let syscfg = &*SYSCFG::ptr() ;
syscfg.memrm.write(|w| w.mem_mode().bits(0b01)); syscfg.memrm.write(|w| w.mem_mode().bits(0b01));
// Impose instruction and memory barriers // Impose instruction and memory barriers
cortex_m::asm::isb(); cortex_m::asm::isb();
cortex_m::asm::dsb(); cortex_m::asm::dsb();
asm!( asm!(
// Set stack pointer to bootloader location // Set stack pointer to bootloader location
"LDR R0, =0x1FFF0000", "LDR R0, =0x1FFF0000",

View File

@ -1,20 +1,27 @@
use crate::{command_handler::JsonBuffer, hw_rev::HWSettings};
use num_traits::Float; use num_traits::Float;
use serde::Serialize; use serde::Serialize;
use stm32f4xx_hal::{ use stm32f4xx_hal::{
pac::TIM8,
pwm::{self, PwmChannels}, pwm::{self, PwmChannels},
pac::TIM8,
};
use uom::si::{
f64::ElectricCurrent,
electric_current::ampere,
};
use crate::{
hw_rev::HWSettings,
command_handler::JsonBuffer,
}; };
use uom::si::{electric_current::ampere, f64::ElectricCurrent};
pub type FanPin = PwmChannels<TIM8, pwm::C4>; pub type FanPin = PwmChannels<TIM8, pwm::C4>;
// as stated in the schematics // as stated in the schematics
const MAX_I: f32 = 3.0; const MAX_TEC_I: f32 = 3.0;
const MAX_USER_FAN_PWM: f32 = 100.0; const MAX_USER_FAN_PWM: f32 = 100.0;
const MIN_USER_FAN_PWM: f32 = 1.0; const MIN_USER_FAN_PWM: f32 = 1.0;
pub struct FanCtrl { pub struct FanCtrl {
fan: Option<FanPin>, fan: Option<FanPin>,
fan_auto: bool, fan_auto: bool,
@ -22,7 +29,7 @@ pub struct FanCtrl {
k_a: f32, k_a: f32,
k_b: f32, k_b: f32,
k_c: f32, k_c: f32,
max_abs_i_measured: f32, abs_max_tec_i: f32,
hw_settings: HWSettings, hw_settings: HWSettings,
} }
@ -37,7 +44,7 @@ impl FanCtrl {
k_a: hw_settings.fan_k_a, k_a: hw_settings.fan_k_a,
k_b: hw_settings.fan_k_b, k_b: hw_settings.fan_k_b,
k_c: hw_settings.fan_k_c, k_c: hw_settings.fan_k_c,
max_abs_i_measured: 0f32, abs_max_tec_i: 0f32,
hw_settings, hw_settings,
}; };
if fan_ctrl.fan_auto { if fan_ctrl.fan_auto {
@ -46,14 +53,12 @@ impl FanCtrl {
fan_ctrl fan_ctrl
} }
pub fn cycle(&mut self, max_abs_i_measured: ElectricCurrent) { pub fn cycle(&mut self, abs_max_tec_i: ElectricCurrent) {
self.max_abs_i_measured = max_abs_i_measured.get::<ampere>() as f32; self.abs_max_tec_i = abs_max_tec_i.get::<ampere>() as f32;
if self.fan_auto && self.hw_settings.fan_available { if self.fan_auto && self.hw_settings.fan_available {
let scaled_current = self.max_abs_i_measured / MAX_I; let scaled_current = self.abs_max_tec_i / MAX_TEC_I;
// do not limit upper bound, as it will be limited in the set_pwm() // do not limit upper bound, as it will be limited in the set_pwm()
let pwm = (MAX_USER_FAN_PWM let pwm = (MAX_USER_FAN_PWM * (scaled_current * (scaled_current * self.k_a + self.k_b) + self.k_c)) as u32;
* (scaled_current * (scaled_current * self.k_a + self.k_b) + self.k_c))
as u32;
self.set_pwm(pwm); self.set_pwm(pwm);
} }
} }
@ -62,7 +67,7 @@ impl FanCtrl {
if self.hw_settings.fan_available { if self.hw_settings.fan_available {
let summary = FanSummary { let summary = FanSummary {
fan_pwm: self.get_pwm(), fan_pwm: self.get_pwm(),
max_abs_i_measured: self.max_abs_i_measured, abs_max_tec_i: self.abs_max_tec_i,
auto_mode: self.fan_auto, auto_mode: self.fan_auto,
k_a: self.k_a, k_a: self.k_a,
k_b: self.k_b, k_b: self.k_b,
@ -86,26 +91,18 @@ impl FanCtrl {
} }
pub fn restore_defaults(&mut self) { pub fn restore_defaults(&mut self) {
self.set_curve( self.set_curve(self.hw_settings.fan_k_a,
self.hw_settings.fan_k_a, self.hw_settings.fan_k_b,
self.hw_settings.fan_k_b, self.hw_settings.fan_k_c);
self.hw_settings.fan_k_c,
);
} }
pub fn set_pwm(&mut self, fan_pwm: u32) -> f32 { pub fn set_pwm(&mut self, fan_pwm: u32) -> f32 {
if self.fan.is_none() || (!self.pwm_enabled && !self.enable_pwm()) { if self.fan.is_none() || (!self.pwm_enabled && !self.enable_pwm()) {
return 0f32; return 0f32;
} }
let fan = self.fan.as_mut().unwrap(); let fan = self.fan.as_mut().unwrap();
let fan_pwm = fan_pwm.clamp(MIN_USER_FAN_PWM as u32, MAX_USER_FAN_PWM as u32); let fan_pwm = fan_pwm.min(MAX_USER_FAN_PWM as u32).max(MIN_USER_FAN_PWM as u32);
let duty = scale_number( let duty = scale_number(fan_pwm as f32, self.hw_settings.min_fan_pwm, self.hw_settings.max_fan_pwm, MIN_USER_FAN_PWM, MAX_USER_FAN_PWM);
fan_pwm as f32,
self.hw_settings.min_fan_pwm,
self.hw_settings.max_fan_pwm,
MIN_USER_FAN_PWM,
MAX_USER_FAN_PWM,
);
let max = fan.get_max_duty(); let max = fan.get_max_duty();
let value = ((duty * (max as f32)) as u16).min(max); let value = ((duty * (max as f32)) as u16).min(max);
fan.set_duty(value); fan.set_duty(value);
@ -124,17 +121,8 @@ impl FanCtrl {
if let Some(fan) = &self.fan { if let Some(fan) = &self.fan {
let duty = fan.get_duty(); let duty = fan.get_duty();
let max = fan.get_max_duty(); let max = fan.get_max_duty();
scale_number( scale_number(duty as f32 / (max as f32), MIN_USER_FAN_PWM, MAX_USER_FAN_PWM, self.hw_settings.min_fan_pwm, self.hw_settings.max_fan_pwm).round() as u32
duty as f32 / (max as f32), } else { 0 }
MIN_USER_FAN_PWM,
MAX_USER_FAN_PWM,
self.hw_settings.min_fan_pwm,
self.hw_settings.max_fan_pwm,
)
.round() as u32
} else {
0
}
} }
fn enable_pwm(&mut self) -> bool { fn enable_pwm(&mut self) -> bool {
@ -150,6 +138,7 @@ impl FanCtrl {
} }
} }
fn scale_number(unscaled: f32, to_min: f32, to_max: f32, from_min: f32, from_max: f32) -> f32 { fn scale_number(unscaled: f32, to_min: f32, to_max: f32, from_min: f32, from_max: f32) -> f32 {
(to_max - to_min) * (unscaled - from_min) / (from_max - from_min) + to_min (to_max - to_min) * (unscaled - from_min) / (from_max - from_min) + to_min
} }
@ -157,7 +146,7 @@ fn scale_number(unscaled: f32, to_min: f32, to_max: f32, from_min: f32, from_max
#[derive(Serialize)] #[derive(Serialize)]
pub struct FanSummary { pub struct FanSummary {
fan_pwm: u32, fan_pwm: u32,
max_abs_i_measured: f32, abs_max_tec_i: f32,
auto_mode: bool, auto_mode: bool,
k_a: f32, k_a: f32,
k_b: f32, k_b: f32,

View File

@ -1,9 +1,9 @@
use log::{error, info}; use log::{info, error};
use sfkv::{Store, StoreBackend};
use stm32f4xx_hal::{ use stm32f4xx_hal::{
flash::{Error, FlashExt}, flash::{Error, FlashExt},
stm32::FLASH, stm32::FLASH,
}; };
use sfkv::{Store, StoreBackend};
/// 16 KiB /// 16 KiB
pub const FLASH_SECTOR_SIZE: usize = 0x4000; pub const FLASH_SECTOR_SIZE: usize = 0x4000;
@ -21,7 +21,9 @@ pub struct FlashBackend {
} }
fn get_offset() -> usize { fn get_offset() -> usize {
unsafe { (&_config_start as *const usize as usize) - (&_flash_start as *const usize as usize) } unsafe {
(&_config_start as *const usize as usize) - (&_flash_start as *const usize as usize)
}
} }
impl StoreBackend for FlashBackend { impl StoreBackend for FlashBackend {
@ -38,8 +40,7 @@ impl StoreBackend for FlashBackend {
} }
fn program(&mut self, offset: usize, payload: &[u8]) -> Result<(), Self::Error> { fn program(&mut self, offset: usize, payload: &[u8]) -> Result<(), Self::Error> {
self.flash self.flash.unlocked()
.unlocked()
.program(get_offset() + offset, payload.iter()) .program(get_offset() + offset, payload.iter())
} }
@ -59,8 +60,7 @@ pub fn store(flash: FLASH) -> FlashStore {
Ok(_) => {} Ok(_) => {}
Err(e) => { Err(e) => {
error!("corrupt store, erasing. error: {:?}", e); error!("corrupt store, erasing. error: {:?}", e);
let _ = store let _ = store.erase()
.erase()
.map_err(|e| error!("flash erase failed: {:?}", e)); .map_err(|e| error!("flash erase failed: {:?}", e));
} }
} }

View File

@ -1,6 +1,9 @@
use serde::Serialize; use serde::Serialize;
use crate::{command_handler::JsonBuffer, pins::HWRevPins}; use crate::{
pins::HWRevPins,
command_handler::JsonBuffer,
};
#[derive(Serialize, Copy, Clone)] #[derive(Serialize, Copy, Clone)]
pub struct HWRev { pub struct HWRev {
@ -28,17 +31,13 @@ struct HWSummary<'a> {
impl HWRev { impl HWRev {
pub fn detect_hw_rev(hwrev_pins: &HWRevPins) -> Self { pub fn detect_hw_rev(hwrev_pins: &HWRevPins) -> Self {
let (h0, h1, h2, h3) = ( let (h0, h1, h2, h3) = (hwrev_pins.hwrev0.is_high(), hwrev_pins.hwrev1.is_high(),
hwrev_pins.hwrev0.is_high(), hwrev_pins.hwrev2.is_high(), hwrev_pins.hwrev3.is_high());
hwrev_pins.hwrev1.is_high(),
hwrev_pins.hwrev2.is_high(),
hwrev_pins.hwrev3.is_high(),
);
match (h0, h1, h2, h3) { match (h0, h1, h2, h3) {
(true, true, true, false) => HWRev { major: 1, minor: 0 }, (true, true, true, false) => HWRev { major: 1, minor: 0 },
(true, false, false, false) => HWRev { major: 2, minor: 0 }, (true, false, false, false) => HWRev { major: 2, minor: 0 },
(false, true, false, false) => HWRev { major: 2, minor: 2 }, (false, true, false, false) => HWRev { major: 2, minor: 2 },
(_, _, _, _) => HWRev { major: 0, minor: 0 }, (_, _, _, _) => HWRev { major: 0, minor: 0 }
} }
} }
@ -71,16 +70,13 @@ impl HWRev {
fan_pwm_freq_hz: 0, fan_pwm_freq_hz: 0,
fan_available: false, fan_available: false,
fan_pwm_recommended: false, fan_pwm_recommended: false,
}, }
} }
} }
pub fn summary(&self) -> Result<JsonBuffer, serde_json_core::ser::Error> { pub fn summary(&self) -> Result<JsonBuffer, serde_json_core::ser::Error> {
let settings = self.settings(); let settings = self.settings();
let summary = HWSummary { let summary = HWSummary { rev: self, settings: &settings };
rev: self,
settings: &settings,
};
serde_json_core::to_vec(&summary) serde_json_core::to_vec(&summary)
} }
} }

View File

@ -10,15 +10,17 @@ pub fn init_log() {
#[cfg(feature = "semihosting")] #[cfg(feature = "semihosting")]
pub fn init_log() { pub fn init_log() {
use cortex_m_log::log::{init, Logger};
use cortex_m_log::printer::semihosting::{hio::HStdout, InterruptOk};
use log::LevelFilter; use log::LevelFilter;
use cortex_m_log::log::{Logger, init};
use cortex_m_log::printer::semihosting::{InterruptOk, hio::HStdout};
static mut LOGGER: Option<Logger<InterruptOk<HStdout>>> = None; static mut LOGGER: Option<Logger<InterruptOk<HStdout>>> = None;
let logger = Logger { let logger = Logger {
inner: InterruptOk::<_>::stdout().expect("semihosting stdout"), inner: InterruptOk::<_>::stdout().expect("semihosting stdout"),
level: LevelFilter::Info, level: LevelFilter::Info,
}; };
let logger = unsafe { LOGGER.get_or_insert(logger) }; let logger = unsafe {
LOGGER.get_or_insert(logger)
};
init(logger).expect("set logger"); init(logger).expect("set logger");
} }

View File

@ -1,6 +1,6 @@
use stm32f4xx_hal::{ use stm32f4xx_hal::{
gpio::{ gpio::{
gpiod::{PD10, PD11, PD9}, gpiod::{PD9, PD10, PD11},
Output, PushPull, Output, PushPull,
}, },
hal::digital::v2::OutputPin, hal::digital::v2::OutputPin,

View File

@ -8,26 +8,30 @@ use panic_halt as _;
#[cfg(all(feature = "semihosting", not(test)))] #[cfg(all(feature = "semihosting", not(test)))]
use panic_semihosting as _; use panic_semihosting as _;
use log::{error, info, warn};
use cortex_m::asm::wfi; use cortex_m::asm::wfi;
use cortex_m_rt::entry; use cortex_m_rt::entry;
use log::{error, info, warn};
use smoltcp::{socket::TcpSocket, time::Instant, wire::EthernetAddress};
use stm32f4xx_hal::{ use stm32f4xx_hal::{
hal::watchdog::{Watchdog, WatchdogEnable}, hal::watchdog::{WatchdogEnable, Watchdog},
rcc::RccExt, rcc::RccExt,
stm32::{CorePeripherals, Peripherals, SCB}, stm32::{CorePeripherals, Peripherals, SCB},
time::{MegaHertz, U32Ext}, time::{U32Ext, MegaHertz},
watchdog::IndependentWatchdog, watchdog::IndependentWatchdog,
}; };
use smoltcp::{
time::Instant,
socket::TcpSocket,
wire::EthernetAddress,
};
mod init_log; mod init_log;
use init_log::init_log; use init_log::init_log;
mod usb;
mod leds; mod leds;
mod pins; mod pins;
mod usb;
use pins::Pins; use pins::Pins;
mod ad5680;
mod ad7172; mod ad7172;
mod ad5680;
mod net; mod net;
mod server; mod server;
use server::Server; use server::Server;
@ -35,18 +39,18 @@ mod session;
use session::{Session, SessionInput}; use session::{Session, SessionInput};
mod command_parser; mod command_parser;
use command_parser::Ipv4Config; use command_parser::Ipv4Config;
mod channels; mod timer;
mod pid; mod pid;
mod steinhart_hart; mod steinhart_hart;
mod timer; mod channels;
use channels::{Channels, CHANNELS}; use channels::{CHANNELS, Channels};
mod channel; mod channel;
mod channel_state; mod channel_state;
mod config; mod config;
use config::ChannelConfig; use config::ChannelConfig;
mod command_handler;
mod dfu;
mod flash_store; mod flash_store;
mod dfu;
mod command_handler;
use command_handler::Handler; use command_handler::Handler;
mod fan_ctrl; mod fan_ctrl;
use fan_ctrl::FanCtrl; use fan_ctrl::FanCtrl;
@ -69,19 +73,19 @@ fn send_line(socket: &mut TcpSocket, data: &[u8]) -> bool {
// instead of sending incomplete line // instead of sending incomplete line
warn!( warn!(
"TCP socket has only {}/{} needed {}", "TCP socket has only {}/{} needed {}",
send_free + 1, send_free + 1, socket.send_capacity(), data.len(),
socket.send_capacity(),
data.len(),
); );
} else { } else {
match socket.send_slice(data) { match socket.send_slice(&data) {
Ok(sent) if sent == data.len() => { Ok(sent) if sent == data.len() => {
let _ = socket.send_slice(b"\n"); let _ = socket.send_slice(b"\n");
// success // success
return true; return true
} }
Ok(sent) => warn!("sent only {}/{} bytes", sent, data.len()), Ok(sent) =>
Err(e) => error!("error sending line: {:?}", e), warn!("sent only {}/{} bytes", sent, data.len()),
Err(e) =>
error!("error sending line: {:?}", e),
} }
} }
// not success // not success
@ -100,9 +104,7 @@ fn main() -> ! {
cp.SCB.enable_dcache(&mut cp.CPUID); cp.SCB.enable_dcache(&mut cp.CPUID);
let dp = Peripherals::take().unwrap(); let dp = Peripherals::take().unwrap();
let clocks = dp let clocks = dp.RCC.constrain()
.RCC
.constrain()
.cfgr .cfgr
.use_hse(HSE) .use_hse(HSE)
.sysclk(168.mhz()) .sysclk(168.mhz())
@ -118,21 +120,10 @@ fn main() -> ! {
timer::setup(cp.SYST, clocks); timer::setup(cp.SYST, clocks);
let (pins, mut leds, mut eeprom, eth_pins, usb, fan, hwrev, hw_settings) = Pins::setup( let (pins, mut leds, mut eeprom, eth_pins, usb, fan, hwrev, hw_settings) = Pins::setup(
clocks, clocks, dp.TIM1, dp.TIM3, dp.TIM8,
dp.TIM1, dp.GPIOA, dp.GPIOB, dp.GPIOC, dp.GPIOD, dp.GPIOE, dp.GPIOF, dp.GPIOG,
dp.TIM3,
dp.TIM8,
dp.GPIOA,
dp.GPIOB,
dp.GPIOC,
dp.GPIOD,
dp.GPIOE,
dp.GPIOF,
dp.GPIOG,
dp.I2C1, dp.I2C1,
dp.SPI2, dp.SPI2, dp.SPI4, dp.SPI5,
dp.SPI4,
dp.SPI5,
dp.ADC1, dp.ADC1,
dp.OTG_FS_GLOBAL, dp.OTG_FS_GLOBAL,
dp.OTG_FS_DEVICE, dp.OTG_FS_DEVICE,
@ -150,9 +141,12 @@ fn main() -> ! {
let mut channels = Channels::new(pins, &hwrev); let mut channels = Channels::new(pins, &hwrev);
for c in 0..CHANNELS { for c in 0..CHANNELS {
match store.read_value::<ChannelConfig>(CHANNEL_CONFIG_KEY[c]) { match store.read_value::<ChannelConfig>(CHANNEL_CONFIG_KEY[c]) {
Ok(Some(config)) => config.apply(&mut channels, c), Ok(Some(config)) =>
Ok(None) => error!("flash config not found for channel {}", c), config.apply(&mut channels, c),
Err(e) => error!("unable to load config {} from flash: {:?}", c, e), Ok(None) =>
error!("flash config not found for channel {}", c),
Err(e) =>
error!("unable to load config {} from flash: {:?}", c, e),
} }
} }
@ -165,9 +159,11 @@ fn main() -> ! {
gateway: None, gateway: None,
}; };
match store.read_value("ipv4") { match store.read_value("ipv4") {
Ok(Some(config)) => ipv4_config = config, Ok(Some(config)) =>
ipv4_config = config,
Ok(None) => {} Ok(None) => {}
Err(e) => error!("cannot read ipv4 config: {:?}", e), Err(e) =>
error!("cannot read ipv4 config: {:?}", e),
} }
// EEPROM ships with a read-only EUI-48 identifier // EEPROM ships with a read-only EUI-48 identifier
@ -176,131 +172,118 @@ fn main() -> ! {
let hwaddr = EthernetAddress(eui48); let hwaddr = EthernetAddress(eui48);
info!("EEPROM MAC address: {}", hwaddr); info!("EEPROM MAC address: {}", hwaddr);
net::run( net::run(clocks, dp.ETHERNET_MAC, dp.ETHERNET_DMA, eth_pins, hwaddr, ipv4_config.clone(), |iface| {
clocks, Server::<Session>::run(iface, |server| {
dp.ETHERNET_MAC, leds.r1.off();
dp.ETHERNET_DMA, let mut should_reset = false;
eth_pins,
hwaddr,
ipv4_config.clone(),
|iface| {
Server::<Session>::run(iface, |server| {
leds.r1.off();
let mut should_reset = false;
loop { loop {
let mut new_ipv4_config = None; let mut new_ipv4_config = None;
let instant = Instant::from_millis(i64::from(timer::now())); let instant = Instant::from_millis(i64::from(timer::now()));
let updated_channel = channels.poll_adc(instant); let updated_channel = channels.poll_adc(instant);
if let Some(channel) = updated_channel { if let Some(channel) = updated_channel {
server.for_each(|_, session| session.set_report_pending(channel.into())); server.for_each(|_, session| session.set_report_pending(channel.into()));
} }
fan_ctrl.cycle(channels.max_abs_i_measured()); fan_ctrl.cycle(channels.current_abs_max_tec_i());
if channels.pid_engaged() { if channels.pid_engaged() {
leds.g3.on(); leds.g3.on();
} else { } else {
leds.g3.off(); leds.g3.off();
} }
let instant = Instant::from_millis(i64::from(timer::now())); let instant = Instant::from_millis(i64::from(timer::now()));
cortex_m::interrupt::free(net::clear_pending); cortex_m::interrupt::free(net::clear_pending);
server.poll(instant).unwrap_or_else(|e| { server.poll(instant)
.unwrap_or_else(|e| {
warn!("poll: {:?}", e); warn!("poll: {:?}", e);
}); });
if !should_reset { if ! should_reset {
// TCP protocol handling // TCP protocol handling
server.for_each(|mut socket, session| { server.for_each(|mut socket, session| {
if !socket.is_active() { if ! socket.is_active() {
let _ = socket.listen(TCP_PORT); let _ = socket.listen(TCP_PORT);
session.reset(); session.reset();
} else if socket.may_send() && !socket.may_recv() { } else if socket.may_send() && !socket.may_recv() {
socket.close() socket.close()
} else if socket.can_send() && socket.can_recv() { } else if socket.can_send() && socket.can_recv() {
match socket.recv(|buf| session.feed(buf)) { match socket.recv(|buf| session.feed(buf)) {
// SessionInput::Nothing happens when the line reader parses a string of characters that is not // SessionInput::Nothing happens when the line reader parses a string of characters that is not
// followed by a newline character. Could be due to partial commands not terminated with newline, // followed by a newline character. Could be due to partial commands not terminated with newline,
// socket RX ring buffer wraps around, or when the command is sent as seperate TCP packets etc. // socket RX ring buffer wraps around, or when the command is sent as seperate TCP packets etc.
// Do nothing and feed more data to the line reader in the next loop cycle. // Do nothing and feed more data to the line reader in the next loop cycle.
Ok(SessionInput::Nothing) => {} Ok(SessionInput::Nothing) => {}
Ok(SessionInput::Command(command)) => { Ok(SessionInput::Command(command)) => {
match Handler::handle_command( match Handler::handle_command(command, &mut socket, &mut channels, session, &mut store, &mut ipv4_config, &mut fan_ctrl, hwrev) {
command, Ok(Handler::NewIPV4(ip)) => new_ipv4_config = Some(ip),
&mut socket, Ok(Handler::Handled) => {},
&mut channels, Ok(Handler::CloseSocket) => socket.close(),
session, Ok(Handler::Reset) => should_reset = true,
&mut store, Err(_) => {},
&mut ipv4_config,
&mut fan_ctrl,
hwrev,
) {
Ok(Handler::NewIPV4(ip)) => new_ipv4_config = Some(ip),
Ok(Handler::Handled) => {}
Ok(Handler::CloseSocket) => socket.close(),
Ok(Handler::Reset) => should_reset = true,
Err(_) => {}
}
} }
Ok(SessionInput::Error(e)) => {
error!("session input: {:?}", e);
send_line(&mut socket, b"{ \"error\": \"invalid input\" }");
}
Err(_) => socket.close(),
} }
} else if socket.can_send() { Ok(SessionInput::Error(e)) => {
if let Some(channel) = session.is_report_pending() { error!("session input: {:?}", e);
match channels.reports_json() { send_line(&mut socket, b"{ \"error\": \"invalid input\" }");
Ok(buf) => { }
send_line(&mut socket, &buf[..]); Err(_) =>
session.mark_report_sent(channel); socket.close(),
} }
Err(e) => { } else if socket.can_send() {
error!("unable to serialize report: {:?}", e); if let Some(channel) = session.is_report_pending() {
} match channels.reports_json() {
Ok(buf) => {
send_line(&mut socket, &buf[..]);
session.mark_report_sent(channel);
}
Err(e) => {
error!("unable to serialize report: {:?}", e);
} }
} }
} }
});
} else {
// Should reset, close all TCP sockets.
let mut any_socket_alive = false;
server.for_each(|mut socket, _| {
if socket.is_active() {
socket.abort();
any_socket_alive = true;
}
});
// Must let loop run for one more cycle to poll server for RST to be sent,
// this makes sure system does not reset right after socket.abort() is called.
if !any_socket_alive {
SCB::sys_reset();
}
}
// Apply new IPv4 address/gateway
if let Some(config) = new_ipv4_config.take() {
server.set_ipv4_config(config.clone());
ipv4_config = config;
}
// Update watchdog
wd.feed();
leds.g4.off();
cortex_m::interrupt::free(|cs| {
if !net::is_pending(cs) {
// Wait for interrupts
// (Ethernet, SysTick, or USB)
wfi();
} }
}); });
leds.g4.on(); } else {
// Should reset, close all TCP sockets.
let mut any_socket_alive = false;
server.for_each(|mut socket, _| {
if socket.is_active() {
socket.abort();
any_socket_alive = true;
}
});
// Must let loop run for one more cycle to poll server for RST to be sent,
// this makes sure system does not reset right after socket.abort() is called.
if !any_socket_alive {
SCB::sys_reset();
}
} }
});
}, // Apply new IPv4 address/gateway
); new_ipv4_config.take()
.map(|config| {
server.set_ipv4_config(config.clone());
ipv4_config = config;
});
// Update watchdog
wd.feed();
leds.g4.off();
cortex_m::interrupt::free(|cs| {
if !net::is_pending(cs) {
// Wait for interrupts
// (Ethernet, SysTick, or USB)
wfi();
}
});
leds.g4.on();
}
});
});
unreachable!() unreachable!()
} }

View File

@ -1,17 +1,20 @@
//! As there is only one peripheral, supporting data structures are //! As there is only one peripheral, supporting data structures are
//! declared once and globally. //! declared once and globally.
use crate::command_parser::Ipv4Config;
use crate::pins::EthernetPins;
use core::cell::RefCell; use core::cell::RefCell;
use cortex_m::interrupt::{CriticalSection, Mutex}; use cortex_m::interrupt::{CriticalSection, Mutex};
use smoltcp::iface::{EthernetInterface, EthernetInterfaceBuilder, NeighborCache, Routes};
use smoltcp::wire::{EthernetAddress, Ipv4Address, Ipv4Cidr};
use stm32_eth::{Eth, RingEntry, RxDescriptor, TxDescriptor};
use stm32f4xx_hal::{ use stm32f4xx_hal::{
pac::{interrupt, Peripherals, ETHERNET_DMA, ETHERNET_MAC},
rcc::Clocks, rcc::Clocks,
pac::{interrupt, Peripherals, ETHERNET_MAC, ETHERNET_DMA},
}; };
use smoltcp::wire::{EthernetAddress, Ipv4Address, Ipv4Cidr};
use smoltcp::iface::{
EthernetInterfaceBuilder, EthernetInterface,
NeighborCache, Routes,
};
use stm32_eth::{Eth, RingEntry, RxDescriptor, TxDescriptor};
use crate::command_parser::Ipv4Config;
use crate::pins::EthernetPins;
/// Not on the stack so that stack can be placed in CCMRAM (which the /// Not on the stack so that stack can be placed in CCMRAM (which the
/// ethernet peripheral cannot access) /// ethernet peripheral cannot access)
@ -27,27 +30,27 @@ static NET_PENDING: Mutex<RefCell<bool>> = Mutex::new(RefCell::new(false));
/// Run callback `f` with ethernet driver and TCP/IP stack /// Run callback `f` with ethernet driver and TCP/IP stack
pub fn run<F>( pub fn run<F>(
clocks: Clocks, clocks: Clocks,
ethernet_mac: ETHERNET_MAC, ethernet_mac: ETHERNET_MAC, ethernet_dma: ETHERNET_DMA,
ethernet_dma: ETHERNET_DMA,
eth_pins: EthernetPins, eth_pins: EthernetPins,
ethernet_addr: EthernetAddress, ethernet_addr: EthernetAddress,
ipv4_config: Ipv4Config, ipv4_config: Ipv4Config,
f: F, f: F
) where ) where
F: FnOnce(EthernetInterface<&mut stm32_eth::Eth<'static, 'static>>), F: FnOnce(EthernetInterface<&mut stm32_eth::Eth<'static, 'static>>),
{ {
let rx_ring = unsafe { RX_RING.get_or_insert(Default::default()) }; let rx_ring = unsafe {
let tx_ring = unsafe { TX_RING.get_or_insert(Default::default()) }; RX_RING.get_or_insert(Default::default())
};
let tx_ring = unsafe {
TX_RING.get_or_insert(Default::default())
};
// Ethernet driver // Ethernet driver
let mut eth_dev = Eth::new( let mut eth_dev = Eth::new(
ethernet_mac, ethernet_mac, ethernet_dma,
ethernet_dma, &mut rx_ring[..], &mut tx_ring[..],
&mut rx_ring[..],
&mut tx_ring[..],
clocks, clocks,
eth_pins, eth_pins,
) ).unwrap();
.unwrap();
eth_dev.enable_interrupt(); eth_dev.enable_interrupt();
// IP stack // IP stack
@ -73,7 +76,8 @@ pub fn run<F>(
#[interrupt] #[interrupt]
fn ETH() { fn ETH() {
cortex_m::interrupt::free(|cs| { cortex_m::interrupt::free(|cs| {
*NET_PENDING.borrow(cs).borrow_mut() = true; *NET_PENDING.borrow(cs)
.borrow_mut() = true;
}); });
let p = unsafe { Peripherals::steal() }; let p = unsafe { Peripherals::steal() };
@ -82,13 +86,15 @@ fn ETH() {
/// Has an interrupt occurred since last call to `clear_pending()`? /// Has an interrupt occurred since last call to `clear_pending()`?
pub fn is_pending(cs: &CriticalSection) -> bool { pub fn is_pending(cs: &CriticalSection) -> bool {
*NET_PENDING.borrow(cs).borrow() *NET_PENDING.borrow(cs)
.borrow()
} }
/// Clear the interrupt pending flag before polling the interface for /// Clear the interrupt pending flag before polling the interface for
/// data. /// data.
pub fn clear_pending(cs: &CriticalSection) { pub fn clear_pending(cs: &CriticalSection) {
*NET_PENDING.borrow(cs).borrow_mut() = false; *NET_PENDING.borrow(cs)
.borrow_mut() = false;
} }
/// utility for destructuring into smoltcp types /// utility for destructuring into smoltcp types

View File

@ -1,4 +1,4 @@
use serde::{Deserialize, Serialize}; use serde::{Serialize, Deserialize};
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Parameters { pub struct Parameters {
@ -29,39 +29,40 @@ impl Default for Parameters {
#[derive(Clone)] #[derive(Clone)]
pub struct Controller { pub struct Controller {
pub parameters: Parameters, pub parameters: Parameters,
pub target: f64, pub target : f64,
u1: f64, u1 : f64,
x1: f64, x1 : f64,
x2: f64, x2 : f64,
pub y1: f64, pub y1 : f64,
} }
impl Controller { impl Controller {
pub const fn new(parameters: Parameters) -> Controller { pub const fn new(parameters: Parameters) -> Controller {
Controller { Controller {
parameters, parameters: parameters,
target: 0.0, target : 0.0,
u1: 0.0, u1 : 0.0,
x1: 0.0, x1 : 0.0,
x2: 0.0, x2 : 0.0,
y1: 0.0, y1 : 0.0,
} }
} }
// Based on https://hackmd.io/IACbwcOTSt6Adj3_F9bKuw PID implementation // Based on https://hackmd.io/IACbwcOTSt6Adj3_F9bKuw PID implementation
// Input x(t), target u(t), output y(t) // Input x(t), target u(t), output y(t)
// y0' = y1 - ki * u0 // y0' = y1 - ki * u0
// + x0 * (kp + ki + kd) // + x0 * (kp + ki + kd)
// - x1 * (kp + 2kd) // - x1 * (kp + 2kd)
// + x2 * kd // + x2 * kd
// + kp * (u0 - u1) // + kp * (u0 - u1)
// y0 = clip(y0', ymin, ymax) // y0 = clip(y0', ymin, ymax)
pub fn update(&mut self, input: f64) -> f64 { pub fn update(&mut self, input: f64) -> f64 {
let mut output: f64 = self.y1 - self.target * f64::from(self.parameters.ki) let mut output: f64 = self.y1 - self.target * f64::from(self.parameters.ki)
+ input * f64::from(self.parameters.kp + self.parameters.ki + self.parameters.kd) + input * f64::from(self.parameters.kp + self.parameters.ki + self.parameters.kd)
- self.x1 * f64::from(self.parameters.kp + 2.0 * self.parameters.kd) - self.x1 * f64::from(self.parameters.kp + 2.0 * self.parameters.kd)
+ self.x2 * f64::from(self.parameters.kd) + self.x2 * f64::from(self.parameters.kd)
+ f64::from(self.parameters.kp) * (self.target - self.u1); + f64::from(self.parameters.kp) * (self.target - self.u1);
if output < self.parameters.output_min.into() { if output < self.parameters.output_min.into() {
output = self.parameters.output_min.into(); output = self.parameters.output_min.into();
} }
@ -71,7 +72,7 @@ impl Controller {
self.x2 = self.x1; self.x2 = self.x1;
self.x1 = input; self.x1 = input;
self.u1 = self.target; self.u1 = self.target;
self.y1 = output; self.y1 = output;
output output
} }
@ -110,17 +111,17 @@ mod test {
#[test] #[test]
fn test_controller() { fn test_controller() {
// Initial and ambient temperature // Initial and ambient temperature
const DEFAULT: f64 = 20.0; const DEFAULT: f64 = 20.0;
// Target temperature // Target temperature
const TARGET: f64 = 40.0; const TARGET: f64 = 40.0;
// Control tolerance // Control tolerance
const ERROR: f64 = 0.01; const ERROR: f64 = 0.01;
// System response delay // System response delay
const DELAY: usize = 10; const DELAY: usize = 10;
// Heat lost // Heat lost
const LOSS: f64 = 0.05; const LOSS: f64 = 0.05;
// Limit simulation cycle, reaching this limit before settling fails test // Limit simulation cycle, reaching this limit before settling fails test
const CYCLE_LIMIT: u32 = 1000; const CYCLE_LIMIT: u32 = 1000;
let mut pid = Controller::new(PARAMETERS.clone()); let mut pid = Controller::new(PARAMETERS.clone());
pid.target = TARGET; pid.target = TARGET;

View File

@ -1,41 +1,49 @@
use crate::{
channel::{Channel0, Channel1},
fan_ctrl::FanPin,
hw_rev::{HWRev, HWSettings},
leds::Leds,
};
use eeprom24x::{self, Eeprom24x};
use stm32_eth::EthPins;
use stm32f4xx_hal::{ use stm32f4xx_hal::{
adc::Adc, adc::Adc,
gpio::{ gpio::{
gpioa::*, gpiob::*, gpioc::*, gpioe::*, gpiof::*, gpiog::*, Alternate, AlternateOD, Analog, AF5, Alternate, AlternateOD, Analog, Floating, Input,
Floating, GpioExt, Input, Output, PushPull, AF5, gpioa::*,
gpiob::*,
gpioc::*,
gpioe::*,
gpiof::*,
gpiog::*,
GpioExt,
Output, PushPull,
}, },
hal::{self, blocking::spi::Transfer, digital::v2::OutputPin}, hal::{self, blocking::spi::Transfer, digital::v2::OutputPin},
i2c::I2c, i2c::I2c,
otg_fs::USB, otg_fs::USB,
pac::{
ADC1, GPIOA, GPIOB, GPIOC, GPIOD, GPIOE, GPIOF, GPIOG, I2C1, OTG_FS_DEVICE, OTG_FS_GLOBAL,
OTG_FS_PWRCLK, SPI2, SPI4, SPI5, TIM1, TIM3, TIM8,
},
pwm::{self, PwmChannels},
rcc::Clocks, rcc::Clocks,
spi::{NoMiso, Spi, TransferModeNormal}, pwm::{self, PwmChannels},
time::U32Ext, spi::{Spi, NoMiso, TransferModeNormal},
pac::{
ADC1,
GPIOA, GPIOB, GPIOC, GPIOD, GPIOE, GPIOF, GPIOG,
I2C1,
OTG_FS_GLOBAL, OTG_FS_DEVICE, OTG_FS_PWRCLK,
SPI2, SPI4, SPI5,
TIM1, TIM3, TIM8
},
timer::Timer, timer::Timer,
time::U32Ext,
};
use eeprom24x::{self, Eeprom24x};
use stm32_eth::EthPins;
use crate::{
channel::{Channel0, Channel1},
leds::Leds,
fan_ctrl::FanPin,
hw_rev::{HWRev, HWSettings},
}; };
pub type Eeprom = Eeprom24x< pub type Eeprom = Eeprom24x<
I2c< I2c<I2C1, (
I2C1, PB8<AlternateOD<{ stm32f4xx_hal::gpio::AF4 }>>,
( PB9<AlternateOD<{ stm32f4xx_hal::gpio::AF4 }>>
PB8<AlternateOD<{ stm32f4xx_hal::gpio::AF4 }>>, )>,
PB9<AlternateOD<{ stm32f4xx_hal::gpio::AF4 }>>,
),
>,
eeprom24x::page_size::B8, eeprom24x::page_size::B8,
eeprom24x::addr_size::OneByte, eeprom24x::addr_size::OneByte
>; >;
pub type EthernetPins = EthPins< pub type EthernetPins = EthPins<
@ -46,7 +54,7 @@ pub type EthernetPins = EthPins<
PB13<Input<Floating>>, PB13<Input<Floating>>,
PC4<Input<Floating>>, PC4<Input<Floating>>,
PC5<Input<Floating>>, PC5<Input<Floating>>,
>; >;
pub trait ChannelPins { pub trait ChannelPins {
type DacSpi: Transfer<u8>; type DacSpi: Transfer<u8>;
@ -79,15 +87,7 @@ impl ChannelPins for Channel1 {
} }
/// SPI peripheral used for communication with the ADC /// SPI peripheral used for communication with the ADC
pub type AdcSpi = Spi< pub type AdcSpi = Spi<SPI2, (PB10<Alternate<AF5>>, PB14<Alternate<AF5>>, PB15<Alternate<AF5>>), TransferModeNormal>;
SPI2,
(
PB10<Alternate<AF5>>,
PB14<Alternate<AF5>>,
PB15<Alternate<AF5>>,
),
TransferModeNormal,
>;
pub type AdcNss = PB12<Output<PushPull>>; pub type AdcNss = PB12<Output<PushPull>>;
type Dac0Spi = Spi<SPI4, (PE2<Alternate<AF5>>, NoMiso, PE6<Alternate<AF5>>), TransferModeNormal>; type Dac0Spi = Spi<SPI4, (PE2<Alternate<AF5>>, NoMiso, PE6<Alternate<AF5>>), TransferModeNormal>;
type Dac1Spi = Spi<SPI5, (PF7<Alternate<AF5>>, NoMiso, PF9<Alternate<AF5>>), TransferModeNormal>; type Dac1Spi = Spi<SPI5, (PF7<Alternate<AF5>>, NoMiso, PF9<Alternate<AF5>>), TransferModeNormal>;
@ -123,34 +123,13 @@ impl Pins {
/// Setup GPIO pins and configure MCU peripherals /// Setup GPIO pins and configure MCU peripherals
pub fn setup( pub fn setup(
clocks: Clocks, clocks: Clocks,
tim1: TIM1, tim1: TIM1, tim3: TIM3, tim8: TIM8,
tim3: TIM3, gpioa: GPIOA, gpiob: GPIOB, gpioc: GPIOC, gpiod: GPIOD, gpioe: GPIOE, gpiof: GPIOF, gpiog: GPIOG,
tim8: TIM8,
gpioa: GPIOA,
gpiob: GPIOB,
gpioc: GPIOC,
gpiod: GPIOD,
gpioe: GPIOE,
gpiof: GPIOF,
gpiog: GPIOG,
i2c1: I2C1, i2c1: I2C1,
spi2: SPI2, spi2: SPI2, spi4: SPI4, spi5: SPI5,
spi4: SPI4,
spi5: SPI5,
adc1: ADC1, adc1: ADC1,
otg_fs_global: OTG_FS_GLOBAL, otg_fs_global: OTG_FS_GLOBAL, otg_fs_device: OTG_FS_DEVICE, otg_fs_pwrclk: OTG_FS_PWRCLK,
otg_fs_device: OTG_FS_DEVICE, ) -> (Self, Leds, Eeprom, EthernetPins, USB, Option<FanPin>, HWRev, HWSettings) {
otg_fs_pwrclk: OTG_FS_PWRCLK,
) -> (
Self,
Leds,
Eeprom,
EthernetPins,
USB,
Option<FanPin>,
HWRev,
HWSettings,
) {
let gpioa = gpioa.split(); let gpioa = gpioa.split();
let gpiob = gpiob.split(); let gpiob = gpiob.split();
let gpioc = gpioc.split(); let gpioc = gpioc.split();
@ -165,12 +144,18 @@ impl Pins {
let pins_adc = Adc::adc1(adc1, true, Default::default()); let pins_adc = Adc::adc1(adc1, true, Default::default());
let pwm = PwmPins::setup( let pwm = PwmPins::setup(
clocks, tim1, tim3, gpioc.pc6, gpioc.pc7, gpioe.pe9, gpioe.pe11, gpioe.pe13, gpioe.pe14, clocks, tim1, tim3,
gpioc.pc6, gpioc.pc7,
gpioe.pe9, gpioe.pe11,
gpioe.pe13, gpioe.pe14
); );
let (dac0_spi, dac0_sync) = Self::setup_dac0(clocks, spi4, gpioe.pe2, gpioe.pe4, gpioe.pe6); let (dac0_spi, dac0_sync) = Self::setup_dac0(
clocks, spi4,
gpioe.pe2, gpioe.pe4, gpioe.pe6
);
let mut shdn0 = gpioe.pe10.into_push_pull_output(); let mut shdn0 = gpioe.pe10.into_push_pull_output();
shdn0.set_low(); let _ = shdn0.set_low();
let vref0_pin = gpioa.pa0.into_analog(); let vref0_pin = gpioa.pa0.into_analog();
let itec0_pin = gpioa.pa6.into_analog(); let itec0_pin = gpioa.pa6.into_analog();
let dac_feedback0_pin = gpioa.pa4.into_analog(); let dac_feedback0_pin = gpioa.pa4.into_analog();
@ -185,9 +170,12 @@ impl Pins {
tec_u_meas_pin: tec_u_meas0_pin, tec_u_meas_pin: tec_u_meas0_pin,
}; };
let (dac1_spi, dac1_sync) = Self::setup_dac1(clocks, spi5, gpiof.pf7, gpiof.pf6, gpiof.pf9); let (dac1_spi, dac1_sync) = Self::setup_dac1(
clocks, spi5,
gpiof.pf7, gpiof.pf6, gpiof.pf9
);
let mut shdn1 = gpioe.pe15.into_push_pull_output(); let mut shdn1 = gpioe.pe15.into_push_pull_output();
shdn1.set_low(); let _ = shdn1.set_low();
let vref1_pin = gpioa.pa3.into_analog(); let vref1_pin = gpioa.pa3.into_analog();
let itec1_pin = gpiob.pb0.into_analog(); let itec1_pin = gpiob.pb0.into_analog();
let dac_feedback1_pin = gpioa.pa5.into_analog(); let dac_feedback1_pin = gpioa.pa5.into_analog();
@ -203,27 +191,18 @@ impl Pins {
}; };
let pins = Pins { let pins = Pins {
adc_spi, adc_spi, adc_nss,
adc_nss,
pins_adc, pins_adc,
pwm, pwm,
channel0, channel0,
channel1, channel1,
}; };
let hwrev = HWRev::detect_hw_rev(&HWRevPins { let hwrev = HWRev::detect_hw_rev(&HWRevPins {hwrev0: gpiod.pd0, hwrev1: gpiod.pd1,
hwrev0: gpiod.pd0, hwrev2: gpiod.pd2, hwrev3: gpiod.pd3});
hwrev1: gpiod.pd1,
hwrev2: gpiod.pd2,
hwrev3: gpiod.pd3,
});
let hw_settings = hwrev.settings(); let hw_settings = hwrev.settings();
let leds = Leds::new( let leds = Leds::new(gpiod.pd9, gpiod.pd10.into_push_pull_output(), gpiod.pd11.into_push_pull_output());
gpiod.pd9,
gpiod.pd10.into_push_pull_output(),
gpiod.pd11.into_push_pull_output(),
);
let eeprom_scl = gpiob.pb8.into_alternate().set_open_drain(); let eeprom_scl = gpiob.pb8.into_alternate().set_open_drain();
let eeprom_sda = gpiob.pb9.into_alternate().set_open_drain(); let eeprom_sda = gpiob.pb9.into_alternate().set_open_drain();
@ -250,13 +229,8 @@ impl Pins {
}; };
let fan = if hw_settings.fan_available { let fan = if hw_settings.fan_available {
Some( Some(Timer::new(tim8, &clocks).pwm(gpioc.pc9.into_alternate(), hw_settings.fan_pwm_freq_hz.hz()))
Timer::new(tim8, &clocks) } else { None };
.pwm(gpioc.pc9.into_alternate(), hw_settings.fan_pwm_freq_hz.hz()),
)
} else {
None
};
(pins, leds, eeprom, eth_pins, usb, fan, hwrev, hw_settings) (pins, leds, eeprom, eth_pins, usb, fan, hwrev, hw_settings)
} }
@ -268,7 +242,8 @@ impl Pins {
sck: PB10<M1>, sck: PB10<M1>,
miso: PB14<M2>, miso: PB14<M2>,
mosi: PB15<M3>, mosi: PB15<M3>,
) -> AdcSpi { ) -> AdcSpi
{
let sck = sck.into_alternate(); let sck = sck.into_alternate();
let miso = miso.into_alternate(); let miso = miso.into_alternate();
let mosi = mosi.into_alternate(); let mosi = mosi.into_alternate();
@ -277,16 +252,13 @@ impl Pins {
(sck, miso, mosi), (sck, miso, mosi),
crate::ad7172::SPI_MODE, crate::ad7172::SPI_MODE,
crate::ad7172::SPI_CLOCK, crate::ad7172::SPI_CLOCK,
clocks, clocks
) )
} }
fn setup_dac0<M1, M2, M3>( fn setup_dac0<M1, M2, M3>(
clocks: Clocks, clocks: Clocks, spi4: SPI4,
spi4: SPI4, sclk: PE2<M1>, sync: PE4<M2>, sdin: PE6<M3>
sclk: PE2<M1>,
sync: PE4<M2>,
sdin: PE6<M3>,
) -> (Dac0Spi, <Channel0 as ChannelPins>::DacSync) { ) -> (Dac0Spi, <Channel0 as ChannelPins>::DacSync) {
let sclk = sclk.into_alternate(); let sclk = sclk.into_alternate();
let sdin = sdin.into_alternate(); let sdin = sdin.into_alternate();
@ -295,7 +267,7 @@ impl Pins {
(sclk, NoMiso {}, sdin), (sclk, NoMiso {}, sdin),
crate::ad5680::SPI_MODE, crate::ad5680::SPI_MODE,
crate::ad5680::SPI_CLOCK, crate::ad5680::SPI_CLOCK,
clocks, clocks
); );
let sync = sync.into_push_pull_output(); let sync = sync.into_push_pull_output();
@ -303,11 +275,8 @@ impl Pins {
} }
fn setup_dac1<M1, M2, M3>( fn setup_dac1<M1, M2, M3>(
clocks: Clocks, clocks: Clocks, spi5: SPI5,
spi5: SPI5, sclk: PF7<M1>, sync: PF6<M2>, sdin: PF9<M3>
sclk: PF7<M1>,
sync: PF6<M2>,
sdin: PF9<M3>,
) -> (Dac1Spi, <Channel1 as ChannelPins>::DacSync) { ) -> (Dac1Spi, <Channel1 as ChannelPins>::DacSync) {
let sclk = sclk.into_alternate(); let sclk = sclk.into_alternate();
let sdin = sdin.into_alternate(); let sdin = sdin.into_alternate();
@ -316,7 +285,7 @@ impl Pins {
(sclk, NoMiso {}, sdin), (sclk, NoMiso {}, sdin),
crate::ad5680::SPI_MODE, crate::ad5680::SPI_MODE,
crate::ad5680::SPI_CLOCK, crate::ad5680::SPI_CLOCK,
clocks, clocks
); );
let sync = sync.into_push_pull_output(); let sync = sync.into_push_pull_output();
@ -347,11 +316,14 @@ impl PwmPins {
) -> PwmPins { ) -> PwmPins {
let freq = 20u32.khz(); let freq = 20u32.khz();
fn init_pwm_pin<P: hal::PwmPin<Duty = u16>>(pin: &mut P) { fn init_pwm_pin<P: hal::PwmPin<Duty=u16>>(pin: &mut P) {
pin.set_duty(0); pin.set_duty(0);
pin.enable(); pin.enable();
} }
let channels = (max_v0.into_alternate(), max_v1.into_alternate()); let channels = (
max_v0.into_alternate(),
max_v1.into_alternate(),
);
//let (mut max_v0, mut max_v1) = pwm::tim3(tim3, channels, clocks, freq); //let (mut max_v0, mut max_v1) = pwm::tim3(tim3, channels, clocks, freq);
let (mut max_v0, mut max_v1) = Timer::new(tim3, &clocks).pwm(channels, freq); let (mut max_v0, mut max_v1) = Timer::new(tim3, &clocks).pwm(channels, freq);
init_pwm_pin(&mut max_v0); init_pwm_pin(&mut max_v0);
@ -371,12 +343,9 @@ impl PwmPins {
init_pwm_pin(&mut max_i_neg1); init_pwm_pin(&mut max_i_neg1);
PwmPins { PwmPins {
max_v0, max_v0, max_v1,
max_v1, max_i_pos0, max_i_pos1,
max_i_pos0, max_i_neg0, max_i_neg1,
max_i_pos1,
max_i_neg0,
max_i_neg1,
} }
} }
} }

View File

@ -1,29 +1,25 @@
use crate::command_parser::Ipv4Config;
use crate::net::split_ipv4_config;
use smoltcp::{ use smoltcp::{
iface::EthernetInterface, iface::EthernetInterface,
socket::{SocketHandle, SocketRef, SocketSet, TcpSocket, TcpSocketBuffer}, socket::{SocketSet, SocketHandle, TcpSocket, TcpSocketBuffer, SocketRef},
time::Instant, time::Instant,
wire::{IpAddress, IpCidr, Ipv4Address, Ipv4Cidr}, wire::{IpAddress, IpCidr, Ipv4Address, Ipv4Cidr},
}; };
use crate::command_parser::Ipv4Config;
use crate::net::split_ipv4_config;
pub struct SocketState<S> { pub struct SocketState<S> {
handle: SocketHandle, handle: SocketHandle,
state: S, state: S,
} }
impl<'a, S: Default> SocketState<S> { impl<'a, S: Default> SocketState<S>{
fn new( fn new(sockets: &mut SocketSet<'a>, tcp_rx_storage: &'a mut [u8; TCP_RX_BUFFER_SIZE], tcp_tx_storage: &'a mut [u8; TCP_TX_BUFFER_SIZE]) -> SocketState<S> {
sockets: &mut SocketSet<'a>,
tcp_rx_storage: &'a mut [u8; TCP_RX_BUFFER_SIZE],
tcp_tx_storage: &'a mut [u8; TCP_TX_BUFFER_SIZE],
) -> SocketState<S> {
let tcp_rx_buffer = TcpSocketBuffer::new(&mut tcp_rx_storage[..]); let tcp_rx_buffer = TcpSocketBuffer::new(&mut tcp_rx_storage[..]);
let tcp_tx_buffer = TcpSocketBuffer::new(&mut tcp_tx_storage[..]); let tcp_tx_buffer = TcpSocketBuffer::new(&mut tcp_tx_storage[..]);
let tcp_socket = TcpSocket::new(tcp_rx_buffer, tcp_tx_buffer); let tcp_socket = TcpSocket::new(tcp_rx_buffer, tcp_tx_buffer);
SocketState::<S> { SocketState::<S> {
handle: sockets.add(tcp_socket), handle: sockets.add(tcp_socket),
state: S::default(), state: S::default()
} }
} }
} }
@ -54,7 +50,7 @@ impl<'a, 'b, S: Default> Server<'a, 'b, S> {
($rx_storage:ident, $tx_storage:ident) => { ($rx_storage:ident, $tx_storage:ident) => {
let mut $rx_storage = [0; TCP_RX_BUFFER_SIZE]; let mut $rx_storage = [0; TCP_RX_BUFFER_SIZE];
let mut $tx_storage = [0; TCP_TX_BUFFER_SIZE]; let mut $tx_storage = [0; TCP_TX_BUFFER_SIZE];
}; }
} }
create_rtx_storage!(tcp_rx_storage0, tcp_tx_storage0); create_rtx_storage!(tcp_rx_storage0, tcp_tx_storage0);
@ -103,10 +99,15 @@ impl<'a, 'b, S: Default> Server<'a, 'b, S> {
fn set_ipv4_address(&mut self, ipv4_address: Ipv4Cidr) { fn set_ipv4_address(&mut self, ipv4_address: Ipv4Cidr) {
self.net.update_ip_addrs(|addrs| { self.net.update_ip_addrs(|addrs| {
for addr in addrs.iter_mut() { for addr in addrs.iter_mut() {
if let IpCidr::Ipv4(_) = addr { match addr {
*addr = IpCidr::Ipv4(ipv4_address); IpCidr::Ipv4(_) => {
// done *addr = IpCidr::Ipv4(ipv4_address);
break; // done
break
}
_ => {
// skip
}
} }
} }
}); });
@ -115,9 +116,10 @@ impl<'a, 'b, S: Default> Server<'a, 'b, S> {
fn set_gateway(&mut self, gateway: Option<Ipv4Address>) { fn set_gateway(&mut self, gateway: Option<Ipv4Address>) {
let routes = self.net.routes_mut(); let routes = self.net.routes_mut();
match gateway { match gateway {
None => routes.update(|routes_storage| { None =>
routes_storage.remove(&IpCidr::new(IpAddress::v4(0, 0, 0, 0), 0)); routes.update(|routes_storage| {
}), routes_storage.remove(&IpCidr::new(IpAddress::v4(0, 0, 0, 0), 0));
}),
Some(gateway) => { Some(gateway) => {
routes.add_default_ipv4_route(gateway).unwrap(); routes.add_default_ipv4_route(gateway).unwrap();
} }

View File

@ -1,5 +1,5 @@
use super::channels::CHANNELS;
use super::command_parser::{Command, Error as ParserError}; use super::command_parser::{Command, Error as ParserError};
use super::channels::CHANNELS;
const MAX_LINE_LEN: usize = 64; const MAX_LINE_LEN: usize = 64;
@ -46,8 +46,7 @@ pub enum SessionInput {
impl From<Result<Command, ParserError>> for SessionInput { impl From<Result<Command, ParserError>> for SessionInput {
fn from(input: Result<Command, ParserError>) -> Self { fn from(input: Result<Command, ParserError>) -> Self {
input input.map(SessionInput::Command)
.map(SessionInput::Command)
.unwrap_or_else(SessionInput::Error) .unwrap_or_else(SessionInput::Error)
} }
} }
@ -90,15 +89,16 @@ impl Session {
} }
pub fn is_report_pending(&self) -> Option<usize> { pub fn is_report_pending(&self) -> Option<usize> {
if !self.reporting { if ! self.reporting {
None None
} else { } else {
self.report_pending.iter().enumerate().fold( self.report_pending.iter()
None, .enumerate()
|result, (channel, report_pending)| { .fold(None, |result, (channel, report_pending)| {
result.or(if *report_pending { Some(channel) } else { None }) result.or_else(|| {
}, if *report_pending { Some(channel) } else { None }
) })
})
} }
} }
@ -111,12 +111,18 @@ impl Session {
for (i, b) in buf.iter().enumerate() { for (i, b) in buf.iter().enumerate() {
buf_bytes = i + 1; buf_bytes = i + 1;
let line = self.reader.feed(*b); let line = self.reader.feed(*b);
if let Some(line) = line { match line {
let command = Command::parse(line); Some(line) => {
if let Ok(Command::Reporting(reporting)) = command { let command = Command::parse(&line);
self.reporting = reporting; match command {
Ok(Command::Reporting(reporting)) => {
self.reporting = reporting;
}
_ => {}
}
return (buf_bytes, command.into());
} }
return (buf_bytes, command.into()); None => {}
} }
} }
(buf_bytes, SessionInput::Nothing) (buf_bytes, SessionInput::Nothing)

View File

@ -1,11 +1,14 @@
use num_traits::float::Float; use num_traits::float::Float;
use serde::{Deserialize, Serialize};
use uom::si::{ use uom::si::{
f64::{
ElectricalResistance,
ThermodynamicTemperature,
},
electrical_resistance::ohm, electrical_resistance::ohm,
f64::{ElectricalResistance, ThermodynamicTemperature},
ratio::ratio, ratio::ratio,
thermodynamic_temperature::{degree_celsius, kelvin}, thermodynamic_temperature::{degree_celsius, kelvin},
}; };
use serde::{Deserialize, Serialize};
/// Steinhart-Hart equation parameters /// Steinhart-Hart equation parameters
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]

View File

@ -4,9 +4,9 @@ use cortex_m::interrupt::Mutex;
use cortex_m_rt::exception; use cortex_m_rt::exception;
use stm32f4xx_hal::{ use stm32f4xx_hal::{
rcc::Clocks, rcc::Clocks,
stm32::SYST,
time::U32Ext, time::U32Ext,
timer::{Event as TimerEvent, Timer}, timer::{Timer, Event as TimerEvent},
stm32::SYST,
}; };
/// Rate in Hz /// Rate in Hz
@ -18,6 +18,7 @@ static TIMER_MS: Mutex<RefCell<u32>> = Mutex::new(RefCell::new(0));
/// Setup SysTick exception /// Setup SysTick exception
pub fn setup(syst: SYST, clocks: Clocks) { pub fn setup(syst: SYST, clocks: Clocks) {
let timer = Timer::syst(syst, &clocks); let timer = Timer::syst(syst, &clocks);
let mut countdown = timer.start_count_down(TIMER_RATE.hz()); let mut countdown = timer.start_count_down(TIMER_RATE.hz());
countdown.listen(TimerEvent::TimeOut); countdown.listen(TimerEvent::TimeOut);
@ -27,13 +28,18 @@ pub fn setup(syst: SYST, clocks: Clocks) {
#[exception] #[exception]
fn SysTick() { fn SysTick() {
cortex_m::interrupt::free(|cs| { cortex_m::interrupt::free(|cs| {
*TIMER_MS.borrow(cs).borrow_mut() += TIMER_DELTA; *TIMER_MS.borrow(cs)
.borrow_mut() += TIMER_DELTA;
}); });
} }
/// Obtain current time in milliseconds /// Obtain current time in milliseconds
pub fn now() -> u32 { pub fn now() -> u32 {
cortex_m::interrupt::free(|cs| *TIMER_MS.borrow(cs).borrow().deref()) cortex_m::interrupt::free(|cs| {
*TIMER_MS.borrow(cs)
.borrow()
.deref()
})
} }
/// block for at least `amount` milliseconds /// block for at least `amount` milliseconds

View File

@ -1,18 +1,15 @@
use core::{ use core::{fmt::{self, Write}, mem::MaybeUninit};
fmt::{self, Write},
mem::MaybeUninit,
};
use cortex_m::interrupt::free; use cortex_m::interrupt::free;
use log::{Log, Metadata, Record};
use stm32f4xx_hal::{ use stm32f4xx_hal::{
otg_fs::{UsbBus as Bus, USB}, otg_fs::{USB, UsbBus as Bus},
stm32::{interrupt, Interrupt, NVIC}, stm32::{interrupt, Interrupt, NVIC},
}; };
use usb_device::{ use usb_device::{
class_prelude::UsbBusAllocator, class_prelude::{UsbBusAllocator},
prelude::{UsbDevice, UsbDeviceBuilder, UsbVidPid}, prelude::{UsbDevice, UsbDeviceBuilder, UsbVidPid},
}; };
use usbd_serial::SerialPort; use usbd_serial::SerialPort;
use log::{Record, Log, Metadata};
static mut EP_MEMORY: [u32; 1024] = [0; 1024]; static mut EP_MEMORY: [u32; 1024] = [0; 1024];
@ -39,8 +36,8 @@ impl State {
.device_class(usbd_serial::USB_CLASS_CDC) .device_class(usbd_serial::USB_CLASS_CDC)
.build(); .build();
free(|_| unsafe { free(|_| {
STATE = Some(State { serial, dev }); unsafe { STATE = Some(State { serial, dev }); }
}); });
unsafe { unsafe {
@ -97,7 +94,8 @@ impl Write for SerialOutput {
fn write_str(&mut self, s: &str) -> core::result::Result<(), core::fmt::Error> { fn write_str(&mut self, s: &str) -> core::result::Result<(), core::fmt::Error> {
if let Some(ref mut state) = State::get() { if let Some(ref mut state) = State::get() {
for chunk in s.as_bytes().chunks(16) { for chunk in s.as_bytes().chunks(16) {
free(|_| state.serial.write(chunk)).map_err(|_| fmt::Error)?; free(|_| state.serial.write(chunk))
.map_err(|_| fmt::Error)?;
} }
} }
Ok(()) Ok(())