From 312436d3e7fc56007dd820e9af42e151b4d44b2a Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Sat, 21 Dec 2024 23:43:04 +0100 Subject: [PATCH 01/11] Implement Observable POC Co-authored-by: Adin Ackerman --- examples/comp.rs | 8 ++-- src/comparator.rs | 120 +++++++++++++++++++++++++++++----------------- src/gpio.rs | 3 ++ src/lib.rs | 1 + src/observable.rs | 75 +++++++++++++++++++++++++++++ 5 files changed, 160 insertions(+), 47 deletions(-) create mode 100644 src/observable.rs diff --git a/examples/comp.rs b/examples/comp.rs index 5893e6d0..e06635ec 100644 --- a/examples/comp.rs +++ b/examples/comp.rs @@ -15,7 +15,7 @@ use rt::entry; #[entry] fn main() -> ! { - use hal::comparator::{ComparatorExt, ComparatorSplit, Config, Hysteresis, RefintInput}; + use hal::comparator::{refint_input, ComparatorExt, ComparatorSplit, Config, Hysteresis}; use hal::gpio::GpioExt; use hal::prelude::OutputPin; use hal::rcc::RccExt; @@ -31,7 +31,7 @@ fn main() -> ! { let pa1 = gpioa.pa1.into_analog(); let pa0 = gpioa.pa0.into_analog(); - let comp1 = comp1.comparator(&pa1, pa0, Config::default(), &rcc.clocks); + let comp1 = comp1.comparator(pa1, pa0, Config::default(), &rcc.clocks); let comp1 = comp1.enable(); // led1 pa1 will be updated manually when to match comp1 value @@ -39,8 +39,8 @@ fn main() -> ! { let pa7 = gpioa.pa7.into_analog(); let comp2 = comp2.comparator( - &pa7, - RefintInput::VRefintM12, + pa7, + refint_input::VRefintM12, Config::default() .hysteresis(Hysteresis::None) .output_inverted(), diff --git a/src/comparator.rs b/src/comparator.rs index b5e80fc4..6b0bdbed 100644 --- a/src/comparator.rs +++ b/src/comparator.rs @@ -34,6 +34,7 @@ use crate::gpio::{ use crate::gpio::gpioc::{PC0, PC1}; use crate::gpio::gpioe::{PE7, PE8}; use crate::gpio::gpiof::PF1; +use crate::observable::IntoObservationToken; use crate::rcc::{Clocks, Rcc}; use crate::stm32::{COMP, EXTI}; @@ -140,7 +141,7 @@ pub enum Hysteresis { /// Comparator positive input pub trait PositiveInput { - fn setup(&self, comp: &C); + fn setup(comp: &mut C); } /// Comparator negative input @@ -153,21 +154,21 @@ pub trait NegativeInput { /// Does this input rely on dividing Vrefint using an internal resistor divider /// /// This is only relevant for `RefintInput` other than `RefintInput::VRefint` - fn use_resistor_divider(&self) -> bool; + const USE_RESISTOR_DIVIDER: bool = false; - fn setup(&self, comp: &C); + fn setup(comp: &mut C); } macro_rules! positive_input_pin { ($COMP:ident, $pin_0:ident, $pin_1:ident) => { - impl PositiveInput<$COMP> for &$pin_0 { - fn setup(&self, comp: &$COMP) { + impl PositiveInput<$COMP> for $pin_0 { + fn setup(comp: &mut $COMP) { comp.csr().modify(|_, w| w.inpsel().bit(false)); } } - impl PositiveInput<$COMP> for &$pin_1 { - fn setup(&self, comp: &$COMP) { + impl PositiveInput<$COMP> for $pin_1 { + fn setup(comp: &mut $COMP) { comp.csr().modify(|_, w| w.inpsel().bit(true)); } } @@ -208,11 +209,7 @@ macro_rules! negative_input_pin_helper { impl NegativeInput<$COMP> for $input { const USE_VREFINT: bool = false; - fn use_resistor_divider(&self) -> bool { - false - } - - fn setup(&self, comp: &$COMP) { + fn setup(comp: &mut $COMP) { comp.csr().modify(|_, w| unsafe { w.inmsel().bits($bits) }); } } @@ -245,30 +242,54 @@ negative_input_pin! { COMP7: PD15, PB12, } -#[derive(Copy, Clone, Eq, PartialEq)] -pub enum RefintInput { +pub mod refint_input { /// VRefint * 1/4 - VRefintM14 = 0b000, + #[derive(Copy, Clone)] + pub struct VRefintM14; + /// VRefint * 1/2 - VRefintM12 = 0b001, + #[derive(Copy, Clone)] + pub struct VRefintM12; + /// VRefint * 3/4 - VRefintM34 = 0b010, + #[derive(Copy, Clone)] + pub struct VRefintM34; + /// VRefint - VRefint = 0b011, + #[derive(Copy, Clone)] + pub struct VRefint; + macro_rules! impl_vrefint { + ($t:ty, $bits:expr, $use_r_div:expr) => { + impl super::RefintInput for $t { + const BITS: u8 = $bits; + const USE_RESISTOR_DIVIDER: bool = $use_r_div; + } + + impl crate::observable::Observable for $t {} + impl crate::Sealed for $t {} + }; + } + + impl_vrefint!(VRefintM14, 0b000, true); + impl_vrefint!(VRefintM12, 0b001, true); + impl_vrefint!(VRefintM34, 0b010, true); + impl_vrefint!(VRefint, 0b011, false); +} + +pub trait RefintInput { + const BITS: u8; + const USE_RESISTOR_DIVIDER: bool; } macro_rules! refint_input { ($($COMP:ident, )+) => {$( - impl NegativeInput<$COMP> for RefintInput { + impl NegativeInput<$COMP> for REF { const USE_VREFINT: bool = true; + const USE_RESISTOR_DIVIDER: bool = ::USE_RESISTOR_DIVIDER; - fn use_resistor_divider(&self) -> bool { - *self != RefintInput::VRefint - } - - fn setup(&self, comp: &$COMP) { + fn setup(comp: &mut $COMP) { comp.csr() - .modify(|_, w| unsafe { w.inmsel().bits(*self as u8) }); + .modify(|_, w| unsafe { w.inmsel().bits(::BITS) }); } } )+}; @@ -289,11 +310,7 @@ macro_rules! dac_input_helper { impl NegativeInput<$COMP> for &dac::$channel<{ dac::$MODE }, ED> { const USE_VREFINT: bool = false; - fn use_resistor_divider(&self) -> bool { - false - } - - fn setup(&self, comp: &$COMP) { + fn setup(comp: &mut $COMP) { comp.csr().modify(|_, w| unsafe { w.inmsel().bits($bits) }); } } @@ -371,27 +388,38 @@ pub struct Comparator { pub trait ComparatorExt { /// Initializes a comparator - fn comparator, N: NegativeInput>( + fn comparator( self, positive_input: P, negative_input: N, config: Config, clocks: &Clocks, - ) -> Comparator; + ) -> Comparator + where + PP: PositiveInput, + NP: NegativeInput, + P: IntoObservationToken, + N: IntoObservationToken; } macro_rules! impl_comparator { ($COMP:ty, $comp:ident, $Event:expr) => { impl ComparatorExt<$COMP> for $COMP { - fn comparator, N: NegativeInput<$COMP>>( - self, - positive_input: P, - negative_input: N, + fn comparator( + mut self, + _positive_input: P, // TODO: Store these + _negative_input: N, // TODO: Store these config: Config, clocks: &Clocks, - ) -> Comparator<$COMP, Disabled> { - positive_input.setup(&self); - negative_input.setup(&self); + ) -> Comparator<$COMP, Disabled> + where + PP: PositiveInput<$COMP>, + NP: NegativeInput<$COMP>, + P: IntoObservationToken, + N: IntoObservationToken, + { + PP::setup(&mut self); + PP::setup(&mut self); // Delay for scaler voltage bridge initialization for certain negative inputs let voltage_scaler_delay = clocks.sys_clk.raw() / (1_000_000 / 200); // 200us cortex_m::asm::delay(voltage_scaler_delay); @@ -399,9 +427,9 @@ macro_rules! impl_comparator { w.hyst() .bits(config.hysteresis as u8) .scalen() - .bit(N::USE_VREFINT) + .bit(NP::USE_VREFINT) .brgen() - .bit(negative_input.use_resistor_divider()) + .bit(NP::USE_RESISTOR_DIVIDER) .pol() .bit(config.inverted) }); @@ -415,13 +443,19 @@ macro_rules! impl_comparator { impl Comparator<$COMP, Disabled> { /// Initializes a comparator - pub fn $comp, N: NegativeInput<$COMP>>( + pub fn $comp( comp: $COMP, positive_input: P, negative_input: N, config: Config, clocks: &Clocks, - ) -> Self { + ) -> Self + where + PP: PositiveInput<$COMP>, + NP: NegativeInput<$COMP>, + P: IntoObservationToken, + N: IntoObservationToken, + { comp.comparator(positive_input, negative_input, config, clocks) } diff --git a/src/gpio.rs b/src/gpio.rs index 7bb05cef..00fdf28a 100644 --- a/src/gpio.rs +++ b/src/gpio.rs @@ -1,6 +1,7 @@ //! General Purpose Input / Output use core::marker::PhantomData; +use crate::observable::Observable; use crate::rcc::Rcc; use crate::stm32::EXTI; use crate::syscfg::SysCfg; @@ -379,6 +380,8 @@ macro_rules! gpio { } } + impl Observable for $PXi { } + impl $PXi { /// Configures the pin to operate as a floating input pin pub fn into_floating_input(self) -> $PXi> { diff --git a/src/lib.rs b/src/lib.rs index 93c12d07..57d0fabd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -84,6 +84,7 @@ pub mod exti; pub mod flash; pub mod gpio; pub mod i2c; +pub mod observable; pub mod opamp; pub mod prelude; pub mod pwm; diff --git a/src/observable.rs b/src/observable.rs new file mode 100644 index 00000000..969dc248 --- /dev/null +++ b/src/observable.rs @@ -0,0 +1,75 @@ +use crate::Sealed; +use core::marker::PhantomData; + +pub trait IntoObservationToken: Sized + crate::Sealed { + type Peripheral; + fn into_ot(self) -> ObservationToken; +} + +/// A struct to hold peripherals which are to be observed. +/// +/// This prevents the observed peripheral from being consumed. Thus +/// preventing things like a an observed gpio pin changing mode or an opamp from +/// being disabled. This makes sure the underlaying peripheral will not +/// change mode into something that is not compatible with what ever may be observing it. +pub struct Observed { + peripheral: P, +} + +impl Observed { + /// Release the observation of this peripheral + /// + /// This returns the underlaying perpheral type. Since it is no longer + /// observed, you are once again free to do what you want with it. + pub fn release(self, _data: [ObservationToken

; OBSERVER_COUNT]) -> P { + self.peripheral + } +} + +/// A struct to represent a registered observation of a peripheral of type `P` +/// +/// The existence of this type guarantees that the observed peripheral will not +/// change mode into something that is not compatibe with what ever is observing it +pub struct ObservationToken

{ + _p: PhantomData

, +} + +/// A trait providing an interface to make peripherals observed +/// +/// See [`Observed`] and [`ObservationToken`] +pub trait Observable: Sized { + fn observe(self) -> (Observed, [ObservationToken; N]) { + ( + Observed { peripheral: self }, + core::array::from_fn(|_| ObservationToken { _p: PhantomData }), + ) + } +} + +impl IntoObservationToken for P { + type Peripheral = P; + fn into_ot(self) -> ObservationToken { + let (_peripheral, [ot]) = self.observe(); + ot + } +} + +impl Sealed for ObservationToken

{} +impl IntoObservationToken for ObservationToken

{ + type Peripheral = P; + fn into_ot(self) -> Self { + self + } +} + +impl AsRef

for Observed { + fn as_ref(&self) -> &P { + &self.peripheral + } +} + +impl AsMut

for Observed { + fn as_mut(&mut self) -> &mut P { + &mut self.peripheral + } +} From 845448e0fa32b754653825556b05658579441efc Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Sun, 22 Dec 2024 00:14:35 +0100 Subject: [PATCH 02/11] Fix comp_w_dac example --- examples/comp_w_dac.rs | 10 ++++++---- src/comparator.rs | 6 +++--- src/dac.rs | 3 +++ 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/examples/comp_w_dac.rs b/examples/comp_w_dac.rs index 20600068..38384ce2 100644 --- a/examples/comp_w_dac.rs +++ b/examples/comp_w_dac.rs @@ -7,6 +7,7 @@ mod utils; extern crate cortex_m_rt as rt; use rt::entry; +use stm32g4xx_hal::observable::Observable; #[entry] fn main() -> ! { @@ -30,15 +31,16 @@ fn main() -> ! { // Set up DAC to output to pa4 and to internal signal Dac1IntSig1 // which just so happens is compatible with comp1 let dac1ch1 = dp.DAC1.constrain((gpioa.pa4, Dac1IntSig1), &mut rcc); - let mut dac = dac1ch1.calibrate_buffer(&mut delay).enable(); + let dac = dac1ch1.calibrate_buffer(&mut delay).enable(); + let (mut dac, [dac_token]) = dac.observe(); let (comp1, _comp2, ..) = dp.COMP.split(&mut rcc); let pa1 = gpioa.pa1.into_analog(); // Set up comparator with pa1 as positive, and the DAC as negative input let comp = comp1.comparator( - &pa1, - &dac, + pa1, + dac_token, comparator::Config::default().hysteresis(comparator::Hysteresis::None), &rcc.clocks, ); @@ -59,7 +61,7 @@ fn main() -> ! { // * 0V at p1 => 0% duty // * VDDA at p1 => 100% duty loop { - dac.set_value(val); + dac.as_mut().set_value(val); match val { 0 => dir = Direction::Upcounting, 4095 => dir = Direction::Downcounting, diff --git a/src/comparator.rs b/src/comparator.rs index 6b0bdbed..7c735e61 100644 --- a/src/comparator.rs +++ b/src/comparator.rs @@ -148,12 +148,12 @@ pub trait PositiveInput { pub trait NegativeInput { /// Does this input use the internal reference Vrefint /// - /// This only true for RefintInput + /// This only true for [`RefintInput`] const USE_VREFINT: bool; /// Does this input rely on dividing Vrefint using an internal resistor divider /// - /// This is only relevant for `RefintInput` other than `RefintInput::VRefint` + /// This is only relevant for [`RefintInput`] other than [`refint_input::VRefint`] const USE_RESISTOR_DIVIDER: bool = false; fn setup(comp: &mut C); @@ -307,7 +307,7 @@ refint_input!(COMP5, COMP6, COMP7,); macro_rules! dac_input_helper { ($COMP:ident: $channel:ident, $MODE:ident, $bits:expr) => { - impl NegativeInput<$COMP> for &dac::$channel<{ dac::$MODE }, ED> { + impl NegativeInput<$COMP> for dac::$channel<{ dac::$MODE }, ED> { const USE_VREFINT: bool = false; fn setup(comp: &mut $COMP) { diff --git a/src/dac.rs b/src/dac.rs index 991e8467..48c948c1 100644 --- a/src/dac.rs +++ b/src/dac.rs @@ -218,6 +218,9 @@ macro_rules! dac_helper { } } + impl crate::Sealed for $CX { } + impl crate::observable::Observable for $CX { } + impl $CX { /// Calibrate the DAC output buffer by performing a "User /// trimming" operation. It is useful when the VDDA/VREF+ From d1c6c225d91f7b9e2877a61b012147a8d944932e Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Sun, 22 Dec 2024 01:58:40 +0100 Subject: [PATCH 03/11] Use ObservationLock --- src/comparator.rs | 14 +++++++------- src/observable.rs | 18 +++++------------- 2 files changed, 12 insertions(+), 20 deletions(-) diff --git a/src/comparator.rs b/src/comparator.rs index 7c735e61..5ebb880e 100644 --- a/src/comparator.rs +++ b/src/comparator.rs @@ -34,7 +34,7 @@ use crate::gpio::{ use crate::gpio::gpioc::{PC0, PC1}; use crate::gpio::gpioe::{PE7, PE8}; use crate::gpio::gpiof::PF1; -use crate::observable::IntoObservationToken; +use crate::observable::ObservationLock; use crate::rcc::{Clocks, Rcc}; use crate::stm32::{COMP, EXTI}; @@ -398,8 +398,8 @@ pub trait ComparatorExt { where PP: PositiveInput, NP: NegativeInput, - P: IntoObservationToken, - N: IntoObservationToken; + P: ObservationLock, + N: ObservationLock; } macro_rules! impl_comparator { @@ -415,8 +415,8 @@ macro_rules! impl_comparator { where PP: PositiveInput<$COMP>, NP: NegativeInput<$COMP>, - P: IntoObservationToken, - N: IntoObservationToken, + P: ObservationLock, + N: ObservationLock, { PP::setup(&mut self); PP::setup(&mut self); @@ -453,8 +453,8 @@ macro_rules! impl_comparator { where PP: PositiveInput<$COMP>, NP: NegativeInput<$COMP>, - P: IntoObservationToken, - N: IntoObservationToken, + P: ObservationLock, + N: ObservationLock, { comp.comparator(positive_input, negative_input, config, clocks) } diff --git a/src/observable.rs b/src/observable.rs index 969dc248..c912b4c1 100644 --- a/src/observable.rs +++ b/src/observable.rs @@ -1,9 +1,8 @@ use crate::Sealed; use core::marker::PhantomData; -pub trait IntoObservationToken: Sized + crate::Sealed { - type Peripheral; - fn into_ot(self) -> ObservationToken; +pub trait ObservationLock: Sized + crate::Sealed { + type Peripheral: Observable; } /// A struct to hold peripherals which are to be observed. @@ -16,7 +15,7 @@ pub struct Observed { peripheral: P, } -impl Observed { +impl Observed { /// Release the observation of this peripheral /// /// This returns the underlaying perpheral type. Since it is no longer @@ -46,20 +45,13 @@ pub trait Observable: Sized { } } -impl IntoObservationToken for P { +impl ObservationLock for P { type Peripheral = P; - fn into_ot(self) -> ObservationToken { - let (_peripheral, [ot]) = self.observe(); - ot - } } impl Sealed for ObservationToken

{} -impl IntoObservationToken for ObservationToken

{ +impl ObservationLock for ObservationToken

{ type Peripheral = P; - fn into_ot(self) -> Self { - self - } } impl AsRef

for Observed { From 46d43fe8d926953593491dc51df28bf1d903a888 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Sun, 22 Dec 2024 02:05:07 +0100 Subject: [PATCH 04/11] Add more trait bounds --- src/observable.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/observable.rs b/src/observable.rs index c912b4c1..aaac5dbf 100644 --- a/src/observable.rs +++ b/src/observable.rs @@ -11,7 +11,7 @@ pub trait ObservationLock: Sized + crate::Sealed { /// preventing things like a an observed gpio pin changing mode or an opamp from /// being disabled. This makes sure the underlaying peripheral will not /// change mode into something that is not compatible with what ever may be observing it. -pub struct Observed { +pub struct Observed { peripheral: P, } @@ -29,7 +29,7 @@ impl Observed { /// /// The existence of this type guarantees that the observed peripheral will not /// change mode into something that is not compatibe with what ever is observing it -pub struct ObservationToken

{ +pub struct ObservationToken { _p: PhantomData

, } @@ -54,13 +54,13 @@ impl ObservationLock for ObservationToken

{ type Peripheral = P; } -impl AsRef

for Observed { +impl AsRef

for Observed { fn as_ref(&self) -> &P { &self.peripheral } } -impl AsMut

for Observed { +impl AsMut

for Observed { fn as_mut(&mut self) -> &mut P { &mut self.peripheral } From 1e332f9a02b0a71f1c0a6aa0b3e64e08a67c9275 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Sun, 22 Dec 2024 02:14:00 +0100 Subject: [PATCH 05/11] impl Deref for Observed --- examples/comp_w_dac.rs | 2 +- src/observable.rs | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/examples/comp_w_dac.rs b/examples/comp_w_dac.rs index 38384ce2..27bce315 100644 --- a/examples/comp_w_dac.rs +++ b/examples/comp_w_dac.rs @@ -61,7 +61,7 @@ fn main() -> ! { // * 0V at p1 => 0% duty // * VDDA at p1 => 100% duty loop { - dac.as_mut().set_value(val); + dac.set_value(val); match val { 0 => dir = Direction::Upcounting, 4095 => dir = Direction::Downcounting, diff --git a/src/observable.rs b/src/observable.rs index aaac5dbf..dce489c9 100644 --- a/src/observable.rs +++ b/src/observable.rs @@ -1,5 +1,5 @@ use crate::Sealed; -use core::marker::PhantomData; +use core::{marker::PhantomData, ops}; pub trait ObservationLock: Sized + crate::Sealed { type Peripheral: Observable; @@ -65,3 +65,17 @@ impl AsMut

for Observed { &mut self.peripheral } } + +impl ops::Deref for Observed { + type Target = P; + + fn deref(&self) -> &Self::Target { + &self.peripheral + } +} + +impl ops::DerefMut for Observed { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.peripheral + } +} From fa03e3b5d38c34f20cec2b9a52c1c40c5cc9a4ad Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Mon, 23 Dec 2024 00:49:17 +0100 Subject: [PATCH 06/11] Add Observed things to opamp --- examples/opamp.rs | 28 +-- src/opamp.rs | 515 ++++++++++++++++++---------------------------- 2 files changed, 218 insertions(+), 325 deletions(-) diff --git a/examples/opamp.rs b/examples/opamp.rs index e4576a7a..37fa8925 100644 --- a/examples/opamp.rs +++ b/examples/opamp.rs @@ -5,7 +5,7 @@ use stm32g4xx_hal::adc::AdcClaim; use stm32g4xx_hal::adc::ClockSource; -use stm32g4xx_hal::opamp::{Gain, InternalOutput}; +use stm32g4xx_hal::opamp::Gain; use stm32g4xx_hal::prelude::*; use stm32g4xx_hal::pwr::PwrExt; @@ -34,12 +34,20 @@ fn main() -> ! { // setup opamps let (opamp1, opamp2, opamp3, ..) = dp.OPAMP.split(&mut rcc); + let pa1 = gpioa.pa1.into_analog(); + let pa2 = gpioa.pa2.into_analog(); + let pa7 = gpioa.pa7.into_analog(); + + let pb0 = gpiob.pb0.into_analog(); + let pb1 = gpiob.pb1.into_analog(); + let pb2 = gpiob.pb2.into_analog(); + // Set up opamp1 and opamp2 in follower mode - let opamp1 = opamp1.follower(gpioa.pa1, gpioa.pa2); - let opamp2 = opamp2.follower(gpioa.pa7, InternalOutput); + let opamp1 = opamp1.follower(pa1).enable_output(pa2); + let opamp2 = opamp2.follower(pa7); // Set up opamp1 and opamp2 in open loop mode - let opamp3 = opamp3.open_loop(gpiob.pb0, gpiob.pb2, gpiob.pb1); + let opamp3 = opamp3.open_loop(pb0, pb2).enable_output(pb1); // disable opamps let (opamp1, pa1, pa2) = opamp1.disable(); @@ -48,18 +56,10 @@ fn main() -> ! { let (_opamp3, _pb0, _pb2, _pb1) = opamp3.disable(); // Configure opamp1 with pa1 as non-inverting input and set gain to x2 - let _opamp1 = opamp1.pga( - pa1, - pa2, // Route output to pin pa2 - Gain::Gain2, - ); + let _opamp1 = opamp1.pga(pa1, Gain::Gain2).enable_output(pa2); // Route output to pin pa2 // Configure op with pa7 as non-inverting input and set gain to x4 - let opamp2 = opamp2.pga( - pa7, - InternalOutput, // Do not route output to any external pin, use internal AD instead - Gain::Gain4, - ); + let opamp2 = opamp2.pga(pa7, Gain::Gain4); // Do not route output to any external pin, use internal AD instead // Lock opamp2. After the opamp is locked its registers cannot be written // until the device is reset (even if using unsafe register accesses). diff --git a/src/opamp.rs b/src/opamp.rs index 98cf644b..d96e0201 100644 --- a/src/opamp.rs +++ b/src/opamp.rs @@ -59,7 +59,8 @@ // TODO: Add support for calibration -use core::{borrow::Borrow, marker::PhantomData}; +use crate::observable::{Observable, ObservationLock}; +use core::marker::PhantomData; /// PGA Gain pub enum Gain { @@ -111,83 +112,70 @@ pub struct Locked { pub struct InternalOutput; /// Trait for opamps that can be run in follower mode. -pub trait IntoFollower { +pub trait IntoFollower { /// Coonfigures the opamp as voltage follower. - fn follower(self, input: IntoInput, output: IntoOutput) -> Follower; + fn follower(self, input: Input) -> Follower; } /// Trait for opamps that can be run in open-loop mode. -pub trait IntoOpenLoop< - Opamp, - IntoNonInverting, - IntoInverting, - IntoOutput, - NonInverting, - Inverting, - Output, -> -{ +pub trait IntoOpenLoop { /// Configures the opamp for open-loop operation. fn open_loop( self, - non_inverting: IntoNonInverting, - inverting: IntoInverting, - output: IntoOutput, - ) -> OpenLoop; + non_inverting: NonInverting, + inverting: Inverting, + ) -> OpenLoop; } /// Trait for opamps that can be run in programmable gain mode. #[allow(private_bounds)] -pub trait IntoPga +pub trait IntoPga where Opamp: ConfigurePgaReg + LookupPgaGain, { /// Configures the opamp for programmable gain operation. - fn pga>( + fn pga( self, - non_inverting: B, - output: Output, + non_inverting: NonInverting, gain: Gain, - ) -> Pga; + ) -> Pga; /// Trait for opamps that can be run in programmable gain mode, /// with external filtering. - fn pga_external_filter< - B1: Borrow, - B2: Borrow<>::Vinm0>, - >( + fn pga_external_filter( self, - non_inverting: B1, - filter: B2, - output: Output, + non_inverting: NonInverting, + filter: F, gain: Gain, - ) -> Pga; + ) -> Pga + where + F: ObservationLock, + Opamp::Vinm0: Observable; /// Configures the opamp for programmable gain operation, with /// external filtering. - fn pga_external_bias< - B1: Borrow, - B2: Borrow<>::Vinm0>, - >( + fn pga_external_bias( self, - non_inverting: B1, - inverting: B2, - output: Output, + non_inverting: NonInverting, + inverting: N, gain: Gain, - ) -> Pga; + ) -> Pga + where + N: ObservationLock, + Opamp::Vinm0: Observable; /// Configures the opamp for programmable gain operation, with /// external filtering. - fn pga_external_bias_and_filter< - B1: Borrow, - B2: Borrow<>::Vinm0>, - B3: Borrow<>::Vinm1>, - >( + fn pga_external_bias_and_filter( self, - non_inverting: B1, - inverting: B2, - filter: B3, - output: Output, + non_inverting: NonInverting, + inverting: N, + filter: F, gain: Gain, - ) -> Pga; + ) -> Pga + where + N: ObservationLock, + F: ObservationLock, + Opamp::Vinm0: Observable, + Opamp::Vinm1: Observable; } /// Internal trait implementing the low level register write used to @@ -197,28 +185,23 @@ where Opamp: LookupPgaGain, { /// Type of the associated vinm0 input. - type Vinm0; + type Vinm0: Observable; /// Type of the associated vinm1 input. - type Vinm1; + type Vinm1: Observable; /// Write the opamp CSR register configuring the opamp PGA. /// /// Safety: This is a raw register access. - unsafe fn write_pga_reg(gain: Gain, mode: PgaMode, output_enable: bool); + unsafe fn write_pga_reg(gain: Gain, mode: PgaMode); /// Configure the /// - fn configure_pga( - output: Output, - gain: Gain, - mode: PgaMode, - output_enable: bool, - ) -> Pga { - unsafe { Self::write_pga_reg(gain, mode, output_enable) }; + fn configure_pga(gain: Gain, mode: PgaMode) -> Pga { + unsafe { Self::write_pga_reg(gain, mode) }; Pga { opamp: PhantomData, non_inverting: PhantomData, - output, + output: InternalOutput, } } } @@ -245,6 +228,44 @@ trait LookupPgaGain { fn pga_gain(mode: PgaMode, gain: Gain) -> Self::PgaGainReg; } +/// A signal source that can be connected to the `OPAMP`'s non inverting input +pub trait NonInverting { + /// Type for selecting this as the non inverting input of this opamp + type VP_SEL; + /// Configuration value for selecting this as the non inverting input of this opamp + const VP_SEL: Self::VP_SEL; +} + +/// A signal source that can be connected to the `OPAMP`'s inverting input +pub trait Inverting { + /// Type for selecting this as the inverting input of this opamp + type VM_SEL; + /// Configuration value for selecting this as the inverting input of this opamp + const VM_SEL: Self::VM_SEL; +} + +macro_rules! impl_inverting { + ($opamp:ty, [$($t:ty: $vinmX:ident),+]) => { + $(paste::paste!{ + impl Inverting<$opamp> for $t { + type VM_SEL = crate::stm32::opamp::[<$opamp:lower _csr>]::VM_SEL; + const VM_SEL: Self::VM_SEL = Self::VM_SEL::$vinmX; + } + })* + }; +} + +macro_rules! impl_non_inverting { + ($opamp:ident, [$($t:ty: $vinpX:ident),+]) => { + $(paste::paste!{ + impl NonInverting<$opamp> for $t { + type VP_SEL = crate::stm32::opamp::[<$opamp:lower _csr>]::VP_SEL; + const VP_SEL: Self::VP_SEL = Self::VP_SEL::$vinpX; + } + })+ + }; +} + macro_rules! opamps { { $( @@ -356,6 +377,9 @@ macro_rules! opamps { } } + impl crate::Sealed for Follower<$opamp, Input, Output> { } + impl crate::observable::Observable for Follower<$opamp, Input, Output> { } + impl Follower<$opamp, Input, Output> { /// Set the lock bit in the registers. After the lock bit is /// set the opamp cannot be reconfigured until the chip is @@ -394,10 +418,12 @@ macro_rules! opamps { } /// Enables the external output pin. - pub fn enable_output(self, output:$output) -> Follower<$opamp, Input, $output> { + pub fn enable_output(self, output: O) -> Follower<$opamp, Input, O> + where O: ObservationLock + { unsafe { $opamp::_enable_output(); } - Follower::<$opamp, Input, $output> { + Follower { opamp: PhantomData, input: self.input, output, @@ -405,6 +431,9 @@ macro_rules! opamps { } } + impl crate::Sealed for OpenLoop<$opamp, NonInverting, Inverting, Output> { } + impl crate::observable::Observable for OpenLoop<$opamp, NonInverting, Inverting, Output> { } + impl OpenLoop<$opamp, NonInverting, Inverting, Output> { /// Set the lock bit in the registers. After the lock bit is /// set the opamp cannot be reconfigured until the chip is @@ -444,10 +473,12 @@ macro_rules! opamps { } /// Enables the external output pin. - pub fn enable_output(self, output:$output) -> OpenLoop<$opamp, NonInverting, Inverting, $output> { + pub fn enable_output(self, output: O) -> OpenLoop<$opamp, NonInverting, Inverting, O> + where O: ObservationLock, + { unsafe { $opamp::_enable_output(); } - OpenLoop::<$opamp, NonInverting, Inverting, $output> { + OpenLoop { opamp: PhantomData, inverting: self.inverting, non_inverting: self.non_inverting, @@ -456,6 +487,9 @@ macro_rules! opamps { } } + impl crate::Sealed for Pga<$opamp, NonInverting, Output> { } + impl crate::observable::Observable for Pga<$opamp, NonInverting, Output> { } + impl Pga<$opamp, NonInverting, Output> { /// Set the lock bit in the registers. After the lock bit is /// set the opamp cannot be reconfigured until the chip is @@ -477,7 +511,7 @@ macro_rules! opamps { /// This will connect the opamp output to the internal ADC. pub fn disable_output(self) -> (Pga<$opamp, NonInverting, InternalOutput>, $output) { unsafe { $opamp::_disable_output(); } - (Pga::<$opamp, NonInverting, InternalOutput> { + (Pga { opamp: PhantomData, non_inverting: self.non_inverting, output: InternalOutput, @@ -493,23 +527,26 @@ macro_rules! opamps { } /// Enables the external output pin. - pub fn enable_output(self, output: IntoOutput) -> Pga<$opamp, NonInverting, $output> + pub fn enable_output(self, output: O) -> Pga<$opamp, NonInverting, O> where - IntoOutput: Into<$output>, + O: ObservationLock, { unsafe { $opamp::_enable_output(); } - Pga::<$opamp, NonInverting, $output> { + Pga::<$opamp, NonInverting, O> { opamp: PhantomData, non_inverting: self.non_inverting, - output: output.into(), + output, } } } - opamps!{ @follower $opamp => $opampreg, $output, $($non_inverting_mask, $non_inverting),* } - opamps!{ @open_loop_tt $opamp => $opampreg, $output, $($non_inverting_mask, $non_inverting),* : ($($inverting_mask, $inverting),*) } - opamps!{ @pga_tt $opamp => $opampreg, $output, $($non_inverting_mask, $non_inverting),* : $vinm0, $vinm1 } + impl_inverting!($opamp, [$($inverting: $inverting_mask),*]); + impl_non_inverting!($opamp, [$($non_inverting: $non_inverting_mask),* ]); + + opamps!{ @follower $opamp => $opampreg, $output } + opamps!{ @open_loop $opamp => $opampreg, $output } + opamps!{ @pga $opamp => $opampreg, $output : $vinm0, $vinm1 } )* @@ -546,52 +583,28 @@ macro_rules! opamps { $opamp:ident => $opampreg:ident , $output:ty - , - $( - $input_mask:tt - , - $input:ty - ),* } => { paste::paste!{ - $( - impl IntoFollower <$opamp, IntoInput, IntoOutput, $input, $output> for Disabled<$opamp> + impl IntoFollower<$opamp, Input, InternalOutput> for Disabled<$opamp> where - IntoInput: Into<$input>, - IntoOutput: Into<$output>, + I: NonInverting<$opamp, VP_SEL=crate::stm32::opamp::[<$opampreg _csr>]::VP_SEL>, + Input: ObservationLock, { - #[inline] fn follower( self, - input: IntoInput, - output: IntoOutput, - ) -> Follower<$opamp, $input, $output> { - self.follower(input, InternalOutput).enable_output(output.into()) - } - } - - impl IntoFollower <$opamp, IntoInput, InternalOutput, $input, InternalOutput> for Disabled<$opamp> - where - IntoInput: Into<$input>, - { - fn follower( - self, - input: IntoInput, - _output: InternalOutput, - ) -> Follower<$opamp, $input, InternalOutput> { - let input = input.into(); + input: Input, + ) -> Follower<$opamp, Input, InternalOutput> { unsafe { - use crate::stm32::opamp::[<$opampreg _csr>]::OPAINTOEN; (*crate::stm32::OPAMP::ptr()) .[<$opampreg _csr>]() .write(|csr_w| csr_w .vp_sel() - .$input_mask() + .variant(I::VP_SEL) .vm_sel() .output() .opaintoen() - .variant(OPAINTOEN::Adcchannel) + .adcchannel() .opaen() .enabled() ); @@ -599,82 +612,39 @@ macro_rules! opamps { Follower {opamp: PhantomData, input, output: InternalOutput} } } - - )* } }; - { - @open_loop_tt - $opamp:ident => $opampreg:ident - , - $output:ty - , - $($non_inverting_mask:tt, $non_inverting:ty),* - : - $invertings:tt - } => { - $( - opamps!{ @open_loop $opamp => $opampreg, $output, $non_inverting_mask, $non_inverting, $invertings } - )* - }; - { @open_loop $opamp:ident => $opampreg:ident , $output:ty - , - $non_inverting_mask:tt - , - $non_inverting:ty - , - ($($inverting_mask:tt, $inverting:ty),*) } => { paste::paste!{ - $( - impl IntoOpenLoop - <$opamp, IntoNonInverting, IntoInverting, IntoOutput, $non_inverting, $inverting, $output> for Disabled<$opamp> - where - IntoNonInverting: Into<$non_inverting>, - IntoInverting: Into<$inverting>, - IntoOutput: Into<$output>, - { - #[inline] - fn open_loop( - self, - non_inverting: IntoNonInverting, - inverting: IntoInverting, - output: IntoOutput, - ) -> OpenLoop<$opamp, $non_inverting, $inverting, $output> { - self.open_loop(non_inverting, inverting, InternalOutput).enable_output(output.into()) - } - } - impl IntoOpenLoop - <$opamp, IntoNonInverting, IntoInverting, InternalOutput, $non_inverting, $inverting, InternalOutput> for Disabled<$opamp> + impl IntoOpenLoop + <$opamp, P, N> for Disabled<$opamp> where - IntoNonInverting: Into<$non_inverting>, - IntoInverting: Into<$inverting>, + PP: NonInverting<$opamp, VP_SEL=crate::stm32::opamp::[<$opampreg _csr>]::VP_SEL>, + NP: Inverting<$opamp, VM_SEL=crate::stm32::opamp::[<$opampreg _csr>]::VM_SEL>, + P: ObservationLock, + N: ObservationLock, { fn open_loop( self, - non_inverting: IntoNonInverting, - inverting: IntoInverting, - _output: InternalOutput, - ) -> OpenLoop<$opamp, $non_inverting, $inverting, InternalOutput> { - let non_inverting = non_inverting.into(); - let inverting = inverting.into(); + non_inverting: P, + inverting: N, + ) -> OpenLoop<$opamp, P, N, InternalOutput> { unsafe { - use crate::stm32::opamp::[<$opampreg _csr>]::OPAINTOEN; (*crate::stm32::OPAMP::ptr()) .[<$opampreg _csr>]() .write(|csr_w| csr_w.vp_sel() - .$non_inverting_mask() + .variant(PP::VP_SEL) .vm_sel() - .$inverting_mask() + .variant(NP::VM_SEL) .opaintoen() - .variant(OPAINTOEN::Adcchannel) + .adcchannel() .opaen() .enabled() ); @@ -682,182 +652,105 @@ macro_rules! opamps { OpenLoop {opamp: PhantomData, non_inverting, inverting, output: InternalOutput} } } - )* } }; - { - @pga_tt - $opamp:ident => $opampreg:ident - , - $output:ty - , - $($non_inverting_mask:tt, $non_inverting:ty),* - : - $vinm0:ty - , - $vinm1:ty - } => { - $( - opamps!{ @pga $opamp => $opampreg, $output, $non_inverting_mask, $non_inverting, $vinm0, $vinm1 } - )* - }; - { @pga $opamp:ident => $opampreg:ident , $output:ty - , - $non_inverting_mask:tt - , - $non_inverting:ty - , + : $vinm0:ty , $vinm1:ty } => { paste::paste!{ - impl ConfigurePgaReg<$opamp, $non_inverting> for $opamp + impl ConfigurePgaReg<$opamp, P> for $opamp + where + PP: NonInverting<$opamp, VP_SEL=crate::stm32::opamp::[<$opampreg _csr>]::VP_SEL>, + P: ObservationLock, { type Vinm0 = $vinm0; type Vinm1 = $vinm1; /// Configures the opamp for programmable gain operation. - unsafe fn write_pga_reg(gain: Gain, mode: PgaMode, output_enable: bool) { - use crate::stm32::opamp::[<$opampreg _csr>]::OPAINTOEN; - + unsafe fn write_pga_reg(gain: Gain, mode: PgaMode) { (*crate::stm32::OPAMP::ptr()) .[<$opampreg _csr>]() .write(|csr_w| csr_w.vp_sel() - .$non_inverting_mask() + .variant(PP::VP_SEL) .vm_sel() .pga() .pga_gain() .variant($opamp::pga_gain(mode, gain)) .opaintoen() - .variant(match output_enable { - true => OPAINTOEN::OutputPin, - false => OPAINTOEN::Adcchannel, - }) + .adcchannel() .opaen() .enabled() ); } } - impl IntoPga<$opamp, $non_inverting, $output> for Disabled<$opamp> + impl IntoPga<$opamp, P> for Disabled<$opamp> + where + PP: NonInverting<$opamp, VP_SEL=crate::stm32::opamp::[<$opampreg _csr>]::VP_SEL>, + P: ObservationLock, { - fn pga>( + fn pga( self, - _non_inverting: B, - output: $output, + _non_inverting: P, gain: Gain, - ) -> Pga<$opamp, $non_inverting, $output> + ) -> Pga<$opamp, P, InternalOutput> + where + PP: NonInverting<$opamp, VP_SEL=crate::stm32::opamp::[<$opampreg _csr>]::VP_SEL>, + P: ObservationLock, { - $opamp::configure_pga(output.into(), gain, PgaMode::Pga, true) - } - - #[allow(private_bounds)] - fn pga_external_filter< - B1: Borrow<$non_inverting>, - B2: Borrow<<$opamp as ConfigurePgaReg<$opamp, $non_inverting>>::Vinm0>, - >( - self, - _non_inverting: B1, - _filter: B2, - output: $output, - gain: Gain, - ) -> Pga<$opamp, $non_inverting, $output> { - $opamp::configure_pga(output.into(), gain, PgaMode::PgaExternalFilter, true) - } - - #[allow(private_bounds)] - fn pga_external_bias< - B1: Borrow<$non_inverting>, - B2: Borrow<<$opamp as ConfigurePgaReg<$opamp, $non_inverting>>::Vinm0>, - >( - self, - _non_inverting: B1, - _inverting: B2, - output: $output, - gain: Gain, - ) -> Pga<$opamp, $non_inverting, $output> { - $opamp::configure_pga(output.into(), gain, PgaMode::PgaExternalBias, true) + $opamp::configure_pga(gain, PgaMode::Pga) } #[allow(private_bounds)] - fn pga_external_bias_and_filter< - B1: Borrow<$non_inverting>, - B2: Borrow<<$opamp as ConfigurePgaReg<$opamp, $non_inverting>>::Vinm0>, - B3: Borrow<<$opamp as ConfigurePgaReg<$opamp, $non_inverting>>::Vinm1>, - >( + fn pga_external_filter( self, - _non_inverting: B1, - _inverting: B2, - _filter: B3, - output: $output, + _non_inverting: P, + _filter: F, gain: Gain, - ) -> Pga<$opamp, $non_inverting, $output> { - $opamp::configure_pga(output.into(), gain, PgaMode::PgaExternalBiasAndFilter, true) - } - } - - impl IntoPga<$opamp, $non_inverting, InternalOutput> for Disabled<$opamp> - { - fn pga>( - self, - _non_inverting: B, - _output: InternalOutput, - gain: Gain, - ) -> Pga<$opamp, $non_inverting, InternalOutput> + ) -> Pga<$opamp, P, InternalOutput> + where + F: ObservationLock>::Vinm0>, + F::Peripheral: Observable { - $opamp::configure_pga(InternalOutput, gain, PgaMode::Pga, true) + $opamp::configure_pga(gain, PgaMode::PgaExternalFilter) } #[allow(private_bounds)] - fn pga_external_filter< - B1: Borrow<$non_inverting>, - B2: Borrow<<$opamp as ConfigurePgaReg<$opamp, $non_inverting>>::Vinm0>, - >( + fn pga_external_bias( self, - _non_inverting: B1, - _filter: B2, - _output: InternalOutput, + _non_inverting: P, + _inverting: N, gain: Gain, - ) -> Pga<$opamp, $non_inverting, InternalOutput> { - $opamp::configure_pga(InternalOutput, gain, PgaMode::PgaExternalFilter, true) - } - - #[allow(private_bounds)] - fn pga_external_bias< - B1: Borrow<$non_inverting>, - B2: Borrow<<$opamp as ConfigurePgaReg<$opamp, $non_inverting>>::Vinm0>, - >( - self, - _non_inverting: B1, - _inverting: B2, - _output: InternalOutput, - gain: Gain, - ) -> Pga<$opamp, $non_inverting, InternalOutput> { - $opamp::configure_pga(InternalOutput, gain, PgaMode::PgaExternalBias, true) + ) -> Pga<$opamp, P, InternalOutput> + where + N: ObservationLock>::Vinm0>, + N::Peripheral: Observable + { + $opamp::configure_pga(gain, PgaMode::PgaExternalBias) } #[allow(private_bounds)] - fn pga_external_bias_and_filter< - B1: Borrow<$non_inverting>, - B2: Borrow<<$opamp as ConfigurePgaReg<$opamp, $non_inverting>>::Vinm0>, - B3: Borrow<<$opamp as ConfigurePgaReg<$opamp, $non_inverting>>::Vinm1>, - >( + fn pga_external_bias_and_filter( self, - _non_inverting: B1, - _inverting: B2, - _filter: B3, - _output: InternalOutput, + _non_inverting: P, + _inverting: N, + _filter: F, gain: Gain, - ) -> Pga<$opamp, $non_inverting, InternalOutput> { - $opamp::configure_pga(InternalOutput, gain, PgaMode::PgaExternalBiasAndFilter, true) + ) -> Pga<$opamp, P, InternalOutput> + where + N: ObservationLock>::Vinm0>, + F: ObservationLock>::Vinm1>, + { + $opamp::configure_pga(gain, PgaMode::PgaExternalBiasAndFilter) } } } @@ -984,13 +877,13 @@ opamps! { vinm0: crate::gpio::gpioa::PA3, vinm1: crate::gpio::gpioc::PC5, inverting: { - crate::gpio::gpioa::PA3: vinm0, - crate::gpio::gpioc::PC5: vinm1, + crate::gpio::gpioa::PA3: Vinm0, + crate::gpio::gpioc::PC5: Vinm1, }, non_inverting: { - crate::gpio::gpioa::PA1: vinp0, - crate::gpio::gpioa::PA3: vinp1, - crate::gpio::gpioa::PA7: vinp2, + crate::gpio::gpioa::PA1: Vinp0, + crate::gpio::gpioa::PA3: Vinp1, + crate::gpio::gpioa::PA7: Vinp2, }, output: crate::gpio::gpioa::PA2, }, @@ -998,14 +891,14 @@ opamps! { vinm0: crate::gpio::gpioa::PA5, vinm1: crate::gpio::gpioc::PC5, inverting: { - crate::gpio::gpioa::PA5: vinm0, - crate::gpio::gpioc::PC5: vinm1, + crate::gpio::gpioa::PA5: Vinm0, + crate::gpio::gpioc::PC5: Vinm1, }, non_inverting: { - crate::gpio::gpioa::PA7: vinp0, - crate::gpio::gpiob::PB14: vinp1, - crate::gpio::gpiob::PB0: vinp2, - crate::gpio::gpiod::PD14: vinp3, + crate::gpio::gpioa::PA7: Vinp0, + crate::gpio::gpiob::PB14: Vinp1, + crate::gpio::gpiob::PB0: Vinp2, + crate::gpio::gpiod::PD14: Vinp3, }, output: crate::gpio::gpioa::PA6, }, @@ -1013,13 +906,13 @@ opamps! { vinm0: crate::gpio::gpiob::PB2, vinm1: crate::gpio::gpiob::PB10, inverting: { - crate::gpio::gpiob::PB2: vinm0, - crate::gpio::gpiob::PB10: vinm1, + crate::gpio::gpiob::PB2: Vinm0, + crate::gpio::gpiob::PB10: Vinm1, }, non_inverting: { - crate::gpio::gpiob::PB0: vinp0, - crate::gpio::gpiob::PB13: vinp1, - crate::gpio::gpioa::PA1: vinp2, + crate::gpio::gpiob::PB0: Vinp0, + crate::gpio::gpiob::PB13: Vinp1, + crate::gpio::gpioa::PA1: Vinp2, }, output: crate::gpio::gpiob::PB1, }, @@ -1027,13 +920,13 @@ opamps! { vinm0: crate::gpio::gpiob::PB10, vinm1: crate::gpio::gpiod::PD8, inverting: { - crate::gpio::gpiob::PB10: vinm0, - crate::gpio::gpiod::PD8: vinm1, + crate::gpio::gpiob::PB10: Vinm0, + crate::gpio::gpiod::PD8: Vinm1, }, non_inverting: { - crate::gpio::gpiob::PB13: vinp0, - crate::gpio::gpiod::PD11: vinp1, - crate::gpio::gpiob::PB11: vinp2, + crate::gpio::gpiob::PB13: Vinp0, + crate::gpio::gpiod::PD11: Vinp1, + crate::gpio::gpiob::PB11: Vinp2, }, output: crate::gpio::gpiob::PB12, }, @@ -1041,13 +934,13 @@ opamps! { vinm0: crate::gpio::gpiob::PB15, vinm1: crate::gpio::gpioa::PA3, inverting: { - crate::gpio::gpiob::PB15: vinm0, - crate::gpio::gpioa::PA3: vinm1, + crate::gpio::gpiob::PB15: Vinm0, + crate::gpio::gpioa::PA3: Vinm1, }, non_inverting: { - crate::gpio::gpiob::PB14: vinp0, - crate::gpio::gpiod::PD12: vinp1, - crate::gpio::gpioc::PC3: vinp2, + crate::gpio::gpiob::PB14: Vinp0, + crate::gpio::gpiod::PD12: Vinp1, + crate::gpio::gpioc::PC3: Vinp2, }, output: crate::gpio::gpioa::PA8, }, @@ -1055,13 +948,13 @@ opamps! { vinm0: crate::gpio::gpioa::PA1, vinm1: crate::gpio::gpiob::PB1, inverting: { - crate::gpio::gpioa::PA1: vinm0, - crate::gpio::gpiob::PB1: vinm1, + crate::gpio::gpioa::PA1: Vinm0, + crate::gpio::gpiob::PB1: Vinm1, }, non_inverting: { - crate::gpio::gpiob::PB12: vinp0, - crate::gpio::gpiod::PD9: vinp1, - crate::gpio::gpiob::PB13: vinp2, + crate::gpio::gpiob::PB12: Vinp0, + crate::gpio::gpiod::PD9: Vinp1, + crate::gpio::gpiob::PB13: Vinp2, }, output: crate::gpio::gpiob::PB11, }, From 97fd4e4a39324599223b1ebcdcaaeedc759bda25 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Mon, 23 Dec 2024 00:49:52 +0100 Subject: [PATCH 07/11] Add rough doc of Observable::observe --- src/observable.rs | 82 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 81 insertions(+), 1 deletion(-) diff --git a/src/observable.rs b/src/observable.rs index dce489c9..fd15c225 100644 --- a/src/observable.rs +++ b/src/observable.rs @@ -35,8 +35,88 @@ pub struct ObservationToken { /// A trait providing an interface to make peripherals observed /// -/// See [`Observed`] and [`ObservationToken`] +/// See [`Observable::observe`], [`Observed`] and [`ObservationToken`] pub trait Observable: Sized { + /// Observe this peripheral to split it into a [`Observed`] and a set of [`ObservationToken`]'s + /// + /// This is useful when you need the same peripherals for multiple things at the same time. + /// + /// For example let's say you want to keep track of the voltage of a pin. You want to log it + /// every second but if it rises above a threshold then you need to react really fast. + /// + /// This can be solved by connecting the pin to a comparator that compares the pins + /// voltage to a reference. If the voltage rises above the reference then the comparator + /// will quickly detect this and an interrupt can be generated or similar (not shown here). + /// + /// ``` + /// let dp = stm32::Peripherals::take().unwrap(); + /// let mut rcc = dp.RCC.constrain(); + /// + /// let gpioa = dp.GPIOA.split(&mut rcc); + /// + /// let (comp1, comp2, ..) = dp.COMP.split(&mut rcc); + /// + /// let pa1 = gpioa.pa1.into_analog(); // <- The pin to keep track of + /// let pa0 = gpioa.pa0.into_analog(); // <- Reference voltage + /// + /// // Pins consumed here + /// let comp1 = comp1.comparator(pa1, pa0, Config::default(), &rcc.clocks); + /// let comp1 = comp1.enable(); + /// + /// // Can not use pa0 and pa1 for AD readings + /// ``` + /// + /// However we still want to perform AD readings every second. Since the pins are consumed + /// by the comparator this is impossible. + /// + /// It turns ut that to construct the comparator we do not actually need a pin. We + /// just need proof that there is a pin that is setup in the correct mode and which + /// will stay in that mode as long as the comparator lives. + /// + /// This is where [`Observable::observe`] comes in. It splits the peripheral, in this case + /// a pin, into an [`Observed`] and a set of [`ObservationToken`]'s. The `Observed` + /// type can be used just like the peripheral would normally be used. For our pin we can + /// use it to perform AD readings etc. There is however one vital difference, we can not + /// reconfigure the observed peripheral. The `ObservationToken`'s on the other hand + /// are tokens that proove that the peripheral will not be reconfigured. These can then + /// be used instead of the peripheral to pass as arguments to other peripherals. + /// + /// ``` + /// let dp = stm32::Peripherals::take().unwrap(); + /// let mut rcc = dp.RCC.constrain(); + /// + /// let gpioa = dp.GPIOA.split(&mut rcc); + /// + /// let (comp1, comp2, ..) = dp.COMP.split(&mut rcc); + /// + /// let (pa1, [pa1_token]) = gpioa // <- The pin to keep track of + /// .pa1 + /// .into_analog() + /// .observe(); + /// let pa0 = gpioa.pa0.into_analog(); // <- Reference voltage + /// + /// // Only pa1_token and pa0 consumed here + /// let comp1 = comp1.comparator(pa1_token, pa0, Config::default(), &rcc.clocks); + /// let comp1 = comp1.enable(); + /// + /// let mut delay = cp.SYST.delay(&rcc.clocks); + /// let mut adc = dp.ADC2.claim_and_configure( + /// stm32g4xx_hal::adc::ClockSource::SystemClock, + /// &rcc, + /// stm32g4xx_hal::adc::config::AdcConfig::default(), + /// &mut delay, + /// false, + /// ); + /// + /// // Can not reconfigure pa1 here + /// + /// loop { + /// // Can still use pa1 here + /// let sample = adc.convert(&pa1, SampleTime::Cycles_640_5); + /// defmt::info!("Reading: {}", sample); + /// delay.delay(1000.millis()); + /// } + /// ``` fn observe(self) -> (Observed, [ObservationToken; N]) { ( Observed { peripheral: self }, From c9c569521b66316f98e9019b33b7fcc2be3bbe8a Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Mon, 23 Dec 2024 01:08:49 +0100 Subject: [PATCH 08/11] Update doc --- src/observable.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/observable.rs b/src/observable.rs index fd15c225..40067347 100644 --- a/src/observable.rs +++ b/src/observable.rs @@ -82,12 +82,13 @@ pub trait Observable: Sized { /// be used instead of the peripheral to pass as arguments to other peripherals. /// /// ``` + /// let cp = cortex_m::Peripherals::take().unwrap(); /// let dp = stm32::Peripherals::take().unwrap(); /// let mut rcc = dp.RCC.constrain(); /// /// let gpioa = dp.GPIOA.split(&mut rcc); /// - /// let (comp1, comp2, ..) = dp.COMP.split(&mut rcc); + /// let (comp1, ..) = dp.COMP.split(&mut rcc); /// /// let (pa1, [pa1_token]) = gpioa // <- The pin to keep track of /// .pa1 @@ -97,10 +98,10 @@ pub trait Observable: Sized { /// /// // Only pa1_token and pa0 consumed here /// let comp1 = comp1.comparator(pa1_token, pa0, Config::default(), &rcc.clocks); - /// let comp1 = comp1.enable(); + /// let _comp1 = comp1.enable(); // <-- TODO: Do things with comparator /// /// let mut delay = cp.SYST.delay(&rcc.clocks); - /// let mut adc = dp.ADC2.claim_and_configure( + /// let mut adc = dp.ADC1.claim_and_configure( /// stm32g4xx_hal::adc::ClockSource::SystemClock, /// &rcc, /// stm32g4xx_hal::adc::config::AdcConfig::default(), @@ -109,10 +110,9 @@ pub trait Observable: Sized { /// ); /// /// // Can not reconfigure pa1 here - /// /// loop { /// // Can still use pa1 here - /// let sample = adc.convert(&pa1, SampleTime::Cycles_640_5); + /// let sample = adc.convert(pa1.as_ref(), SampleTime::Cycles_640_5); /// defmt::info!("Reading: {}", sample); /// delay.delay(1000.millis()); /// } From 34aa3c6dd6f3e90b58957f3f9d36c91f20221cf9 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Mon, 23 Dec 2024 01:09:21 +0100 Subject: [PATCH 09/11] Add example of using pin.observe --- examples/observe.rs | 63 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 examples/observe.rs diff --git a/examples/observe.rs b/examples/observe.rs new file mode 100644 index 00000000..09227a96 --- /dev/null +++ b/examples/observe.rs @@ -0,0 +1,63 @@ +//! ## Origin +//! +//! This code has been taken from the stm32g0xx-hal project and modified slightly to support +//! STM32G4xx MCUs. + +//#![deny(warnings)] +#![deny(unsafe_code)] +#![no_main] +#![no_std] + +mod utils; +extern crate cortex_m_rt as rt; + +use fugit::ExtU32 as _; +use hal::{ + adc::AdcClaim as _, + comparator::{ComparatorExt, ComparatorSplit, Config}, + delay::SYSTDelayExt as _, + gpio::GpioExt, + observable::Observable as _, + rcc::RccExt, + stm32, +}; +use rt::entry; +use stm32g4xx_hal::{self as hal, adc::config::SampleTime, delay::DelayExt as _}; + +#[entry] +fn main() -> ! { + let cp = cortex_m::Peripherals::take().unwrap(); + let dp = stm32::Peripherals::take().unwrap(); + let mut rcc = dp.RCC.constrain(); + + let gpioa = dp.GPIOA.split(&mut rcc); + + let (comp1, ..) = dp.COMP.split(&mut rcc); + + let (pa1, [pa1_token]) = gpioa // <- The pin to keep track of + .pa1 + .into_analog() + .observe(); + let pa0 = gpioa.pa0.into_analog(); // <- Reference voltage + + // Only pa1_token and pa0 consumed here + let comp1 = comp1.comparator(pa1_token, pa0, Config::default(), &rcc.clocks); + let _comp1 = comp1.enable(); // <-- TODO: Do things with comparator + + let mut delay = cp.SYST.delay(&rcc.clocks); + let mut adc = dp.ADC1.claim_and_configure( + stm32g4xx_hal::adc::ClockSource::SystemClock, + &rcc, + stm32g4xx_hal::adc::config::AdcConfig::default(), + &mut delay, + false, + ); + + // Can not reconfigure pa1 here + loop { + // Can still use pa1 here + let sample = adc.convert(pa1.as_ref(), SampleTime::Cycles_640_5); + defmt::info!("Reading: {}", sample); + delay.delay(1000.millis()); + } +} From 595723c46e8019409cfddada20b056249e88eb6d Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Mon, 23 Dec 2024 01:13:26 +0100 Subject: [PATCH 10/11] Fix opamp for g431 etc. --- src/opamp.rs | 74 ++++++++++++++++++++++++++-------------------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/src/opamp.rs b/src/opamp.rs index d96e0201..63dea5bd 100644 --- a/src/opamp.rs +++ b/src/opamp.rs @@ -764,13 +764,13 @@ opamps! { vinm0: crate::gpio::gpioa::PA3, vinm1: crate::gpio::gpioc::PC5, inverting: { - crate::gpio::gpioa::PA3: vinm0, - crate::gpio::gpioc::PC5: vinm1, + crate::gpio::gpioa::PA3: Vinm0, + crate::gpio::gpioc::PC5: Vinm1, }, non_inverting: { - crate::gpio::gpioa::PA1: vinp0, - crate::gpio::gpioa::PA3: vinp1, - crate::gpio::gpioa::PA7: vinp2, + crate::gpio::gpioa::PA1: Vinp0, + crate::gpio::gpioa::PA3: Vinp1, + crate::gpio::gpioa::PA7: Vinp2, }, output: crate::gpio::gpioa::PA2, }, @@ -778,14 +778,14 @@ opamps! { vinm0: crate::gpio::gpioa::PA5, vinm1: crate::gpio::gpioc::PC5, inverting: { - crate::gpio::gpioa::PA5: vinm0, - crate::gpio::gpioc::PC5: vinm1, + crate::gpio::gpioa::PA5: Vinm0, + crate::gpio::gpioc::PC5: Vinm1, }, non_inverting: { - crate::gpio::gpioa::PA7: vinp0, - crate::gpio::gpiob::PB14: vinp1, - crate::gpio::gpiob::PB0: vinp2, - crate::gpio::gpiod::PD14: vinp3, + crate::gpio::gpioa::PA7: Vinp0, + crate::gpio::gpiob::PB14: Vinp1, + crate::gpio::gpiob::PB0: Vinp2, + crate::gpio::gpiod::PD14: Vinp3, }, output: crate::gpio::gpioa::PA6, }, @@ -793,13 +793,13 @@ opamps! { vinm0: crate::gpio::gpiob::PB2, vinm1: crate::gpio::gpiob::PB10, inverting: { - crate::gpio::gpiob::PB2: vinm0, - crate::gpio::gpiob::PB10: vinm1, + crate::gpio::gpiob::PB2: Vinm0, + crate::gpio::gpiob::PB10: Vinm1, }, non_inverting: { - crate::gpio::gpiob::PB0: vinp0, - crate::gpio::gpiob::PB13: vinp1, - crate::gpio::gpioa::PA1: vinp2, + crate::gpio::gpiob::PB0: Vinp0, + crate::gpio::gpiob::PB13: Vinp1, + crate::gpio::gpioa::PA1: Vinp2, }, output: crate::gpio::gpiob::PB1, }, @@ -811,13 +811,13 @@ opamps! { vinm0: crate::gpio::gpioa::PA3, vinm1: crate::gpio::gpioc::PC5, inverting: { - crate::gpio::gpioa::PA3: vinm0, - crate::gpio::gpioc::PC5: vinm1, + crate::gpio::gpioa::PA3: Vinm0, + crate::gpio::gpioc::PC5: Vinm1, }, non_inverting: { - crate::gpio::gpioa::PA1: vinp0, - crate::gpio::gpioa::PA3: vinp1, - crate::gpio::gpioa::PA7: vinp2, + crate::gpio::gpioa::PA1: Vinp0, + crate::gpio::gpioa::PA3: Vinp1, + crate::gpio::gpioa::PA7: Vinp2, }, output: crate::gpio::gpioa::PA2, }, @@ -825,14 +825,14 @@ opamps! { vinm0: crate::gpio::gpioa::PA5, vinm1: crate::gpio::gpioc::PC5, inverting: { - crate::gpio::gpioa::PA5: vinm0, - crate::gpio::gpioc::PC5: vinm1, + crate::gpio::gpioa::PA5: Vinm0, + crate::gpio::gpioc::PC5: Vinm1, }, non_inverting: { - crate::gpio::gpioa::PA7: vinp0, - crate::gpio::gpiob::PB14: vinp1, - crate::gpio::gpiob::PB0: vinp2, - crate::gpio::gpiod::PD14: vinp3, + crate::gpio::gpioa::PA7: Vinp0, + crate::gpio::gpiob::PB14: Vinp1, + crate::gpio::gpiob::PB0: Vinp2, + crate::gpio::gpiod::PD14: Vinp3, }, output: crate::gpio::gpioa::PA6, }, @@ -840,13 +840,13 @@ opamps! { vinm0: crate::gpio::gpiob::PB2, vinm1: crate::gpio::gpiob::PB10, inverting: { - crate::gpio::gpiob::PB2: vinm0, - crate::gpio::gpiob::PB10: vinm1, + crate::gpio::gpiob::PB2: Vinm0, + crate::gpio::gpiob::PB10: Vinm1, }, non_inverting: { - crate::gpio::gpiob::PB0: vinp0, - crate::gpio::gpiob::PB13: vinp1, - crate::gpio::gpioa::PA1: vinp2, + crate::gpio::gpiob::PB0: Vinp0, + crate::gpio::gpiob::PB13: Vinp1, + crate::gpio::gpioa::PA1: Vinp2, }, output: crate::gpio::gpiob::PB1, }, @@ -854,13 +854,13 @@ opamps! { vinm0: crate::gpio::gpioa::PA1, vinm1: crate::gpio::gpiob::PB1, inverting: { - crate::gpio::gpioa::PA1: vinm0, - crate::gpio::gpiob::PB1: vinm1, + crate::gpio::gpioa::PA1: Vinm0, + crate::gpio::gpiob::PB1: Vinm1, }, non_inverting: { - crate::gpio::gpiob::PB12: vinp0, - crate::gpio::gpiod::PD9: vinp1, - crate::gpio::gpiob::PB13: vinp2, + crate::gpio::gpiob::PB12: Vinp0, + crate::gpio::gpiod::PD9: Vinp1, + crate::gpio::gpiob::PB13: Vinp2, }, output: crate::gpio::gpiob::PB11, }, From 8fa58c94d7515a6c911e877b63589288d6c076f1 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Mon, 23 Dec 2024 01:15:59 +0100 Subject: [PATCH 11/11] Fix example --- examples/observe.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/observe.rs b/examples/observe.rs index 09227a96..4b184c6a 100644 --- a/examples/observe.rs +++ b/examples/observe.rs @@ -9,6 +9,7 @@ #![no_std] mod utils; +use utils::logger::info; extern crate cortex_m_rt as rt; use fugit::ExtU32 as _; @@ -57,7 +58,7 @@ fn main() -> ! { loop { // Can still use pa1 here let sample = adc.convert(pa1.as_ref(), SampleTime::Cycles_640_5); - defmt::info!("Reading: {}", sample); + info!("Reading: {}", sample); delay.delay(1000.millis()); } }