Skip to content

Commit d7451ab

Browse files
committed
opamp in pga-mode seems to work
1 parent 4b46238 commit d7451ab

File tree

3 files changed

+184
-80
lines changed

3 files changed

+184
-80
lines changed

examples/opamp.rs

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
//! Integrated opamps.
2+
3+
#![no_std]
4+
#![no_main]
5+
6+
use stm32g4xx_hal::adc::AdcClaim;
7+
use stm32g4xx_hal::adc::ClockSource;
8+
use stm32g4xx_hal::gpio::gpioa::*;
9+
use stm32g4xx_hal::gpio::gpiob::*;
10+
use stm32g4xx_hal::gpio::Analog;
11+
use stm32g4xx_hal::opamp::opamp1::IntoPga as _;
12+
use stm32g4xx_hal::opamp::opamp2::IntoPga as _;
13+
use stm32g4xx_hal::opamp::NonInvertingGain;
14+
use stm32g4xx_hal::opamp::PgaModeInternal;
15+
use stm32g4xx_hal::prelude::*;
16+
17+
use utils::logger::info;
18+
19+
#[macro_use]
20+
mod utils;
21+
22+
#[cortex_m_rt::entry]
23+
fn main() -> ! {
24+
utils::logger::init();
25+
26+
// take peripherals
27+
let dp = stm32g4xx_hal::stm32::Peripherals::take().unwrap();
28+
let cp = cortex_m::Peripherals::take().expect("cannot take core peripherals");
29+
30+
// setup clock
31+
let config = stm32g4xx_hal::rcc::Config::hsi();
32+
let mut rcc = dp.RCC.freeze(config);
33+
34+
// split gpio
35+
let gpioa = dp.GPIOA.split(&mut rcc);
36+
let gpiob = dp.GPIOB.split(&mut rcc);
37+
38+
// setup opamps
39+
let (opamp1, opamp2, opamp3, opamp4, _opamp5, _opamp6) = dp.OPAMP.split(&mut rcc);
40+
41+
let opamp1 = opamp1.follower(gpioa.pa1, Some(gpioa.pa2));
42+
let opamp2 = opamp2.follower(gpioa.pa7, Option::<PA6<Analog>>::None);
43+
44+
let opamp3 = opamp3.open_loop(gpiob.pb0, gpiob.pb2, Some(gpiob.pb1));
45+
let opamp4 = opamp4.open_loop(gpiob.pb11, gpiob.pb10, Option::<PB12<Analog>>::None);
46+
47+
// disable opamps
48+
let (opamp1, pa1, some_pa2) = opamp1.disable();
49+
let (opamp2, pa7, _none) = opamp2.disable();
50+
51+
let (_opamp3, _pb0, _pb2, _some_pb1) = opamp3.disable();
52+
let (_opamp4, _pb11, _pb10, _none) = opamp4.disable();
53+
54+
/*let _opamp1 = opamp1.pga(
55+
pa1,
56+
PgaModeInternal::gain(NonInvertingGain::Gain2),
57+
some_pa2,
58+
);*/
59+
let mut opamp2 = opamp2.pga(
60+
pa7,
61+
PgaModeInternal::gain(NonInvertingGain::Gain4),
62+
//Some(gpioa.pa6),
63+
Option::<PA6<Analog>>::None,
64+
);
65+
66+
let mut delay = cp.SYST.delay(&rcc.clocks);
67+
let mut adc = dp
68+
.ADC2
69+
.claim(ClockSource::SystemClock, &rcc, &mut delay, true);
70+
71+
loop {
72+
let sample = adc.convert(&mut opamp2, stm32g4xx_hal::adc::config::SampleTime::Cycles_640_5);
73+
74+
delay.delay_ms(100);
75+
76+
let millivolts = adc.sample_to_millivolts(sample);
77+
info!("opamp2 thus 2x pa7: {}mV", millivolts);
78+
}
79+
80+
#[allow(unreachable_code)]
81+
{
82+
//let (_opamp1, _pa1, _mode, _some_pa2) = _opamp1.disable();
83+
let (_opamp2, _pa7, _mode, _none) = opamp2.disable();
84+
85+
loop {}
86+
}
87+
}

src/adc.rs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2359,20 +2359,19 @@ adc_pins!(
23592359
adc_op!(
23602360
// TODO: Add all opamp types: Follower, OpenLoop
23612361
// TODO: Should we restrict type parameters A and B?
2362+
// TODO: Also allow AD-channels shared by pins
23622363

2363-
opamp::opamp1::Pga<A, B> => (ADC1, 3),
2364+
opamp::opamp1::Pga<A, B> => (ADC1, 13),
23642365

2365-
opamp::opamp2::Pga<A, B> => (ADC2, 3),
2366+
opamp::opamp2::Pga<A, B> => (ADC2, 16),
23662367

2367-
opamp::opamp3::Pga<A, B> => (ADC1, 12),
2368+
opamp::opamp3::Pga<A, B> => (ADC2, 18),
23682369

2369-
opamp::opamp4::Pga<A, B> => (ADC4, 3),
2370-
opamp::opamp4::Pga<A, B> => (ADC1, 11),
2370+
opamp::opamp4::Pga<A, B> => (ADC5, 5),
23712371

2372-
opamp::opamp5::Pga<A, B> => (ADC5, 1),
2372+
opamp::opamp5::Pga<A, B> => (ADC5, 3),
23732373

2374-
opamp::opamp6::Pga<A, B> => (ADC1, 14),
2375-
opamp::opamp6::Pga<A, B> => (ADC2, 14),
2374+
opamp::opamp6::Pga<A, B> => (ADC4, 17),
23762375
);
23772376

23782377
#[cfg(any(feature = "stm32g491", feature = "stm32g4a1",))]

src/opamp.rs

Lines changed: 90 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -35,19 +35,66 @@
3535
//! let opamp4 = opamp4.open_loop(gpiob.pb11, gpiob.pb10, Option::<PB12<Analog>>::None);
3636
//!
3737
//! // disable opamps
38-
//! let (_opamp1, _pa1, _some_pa2) = opamp1.disable();
39-
//! let (_opamp2, _pa7, _none) = opamp2.disable();
38+
//! let (opamp1, pa1, some_pa2) = opamp1.disable();
39+
//! let (opamp2, pa7, _none) = opamp2.disable();
4040
//!
4141
//! let (_opamp3, _pb0, _pb2, _some_pb1) = opamp3.disable();
4242
//! let (_opamp4, _pb11, _pb10, _none) = opamp4.disable();
4343
//!
44+
//! let opamp1 = opamp1.pga(pa1, some_pa2);
45+
//! let opamp2 = opamp2.pga(pa7, Option::<PA6<Analog>>::None);
46+
//!
47+
//! let (_opamp1, _pa1, _some_pa2) = opamp1.disable();
48+
//! let (_opamp2, _pa7, _none) = opamp2.disable();
49+
//!
4450
//! loop {}
4551
//! }
4652
//! ```
4753
4854
// TODO: Add support for locking using the `LOCK` bit in `OPAMPx_CSR`
4955
// TODO: Add support for calibration
50-
// TODO: Add support for PGA mode
56+
// TODO: The output can not be a Option<PIN> if we want to handle "route to pin vs adc"
57+
// in a compile time way. See OPAINTOEN in OPAMPx_CSR
58+
59+
/// Pga mode internal
60+
///
61+
/// This mode does not expose the inverting signal on any pin,
62+
/// only connecting it to the programmable gain divider
63+
pub struct PgaModeInternal(NonInvertingGain);
64+
impl PgaModeInternal {
65+
/// Create new instance with the given gain setting
66+
pub fn gain(gain: NonInvertingGain) -> Self {
67+
PgaModeInternal(gain)
68+
}
69+
}
70+
71+
/// Same as PgaModeInternal but the inverted signal is routed to pin
72+
/// to allow external filter
73+
pub struct PgaModeInvertedInputFiltered<PIN> {
74+
gain: NonInvertingGain,
75+
pin: core::marker::PhantomData<PIN>,
76+
}
77+
78+
/// PGA Gain for non inverted modes
79+
pub enum NonInvertingGain {
80+
/// 2x Gain
81+
Gain2 = 0,
82+
83+
/// 4x Gain
84+
Gain4 = 1,
85+
86+
/// 8x Gain
87+
Gain8 = 2,
88+
89+
/// 16x Gain
90+
Gain16 = 3,
91+
92+
/// 32x Gain
93+
Gain32 = 4,
94+
95+
/// 64x Gain
96+
Gain64 = 5,
97+
}
5198

5299
macro_rules! opamps {
53100
{
@@ -95,10 +142,38 @@ macro_rules! opamps {
95142
}
96143

97144
$(
145+
impl From<&PgaModeInternal> for crate::stm32::opamp::[<$opamp _csr>]::PGA_GAIN_A {
146+
fn from(x: &PgaModeInternal) -> crate::stm32::opamp::[<$opamp _csr>]::PGA_GAIN_A {
147+
use crate::stm32::opamp::[<$opamp _csr>]::PGA_GAIN_A;
148+
149+
match x.0 {
150+
NonInvertingGain::Gain2 => PGA_GAIN_A::Gain2,
151+
NonInvertingGain::Gain4 => PGA_GAIN_A::Gain4,
152+
NonInvertingGain::Gain8 => PGA_GAIN_A::Gain8,
153+
NonInvertingGain::Gain16 => PGA_GAIN_A::Gain16,
154+
NonInvertingGain::Gain32 => PGA_GAIN_A::Gain32,
155+
NonInvertingGain::Gain64 => PGA_GAIN_A::Gain64,
156+
}
157+
}
158+
}
159+
160+
impl<PIN> From<&PgaModeInvertedInputFiltered<PIN>> for crate::stm32::opamp::[<$opamp _csr>]::PGA_GAIN_A {
161+
fn from(x: &PgaModeInvertedInputFiltered<PIN>) -> crate::stm32::opamp::[<$opamp _csr>]::PGA_GAIN_A {
162+
use crate::stm32::opamp::[<$opamp _csr>]::PGA_GAIN_A;
163+
164+
match x.gain {
165+
NonInvertingGain::Gain2 => PGA_GAIN_A::Gain2FilteringVinm0,
166+
NonInvertingGain::Gain4 => PGA_GAIN_A::Gain4FilteringVinm0,
167+
NonInvertingGain::Gain8 => PGA_GAIN_A::Gain8FilteringVinm0,
168+
NonInvertingGain::Gain16 => PGA_GAIN_A::Gain16FilteringVinm0,
169+
NonInvertingGain::Gain32 => PGA_GAIN_A::Gain32FilteringVinm0,
170+
NonInvertingGain::Gain64 => PGA_GAIN_A::Gain64FilteringVinm0,
171+
}
172+
}
173+
}
174+
98175
/// States for opampX.
99176
pub mod $opamp {
100-
use crate::stm32::opamp::[<$opamp _csr>]::PGA_GAIN_A;
101-
102177
#[allow(unused_imports)]
103178
use crate::gpio::gpioa::*;
104179

@@ -121,70 +196,6 @@ macro_rules! opamps {
121196
output: Option<$output>,
122197
}
123198

124-
/// State type for opamp running in programmable-gain mode.
125-
pub struct Pga<NonInverting, MODE> {
126-
non_inverting: NonInverting,
127-
config: MODE,
128-
output: Option<$output>,
129-
}
130-
131-
trait PgaMode{
132-
fn pga_gain_bits(&self) -> PGA_GAIN_A;
133-
}
134-
135-
struct PgaModeInternal(NonInvertingGain);
136-
impl PgaMode for PgaModeInternal {
137-
fn pga_gain_bits(&self) -> PGA_GAIN_A {
138-
match self.0 {
139-
NonInvertingGain::Gain2 => PGA_GAIN_A::Gain2,
140-
NonInvertingGain::Gain4 => PGA_GAIN_A::Gain4,
141-
NonInvertingGain::Gain8 => PGA_GAIN_A::Gain8,
142-
NonInvertingGain::Gain16 => PGA_GAIN_A::Gain16,
143-
NonInvertingGain::Gain32 => PGA_GAIN_A::Gain32,
144-
NonInvertingGain::Gain64 => PGA_GAIN_A::Gain64,
145-
}
146-
}
147-
}
148-
149-
/// Same as PgaModeInternal but the inverted signal is routed to pin to allow external filter
150-
struct PgaModeInvertedInputFiltered<PIN> {
151-
gain: NonInvertingGain,
152-
pin: core::marker::PhantomData<PIN>
153-
}
154-
impl<PIN> PgaMode for PgaModeInvertedInputFiltered<PIN> {
155-
fn pga_gain_bits(&self) -> PGA_GAIN_A {
156-
match self.gain {
157-
NonInvertingGain::Gain2 => PGA_GAIN_A::Gain2FilteringVinm0,
158-
NonInvertingGain::Gain4 => PGA_GAIN_A::Gain4FilteringVinm0,
159-
NonInvertingGain::Gain8 => PGA_GAIN_A::Gain8FilteringVinm0,
160-
NonInvertingGain::Gain16 => PGA_GAIN_A::Gain16FilteringVinm0,
161-
NonInvertingGain::Gain32 => PGA_GAIN_A::Gain32FilteringVinm0,
162-
NonInvertingGain::Gain64 => PGA_GAIN_A::Gain64FilteringVinm0,
163-
}
164-
}
165-
}
166-
167-
/// PGA Gain for non inverted modes
168-
pub enum NonInvertingGain {
169-
/// 2x Gain
170-
Gain2 = 0,
171-
172-
/// 4x Gain
173-
Gain4 = 1,
174-
175-
/// 8x Gain
176-
Gain8 = 2,
177-
178-
/// 16x Gain
179-
Gain16 = 3,
180-
181-
/// 32x Gain
182-
Gain32 = 4,
183-
184-
/// 64x Gain
185-
Gain64 = 5
186-
}
187-
188199
// TODO: Inverting gain
189200

190201
/// Trait for opamps that can be run in follower mode.
@@ -206,6 +217,13 @@ macro_rules! opamps {
206217
-> OpenLoop<NonInverting, Inverting>;
207218
}
208219

220+
/// State type for opamp running in programmable-gain mode.
221+
pub struct Pga<NonInverting, MODE> {
222+
non_inverting: NonInverting,
223+
config: MODE,
224+
output: Option<$output>,
225+
}
226+
209227
/// Trait for opamps that can be run in programmable gain mode.
210228
pub trait IntoPga <IntoNonInverting, MODE, IntoOutput, NonInverting>
211229
where
@@ -466,8 +484,8 @@ macro_rules! opamps {
466484
$vinm0:ident
467485
} => {
468486
$(
469-
opamps!{ @pga $opamp, $output, $non_inverting_mask, $non_inverting, PgaModeInternal }
470-
opamps!{ @pga $opamp, $output, $non_inverting_mask, $non_inverting, PgaModeInvertedInputFiltered<$vinm0<crate::gpio::Analog>> }
487+
opamps!{ @pga $opamp, $output, $non_inverting_mask, $non_inverting, crate::opamp::PgaModeInternal }
488+
opamps!{ @pga $opamp, $output, $non_inverting_mask, $non_inverting, crate::opamp::PgaModeInvertedInputFiltered<$vinm0<crate::gpio::Analog>> }
471489
// TODO: Add `PGA mode, non-inverting gain setting (x2/x4/x8/x16/x32/x64) or inverting gain setting (x-1/x-3/x-7/x-15/x-31/x-63)`
472490
// TODO: Add `PGA mode, non-inverting gain setting (x2/x4/x8/x16/x32/x64) or inverting gain setting (x-1/x-3/x-7/x-15/x-31/x-63) with filtering`
473491
)*
@@ -511,7 +529,7 @@ macro_rules! opamps {
511529
.vm_sel()
512530
.pga()
513531
.pga_gain()
514-
.variant(config.pga_gain_bits())
532+
.variant((&config).into())
515533
.opaintoen()
516534
.variant(match output {
517535
Some(_) => OPAINTOEN_A::OutputPin,

0 commit comments

Comments
 (0)