Skip to content

Commit a34ae60

Browse files
usbalbinAdinAck
andauthored
Implement Observable POC (#157)
* Add stasis copy proto-hal/src/stasis.rs from https://github.com/AdinAck/proto-hal/tree/main Add `Ad` wrapper around ADC-type in Channel and OneShot impl to avoid problems with orphan rule Co-authored-by: Adin Ackerman <[email protected]> * Add example of using pin.observe * observe opamp * observe opamp - update example * Fix some warnings * Setup negative input * clippy * Add tests for DAC, OPAMP, COMP etc * Add test with external opamp pin * cleanup * cleanup --------- Co-authored-by: Adin Ackerman <[email protected]>
1 parent c9c85a4 commit a34ae60

File tree

14 files changed

+870
-445
lines changed

14 files changed

+870
-445
lines changed

Cargo.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,11 @@ name = "cordic"
140140
required-features = ["cordic"]
141141

142142
[[test]]
143-
name = "tests"
143+
name = "nucleo-g474"
144+
harness = false
145+
146+
[[test]]
147+
name = "nucleo-g474_w_jumpers"
144148
harness = false
145149

146150
[lib]

examples/comp.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use rt::entry;
1515

1616
#[entry]
1717
fn main() -> ! {
18-
use hal::comparator::{ComparatorExt, ComparatorSplit, Config, Hysteresis, RefintInput};
18+
use hal::comparator::{refint_input, ComparatorExt, ComparatorSplit, Config, Hysteresis};
1919
use hal::gpio::GpioExt;
2020
use hal::prelude::OutputPin;
2121
use hal::rcc::RccExt;
@@ -31,16 +31,16 @@ fn main() -> ! {
3131

3232
let pa1 = gpioa.pa1.into_analog();
3333
let pa0 = gpioa.pa0.into_analog();
34-
let comp1 = comp1.comparator(&pa1, pa0, Config::default(), &rcc.clocks);
34+
let comp1 = comp1.comparator(pa1, pa0, Config::default(), &rcc.clocks);
3535
let comp1 = comp1.enable();
3636

3737
// led1 pa1 will be updated manually when to match comp1 value
3838
let mut led1 = gpioa.pa5.into_push_pull_output();
3939

4040
let pa7 = gpioa.pa7.into_analog();
4141
let comp2 = comp2.comparator(
42-
&pa7,
43-
RefintInput::VRefintM12,
42+
pa7,
43+
refint_input::VRefintM12,
4444
Config::default()
4545
.hysteresis(Hysteresis::None)
4646
.output_inverted(),

examples/comp_w_dac.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ fn main() -> ! {
1515
use hal::delay::SYSTDelayExt;
1616
use hal::gpio::GpioExt;
1717
use hal::rcc::RccExt;
18+
use hal::stasis::Freeze;
1819
use hal::stm32;
1920
use stm32g4xx_hal as hal;
2021

@@ -29,15 +30,16 @@ fn main() -> ! {
2930
// Set up DAC to output to pa4 and to internal signal Dac1IntSig1
3031
// which just so happens is compatible with comp1
3132
let dac1ch1 = dp.DAC1.constrain((gpioa.pa4, Dac1IntSig1), &mut rcc);
32-
let mut dac = dac1ch1.calibrate_buffer(&mut delay).enable(&mut rcc);
33+
let dac = dac1ch1.calibrate_buffer(&mut delay).enable(&mut rcc);
34+
let (mut dac, [dac_token]) = dac.freeze();
3335

3436
let (comp1, _comp2, ..) = dp.COMP.split(&mut rcc);
3537
let pa1 = gpioa.pa1.into_analog();
3638

3739
// Set up comparator with pa1 as positive, and the DAC as negative input
3840
let comp = comp1.comparator(
39-
&pa1,
40-
&dac,
41+
pa1,
42+
dac_token,
4143
comparator::Config::default().hysteresis(comparator::Hysteresis::None),
4244
&rcc.clocks,
4345
);

examples/observe.rs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
//#![deny(warnings)]
2+
#![deny(unsafe_code)]
3+
#![no_main]
4+
#![no_std]
5+
6+
mod utils;
7+
use utils::logger::info;
8+
extern crate cortex_m_rt as rt;
9+
10+
use fugit::ExtU32 as _;
11+
use hal::{
12+
adc::AdcClaim as _,
13+
comparator::{ComparatorExt, ComparatorSplit, Config},
14+
delay::SYSTDelayExt as _,
15+
gpio::GpioExt,
16+
rcc::RccExt,
17+
stm32,
18+
};
19+
use rt::entry;
20+
use stm32g4xx_hal::{self as hal, adc::config::SampleTime, delay::DelayExt as _, stasis::Freeze};
21+
22+
#[entry]
23+
fn main() -> ! {
24+
let cp = cortex_m::Peripherals::take().unwrap();
25+
let dp = stm32::Peripherals::take().unwrap();
26+
let mut rcc = dp.RCC.constrain();
27+
28+
let gpioa = dp.GPIOA.split(&mut rcc);
29+
30+
let (comp1, ..) = dp.COMP.split(&mut rcc);
31+
32+
let (pa1, [pa1_token]) = gpioa // <- The pin to keep track of
33+
.pa1
34+
.into_analog()
35+
.freeze();
36+
let pa0 = gpioa.pa0.into_analog(); // <- Reference voltage
37+
38+
// Only pa1_token and pa0 consumed here
39+
let comp1 = comp1.comparator(pa1_token, pa0, Config::default(), &rcc.clocks);
40+
let _comp1 = comp1.enable(); // <-- TODO: Do things with comparator
41+
42+
let mut delay = cp.SYST.delay(&rcc.clocks);
43+
let mut adc = dp.ADC1.claim_and_configure(
44+
stm32g4xx_hal::adc::ClockSource::SystemClock,
45+
&rcc,
46+
stm32g4xx_hal::adc::config::AdcConfig::default(),
47+
&mut delay,
48+
false,
49+
);
50+
51+
// Can not reconfigure pa1 here
52+
loop {
53+
// Can still use pa1 here
54+
let sample = adc.convert(&pa1, SampleTime::Cycles_640_5);
55+
info!("Reading: {}", sample);
56+
delay.delay(1000.millis());
57+
}
58+
}

examples/opamp.rs

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
use stm32g4xx_hal::adc::AdcClaim;
77
use stm32g4xx_hal::adc::ClockSource;
8-
use stm32g4xx_hal::opamp::{Gain, InternalOutput};
8+
use stm32g4xx_hal::opamp::Gain;
99
use stm32g4xx_hal::prelude::*;
1010
use stm32g4xx_hal::pwr::PwrExt;
1111

@@ -34,12 +34,20 @@ fn main() -> ! {
3434
// setup opamps
3535
let (opamp1, opamp2, opamp3, ..) = dp.OPAMP.split(&mut rcc);
3636

37+
let pa1 = gpioa.pa1.into_analog();
38+
let pa2 = gpioa.pa2.into_analog();
39+
let pa7 = gpioa.pa7.into_analog();
40+
41+
let pb0 = gpiob.pb0.into_analog();
42+
let pb1 = gpiob.pb1.into_analog();
43+
let pb2 = gpiob.pb2.into_analog();
44+
3745
// Set up opamp1 and opamp2 in follower mode
38-
let opamp1 = opamp1.follower(gpioa.pa1, gpioa.pa2);
39-
let opamp2 = opamp2.follower(gpioa.pa7, InternalOutput);
46+
let opamp1 = opamp1.follower(pa1).enable_output(pa2);
47+
let opamp2 = opamp2.follower(pa7);
4048

4149
// Set up opamp1 and opamp2 in open loop mode
42-
let opamp3 = opamp3.open_loop(gpiob.pb0, gpiob.pb2, gpiob.pb1);
50+
let opamp3 = opamp3.open_loop(pb0, pb2).enable_output(pb1);
4351

4452
// disable opamps
4553
let (opamp1, pa1, pa2) = opamp1.disable();
@@ -48,18 +56,10 @@ fn main() -> ! {
4856
let (_opamp3, _pb0, _pb2, _pb1) = opamp3.disable();
4957

5058
// Configure opamp1 with pa1 as non-inverting input and set gain to x2
51-
let _opamp1 = opamp1.pga(
52-
pa1,
53-
pa2, // Route output to pin pa2
54-
Gain::Gain2,
55-
);
59+
let _opamp1 = opamp1.pga(pa1, Gain::Gain2).enable_output(pa2); // Route output to pin pa2
5660

5761
// Configure op with pa7 as non-inverting input and set gain to x4
58-
let opamp2 = opamp2.pga(
59-
pa7,
60-
InternalOutput, // Do not route output to any external pin, use internal AD instead
61-
Gain::Gain4,
62-
);
62+
let opamp2 = opamp2.pga(pa7, Gain::Gain4); // Do not route output to any external pin, use internal AD instead
6363

6464
// Lock opamp2. After the opamp is locked its registers cannot be written
6565
// until the device is reset (even if using unsafe register accesses).

src/adc.rs

Lines changed: 45 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
*/
1010

1111
//use crate::dma::traits::PeriAddress;
12+
use crate::stasis;
1213
pub use crate::time::U32Ext as _;
1314
use crate::{
1415
dma::{mux::DmaMuxResources, traits::TargetAddress, PeripheralToMemory},
@@ -51,12 +52,16 @@ impl Vref {
5152
Self::sample_to_millivolts_ext(sample, VDDA_CALIB, config::Resolution::Twelve)
5253
}
5354
}
55+
impl stasis::Freeze for Vref {}
5456

5557
/// Vbat internal signal, used for monitoring the battery (if used)
5658
pub struct Vbat;
59+
impl stasis::Freeze for Vbat {}
5760

5861
/// Core temperature internal signal
5962
pub struct Temperature;
63+
impl stasis::Freeze for Temperature {}
64+
6065
impl Temperature {
6166
/// Precompute the inverse of `VTEMP_CAL_VREFANALOG`, in volts,
6267
/// for floating point calculations
@@ -131,39 +136,49 @@ impl Temperature {
131136
}
132137
}
133138

139+
// TODO: Is there any way to avoid this wrapper to overcome the orphan rule
140+
/// Wrapper to side step orphan rule
141+
pub struct Ad<T>(T);
142+
macro_rules! adc_channel_helper {
143+
($adc:ident, $chan:expr, $r:ty, $($a:ident),*) => {
144+
impl<$($a,)*> Channel<Ad<stm32::$adc>> for stasis::Entitlement<$r> {
145+
type ID = u8;
146+
fn channel() -> u8 {
147+
$chan
148+
}
149+
}
150+
151+
impl<const N: usize, $($a,)*> Channel<Ad<stm32::$adc>> for stasis::Frozen<$r, N> {
152+
type ID = u8;
153+
fn channel() -> u8 {
154+
$chan
155+
}
156+
}
157+
158+
impl<$($a,)*> Channel<Ad<stm32::$adc>> for $r {
159+
type ID = u8;
160+
fn channel() -> u8 {
161+
$chan
162+
}
163+
}
164+
};
165+
}
166+
134167
macro_rules! adc_pins {
135168
($($pin:ty => ($adc:ident, $chan:expr)),+ $(,)*) => {
136169
$(
137-
impl Channel<stm32::$adc> for $pin {
138-
type ID = u8;
139-
fn channel() -> u8 { $chan }
140-
}
170+
adc_channel_helper!($adc, $chan, $pin,);
141171
)+
142172
};
143173
}
144174

145175
macro_rules! adc_opamp {
146176
($($opamp:ty => ($adc:ident, $chan:expr)),+ $(,)*) => {
147177
$(
148-
impl<A> Channel<stm32::$adc> for opamp::Follower<$opamp, A, InternalOutput> {
149-
type ID = u8;
150-
fn channel() -> u8 { $chan }
151-
}
152-
153-
impl<A, B> Channel<stm32::$adc> for opamp::OpenLoop<$opamp, A, B, InternalOutput> {
154-
type ID = u8;
155-
fn channel() -> u8 { $chan }
156-
}
157-
158-
impl<A> Channel<stm32::$adc> for opamp::Pga<$opamp, A, InternalOutput> {
159-
type ID = u8;
160-
fn channel() -> u8 { $chan }
161-
}
162-
163-
impl Channel<stm32::$adc> for opamp::Locked<$opamp, InternalOutput> {
164-
type ID = u8;
165-
fn channel() -> u8 { $chan }
166-
}
178+
adc_channel_helper!($adc, $chan, opamp::Follower<$opamp, A, InternalOutput>, A);
179+
adc_channel_helper!($adc, $chan, opamp::OpenLoop<$opamp, A, B, InternalOutput>, A, B);
180+
adc_channel_helper!($adc, $chan, opamp::Pga<$opamp, A, InternalOutput>, A);
181+
adc_channel_helper!($adc, $chan, opamp::Locked<$opamp, InternalOutput>,);
167182
)+
168183
};
169184
}
@@ -1898,7 +1913,7 @@ macro_rules! adc {
18981913
/// to sample for at a given ADC clock frequency
18991914
pub fn configure_channel<CHANNEL>(&mut self, _channel: &CHANNEL, sequence: config::Sequence, sample_time: config::SampleTime)
19001915
where
1901-
CHANNEL: Channel<stm32::$adc_type, ID=u8>
1916+
CHANNEL: Channel<Ad<stm32::$adc_type>, ID=u8>
19021917
{
19031918

19041919
//Check the sequence is long enough
@@ -1964,7 +1979,7 @@ macro_rules! adc {
19641979
/// Note that it reconfigures the adc sequence and doesn't restore it
19651980
pub fn convert<PIN>(&mut self, pin: &PIN, sample_time: config::SampleTime) -> u16
19661981
where
1967-
PIN: Channel<stm32::$adc_type, ID=u8>
1982+
PIN: Channel<Ad<stm32::$adc_type>, ID=u8>
19681983
{
19691984
let saved_config = self.config;
19701985
unsafe {
@@ -2410,7 +2425,7 @@ macro_rules! adc {
24102425
#[inline(always)]
24112426
pub fn configure_channel<CHANNEL>(&mut self, channel: &CHANNEL, sequence: config::Sequence, sample_time: config::SampleTime)
24122427
where
2413-
CHANNEL: Channel<stm32::$adc_type, ID=u8>
2428+
CHANNEL: Channel<Ad<stm32::$adc_type>, ID=u8>
24142429
{
24152430
self.adc.configure_channel(channel, sequence, sample_time)
24162431
}
@@ -2420,7 +2435,7 @@ macro_rules! adc {
24202435
#[inline(always)]
24212436
pub fn convert<PIN>(&mut self, pin: &PIN, sample_time: config::SampleTime) -> u16
24222437
where
2423-
PIN: Channel<stm32::$adc_type, ID=u8>
2438+
PIN: Channel<Ad<stm32::$adc_type>, ID=u8>
24242439
{
24252440
self.adc.convert(pin, sample_time)
24262441
}
@@ -2463,7 +2478,7 @@ macro_rules! adc {
24632478
#[inline(always)]
24642479
pub fn convert<PIN>(&mut self, pin: &PIN, sample_time: config::SampleTime) -> u16
24652480
where
2466-
PIN: Channel<stm32::$adc_type, ID=u8>
2481+
PIN: Channel<Ad<stm32::$adc_type>, ID=u8>
24672482
{
24682483
self.adc.reset_sequence();
24692484
self.adc.configure_channel(pin, config::Sequence::One, sample_time);
@@ -2604,9 +2619,9 @@ macro_rules! adc {
26042619
const REQUEST_LINE: Option<u8> = Some($mux as u8);
26052620
}
26062621

2607-
impl<PIN> OneShot<stm32::$adc_type, u16, PIN> for Adc<stm32::$adc_type, Disabled>
2622+
impl<PIN> OneShot<Ad<stm32::$adc_type>, u16, PIN> for Adc<stm32::$adc_type, Disabled>
26082623
where
2609-
PIN: Channel<stm32::$adc_type, ID=u8>,
2624+
PIN: Channel<Ad<stm32::$adc_type>, ID=u8>,
26102625
{
26112626
type Error = ();
26122627

0 commit comments

Comments
 (0)