cargo fmt

- rustfmt.toml is ported from artiq-zynq repo
This commit is contained in:
linuswck 2024-04-23 17:09:26 +08:00
parent ded7dd7694
commit b8241d1f27
36 changed files with 1565 additions and 1233 deletions

View File

@ -1,7 +1,4 @@
use std::env; use std::{env, fs::File, io::Write, path::PathBuf};
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;
fn main() { fn main() {
// Put the linker script somewhere the linker can find it // Put the linker script somewhere the linker can find it

View File

@ -3,11 +3,11 @@
"mozilla-overlay": { "mozilla-overlay": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1695805681, "lastModified": 1704373101,
"narHash": "sha256-1ElPLD8eFfnuIk0G52HGGpRtQZ4QPCjChRlEOfkZ5ro=", "narHash": "sha256-+gi59LRWRQmwROrmE1E2b3mtocwueCQqZ60CwLG+gbg=",
"owner": "mozilla", "owner": "mozilla",
"repo": "nixpkgs-mozilla", "repo": "nixpkgs-mozilla",
"rev": "6eabade97bc28d707a8b9d82ad13ef143836736e", "rev": "9b11a87c0cc54e308fa83aac5b4ee1816d5418a2",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -18,11 +18,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1710951922, "lastModified": 1713725259,
"narHash": "sha256-FOOBJ3DQenLpTNdxMHR2CpGZmYuctb92gF0lpiirZ30=", "narHash": "sha256-9ZR/Rbx5/Z/JZf5ehVNMoz/s5xjpP0a22tL6qNvLt5E=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "f091af045dff8347d66d186a62d42aceff159456", "rev": "a5e4bbcb4780c63c79c87d29ea409abf097de3f7",
"type": "github" "type": "github"
}, },
"original": { "original": {

View File

@ -8,8 +8,8 @@
let let
pkgs = import nixpkgs { system = "x86_64-linux"; overlays = [ (import mozilla-overlay) ]; }; pkgs = import nixpkgs { system = "x86_64-linux"; overlays = [ (import mozilla-overlay) ]; };
rustManifest = pkgs.fetchurl { rustManifest = pkgs.fetchurl {
url = "https://static.rust-lang.org/dist/2024-03-21/channel-rust-stable.toml"; url = "https://static.rust-lang.org/dist/2024-03-21/channel-rust-nightly.toml";
sha256 = "faccaa01dda45fc2956bcfd4da0cf76e52104d3b1862ddd4eb7c4159a18e49cf"; sha256 = "1c7db6ab80d20682b5cc5bda7360c63311d7188c0c082902d3790820527cd4e0";
}; };
targets = [ targets = [
@ -22,7 +22,7 @@
inherit targets; inherit targets;
extensions = ["rust-src"]; extensions = ["rust-src"];
}; };
rust = rustChannelOfTargets "stable" null targets; rust = rustChannelOfTargets "nightly" null targets;
rustPlatform = pkgs.recurseIntoAttrs (pkgs.makeRustPlatform { rustPlatform = pkgs.recurseIntoAttrs (pkgs.makeRustPlatform {
rustc = rust; rustc = rust;
cargo = rust; cargo = rust;

64
rustfmt.toml Normal file
View File

@ -0,0 +1,64 @@
max_width = 120
hard_tabs = false
tab_spaces = 4
newline_style = "Auto"
use_small_heuristics = "Default"
indent_style = "Block"
wrap_comments = false
format_code_in_doc_comments = false
comment_width = 100
normalize_comments = false
normalize_doc_attributes = false
format_strings = true
format_macro_matchers = true
format_macro_bodies = true
empty_item_single_line = true
struct_lit_single_line = true
fn_single_line = false
where_single_line = true
imports_indent = "Visual"
imports_layout = "Mixed"
imports_granularity="Crate"
group_imports = "StdExternalCrate"
reorder_imports = true
reorder_modules = true
reorder_impl_items = false
type_punctuation_density = "Wide"
space_before_colon = false
space_after_colon = true
spaces_around_ranges = false
binop_separator = "Front"
remove_nested_parens = true
combine_control_expr = true
overflow_delimited_expr = false
struct_field_align_threshold = 0
enum_discrim_align_threshold = 0
match_arm_blocks = true
match_arm_leading_pipes = "Never"
force_multiline_blocks = false
fn_params_layout = "Tall"
brace_style = "SameLineWhere"
control_brace_style = "AlwaysSameLine"
trailing_semicolon = true
trailing_comma = "Vertical"
match_block_trailing_comma = false
blank_lines_upper_bound = 1
blank_lines_lower_bound = 0
edition = "2018"
version = "Two"
inline_attribute_width = 0
merge_derives = true
use_try_shorthand = false
use_field_init_shorthand = false
force_explicit_abi = true
condense_wildcard_suffixes = false
color = "Auto"
unstable_features = false
disable_all_formatting = false
skip_children = false
hide_parse_errors = false
error_on_line_overflow = false
error_on_unformatted = false
ignore = []
emit_mode = "Files"
make_backup = false

View File

@ -1,23 +1,19 @@
use super::{gpio, sys_timer, usb};
use crate::device::flash_store::{self, FlashStore};
use crate::laser_diode::ld_ctrl::{*};
use crate::laser_diode::laser_diode::LdDrive;
use crate::thermostat::max1968::MAX1968;
use crate::thermostat::thermostat::Thermostat;
use crate::net::net::{IpSettings, ServerHandle};
use stm32_eth;
use fugit::ExtU32; use fugit::ExtU32;
use log::{info, debug}; use log::{debug, info};
use stm32f4xx_hal::timer::TimerExt; use stm32f4xx_hal::{pac::{CorePeripherals, Peripherals},
use stm32f4xx_hal::{ rcc::RccExt,
pac::{CorePeripherals, Peripherals}, time::MegaHertz,
rcc::RccExt, timer::TimerExt,
time::MegaHertz, watchdog::IndependentWatchdog};
watchdog::IndependentWatchdog, use uom::si::{electric_current::{ampere, milliampere},
}; f32::ElectricCurrent};
use crate::DeviceSettings;
use uom::si::electric_current::milliampere; use super::{gpio, sys_timer, usb};
use uom::si::{electric_current::ampere, f32::ElectricCurrent}; use crate::{device::flash_store::{self, FlashStore},
laser_diode::{laser_diode::LdDrive, ld_ctrl::*},
net::net::{IpSettings, ServerHandle},
thermostat::{max1968::MAX1968, thermostat::Thermostat},
DeviceSettings};
#[cfg(not(feature = "semihosting"))] #[cfg(not(feature = "semihosting"))]
const WATCHDOG_PERIOD: u32 = 4000; const WATCHDOG_PERIOD: u32 = 4000;
@ -44,21 +40,22 @@ pub fn bootup(
sys_timer::setup(core_perif.SYST, clocks); sys_timer::setup(core_perif.SYST, clocks);
let (mut hw_rev, eth_pins, eth_mgmt_pins, usb, current_source_phy, ad7172_phy, max1968_phy, pd_mon_phy) = gpio::setup( let (mut hw_rev, eth_pins, eth_mgmt_pins, usb, current_source_phy, ad7172_phy, max1968_phy, pd_mon_phy) =
clocks, gpio::setup(
perif.TIM4, clocks,
perif.GPIOA, perif.TIM4,
perif.GPIOB, perif.GPIOA,
perif.GPIOC, perif.GPIOB,
perif.GPIOD, perif.GPIOC,
perif.GPIOE, perif.GPIOD,
perif.SPI1, perif.GPIOE,
perif.SPI2, perif.SPI1,
perif.SPI3, perif.SPI2,
perif.OTG_FS_GLOBAL, perif.SPI3,
perif.OTG_FS_DEVICE, perif.OTG_FS_GLOBAL,
perif.OTG_FS_PWRCLK, perif.OTG_FS_DEVICE,
); perif.OTG_FS_PWRCLK,
);
usb::State::setup(usb); usb::State::setup(usb);
@ -79,12 +76,12 @@ pub fn bootup(
laser.set_pd_i_limit(ElectricCurrent::new::<milliampere>(2.5)); laser.set_pd_i_limit(ElectricCurrent::new::<milliampere>(2.5));
laser.set_pd_mon_calibrated_vdda(thermostat.get_calibrated_vdda()); laser.set_pd_mon_calibrated_vdda(thermostat.get_calibrated_vdda());
laser.power_up(); laser.power_up();
debug!("Setting up Internal Flash Driver"); debug!("Setting up Internal Flash Driver");
let flash_store = flash_store::store(perif.FLASH); let flash_store = flash_store::store(perif.FLASH);
let mut ip_settings: IpSettings = IpSettings::default(); let mut ip_settings: IpSettings = IpSettings::default();
let device_settings : DeviceSettings; let device_settings: DeviceSettings;
match flash_store.read_value("Device") { match flash_store.read_value("Device") {
Ok(Some(config)) => { Ok(Some(config)) => {
device_settings = config; device_settings = config;
@ -107,7 +104,14 @@ pub fn bootup(
mmc: perif.ETHERNET_MMC, mmc: perif.ETHERNET_MMC,
ptp: perif.ETHERNET_PTP, ptp: perif.ETHERNET_PTP,
}; };
ServerHandle::new(eth_pins, eth_mgmt_pins, ethernet_parts_in, clocks, mac_addr, ip_settings); ServerHandle::new(
eth_pins,
eth_mgmt_pins,
ethernet_parts_in,
clocks,
mac_addr,
ip_settings,
);
debug!("Setting Watchdog"); debug!("Setting Watchdog");
let mut wd = IndependentWatchdog::new(perif.IWDG); let mut wd = IndependentWatchdog::new(perif.IWDG);

View File

@ -1,6 +1,7 @@
use core::arch::asm;
use cortex_m_rt::pre_init; use cortex_m_rt::pre_init;
use stm32f4xx_hal::pac::{RCC, SYSCFG}; use stm32f4xx_hal::pac::{RCC, SYSCFG};
use core::arch::asm;
const DFU_TRIG_MSG: u32 = 0xDECAFBAD; const DFU_TRIG_MSG: u32 = 0xDECAFBAD;
@ -14,7 +15,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 +28,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,9 +1,7 @@
use log::error; use log::error;
use stm32f4xx_hal::{
flash::{Error, FlashExt},
pac::FLASH,
};
use sfkv::{Store, StoreBackend}; use sfkv::{Store, StoreBackend};
use stm32f4xx_hal::{flash::{Error, FlashExt},
pac::FLASH};
// Last flash sector is used to avoid overwriting the code in flash. // Last flash sector is used to avoid overwriting the code in flash.
pub const FLASH_SECTOR: u8 = 11; pub const FLASH_SECTOR: u8 = 11;
@ -34,8 +32,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.unlocked() self.flash.unlocked().program(get_offset() + offset, payload.iter())
.program(get_offset() + offset, payload.iter())
} }
fn backup_space(&self) -> &'static mut [u8] { fn backup_space(&self) -> &'static mut [u8] {
@ -53,8 +50,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.erase() let _ = store.erase().map_err(|e| error!("flash erase failed: {:?}", e));
.map_err(|e| error!("flash erase failed: {:?}", e));
} }
} }

View File

@ -1,27 +1,25 @@
use crate::laser_diode::ld_ctrl::{self, LdCtrlPhy};
use crate::laser_diode::max5719;
use crate::laser_diode::ld_pwr_exc_protector::LdPwrExcProtectorPhy;
use crate::thermostat::ad5680;
use crate::thermostat::max1968::{self, MAX1968PinSet, MAX1968Phy, PWM_FREQ_KHZ};
use crate::thermostat::ad7172;
use crate::net::net::EthernetMgmtPins;
use crate::device::hw_rev::{HWRev, HwRevPins};
use stm32_eth::EthPins; use stm32_eth::EthPins;
use stm32f4xx_hal::gpio::alt::otg_fs::{Dm, Dp}; use stm32f4xx_hal::{gpio::{alt::otg_fs::{Dm, Dp},
use stm32f4xx_hal::{ gpioa::*,
gpio::{gpioa::*, gpiob::*, gpioc::*, GpioExt, Input, Speed}, gpiob::*,
otg_fs::USB, gpioc::*,
pac::{ GpioExt, Input, Speed},
GPIOA, GPIOB, GPIOC, GPIOD, GPIOE, OTG_FS_DEVICE, OTG_FS_GLOBAL, OTG_FS_PWRCLK, SPI1, SPI2, SPI3, otg_fs::USB,
TIM4, pac::{GPIOA, GPIOB, GPIOC, GPIOD, GPIOE, OTG_FS_DEVICE, OTG_FS_GLOBAL, OTG_FS_PWRCLK, SPI1, SPI2,
}, SPI3, TIM4},
rcc::Clocks, rcc::Clocks,
spi::{NoMiso, Spi}, spi::{NoMiso, Spi},
timer::{Channel1, Channel2, Channel3, pwm::PwmExt} timer::{pwm::PwmExt, Channel1, Channel2, Channel3}};
};
pub type EthernetPins = use crate::{device::hw_rev::{HWRev, HwRevPins},
EthPins<PA1<Input>, PA7<Input>, PB11<Input>, PB12<Input>, PB13<Input>, PC4<Input>, PC5<Input>>; laser_diode::{ld_ctrl::{self, LdCtrlPhy},
ld_pwr_exc_protector::LdPwrExcProtectorPhy,
max5719},
net::net::EthernetMgmtPins,
thermostat::{ad5680, ad7172,
max1968::{self, MAX1968Phy, MAX1968PinSet, PWM_FREQ_KHZ}}};
pub type EthernetPins = EthPins<PA1<Input>, PA7<Input>, PB11<Input>, PB12<Input>, PB13<Input>, PC4<Input>, PC5<Input>>;
pub fn setup( pub fn setup(
clocks: Clocks, clocks: Clocks,
@ -45,7 +43,7 @@ pub fn setup(
LdCtrlPhy<ld_ctrl::Channel0>, LdCtrlPhy<ld_ctrl::Channel0>,
ad7172::AdcPhy, ad7172::AdcPhy,
MAX1968Phy<max1968::Channel0>, MAX1968Phy<max1968::Channel0>,
LdPwrExcProtectorPhy LdPwrExcProtectorPhy,
) { ) {
let gpioa = gpioa.split(); let gpioa = gpioa.split();
let gpiob = gpiob.split(); let gpiob = gpiob.split();
@ -53,14 +51,12 @@ pub fn setup(
let gpiod = gpiod.split(); let gpiod = gpiod.split();
let gpioe = gpioe.split(); let gpioe = gpioe.split();
let mut hw_rev = HWRev::detect_hw_rev( let mut hw_rev = HWRev::detect_hw_rev(HwRevPins {
HwRevPins { h0: gpioe.pe8.into_input(),
h0: gpioe.pe8.into_input(), h1: gpioe.pe9.into_input(),
h1: gpioe.pe9.into_input(), h2: gpioe.pe10.into_input(),
h2: gpioe.pe10.into_input(), h3: gpioe.pe11.into_input(),
h3: gpioe.pe11.into_input(), });
}
);
hw_rev.startup_delay_before_gpio_init(); hw_rev.startup_delay_before_gpio_init();
@ -82,28 +78,26 @@ pub fn setup(
rx_d0: gpioc.pc4, rx_d0: gpioc.pc4,
rx_d1: gpioc.pc5, rx_d1: gpioc.pc5,
}; };
let mut eth_mgmt_pins = EthernetMgmtPins { let mut eth_mgmt_pins = EthernetMgmtPins {
mdio: gpioa.pa2.into_alternate::<11>(), mdio: gpioa.pa2.into_alternate::<11>(),
mdc: gpioc.pc1.into_alternate::<11>(), mdc: gpioc.pc1.into_alternate::<11>(),
}; };
eth_mgmt_pins.mdio.set_speed(Speed::VeryHigh); eth_mgmt_pins.mdio.set_speed(Speed::VeryHigh);
eth_mgmt_pins.mdc.set_speed(Speed::VeryHigh); eth_mgmt_pins.mdc.set_speed(Speed::VeryHigh);
let current_source_phy = LdCtrlPhy { let current_source_phy = LdCtrlPhy {
dac: max5719::Dac::new(Spi::new( dac: max5719::Dac::new(
spi2, Spi::new(
( spi2,
gpiob.pb10.into_alternate(), (gpiob.pb10.into_alternate(), NoMiso::new(), gpiob.pb15.into_alternate()),
NoMiso::new(), max5719::SPI_MODE,
gpiob.pb15.into_alternate(), max5719::SPI_CLOCK_MHZ.convert(),
), &clocks,
max5719::SPI_MODE,
max5719::SPI_CLOCK_MHZ.convert(),
&clocks,
), gpiod.pd8.into_push_pull_output(),
gpiob.pb14.into_push_pull_output(),
), ),
gpiod.pd8.into_push_pull_output(),
gpiob.pb14.into_push_pull_output(),
),
current_source_short_pin: gpioa.pa4.into_push_pull_output(), current_source_short_pin: gpioa.pa4.into_push_pull_output(),
termination_status_pin: gpiod.pd7.internal_pull_up(true), termination_status_pin: gpiod.pd7.internal_pull_up(true),
}; };
@ -118,18 +112,13 @@ pub fn setup(
Channel2::new(gpiob.pb7), Channel2::new(gpiob.pb7),
Channel3::new(gpiob.pb8), Channel3::new(gpiob.pb8),
); );
let (max_i_neg0, max_v0, max_i_pos0) = let (max_i_neg0, max_v0, max_i_pos0) = tim4.pwm_hz(pwm_chs, PWM_FREQ_KHZ.convert(), &clocks).split();
tim4.pwm_hz(pwm_chs, PWM_FREQ_KHZ.convert(), &clocks).split();
let max1968_phy = MAX1968Phy::new(MAX1968PinSet { let max1968_phy = MAX1968Phy::new(MAX1968PinSet {
dac: ad5680::Dac::new( dac: ad5680::Dac::new(
Spi::new( Spi::new(
spi1, spi1,
( (gpiob.pb3.into_alternate(), NoMiso::new(), gpiob.pb5.into_alternate()),
gpiob.pb3.into_alternate(),
NoMiso::new(),
gpiob.pb5.into_alternate(),
),
ad5680::SPI_MODE, ad5680::SPI_MODE,
ad5680::SPI_CLOCK_MHZ.convert(), ad5680::SPI_CLOCK_MHZ.convert(),
&clocks, &clocks,
@ -147,7 +136,8 @@ pub fn setup(
}); });
let ad7172_phy = ad7172::Adc::new( let ad7172_phy = ad7172::Adc::new(
Spi::new(spi3, Spi::new(
spi3,
( (
gpioc.pc10.into_alternate(), gpioc.pc10.into_alternate(),
gpioc.pc11.into_alternate(), gpioc.pc11.into_alternate(),
@ -155,10 +145,20 @@ pub fn setup(
), ),
ad7172::SPI_MODE, ad7172::SPI_MODE,
ad7172::SPI_CLOCK_MHZ.convert(), ad7172::SPI_CLOCK_MHZ.convert(),
&clocks &clocks,
), ),
gpioa.pa15.into_push_pull_output(), gpioa.pa15.into_push_pull_output(),
).unwrap(); )
.unwrap();
(hw_rev, eth_pins, eth_mgmt_pins, usb, current_source_phy, ad7172_phy, max1968_phy, pd_mon_phy) (
hw_rev,
eth_pins,
eth_mgmt_pins,
usb,
current_source_phy,
ad7172_phy,
max1968_phy,
pd_mon_phy,
)
} }

View File

@ -1,9 +1,10 @@
use stm32f4xx_hal::gpio::{Input, PE10, PE11, PE8, PE9};
use crate::device::sys_timer::sleep;
use stm32f4xx_hal::signature;
use crc::{Crc, CRC_24_BLE}; use crc::{Crc, CRC_24_BLE};
use stm32f4xx_hal::{gpio::{Input, PE10, PE11, PE8, PE9},
signature};
pub struct HwRevPins{ use crate::device::sys_timer::sleep;
pub struct HwRevPins {
pub h0: PE8<Input>, pub h0: PE8<Input>,
pub h1: PE9<Input>, pub h1: PE9<Input>,
pub h2: PE10<Input>, pub h2: PE10<Input>,
@ -18,19 +19,21 @@ pub struct HWRev {
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.h0.is_high(), hwrev_pins.h1.is_high(), hwrev_pins.h0.is_high(),
hwrev_pins.h2.is_high(), hwrev_pins.h3.is_high() hwrev_pins.h1.is_high(),
hwrev_pins.h2.is_high(),
hwrev_pins.h3.is_high(),
); );
match (h0, h1, h2, h3) { match (h0, h1, h2, h3) {
(true, true, true, true) => HWRev { major: 0, minor: 3 }, (true, true, true, true) => HWRev { major: 0, minor: 3 },
(_, _, _, _) => HWRev { major: 0, minor: 0 } (_, _, _, _) => HWRev { major: 0, minor: 0 },
} }
} }
/// On Rev0_3, during power up, digital power rails are stabilized way before analog power rails /// On Rev0_3, during power up, digital power rails are stabilized way before analog power rails
/// This causes improper initialization on any peripherals requiring calibrations /// This causes improper initialization on any peripherals requiring calibrations
/// See Issue #32 on Kirdy Hw Repo /// See Issue #32 on Kirdy Hw Repo
pub fn startup_delay_before_gpio_init(&mut self){ pub fn startup_delay_before_gpio_init(&mut self) {
if self.major == 0 && self.minor == 3 { if self.major == 0 && self.minor == 3 {
sleep(5000); sleep(5000);
} }
@ -54,7 +57,7 @@ impl HWRev {
digest.update(&uid_data); digest.update(&uid_data);
let crc24 = digest.finalize(); let crc24 = digest.finalize();
[ 0x02, 0xE0, 0xD5, (crc24 >> 16) as u8, (crc24 >> 8) as u8, (crc24 as u8)] [0x02, 0xE0, 0xD5, (crc24 >> 16) as u8, (crc24 >> 8) as u8, (crc24 as u8)]
} else { } else {
unimplemented!() unimplemented!()
} }

View File

@ -3,13 +3,15 @@ pub fn init_log() {
use super::usb; use super::usb;
static USB_LOGGER: usb::Logger = usb::Logger; static USB_LOGGER: usb::Logger = usb::Logger;
let _ = log::set_logger(&USB_LOGGER); let _ = log::set_logger(&USB_LOGGER);
log::set_max_level(log::LevelFilter::Debug); // log::set_max_level(log::LevelFilter::Debug);
log::set_max_level(log::LevelFilter::Trace);
} }
#[cfg(feature = "RTT")] #[cfg(feature = "RTT")]
pub fn init_log() { pub fn init_log() {
use super::rtt_logger;
use rtt_target::rtt_init_print; use rtt_target::rtt_init_print;
use super::rtt_logger;
static RTT_LOGGER: rtt_logger::Logger = rtt_logger::Logger; static RTT_LOGGER: rtt_logger::Logger = rtt_logger::Logger;
rtt_init_print!(); rtt_init_print!();
let _ = log::set_logger(&RTT_LOGGER); let _ = log::set_logger(&RTT_LOGGER);
@ -18,8 +20,8 @@ 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::{log::{init, Logger},
use cortex_m_log::printer::semihosting::{hio::HStdout, InterruptOk}; printer::semihosting::{hio::HStdout, InterruptOk}};
use log::LevelFilter; use log::LevelFilter;
static mut LOGGER: Option<Logger<InterruptOk<HStdout>>> = None; static mut LOGGER: Option<Logger<InterruptOk<HStdout>>> = None;
let logger = Logger { let logger = Logger {

View File

@ -1,9 +1,9 @@
pub mod boot; pub mod boot;
pub mod dfu;
pub mod flash_store;
pub mod gpio; pub mod gpio;
pub mod hw_rev;
pub mod log_setup; pub mod log_setup;
pub mod rtt_logger; pub mod rtt_logger;
pub mod sys_timer; pub mod sys_timer;
pub mod usb; pub mod usb;
pub mod flash_store;
pub mod hw_rev;
pub mod dfu;

View File

@ -1,7 +1,6 @@
use core::cell::RefCell; use core::{cell::RefCell, ops::Deref};
use core::ops::Deref;
use cortex_m::interrupt::Mutex; use cortex_m::{interrupt::Mutex, peripheral::syst::SystClkSource};
use cortex_m::peripheral::syst::SystClkSource;
use cortex_m_rt::exception; use cortex_m_rt::exception;
use stm32f4xx_hal::{pac::SYST, rcc::Clocks}; use stm32f4xx_hal::{pac::SYST, rcc::Clocks};

View File

@ -1,19 +1,14 @@
use core::{ use core::{fmt::{self, Write},
fmt::{self, Write}, mem::MaybeUninit};
mem::MaybeUninit,
};
use cortex_m::interrupt::free; use cortex_m::interrupt::free;
use log::{Log, Metadata, Record}; use log::{Log, Metadata, Record};
use stm32f4xx_hal::{ use stm32f4xx_hal::{otg_fs::{UsbBus as Bus, USB},
otg_fs::{UsbBus as Bus, USB}, pac::{interrupt, Interrupt, NVIC}};
pac::{interrupt, Interrupt, NVIC}, use usb_device::{class_prelude::UsbBusAllocator,
}; descriptor::lang_id,
use usb_device::{ device::StringDescriptors,
descriptor::lang_id, prelude::{UsbDevice, UsbDeviceBuilder, UsbVidPid}};
device::StringDescriptors,
class_prelude::UsbBusAllocator,
prelude::{UsbDevice, UsbDeviceBuilder, UsbVidPid},
};
use usbd_serial::SerialPort; use usbd_serial::SerialPort;
static mut EP_MEMORY: [u32; 1024] = [0; 1024]; static mut EP_MEMORY: [u32; 1024] = [0; 1024];
@ -41,7 +36,8 @@ impl State {
.device_release(0x20) .device_release(0x20)
.self_powered(true) .self_powered(true)
.device_class(usbd_serial::USB_CLASS_CDC) .device_class(usbd_serial::USB_CLASS_CDC)
.strings(&[str_descriptor]).unwrap() .strings(&[str_descriptor])
.unwrap()
.build(); .build();
free(|_| unsafe { free(|_| unsafe {

View File

@ -1,28 +1,27 @@
use miniconf::Tree;
use stm32f4xx_hal::timer::CounterUs;
use stm32f4xx_hal::pac::{ADC3, TIM2};
use uom::si::electric_current::ampere;
use uom::si::power::milliwatt;
use crate::laser_diode::ld_ctrl::{LdCtrl, Impedance};
use crate::laser_diode::ld_pwr_exc_protector::{LdPwrExcProtector, self};
use crate::laser_diode::pd_mon_params;
use crate::laser_diode::ld_current_out_ctrl_timer::LdCurrentOutCtrlTimer;
use core::marker::PhantomData; use core::marker::PhantomData;
use crate::device::sys_timer::sleep;
use serde::{Deserialize, Serialize};
use uom::si::{ use miniconf::Tree;
electric_current::milliampere, use serde::{Deserialize, Serialize};
f32::{ElectricPotential, ElectricCurrent, Power}, use stm32f4xx_hal::{pac::{ADC3, TIM2},
}; timer::CounterUs};
use uom::{si::{ISQ, SI, Quantity}, typenum::*}; use uom::{si::{electric_current::{ampere, milliampere},
f32::{ElectricCurrent, ElectricPotential, Power},
power::milliwatt,
Quantity, ISQ, SI},
typenum::*};
use crate::{device::sys_timer::sleep,
laser_diode::{ld_ctrl::{Impedance, LdCtrl},
ld_current_out_ctrl_timer::LdCurrentOutCtrlTimer,
ld_pwr_exc_protector::{self, LdPwrExcProtector},
pd_mon_params}};
// Volt / Ampere // Volt / Ampere
pub type TransimpedanceUnit = Quantity<ISQ<P2, P1, N3, N2, Z0, Z0, Z0>, SI<f32>, f32>; pub type TransimpedanceUnit = Quantity<ISQ<P2, P1, N3, N2, Z0, Z0, Z0>, SI<f32>, f32>;
// Ampere / Volt // Ampere / Volt
type TransconductanceUnit = Quantity<ISQ<N2, N1, P3, P2, Z0, Z0, Z0>, SI<f32>, f32>; type TransconductanceUnit = Quantity<ISQ<N2, N1, P3, P2, Z0, Z0, Z0>, SI<f32>, f32>;
impl Settings{ impl Settings {
pub const LD_CURRENT_MAX: ElectricCurrent = ElectricCurrent { pub const LD_CURRENT_MAX: ElectricCurrent = ElectricCurrent {
dimension: PhantomData, dimension: PhantomData,
units: PhantomData, units: PhantomData,
@ -83,31 +82,40 @@ pub struct StatusReport {
term_status: Impedance, term_status: Impedance,
} }
pub struct LdDrive{ pub struct LdDrive {
ctrl: LdCtrl, ctrl: LdCtrl,
settings: Settings, settings: Settings,
} }
impl LdDrive{ impl LdDrive {
pub fn new(current_source: LdCtrl, pins_adc: ADC3, tim2: CounterUs<TIM2>, phy: ld_pwr_exc_protector::LdPwrExcProtectorPhy)-> Self { pub fn new(
current_source: LdCtrl,
pins_adc: ADC3,
tim2: CounterUs<TIM2>,
phy: ld_pwr_exc_protector::LdPwrExcProtectorPhy,
) -> Self {
LdPwrExcProtector::setup(pins_adc, phy); LdPwrExcProtector::setup(pins_adc, phy);
LdCurrentOutCtrlTimer::setup(tim2); LdCurrentOutCtrlTimer::setup(tim2);
LdDrive { LdDrive {
ctrl: current_source, ctrl: current_source,
settings: Settings::default() settings: Settings::default(),
} }
} }
pub fn setup(&mut self) { pub fn setup(&mut self) {
LdPwrExcProtector::pwr_off(); LdPwrExcProtector::pwr_off();
self.ctrl.set_i(ElectricCurrent::new::<milliampere>(0.0), Settings::LD_DRIVE_TRANSIMPEDANCE, Settings::DAC_OUT_V_MAX); self.ctrl.set_i(
ElectricCurrent::new::<milliampere>(0.0),
Settings::LD_DRIVE_TRANSIMPEDANCE,
Settings::DAC_OUT_V_MAX,
);
self.set_ld_drive_current_limit(Settings::LD_CURRENT_MAX); self.set_ld_drive_current_limit(Settings::LD_CURRENT_MAX);
LdCurrentOutCtrlTimer::reset(); LdCurrentOutCtrlTimer::reset();
self.ld_short(); self.ld_short();
} }
pub fn set_ld_drive_current_limit(&mut self, i_limit: ElectricCurrent){ pub fn set_ld_drive_current_limit(&mut self, i_limit: ElectricCurrent) {
self.settings.ld_drive_current_limit = i_limit.min(Settings::LD_CURRENT_MAX); self.settings.ld_drive_current_limit = i_limit.min(Settings::LD_CURRENT_MAX);
} }
@ -121,10 +129,14 @@ impl LdDrive{
self.settings.ld_terms_short = false; self.settings.ld_terms_short = false;
} }
pub fn power_up(&mut self){ pub fn power_up(&mut self) {
let prev_i_set = self.settings.ld_drive_current; let prev_i_set = self.settings.ld_drive_current;
LdCurrentOutCtrlTimer::reset(); LdCurrentOutCtrlTimer::reset();
let _ = self.ctrl.set_i(ElectricCurrent::new::<milliampere>(0.0), Settings::LD_DRIVE_TRANSIMPEDANCE, Settings::DAC_OUT_V_MAX); let _ = self.ctrl.set_i(
ElectricCurrent::new::<milliampere>(0.0),
Settings::LD_DRIVE_TRANSIMPEDANCE,
Settings::DAC_OUT_V_MAX,
);
// Wait for the DAC to reset its voltage back to 0V // Wait for the DAC to reset its voltage back to 0V
sleep(35); sleep(35);
LdPwrExcProtector::pwr_on_and_arm_protection(); LdPwrExcProtector::pwr_on_and_arm_protection();
@ -134,7 +146,7 @@ impl LdDrive{
self.settings.pwr_on = true; self.settings.pwr_on = true;
} }
pub fn power_down(&mut self){ pub fn power_down(&mut self) {
LdPwrExcProtector::pwr_off(); LdPwrExcProtector::pwr_off();
self.settings.pwr_on = false; self.settings.pwr_on = false;
} }
@ -144,10 +156,12 @@ impl LdDrive{
} }
pub fn get_pd_pwr(&mut self) -> Power { pub fn get_pd_pwr(&mut self) -> Power {
self.settings.pd_mon_params.get_ld_pwr_from_ld_i(LdPwrExcProtector::get_status().v * Settings::PD_MON_TRANSCONDUCTANCE) self.settings
.pd_mon_params
.get_ld_pwr_from_ld_i(LdPwrExcProtector::get_status().v * Settings::PD_MON_TRANSCONDUCTANCE)
} }
pub fn ld_set_i(&mut self, i: ElectricCurrent){ pub fn ld_set_i(&mut self, i: ElectricCurrent) {
self.settings.ld_drive_current = i.min(self.settings.ld_drive_current_limit); self.settings.ld_drive_current = i.min(self.settings.ld_drive_current_limit);
LdCurrentOutCtrlTimer::set_target_i_and_listen_irq(self.settings.ld_drive_current, self.ctrl.get_i_set()); LdCurrentOutCtrlTimer::set_target_i_and_listen_irq(self.settings.ld_drive_current, self.ctrl.get_i_set());
} }
@ -155,13 +169,13 @@ impl LdDrive{
pub fn poll_and_update_output_current(&mut self) -> ElectricCurrent { pub fn poll_and_update_output_current(&mut self) -> ElectricCurrent {
match LdCurrentOutCtrlTimer::get_irq_status() { match LdCurrentOutCtrlTimer::get_irq_status() {
Some(i_set) => { Some(i_set) => {
let i_set = self.ctrl.set_i(i_set, Settings::LD_DRIVE_TRANSIMPEDANCE, Settings::DAC_OUT_V_MAX); let i_set = self
.ctrl
.set_i(i_set, Settings::LD_DRIVE_TRANSIMPEDANCE, Settings::DAC_OUT_V_MAX);
LdCurrentOutCtrlTimer::clear_alarm_and_resume_listening(); LdCurrentOutCtrlTimer::clear_alarm_and_resume_listening();
i_set i_set
} }
None => { None => ElectricCurrent::new::<ampere>(0.0),
ElectricCurrent::new::<ampere>(0.0)
}
} }
} }
@ -174,26 +188,26 @@ impl LdDrive{
LdPwrExcProtector::clear_alarm_status(); LdPwrExcProtector::clear_alarm_status();
} }
pub fn set_pd_responsitivity(&mut self, responsitivity: pd_mon_params::ResponsitivityUnit){ pub fn set_pd_responsitivity(&mut self, responsitivity: pd_mon_params::ResponsitivityUnit) {
self.settings.pd_mon_params.set(responsitivity); self.settings.pd_mon_params.set(responsitivity);
} }
pub fn set_pd_dark_current(&mut self, i_dark: ElectricCurrent){ pub fn set_pd_dark_current(&mut self, i_dark: ElectricCurrent) {
self.settings.pd_mon_params.set_i_dark(i_dark); self.settings.pd_mon_params.set_i_dark(i_dark);
} }
pub fn set_ld_power_limit(&mut self, pwr_limit: Power){ pub fn set_ld_power_limit(&mut self, pwr_limit: Power) {
LdPwrExcProtector::set_trigger_threshold_v(self.settings.pd_mon_params LdPwrExcProtector::set_trigger_threshold_v(
.get_ld_i_from_ld_pwr(pwr_limit) / Settings::PD_MON_TRANSCONDUCTANCE self.settings.pd_mon_params.get_ld_i_from_ld_pwr(pwr_limit) / Settings::PD_MON_TRANSCONDUCTANCE,
); );
self.settings.ld_pwr_limit = pwr_limit; self.settings.ld_pwr_limit = pwr_limit;
} }
pub fn set_pd_i_limit(&mut self, i: ElectricCurrent){ pub fn set_pd_i_limit(&mut self, i: ElectricCurrent) {
LdPwrExcProtector::set_trigger_threshold_v(i / Settings::PD_MON_TRANSCONDUCTANCE); LdPwrExcProtector::set_trigger_threshold_v(i / Settings::PD_MON_TRANSCONDUCTANCE);
} }
pub fn set_default_pwr_on(&mut self, pwr_on: bool){ pub fn set_default_pwr_on(&mut self, pwr_on: bool) {
self.settings.default_pwr_on = pwr_on; self.settings.default_pwr_on = pwr_on;
} }
@ -221,8 +235,14 @@ impl LdDrive{
let settings = self.settings; let settings = self.settings;
LdSettingsSummary { LdSettingsSummary {
default_pwr_on: self.settings.default_pwr_on, default_pwr_on: self.settings.default_pwr_on,
ld_drive_current: LdSettingsSummaryField { value: settings.ld_drive_current, max: Settings::LD_CURRENT_MAX}, ld_drive_current: LdSettingsSummaryField {
ld_drive_current_limit: LdSettingsSummaryField { value: settings.ld_drive_current_limit, max: Settings::LD_CURRENT_MAX}, value: settings.ld_drive_current,
max: Settings::LD_CURRENT_MAX,
},
ld_drive_current_limit: LdSettingsSummaryField {
value: settings.ld_drive_current_limit,
max: Settings::LD_CURRENT_MAX,
},
pd_mon_params: settings.pd_mon_params, pd_mon_params: settings.pd_mon_params,
ld_pwr_limit: settings.ld_pwr_limit, ld_pwr_limit: settings.ld_pwr_limit,
ld_terms_short: settings.ld_terms_short, ld_terms_short: settings.ld_terms_short,

View File

@ -1,19 +1,15 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use stm32f4xx_hal::{ use stm32f4xx_hal::{gpio::{gpioa::*, gpiob::*, gpiod::*, Input, Output, PushPull},
gpio::{gpioa::*, gpiob::*, gpiod::*, Input, Output, PushPull}, hal::{digital::{InputPin, OutputPin},
hal::{spi::SpiBus, digital::{OutputPin, InputPin}}, spi::SpiBus},
pac::SPI2, pac::SPI2,
spi::Spi, spi::Spi};
}; use uom::si::{electric_current::ampere,
f32::{ElectricCurrent, ElectricPotential},
ratio::ratio};
use uom::si::{ use crate::laser_diode::{laser_diode::TransimpedanceUnit,
ratio::ratio, max5719::{self, Dac}};
f32::{ElectricPotential, ElectricCurrent},
electric_current::ampere,
};
use crate::laser_diode::max5719::{self, Dac};
use crate::laser_diode::laser_diode::TransimpedanceUnit;
#[derive(Deserialize, Serialize, Debug, Clone, Copy)] #[derive(Deserialize, Serialize, Debug, Clone, Copy)]
pub enum Impedance { pub enum Impedance {
@ -32,7 +28,7 @@ pub trait ChannelPins {
pub struct LdCtrlPhy<C: ChannelPins> { pub struct LdCtrlPhy<C: ChannelPins> {
pub dac: Dac<C::Max5719Spi, C::Max5719Cs, C::Max5719Load>, pub dac: Dac<C::Max5719Spi, C::Max5719Cs, C::Max5719Load>,
pub current_source_short_pin: C::CurrentSourceShort, pub current_source_short_pin: C::CurrentSourceShort,
pub termination_status_pin: C::TerminationStatus pub termination_status_pin: C::TerminationStatus,
} }
pub struct Channel0; pub struct Channel0;
@ -48,7 +44,7 @@ type DacSpi = Spi<SPI2>;
type DacCs = PD8<Output<PushPull>>; type DacCs = PD8<Output<PushPull>>;
type DacLoad = PB14<Output<PushPull>>; type DacLoad = PB14<Output<PushPull>>;
pub struct LdCtrl{ pub struct LdCtrl {
pub phy: LdCtrlPhy<Channel0>, pub phy: LdCtrlPhy<Channel0>,
i_set: ElectricCurrent, i_set: ElectricCurrent,
} }
@ -57,7 +53,7 @@ impl LdCtrl {
pub fn new(phy_ch0: LdCtrlPhy<Channel0>) -> Self { pub fn new(phy_ch0: LdCtrlPhy<Channel0>) -> Self {
LdCtrl { LdCtrl {
phy: phy_ch0, phy: phy_ch0,
i_set: ElectricCurrent::new::<ampere>(0.0) i_set: ElectricCurrent::new::<ampere>(0.0),
} }
} }
@ -71,28 +67,31 @@ impl LdCtrl {
self.phy.current_source_short_pin.set_high(); self.phy.current_source_short_pin.set_high();
} }
pub fn get_lf_mod_in_impedance(&mut self)-> Impedance { pub fn get_lf_mod_in_impedance(&mut self) -> Impedance {
if self.phy.termination_status_pin.is_high() { if self.phy.termination_status_pin.is_high() {
Impedance::Is50Ohm Impedance::Is50Ohm
} } else {
else {
Impedance::Not50Ohm Impedance::Not50Ohm
} }
} }
pub fn set_dac(&mut self, voltage: ElectricPotential, dac_out_v_max: ElectricPotential) -> ElectricPotential { pub fn set_dac(&mut self, voltage: ElectricPotential, dac_out_v_max: ElectricPotential) -> ElectricPotential {
let value = ((voltage / dac_out_v_max).get::<ratio>() let value = ((voltage / dac_out_v_max).get::<ratio>() * (max5719::MAX_VALUE as f32)) as u32;
* (max5719::MAX_VALUE as f32)) as u32;
self.phy.dac.set(value).unwrap(); self.phy.dac.set(value).unwrap();
value as f32 * dac_out_v_max / max5719::MAX_VALUE as f32 value as f32 * dac_out_v_max / max5719::MAX_VALUE as f32
} }
pub fn set_i(&mut self, current: ElectricCurrent, transimpedance: TransimpedanceUnit, dac_out_v_max: ElectricPotential) -> ElectricCurrent { pub fn set_i(
&mut self,
current: ElectricCurrent,
transimpedance: TransimpedanceUnit,
dac_out_v_max: ElectricPotential,
) -> ElectricCurrent {
self.i_set = self.set_dac(current * transimpedance, dac_out_v_max) / transimpedance; self.i_set = self.set_dac(current * transimpedance, dac_out_v_max) / transimpedance;
self.i_set self.i_set
} }
pub fn get_i_set(&mut self) -> ElectricCurrent{ pub fn get_i_set(&mut self) -> ElectricCurrent {
self.i_set self.i_set
} }
} }

View File

@ -1,10 +1,11 @@
use stm32f4xx_hal::timer::{Event, Counter};
use stm32f4xx_hal::pac::{interrupt, Interrupt, TIM2};
use stm32f4xx_hal::Listen;
use uom::si::{f32::ElectricCurrent, electric_current::ampere};
use fugit::{TimerDurationU32, KilohertzU32};
use core::marker::PhantomData; use core::marker::PhantomData;
use fugit::{KilohertzU32, TimerDurationU32};
use log::debug; use log::debug;
use stm32f4xx_hal::{pac::{interrupt, Interrupt, TIM2},
timer::{Counter, Event},
Listen};
use uom::si::{electric_current::ampere, f32::ElectricCurrent};
pub struct LdCurrentOutCtrlTimer { pub struct LdCurrentOutCtrlTimer {
target_i: ElectricCurrent, target_i: ElectricCurrent,
@ -32,14 +33,12 @@ impl LdCurrentOutCtrlTimer {
cortex_m::peripheral::NVIC::unmask(Interrupt::TIM2); cortex_m::peripheral::NVIC::unmask(Interrupt::TIM2);
} }
unsafe { unsafe {
LD_CURRENT_OUT_CTRL_TIMER = Some( LD_CURRENT_OUT_CTRL_TIMER = Some(LdCurrentOutCtrlTimer {
LdCurrentOutCtrlTimer { target_i: ElectricCurrent::new::<ampere>(0.0),
target_i: ElectricCurrent::new::<ampere>(0.0), now_i: ElectricCurrent::new::<ampere>(0.0),
now_i: ElectricCurrent::new::<ampere>(0.0), timer: tim2,
timer: tim2, timeout: false,
timeout: false });
}
);
} }
} }
@ -48,7 +47,7 @@ impl LdCurrentOutCtrlTimer {
} }
pub fn reset() { pub fn reset() {
if let Some(ref mut ld_current_out_ctrl_timer ) = LdCurrentOutCtrlTimer::get() { if let Some(ref mut ld_current_out_ctrl_timer) = LdCurrentOutCtrlTimer::get() {
ld_current_out_ctrl_timer.target_i = ElectricCurrent::new::<ampere>(0.0); ld_current_out_ctrl_timer.target_i = ElectricCurrent::new::<ampere>(0.0);
ld_current_out_ctrl_timer.now_i = ElectricCurrent::new::<ampere>(0.0); ld_current_out_ctrl_timer.now_i = ElectricCurrent::new::<ampere>(0.0);
ld_current_out_ctrl_timer.timeout = false; ld_current_out_ctrl_timer.timeout = false;
@ -57,29 +56,28 @@ impl LdCurrentOutCtrlTimer {
} }
pub fn set_target_i_and_listen_irq(target: ElectricCurrent, now: ElectricCurrent) { pub fn set_target_i_and_listen_irq(target: ElectricCurrent, now: ElectricCurrent) {
if let Some(ref mut ld_current_out_ctrl_timer ) = LdCurrentOutCtrlTimer::get() { if let Some(ref mut ld_current_out_ctrl_timer) = LdCurrentOutCtrlTimer::get() {
cortex_m::interrupt::free(|_| { cortex_m::interrupt::free(|_| {
ld_current_out_ctrl_timer.target_i = target; ld_current_out_ctrl_timer.target_i = target;
ld_current_out_ctrl_timer.now_i = now; ld_current_out_ctrl_timer.now_i = now;
ld_current_out_ctrl_timer.timer.listen(Event::Update); ld_current_out_ctrl_timer.timer.listen(Event::Update);
} })
)
} }
} }
pub fn set_alarm() { pub fn set_alarm() {
if let Some(ref mut ld_current_out_ctrl_timer ) = LdCurrentOutCtrlTimer::get() { if let Some(ref mut ld_current_out_ctrl_timer) = LdCurrentOutCtrlTimer::get() {
ld_current_out_ctrl_timer.timeout = true; ld_current_out_ctrl_timer.timeout = true;
} }
} }
pub fn clear_alarm_and_resume_listening() { pub fn clear_alarm_and_resume_listening() {
if let Some(ref mut ld_current_out_ctrl_timer ) = LdCurrentOutCtrlTimer::get() { if let Some(ref mut ld_current_out_ctrl_timer) = LdCurrentOutCtrlTimer::get() {
ld_current_out_ctrl_timer.timeout = false; ld_current_out_ctrl_timer.timeout = false;
ld_current_out_ctrl_timer.timer.listen(Event::Update); ld_current_out_ctrl_timer.timer.listen(Event::Update);
} }
} }
pub fn get_irq_status() -> Option<ElectricCurrent> { pub fn get_irq_status() -> Option<ElectricCurrent> {
if let Some(ref mut ld_current_out_ctrl_timer ) = LdCurrentOutCtrlTimer::get() { if let Some(ref mut ld_current_out_ctrl_timer) = LdCurrentOutCtrlTimer::get() {
if ld_current_out_ctrl_timer.timeout { if ld_current_out_ctrl_timer.timeout {
return Some(ld_current_out_ctrl_timer.now_i); return Some(ld_current_out_ctrl_timer.now_i);
} }
@ -88,37 +86,40 @@ impl LdCurrentOutCtrlTimer {
} }
pub fn clear_irq_flag() { pub fn clear_irq_flag() {
if let Some(ref mut ld_current_out_ctrl_timer ) = LdCurrentOutCtrlTimer::get() { if let Some(ref mut ld_current_out_ctrl_timer) = LdCurrentOutCtrlTimer::get() {
match ld_current_out_ctrl_timer.timer.wait() { match ld_current_out_ctrl_timer.timer.wait() {
Ok(_) => {} Ok(_) => {}
Err(_) => {debug!("LD CTRL TIMER Interrupt is not present when clear_irq_flag() is called")} Err(_) => {
debug!("LD CTRL TIMER Interrupt is not present when clear_irq_flag() is called")
}
} }
} }
} }
pub fn update_next_i_set() -> bool { pub fn update_next_i_set() -> bool {
if let Some(ref mut ld_current_out_ctrl_timer ) = LdCurrentOutCtrlTimer::get() { if let Some(ref mut ld_current_out_ctrl_timer) = LdCurrentOutCtrlTimer::get() {
let update = ld_current_out_ctrl_timer.now_i != ld_current_out_ctrl_timer.target_i; let update = ld_current_out_ctrl_timer.now_i != ld_current_out_ctrl_timer.target_i;
if ld_current_out_ctrl_timer.target_i > ld_current_out_ctrl_timer.now_i { if ld_current_out_ctrl_timer.target_i > ld_current_out_ctrl_timer.now_i {
ld_current_out_ctrl_timer.now_i = (ld_current_out_ctrl_timer.now_i + LdCurrentOutCtrlTimer::STEP_SIZE).min(ld_current_out_ctrl_timer.target_i); ld_current_out_ctrl_timer.now_i = (ld_current_out_ctrl_timer.now_i + LdCurrentOutCtrlTimer::STEP_SIZE)
.min(ld_current_out_ctrl_timer.target_i);
} else {
ld_current_out_ctrl_timer.now_i = (ld_current_out_ctrl_timer.now_i - LdCurrentOutCtrlTimer::STEP_SIZE)
.max(ld_current_out_ctrl_timer.target_i);
} }
else { return update;
ld_current_out_ctrl_timer.now_i = (ld_current_out_ctrl_timer.now_i - LdCurrentOutCtrlTimer::STEP_SIZE).max(ld_current_out_ctrl_timer.target_i);
}
return update
} }
false false
} }
pub fn stop_listening() { pub fn stop_listening() {
if let Some(ref mut ld_current_out_ctrl_timer ) = LdCurrentOutCtrlTimer::get() { if let Some(ref mut ld_current_out_ctrl_timer) = LdCurrentOutCtrlTimer::get() {
ld_current_out_ctrl_timer.timer.unlisten(Event::Update); ld_current_out_ctrl_timer.timer.unlisten(Event::Update);
} }
} }
} }
#[interrupt] #[interrupt]
fn TIM2(){ fn TIM2() {
if LdCurrentOutCtrlTimer::update_next_i_set() { if LdCurrentOutCtrlTimer::update_next_i_set() {
LdCurrentOutCtrlTimer::set_alarm(); LdCurrentOutCtrlTimer::set_alarm();
} }

View File

@ -1,15 +1,8 @@
use stm32f4xx_hal::pac; use stm32f4xx_hal::{gpio::{gpioa::PA3, gpiod::PD9, Analog, Output, PushPull},
use stm32f4xx_hal::rcc::Enable; interrupt, pac,
use stm32f4xx_hal::{ pac::{ADC3, NVIC},
pac::{ADC3, NVIC}, rcc::Enable};
gpio::{Analog, Output, PushPull, gpioa::PA3, gpiod::PD9}, use uom::si::{electric_potential::millivolt, f32::ElectricPotential, ratio::ratio};
interrupt,
};
use uom::si::{
electric_potential::millivolt,
f32::ElectricPotential,
ratio::ratio
};
// 12 bit Resolution // 12 bit Resolution
const MAX_SAMPLE: u16 = 4095; const MAX_SAMPLE: u16 = 4095;
@ -21,8 +14,8 @@ static mut LD_PWR_EXC_PROTECTOR: Option<LdPwrExcProtector> = None;
pub struct LdPwrExcProtectorPhy { pub struct LdPwrExcProtectorPhy {
// To make sure Pd Mon Pin is configured to Analog mode // To make sure Pd Mon Pin is configured to Analog mode
pub _pd_mon_ch0: PdMonAdcPinType, pub _pd_mon_ch0: PdMonAdcPinType,
pub pwr_en_ch0: LdPwrEnPinType, pub pwr_en_ch0: LdPwrEnPinType,
} }
#[derive(Clone)] #[derive(Clone)]
@ -48,14 +41,14 @@ pub struct LdPwrExcProtector {
pac: ADC3, pac: ADC3,
phy: LdPwrExcProtectorPhy, phy: LdPwrExcProtectorPhy,
alarm_status: Status, alarm_status: Status,
calibrated_vdda: u32, calibrated_vdda: u32,
} }
impl LdPwrExcProtector { impl LdPwrExcProtector {
/// ADC Analog Watchdog is configured to guard a single regular Adc channel on Pd Mon Pin. /// ADC Analog Watchdog is configured to guard a single regular Adc channel on Pd Mon Pin.
/// ADC is configured to start continuous conversion without using DMA immediately. /// ADC is configured to start continuous conversion without using DMA immediately.
/// Interrupt is disabled by default. /// Interrupt is disabled by default.
pub fn setup(pac_adc: ADC3, mut phy: LdPwrExcProtectorPhy){ pub fn setup(pac_adc: ADC3, mut phy: LdPwrExcProtectorPhy) {
unsafe { unsafe {
// All ADCs share the same reset interface. // All ADCs share the same reset interface.
// NOTE(unsafe) this reference will only be used for atomic writes with no side effects. // NOTE(unsafe) this reference will only be used for atomic writes with no side effects.
@ -71,60 +64,68 @@ impl LdPwrExcProtector {
pac_adc.sqr1.reset(); pac_adc.sqr1.reset();
pac_adc.sqr2.reset(); pac_adc.sqr2.reset();
pac_adc.sqr3.reset(); pac_adc.sqr3.reset();
pac_adc.cr1.write(|w| w pac_adc.cr1.write(|w| {
// 12 Bit Resolution w
.res().twelve_bit() // 12 Bit Resolution
// Set Analog Watchdog to guard Single Regular Channel .res()
.awden().enabled() .twelve_bit()
.awdsgl().single_channel() // Set Analog Watchdog to guard Single Regular Channel
.jawden().disabled() .awden()
// Disable Analog Watchdog Interrupt .enabled()
.awdie().disabled() .awdsgl()
// Set Analog Watchdog to monitor Pd Mon Pin .single_channel()
.awdch().variant(PD_MON_ADC_CH_ID) .jawden()
); .disabled()
pac_adc.cr2.write(|w| w // Disable Analog Watchdog Interrupt
// Continous Conversion Mode .awdie()
.cont().set_bit() .disabled()
// Power up ADC // Set Analog Watchdog to monitor Pd Mon Pin
.adon().set_bit() .awdch()
// Set data alignment to the right .variant(PD_MON_ADC_CH_ID)
.align().right() });
// End of conversion selection: Each Sequence pac_adc.cr2.write(|w| {
.eocs().each_sequence() w
.exten().disabled() // Continous Conversion Mode
.extsel().tim1cc1() .cont()
); .set_bit()
// Power up ADC
.adon()
.set_bit()
// Set data alignment to the right
.align()
.right()
// End of conversion selection: Each Sequence
.eocs()
.each_sequence()
.exten()
.disabled()
.extsel()
.tim1cc1()
});
// Set the Conversion Sequence to include Pd Mon Pin // Set the Conversion Sequence to include Pd Mon Pin
pac_adc.sqr3.write(|w| w pac_adc.sqr3.write(|w| w.sq1().variant(PD_MON_ADC_CH_ID));
.sq1().variant(PD_MON_ADC_CH_ID)
);
// Set all sampling channels to have fastest sampling interval // Set all sampling channels to have fastest sampling interval
pac_adc.smpr1.reset(); pac_adc.smpr1.reset();
pac_adc.smpr2.reset(); pac_adc.smpr2.reset();
// Set the higher threshold to be max value initially // Set the higher threshold to be max value initially
pac_adc.htr.write(|w| w.ht().variant(MAX_SAMPLE)); pac_adc.htr.write(|w| w.ht().variant(MAX_SAMPLE));
// Set the lower threshold to be min value initially // Set the lower threshold to be min value initially
pac_adc.ltr.write(|w| w.lt().variant(0)); pac_adc.ltr.write(|w| w.lt().variant(0));
// SWStart should only be set when ADON = 1. Otherwise no conversion is launched. // SWStart should only be set when ADON = 1. Otherwise no conversion is launched.
pac_adc.cr2.modify(|_, w| w pac_adc.cr2.modify(|_, w| w.swstart().set_bit());
.swstart().set_bit()
);
phy.pwr_en_ch0.set_low(); phy.pwr_en_ch0.set_low();
unsafe { unsafe {
LD_PWR_EXC_PROTECTOR = Some( LD_PWR_EXC_PROTECTOR = Some(LdPwrExcProtector {
LdPwrExcProtector { pac: pac_adc,
pac: pac_adc, phy: phy,
phy: phy, alarm_status: Status::default(),
alarm_status: Status::default(), calibrated_vdda: 3300,
calibrated_vdda: 3300, });
}
);
} }
} }
@ -132,36 +133,39 @@ impl LdPwrExcProtector {
unsafe { LD_PWR_EXC_PROTECTOR.as_mut() } unsafe { LD_PWR_EXC_PROTECTOR.as_mut() }
} }
fn convert_sample_to_volt(sample :u16) -> ElectricPotential { fn convert_sample_to_volt(sample: u16) -> ElectricPotential {
if let Some(ref mut wdg ) = LdPwrExcProtector::get() { if let Some(ref mut wdg) = LdPwrExcProtector::get() {
return ElectricPotential::new::<millivolt>(((u32::from(sample) * wdg.calibrated_vdda) / u32::from(MAX_SAMPLE)) as f32) return ElectricPotential::new::<millivolt>(
((u32::from(sample) * wdg.calibrated_vdda) / u32::from(MAX_SAMPLE)) as f32,
);
} }
ElectricPotential::new::<millivolt>(0.0) ElectricPotential::new::<millivolt>(0.0)
} }
pub fn set_trigger_threshold_v(htr: ElectricPotential){ pub fn set_trigger_threshold_v(htr: ElectricPotential) {
if let Some(ref mut wdg ) = LdPwrExcProtector::get() { if let Some(ref mut wdg) = LdPwrExcProtector::get() {
let code: u32 = ((htr / (ElectricPotential::new::<millivolt>(wdg.calibrated_vdda as f32))).get::<ratio>() * (MAX_SAMPLE as f32)) as u32; let code: u32 = ((htr / (ElectricPotential::new::<millivolt>(wdg.calibrated_vdda as f32))).get::<ratio>()
wdg.pac.htr.write(|w| unsafe {w.bits(code)}); * (MAX_SAMPLE as f32)) as u32;
wdg.pac.htr.write(|w| unsafe { w.bits(code) });
} }
} }
pub fn set_calibrated_vdda(val: u32) { pub fn set_calibrated_vdda(val: u32) {
if let Some(ref mut wdg ) = LdPwrExcProtector::get() { if let Some(ref mut wdg) = LdPwrExcProtector::get() {
wdg.calibrated_vdda = val; wdg.calibrated_vdda = val;
} }
} }
pub fn get_status() -> Status { pub fn get_status() -> Status {
if let Some(ref mut wdg ) = LdPwrExcProtector::get() { if let Some(ref mut wdg) = LdPwrExcProtector::get() {
wdg.alarm_status.v = LdPwrExcProtector::convert_sample_to_volt(wdg.pac.dr.read().data().bits()); wdg.alarm_status.v = LdPwrExcProtector::convert_sample_to_volt(wdg.pac.dr.read().data().bits());
return wdg.alarm_status.clone() return wdg.alarm_status.clone();
} }
Status::default() Status::default()
} }
pub fn pwr_on_and_arm_protection(){ pub fn pwr_on_and_arm_protection() {
if let Some(ref mut wdg ) = LdPwrExcProtector::get() { if let Some(ref mut wdg) = LdPwrExcProtector::get() {
wdg.alarm_status = Status::default(); wdg.alarm_status = Status::default();
LdPwrExcProtector::pwr_on(); LdPwrExcProtector::pwr_on();
// Interrupt should be enabled after power on to tackle the following edge case: // Interrupt should be enabled after power on to tackle the following edge case:
@ -170,53 +174,47 @@ impl LdPwrExcProtector {
} }
} }
pub fn clear_alarm_status(){ pub fn clear_alarm_status() {
if let Some(ref mut wdg ) = LdPwrExcProtector::get() { if let Some(ref mut wdg) = LdPwrExcProtector::get() {
wdg.alarm_status.pwr_excursion = false; wdg.alarm_status.pwr_excursion = false;
wdg.alarm_status.v_tripped = ElectricPotential::new::<millivolt>(0.0); wdg.alarm_status.v_tripped = ElectricPotential::new::<millivolt>(0.0);
} }
} }
fn enable_watchdog_interrupt(){ fn enable_watchdog_interrupt() {
if let Some(ref mut wdg ) = LdPwrExcProtector::get() { if let Some(ref mut wdg) = LdPwrExcProtector::get() {
wdg.pac.cr1.modify(|_, w| w wdg.pac.cr1.modify(|_, w| w.awdie().set_bit());
.awdie().set_bit()
);
} }
} }
fn disable_watchdog_interrupt(){ fn disable_watchdog_interrupt() {
if let Some(ref mut wdg ) = LdPwrExcProtector::get() { if let Some(ref mut wdg) = LdPwrExcProtector::get() {
wdg.pac.cr1.modify(|_, w| w wdg.pac.cr1.modify(|_, w| w.awdie().clear_bit());
.awdie().clear_bit()
);
} }
} }
fn clear_interrupt_bit(){ fn clear_interrupt_bit() {
if let Some(ref mut wdg ) = LdPwrExcProtector::get() { if let Some(ref mut wdg) = LdPwrExcProtector::get() {
wdg.pac.sr.modify(|_, w| w wdg.pac.sr.modify(|_, w| w.awd().clear_bit());
.awd().clear_bit()
);
} }
} }
fn pwr_on(){ fn pwr_on() {
if let Some(ref mut wdg ) = LdPwrExcProtector::get() { if let Some(ref mut wdg) = LdPwrExcProtector::get() {
wdg.alarm_status.pwr_engaged = true; wdg.alarm_status.pwr_engaged = true;
wdg.phy.pwr_en_ch0.set_high() wdg.phy.pwr_en_ch0.set_high()
} }
} }
pub fn pwr_off(){ pub fn pwr_off() {
if let Some(ref mut wdg ) = LdPwrExcProtector::get() { if let Some(ref mut wdg) = LdPwrExcProtector::get() {
wdg.alarm_status.pwr_engaged = false; wdg.alarm_status.pwr_engaged = false;
wdg.phy.pwr_en_ch0.set_low() wdg.phy.pwr_en_ch0.set_low()
} }
} }
fn pwr_excursion_handler(){ fn pwr_excursion_handler() {
if let Some(ref mut wdg ) = LdPwrExcProtector::get() { if let Some(ref mut wdg) = LdPwrExcProtector::get() {
let sample = wdg.pac.dr.read().data().bits(); let sample = wdg.pac.dr.read().data().bits();
LdPwrExcProtector::pwr_off(); LdPwrExcProtector::pwr_off();
wdg.alarm_status.pwr_excursion = true; wdg.alarm_status.pwr_excursion = true;
@ -226,12 +224,11 @@ impl LdPwrExcProtector {
} }
#[interrupt] #[interrupt]
fn ADC(){ fn ADC() {
cortex_m::interrupt::free(|_| { cortex_m::interrupt::free(|_| {
LdPwrExcProtector::pwr_excursion_handler(); LdPwrExcProtector::pwr_excursion_handler();
// Disable interrupt to avoid getting stuck in infinite loop // Disable interrupt to avoid getting stuck in infinite loop
LdPwrExcProtector::disable_watchdog_interrupt(); LdPwrExcProtector::disable_watchdog_interrupt();
LdPwrExcProtector::clear_interrupt_bit(); LdPwrExcProtector::clear_interrupt_bit();
} })
)
} }

View File

@ -1,9 +1,8 @@
use crate::device::sys_timer::sleep;
use fugit::MegahertzU32; use fugit::MegahertzU32;
use stm32f4xx_hal::{ use stm32f4xx_hal::{hal::{digital::OutputPin, spi::SpiBus},
hal::{spi::SpiBus, digital::OutputPin}, spi};
spi,
}; use crate::device::sys_timer::sleep;
pub const SPI_MODE: spi::Mode = spi::Mode { pub const SPI_MODE: spi::Mode = spi::Mode {
polarity: spi::Polarity::IdleLow, polarity: spi::Polarity::IdleLow,
@ -14,7 +13,7 @@ pub const SPI_CLOCK_MHZ: MegahertzU32 = MegahertzU32::from_raw(21);
pub const MAX_VALUE: u32 = 0xFFFFF; pub const MAX_VALUE: u32 = 0xFFFFF;
pub struct Dac<SPI: SpiBus<u8>, S1: OutputPin, S2:OutputPin> { pub struct Dac<SPI: SpiBus<u8>, S1: OutputPin, S2: OutputPin> {
spi: SPI, spi: SPI,
cs_n: S1, cs_n: S1,
load_n: S2, load_n: S2,
@ -25,7 +24,7 @@ impl<SPI: SpiBus<u8>, S1: OutputPin, S2: OutputPin> Dac<SPI, S1, S2> {
let _ = cs_n.set_high(); let _ = cs_n.set_high();
let _ = load_n.set_high(); let _ = load_n.set_high();
Dac { spi, cs_n, load_n} Dac { spi, cs_n, load_n }
} }
fn write(&mut self, buf: &mut [u8]) -> Result<(), SPI::Error> { fn write(&mut self, buf: &mut [u8]) -> Result<(), SPI::Error> {

View File

@ -1,6 +1,6 @@
pub mod ld_ctrl;
pub mod max5719;
pub mod laser_diode; pub mod laser_diode;
pub mod pd_mon_params; pub mod ld_ctrl;
pub mod ld_pwr_exc_protector;
pub mod ld_current_out_ctrl_timer; pub mod ld_current_out_ctrl_timer;
pub mod ld_pwr_exc_protector;
pub mod max5719;
pub mod pd_mon_params;

View File

@ -1,17 +1,14 @@
use core::{f32::NAN, marker::PhantomData}; use core::{f32::NAN, marker::PhantomData};
use serde::{Deserialize, Serialize};
use uom::si::{
f32::{
ElectricCurrent,
Power
},
electric_current::microampere,
};
use uom::{si::{ISQ, SI, Quantity}, typenum::*};
use miniconf::Tree; use miniconf::Tree;
use serde::{Deserialize, Serialize};
use uom::{si::{electric_current::microampere,
f32::{ElectricCurrent, Power},
Quantity, ISQ, SI},
typenum::*};
// Ampere / Watt // Ampere / Watt
pub type ResponsitivityUnit = Quantity<ISQ<N2, N1, P3, P1, Z0, Z0, Z0>, SI<f32>, f32>; pub type ResponsitivityUnit = Quantity<ISQ<N2, N1, P3, P1, Z0, Z0, Z0>, SI<f32>, f32>;
#[derive(Deserialize, Serialize, Clone, Copy, Debug, PartialEq, Tree)] #[derive(Deserialize, Serialize, Clone, Copy, Debug, PartialEq, Tree)]
pub struct Parameters { pub struct Parameters {
@ -42,7 +39,11 @@ impl Parameters {
impl Default for Parameters { impl Default for Parameters {
fn default() -> Self { fn default() -> Self {
Parameters { Parameters {
responsitivity: ResponsitivityUnit {dimension: PhantomData, units: PhantomData, value: NAN}, responsitivity: ResponsitivityUnit {
dimension: PhantomData,
units: PhantomData,
value: NAN,
},
i_dark: ElectricCurrent::new::<microampere>(0.0), i_dark: ElectricCurrent::new::<microampere>(0.0),
} }
} }

View File

@ -2,22 +2,24 @@
#![cfg_attr(not(test), no_std)] #![cfg_attr(not(test), no_std)]
use cortex_m_rt::entry; use cortex_m_rt::entry;
use log::{info, debug}; use log::{debug, info};
use stm32f4xx_hal::pac::{CorePeripherals, Peripherals}; use stm32f4xx_hal::pac::{CorePeripherals, Peripherals};
mod device; mod device;
mod laser_diode; mod laser_diode;
mod thermostat;
mod net; mod net;
mod thermostat;
use core::ptr::addr_of_mut; use core::ptr::addr_of_mut;
use device::{boot::bootup, log_setup, sys_timer}; use device::{boot::bootup, log_setup, sys_timer};
use crate::net::net::IpSettings;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use stm32f4xx_hal::pac::SCB; use stm32f4xx_hal::pac::SCB;
// If RTT is used, print panic info through RTT // If RTT is used, print panic info through RTT
#[cfg(all(feature = "RTT", not(test)))] #[cfg(all(feature = "RTT", not(test)))]
use {core::panic::PanicInfo, rtt_target::rprintln}; use {core::panic::PanicInfo, rtt_target::rprintln};
use crate::net::net::IpSettings;
#[cfg(all(feature = "RTT", not(test)))] #[cfg(all(feature = "RTT", not(test)))]
#[panic_handler] #[panic_handler]
fn panic(info: &PanicInfo) -> ! { fn panic(info: &PanicInfo) -> ! {
@ -31,7 +33,7 @@ use panic_halt as _;
static mut ETH_DATA_BUFFER: [u8; 1024] = [0; 1024]; static mut ETH_DATA_BUFFER: [u8; 1024] = [0; 1024];
#[derive(Deserialize, Serialize, Clone, Copy, Debug)] #[derive(Deserialize, Serialize, Clone, Copy, Debug)]
pub struct DeviceSettings{ pub struct DeviceSettings {
ip_settings: IpSettings, ip_settings: IpSettings,
} }
@ -57,22 +59,22 @@ fn main() -> ! {
let core_perif = CorePeripherals::take().unwrap(); let core_perif = CorePeripherals::take().unwrap();
let perif = Peripherals::take().unwrap(); let perif = Peripherals::take().unwrap();
let (mut wd, mut flash_store, mut laser, mut thermostat,) = bootup(core_perif, perif); let (mut wd, mut flash_store, mut laser, mut thermostat) = bootup(core_perif, perif);
let mut device_settings = DeviceSettings { let mut device_settings = DeviceSettings {
ip_settings: IpSettings::default() ip_settings: IpSettings::default(),
}; };
let mut active_report: [bool; net::net::NUM_OF_SOCKETS] = [false; net::net::NUM_OF_SOCKETS]; let mut active_report: [bool; net::net::NUM_OF_SOCKETS] = [false; net::net::NUM_OF_SOCKETS];
let mut state = State::default(); let mut state = State::default();
let eth_data_buffer = unsafe { addr_of_mut!(ETH_DATA_BUFFER).as_mut().unwrap()}; let eth_data_buffer = unsafe { addr_of_mut!(ETH_DATA_BUFFER).as_mut().unwrap() };
loop { loop {
wd.feed(); wd.feed();
if !net::net::eth_poll_link_status_and_update_link_speed() { if net::net::eth_poll_link_status_and_update_link_speed() {
active_report = [false; net::net::NUM_OF_SOCKETS]; active_report = [false; net::net::NUM_OF_SOCKETS];
} }
@ -97,7 +99,7 @@ fn main() -> ! {
continue; continue;
} }
} }
wd.feed(); wd.feed();
let laser_settings: laser_diode::laser_diode::LdSettingsSummary; let laser_settings: laser_diode::laser_diode::LdSettingsSummary;
match flash_store.read_value(CONFIG_KEY[1]) { match flash_store.read_value(CONFIG_KEY[1]) {
@ -139,9 +141,9 @@ fn main() -> ! {
} }
State::MainLoop => { State::MainLoop => {
let mut eth_is_pending = false; let mut eth_is_pending = false;
laser.poll_and_update_output_current(); laser.poll_and_update_output_current();
if thermostat.poll_adc() { if thermostat.poll_adc() {
thermostat.update_pid(); thermostat.update_pid();
if thermostat.get_temp_mon_status().over_temp_alarm { if thermostat.get_temp_mon_status().over_temp_alarm {
@ -150,34 +152,45 @@ fn main() -> ! {
thermostat.power_down(); thermostat.power_down();
} }
net::net::for_each(|mut socket, id| { net::net::for_each(|mut socket, id| {
if net::net::eth_is_socket_active(socket) && net::net::eth_is_socket_connected(socket) { if net::net::eth_is_socket_active(socket) && net::net::eth_is_socket_connected(socket) {
if active_report[id] { if active_report[id] {
net::cmd_handler::send_status_report(eth_data_buffer, &mut laser, &mut thermostat, &mut socket); net::cmd_handler::send_status_report(
eth_data_buffer,
&mut laser,
&mut thermostat,
&mut socket,
);
} }
} } else {
else {
active_report[id] = false; active_report[id] = false;
} }
}); });
thermostat.start_tec_readings_conversion(); thermostat.start_tec_readings_conversion();
} }
cortex_m::interrupt::free(|cs| cortex_m::interrupt::free(|cs| {
{ eth_is_pending = net::net::is_pending(cs);
eth_is_pending = net::net::is_pending(cs); net::net::clear_pending(cs);
net::net::clear_pending(cs); });
}
);
if eth_is_pending { if eth_is_pending {
net::net::for_each(|mut socket, id| { net::net::for_each(|mut socket, id| {
if net::net::eth_is_socket_active(socket) && net::net::eth_is_socket_connected(socket){ if net::net::eth_is_socket_active(socket) && net::net::eth_is_socket_connected(socket) {
let bytes = net::net::eth_recv(eth_data_buffer, socket); let bytes = net::net::eth_recv(eth_data_buffer, socket);
if bytes != 0 { if bytes != 0 {
info!("Ts: {:?}", sys_timer::now()); info!("Ts: {:?}", sys_timer::now());
debug!("Number of bytes recv: {:?}", bytes); debug!("Number of bytes recv: {:?}", bytes);
// State Transition // State Transition
net::cmd_handler::execute_cmd(eth_data_buffer, bytes, &mut socket, &mut laser, &mut thermostat, &mut state, &mut device_settings, &mut active_report[id]); net::cmd_handler::execute_cmd(
eth_data_buffer,
bytes,
&mut socket,
&mut laser,
&mut thermostat,
&mut state,
&mut device_settings,
&mut active_report[id],
);
} }
} }
}) })
@ -230,9 +243,14 @@ fn main() -> ! {
wd.feed(); wd.feed();
laser.power_down(); laser.power_down();
thermostat.power_down(); thermostat.power_down();
net::net::for_each(|mut socket, _| { net::net::for_each(|mut socket, _| {
if net::net::eth_is_socket_active(socket) { if net::net::eth_is_socket_active(socket) {
net::cmd_handler::send_response(eth_data_buffer, net::cmd_handler::ResponseEnum::HardReset, None, &mut socket); net::cmd_handler::send_response(
eth_data_buffer,
net::cmd_handler::ResponseEnum::HardReset,
None,
&mut socket,
);
} }
}); });
} }
@ -241,18 +259,18 @@ fn main() -> ! {
laser.power_down(); laser.power_down();
thermostat.power_down(); thermostat.power_down();
let mut any_socket_alive = false; let mut any_socket_alive = false;
net::net::for_each(|socket, _| { net::net::for_each(|socket, _| {
if net::net::eth_is_socket_active(socket) { if net::net::eth_is_socket_active(socket) {
net::net::eth_close_socket(socket); net::net::eth_close_socket(socket);
any_socket_alive = true; any_socket_alive = true;
} }
}); });
// Must let loop run for one more cycle to poll server for RST to be sent, // 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. // this makes sure system does not reset right after socket.abort() is called.
if !any_socket_alive { if !any_socket_alive {
SCB::sys_reset(); SCB::sys_reset();
} }
} }
} }
} }

View File

@ -1,26 +1,24 @@
use core::{fmt::Debug, marker::PhantomData}; use core::{fmt::Debug, marker::PhantomData};
use log::{debug, info};
use miniconf::{JsonCoreSlash, Tree}; use miniconf::{JsonCoreSlash, Tree};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use uom::si::{
electric_current::{ampere, ElectricCurrent},
electric_potential::{volt, ElectricPotential},
electrical_resistance::{ohm, ElectricalResistance},
power::{watt, Power},
thermodynamic_temperature::{degree_celsius, ThermodynamicTemperature}
};
use crate::{laser_diode::{laser_diode::{
LdDrive, LdSettingsSummary, StatusReport as LdStatusReport},
pd_mon_params::ResponsitivityUnit
},
net::net,
thermostat::{ad7172::FilterType, thermostat::{StatusReport as TecStatusReport, TempAdcFilter}}
};
use crate::thermostat::thermostat::{Thermostat, ThermostatSettingsSummary};
use crate::thermostat::pid_state::PidSettings::*;
use crate::device::{dfu, sys_timer};
use log::{info, debug};
use crate::{DeviceSettings, State, IpSettings};
use smoltcp::iface::SocketHandle; use smoltcp::iface::SocketHandle;
use uom::si::{electric_current::{ampere, ElectricCurrent},
electric_potential::{volt, ElectricPotential},
electrical_resistance::{ohm, ElectricalResistance},
power::{watt, Power},
thermodynamic_temperature::{degree_celsius, ThermodynamicTemperature}};
use crate::{device::{dfu, sys_timer},
laser_diode::{laser_diode::{LdDrive, LdSettingsSummary, StatusReport as LdStatusReport},
pd_mon_params::ResponsitivityUnit},
net::net,
thermostat::{ad7172::FilterType,
pid_state::PidSettings::*,
thermostat::{StatusReport as TecStatusReport, TempAdcFilter, Thermostat,
ThermostatSettingsSummary}},
DeviceSettings, IpSettings, State};
#[derive(Deserialize, Serialize, Copy, Clone, Default, Debug)] #[derive(Deserialize, Serialize, Copy, Clone, Default, Debug)]
pub enum ResponseEnum { pub enum ResponseEnum {
@ -42,19 +40,19 @@ pub struct Response<'a> {
msg: Option<&'a str>, msg: Option<&'a str>,
} }
impl Default for Response<'static>{ impl Default for Response<'static> {
fn default() -> Self { fn default() -> Self {
Response{ Response {
msg_type: ResponseEnum:: Reserved, msg_type: ResponseEnum::Reserved,
msg: None, msg: None,
} }
} }
} }
#[derive(Deserialize, Serialize, Copy, Clone, Debug, Tree)] #[derive(Deserialize, Serialize, Copy, Clone, Debug, Tree)]
pub struct ResponseObj<'a>{ pub struct ResponseObj<'a> {
#[serde(borrow)] #[serde(borrow)]
json: Response<'a> json: Response<'a>,
} }
#[derive(Deserialize, Serialize, Copy, Clone, Default, Debug)] #[derive(Deserialize, Serialize, Copy, Clone, Default, Debug)]
@ -124,7 +122,7 @@ enum ThermostatCmdEnum {
SetShBeta, SetShBeta,
} }
const ERR_MSG_MISSING_DATA_F32 : &str = "Required field \"data_f32\" does not exist"; const ERR_MSG_MISSING_DATA_F32: &str = "Required field \"data_f32\" does not exist";
const ERR_MSG_MISSING_DATA_BOOL: &str = "Required field \"bool\" does not exist"; const ERR_MSG_MISSING_DATA_BOOL: &str = "Required field \"bool\" does not exist";
const ERR_MSG_MISSING_IP_SETTINGS: &str = "Required field \"ip_settings\" does not exist"; const ERR_MSG_MISSING_IP_SETTINGS: &str = "Required field \"ip_settings\" does not exist";
const ERR_MSG_MISSING_TEMP_ADC_FILTER: &str = "Required field \"temp_adc_filter\" does not exist"; const ERR_MSG_MISSING_TEMP_ADC_FILTER: &str = "Required field \"temp_adc_filter\" does not exist";
@ -134,7 +132,7 @@ const ERR_MSG_MISSING_POSTFILTER: &str = "Required field \"PostFilter\" does not
const ERR_MSG_MISSING_SINC3FINEODR: &str = "Required field \"sinc3fineodr\" does not exist"; const ERR_MSG_MISSING_SINC3FINEODR: &str = "Required field \"sinc3fineodr\" does not exist";
#[derive(Deserialize, Serialize, Copy, Clone, Debug, Default, Tree)] #[derive(Deserialize, Serialize, Copy, Clone, Debug, Default, Tree)]
pub struct CmdJsonObj{ pub struct CmdJsonObj {
laser_diode_cmd: Option<LdCmdEnum>, laser_diode_cmd: Option<LdCmdEnum>,
thermostat_cmd: Option<ThermostatCmdEnum>, thermostat_cmd: Option<ThermostatCmdEnum>,
device_cmd: Option<DeviceCmd>, device_cmd: Option<DeviceCmd>,
@ -146,7 +144,7 @@ pub struct CmdJsonObj{
} }
#[derive(Deserialize, Serialize, Copy, Clone, Debug, Default, Tree)] #[derive(Deserialize, Serialize, Copy, Clone, Debug, Default, Tree)]
pub struct Cmd { pub struct Cmd {
json: CmdJsonObj json: CmdJsonObj,
} }
#[derive(Deserialize, Serialize, Copy, Clone, Debug, Tree)] #[derive(Deserialize, Serialize, Copy, Clone, Debug, Tree)]
@ -159,7 +157,7 @@ pub struct StatusReport {
#[derive(Deserialize, Serialize, Copy, Clone, Debug, Tree)] #[derive(Deserialize, Serialize, Copy, Clone, Debug, Tree)]
pub struct StatusReportObj { pub struct StatusReportObj {
json: StatusReport json: StatusReport,
} }
#[derive(Deserialize, Serialize, Copy, Clone, Debug, Tree)] #[derive(Deserialize, Serialize, Copy, Clone, Debug, Tree)]
@ -171,15 +169,15 @@ pub struct SettingsSummary {
#[derive(Deserialize, Serialize, Copy, Clone, Debug, Tree)] #[derive(Deserialize, Serialize, Copy, Clone, Debug, Tree)]
pub struct SettingsSummaryObj { pub struct SettingsSummaryObj {
json: SettingsSummary json: SettingsSummary,
} }
pub fn send_response(buffer: &mut [u8], msg_type: ResponseEnum, msg: MsgType, socket: &mut SocketHandle){ pub fn send_response(buffer: &mut [u8], msg_type: ResponseEnum, msg: MsgType, socket: &mut SocketHandle) {
let response = ResponseObj { let response = ResponseObj {
json: Response { json: Response {
msg_type: msg_type, msg_type: msg_type,
msg: msg, msg: msg,
} },
}; };
debug!("{:?}", response.json); debug!("{:?}", response.json);
@ -189,13 +187,18 @@ pub fn send_response(buffer: &mut [u8], msg_type: ResponseEnum, msg: MsgType, so
net::eth_send(buffer, num_bytes, *socket); net::eth_send(buffer, num_bytes, *socket);
} }
pub fn send_settings_summary(buffer: &mut [u8], laser: &mut LdDrive, thermostat: &mut Thermostat, socket: &mut SocketHandle){ pub fn send_settings_summary(
buffer: &mut [u8],
laser: &mut LdDrive,
thermostat: &mut Thermostat,
socket: &mut SocketHandle,
) {
let settings_summary = SettingsSummaryObj { let settings_summary = SettingsSummaryObj {
json: SettingsSummary { json: SettingsSummary {
msg_type: ResponseEnum::Settings, msg_type: ResponseEnum::Settings,
laser: laser.get_settings_summary(), laser: laser.get_settings_summary(),
thermostat: thermostat.get_settings_summary(), thermostat: thermostat.get_settings_summary(),
} },
}; };
let mut num_bytes = settings_summary.get_json("/json", buffer).unwrap(); let mut num_bytes = settings_summary.get_json("/json", buffer).unwrap();
buffer[num_bytes] = b'\n'; buffer[num_bytes] = b'\n';
@ -203,15 +206,19 @@ pub fn send_settings_summary(buffer: &mut [u8], laser: &mut LdDrive, thermostat:
net::eth_send(buffer, num_bytes, *socket); net::eth_send(buffer, num_bytes, *socket);
} }
pub fn send_status_report(
pub fn send_status_report(buffer: &mut [u8], laser: &mut LdDrive, thermostat: &mut Thermostat, socket: &mut SocketHandle){ buffer: &mut [u8],
laser: &mut LdDrive,
thermostat: &mut Thermostat,
socket: &mut SocketHandle,
) {
let status_report = StatusReportObj { let status_report = StatusReportObj {
json: StatusReport { json: StatusReport {
ts: sys_timer::now(), ts: sys_timer::now(),
msg_type: ResponseEnum::Report, msg_type: ResponseEnum::Report,
laser: laser.get_status_report(), laser: laser.get_status_report(),
thermostat: thermostat.get_status_report(), thermostat: thermostat.get_status_report(),
} },
}; };
let mut num_bytes = status_report.get_json("/json", buffer).unwrap(); let mut num_bytes = status_report.get_json("/json", buffer).unwrap();
buffer[num_bytes] = b'\n'; buffer[num_bytes] = b'\n';
@ -222,52 +229,67 @@ pub fn send_status_report(buffer: &mut [u8], laser: &mut LdDrive, thermostat: &m
// Use a minimal struct for high speed cmd ctrl to reduce processing overhead // Use a minimal struct for high speed cmd ctrl to reduce processing overhead
#[derive(Deserialize, Serialize, Copy, Clone, Debug, Default, Tree)] #[derive(Deserialize, Serialize, Copy, Clone, Debug, Default, Tree)]
pub struct TecSetICmdJson { pub struct TecSetICmdJson {
tec_set_i: f32 tec_set_i: f32,
} }
#[derive(Deserialize, Serialize, Copy, Clone, Debug, Default, Tree)] #[derive(Deserialize, Serialize, Copy, Clone, Debug, Default, Tree)]
pub struct TecSetICmd { pub struct TecSetICmd {
json: TecSetICmdJson json: TecSetICmdJson,
} }
/// Miniconf is very slow in debug builds (~3-4ms of cmd decoding time). /// Miniconf is very slow in debug builds (~3-4ms of cmd decoding time).
/// Make sure kirdy's firmware is flashed with release builds. /// Make sure kirdy's firmware is flashed with release builds.
/// The received message must contain only one json cmd. TCP client should set TCP_NODELAY or equivalent flag in its TCP Socket /// The received message must contain only one json cmd. TCP client should set TCP_NODELAY or equivalent flag in its TCP Socket
/// Settings to avoid unwanted buffering on TX Data and minimize TX latency. /// Settings to avoid unwanted buffering on TX Data and minimize TX latency.
pub fn execute_cmd(buffer: &mut [u8], buffer_size: usize, socket: &mut SocketHandle, laser: &mut LdDrive, thermostat: &mut Thermostat, state: &mut State, device_settings: &mut DeviceSettings, active_report: &mut bool){ pub fn execute_cmd(
buffer: &mut [u8],
buffer_size: usize,
socket: &mut SocketHandle,
laser: &mut LdDrive,
thermostat: &mut Thermostat,
state: &mut State,
device_settings: &mut DeviceSettings,
active_report: &mut bool,
) {
let mut cmd = TecSetICmd { let mut cmd = TecSetICmd {
json: TecSetICmdJson::default() json: TecSetICmdJson::default(),
}; };
match cmd.set_json("/json", &buffer[0..buffer_size]){ match cmd.set_json("/json", &buffer[0..buffer_size]) {
Ok(_) => { Ok(_) => {
thermostat.set_i(ElectricCurrent::new::<ampere>(cmd.json.tec_set_i)); thermostat.set_i(ElectricCurrent::new::<ampere>(cmd.json.tec_set_i));
send_response(buffer, ResponseEnum::Acknowledge, None, socket); send_response(buffer, ResponseEnum::Acknowledge, None, socket);
return; return;
} }
Err(_) => { /* Do Nothing */} Err(_) => { /* Do Nothing */ }
} }
let mut cmd = Cmd { let mut cmd = Cmd {
json: CmdJsonObj::default() json: CmdJsonObj::default(),
}; };
match cmd.set_json("/json", &buffer[0..buffer_size]){ match cmd.set_json("/json", &buffer[0..buffer_size]) {
Ok(_) => { Ok(_) => {
info!("############ Laser Diode Command Received {:?}", cmd.json.laser_diode_cmd); info!(
"############ Laser Diode Command Received {:?}",
cmd.json.laser_diode_cmd
);
info!("############ Thermostat Command Received {:?}", cmd.json.thermostat_cmd); info!("############ Thermostat Command Received {:?}", cmd.json.thermostat_cmd);
info!("############ Device Command Received {:?}", cmd.json.device_cmd); info!("############ Device Command Received {:?}", cmd.json.device_cmd);
match cmd.json.device_cmd { match cmd.json.device_cmd {
Some(DeviceCmd::SetIPSettings) => { Some(DeviceCmd::SetIPSettings) => match cmd.json.ip_settings {
match cmd.json.ip_settings { Some(val) => {
Some(val) => { send_response(buffer, ResponseEnum::Acknowledge, None, socket);
send_response(buffer, ResponseEnum::Acknowledge, None, socket); device_settings.ip_settings = val;
device_settings.ip_settings = val; *state = State::SaveDeviceSettings;
*state = State::SaveDeviceSettings;
}
None => {
send_response(buffer, ResponseEnum::InvalidDatatype, Some(ERR_MSG_MISSING_IP_SETTINGS), socket);
}
} }
} None => {
send_response(
buffer,
ResponseEnum::InvalidDatatype,
Some(ERR_MSG_MISSING_IP_SETTINGS),
socket,
);
}
},
Some(DeviceCmd::Dfu) => { Some(DeviceCmd::Dfu) => {
send_response(buffer, ResponseEnum::Acknowledge, None, socket); send_response(buffer, ResponseEnum::Acknowledge, None, socket);
unsafe { unsafe {
@ -275,17 +297,20 @@ pub fn execute_cmd(buffer: &mut [u8], buffer_size: usize, socket: &mut SocketHan
} }
*state = State::HardReset; *state = State::HardReset;
} }
Some(DeviceCmd::SetActiveReportMode) => { Some(DeviceCmd::SetActiveReportMode) => match cmd.json.data_bool {
match cmd.json.data_bool{ Some(val) => {
Some(val) => { send_response(buffer, ResponseEnum::Acknowledge, None, socket);
send_response(buffer, ResponseEnum::Acknowledge, None, socket); *active_report = val;
*active_report = val;
}
None => {
send_response(buffer, ResponseEnum::InvalidDatatype, Some(ERR_MSG_MISSING_DATA_BOOL), socket);
}
} }
} None => {
send_response(
buffer,
ResponseEnum::InvalidDatatype,
Some(ERR_MSG_MISSING_DATA_BOOL),
socket,
);
}
},
Some(DeviceCmd::GetStatusReport) => { Some(DeviceCmd::GetStatusReport) => {
send_status_report(buffer, laser, thermostat, socket); send_status_report(buffer, laser, thermostat, socket);
} }
@ -304,26 +329,28 @@ pub fn execute_cmd(buffer: &mut [u8], buffer_size: usize, socket: &mut SocketHan
send_response(buffer, ResponseEnum::Acknowledge, None, socket); send_response(buffer, ResponseEnum::Acknowledge, None, socket);
*state = State::PrepareForHardReset; *state = State::PrepareForHardReset;
} }
None => { /* Do Nothing */} None => { /* Do Nothing */ }
_ => { _ => {
send_response(buffer, ResponseEnum::InvalidCmd, None, socket); send_response(buffer, ResponseEnum::InvalidCmd, None, socket);
debug!("Unimplemented Command") debug!("Unimplemented Command")
} }
} }
match cmd.json.laser_diode_cmd { match cmd.json.laser_diode_cmd {
Some(LdCmdEnum::SetDefaultPowerOn) => { Some(LdCmdEnum::SetDefaultPowerOn) => match cmd.json.data_bool {
match cmd.json.data_bool { Some(val) => {
Some(val) => { send_response(buffer, ResponseEnum::Acknowledge, None, socket);
send_response(buffer, ResponseEnum::Acknowledge, None, socket); laser.set_default_pwr_on(val);
laser.set_default_pwr_on(val);
}
None => {
send_response(buffer, ResponseEnum::InvalidDatatype, Some(ERR_MSG_MISSING_DATA_BOOL), socket);
}
} }
None => {
} send_response(
buffer,
ResponseEnum::InvalidDatatype,
Some(ERR_MSG_MISSING_DATA_BOOL),
socket,
);
}
},
Some(LdCmdEnum::PowerUp) => { Some(LdCmdEnum::PowerUp) => {
send_response(buffer, ResponseEnum::Acknowledge, None, socket); send_response(buffer, ResponseEnum::Acknowledge, None, socket);
laser.power_up() laser.power_up()
@ -340,61 +367,80 @@ pub fn execute_cmd(buffer: &mut [u8], buffer_size: usize, socket: &mut SocketHan
send_response(buffer, ResponseEnum::Acknowledge, None, socket); send_response(buffer, ResponseEnum::Acknowledge, None, socket);
laser.ld_open(); laser.ld_open();
} }
Some(LdCmdEnum::SetI) => { Some(LdCmdEnum::SetI) => match cmd.json.data_f32 {
match cmd.json.data_f32 { Some(val) => {
Some(val) => { send_response(buffer, ResponseEnum::Acknowledge, None, socket);
send_response(buffer, ResponseEnum::Acknowledge, None, socket); laser.ld_set_i(ElectricCurrent::new::<ampere>(val));
laser.ld_set_i(ElectricCurrent::new::<ampere>(val));
}
None => {
send_response(buffer, ResponseEnum::InvalidDatatype, Some(ERR_MSG_MISSING_DATA_F32), socket);
}
} }
} None => {
Some(LdCmdEnum::SetISoftLimit) => { send_response(
match cmd.json.data_f32 { buffer,
Some(val) => { ResponseEnum::InvalidDatatype,
send_response(buffer, ResponseEnum::Acknowledge, None, socket); Some(ERR_MSG_MISSING_DATA_F32),
laser.set_ld_drive_current_limit(ElectricCurrent::new::<ampere>(val)) socket,
} );
None => {
send_response(buffer, ResponseEnum::InvalidDatatype, Some(ERR_MSG_MISSING_DATA_F32), socket);
}
} }
} },
Some(LdCmdEnum::SetPdResponsitivity) => { Some(LdCmdEnum::SetISoftLimit) => match cmd.json.data_f32 {
match cmd.json.data_f32 { Some(val) => {
Some(val) => { send_response(buffer, ResponseEnum::Acknowledge, None, socket);
send_response(buffer, ResponseEnum::Acknowledge, None, socket); laser.set_ld_drive_current_limit(ElectricCurrent::new::<ampere>(val))
laser.set_pd_responsitivity(ResponsitivityUnit {dimension: PhantomData, units: PhantomData, value: val})
}
None => {
send_response(buffer, ResponseEnum::InvalidDatatype, Some(ERR_MSG_MISSING_DATA_F32), socket);
}
} }
} None => {
Some(LdCmdEnum::SetPdDarkCurrent) => { send_response(
match cmd.json.data_f32 { buffer,
Some(val) => { ResponseEnum::InvalidDatatype,
send_response(buffer, ResponseEnum::Acknowledge, None, socket); Some(ERR_MSG_MISSING_DATA_F32),
laser.set_pd_dark_current(ElectricCurrent::new::<ampere>(val)) socket,
} );
None => {
send_response(buffer, ResponseEnum::InvalidDatatype, Some(ERR_MSG_MISSING_DATA_F32), socket);
}
} }
} },
Some(LdCmdEnum::SetLdPwrLimit) => { Some(LdCmdEnum::SetPdResponsitivity) => match cmd.json.data_f32 {
match cmd.json.data_f32 { Some(val) => {
Some(val) => { send_response(buffer, ResponseEnum::Acknowledge, None, socket);
send_response(buffer, ResponseEnum::Acknowledge, None, socket); laser.set_pd_responsitivity(ResponsitivityUnit {
laser.set_ld_power_limit(Power::new::<watt>(val)) dimension: PhantomData,
} units: PhantomData,
None => { value: val,
send_response(buffer, ResponseEnum::InvalidDatatype, Some(ERR_MSG_MISSING_DATA_F32), socket); })
}
} }
} None => {
send_response(
buffer,
ResponseEnum::InvalidDatatype,
Some(ERR_MSG_MISSING_DATA_F32),
socket,
);
}
},
Some(LdCmdEnum::SetPdDarkCurrent) => match cmd.json.data_f32 {
Some(val) => {
send_response(buffer, ResponseEnum::Acknowledge, None, socket);
laser.set_pd_dark_current(ElectricCurrent::new::<ampere>(val))
}
None => {
send_response(
buffer,
ResponseEnum::InvalidDatatype,
Some(ERR_MSG_MISSING_DATA_F32),
socket,
);
}
},
Some(LdCmdEnum::SetLdPwrLimit) => match cmd.json.data_f32 {
Some(val) => {
send_response(buffer, ResponseEnum::Acknowledge, None, socket);
laser.set_ld_power_limit(Power::new::<watt>(val))
}
None => {
send_response(
buffer,
ResponseEnum::InvalidDatatype,
Some(ERR_MSG_MISSING_DATA_F32),
socket,
);
}
},
Some(LdCmdEnum::ClearAlarm) => { Some(LdCmdEnum::ClearAlarm) => {
send_response(buffer, ResponseEnum::Acknowledge, None, socket); send_response(buffer, ResponseEnum::Acknowledge, None, socket);
laser.pd_mon_clear_alarm() laser.pd_mon_clear_alarm()
@ -407,17 +453,20 @@ pub fn execute_cmd(buffer: &mut [u8], buffer_size: usize, socket: &mut SocketHan
} }
match cmd.json.thermostat_cmd { match cmd.json.thermostat_cmd {
Some(ThermostatCmdEnum::SetDefaultPowerOn) => { Some(ThermostatCmdEnum::SetDefaultPowerOn) => match cmd.json.data_bool {
match cmd.json.data_bool { Some(val) => {
Some(val) => { send_response(buffer, ResponseEnum::Acknowledge, None, socket);
send_response(buffer, ResponseEnum::Acknowledge, None, socket); thermostat.set_default_pwr_on(val);
thermostat.set_default_pwr_on(val);
}
None => {
send_response(buffer, ResponseEnum::InvalidDatatype, Some(ERR_MSG_MISSING_DATA_F32), socket);
}
} }
} None => {
send_response(
buffer,
ResponseEnum::InvalidDatatype,
Some(ERR_MSG_MISSING_DATA_F32),
socket,
);
}
},
Some(ThermostatCmdEnum::PowerUp) => { Some(ThermostatCmdEnum::PowerUp) => {
send_response(buffer, ResponseEnum::Acknowledge, None, socket); send_response(buffer, ResponseEnum::Acknowledge, None, socket);
thermostat.power_up() thermostat.power_up()
@ -426,61 +475,76 @@ pub fn execute_cmd(buffer: &mut [u8], buffer_size: usize, socket: &mut SocketHan
send_response(buffer, ResponseEnum::Acknowledge, None, socket); send_response(buffer, ResponseEnum::Acknowledge, None, socket);
thermostat.power_down() thermostat.power_down()
} }
Some(ThermostatCmdEnum::SetTecMaxV) => { Some(ThermostatCmdEnum::SetTecMaxV) => match cmd.json.data_f32 {
match cmd.json.data_f32 { Some(val) => {
Some(val) => { send_response(buffer, ResponseEnum::Acknowledge, None, socket);
send_response(buffer, ResponseEnum::Acknowledge, None, socket); thermostat.set_max_v(ElectricPotential::new::<volt>(val));
thermostat.set_max_v(ElectricPotential::new::<volt>(val));
}
None => {
send_response(buffer, ResponseEnum::InvalidDatatype, Some(ERR_MSG_MISSING_DATA_F32), socket);
}
} }
} None => {
Some(ThermostatCmdEnum::SetTecMaxIPos) => { send_response(
match cmd.json.data_f32 { buffer,
Some(val) => { ResponseEnum::InvalidDatatype,
send_response(buffer, ResponseEnum::Acknowledge, None, socket); Some(ERR_MSG_MISSING_DATA_F32),
thermostat.set_max_i_pos(ElectricCurrent::new::<ampere>(val)); socket,
} );
None => {
send_response(buffer, ResponseEnum::InvalidDatatype, Some(ERR_MSG_MISSING_DATA_F32), socket);
}
} }
} },
Some(ThermostatCmdEnum::SetTecMaxINeg) => { Some(ThermostatCmdEnum::SetTecMaxIPos) => match cmd.json.data_f32 {
match cmd.json.data_f32 { Some(val) => {
Some(val) => { send_response(buffer, ResponseEnum::Acknowledge, None, socket);
send_response(buffer, ResponseEnum::Acknowledge, None, socket); thermostat.set_max_i_pos(ElectricCurrent::new::<ampere>(val));
thermostat.set_max_i_pos(ElectricCurrent::new::<ampere>(val));
}
None => {
send_response(buffer, ResponseEnum::InvalidDatatype, Some(ERR_MSG_MISSING_DATA_F32), socket);
}
} }
} None => {
Some(ThermostatCmdEnum::SetTecIOut) => { send_response(
match cmd.json.data_f32 { buffer,
Some(val) => { ResponseEnum::InvalidDatatype,
send_response(buffer, ResponseEnum::Acknowledge, None, socket); Some(ERR_MSG_MISSING_DATA_F32),
thermostat.set_i(ElectricCurrent::new::<ampere>(val)); socket,
} );
None => {
send_response(buffer, ResponseEnum::InvalidDatatype, Some(ERR_MSG_MISSING_DATA_F32), socket);
}
} }
} },
Some(ThermostatCmdEnum::SetTemperatureSetpoint) => { Some(ThermostatCmdEnum::SetTecMaxINeg) => match cmd.json.data_f32 {
match cmd.json.data_f32 { Some(val) => {
Some(val) => { send_response(buffer, ResponseEnum::Acknowledge, None, socket);
send_response(buffer, ResponseEnum::Acknowledge, None, socket); thermostat.set_max_i_pos(ElectricCurrent::new::<ampere>(val));
thermostat.set_temperature_setpoint(ThermodynamicTemperature::new::<degree_celsius>(val));
}
None => {
send_response(buffer, ResponseEnum::InvalidDatatype, Some(ERR_MSG_MISSING_DATA_F32), socket);
}
} }
} None => {
send_response(
buffer,
ResponseEnum::InvalidDatatype,
Some(ERR_MSG_MISSING_DATA_F32),
socket,
);
}
},
Some(ThermostatCmdEnum::SetTecIOut) => match cmd.json.data_f32 {
Some(val) => {
send_response(buffer, ResponseEnum::Acknowledge, None, socket);
thermostat.set_i(ElectricCurrent::new::<ampere>(val));
}
None => {
send_response(
buffer,
ResponseEnum::InvalidDatatype,
Some(ERR_MSG_MISSING_DATA_F32),
socket,
);
}
},
Some(ThermostatCmdEnum::SetTemperatureSetpoint) => match cmd.json.data_f32 {
Some(val) => {
send_response(buffer, ResponseEnum::Acknowledge, None, socket);
thermostat.set_temperature_setpoint(ThermodynamicTemperature::new::<degree_celsius>(val));
}
None => {
send_response(
buffer,
ResponseEnum::InvalidDatatype,
Some(ERR_MSG_MISSING_DATA_F32),
socket,
);
}
},
Some(ThermostatCmdEnum::SetPidEngage) => { Some(ThermostatCmdEnum::SetPidEngage) => {
send_response(buffer, ResponseEnum::Acknowledge, None, socket); send_response(buffer, ResponseEnum::Acknowledge, None, socket);
thermostat.set_pid_engaged(true); thermostat.set_pid_engaged(true);
@ -489,186 +553,230 @@ pub fn execute_cmd(buffer: &mut [u8], buffer_size: usize, socket: &mut SocketHan
send_response(buffer, ResponseEnum::Acknowledge, None, socket); send_response(buffer, ResponseEnum::Acknowledge, None, socket);
thermostat.set_pid_engaged(false); thermostat.set_pid_engaged(false);
} }
Some(ThermostatCmdEnum::SetPidKp) => { Some(ThermostatCmdEnum::SetPidKp) => match cmd.json.data_f32 {
match cmd.json.data_f32 { Some(val) => {
Some(val) => { send_response(buffer, ResponseEnum::Acknowledge, None, socket);
send_response(buffer, ResponseEnum::Acknowledge, None, socket); thermostat.set_pid(Kp, val);
thermostat.set_pid(Kp, val);
}
None => {
send_response(buffer, ResponseEnum::InvalidDatatype, Some(ERR_MSG_MISSING_DATA_F32), socket);
}
} }
} None => {
Some(ThermostatCmdEnum::SetPidKi) => { send_response(
match cmd.json.data_f32 { buffer,
Some(val) => { ResponseEnum::InvalidDatatype,
send_response(buffer, ResponseEnum::Acknowledge, None, socket); Some(ERR_MSG_MISSING_DATA_F32),
thermostat.set_pid(Ki, val); socket,
} );
None => {
send_response(buffer, ResponseEnum::InvalidDatatype, Some(ERR_MSG_MISSING_DATA_F32), socket);
}
} }
} },
Some(ThermostatCmdEnum::SetPidKd) => { Some(ThermostatCmdEnum::SetPidKi) => match cmd.json.data_f32 {
match cmd.json.data_f32 { Some(val) => {
Some(val) => { send_response(buffer, ResponseEnum::Acknowledge, None, socket);
send_response(buffer, ResponseEnum::Acknowledge, None, socket); thermostat.set_pid(Ki, val);
thermostat.set_pid(Kd, val);
}
None => {
send_response(buffer, ResponseEnum::InvalidDatatype, Some(ERR_MSG_MISSING_DATA_F32), socket);
}
} }
} None => {
Some(ThermostatCmdEnum::SetPidOutMin) => { send_response(
match cmd.json.data_f32 { buffer,
Some(val) => { ResponseEnum::InvalidDatatype,
send_response(buffer, ResponseEnum::Acknowledge, None, socket); Some(ERR_MSG_MISSING_DATA_F32),
thermostat.set_pid(Min, val); socket,
} );
None => {
send_response(buffer, ResponseEnum::InvalidDatatype, Some(ERR_MSG_MISSING_DATA_F32), socket);
}
} }
} },
Some(ThermostatCmdEnum::SetPidOutMax) => { Some(ThermostatCmdEnum::SetPidKd) => match cmd.json.data_f32 {
match cmd.json.data_f32 { Some(val) => {
Some(val) => { send_response(buffer, ResponseEnum::Acknowledge, None, socket);
send_response(buffer, ResponseEnum::Acknowledge, None, socket); thermostat.set_pid(Kd, val);
thermostat.set_pid(Max, val);
}
None => {
send_response(buffer, ResponseEnum::InvalidDatatype, Some(ERR_MSG_MISSING_DATA_F32), socket);
}
} }
} None => {
send_response(
buffer,
ResponseEnum::InvalidDatatype,
Some(ERR_MSG_MISSING_DATA_F32),
socket,
);
}
},
Some(ThermostatCmdEnum::SetPidOutMin) => match cmd.json.data_f32 {
Some(val) => {
send_response(buffer, ResponseEnum::Acknowledge, None, socket);
thermostat.set_pid(Min, val);
}
None => {
send_response(
buffer,
ResponseEnum::InvalidDatatype,
Some(ERR_MSG_MISSING_DATA_F32),
socket,
);
}
},
Some(ThermostatCmdEnum::SetPidOutMax) => match cmd.json.data_f32 {
Some(val) => {
send_response(buffer, ResponseEnum::Acknowledge, None, socket);
thermostat.set_pid(Max, val);
}
None => {
send_response(
buffer,
ResponseEnum::InvalidDatatype,
Some(ERR_MSG_MISSING_DATA_F32),
socket,
);
}
},
Some(ThermostatCmdEnum::SetPidUpdateInterval) => { Some(ThermostatCmdEnum::SetPidUpdateInterval) => {
send_response(buffer, ResponseEnum::InvalidCmd, None, socket); send_response(buffer, ResponseEnum::InvalidCmd, None, socket);
debug!("Not supported Yet") debug!("Not supported Yet")
} }
Some(ThermostatCmdEnum::ConfigTempAdcFilter) => { Some(ThermostatCmdEnum::ConfigTempAdcFilter) => match cmd.json.temp_adc_filter {
match cmd.json.temp_adc_filter { Some(val) => match val.filter_type {
Some(val) => { FilterType::Sinc5Sinc1With50hz60HzRejection => match val.sinc5sinc1postfilter {
match val.filter_type { Some(val2) => {
FilterType::Sinc5Sinc1With50hz60HzRejection => { send_response(buffer, ResponseEnum::Acknowledge, None, socket);
match val.sinc5sinc1postfilter { thermostat.set_temp_adc_sinc5_sinc1_with_postfilter(0, val2);
Some(val2) => {
send_response(buffer, ResponseEnum::Acknowledge, None, socket);
thermostat.set_temp_adc_sinc5_sinc1_with_postfilter(0, val2);
}
None => {
send_response(buffer, ResponseEnum::InvalidDatatype, Some(ERR_MSG_MISSING_POSTFILTER), socket);
}
}
}
FilterType::Sinc5Sinc1 => {
match val.sinc5sinc1odr {
Some(val2) => {
send_response(buffer, ResponseEnum::Acknowledge, None, socket);
thermostat.set_temp_adc_sinc5_sinc1_filter(0, val2);
}
None => {
send_response(buffer, ResponseEnum::InvalidDatatype, Some(ERR_MSG_MISSING_SINC5SINC1ODR), socket);
}
}
}
FilterType::Sinc3WithFineODR => {
match val.sinc3fineodr {
Some(val2) => {
send_response(buffer, ResponseEnum::Acknowledge, None, socket);
thermostat.set_temp_adc_sinc3_fine_filter(0, val2);
}
None => {
send_response(buffer, ResponseEnum::InvalidDatatype, Some(ERR_MSG_MISSING_SINC3FINEODR), socket);
}
}
}
FilterType::Sinc3 => {
match val.sinc3odr {
Some(val2) => {
send_response(buffer, ResponseEnum::Acknowledge, None, socket);
thermostat.set_temp_adc_sinc3_filter(0, val2);
}
None => {
send_response(buffer, ResponseEnum::InvalidDatatype, Some(ERR_MSG_MISSING_SINC3ODR), socket);
}
}
}
} }
} None => {
None => { send_response(
send_response(buffer, ResponseEnum::InvalidDatatype, Some(ERR_MSG_MISSING_TEMP_ADC_FILTER), socket); buffer,
} ResponseEnum::InvalidDatatype,
Some(ERR_MSG_MISSING_POSTFILTER),
socket,
);
}
},
FilterType::Sinc5Sinc1 => match val.sinc5sinc1odr {
Some(val2) => {
send_response(buffer, ResponseEnum::Acknowledge, None, socket);
thermostat.set_temp_adc_sinc5_sinc1_filter(0, val2);
}
None => {
send_response(
buffer,
ResponseEnum::InvalidDatatype,
Some(ERR_MSG_MISSING_SINC5SINC1ODR),
socket,
);
}
},
FilterType::Sinc3WithFineODR => match val.sinc3fineodr {
Some(val2) => {
send_response(buffer, ResponseEnum::Acknowledge, None, socket);
thermostat.set_temp_adc_sinc3_fine_filter(0, val2);
}
None => {
send_response(
buffer,
ResponseEnum::InvalidDatatype,
Some(ERR_MSG_MISSING_SINC3FINEODR),
socket,
);
}
},
FilterType::Sinc3 => match val.sinc3odr {
Some(val2) => {
send_response(buffer, ResponseEnum::Acknowledge, None, socket);
thermostat.set_temp_adc_sinc3_filter(0, val2);
}
None => {
send_response(
buffer,
ResponseEnum::InvalidDatatype,
Some(ERR_MSG_MISSING_SINC3ODR),
socket,
);
}
},
},
None => {
send_response(
buffer,
ResponseEnum::InvalidDatatype,
Some(ERR_MSG_MISSING_TEMP_ADC_FILTER),
socket,
);
} }
} },
Some(ThermostatCmdEnum::SetTempMonUpperLimit) => { Some(ThermostatCmdEnum::SetTempMonUpperLimit) => match cmd.json.data_f32 {
match cmd.json.data_f32 { Some(val) => {
Some(val) => { send_response(buffer, ResponseEnum::Acknowledge, None, socket);
send_response(buffer, ResponseEnum::Acknowledge, None, socket); thermostat.set_temp_mon_upper_limit(ThermodynamicTemperature::new::<degree_celsius>(val));
thermostat.set_temp_mon_upper_limit(ThermodynamicTemperature::new::<degree_celsius>(val));
}
None => {
send_response(buffer, ResponseEnum::InvalidDatatype, Some(ERR_MSG_MISSING_DATA_F32), socket);
}
} }
} None => {
Some(ThermostatCmdEnum::SetTempMonLowerLimit) => { send_response(
match cmd.json.data_f32 { buffer,
Some(val) => { ResponseEnum::InvalidDatatype,
send_response(buffer, ResponseEnum::Acknowledge, None, socket); Some(ERR_MSG_MISSING_DATA_F32),
thermostat.set_temp_mon_lower_limit(ThermodynamicTemperature::new::<degree_celsius>(val)); socket,
} );
None => {
send_response(buffer, ResponseEnum::InvalidDatatype, Some(ERR_MSG_MISSING_DATA_F32), socket);
}
} }
} },
Some(ThermostatCmdEnum::SetTempMonLowerLimit) => match cmd.json.data_f32 {
Some(val) => {
send_response(buffer, ResponseEnum::Acknowledge, None, socket);
thermostat.set_temp_mon_lower_limit(ThermodynamicTemperature::new::<degree_celsius>(val));
}
None => {
send_response(
buffer,
ResponseEnum::InvalidDatatype,
Some(ERR_MSG_MISSING_DATA_F32),
socket,
);
}
},
Some(ThermostatCmdEnum::ClearAlarm) => { Some(ThermostatCmdEnum::ClearAlarm) => {
send_response(buffer, ResponseEnum::Acknowledge, None, socket); send_response(buffer, ResponseEnum::Acknowledge, None, socket);
thermostat.clear_temp_mon_alarm(); thermostat.clear_temp_mon_alarm();
} }
Some(ThermostatCmdEnum::SetShT0) => { Some(ThermostatCmdEnum::SetShT0) => match cmd.json.data_f32 {
match cmd.json.data_f32 { Some(val) => {
Some(val) => { send_response(buffer, ResponseEnum::Acknowledge, None, socket);
send_response(buffer, ResponseEnum::Acknowledge, None, socket); thermostat.set_sh_t0(ThermodynamicTemperature::new::<degree_celsius>(val));
thermostat.set_sh_t0(ThermodynamicTemperature::new::<degree_celsius>(val));
}
None => {
send_response(buffer, ResponseEnum::InvalidDatatype, Some(ERR_MSG_MISSING_DATA_F32), socket);
}
} }
} None => {
Some(ThermostatCmdEnum::SetShR0) => { send_response(
match cmd.json.data_f32 { buffer,
Some(val) => { ResponseEnum::InvalidDatatype,
send_response(buffer, ResponseEnum::Acknowledge, None, socket); Some(ERR_MSG_MISSING_DATA_F32),
thermostat.set_sh_r0(ElectricalResistance::new::<ohm>(val)); socket,
} );
None => {
send_response(buffer, ResponseEnum::InvalidDatatype, Some(ERR_MSG_MISSING_DATA_F32), socket);
}
} }
} },
Some(ThermostatCmdEnum::SetShBeta) => { Some(ThermostatCmdEnum::SetShR0) => match cmd.json.data_f32 {
match cmd.json.data_f32 { Some(val) => {
Some(val) => { send_response(buffer, ResponseEnum::Acknowledge, None, socket);
send_response(buffer, ResponseEnum::Acknowledge, None, socket); thermostat.set_sh_r0(ElectricalResistance::new::<ohm>(val));
thermostat.set_sh_beta(val);
}
None => {
send_response(buffer, ResponseEnum::InvalidDatatype, Some(ERR_MSG_MISSING_DATA_F32), socket);
}
} }
} None => {
send_response(
buffer,
ResponseEnum::InvalidDatatype,
Some(ERR_MSG_MISSING_DATA_F32),
socket,
);
}
},
Some(ThermostatCmdEnum::SetShBeta) => match cmd.json.data_f32 {
Some(val) => {
send_response(buffer, ResponseEnum::Acknowledge, None, socket);
thermostat.set_sh_beta(val);
}
None => {
send_response(
buffer,
ResponseEnum::InvalidDatatype,
Some(ERR_MSG_MISSING_DATA_F32),
socket,
);
}
},
None => { /* Do Nothing*/ } None => { /* Do Nothing*/ }
_ => { _ => {
send_response(buffer, ResponseEnum::InvalidCmd, None, socket); send_response(buffer, ResponseEnum::InvalidCmd, None, socket);
} }
} }
} }
Err(_) => { Err(_) => {
info!("cmd_recv: {:?}", buffer);
send_response(buffer, ResponseEnum::InvalidCmd, None, socket); send_response(buffer, ResponseEnum::InvalidCmd, None, socket);
} }
} }

View File

@ -1,2 +1,2 @@
pub mod cmd_handler;
pub mod net; pub mod net;
pub mod cmd_handler;

View File

@ -1,25 +1,21 @@
use crate::device::sys_timer; use core::{cell::RefCell,
use core::mem::{self, MaybeUninit}; mem::{self, MaybeUninit}};
use core::cell::RefCell;
use cortex_m::interrupt::{CriticalSection, Mutex}; use cortex_m::interrupt::{CriticalSection, Mutex};
use log::{debug, info}; use log::{debug, info};
use smoltcp::{
iface::{
self, Interface, SocketHandle, SocketSet, SocketStorage
}, socket::tcp::{Socket, SocketBuffer, State}, time::{Instant, Duration}, wire::{EthernetAddress, IpAddress, IpCidr, Ipv4Address, Ipv4Cidr}
};
use stm32_eth::{
Parts, EthPins, PartsIn,
mac::{Speed, EthernetMACWithMii},
dma::{
TxRingEntry, RxRingEntry, EthernetDMA
}};
use stm32f4xx_hal::{
gpio::{gpioa::*, gpiob::*, gpioc::*, Alternate, Input, Pin},
rcc::Clocks,
interrupt,
};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use smoltcp::{iface::{self, Interface, SocketHandle, SocketSet, SocketStorage},
socket::tcp::{Socket, SocketBuffer, State},
time::{Duration, Instant},
wire::{EthernetAddress, IpAddress, IpCidr, Ipv4Address, Ipv4Cidr}};
use stm32_eth::{dma::{EthernetDMA, RxRingEntry, TxRingEntry},
mac::{EthernetMACWithMii, Speed},
EthPins, Parts, PartsIn};
use stm32f4xx_hal::{gpio::{gpioa::*, gpiob::*, gpioc::*, Alternate, Input, Pin},
interrupt,
rcc::Clocks};
use crate::device::sys_timer;
#[derive(Debug, Clone, Copy, Deserialize, Serialize)] #[derive(Debug, Clone, Copy, Deserialize, Serialize)]
pub struct IpSettings { pub struct IpSettings {
@ -35,7 +31,7 @@ impl Default for IpSettings {
addr: [192, 168, 1, 128], addr: [192, 168, 1, 128],
port: 1337, port: 1337,
prefix_len: 24, prefix_len: 24,
gateway: [192, 168, 1, 1] gateway: [192, 168, 1, 1],
} }
} }
} }
@ -53,55 +49,56 @@ pub struct ServerHandle {
phy: EthernetPhy<EthernetMACWithMii<Pin<'A', 2, Alternate<11>>, Pin<'C', 1, Alternate<11>>>>, phy: EthernetPhy<EthernetMACWithMii<Pin<'A', 2, Alternate<11>>, Pin<'C', 1, Alternate<11>>>>,
link_was_up: bool, link_was_up: bool,
} }
pub type EthernetPins = pub type EthernetPins = EthPins<PA1<Input>, PA7<Input>, PB11<Input>, PB12<Input>, PB13<Input>, PC4<Input>, PC5<Input>>;
EthPins<PA1<Input>, PA7<Input>, PB11<Input>, PB12<Input>, PB13<Input>, PC4<Input>, PC5<Input>>;
pub struct EthernetMgmtPins { pub struct EthernetMgmtPins {
pub mdio: PA2<Alternate<11>>, pub mdio: PA2<Alternate<11>>,
pub mdc: PC1<Alternate<11>>, pub mdc: PC1<Alternate<11>>,
} }
pub type EthInterface = Interface; pub type EthInterface = Interface;
pub const NUM_OF_SOCKETS : usize = 4; pub const NUM_OF_SOCKETS: usize = 4;
const TCP_BUFFER_SIZE: usize = 2048; const TCP_BUFFER_SIZE: usize = 4096;
static mut RX_RING: Option<[RxRingEntry; 8]> = None; static mut RX_RING: Option<[RxRingEntry; 8]> = None;
static mut TX_RING: Option<[TxRingEntry; 2]> = None; static mut TX_RING: Option<[TxRingEntry; 8]> = None;
static mut SOCKET_STORAGE: Option<[SocketStorage<'static>; NUM_OF_SOCKETS]> = None; static mut SOCKET_STORAGE: Option<[SocketStorage<'static>; NUM_OF_SOCKETS]> = None;
fn now_fn() -> smoltcp::time::Instant { fn now_fn() -> smoltcp::time::Instant {
Instant::from_millis(i64::from(sys_timer::now())) Instant::from_millis(i64::from(sys_timer::now()))
} }
static mut SERVER_HANDLE : Option<ServerHandle> = None; static mut SERVER_HANDLE: Option<ServerHandle> = None;
impl ServerHandle { impl ServerHandle {
pub fn new (eth_pins: EthernetPins, pub fn new(
eth_pins: EthernetPins,
eth_mgmt_pins: EthernetMgmtPins, eth_mgmt_pins: EthernetMgmtPins,
ethernet_parts_in: PartsIn, ethernet_parts_in: PartsIn,
clocks: Clocks, clocks: Clocks,
mac_addr: [u8; 6], mac_addr: [u8; 6],
ip_settings: IpSettings, ip_settings: IpSettings,
) { ) {
let rx_ring = unsafe { RX_RING.get_or_insert(Default::default()) }; let rx_ring = unsafe { RX_RING.get_or_insert(Default::default()) };
let tx_ring = unsafe { TX_RING.get_or_insert(Default::default()) }; let tx_ring = unsafe { TX_RING.get_or_insert(Default::default()) };
let socket_storage = unsafe { SOCKET_STORAGE.get_or_insert([SocketStorage::EMPTY; NUM_OF_SOCKETS]) }; let socket_storage = unsafe { SOCKET_STORAGE.get_or_insert([SocketStorage::EMPTY; NUM_OF_SOCKETS]) };
let Parts { let Parts { mut dma, mac, .. } = stm32_eth::new_with_mii(
mut dma,
mac,
#[cfg(feature = "ptp")]
..
} = stm32_eth::new_with_mii(
ethernet_parts_in, ethernet_parts_in,
&mut rx_ring[..], &mut rx_ring[..],
&mut tx_ring[..], &mut tx_ring[..],
clocks, clocks,
eth_pins, eth_pins,
eth_mgmt_pins.mdio, eth_mgmt_pins.mdio,
eth_mgmt_pins.mdc eth_mgmt_pins.mdc,
).unwrap(); )
.unwrap();
let ip_init = IpCidr::Ipv4(Ipv4Cidr::new( let ip_init = IpCidr::Ipv4(Ipv4Cidr::new(
Ipv4Address::new(ip_settings.addr[0], ip_settings.addr[1], ip_settings.addr[2], ip_settings.addr[3]), Ipv4Address::new(
ip_settings.addr[0],
ip_settings.addr[1],
ip_settings.addr[2],
ip_settings.addr[3],
),
ip_settings.prefix_len, ip_settings.prefix_len,
)); ));
let socket_addr: (IpAddress, u16) = ( let socket_addr: (IpAddress, u16) = (
@ -116,7 +113,12 @@ impl ServerHandle {
let mut routes = smoltcp::iface::Routes::new(); let mut routes = smoltcp::iface::Routes::new();
routes routes
.add_default_ipv4_route(Ipv4Address::new(ip_settings.gateway[0], ip_settings.gateway[1], ip_settings.gateway[2], ip_settings.gateway[3])) .add_default_ipv4_route(Ipv4Address::new(
ip_settings.gateway[0],
ip_settings.gateway[1],
ip_settings.gateway[2],
ip_settings.gateway[3],
))
.ok(); .ok();
dma.enable_interrupt(); dma.enable_interrupt();
@ -132,22 +134,20 @@ impl ServerHandle {
let mut socket_set = SocketSet::new(&mut socket_storage[..]); let mut socket_set = SocketSet::new(&mut socket_storage[..]);
let tcp_handles = { let tcp_handles = {
// Do not use NUM_OF_SOCKETS to define array size to // Do not use NUM_OF_SOCKETS to define array size to
// remind developers to create/remove tcp_handles accordingly after changing NUM_OF_SOCKETS // remind developers to create/remove tcp_handles accordingly after changing NUM_OF_SOCKETS
let mut tcp_handles: [MaybeUninit<SocketHandle>; 4]= unsafe { let mut tcp_handles: [MaybeUninit<SocketHandle>; 4] = unsafe { MaybeUninit::uninit().assume_init() };
MaybeUninit::uninit().assume_init()
};
macro_rules! create_tcp_handle { macro_rules! create_tcp_handle {
($rx_storage:ident, $tx_storage:ident, $handle:expr) => { ($rx_storage:ident, $tx_storage:ident, $handle:expr) => {
static mut $rx_storage : [u8; TCP_BUFFER_SIZE] = [0; TCP_BUFFER_SIZE]; static mut $rx_storage: [u8; TCP_BUFFER_SIZE] = [0; TCP_BUFFER_SIZE];
static mut $tx_storage : [u8; TCP_BUFFER_SIZE] = [0; TCP_BUFFER_SIZE]; static mut $tx_storage: [u8; TCP_BUFFER_SIZE] = [0; TCP_BUFFER_SIZE];
unsafe { unsafe {
let rx_buffer = SocketBuffer::new(&mut $rx_storage[..]); let rx_buffer = SocketBuffer::new(&mut $rx_storage[..]);
let tx_buffer = SocketBuffer::new(&mut $tx_storage[..]); let tx_buffer = SocketBuffer::new(&mut $tx_storage[..]);
$handle.write(socket_set.add(Socket::new(rx_buffer, tx_buffer))); $handle.write(socket_set.add(Socket::new(rx_buffer, tx_buffer)));
} }
} };
} }
create_tcp_handle!(RX_STORAGE0, TX_STORAGE0, tcp_handles[0]); create_tcp_handle!(RX_STORAGE0, TX_STORAGE0, tcp_handles[0]);
create_tcp_handle!(RX_STORAGE1, TX_STORAGE1, tcp_handles[1]); create_tcp_handle!(RX_STORAGE1, TX_STORAGE1, tcp_handles[1]);
@ -156,24 +156,25 @@ impl ServerHandle {
unsafe { mem::transmute::<_, [SocketHandle; 4]>(tcp_handles) } unsafe { mem::transmute::<_, [SocketHandle; 4]>(tcp_handles) }
}; };
for i in 0..NUM_OF_SOCKETS { for i in 0..NUM_OF_SOCKETS {
let socket = socket_set.get_mut::<Socket>(tcp_handles[i]); let socket = socket_set.get_mut::<Socket>(tcp_handles[i]);
socket.listen(socket_addr).ok(); socket.listen(socket_addr).ok();
socket.set_keep_alive(Some(Duration::from_secs(1))); socket.set_keep_alive(Some(Duration::from_secs(1)));
socket.set_nagle_enabled(false); socket.set_nagle_enabled(false);
} }
iface.poll(Instant::from_millis(i64::from(sys_timer::now())), &mut &mut dma, &mut socket_set); iface.poll(
Instant::from_millis(i64::from(sys_timer::now())),
&mut &mut dma,
&mut socket_set,
);
if let Ok(mut phy) = EthernetPhy::from_miim(mac, 0) { if let Ok(mut phy) = EthernetPhy::from_miim(mac, 0) {
info!( info!("Resetting PHY as an extra step. Type: {}", phy.ident_string());
"Resetting PHY as an extra step. Type: {}",
phy.ident_string()
);
phy.phy_init(); phy.phy_init();
let server = ServerHandle { let server = ServerHandle {
socket_handles: tcp_handles, socket_handles: tcp_handles,
socket_set: socket_set, socket_set: socket_set,
@ -183,7 +184,7 @@ impl ServerHandle {
phy: phy, phy: phy,
link_was_up: false, link_was_up: false,
}; };
unsafe { unsafe {
SERVER_HANDLE = Some(server); SERVER_HANDLE = Some(server);
} }
@ -192,7 +193,7 @@ impl ServerHandle {
} }
} }
pub fn update_link_speed(&mut self)-> bool { pub fn update_link_speed(&mut self) -> bool {
if !self.link_was_up & self.phy.phy_link_up() { if !self.link_was_up & self.phy.phy_link_up() {
if let Some(speed) = self.phy.speed().map(|s| match s { if let Some(speed) = self.phy.speed().map(|s| match s {
PhySpeed::HalfDuplexBase10T => Speed::HalfDuplexBase10T, PhySpeed::HalfDuplexBase10T => Speed::HalfDuplexBase10T,
@ -213,10 +214,14 @@ impl ServerHandle {
} }
pub fn poll_iface(&mut self) { pub fn poll_iface(&mut self) {
self.iface.poll(now_fn(), &mut &mut self.dma, &mut self.socket_set); self.iface.poll(now_fn(), &mut &mut self.dma, &mut self.socket_set);
} }
pub fn recv(&mut self, buffer: &mut [u8], socket_handles: SocketHandle)-> Result<usize, smoltcp::socket::tcp::RecvError> { pub fn recv(
&mut self,
buffer: &mut [u8],
socket_handles: SocketHandle,
) -> Result<usize, smoltcp::socket::tcp::RecvError> {
let socket = self.socket_set.get_mut::<Socket>(socket_handles); let socket = self.socket_set.get_mut::<Socket>(socket_handles);
socket.recv_slice(buffer) socket.recv_slice(buffer)
@ -231,12 +236,12 @@ impl ServerHandle {
} }
} }
pub fn is_socket_connected(&mut self, socket_handles: SocketHandle)->bool { pub fn is_socket_connected(&mut self, socket_handles: SocketHandle) -> bool {
let socket = self.socket_set.get_mut::<Socket>(socket_handles); let socket = self.socket_set.get_mut::<Socket>(socket_handles);
socket.state() == State::Established socket.state() == State::Established
} }
pub fn poll_socket_status(&mut self, socket_handles: SocketHandle)-> bool { pub fn poll_socket_status(&mut self, socket_handles: SocketHandle) -> bool {
let socket = self.socket_set.get_mut::<Socket>(socket_handles); let socket = self.socket_set.get_mut::<Socket>(socket_handles);
if !socket.is_listening() && !socket.is_open() || socket.state() == State::CloseWait { if !socket.is_listening() && !socket.is_open() || socket.state() == State::CloseWait {
socket.abort(); socket.abort();
@ -255,14 +260,9 @@ impl ServerHandle {
} }
} }
use ieee802_3_miim::{ use ieee802_3_miim::{phy::{lan87xxa::{LAN8720A, LAN8742A},
phy::{ BarePhy, PhySpeed, KSZ8081R},
lan87xxa::{LAN8720A, LAN8742A}, Miim, Pause, Phy};
BarePhy, KSZ8081R,
PhySpeed
},
Miim, Pause, Phy,
};
/// An ethernet PHY /// An ethernet PHY
pub enum EthernetPhy<M: Miim> { pub enum EthernetPhy<M: Miim> {
@ -359,7 +359,7 @@ impl<M: Miim> EthernetPhy<M> {
pub fn eth_poll_link_status_and_update_link_speed() -> bool { pub fn eth_poll_link_status_and_update_link_speed() -> bool {
unsafe { unsafe {
if let Some(ref mut server_handle ) = SERVER_HANDLE { if let Some(ref mut server_handle) = SERVER_HANDLE {
let new_link_is_up = server_handle.update_link_speed(); let new_link_is_up = server_handle.update_link_speed();
if new_link_is_up { if new_link_is_up {
info!("Resetting TCP Sockets"); info!("Resetting TCP Sockets");
@ -368,8 +368,7 @@ pub fn eth_poll_link_status_and_update_link_speed() -> bool {
}); });
} }
return new_link_is_up; return new_link_is_up;
} } else {
else {
panic!("eth_poll_link_status_and_update_link_speed is called before init"); panic!("eth_poll_link_status_and_update_link_speed is called before init");
} }
} }
@ -377,10 +376,9 @@ pub fn eth_poll_link_status_and_update_link_speed() -> bool {
pub fn eth_poll_iface() { pub fn eth_poll_iface() {
unsafe { unsafe {
if let Some(ref mut server_handle ) = SERVER_HANDLE { if let Some(ref mut server_handle) = SERVER_HANDLE {
server_handle.poll_iface(); server_handle.poll_iface();
} } else {
else {
panic!("eth_poll_packet is called before init"); panic!("eth_poll_packet is called before init");
} }
} }
@ -388,27 +386,25 @@ pub fn eth_poll_iface() {
pub fn eth_send(buffer: &mut [u8], num_bytes: usize, socket_handles: SocketHandle) { pub fn eth_send(buffer: &mut [u8], num_bytes: usize, socket_handles: SocketHandle) {
unsafe { unsafe {
if let Some(ref mut server_handle ) = SERVER_HANDLE { if let Some(ref mut server_handle) = SERVER_HANDLE {
server_handle.send(buffer, num_bytes, socket_handles); server_handle.send(buffer, num_bytes, socket_handles);
} } else {
else {
panic!("eth_send is called before init"); panic!("eth_send is called before init");
} }
} }
} }
pub fn eth_recv(buffer: &mut [u8], socket_handles: SocketHandle)-> usize{ pub fn eth_recv(buffer: &mut [u8], socket_handles: SocketHandle) -> usize {
unsafe { unsafe {
if let Some(ref mut server_handle ) = SERVER_HANDLE { if let Some(ref mut server_handle) = SERVER_HANDLE {
match server_handle.recv(buffer, socket_handles){ match server_handle.recv(buffer, socket_handles) {
Ok(recv_bytes) => {return recv_bytes} Ok(recv_bytes) => return recv_bytes,
Err(err) => { Err(err) => {
debug!("TCP Recv Error: {}", err); debug!("TCP Recv Error: {}", err);
return 0 return 0;
} }
}; };
} } else {
else {
panic!("eth_send is called before init"); panic!("eth_send is called before init");
} }
} }
@ -416,10 +412,9 @@ pub fn eth_recv(buffer: &mut [u8], socket_handles: SocketHandle)-> usize{
pub fn eth_is_socket_connected(socket_handles: SocketHandle) -> bool { pub fn eth_is_socket_connected(socket_handles: SocketHandle) -> bool {
unsafe { unsafe {
if let Some(ref mut server_handle ) = SERVER_HANDLE { if let Some(ref mut server_handle) = SERVER_HANDLE {
server_handle.is_socket_connected(socket_handles) server_handle.is_socket_connected(socket_handles)
} } else {
else {
panic!("eth_is_socket_connected is called before init"); panic!("eth_is_socket_connected is called before init");
} }
} }
@ -427,10 +422,9 @@ pub fn eth_is_socket_connected(socket_handles: SocketHandle) -> bool {
pub fn eth_is_socket_active(socket_handles: SocketHandle) -> bool { pub fn eth_is_socket_active(socket_handles: SocketHandle) -> bool {
unsafe { unsafe {
if let Some(ref mut server_handle ) = SERVER_HANDLE { if let Some(ref mut server_handle) = SERVER_HANDLE {
server_handle.poll_socket_status(socket_handles) server_handle.poll_socket_status(socket_handles)
} } else {
else {
panic!("eth_is_socket_active is called before init"); panic!("eth_is_socket_active is called before init");
} }
} }
@ -438,10 +432,9 @@ pub fn eth_is_socket_active(socket_handles: SocketHandle) -> bool {
pub fn eth_close_socket(socket_handles: SocketHandle) { pub fn eth_close_socket(socket_handles: SocketHandle) {
unsafe { unsafe {
if let Some(ref mut server_handle ) = SERVER_HANDLE { if let Some(ref mut server_handle) = SERVER_HANDLE {
server_handle.close_socket(socket_handles) server_handle.close_socket(socket_handles)
} } else {
else {
panic!("eth_close_socket is called before init"); panic!("eth_close_socket is called before init");
} }
} }
@ -449,12 +442,11 @@ pub fn eth_close_socket(socket_handles: SocketHandle) {
pub fn for_each<F: FnMut(SocketHandle, usize)>(mut callback: F) { pub fn for_each<F: FnMut(SocketHandle, usize)>(mut callback: F) {
unsafe { unsafe {
if let Some(ref mut server_handle ) = SERVER_HANDLE { if let Some(ref mut server_handle) = SERVER_HANDLE {
for i in 0..NUM_OF_SOCKETS { for i in 0..NUM_OF_SOCKETS {
callback(server_handle.socket_handles[i], i); callback(server_handle.socket_handles[i], i);
} }
} } else {
else {
panic!("eth_close_socket is called before init"); panic!("eth_close_socket is called before init");
} }
} }
@ -467,8 +459,7 @@ fn ETH() {
let interrupt_reason = stm32_eth::eth_interrupt_handler(); let interrupt_reason = stm32_eth::eth_interrupt_handler();
cortex_m::interrupt::free(|cs| { cortex_m::interrupt::free(|cs| {
if interrupt_reason.rx { if interrupt_reason.rx {
*NET_PENDING.borrow(cs) *NET_PENDING.borrow(cs).borrow_mut() = true;
.borrow_mut() = true;
eth_poll_iface(); eth_poll_iface();
} }
}); });
@ -477,13 +468,11 @@ 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) *NET_PENDING.borrow(cs).borrow()
.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) *NET_PENDING.borrow(cs).borrow_mut() = false;
.borrow_mut() = false;
} }

View File

@ -1,9 +1,8 @@
use crate::device::sys_timer::sleep;
use fugit::MegahertzU32; use fugit::MegahertzU32;
use stm32f4xx_hal::{ use stm32f4xx_hal::{hal::{digital::OutputPin, spi::SpiBus},
hal::{spi::SpiBus, digital::OutputPin}, spi};
spi,
}; use crate::device::sys_timer::sleep;
/// SPI Mode 1 /// SPI Mode 1
pub const SPI_MODE: spi::Mode = spi::Mode { pub const SPI_MODE: spi::Mode = spi::Mode {

View File

@ -1,22 +1,16 @@
use core::fmt; use core::fmt;
use log::{info, warn}; use log::{info, warn};
use stm32f4xx_hal:: use stm32f4xx_hal::{gpio::{Output, PushPull, PA15},
{ hal::{digital::OutputPin, spi::SpiBus},
spi::Spi, pac::SPI3,
pac::SPI3, spi::Spi};
gpio::{PA15, Output, PushPull}, use uom::si::{electric_potential::volt, f32::ElectricPotential};
hal::{
spi::SpiBus, use super::{checksum::{Checksum, ChecksumMode},
digital::OutputPin, regs::{self, Register, RegisterData},
}, sinc3_fine_odr_closest, sinc3_fine_odr_output_rate, DigitalFilterOrder, FilterType, Input, Mode,
}; PostFilter, RefSource, SingleChODR};
use uom::si::{
f32::ElectricPotential,
electric_potential::volt,
};
use super::{
checksum::{Checksum, ChecksumMode}, regs::{self, Register, RegisterData}, sinc3_fine_odr_closest, sinc3_fine_odr_output_rate, DigitalFilterOrder, FilterType, Input, Mode, PostFilter, RefSource, SingleChODR
};
/// AD7172-2 implementation /// AD7172-2 implementation
/// ///
@ -35,7 +29,8 @@ impl<SPI: SpiBus<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, nss, spi,
nss,
checksum_mode: ChecksumMode::Off, checksum_mode: ChecksumMode::Off,
}; };
adc.reset()?; adc.reset()?;
@ -64,8 +59,7 @@ impl<SPI: SpiBus<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) self.read_reg(&regs::Id).map(|id| id.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> {
@ -84,9 +78,7 @@ impl<SPI: SpiBus<u8, Error = E>, NSS: OutputPin, E: fmt::Debug> Adc<SPI, NSS> {
}) })
} }
pub fn setup_channel( pub fn setup_channel(&mut self, index: u8, in_pos: Input, in_neg: Input) -> Result<(), SPI::Error> {
&mut self, index: u8, in_pos: Input, in_neg: Input
) -> 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);
data.set_refbuf_pos(true); data.set_refbuf_pos(true);
@ -127,29 +119,40 @@ impl<SPI: SpiBus<u8, Error = E>, NSS: OutputPin, E: fmt::Debug> Adc<SPI, NSS> {
pub fn get_filter_type_and_rate(&mut self, index: u8) -> Result<(FilterType, f32), SPI::Error> { pub fn get_filter_type_and_rate(&mut self, index: u8) -> Result<(FilterType, f32), SPI::Error> {
let mut filter_type: FilterType = FilterType::Sinc5Sinc1With50hz60HzRejection; let mut filter_type: FilterType = FilterType::Sinc5Sinc1With50hz60HzRejection;
let mut rate: f32 = -1.0; let mut rate: f32 = -1.0;
self.read_reg(&regs::FiltCon { index }) self.read_reg(&regs::FiltCon { index }).map(|data| {
.map(|data| {
if data.sinc3_map() { if data.sinc3_map() {
filter_type = FilterType::Sinc3WithFineODR; filter_type = FilterType::Sinc3WithFineODR;
let odr : u16 = (data.sinc3_map_fine_odr_msb() as u16) << 8 | data.sinc3_map_fine_odr_lsb() as u16; let odr: u16 = (data.sinc3_map_fine_odr_msb() as u16) << 8 | data.sinc3_map_fine_odr_lsb() as u16;
rate = sinc3_fine_odr_output_rate(odr); rate = sinc3_fine_odr_output_rate(odr);
} else if data.enh_filt_en() { } else if data.enh_filt_en() {
filter_type = FilterType::Sinc5Sinc1With50hz60HzRejection; filter_type = FilterType::Sinc5Sinc1With50hz60HzRejection;
match data.enh_filt().output_rate(){ match data.enh_filt().output_rate() {
Some(val) => { rate = val; } Some(val) => {
None => { rate = -1.0; } rate = val;
}
None => {
rate = -1.0;
}
}; };
} else if data.order() == DigitalFilterOrder::Sinc5Sinc1 { } else if data.order() == DigitalFilterOrder::Sinc5Sinc1 {
filter_type = FilterType::Sinc5Sinc1; filter_type = FilterType::Sinc5Sinc1;
match data.odr().output_rate(){ match data.odr().output_rate() {
Some(val) => { rate = val; } Some(val) => {
None => { rate = -1.0; } rate = val;
}
None => {
rate = -1.0;
}
} }
} else { } else {
filter_type = FilterType::Sinc3; filter_type = FilterType::Sinc3;
match data.odr().output_rate(){ match data.odr().output_rate() {
Some(val) => { rate = val; } Some(val) => {
None => { rate = -1.0; } rate = val;
}
None => {
rate = -1.0;
}
} }
} }
})?; })?;
@ -186,30 +189,24 @@ impl<SPI: SpiBus<u8, Error = E>, NSS: OutputPin, E: fmt::Debug> Adc<SPI, NSS> {
pub fn set_sinc3_fine_filter(&mut self, index: u8, rate: f32) -> Result<f32, SPI::Error> { pub fn set_sinc3_fine_filter(&mut self, index: u8, rate: f32) -> Result<f32, SPI::Error> {
let sinc3_fine_odr_u16 = sinc3_fine_odr_closest(rate); let sinc3_fine_odr_u16 = sinc3_fine_odr_closest(rate);
self.update_reg(&regs::FiltCon { index }, |data| { self.update_reg(&regs::FiltCon { index }, |data| {
data.set_sinc3_map(true); data.set_sinc3_map(true);
data.set_sinc3_map_fine_odr_msb((sinc3_fine_odr_u16 >> 8 & 0xFF) as u8); data.set_sinc3_map_fine_odr_msb((sinc3_fine_odr_u16 >> 8 & 0xFF) as u8);
data.set_sinc3_map_fine_odr_lsb((sinc3_fine_odr_u16 & 0xFF) as u8); data.set_sinc3_map_fine_odr_lsb((sinc3_fine_odr_u16 & 0xFF) as u8);
}).map(|_| sinc3_fine_odr_output_rate(sinc3_fine_odr_u16)) })
.map(|_| sinc3_fine_odr_output_rate(sinc3_fine_odr_u16))
} }
/// 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) self.read_reg(&regs::Status)
.map(|status| { .map(|status| if status.ready() { Some(status.channel()) } else { None })
if status.ready() {
Some(status.channel())
} 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) self.read_reg(&regs::Data).map(|data| data.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> {
@ -228,7 +225,12 @@ impl<SPI: SpiBus<u8, Error = E>, NSS: OutputPin, E: fmt::Debug> Adc<SPI, NSS> {
break; break;
} }
// Retry // Retry
warn!("read_reg {:02X}: checksum error: {:?}!={:?}, retrying", reg.address(), checksum_expected, checksum_in); warn!(
"read_reg {:02X}: checksum error: {:?}!={:?}, retrying",
reg.address(),
checksum_expected,
checksum_in
);
} }
Ok(reg_data) Ok(reg_data)
} }
@ -254,7 +256,10 @@ impl<SPI: SpiBus<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!("write_reg {:02X}: readback error, {:?}!={:?}, retrying", address, &*readback_data, &**reg_data); warn!(
"write_reg {:02X}: readback error, {:?}!={:?}, retrying",
address, &*readback_data, &**reg_data
);
} }
} }
@ -278,7 +283,12 @@ impl<SPI: SpiBus<u8, Error = E>, NSS: OutputPin, E: fmt::Debug> Adc<SPI, NSS> {
Ok(()) Ok(())
} }
fn transfer<'w>(&mut self, addr: u8, reg_data: &'w mut [u8], checksum: Option<u8>) -> Result<Option<u8>, SPI::Error> { fn transfer<'w>(
&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();
@ -287,8 +297,7 @@ impl<SPI: SpiBus<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_in_place(&mut checksum_buf) { match self.spi.transfer_in_place(&mut checksum_buf) {
@ -296,8 +305,7 @@ impl<SPI: SpiBus<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,11 +1,12 @@
use core::fmt; use core::fmt;
use num_traits::float::Float;
use serde::{Serialize, Deserialize};
use fugit::MegahertzU32; use fugit::MegahertzU32;
use num_traits::float::Float;
use serde::{Deserialize, Serialize};
use stm32f4xx_hal::spi; use stm32f4xx_hal::spi;
pub mod regs;
mod checksum; mod checksum;
pub mod regs;
pub use checksum::ChecksumMode; pub use checksum::ChecksumMode;
mod adc; mod adc;
pub use adc::*; pub use adc::*;
@ -20,7 +21,6 @@ pub const SPI_CLOCK_MHZ: MegahertzU32 = MegahertzU32::from_raw(21);
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 {
@ -103,7 +103,8 @@ impl fmt::Display for Input {
RefPos => "ref+", RefPos => "ref+",
RefNeg => "ref-", RefNeg => "ref-",
_ => "<INVALID>", _ => "<INVALID>",
}.fmt(fmt) }
.fmt(fmt)
} }
} }
@ -139,7 +140,8 @@ impl fmt::Display for RefSource {
Internal => "internal", Internal => "internal",
Avdd1MinusAvss => "avdd1-avss", Avdd1MinusAvss => "avdd1-avss",
_ => "<INVALID>", _ => "<INVALID>",
}.fmt(fmt) }
.fmt(fmt)
} }
} }
@ -169,9 +171,7 @@ impl PostFilter {
let mut best: Option<(f32, Self)> = None; let mut best: Option<(f32, Self)> = None;
for value in Self::VALID_VALUES { for value in Self::VALID_VALUES {
let error = (rate - value.output_rate().unwrap()).abs(); let error = (rate - value.output_rate().unwrap()).abs();
let better = best let better = best.map(|(best_error, _)| error < best_error).unwrap_or(true);
.map(|(best_error, _)| error < best_error)
.unwrap_or(true);
if better { if better {
best = Some((error, *value)); best = Some((error, *value));
} }
@ -237,22 +237,22 @@ pub enum SingleChODR {
F31250_0SPS = 0b00101, F31250_0SPS = 0b00101,
F15625_0SPS = 0b00110, F15625_0SPS = 0b00110,
F10417_0SPS = 0b00111, F10417_0SPS = 0b00111,
F5208_0SPS = 0b01000, F5208_0SPS = 0b01000,
F2597_0SPS = 0b01001, F2597_0SPS = 0b01001,
F1007_0SPS = 0b01010, F1007_0SPS = 0b01010,
F503_8SPS = 0b01011, F503_8SPS = 0b01011,
F381_0SPS = 0b01100, F381_0SPS = 0b01100,
F200_3SPS = 0b01101, F200_3SPS = 0b01101,
F100_2SPS = 0b01110, F100_2SPS = 0b01110,
F59_52SPS = 0b01111, F59_52SPS = 0b01111,
F49_68SPS = 0b10000, F49_68SPS = 0b10000,
F20_01SPS = 0b10001, F20_01SPS = 0b10001,
F16_63SPS = 0b10010, F16_63SPS = 0b10010,
F10_0SPS = 0b10011, F10_0SPS = 0b10011,
F5_0SPS = 0b10100, F5_0SPS = 0b10100,
F2_5SPS = 0b10101, F2_5SPS = 0b10101,
F1_25SPS = 0b10110, F1_25SPS = 0b10110,
Invalid = 0b11111, Invalid = 0b11111,
} }
impl SingleChODR { impl SingleChODR {
@ -281,9 +281,7 @@ impl SingleChODR {
let mut best: Option<(f32, Self)> = None; let mut best: Option<(f32, Self)> = None;
for value in Self::VALID_VALUES { for value in Self::VALID_VALUES {
let error = (rate - value.output_rate().unwrap()).abs(); let error = (rate - value.output_rate().unwrap()).abs();
let better = best let better = best.map(|(best_error, _)| error < best_error).unwrap_or(true);
.map(|(best_error, _)| error < best_error)
.unwrap_or(true);
if better { if better {
best = Some((error, *value)); best = Some((error, *value));
} }
@ -334,7 +332,7 @@ impl From<u8> for SingleChODR {
0b10000 => SingleChODR::F49_68SPS, 0b10000 => SingleChODR::F49_68SPS,
0b10001 => SingleChODR::F20_01SPS, 0b10001 => SingleChODR::F20_01SPS,
0b10010 => SingleChODR::F16_63SPS, 0b10010 => SingleChODR::F16_63SPS,
0b10011 => SingleChODR::F10_0SPS, 0b10011 => SingleChODR::F10_0SPS,
0b10100 => SingleChODR::F5_0SPS, 0b10100 => SingleChODR::F5_0SPS,
0b10101 => SingleChODR::F2_5SPS, 0b10101 => SingleChODR::F2_5SPS,
0b10110 => SingleChODR::F1_25SPS, 0b10110 => SingleChODR::F1_25SPS,
@ -344,9 +342,9 @@ impl From<u8> for SingleChODR {
} }
pub fn sinc3_fine_odr_output_rate(odr: u16) -> f32 { pub fn sinc3_fine_odr_output_rate(odr: u16) -> f32 {
1.0 * 1e6 / (32.0 * odr as f32) 1.0 * 1e6 / (32.0 * odr as f32)
} }
pub fn sinc3_fine_odr_closest(rate: f32) -> u16 { pub fn sinc3_fine_odr_closest(rate: f32) -> u16 {
(1.0e6 / ( 32.0 * rate )).max(1.0 as f32).min(0x7FFF as f32) as u16 (1.0e6 / (32.0 * rate)).max(1.0 as f32).min(0x7FFF as f32) as u16
} }

View File

@ -1,6 +1,7 @@
use core::ops::{Deref, DerefMut}; use core::ops::{Deref, DerefMut};
use byteorder::{BigEndian, ByteOrder};
use bit_field::BitField; use bit_field::BitField;
use byteorder::{BigEndian, ByteOrder};
use super::*; use super::*;
@ -9,12 +10,12 @@ 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;
} }
macro_rules! def_reg { macro_rules! def_reg {
($Reg: ident, $reg: ident, $addr: expr, $size: expr) => { ($Reg:ident, $reg:ident, $addr:expr, $size:expr) => {
/// AD7172 register /// AD7172 register
pub struct $Reg; pub struct $Reg;
impl Register for $Reg { impl Register for $Reg {
@ -48,8 +49,10 @@ 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 index: u8, } pub struct $Reg {
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 {
@ -76,18 +79,18 @@ macro_rules! def_reg {
} }
} }
} }
} };
} }
macro_rules! reg_bit { macro_rules! reg_bit {
($getter: ident, $byte: expr, $bit: expr, $doc: expr) => { ($getter:ident, $byte:expr, $bit:expr, $doc:expr) => {
#[allow(unused)] #[allow(unused)]
#[doc = $doc] #[doc = $doc]
pub fn $getter(&self) -> bool { pub fn $getter(&self) -> bool {
self.0[$byte].get_bit($bit) self.0[$byte].get_bit($bit)
} }
}; };
($getter: ident, $setter: ident, $byte: expr, $bit: expr, $doc: expr) => { ($getter:ident, $setter:ident, $byte:expr, $bit:expr, $doc:expr) => {
#[allow(unused)] #[allow(unused)]
#[doc = $doc] #[doc = $doc]
pub fn $getter(&self) -> bool { pub fn $getter(&self) -> bool {
@ -102,14 +105,14 @@ macro_rules! reg_bit {
} }
macro_rules! reg_bits { macro_rules! reg_bits {
($getter: ident, $byte: expr, $bits: expr, $doc: expr) => { ($getter:ident, $byte:expr, $bits:expr, $doc:expr) => {
#[allow(unused)] #[allow(unused)]
#[doc = $doc] #[doc = $doc]
pub fn $getter(&self) -> u8 { pub fn $getter(&self) -> u8 {
self.0[$byte].get_bits($bits) self.0[$byte].get_bits($bits)
} }
}; };
($getter: ident, $setter: ident, $byte: expr, $bits: expr, $doc: expr) => { ($getter:ident, $setter:ident, $byte:expr, $bits:expr, $doc:expr) => {
#[allow(unused)] #[allow(unused)]
#[doc = $doc] #[doc = $doc]
pub fn $getter(&self) -> u8 { pub fn $getter(&self) -> u8 {
@ -121,14 +124,14 @@ macro_rules! reg_bits {
self.0[$byte].set_bits($bits, value); self.0[$byte].set_bits($bits, value);
} }
}; };
($getter: ident, $byte: expr, $bits: expr, $ty: ty, $doc: expr) => { ($getter:ident, $byte:expr, $bits:expr, $ty:ty, $doc:expr) => {
#[allow(unused)] #[allow(unused)]
#[doc = $doc] #[doc = $doc]
pub fn $getter(&self) -> $ty { pub fn $getter(&self) -> $ty {
self.0[$byte].get_bits($bits) as $ty self.0[$byte].get_bits($bits) as $ty
} }
}; };
($getter: ident, $setter: ident, $byte: expr, $bits: expr, $ty: ty, $doc: expr) => { ($getter:ident, $setter:ident, $byte:expr, $bits:expr, $ty:ty, $doc:expr) => {
#[allow(unused)] #[allow(unused)]
#[doc = $doc] #[doc = $doc]
pub fn $getter(&self) -> $ty { pub fn $getter(&self) -> $ty {
@ -146,7 +149,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,7 +164,13 @@ 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!(sing_cyc, set_sing_cyc, 0, 5, "Can only used with single channel"); reg_bit!(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!(ref_en, set_ref_en, 0, 7, "Enable internal reference, output buffered 2.5 V to REFOUT"); reg_bit!(
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");
} }
@ -174,9 +183,7 @@ 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[0]) << 16) | (u32::from(self.0[1]) << 8) | u32::from(self.0[2])
(u32::from(self.0[1]) << 8) |
u32::from(self.0[2])
} }
} }
@ -200,8 +207,7 @@ 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[0].get_bits(0..=1) << 3) | self.0[1].get_bits(5..=7)).into()
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)]
@ -210,39 +216,108 @@ 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!(a_in_neg, set_a_in_neg, 1, 0..=4, Input, reg_bits!(
"Which input is connected to negative input of this channel"); a_in_neg,
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!(bipolar, set_bipolar, 0, 4, "Unipolar (`false`) or bipolar (`true`) coded output"); reg_bit!(
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!(
reg_bits!(ref_sel, set_ref_sel, 1, 4..=5, RefSource, "Select reference source for conversion"); 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!(
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, set_sinc3_map, 0, 7, "If set, Sinc3 Filter's notch frequency rejection position can be fine tuned with FiltCon[14:0]. Best to be used with Single Channel Enabled"); reg_bit!(
reg_bit!(enh_filt_en, set_enh_filt_en, 0, 3, "Enable postfilters for enhanced 50Hz and 60Hz rejection"); sinc3_map,
reg_bits!(enh_filt, set_enh_filt, 0, 0..=2, PostFilter, "Select postfilters output data rate for enhanced 50Hz and 60Hz rejection"); set_sinc3_map,
reg_bits!(order, set_order, 1, 5..=6, DigitalFilterOrder, "order of the digital filter that processes the modulator data"); 0,
reg_bits!(odr, set_odr, 1, 0..=4, SingleChODR, "Output data rate for normal Sin5c + Sinc1 and Sinc3 filter with SING_CYC = 0 and Single Channel Enabled"); 7,
reg_bits!(sinc3_map_fine_odr_msb, set_sinc3_map_fine_odr_msb, 0, 0..=6, "MSB Byte Sinc3 Fine Output Config"); "If set, Sinc3 Filter's notch frequency rejection position can be fine tuned with FiltCon[14:0]. Best to be \
reg_bits!(sinc3_map_fine_odr_lsb, set_sinc3_map_fine_odr_lsb, 1, 0..=7, "LSB Byte Sinc3 Fine Output Config"); used with Single Channel Enabled"
);
reg_bit!(
enh_filt_en,
set_enh_filt_en,
0,
3,
"Enable postfilters for enhanced 50Hz and 60Hz rejection"
);
reg_bits!(
enh_filt,
set_enh_filt,
0,
0..=2,
PostFilter,
"Select postfilters output data rate 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,
SingleChODR,
"Output data rate for normal Sin5c + Sinc1 and Sinc3 filter with SING_CYC = 0 and Single Channel Enabled"
);
reg_bits!(
sinc3_map_fine_odr_msb,
set_sinc3_map_fine_odr_msb,
0,
0..=6,
"MSB Byte Sinc3 Fine Output Config"
);
reg_bits!(
sinc3_map_fine_odr_lsb,
set_sinc3_map_fine_odr_lsb,
1,
0..=7,
"LSB Byte Sinc3 Fine Output Config"
);
} }
def_reg!(Offset, u8, offset, 0x30, 3); 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[0]) << 16) | (u32::from(self.0[1]) << 8) | u32::from(self.0[2])
(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) {
@ -256,9 +331,7 @@ 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[0]) << 16) | (u32::from(self.0[1]) << 8) | u32::from(self.0[2])
(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,22 +1,18 @@
use crate::thermostat::ad5680;
use core::ptr::addr_of_mut; use core::ptr::addr_of_mut;
use fugit::KilohertzU32;
use stm32f4xx_hal::{
adc::{config::{self, AdcConfig}, Adc},
dma::{config::DmaConfig, PeripheralToMemory, Stream2, StreamsTuple, Transfer as DMA_Transfer},
gpio::{gpioa::*, gpiob::*, gpioc::*, Analog, Output, PushPull},
hal::{self, spi::SpiBus, digital::OutputPin},
pac::{ADC1, ADC2, DMA2, SPI1, TIM4, Peripherals, NVIC},
spi::Spi,
timer::pwm::PwmChannel,
interrupt
};
use uom::si::{ use fugit::KilohertzU32;
electric_potential::millivolt, use stm32f4xx_hal::{adc::{config::{self, AdcConfig},
f32::ElectricPotential, Adc},
ratio::ratio, dma::{config::DmaConfig, PeripheralToMemory, Stream2, StreamsTuple, Transfer as DMA_Transfer},
}; gpio::{gpioa::*, gpiob::*, gpioc::*, Analog, Output, PushPull},
hal::{digital::OutputPin, spi::SpiBus},
interrupt,
pac::{Peripherals, ADC1, ADC2, DMA2, NVIC, SPI1, TIM4},
spi::Spi,
timer::pwm::PwmChannel};
use uom::si::{electric_potential::millivolt, f32::ElectricPotential, ratio::ratio};
use crate::thermostat::ad5680;
pub const PWM_FREQ_KHZ: KilohertzU32 = KilohertzU32::from_raw(20); pub const PWM_FREQ_KHZ: KilohertzU32 = KilohertzU32::from_raw(20);
@ -115,8 +111,8 @@ impl<C: ChannelPins> MAX1968Phy<C> {
} }
} }
static mut ADC2_FIRST_BUFFER : [u16; 16] = [0; 16]; static mut ADC2_FIRST_BUFFER: [u16; 16] = [0; 16];
static mut ADC2_LOCAL_BUFFER : [u16; 16] = [0; 16]; static mut ADC2_LOCAL_BUFFER: [u16; 16] = [0; 16];
impl MAX1968 { impl MAX1968 {
pub fn new(phy_ch0: MAX1968Phy<Channel0>, adc1: ADC1, adc2: ADC2, dma2: DMA2) -> Self { pub fn new(phy_ch0: MAX1968Phy<Channel0>, adc1: ADC1, adc2: ADC2, dma2: DMA2) -> Self {
@ -125,7 +121,7 @@ impl MAX1968 {
.default_sample_time(config::SampleTime::Cycles_480); .default_sample_time(config::SampleTime::Cycles_480);
// Do not set reset RCCs as it causes other ADCs' clock to be disabled // Do not set reset RCCs as it causes other ADCs' clock to be disabled
let mut pins_adc1 = Adc::adc1(adc1, false, adc_config); let mut pins_adc1 = Adc::adc1(adc1, false, adc_config);
// adc1.calibrate() fn only read REFINT once to assign the calibration value. // adc1.calibrate() fn only read REFINT once to assign the calibration value.
// It does not take the STM32F4's ADC Precision Limitation into account. // It does not take the STM32F4's ADC Precision Limitation into account.
// AN4073: ADC Reading Dispersion can be reduced through Averaging // AN4073: ADC Reading Dispersion can be reduced through Averaging
@ -137,7 +133,7 @@ impl MAX1968 {
vdda_mv = vdda_mv / 512 as u32; vdda_mv = vdda_mv / 512 as u32;
pins_adc1.apply_config(adc_config.reference_voltage(vdda_mv)); pins_adc1.apply_config(adc_config.reference_voltage(vdda_mv));
let adc_config = AdcConfig::default() let adc_config = AdcConfig::default()
.clock(config::Clock::Pclk2_div_8) .clock(config::Clock::Pclk2_div_8)
.default_sample_time(config::SampleTime::Cycles_480) .default_sample_time(config::SampleTime::Cycles_480)
.dma(config::Dma::Continuous) .dma(config::Dma::Continuous)
@ -150,25 +146,79 @@ impl MAX1968 {
let mut pins_adc2 = Adc::adc2(adc2, false, adc_config); let mut pins_adc2 = Adc::adc2(adc2, false, adc_config);
pins_adc2.configure_channel(&phy_ch0.itec_pin, config::Sequence::One, config::SampleTime::Cycles_480); pins_adc2.configure_channel(&phy_ch0.itec_pin, config::Sequence::One, config::SampleTime::Cycles_480);
pins_adc2.configure_channel(&phy_ch0.vtec_pin, config::Sequence::Two, config::SampleTime::Cycles_480); pins_adc2.configure_channel(&phy_ch0.vtec_pin, config::Sequence::Two, config::SampleTime::Cycles_480);
pins_adc2.configure_channel(&phy_ch0.itec_pin, config::Sequence::Three, config::SampleTime::Cycles_480); pins_adc2.configure_channel(
pins_adc2.configure_channel(&phy_ch0.vtec_pin, config::Sequence::Four, config::SampleTime::Cycles_480); &phy_ch0.itec_pin,
pins_adc2.configure_channel(&phy_ch0.itec_pin, config::Sequence::Five, config::SampleTime::Cycles_480); config::Sequence::Three,
config::SampleTime::Cycles_480,
);
pins_adc2.configure_channel(
&phy_ch0.vtec_pin,
config::Sequence::Four,
config::SampleTime::Cycles_480,
);
pins_adc2.configure_channel(
&phy_ch0.itec_pin,
config::Sequence::Five,
config::SampleTime::Cycles_480,
);
pins_adc2.configure_channel(&phy_ch0.vtec_pin, config::Sequence::Six, config::SampleTime::Cycles_480); pins_adc2.configure_channel(&phy_ch0.vtec_pin, config::Sequence::Six, config::SampleTime::Cycles_480);
pins_adc2.configure_channel(&phy_ch0.itec_pin, config::Sequence::Seven, config::SampleTime::Cycles_480); pins_adc2.configure_channel(
pins_adc2.configure_channel(&phy_ch0.vtec_pin, config::Sequence::Eight, config::SampleTime::Cycles_480); &phy_ch0.itec_pin,
pins_adc2.configure_channel(&phy_ch0.itec_pin, config::Sequence::Nine, config::SampleTime::Cycles_480); config::Sequence::Seven,
config::SampleTime::Cycles_480,
);
pins_adc2.configure_channel(
&phy_ch0.vtec_pin,
config::Sequence::Eight,
config::SampleTime::Cycles_480,
);
pins_adc2.configure_channel(
&phy_ch0.itec_pin,
config::Sequence::Nine,
config::SampleTime::Cycles_480,
);
pins_adc2.configure_channel(&phy_ch0.vtec_pin, config::Sequence::Ten, config::SampleTime::Cycles_480); pins_adc2.configure_channel(&phy_ch0.vtec_pin, config::Sequence::Ten, config::SampleTime::Cycles_480);
pins_adc2.configure_channel(&phy_ch0.itec_pin, config::Sequence::Eleven, config::SampleTime::Cycles_480); pins_adc2.configure_channel(
pins_adc2.configure_channel(&phy_ch0.vtec_pin, config::Sequence::Twelve, config::SampleTime::Cycles_480); &phy_ch0.itec_pin,
pins_adc2.configure_channel(&phy_ch0.itec_pin, config::Sequence::Thirteen, config::SampleTime::Cycles_480); config::Sequence::Eleven,
pins_adc2.configure_channel(&phy_ch0.vtec_pin, config::Sequence::Fourteen, config::SampleTime::Cycles_480); config::SampleTime::Cycles_480,
pins_adc2.configure_channel(&phy_ch0.itec_pin, config::Sequence::Fifteen, config::SampleTime::Cycles_480); );
pins_adc2.configure_channel(&phy_ch0.vtec_pin, config::Sequence::Sixteen, config::SampleTime::Cycles_480); pins_adc2.configure_channel(
&phy_ch0.vtec_pin,
config::Sequence::Twelve,
config::SampleTime::Cycles_480,
);
pins_adc2.configure_channel(
&phy_ch0.itec_pin,
config::Sequence::Thirteen,
config::SampleTime::Cycles_480,
);
pins_adc2.configure_channel(
&phy_ch0.vtec_pin,
config::Sequence::Fourteen,
config::SampleTime::Cycles_480,
);
pins_adc2.configure_channel(
&phy_ch0.itec_pin,
config::Sequence::Fifteen,
config::SampleTime::Cycles_480,
);
pins_adc2.configure_channel(
&phy_ch0.vtec_pin,
config::Sequence::Sixteen,
config::SampleTime::Cycles_480,
);
let dma = StreamsTuple::new(dma2); let dma = StreamsTuple::new(dma2);
let dma_adc : DMA_Transfer<Stream2<DMA2>, 1, Adc<ADC2>, PeripheralToMemory, &'static mut [u16; 16]>; let dma_adc: DMA_Transfer<Stream2<DMA2>, 1, Adc<ADC2>, PeripheralToMemory, &'static mut [u16; 16]>;
unsafe { unsafe {
dma_adc = DMA_Transfer::init_peripheral_to_memory(dma.2, pins_adc2, addr_of_mut!(ADC2_FIRST_BUFFER).as_mut().unwrap(), None, dma_config); dma_adc = DMA_Transfer::init_peripheral_to_memory(
dma.2,
pins_adc2,
addr_of_mut!(ADC2_FIRST_BUFFER).as_mut().unwrap(),
None,
dma_config,
);
NVIC::unmask(interrupt::DMA2_STREAM2); NVIC::unmask(interrupt::DMA2_STREAM2);
} }
@ -176,30 +226,33 @@ impl MAX1968 {
phy: phy_ch0, phy: phy_ch0,
pins_adc: pins_adc1, pins_adc: pins_adc1,
dma_adc: dma_adc, dma_adc: dma_adc,
prev_vtec_volt: ElectricPotential::new::<millivolt>(0.0), prev_vtec_volt: ElectricPotential::new::<millivolt>(0.0),
prev_itec_volt: ElectricPotential::new::<millivolt>(0.0), prev_itec_volt: ElectricPotential::new::<millivolt>(0.0),
} }
} }
pub fn dma_adc_start_conversion(&mut self){ pub fn dma_adc_start_conversion(&mut self) {
if unsafe {DMA_TRANSFER_COMPLETE} { if unsafe { DMA_TRANSFER_COMPLETE } {
unsafe { DMA_TRANSFER_COMPLETE = false; } unsafe {
self.dma_adc.start(|adc| { DMA_TRANSFER_COMPLETE = false;
}
self.dma_adc.start(|adc| {
adc.clear_end_of_conversion_flag(); adc.clear_end_of_conversion_flag();
adc.start_conversion(); adc.start_conversion();
}); });
} }
} }
pub fn get_tec_readings(&mut self) -> (ElectricPotential, ElectricPotential) { pub fn get_tec_readings(&mut self) -> (ElectricPotential, ElectricPotential) {
if unsafe { DMA_TRANSFER_COMPLETE } { if unsafe { DMA_TRANSFER_COMPLETE } {
let buffer: &[u16; 16]; let buffer: &[u16; 16];
unsafe { unsafe {
(buffer, _) = self.dma_adc (buffer, _) = self
.dma_adc
.next_transfer(addr_of_mut!(ADC2_LOCAL_BUFFER).as_mut().unwrap()) .next_transfer(addr_of_mut!(ADC2_LOCAL_BUFFER).as_mut().unwrap())
.unwrap(); .unwrap();
} }
let sample_to_millivolts = self.dma_adc.peripheral().make_sample_to_millivolts(); let sample_to_millivolts = self.dma_adc.peripheral().make_sample_to_millivolts();
let mut itec: u16 = 0; let mut itec: u16 = 0;
for data in buffer.into_iter().step_by(2) { for data in buffer.into_iter().step_by(2) {
@ -240,10 +293,8 @@ impl MAX1968 {
self.phy.shdn.set_high(); self.phy.shdn.set_high();
} }
pub fn set_dac(&mut self, voltage: ElectricPotential, dac_out_v_max: ElectricPotential) -> ElectricPotential { pub fn set_dac(&mut self, voltage: ElectricPotential, dac_out_v_max: ElectricPotential) -> ElectricPotential {
let value = ((voltage / dac_out_v_max).get::<ratio>() let value = ((voltage / dac_out_v_max).get::<ratio>() * (ad5680::MAX_VALUE as f32)) as u32;
* (ad5680::MAX_VALUE as f32)) as u32;
self.phy.dac.set(value).unwrap(); self.phy.dac.set(value).unwrap();
voltage voltage
} }
@ -255,10 +306,10 @@ impl MAX1968 {
sample = match adc_read_target { sample = match adc_read_target {
AdcReadTarget::VREF => { AdcReadTarget::VREF => {
for _ in (0..avg_pt).rev() { for _ in (0..avg_pt).rev() {
sample += self.pins_adc.convert( sample += self
&self.phy.vref_pin, .pins_adc
stm32f4xx_hal::adc::config::SampleTime::Cycles_480, .convert(&self.phy.vref_pin, stm32f4xx_hal::adc::config::SampleTime::Cycles_480)
) as u32; as u32;
} }
sample / avg_pt as u32 sample / avg_pt as u32
} }
@ -273,19 +324,19 @@ impl MAX1968 {
} }
AdcReadTarget::ITec => { AdcReadTarget::ITec => {
for _ in (0..avg_pt).rev() { for _ in (0..avg_pt).rev() {
sample += self.pins_adc.convert( sample += self
&self.phy.itec_pin, .pins_adc
stm32f4xx_hal::adc::config::SampleTime::Cycles_480, .convert(&self.phy.itec_pin, stm32f4xx_hal::adc::config::SampleTime::Cycles_480)
) as u32; as u32;
} }
sample / avg_pt as u32 sample / avg_pt as u32
} }
AdcReadTarget::VTec => { AdcReadTarget::VTec => {
for _ in (0..avg_pt).rev() { for _ in (0..avg_pt).rev() {
sample += self.pins_adc.convert( sample += self
&self.phy.vtec_pin, .pins_adc
stm32f4xx_hal::adc::config::SampleTime::Cycles_480, .convert(&self.phy.vtec_pin, stm32f4xx_hal::adc::config::SampleTime::Cycles_480)
) as u32; as u32;
} }
sample / avg_pt as u32 sample / avg_pt as u32
} }
@ -307,7 +358,6 @@ impl MAX1968 {
max_value = self.phy.max_v.get_max_duty(); max_value = self.phy.max_v.get_max_duty();
value = duty_cycle_value(duty, max_duty, max_value); value = duty_cycle_value(duty, max_duty, max_value);
self.phy.max_v.set_duty(value); self.phy.max_v.set_duty(value);
} }
PwmPinsEnum::MaxPosI => { PwmPinsEnum::MaxPosI => {
self.phy.max_i_pos.enable(); self.phy.max_i_pos.enable();
@ -324,22 +374,24 @@ impl MAX1968 {
} }
return (value as f64) / (max_value as f64); return (value as f64) / (max_value as f64);
} }
} }
#[interrupt] #[interrupt]
fn DMA2_STREAM2(){ fn DMA2_STREAM2() {
cortex_m::interrupt::free(|_| { cortex_m::interrupt::free(|_| {
unsafe { unsafe {
// Clear all DMA2_STREAM2 interrupt flags // Clear all DMA2_STREAM2 interrupt flags
Peripherals::steal().DMA2.lifcr.write(|w| w Peripherals::steal().DMA2.lifcr.write(|w| {
.ctcif2().set_bit() w.ctcif2()
.cdmeif2().set_bit() .set_bit()
.chtif2().set_bit() .cdmeif2()
.cteif2().set_bit() .set_bit()
); .chtif2()
DMA_TRANSFER_COMPLETE = true; .set_bit()
} .cteif2()
.set_bit()
});
DMA_TRANSFER_COMPLETE = true;
} }
) })
} }

View File

@ -1,7 +1,7 @@
pub mod ad5680; pub mod ad5680;
pub mod max1968;
pub mod thermostat;
pub mod ad7172; pub mod ad7172;
pub mod steinhart_hart; pub mod max1968;
pub mod pid_state; pub mod pid_state;
pub mod steinhart_hart;
pub mod temp_mon; pub mod temp_mon;
pub mod thermostat;

View File

@ -1,14 +1,11 @@
use miniconf::Tree; use miniconf::Tree;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use uom::si::{ use uom::si::{electric_potential::volt,
electric_potential::volt, electrical_resistance::ohm, f32::{ electrical_resistance::ohm,
ElectricPotential, ElectricalResistance, ThermodynamicTemperature f32::{ElectricPotential, ElectricalResistance, ThermodynamicTemperature},
}, thermodynamic_temperature::degree_celsius thermodynamic_temperature::degree_celsius};
};
use crate::thermostat::{ use crate::thermostat::{ad7172, steinhart_hart as sh};
ad7172,
steinhart_hart as sh,
};
const R_INNER: f32 = 2.0 * 5100.0; const R_INNER: f32 = 2.0 * 5100.0;
const VREF_SENS: f32 = 3.3 / 2.0; const VREF_SENS: f32 = 3.3 / 2.0;
@ -41,13 +38,12 @@ impl Default for Parameters {
#[derive(Clone)] #[derive(Clone)]
pub struct Controller { pub struct Controller {
pub parameters: Parameters, pub parameters: Parameters,
u1 : f64, u1: f64,
x1 : f64, x1: f64,
x2 : f64, x2: f64,
pub y1 : f64, pub y1: f64,
} }
pub struct PidState { pub struct PidState {
adc_data: Option<u32>, adc_data: Option<u32>,
adc_calibration: ad7172::ChannelCalibration, adc_calibration: ad7172::ChannelCalibration,
@ -67,10 +63,10 @@ impl Default for PidState {
sh: sh::Parameters::default(), sh: sh::Parameters::default(),
controller: Controller { controller: Controller {
parameters: Parameters::default(), parameters: Parameters::default(),
u1 : 0.0, u1: 0.0,
x1 : 0.0, x1: 0.0,
x2 : 0.0, x2: 0.0,
y1 : 0.0, y1: 0.0,
}, },
} }
} }
@ -96,7 +92,7 @@ impl PidState {
// 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
@ -106,10 +102,13 @@ impl PidState {
let input = self.get_temperature()?.get::<degree_celsius>(); let input = self.get_temperature()?.get::<degree_celsius>();
let setpoint = self.set_point.get::<degree_celsius>(); let setpoint = self.set_point.get::<degree_celsius>();
let mut output: f64 = self.controller.y1 - setpoint as f64 * f64::from(self.controller.parameters.ki) let mut output: f64 = self.controller.y1 - setpoint as f64 * f64::from(self.controller.parameters.ki)
+ input as f64 * f64::from(self.controller.parameters.kp + self.controller.parameters.ki + self.controller.parameters.kd) + input as f64
- self.controller.x1 * f64::from(self.controller.parameters.kp + 2.0 * self.controller.parameters.kd) * f64::from(
+ self.controller.x2 * f64::from(self.controller.parameters.kd) self.controller.parameters.kp + self.controller.parameters.ki + self.controller.parameters.kd,
+ f64::from(self.controller.parameters.kp) * (setpoint as f64 - self.controller.u1); )
- self.controller.x1 * f64::from(self.controller.parameters.kp + 2.0 * self.controller.parameters.kd)
+ self.controller.x2 * f64::from(self.controller.parameters.kd)
+ f64::from(self.controller.parameters.kp) * (setpoint as f64 - self.controller.u1);
if output < self.controller.parameters.output_min.into() { if output < self.controller.parameters.output_min.into() {
output = self.controller.parameters.output_min.into(); output = self.controller.parameters.output_min.into();
} }
@ -119,7 +118,7 @@ impl PidState {
self.controller.x2 = self.controller.x1; self.controller.x2 = self.controller.x1;
self.controller.x1 = input as f64; self.controller.x1 = input as f64;
self.controller.u1 = setpoint as f64; self.controller.u1 = setpoint as f64;
self.controller.y1 = output; self.controller.y1 = output;
Some(output) Some(output)
} }
@ -142,38 +141,38 @@ impl PidState {
Some(temperature) Some(temperature)
} }
pub fn apply_pid_params(&mut self, pid_params: Parameters){ pub fn apply_pid_params(&mut self, pid_params: Parameters) {
self.controller.parameters = pid_params; self.controller.parameters = pid_params;
} }
pub fn set_pid_params(&mut self, param: PidSettings, val: f32){ pub fn set_pid_params(&mut self, param: PidSettings, val: f32) {
match param { match param {
PidSettings::Kp => { PidSettings::Kp => {
self.controller.parameters.kp = val; self.controller.parameters.kp = val;
} }
PidSettings::Ki => { PidSettings::Ki => {
self.controller.parameters.ki = val; self.controller.parameters.ki = val;
} }
PidSettings::Kd => { PidSettings::Kd => {
self.controller.parameters.kd = val; self.controller.parameters.kd = val;
} }
PidSettings::Min => { PidSettings::Min => {
self.controller.parameters.output_min = val; self.controller.parameters.output_min = val;
} }
PidSettings::Max => { PidSettings::Max => {
self.controller.parameters.output_max = val; self.controller.parameters.output_max = val;
} }
} }
} }
pub fn reset_pid_state(&mut self){ pub fn reset_pid_state(&mut self) {
self.controller.u1 = 0.0; self.controller.u1 = 0.0;
self.controller.x1 = 0.0; self.controller.x1 = 0.0;
self.controller.x2 = 0.0; self.controller.x2 = 0.0;
self.controller.y1 = 0.0; self.controller.y1 = 0.0;
} }
pub fn set_pid_setpoint(&mut self, temperature: ThermodynamicTemperature){ pub fn set_pid_setpoint(&mut self, temperature: ThermodynamicTemperature) {
self.set_point = temperature; self.set_point = temperature;
} }
@ -181,23 +180,23 @@ impl PidState {
self.set_point self.set_point
} }
pub fn set_sh_t0(&mut self, t0: ThermodynamicTemperature){ pub fn set_sh_t0(&mut self, t0: ThermodynamicTemperature) {
self.sh.t0 = t0 self.sh.t0 = t0
} }
pub fn set_sh_r0(&mut self, r0: ElectricalResistance){ pub fn set_sh_r0(&mut self, r0: ElectricalResistance) {
self.sh.r0 = r0 self.sh.r0 = r0
} }
pub fn set_sh_beta(&mut self, beta: f32){ pub fn set_sh_beta(&mut self, beta: f32) {
self.sh.b = beta self.sh.b = beta
} }
pub fn set_adc_calibration(&mut self, adc_cal: ad7172::ChannelCalibration){ pub fn set_adc_calibration(&mut self, adc_cal: ad7172::ChannelCalibration) {
self.adc_calibration = adc_cal; self.adc_calibration = adc_cal;
} }
pub fn set_pid_engaged(&mut self, pid_engaged: bool){ pub fn set_pid_engaged(&mut self, pid_engaged: bool) {
self.pid_engaged = pid_engaged; self.pid_engaged = pid_engaged;
} }

View File

@ -1,15 +1,10 @@
use miniconf::Tree;
use num_traits::float::Float; use num_traits::float::Float;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use uom::si::{ use uom::si::{electrical_resistance::ohm,
f32::{ f32::{ElectricalResistance, ThermodynamicTemperature},
ElectricalResistance, ratio::ratio,
ThermodynamicTemperature, thermodynamic_temperature::{degree_celsius, kelvin}};
},
electrical_resistance::ohm,
ratio::ratio,
thermodynamic_temperature::{degree_celsius, kelvin},
};
use miniconf::Tree;
/// Steinhart-Hart equation parameters /// Steinhart-Hart equation parameters
#[derive(Deserialize, Serialize, Copy, Clone, Debug, Tree)] #[derive(Deserialize, Serialize, Copy, Clone, Debug, Tree)]

View File

@ -1,9 +1,7 @@
use serde::{Deserialize, Serialize};
use miniconf::Tree; use miniconf::Tree;
use uom::si::{
f32::ThermodynamicTemperature, thermodynamic_temperature::degree_celsius
};
use num_traits::Float; use num_traits::Float;
use serde::{Deserialize, Serialize};
use uom::si::{f32::ThermodynamicTemperature, thermodynamic_temperature::degree_celsius};
#[derive(PartialEq, Deserialize, Serialize, Copy, Clone, Default, Debug)] #[derive(PartialEq, Deserialize, Serialize, Copy, Clone, Default, Debug)]
pub enum TempStatusEnum { pub enum TempStatusEnum {
#[default] #[default]
@ -44,7 +42,7 @@ impl Default for TempMon {
set_point: ThermodynamicTemperature::new::<degree_celsius>(0.0), set_point: ThermodynamicTemperature::new::<degree_celsius>(0.0),
status: TempStatus { status: TempStatus {
status: TempStatusEnum::Off, status: TempStatusEnum::Off,
over_temp_alarm: false over_temp_alarm: false,
}, },
state: State::default(), state: State::default(),
count: 0, count: 0,
@ -117,12 +115,11 @@ impl TempMon {
State::ConstantCurrentMode => { State::ConstantCurrentMode => {
let is_over_temp = temp > self.upper_limit || temp < self.lower_limit; let is_over_temp = temp > self.upper_limit || temp < self.lower_limit;
self.status.status = TempStatusEnum::ConstantCurrentMode; self.status.status = TempStatusEnum::ConstantCurrentMode;
if is_over_temp { if is_over_temp {
self.state = State::OverTempAlarm; self.state = State::OverTempAlarm;
self.status.status = TempStatusEnum::OverTemp; self.status.status = TempStatusEnum::OverTemp;
} } else if !pwr_on {
else if !pwr_on {
self.state = State::PwrOff; self.state = State::PwrOff;
self.status.status = TempStatusEnum::Off; self.status.status = TempStatusEnum::Off;
} else if pid_engaged { } else if pid_engaged {
@ -138,7 +135,7 @@ impl TempMon {
} else { } else {
is_over_temp = (temp.value - self.set_point.value).abs() > 0.5; is_over_temp = (temp.value - self.set_point.value).abs() > 0.5;
} }
let is_within_spec: bool = (temp.value - self.set_point.value).abs() < 0.001; let is_within_spec: bool = (temp.value - self.set_point.value).abs() < 0.001;
if is_over_temp { if is_over_temp {
if self.count > TempMon::OVER_TEMP_COUNT_LIMIT { if self.count > TempMon::OVER_TEMP_COUNT_LIMIT {
@ -160,8 +157,7 @@ impl TempMon {
// State Transition // State Transition
if !pwr_on { if !pwr_on {
self.state = State::PwrOff; self.state = State::PwrOff;
} } else {
else {
if self.status.status == TempStatusEnum::OverTemp { if self.status.status == TempStatusEnum::OverTemp {
self.state = State::OverTempAlarm; self.state = State::OverTempAlarm;
} else if self.is_set_point_changed { } else if self.is_set_point_changed {

View File

@ -1,25 +1,23 @@
use core::f32::NAN; use core::{f32::NAN, marker::PhantomData};
use core::marker::PhantomData;
use crate::sys_timer;
use crate::thermostat::ad5680;
use crate::thermostat::max1968::{MAX1968, AdcReadTarget, PwmPinsEnum};
use crate::thermostat::ad7172::{self, FilterType, PostFilter, SingleChODR};
use crate::thermostat::pid_state::{PidState, PidSettings, Parameters as PidParams};
use crate::thermostat::temp_mon::{TempMon, TempStatus, TempMonSettings};
use crate::thermostat::steinhart_hart::Parameters as Sh_Params;
use serde::{Deserialize, Serialize};
use log::debug;
use uom::si::{
electric_current::ampere,
electric_potential::volt,
electrical_resistance::ohm,
thermodynamic_temperature::degree_celsius,
f32::{ThermodynamicTemperature, ElectricCurrent, ElectricPotential, ElectricalResistance},
ratio::ratio,
};
use miniconf::Tree;
use super::pid_state; use log::debug;
use miniconf::Tree;
use serde::{Deserialize, Serialize};
use uom::si::{electric_current::ampere,
electric_potential::volt,
electrical_resistance::ohm,
f32::{ElectricCurrent, ElectricPotential, ElectricalResistance, ThermodynamicTemperature},
ratio::ratio,
thermodynamic_temperature::degree_celsius};
use crate::{sys_timer,
thermostat::{ad5680,
ad7172::{self, FilterType, PostFilter, SingleChODR},
max1968::{AdcReadTarget, PwmPinsEnum, MAX1968},
pid_state,
pid_state::{Parameters as PidParams, PidSettings, PidState},
steinhart_hart::Parameters as Sh_Params,
temp_mon::{TempMon, TempMonSettings, TempStatus}}};
pub const R_SENSE: ElectricalResistance = ElectricalResistance { pub const R_SENSE: ElectricalResistance = ElectricalResistance {
dimension: PhantomData, dimension: PhantomData,
@ -28,7 +26,7 @@ pub const R_SENSE: ElectricalResistance = ElectricalResistance {
}; };
#[derive(Deserialize, Serialize, Copy, Clone, Debug, Default, Tree)] #[derive(Deserialize, Serialize, Copy, Clone, Debug, Default, Tree)]
pub struct TempAdcFilter{ pub struct TempAdcFilter {
pub filter_type: FilterType, pub filter_type: FilterType,
pub sinc5sinc1odr: Option<SingleChODR>, pub sinc5sinc1odr: Option<SingleChODR>,
pub sinc3odr: Option<SingleChODR>, pub sinc3odr: Option<SingleChODR>,
@ -37,7 +35,6 @@ pub struct TempAdcFilter{
pub rate: Option<f32>, pub rate: Option<f32>,
} }
#[derive(Deserialize, Serialize, Clone, Copy, Debug, Tree)] #[derive(Deserialize, Serialize, Clone, Copy, Debug, Tree)]
pub struct TecSettings { pub struct TecSettings {
pub default_pwr_on: bool, pub default_pwr_on: bool,
@ -49,7 +46,7 @@ pub struct TecSettings {
pub vref: ElectricPotential, pub vref: ElectricPotential,
} }
impl TecSettings{ impl TecSettings {
pub const DAC_OUT_V_MAX: ElectricPotential = ElectricPotential { pub const DAC_OUT_V_MAX: ElectricPotential = ElectricPotential {
dimension: PhantomData, dimension: PhantomData,
units: PhantomData, units: PhantomData,
@ -60,11 +57,11 @@ impl TecSettings{
units: PhantomData, units: PhantomData,
value: 1.65, value: 1.65,
}; };
// Kirdy Design Specs: // Kirdy Design Specs:
// MaxV = 5.0V // MaxV = 5.0V
// MAX Current = +- 1.0A // MAX Current = +- 1.0A
const MAX_I_SET : ElectricCurrent = ElectricCurrent { const MAX_I_SET: ElectricCurrent = ElectricCurrent {
dimension: PhantomData, dimension: PhantomData,
units: PhantomData, units: PhantomData,
value: 1.0, value: 1.0,
@ -79,7 +76,8 @@ impl TecSettings{
units: PhantomData, units: PhantomData,
value: 5.0, value: 5.0,
}; };
const MAX_V_DUTY_MAX: f64 = TecSettings::MAX_V_MAX.value as f64 / TecSettings::MAX_V_DUTY_TO_CURRENT_RATE.value as f64; const MAX_V_DUTY_MAX: f64 =
TecSettings::MAX_V_MAX.value as f64 / TecSettings::MAX_V_DUTY_TO_CURRENT_RATE.value as f64;
const MAX_I_POS_NEG_DUTY_TO_CURRENT_RATE: ElectricCurrent = ElectricCurrent { const MAX_I_POS_NEG_DUTY_TO_CURRENT_RATE: ElectricCurrent = ElectricCurrent {
dimension: PhantomData, dimension: PhantomData,
units: PhantomData, units: PhantomData,
@ -96,8 +94,10 @@ impl TecSettings{
value: 1.0, value: 1.0,
}; };
// .get::<ratio>() is not implemented for const // .get::<ratio>() is not implemented for const
const MAX_I_POS_DUTY_MAX: f64 = TecSettings::MAX_I_POS_CURRENT.value as f64 / TecSettings::MAX_I_POS_NEG_DUTY_TO_CURRENT_RATE.value as f64; const MAX_I_POS_DUTY_MAX: f64 =
const MAX_I_NEG_DUTY_MAX: f64 = TecSettings::MAX_I_NEG_CURRENT.value as f64 / TecSettings::MAX_I_POS_NEG_DUTY_TO_CURRENT_RATE.value as f64; TecSettings::MAX_I_POS_CURRENT.value as f64 / TecSettings::MAX_I_POS_NEG_DUTY_TO_CURRENT_RATE.value as f64;
const MAX_I_NEG_DUTY_MAX: f64 =
TecSettings::MAX_I_NEG_CURRENT.value as f64 / TecSettings::MAX_I_POS_NEG_DUTY_TO_CURRENT_RATE.value as f64;
} }
impl Default for TecSettings { impl Default for TecSettings {
@ -134,9 +134,9 @@ pub struct ThermostatSettingsSummary {
thermistor_params: ThermistorParams, thermistor_params: ThermistorParams,
} }
impl Thermostat{ impl Thermostat {
pub fn new (max1968: MAX1968, ad7172: ad7172::AdcPhy) -> Self { pub fn new(max1968: MAX1968, ad7172: ad7172::AdcPhy) -> Self {
Thermostat{ Thermostat {
max1968: max1968, max1968: max1968,
ad7172: ad7172, ad7172: ad7172,
tec_settings: TecSettings::default(), tec_settings: TecSettings::default(),
@ -144,16 +144,16 @@ impl Thermostat{
temp_mon: TempMon::default(), temp_mon: TempMon::default(),
} }
} }
pub fn setup(&mut self){ pub fn setup(&mut self) {
self.tec_setup(); self.tec_setup();
let t_adc_ch0_cal = self.t_adc_setup(); let t_adc_ch0_cal = self.t_adc_setup();
self.pid_ctrl_ch0.set_adc_calibration(t_adc_ch0_cal) ; self.pid_ctrl_ch0.set_adc_calibration(t_adc_ch0_cal);
} }
/// start_tec_readings_conversion() should not be called before the current /// start_tec_readings_conversion() should not be called before the current
/// DMA request is serviced or the conversion process will be restarted /// DMA request is serviced or the conversion process will be restarted
/// Thus, no new readings is available when you call get_tec_readings() fn /// Thus, no new readings is available when you call get_tec_readings() fn
pub fn start_tec_readings_conversion(&mut self){ pub fn start_tec_readings_conversion(&mut self) {
self.max1968.dma_adc_start_conversion(); self.max1968.dma_adc_start_conversion();
} }
@ -169,24 +169,26 @@ impl Thermostat{
self.set_max_i_neg(self.tec_settings.max_i_neg_set); self.set_max_i_neg(self.tec_settings.max_i_neg_set);
} }
fn t_adc_setup(&mut self)->ad7172::ChannelCalibration{ fn t_adc_setup(&mut self) -> ad7172::ChannelCalibration {
self.ad7172.set_sync_enable(false).unwrap(); self.ad7172.set_sync_enable(false).unwrap();
self.ad7172.setup_channel(0, ad7172::Input::Ain0, ad7172::Input::Ain1).unwrap(); self.ad7172
let adc_calibration0 = self.ad7172.get_calibration(0) .setup_channel(0, ad7172::Input::Ain0, ad7172::Input::Ain1)
.expect("adc_calibration0"); .unwrap();
let adc_calibration0 = self.ad7172.get_calibration(0).expect("adc_calibration0");
self.ad7172.start_continuous_conversion().unwrap(); self.ad7172.start_continuous_conversion().unwrap();
adc_calibration0 adc_calibration0
} }
pub fn poll_adc(&mut self) -> bool { pub fn poll_adc(&mut self) -> bool {
let mut data_rdy = false; let mut data_rdy = false;
self.ad7172.data_ready().unwrap().map(|_ch| { self.ad7172.data_ready().unwrap().map(|_ch| {
let data = self.ad7172.read_data().unwrap(); let data = self.ad7172.read_data().unwrap();
let state: &mut PidState = &mut self.pid_ctrl_ch0; let state: &mut PidState = &mut self.pid_ctrl_ch0;
state.update(data); state.update(data);
let pid_engaged = state.get_pid_engaged(); let pid_engaged = state.get_pid_engaged();
let temp = self.get_temperature(); let temp = self.get_temperature();
self.temp_mon.update_status(pid_engaged, self.max1968.is_powered_on(), temp); self.temp_mon
.update_status(pid_engaged, self.max1968.is_powered_on(), temp);
debug!("state.get_pid_engaged(): {:?}", pid_engaged); debug!("state.get_pid_engaged(): {:?}", pid_engaged);
debug!("Temperature: {:?} degree", temp.get::<degree_celsius>()); debug!("Temperature: {:?} degree", temp.get::<degree_celsius>());
data_rdy = true; data_rdy = true;
@ -201,9 +203,12 @@ impl Thermostat{
match state.update_pid() { match state.update_pid() {
Some(pid_output) => { Some(pid_output) => {
self.set_i(ElectricCurrent::new::<ampere>(pid_output as f32)); self.set_i(ElectricCurrent::new::<ampere>(pid_output as f32));
debug!("Temperature Set Point: {:?} degree", self.pid_ctrl_ch0.get_pid_setpoint().get::<degree_celsius>()); debug!(
"Temperature Set Point: {:?} degree",
self.pid_ctrl_ch0.get_pid_setpoint().get::<degree_celsius>()
);
} }
None => { } None => {}
} }
} }
} }
@ -212,22 +217,22 @@ impl Thermostat{
self.temp_mon.get_status() self.temp_mon.get_status()
} }
pub fn power_up(&mut self){ pub fn power_up(&mut self) {
self.max1968.power_up(); self.max1968.power_up();
} }
pub fn power_down(&mut self){ pub fn power_down(&mut self) {
self.max1968.power_down(); self.max1968.power_down();
self.pid_ctrl_ch0.reset_pid_state(); self.pid_ctrl_ch0.reset_pid_state();
self.set_i(ElectricCurrent::new::<ampere>(0.0)); self.set_i(ElectricCurrent::new::<ampere>(0.0));
} }
fn set_center_pt(&mut self, value: ElectricPotential){ fn set_center_pt(&mut self, value: ElectricPotential) {
self.tec_settings.center_pt = value; self.tec_settings.center_pt = value;
} }
pub fn set_default_pwr_on(&mut self, pwr_on: bool) { pub fn set_default_pwr_on(&mut self, pwr_on: bool) {
self.tec_settings.default_pwr_on = pwr_on; self.tec_settings.default_pwr_on = pwr_on;
} }
pub fn set_i(&mut self, i_tec: ElectricCurrent) -> ElectricCurrent { pub fn set_i(&mut self, i_tec: ElectricCurrent) -> ElectricCurrent {
@ -239,21 +244,27 @@ impl Thermostat{
pub fn set_max_v(&mut self, max_v: ElectricPotential) -> ElectricPotential { pub fn set_max_v(&mut self, max_v: ElectricPotential) -> ElectricPotential {
let duty = (max_v / TecSettings::MAX_V_DUTY_TO_CURRENT_RATE).get::<ratio>(); let duty = (max_v / TecSettings::MAX_V_DUTY_TO_CURRENT_RATE).get::<ratio>();
let duty = self.max1968.set_pwm(PwmPinsEnum::MaxV, duty as f64, TecSettings::MAX_V_DUTY_MAX); let duty = self
.max1968
.set_pwm(PwmPinsEnum::MaxV, duty as f64, TecSettings::MAX_V_DUTY_MAX);
self.tec_settings.max_v_set = duty as f32 * TecSettings::MAX_V_DUTY_TO_CURRENT_RATE; self.tec_settings.max_v_set = duty as f32 * TecSettings::MAX_V_DUTY_TO_CURRENT_RATE;
self.tec_settings.max_v_set self.tec_settings.max_v_set
} }
pub fn set_max_i_pos(&mut self, max_i_pos: ElectricCurrent) -> ElectricCurrent { pub fn set_max_i_pos(&mut self, max_i_pos: ElectricCurrent) -> ElectricCurrent {
let duty = (max_i_pos / TecSettings::MAX_I_POS_NEG_DUTY_TO_CURRENT_RATE).get::<ratio>(); let duty = (max_i_pos / TecSettings::MAX_I_POS_NEG_DUTY_TO_CURRENT_RATE).get::<ratio>();
let duty = self.max1968.set_pwm(PwmPinsEnum::MaxPosI, duty as f64, TecSettings::MAX_I_POS_DUTY_MAX); let duty = self
.max1968
.set_pwm(PwmPinsEnum::MaxPosI, duty as f64, TecSettings::MAX_I_POS_DUTY_MAX);
self.tec_settings.max_i_pos_set = duty as f32 * TecSettings::MAX_I_POS_NEG_DUTY_TO_CURRENT_RATE; self.tec_settings.max_i_pos_set = duty as f32 * TecSettings::MAX_I_POS_NEG_DUTY_TO_CURRENT_RATE;
self.tec_settings.max_i_pos_set self.tec_settings.max_i_pos_set
} }
pub fn set_max_i_neg(&mut self, max_i_neg: ElectricCurrent) -> ElectricCurrent { pub fn set_max_i_neg(&mut self, max_i_neg: ElectricCurrent) -> ElectricCurrent {
let duty = (max_i_neg / TecSettings::MAX_I_POS_NEG_DUTY_TO_CURRENT_RATE).get::<ratio>(); let duty = (max_i_neg / TecSettings::MAX_I_POS_NEG_DUTY_TO_CURRENT_RATE).get::<ratio>();
let duty = self.max1968.set_pwm(PwmPinsEnum::MaxNegI, duty as f64, TecSettings::MAX_I_NEG_DUTY_MAX); let duty = self
.max1968
.set_pwm(PwmPinsEnum::MaxNegI, duty as f64, TecSettings::MAX_I_NEG_DUTY_MAX);
self.tec_settings.max_i_neg_set = duty as f32 * TecSettings::MAX_I_POS_NEG_DUTY_TO_CURRENT_RATE; self.tec_settings.max_i_neg_set = duty as f32 * TecSettings::MAX_I_POS_NEG_DUTY_TO_CURRENT_RATE;
self.tec_settings.max_i_neg_set self.tec_settings.max_i_neg_set
} }
@ -282,32 +293,35 @@ impl Thermostat{
pub fn get_tec_readings(&mut self) -> (ElectricPotential, ElectricCurrent) { pub fn get_tec_readings(&mut self) -> (ElectricPotential, ElectricCurrent) {
let vref = self.tec_settings.vref; let vref = self.tec_settings.vref;
let (vtec, itec) = self.max1968.get_tec_readings(); let (vtec, itec) = self.max1968.get_tec_readings();
((vtec - TecSettings::TEC_VSEC_BIAS_V) * 4.0, (itec - vref) / ElectricalResistance::new::<ohm>(0.4)) (
(vtec - TecSettings::TEC_VSEC_BIAS_V) * 4.0,
(itec - vref) / ElectricalResistance::new::<ohm>(0.4),
)
} }
/// Calibrates the DAC output to match vref of the MAX driver to reduce zero-current offset of the MAX driver output. /// Calibrates the DAC output to match vref of the MAX driver to reduce zero-current offset of the MAX driver output.
/// ///
/// 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) { pub fn calibrate_dac_value(&mut self) {
const DAC_BIT: u32 = 18; const DAC_BIT: u32 = 18;
const ADC_BIT: u32 = 12; const ADC_BIT: u32 = 12;
let target_voltage = self.max1968.adc_read(AdcReadTarget::VREF, 512); let target_voltage = self.max1968.adc_read(AdcReadTarget::VREF, 512);
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);
for step in (DAC_BIT-ADC_BIT-1..DAC_BIT).rev() { for step in (DAC_BIT - ADC_BIT - 1..DAC_BIT).rev() {
let mut prev_value = start_value; let mut prev_value = start_value;
for value in (start_value..=ad5680::MAX_VALUE).step_by(1 << step) { for value in (start_value..=ad5680::MAX_VALUE).step_by(1 << step) {
self.max1968.phy.dac.set(value).unwrap(); self.max1968.phy.dac.set(value).unwrap();
@ -329,13 +343,13 @@ impl Thermostat{
} }
self.tec_settings.vref = target_voltage; self.tec_settings.vref = target_voltage;
} }
pub fn set_pid_engaged(&mut self, val: bool) { pub fn set_pid_engaged(&mut self, val: bool) {
self.pid_ctrl_ch0.set_pid_engaged(val); self.pid_ctrl_ch0.set_pid_engaged(val);
} }
fn get_pid_engaged(&mut self) -> bool { fn get_pid_engaged(&mut self) -> bool {
self.pid_ctrl_ch0.get_pid_engaged() self.pid_ctrl_ch0.get_pid_engaged()
} }
pub fn get_status_report(&mut self) -> StatusReport { pub fn get_status_report(&mut self) -> StatusReport {
@ -343,14 +357,12 @@ impl Thermostat{
let temperature: Option<f32>; let temperature: Option<f32>;
match self.pid_ctrl_ch0.get_temperature() { match self.pid_ctrl_ch0.get_temperature() {
Some(val) => { Some(val) => temperature = Some(val.get::<degree_celsius>()),
temperature = Some(val.get::<degree_celsius>())
}
None => { None => {
temperature = None; temperature = None;
} }
} }
StatusReport { StatusReport {
pwr_on: self.max1968.is_powered_on(), pwr_on: self.max1968.is_powered_on(),
pid_engaged: self.get_pid_engaged(), pid_engaged: self.get_pid_engaged(),
@ -364,10 +376,8 @@ impl Thermostat{
pub fn get_temperature(&mut self) -> ThermodynamicTemperature { pub fn get_temperature(&mut self) -> ThermodynamicTemperature {
match self.pid_ctrl_ch0.get_temperature() { match self.pid_ctrl_ch0.get_temperature() {
Some(val) => { Some(val) => val,
val None => ThermodynamicTemperature::new::<degree_celsius>(NAN),
}
None => { ThermodynamicTemperature::new::<degree_celsius>(NAN) }
} }
} }
@ -382,26 +392,34 @@ impl Thermostat{
r0: sh.r0, r0: sh.r0,
b: sh.b, b: sh.b,
} }
} }
fn apply_steinhart_hart(&mut self, sh: ThermistorParams) { fn apply_steinhart_hart(&mut self, sh: ThermistorParams) {
self.pid_ctrl_ch0.apply_sh( self.pid_ctrl_ch0.apply_sh(Sh_Params {
Sh_Params { t0: ThermodynamicTemperature::new::<degree_celsius>(sh.t0),
t0: ThermodynamicTemperature::new::<degree_celsius>(sh.t0), r0: sh.r0,
r0: sh.r0, b: sh.b,
b: sh.b, })
}
)
} }
fn get_tec_settings(&mut self) -> TecSettingSummary { fn get_tec_settings(&mut self) -> TecSettingSummary {
TecSettingSummary { TecSettingSummary {
i_set: TecSettingsSummaryField { value: self.tec_settings.i_set, max: TecSettings::MAX_I_SET }, i_set: TecSettingsSummaryField {
max_v: TecSettingsSummaryField { value: self.tec_settings.max_v_set, max: TecSettings::MAX_V_MAX }, value: self.tec_settings.i_set,
max_i_pos: TecSettingsSummaryField { value: self.tec_settings.max_i_pos_set, max: TecSettings::MAX_I_POS_CURRENT }, max: TecSettings::MAX_I_SET,
max_i_neg: TecSettingsSummaryField { value: self.tec_settings.max_i_neg_set, max: TecSettings::MAX_I_NEG_CURRENT }, },
max_v: TecSettingsSummaryField {
value: self.tec_settings.max_v_set,
max: TecSettings::MAX_V_MAX,
},
max_i_pos: TecSettingsSummaryField {
value: self.tec_settings.max_i_pos_set,
max: TecSettings::MAX_I_POS_CURRENT,
},
max_i_neg: TecSettingsSummaryField {
value: self.tec_settings.max_i_neg_set,
max: TecSettings::MAX_I_NEG_CURRENT,
},
} }
} }
@ -409,7 +427,7 @@ impl Thermostat{
self.max1968.get_calibrated_vdda() self.max1968.get_calibrated_vdda()
} }
pub fn set_pid(&mut self, param: PidSettings, val: f32){ pub fn set_pid(&mut self, param: PidSettings, val: f32) {
self.pid_ctrl_ch0.set_pid_params(param, val); self.pid_ctrl_ch0.set_pid_params(param, val);
} }
@ -426,14 +444,18 @@ impl Thermostat{
} }
pub fn set_temperature_setpoint(&mut self, t: ThermodynamicTemperature) { pub fn set_temperature_setpoint(&mut self, t: ThermodynamicTemperature) {
let t = t.min(self.temp_mon.get_upper_limit()).max(self.temp_mon.get_lower_limit()); let t = t
.min(self.temp_mon.get_upper_limit())
.max(self.temp_mon.get_lower_limit());
self.pid_ctrl_ch0.set_pid_setpoint(t); self.pid_ctrl_ch0.set_pid_setpoint(t);
self.temp_mon.set_setpoint(t); self.temp_mon.set_setpoint(t);
} }
pub fn apply_temp_mon_settings(&mut self, settings: TempMonSettings){ pub fn apply_temp_mon_settings(&mut self, settings: TempMonSettings) {
self.temp_mon.set_upper_limit(ThermodynamicTemperature::new::<degree_celsius>(settings.upper_limit)); self.temp_mon
self.temp_mon.set_lower_limit(ThermodynamicTemperature::new::<degree_celsius>(settings.lower_limit)); .set_upper_limit(ThermodynamicTemperature::new::<degree_celsius>(settings.upper_limit));
self.temp_mon
.set_lower_limit(ThermodynamicTemperature::new::<degree_celsius>(settings.lower_limit));
} }
pub fn set_temp_mon_upper_limit(&mut self, t: ThermodynamicTemperature) { pub fn set_temp_mon_upper_limit(&mut self, t: ThermodynamicTemperature) {
@ -453,7 +475,9 @@ impl Thermostat{
} }
pub fn set_temp_adc_sinc5_sinc1_with_postfilter(&mut self, index: u8, odr: ad7172::PostFilter) { pub fn set_temp_adc_sinc5_sinc1_with_postfilter(&mut self, index: u8, odr: ad7172::PostFilter) {
self.ad7172.set_sinc5_sinc1_with_50hz_60hz_rejection(index, odr).unwrap(); self.ad7172
.set_sinc5_sinc1_with_50hz_60hz_rejection(index, odr)
.unwrap();
} }
pub fn set_temp_adc_sinc3_fine_filter(&mut self, index: u8, rate: f32) { pub fn set_temp_adc_sinc3_fine_filter(&mut self, index: u8, rate: f32) {
@ -487,13 +511,13 @@ impl Thermostat{
temperature_setpoint: self.pid_ctrl_ch0.get_pid_setpoint().get::<degree_celsius>(), temperature_setpoint: self.pid_ctrl_ch0.get_pid_setpoint().get::<degree_celsius>(),
tec_settings: self.get_tec_settings(), tec_settings: self.get_tec_settings(),
pid_params: self.get_pid_settings(), pid_params: self.get_pid_settings(),
temp_adc_settings: TempAdcFilter{ temp_adc_settings: TempAdcFilter {
filter_type : temp_adc_filter_type, filter_type: temp_adc_filter_type,
sinc5sinc1odr : None, sinc5sinc1odr: None,
sinc3odr : None, sinc3odr: None,
sinc5sinc1postfilter : None, sinc5sinc1postfilter: None,
sinc3fineodr : None, sinc3fineodr: None,
rate : Some(update_rate), rate: Some(update_rate),
}, },
temp_mon_settings: self.get_temp_mon_settings(), temp_mon_settings: self.get_temp_mon_settings(),
thermistor_params: self.get_steinhart_hart(), thermistor_params: self.get_steinhart_hart(),
@ -505,27 +529,19 @@ impl Thermostat{
self.set_max_i_neg(settings.tec_settings.max_i_neg.value); self.set_max_i_neg(settings.tec_settings.max_i_neg.value);
self.set_max_i_pos(settings.tec_settings.max_i_pos.value); self.set_max_i_pos(settings.tec_settings.max_i_pos.value);
self.set_max_v(settings.tec_settings.max_v.value); self.set_max_v(settings.tec_settings.max_v.value);
self.apply_steinhart_hart(settings.thermistor_params); self.apply_steinhart_hart(settings.thermistor_params);
self.apply_temp_mon_settings(settings.temp_mon_settings); self.apply_temp_mon_settings(settings.temp_mon_settings);
match settings.temp_adc_settings.rate { match settings.temp_adc_settings.rate {
Some(rate) => { Some(rate) => match settings.temp_adc_settings.filter_type {
match settings.temp_adc_settings.filter_type { FilterType::Sinc3 => self.set_temp_adc_sinc3_filter(0, SingleChODR::closest(rate).unwrap()),
FilterType::Sinc3 => { FilterType::Sinc5Sinc1 => self.set_temp_adc_sinc5_sinc1_filter(0, SingleChODR::closest(rate).unwrap()),
self.set_temp_adc_sinc3_filter(0, SingleChODR::closest(rate).unwrap()) FilterType::Sinc3WithFineODR => self.set_temp_adc_sinc3_fine_filter(0, rate),
} FilterType::Sinc5Sinc1With50hz60HzRejection => {
FilterType::Sinc5Sinc1 => { self.set_temp_adc_sinc5_sinc1_with_postfilter(0, PostFilter::closest(rate).unwrap())
self.set_temp_adc_sinc5_sinc1_filter(0, SingleChODR::closest(rate).unwrap())
}
FilterType::Sinc3WithFineODR => {
self.set_temp_adc_sinc3_fine_filter(0, rate)
}
FilterType::Sinc5Sinc1With50hz60HzRejection => {
self.set_temp_adc_sinc5_sinc1_with_postfilter(0, PostFilter::closest(rate).unwrap())
}
} }
} },
None => { None => {
debug!(" Temperature ADC Settings is not found"); debug!(" Temperature ADC Settings is not found");
} }
@ -533,7 +549,9 @@ impl Thermostat{
self.set_pid_engaged(settings.pid_engaged); self.set_pid_engaged(settings.pid_engaged);
self.pid_ctrl_ch0.apply_pid_params(settings.pid_params); self.pid_ctrl_ch0.apply_pid_params(settings.pid_params);
self.set_temperature_setpoint(ThermodynamicTemperature::new::<degree_celsius>(settings.temperature_setpoint)); self.set_temperature_setpoint(ThermodynamicTemperature::new::<degree_celsius>(
settings.temperature_setpoint,
));
if !settings.pid_engaged { if !settings.pid_engaged {
self.set_i(settings.tec_settings.i_set.value); self.set_i(settings.tec_settings.i_set.value);
} }
@ -575,5 +593,5 @@ pub struct TecSettingSummary {
pub struct ThermistorParams { pub struct ThermistorParams {
t0: f32, t0: f32,
r0: ElectricalResistance, r0: ElectricalResistance,
b: f32 b: f32,
} }