@@ -35,8 +35,88 @@ pub struct ObservationToken<P: Observable> {
3535
3636/// A trait providing an interface to make peripherals observed
3737///
38- /// See [`Observed`] and [`ObservationToken`]
38+ /// See [`Observable::observe`], [` Observed`] and [`ObservationToken`]
3939pub trait Observable : Sized {
40+ /// Observe this peripheral to split it into a [`Observed<Self>`] and a set of [`ObservationToken`]'s
41+ ///
42+ /// This is useful when you need the same peripherals for multiple things at the same time.
43+ ///
44+ /// For example let's say you want to keep track of the voltage of a pin. You want to log it
45+ /// every second but if it rises above a threshold then you need to react really fast.
46+ ///
47+ /// This can be solved by connecting the pin to a comparator that compares the pins
48+ /// voltage to a reference. If the voltage rises above the reference then the comparator
49+ /// will quickly detect this and an interrupt can be generated or similar (not shown here).
50+ ///
51+ /// ```
52+ /// let dp = stm32::Peripherals::take().unwrap();
53+ /// let mut rcc = dp.RCC.constrain();
54+ ///
55+ /// let gpioa = dp.GPIOA.split(&mut rcc);
56+ ///
57+ /// let (comp1, comp2, ..) = dp.COMP.split(&mut rcc);
58+ ///
59+ /// let pa1 = gpioa.pa1.into_analog(); // <- The pin to keep track of
60+ /// let pa0 = gpioa.pa0.into_analog(); // <- Reference voltage
61+ ///
62+ /// // Pins consumed here
63+ /// let comp1 = comp1.comparator(pa1, pa0, Config::default(), &rcc.clocks);
64+ /// let comp1 = comp1.enable();
65+ ///
66+ /// // Can not use pa0 and pa1 for AD readings
67+ /// ```
68+ ///
69+ /// However we still want to perform AD readings every second. Since the pins are consumed
70+ /// by the comparator this is impossible.
71+ ///
72+ /// It turns ut that to construct the comparator we do not actually need a pin. We
73+ /// just need proof that there is a pin that is setup in the correct mode and which
74+ /// will stay in that mode as long as the comparator lives.
75+ ///
76+ /// This is where [`Observable::observe`] comes in. It splits the peripheral, in this case
77+ /// a pin, into an [`Observed<Self>`] and a set of [`ObservationToken`]'s. The `Observed`
78+ /// type can be used just like the peripheral would normally be used. For our pin we can
79+ /// use it to perform AD readings etc. There is however one vital difference, we can not
80+ /// reconfigure the observed peripheral. The `ObservationToken`'s on the other hand
81+ /// are tokens that proove that the peripheral will not be reconfigured. These can then
82+ /// be used instead of the peripheral to pass as arguments to other peripherals.
83+ ///
84+ /// ```
85+ /// let dp = stm32::Peripherals::take().unwrap();
86+ /// let mut rcc = dp.RCC.constrain();
87+ ///
88+ /// let gpioa = dp.GPIOA.split(&mut rcc);
89+ ///
90+ /// let (comp1, comp2, ..) = dp.COMP.split(&mut rcc);
91+ ///
92+ /// let (pa1, [pa1_token]) = gpioa // <- The pin to keep track of
93+ /// .pa1
94+ /// .into_analog()
95+ /// .observe();
96+ /// let pa0 = gpioa.pa0.into_analog(); // <- Reference voltage
97+ ///
98+ /// // Only pa1_token and pa0 consumed here
99+ /// let comp1 = comp1.comparator(pa1_token, pa0, Config::default(), &rcc.clocks);
100+ /// let comp1 = comp1.enable();
101+ ///
102+ /// let mut delay = cp.SYST.delay(&rcc.clocks);
103+ /// let mut adc = dp.ADC2.claim_and_configure(
104+ /// stm32g4xx_hal::adc::ClockSource::SystemClock,
105+ /// &rcc,
106+ /// stm32g4xx_hal::adc::config::AdcConfig::default(),
107+ /// &mut delay,
108+ /// false,
109+ /// );
110+ ///
111+ /// // Can not reconfigure pa1 here
112+ ///
113+ /// loop {
114+ /// // Can still use pa1 here
115+ /// let sample = adc.convert(&pa1, SampleTime::Cycles_640_5);
116+ /// defmt::info!("Reading: {}", sample);
117+ /// delay.delay(1000.millis());
118+ /// }
119+ /// ```
40120 fn observe < const N : usize > ( self ) -> ( Observed < Self , N > , [ ObservationToken < Self > ; N ] ) {
41121 (
42122 Observed { peripheral : self } ,
0 commit comments