stm32g0xx_hal/analog/
dac.rs

1//! DAC
2
3use core::marker::PhantomData;
4use core::mem::MaybeUninit;
5
6use crate::gpio::{DefaultMode, PA4, PA5};
7use crate::rcc::*;
8use crate::stm32::DAC;
9use hal::blocking::delay::DelayUs;
10
11pub trait DacOut<V> {
12    fn set_value(&mut self, val: V);
13    fn get_value(&mut self) -> V;
14}
15
16pub struct GeneratorConfig {
17    mode: u8,
18    amp: u8,
19}
20
21impl GeneratorConfig {
22    pub fn triangle(amplitude: u8) -> Self {
23        Self {
24            mode: 0b10,
25            amp: amplitude,
26        }
27    }
28
29    pub fn noise(seed: u8) -> Self {
30        Self {
31            mode: 0b01,
32            amp: seed,
33        }
34    }
35}
36
37/// Enabled DAC (type state)
38pub struct Enabled;
39/// Enabled DAC without output buffer (type state)
40pub struct EnabledUnbuffered;
41/// Enabled DAC wave generator (type state)
42pub struct WaveGenerator;
43/// Disabled DAC (type state)
44pub struct Disabled;
45
46pub trait ED {}
47impl ED for Enabled {}
48impl ED for EnabledUnbuffered {}
49impl ED for WaveGenerator {}
50impl ED for Disabled {}
51
52pub struct Channel1<ED> {
53    _enabled: PhantomData<ED>,
54}
55pub struct Channel2<ED> {
56    _enabled: PhantomData<ED>,
57}
58
59/// Trait for GPIO pins that can be converted to DAC output pins
60pub trait Pins<DAC> {
61    type Output;
62}
63
64impl Pins<DAC> for PA4<DefaultMode> {
65    type Output = Channel1<Disabled>;
66}
67
68impl Pins<DAC> for PA5<DefaultMode> {
69    type Output = Channel2<Disabled>;
70}
71
72impl Pins<DAC> for (PA4<DefaultMode>, PA5<DefaultMode>) {
73    type Output = (Channel1<Disabled>, Channel2<Disabled>);
74}
75
76pub fn dac<PINS>(_dac: DAC, _pins: PINS, rcc: &mut Rcc) -> PINS::Output
77where
78    PINS: Pins<DAC>,
79{
80    DAC::enable(rcc);
81    DAC::reset(rcc);
82
83    #[allow(clippy::uninit_assumed_init)]
84    unsafe {
85        MaybeUninit::uninit().assume_init()
86    }
87}
88
89macro_rules! dac {
90    ($($CX:ident: (
91        $en:ident,
92        $cen:ident,
93        $cal_flag:ident,
94        $trim:ident,
95        $mode:ident,
96        $dhrx:ident,
97        $dac_dor:ident,
98        $daccxdhr:ident,
99        $wave:ident,
100        $mamp:ident,
101        $ten:ident,
102        $swtrig:ident
103    ),)+) => {
104        $(
105            impl $CX<Disabled> {
106                pub fn enable(self) -> $CX<Enabled> {
107                    let dac = unsafe { &(*DAC::ptr()) };
108
109                    dac.dac_mcr.modify(|_, w| unsafe { w.$mode().bits(1) });
110                    dac.dac_cr.modify(|_, w| w.$en().set_bit());
111
112                    $CX {
113                        _enabled: PhantomData,
114                    }
115                }
116
117                pub fn enable_unbuffered(self) -> $CX<EnabledUnbuffered> {
118                    let dac = unsafe { &(*DAC::ptr()) };
119
120                    dac.dac_mcr.modify(|_, w| unsafe { w.$mode().bits(2) });
121                    dac.dac_cr.modify(|_, w| w.$en().set_bit());
122
123                    $CX {
124                        _enabled: PhantomData,
125                    }
126                }
127
128                pub fn enable_generator(self, config: GeneratorConfig) -> $CX<WaveGenerator> {
129                    let dac = unsafe { &(*DAC::ptr()) };
130
131                    dac.dac_mcr.modify(|_, w| unsafe { w.$mode().bits(1) });
132                    dac.dac_cr.modify(|_, w| unsafe {
133                        w.$wave().bits(config.mode);
134                        w.$ten().set_bit();
135                        w.$mamp().bits(config.amp);
136                        w.$en().set_bit()
137                    });
138
139                    $CX {
140                        _enabled: PhantomData,
141                    }
142                }
143            }
144
145            impl<ED> $CX<ED> {
146                /// Calibrate the DAC output buffer by performing a "User
147                /// trimming" operation. It is useful when the VDDA/VREF+
148                /// voltage or temperature differ from the factory trimming
149                /// conditions.
150                ///
151                /// The calibration is only valid when the DAC channel is
152                /// operating with the buffer enabled. If applied in other
153                /// modes it has no effect.
154                ///
155                /// After the calibration operation, the DAC channel is
156                /// disabled.
157                pub fn calibrate_buffer<T>(self, delay: &mut T) -> $CX<Disabled>
158                where
159                    T: DelayUs<u32>,
160                {
161                    let dac = unsafe { &(*DAC::ptr()) };
162                    dac.dac_cr.modify(|_, w| w.$en().clear_bit());
163                    dac.dac_mcr.modify(|_, w| unsafe { w.$mode().bits(0) });
164                    dac.dac_cr.modify(|_, w| w.$cen().set_bit());
165                    let mut trim = 0;
166                    while true {
167                        dac.dac_ccr.modify(|_, w| unsafe { w.$trim().bits(trim) });
168                        delay.delay_us(64_u32);
169                        if dac.dac_sr.read().$cal_flag().bit() {
170                            break;
171                        }
172                        trim += 1;
173                    }
174                    dac.dac_cr.modify(|_, w| w.$cen().clear_bit());
175
176                    $CX {
177                        _enabled: PhantomData,
178                    }
179                }
180
181                /// Disable the DAC channel
182                pub fn disable(self) -> $CX<Disabled> {
183                    let dac = unsafe { &(*DAC::ptr()) };
184                    dac.dac_cr.modify(|_, w| unsafe {
185                        w.$en().clear_bit().$wave().bits(0).$ten().clear_bit()
186                    });
187
188                    $CX {
189                        _enabled: PhantomData,
190                    }
191                }
192            }
193
194            /// DacOut implementation available in any Enabled/Disabled
195            /// state
196            impl<ED> DacOut<u16> for $CX<ED> {
197                fn set_value(&mut self, val: u16) {
198                    let dac = unsafe { &(*DAC::ptr()) };
199                    dac.$dhrx.write(|w| unsafe { w.bits(val as u32) });
200                }
201
202                fn get_value(&mut self) -> u16 {
203                    let dac = unsafe { &(*DAC::ptr()) };
204                    dac.$dac_dor.read().bits() as u16
205                }
206            }
207
208            /// Wave generator state implementation
209            impl $CX<WaveGenerator> {
210                pub fn trigger(&mut self) {
211                    let dac = unsafe { &(*DAC::ptr()) };
212                    dac.dac_swtrgr.write(|w| { w.$swtrig().set_bit() });
213                }
214            }
215        )+
216    };
217}
218
219pub trait DacExt {
220    fn constrain<PINS>(self, pins: PINS, rcc: &mut Rcc) -> PINS::Output
221    where
222        PINS: Pins<DAC>;
223}
224
225impl DacExt for DAC {
226    fn constrain<PINS>(self, pins: PINS, rcc: &mut Rcc) -> PINS::Output
227    where
228        PINS: Pins<DAC>,
229    {
230        dac(self, pins, rcc)
231    }
232}
233
234dac!(
235    Channel1:
236        (
237            en1,
238            cen1,
239            cal_flag1,
240            otrim1,
241            mode1,
242            dac_dhr12r1,
243            dac_dor1,
244            dacc1dhr,
245            wave1,
246            mamp1,
247            ten1,
248            swtrig1
249        ),
250    Channel2:
251        (
252            en2,
253            cen2,
254            cal_flag2,
255            otrim2,
256            mode2,
257            dac_dhr12r2,
258            dac_dor2,
259            dacc2dhr,
260            wave2,
261            mamp2,
262            ten2,
263            swtrig2
264        ),
265);