Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow to "freeze" gpio type state #138

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/comp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ fn main() -> ! {
.output_inverted(),
&rcc.clocks,
);
let led2 = gpioa.pa12.into_push_pull_output();
let led2 = gpioa.pa12.into_alternate();
// Configure PA12 to the comparator's alternate function so it gets
// changed directly by the comparator.
comp2.output_pin(led2);
Expand Down
4 changes: 2 additions & 2 deletions examples/comp_w_dac.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ 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 mut dac = dac1ch1.calibrate_buffer(&mut delay).enable().freeze();

let (comp1, _comp2, ..) = dp.COMP.split(&mut rcc);
let pa1 = gpioa.pa1.into_analog();
Expand All @@ -43,7 +43,7 @@ fn main() -> ! {
&rcc.clocks,
);

let led2 = gpioa.pa0.into_push_pull_output();
let led2 = gpioa.pa0.into_alternate();
// Configure PA12 to the comparator's alternate function so it gets
// changed directly by the comparator.
comp.output_pin(led2);
Expand Down
42 changes: 15 additions & 27 deletions examples/opamp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,35 +31,25 @@ fn main() -> ! {
let gpioa = dp.GPIOA.split(&mut rcc);
let gpiob = dp.GPIOB.split(&mut rcc);

// 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();

// Set up opamp1 and opamp2 in follower mode
let opamp1 = opamp1.follower(gpioa.pa1, gpioa.pa2);
let opamp2 = opamp2.follower(gpioa.pa7, InternalOutput);
let pb0 = gpiob.pb0.into_analog();
let pb1 = gpiob.pb1.into_analog();
let pb2 = gpiob.pb2.into_analog();

// Set up opamp1 and opamp2 in open loop mode
let opamp3 = opamp3.open_loop(gpiob.pb0, gpiob.pb2, gpiob.pb1);
// setup opamps
let (opamp1, opamp2, opamp3, ..) = dp.OPAMP.split(&mut rcc);

// disable opamps
let (opamp1, pa1, pa2) = opamp1.disable();
let (opamp2, pa7) = opamp2.disable();
// Configure opamp1 with pa1 as non-inverting input and set gain to x2
let _opamp1 = opamp1.pga(&pa1, pa2, Gain::Gain2);

let (_opamp3, _pb0, _pb2, _pb1) = opamp3.disable();
// Set up opamp2 in follower mode with output routed to internal ADC
let opamp2 = opamp2.follower(&pa7, InternalOutput);

// 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,
);

// 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,
);
// Set up opamp3 in open loop mode
let _opamp3 = opamp3.open_loop(&pb0, pb2, pb1);

// Lock opamp2. After the opamp is locked its registers cannot be written
// until the device is reset (even if using unsafe register accesses).
Expand All @@ -78,15 +68,13 @@ fn main() -> ! {
);

let millivolts = adc.sample_to_millivolts(sample);
info!("opamp2 thus 4x pa7: {}mV", millivolts);
info!("opamp2 thus pa7: {}mV", millivolts);

delay.delay_ms(100);
}

#[allow(unreachable_code)]
{
let (_opamp1, _pin) = _opamp1.disable();

loop {
delay.delay_ms(100);
}
Expand Down
140 changes: 93 additions & 47 deletions src/comparator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
//! This code has been taken from the stm32g0xx-hal project and modified slightly to support
//! STM32G4xx MCUs.

use core::borrow::Borrow;
use core::marker::PhantomData;

use crate::dac;
Expand All @@ -14,8 +15,9 @@ use crate::gpio::{
gpiob::{PB0, PB1, PB14, PB15, PB2, PB6, PB7, PB8, PB9},
gpioc::PC2,
gpiof::PF4,
Analog, OpenDrain, Output, PushPull, SignalEdge, AF2, AF3, AF8,
Alternate, AlternateOD, Analog, SignalEdge, AF2, AF3, AF8,
};
use crate::gpio::{Frozen, IsFrozen};

#[cfg(any(
feature = "stm32g473",
Expand Down Expand Up @@ -139,12 +141,17 @@ pub enum Hysteresis {
}

/// Comparator positive input
pub trait PositiveInput<C> {
fn setup(&self, comp: &C);
pub trait PositiveInput<C>: crate::Sealed {
/// Setup comaprator to use this as its positive input
///
/// # Safety
///
/// Internal use only
unsafe fn setup(&self, comp: &mut C);
}

/// Comparator negative input
pub trait NegativeInput<C> {
pub trait NegativeInput<C>: crate::Sealed {
/// Does this input use the internal reference Vrefint
///
/// This only true for RefintInput
Expand All @@ -155,19 +162,36 @@ pub trait NegativeInput<C> {
/// This is only relevant for `RefintInput` other than `RefintInput::VRefint`
fn use_resistor_divider(&self) -> bool;

fn setup(&self, comp: &C);
/// Setup comaprator to use this as its negative input
///
/// # Safety
///
/// Internal use only
unsafe fn setup(&self, comp: &mut C);
}

macro_rules! positive_input_pin {
($COMP:ident, $pin_0:ident, $pin_1:ident) => {
impl PositiveInput<$COMP> for &$pin_0<Analog> {
fn setup(&self, comp: &$COMP) {
impl PositiveInput<$COMP> for &$pin_0<Analog, IsFrozen> {
unsafe fn setup(&self, comp: &mut $COMP) {
comp.csr().modify(|_, w| w.inpsel().bit(false))
}
}

impl PositiveInput<$COMP> for $pin_0<Analog> {
unsafe fn setup(&self, comp: &mut $COMP) {
comp.csr().modify(|_, w| w.inpsel().bit(false))
}
}

impl PositiveInput<$COMP> for &$pin_1<Analog> {
fn setup(&self, comp: &$COMP) {
impl PositiveInput<$COMP> for &$pin_1<Analog, IsFrozen> {
unsafe fn setup(&self, comp: &mut $COMP) {
comp.csr().modify(|_, w| w.inpsel().bit(true))
}
}

impl PositiveInput<$COMP> for $pin_1<Analog> {
unsafe fn setup(&self, comp: &mut $COMP) {
comp.csr().modify(|_, w| w.inpsel().bit(true))
}
}
Expand Down Expand Up @@ -212,7 +236,7 @@ macro_rules! negative_input_pin_helper {
false
}

fn setup(&self, comp: &$COMP) {
unsafe fn setup(&self, comp: &mut $COMP) {
comp.csr().modify(|_, w| unsafe { w.inmsel().bits($bits) })
}
}
Expand Down Expand Up @@ -257,6 +281,9 @@ pub enum RefintInput {
VRefint = 0b011,
}

impl Frozen<RefintInput> for RefintInput {}
impl crate::Sealed for RefintInput {}

macro_rules! refint_input {
($($COMP:ident, )+) => {$(
impl NegativeInput<$COMP> for RefintInput {
Expand All @@ -266,7 +293,7 @@ macro_rules! refint_input {
*self != RefintInput::VRefint
}

fn setup(&self, comp: &$COMP) {
unsafe fn setup(&self, comp: &mut $COMP) {
comp.csr()
.modify(|_, w| unsafe { w.inmsel().bits(*self as u8) })
}
Expand All @@ -285,15 +312,27 @@ refint_input!(COMP1, COMP2, COMP3, COMP4,);
refint_input!(COMP5, COMP6, COMP7,);

macro_rules! dac_input_helper {
($COMP:ident: $channel:ident, $MODE:ident, $bits:expr) => {
impl<ED> NegativeInput<$COMP> for &dac::$channel<{ dac::$MODE }, ED> {
($COMP:ident: $channel:ident, $MODE:ident, $ED:ty, $bits:expr) => {
impl NegativeInput<$COMP> for &dac::$channel<{ dac::$MODE }, $ED, IsFrozen> {
const USE_VREFINT: bool = false;

fn use_resistor_divider(&self) -> bool {
false
}

unsafe fn setup(&self, comp: &mut $COMP) {
comp.csr().modify(|_, w| unsafe { w.inmsel().bits($bits) })
}
}

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) {
unsafe fn setup(&self, comp: &mut $COMP) {
comp.csr().modify(|_, w| unsafe { w.inmsel().bits($bits) })
}
}
Expand All @@ -302,8 +341,10 @@ macro_rules! dac_input_helper {

macro_rules! dac_input {
($COMP:ident: $channel:ident, $bits:expr) => {
dac_input_helper!($COMP: $channel, M_MIX_SIG, $bits);
dac_input_helper!($COMP: $channel, M_INT_SIG, $bits);
dac_input_helper!($COMP: $channel, M_MIX_SIG, dac::Enabled, $bits);
dac_input_helper!($COMP: $channel, M_MIX_SIG, dac::WaveGenerator, $bits);
dac_input_helper!($COMP: $channel, M_INT_SIG, dac::Enabled, $bits);
dac_input_helper!($COMP: $channel, M_INT_SIG, dac::WaveGenerator, $bits);
};
}

Expand Down Expand Up @@ -371,37 +412,46 @@ pub struct Comparator<C, ED> {

pub trait ComparatorExt<COMP> {
/// Initializes a comparator
fn comparator<P: PositiveInput<COMP>, N: NegativeInput<COMP>>(
fn comparator<PP, NP, P, N>(
self,
positive_input: P,
negative_input: N,
positive_input: PP,
negative_input: NP,
config: Config,
clocks: &Clocks,
) -> Comparator<COMP, Disabled>;
) -> Comparator<COMP, Disabled>
where
PP: Frozen<P> + PositiveInput<COMP>,
NP: Frozen<N> + NegativeInput<COMP>;
}

macro_rules! impl_comparator {
($COMP:ty, $comp:ident, $Event:expr) => {
impl ComparatorExt<$COMP> for $COMP {
fn comparator<P: PositiveInput<$COMP>, N: NegativeInput<$COMP>>(
self,
positive_input: P,
negative_input: N,
fn comparator<PP, NP, P, N>(
mut self,
positive_input: PP,
negative_input: NP,
config: Config,
clocks: &Clocks,
) -> Comparator<$COMP, Disabled> {
positive_input.setup(&self);
negative_input.setup(&self);
) -> Comparator<$COMP, Disabled>
where
PP: Frozen<P> + PositiveInput<$COMP>,
NP: Frozen<N> + NegativeInput<$COMP>,
{
unsafe {
positive_input.borrow().setup(&mut self);
negative_input.borrow().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);
self.csr().modify(|_, w| unsafe {
w.hyst()
.bits(config.hysteresis as u8)
.scalen()
.bit(N::USE_VREFINT)
.bit(NP::USE_VREFINT)
.brgen()
.bit(negative_input.use_resistor_divider())
.bit(negative_input.borrow().use_resistor_divider())
.pol()
.bit(config.inverted)
});
Expand All @@ -415,13 +465,17 @@ macro_rules! impl_comparator {

impl Comparator<$COMP, Disabled> {
/// Initializes a comparator
pub fn $comp<P: PositiveInput<$COMP>, N: NegativeInput<$COMP>>(
pub fn $comp<PP, NP, P, N>(
comp: $COMP,
positive_input: P,
negative_input: N,
positive_input: PP,
negative_input: NP,
config: Config,
clocks: &Clocks,
) -> Self {
) -> Self
where
PP: Frozen<P> + PositiveInput<$COMP>,
NP: Frozen<N> + NegativeInput<$COMP>,
{
comp.comparator(positive_input, negative_input, config, clocks)
}

Expand Down Expand Up @@ -486,9 +540,7 @@ macro_rules! impl_comparator {
/// Configures a GPIO pin to output the signal of the comparator
///
/// Multiple GPIO pins may be configured as the output simultaneously.
pub fn output_pin<P: OutputPin<$COMP>>(&self, pin: P) {
pin.setup();
}
pub fn output_pin<P: OutputPin<$COMP>>(&self, _pin: P) {}
}
};
}
Expand Down Expand Up @@ -587,22 +639,16 @@ impl ComparatorSplit for COMP {
}
}

pub trait OutputPin<COMP> {
fn setup(self);
}
pub trait OutputPin<COMP> {}

#[allow(unused_macros)] // TODO: add support for more devices
macro_rules! output_pin {
($COMP:ident, $pin:ident, $AF:ident, $mode_t:ident, $into:ident) => {
impl OutputPin<$COMP> for $pin<Output<$mode_t>> {
fn setup(self) {
self.$into::<$AF>();
}
}
($COMP:ident, $pin:ident, $AF:ident, $mode_t:ident) => {
impl OutputPin<$COMP> for $pin<$mode_t<$AF>> {}
};
($($COMP:ident: $pin:ident, $AF:ident,)+) => {$(
output_pin!($COMP, $pin, $AF, PushPull, into_alternate);
output_pin!($COMP, $pin, $AF, OpenDrain, into_alternate_open_drain);
output_pin!($COMP, $pin, $AF, Alternate);
output_pin!($COMP, $pin, $AF, AlternateOD);
)+};
}

Expand Down
Loading
Loading