embassy_stm32/dac/
mod.rs

1//! Digital to Analog Converter (DAC)
2#![macro_use]
3
4use core::marker::PhantomData;
5
6use embassy_hal_internal::{into_ref, PeripheralRef};
7
8use crate::dma::NoDma;
9#[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))]
10use crate::pac::dac;
11use crate::rcc::{self, RccPeripheral};
12use crate::{peripherals, Peripheral};
13
14mod tsel;
15pub use tsel::TriggerSel;
16
17/// Operating mode for DAC channel
18#[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))]
19#[derive(Debug, Copy, Clone, Eq, PartialEq)]
20#[cfg_attr(feature = "defmt", derive(defmt::Format))]
21pub enum Mode {
22    /// Normal mode, channel is connected to external pin with buffer enabled.
23    NormalExternalBuffered,
24    /// Normal mode, channel is connected to external pin and internal peripherals
25    /// with buffer enabled.
26    NormalBothBuffered,
27    /// Normal mode, channel is connected to external pin with buffer disabled.
28    NormalExternalUnbuffered,
29    /// Normal mode, channel is connected to internal peripherals with buffer disabled.
30    NormalInternalUnbuffered,
31    /// Sample-and-hold mode, channel is connected to external pin with buffer enabled.
32    SampleHoldExternalBuffered,
33    /// Sample-and-hold mode, channel is connected to external pin and internal peripherals
34    /// with buffer enabled.
35    SampleHoldBothBuffered,
36    /// Sample-and-hold mode, channel is connected to external pin and internal peripherals
37    /// with buffer disabled.
38    SampleHoldBothUnbuffered,
39    /// Sample-and-hold mode, channel is connected to internal peripherals with buffer disabled.
40    SampleHoldInternalUnbuffered,
41}
42
43#[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))]
44impl Mode {
45    fn mode(&self) -> dac::vals::Mode {
46        match self {
47            Mode::NormalExternalBuffered => dac::vals::Mode::NORMAL_EXT_BUFEN,
48            Mode::NormalBothBuffered => dac::vals::Mode::NORMAL_EXT_INT_BUFEN,
49            Mode::NormalExternalUnbuffered => dac::vals::Mode::NORMAL_EXT_BUFDIS,
50            Mode::NormalInternalUnbuffered => dac::vals::Mode::NORMAL_INT_BUFDIS,
51            Mode::SampleHoldExternalBuffered => dac::vals::Mode::SAMPHOLD_EXT_BUFEN,
52            Mode::SampleHoldBothBuffered => dac::vals::Mode::SAMPHOLD_EXT_INT_BUFEN,
53            Mode::SampleHoldBothUnbuffered => dac::vals::Mode::SAMPHOLD_EXT_INT_BUFDIS,
54            Mode::SampleHoldInternalUnbuffered => dac::vals::Mode::SAMPHOLD_INT_BUFDIS,
55        }
56    }
57}
58
59#[derive(Debug, Copy, Clone, Eq, PartialEq)]
60#[cfg_attr(feature = "defmt", derive(defmt::Format))]
61/// Single 8 or 12 bit value that can be output by the DAC.
62///
63/// 12-bit values outside the permitted range are silently truncated.
64pub enum Value {
65    /// 8 bit value
66    Bit8(u8),
67    /// 12 bit value stored in a u16, left-aligned
68    Bit12Left(u16),
69    /// 12 bit value stored in a u16, right-aligned
70    Bit12Right(u16),
71}
72
73#[derive(Debug, Copy, Clone, Eq, PartialEq)]
74#[cfg_attr(feature = "defmt", derive(defmt::Format))]
75/// Dual 8 or 12 bit values that can be output by the DAC channels 1 and 2 simultaneously.
76///
77/// 12-bit values outside the permitted range are silently truncated.
78pub enum DualValue {
79    /// 8 bit value
80    Bit8(u8, u8),
81    /// 12 bit value stored in a u16, left-aligned
82    Bit12Left(u16, u16),
83    /// 12 bit value stored in a u16, right-aligned
84    Bit12Right(u16, u16),
85}
86
87#[derive(Debug, Copy, Clone, Eq, PartialEq)]
88#[cfg_attr(feature = "defmt", derive(defmt::Format))]
89/// Array variant of [`Value`].
90pub enum ValueArray<'a> {
91    /// 8 bit values
92    Bit8(&'a [u8]),
93    /// 12 bit value stored in a u16, left-aligned
94    Bit12Left(&'a [u16]),
95    /// 12 bit values stored in a u16, right-aligned
96    Bit12Right(&'a [u16]),
97}
98
99/// Driver for a single DAC channel.
100///
101/// If you want to use both channels, either together or independently,
102/// create a [`Dac`] first and use it to access each channel.
103pub struct DacChannel<'d, T: Instance, const N: u8, DMA = NoDma> {
104    phantom: PhantomData<&'d mut T>,
105    #[allow(unused)]
106    dma: PeripheralRef<'d, DMA>,
107}
108
109/// DAC channel 1 type alias.
110pub type DacCh1<'d, T, DMA = NoDma> = DacChannel<'d, T, 1, DMA>;
111/// DAC channel 2 type alias.
112pub type DacCh2<'d, T, DMA = NoDma> = DacChannel<'d, T, 2, DMA>;
113
114impl<'d, T: Instance, const N: u8, DMA> DacChannel<'d, T, N, DMA> {
115    const IDX: usize = (N - 1) as usize;
116
117    /// Create a new `DacChannel` instance, consuming the underlying DAC peripheral.
118    ///
119    /// If you're not using DMA, pass [`dma::NoDma`] for the `dma` argument.
120    ///
121    /// The channel is enabled on creation and begin to drive the output pin.
122    /// Note that some methods, such as `set_trigger()` and `set_mode()`, will
123    /// disable the channel; you must re-enable it with `enable()`.
124    ///
125    /// By default, triggering is disabled, but it can be enabled using
126    /// [`DacChannel::set_trigger()`].
127    pub fn new(
128        _peri: impl Peripheral<P = T> + 'd,
129        dma: impl Peripheral<P = DMA> + 'd,
130        pin: impl Peripheral<P = impl DacPin<T, N> + crate::gpio::Pin> + 'd,
131    ) -> Self {
132        into_ref!(dma, pin);
133        pin.set_as_analog();
134        rcc::enable_and_reset::<T>();
135        let mut dac = Self {
136            phantom: PhantomData,
137            dma,
138        };
139        #[cfg(any(dac_v5, dac_v6, dac_v7))]
140        dac.set_hfsel();
141        dac.enable();
142        dac
143    }
144
145    /// Create a new `DacChannel` instance where the external output pin is not used,
146    /// so the DAC can only be used to generate internal signals.
147    /// The GPIO pin is therefore available to be used for other functions.
148    ///
149    /// The channel is set to [`Mode::NormalInternalUnbuffered`] and enabled on creation.
150    /// Note that some methods, such as `set_trigger()` and `set_mode()`, will disable the
151    /// channel; you must re-enable it with `enable()`.
152    ///
153    /// If you're not using DMA, pass [`dma::NoDma`] for the `dma` argument.
154    ///
155    /// By default, triggering is disabled, but it can be enabled using
156    /// [`DacChannel::set_trigger()`].
157    #[cfg(all(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7), not(any(stm32h56x, stm32h57x))))]
158    pub fn new_internal(_peri: impl Peripheral<P = T> + 'd, dma: impl Peripheral<P = DMA> + 'd) -> Self {
159        into_ref!(dma);
160        rcc::enable_and_reset::<T>();
161        let mut dac = Self {
162            phantom: PhantomData,
163            dma,
164        };
165        #[cfg(any(dac_v5, dac_v6, dac_v7))]
166        dac.set_hfsel();
167        dac.set_mode(Mode::NormalInternalUnbuffered);
168        dac.enable();
169        dac
170    }
171
172    /// Enable or disable this channel.
173    pub fn set_enable(&mut self, on: bool) {
174        critical_section::with(|_| {
175            T::regs().cr().modify(|reg| {
176                reg.set_en(Self::IDX, on);
177            });
178        });
179    }
180
181    /// Enable this channel.
182    pub fn enable(&mut self) {
183        self.set_enable(true)
184    }
185
186    /// Disable this channel.
187    pub fn disable(&mut self) {
188        self.set_enable(false)
189    }
190
191    /// Set the trigger source for this channel.
192    ///
193    /// This method disables the channel, so you may need to re-enable afterwards.
194    pub fn set_trigger(&mut self, source: TriggerSel) {
195        critical_section::with(|_| {
196            T::regs().cr().modify(|reg| {
197                reg.set_en(Self::IDX, false);
198                reg.set_tsel(Self::IDX, source as u8);
199            });
200        });
201    }
202
203    /// Enable or disable triggering for this channel.
204    pub fn set_triggering(&mut self, on: bool) {
205        critical_section::with(|_| {
206            T::regs().cr().modify(|reg| {
207                reg.set_ten(Self::IDX, on);
208            });
209        });
210    }
211
212    /// Software trigger this channel.
213    pub fn trigger(&mut self) {
214        T::regs().swtrigr().write(|reg| {
215            reg.set_swtrig(Self::IDX, true);
216        });
217    }
218
219    /// Set mode of this channel.
220    ///
221    /// This method disables the channel, so you may need to re-enable afterwards.
222    #[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))]
223    pub fn set_mode(&mut self, mode: Mode) {
224        critical_section::with(|_| {
225            T::regs().cr().modify(|reg| {
226                reg.set_en(Self::IDX, false);
227            });
228            T::regs().mcr().modify(|reg| {
229                reg.set_mode(Self::IDX, mode.mode());
230            });
231        });
232    }
233
234    /// Write a new value to this channel.
235    ///
236    /// If triggering is not enabled, the new value is immediately output; otherwise,
237    /// it will be output after the next trigger.
238    pub fn set(&mut self, value: Value) {
239        match value {
240            Value::Bit8(v) => T::regs().dhr8r(Self::IDX).write(|reg| reg.set_dhr(v)),
241            Value::Bit12Left(v) => T::regs().dhr12l(Self::IDX).write(|reg| reg.set_dhr(v)),
242            Value::Bit12Right(v) => T::regs().dhr12r(Self::IDX).write(|reg| reg.set_dhr(v)),
243        }
244    }
245
246    /// Read the current output value of the DAC.
247    pub fn read(&self) -> u16 {
248        T::regs().dor(Self::IDX).read().dor()
249    }
250
251    /// Set HFSEL as appropriate for the current peripheral clock frequency.
252    #[cfg(dac_v5)]
253    fn set_hfsel(&mut self) {
254        if T::frequency() >= crate::time::mhz(80) {
255            critical_section::with(|_| {
256                T::regs().cr().modify(|reg| {
257                    reg.set_hfsel(true);
258                });
259            });
260        }
261    }
262
263    /// Set HFSEL as appropriate for the current peripheral clock frequency.
264    #[cfg(any(dac_v6, dac_v7))]
265    fn set_hfsel(&mut self) {
266        if T::frequency() >= crate::time::mhz(160) {
267            critical_section::with(|_| {
268                T::regs().mcr().modify(|reg| {
269                    reg.set_hfsel(0b10);
270                });
271            });
272        } else if T::frequency() >= crate::time::mhz(80) {
273            critical_section::with(|_| {
274                T::regs().mcr().modify(|reg| {
275                    reg.set_hfsel(0b01);
276                });
277            });
278        }
279    }
280}
281
282macro_rules! impl_dma_methods {
283    ($n:literal, $trait:ident) => {
284        impl<'d, T: Instance, DMA> DacChannel<'d, T, $n, DMA>
285        where
286            DMA: $trait<T>,
287        {
288            /// Write `data` to this channel via DMA.
289            ///
290            /// To prevent delays or glitches when outputing a periodic waveform, the `circular`
291            /// flag can be set. This configures a circular DMA transfer that continually outputs
292            /// `data`. Note that for performance reasons in circular mode the transfer-complete
293            /// interrupt is disabled.
294            #[cfg(not(gpdma))]
295            pub async fn write(&mut self, data: ValueArray<'_>, circular: bool) {
296                // Enable DAC and DMA
297                T::regs().cr().modify(|w| {
298                    w.set_en(Self::IDX, true);
299                    w.set_dmaen(Self::IDX, true);
300                });
301
302                let tx_request = self.dma.request();
303                let dma_channel = &mut self.dma;
304
305                let tx_options = crate::dma::TransferOptions {
306                    circular,
307                    half_transfer_ir: false,
308                    complete_transfer_ir: !circular,
309                    ..Default::default()
310                };
311
312                // Initiate the correct type of DMA transfer depending on what data is passed
313                let tx_f = match data {
314                    ValueArray::Bit8(buf) => unsafe {
315                        crate::dma::Transfer::new_write(
316                            dma_channel,
317                            tx_request,
318                            buf,
319                            T::regs().dhr8r(Self::IDX).as_ptr() as *mut u8,
320                            tx_options,
321                        )
322                    },
323                    ValueArray::Bit12Left(buf) => unsafe {
324                        crate::dma::Transfer::new_write(
325                            dma_channel,
326                            tx_request,
327                            buf,
328                            T::regs().dhr12l(Self::IDX).as_ptr() as *mut u16,
329                            tx_options,
330                        )
331                    },
332                    ValueArray::Bit12Right(buf) => unsafe {
333                        crate::dma::Transfer::new_write(
334                            dma_channel,
335                            tx_request,
336                            buf,
337                            T::regs().dhr12r(Self::IDX).as_ptr() as *mut u16,
338                            tx_options,
339                        )
340                    },
341                };
342
343                tx_f.await;
344
345                T::regs().cr().modify(|w| {
346                    w.set_en(Self::IDX, false);
347                    w.set_dmaen(Self::IDX, false);
348                });
349            }
350        }
351    };
352}
353
354impl_dma_methods!(1, DacDma1);
355impl_dma_methods!(2, DacDma2);
356
357impl<'d, T: Instance, const N: u8, DMA> Drop for DacChannel<'d, T, N, DMA> {
358    fn drop(&mut self) {
359        rcc::disable::<T>();
360    }
361}
362
363/// DAC driver.
364///
365/// Use this struct when you want to use both channels, either together or independently.
366///
367/// # Example
368///
369/// ```ignore
370/// // Pins may need to be changed for your specific device.
371/// let (dac_ch1, dac_ch2) = embassy_stm32::dac::Dac::new(p.DAC1, NoDma, NoDma, p.PA4, p.PA5).split();
372/// ```
373pub struct Dac<'d, T: Instance, DMACh1 = NoDma, DMACh2 = NoDma> {
374    ch1: DacChannel<'d, T, 1, DMACh1>,
375    ch2: DacChannel<'d, T, 2, DMACh2>,
376}
377
378impl<'d, T: Instance, DMACh1, DMACh2> Dac<'d, T, DMACh1, DMACh2> {
379    /// Create a new `Dac` instance, consuming the underlying DAC peripheral.
380    ///
381    /// This struct allows you to access both channels of the DAC, where available. You can either
382    /// call `split()` to obtain separate `DacChannel`s, or use methods on `Dac` to use
383    /// the two channels together.
384    ///
385    /// The channels are enabled on creation and begin to drive their output pins.
386    /// Note that some methods, such as `set_trigger()` and `set_mode()`, will
387    /// disable the channel; you must re-enable them with `enable()`.
388    ///
389    /// By default, triggering is disabled, but it can be enabled using the `set_trigger()`
390    /// method on the underlying channels.
391    pub fn new(
392        _peri: impl Peripheral<P = T> + 'd,
393        dma_ch1: impl Peripheral<P = DMACh1> + 'd,
394        dma_ch2: impl Peripheral<P = DMACh2> + 'd,
395        pin_ch1: impl Peripheral<P = impl DacPin<T, 1> + crate::gpio::Pin> + 'd,
396        pin_ch2: impl Peripheral<P = impl DacPin<T, 2> + crate::gpio::Pin> + 'd,
397    ) -> Self {
398        into_ref!(dma_ch1, dma_ch2, pin_ch1, pin_ch2);
399        pin_ch1.set_as_analog();
400        pin_ch2.set_as_analog();
401
402        // Enable twice to increment the DAC refcount for each channel.
403        rcc::enable_and_reset::<T>();
404        rcc::enable_and_reset::<T>();
405
406        let mut ch1 = DacCh1 {
407            phantom: PhantomData,
408            dma: dma_ch1,
409        };
410        #[cfg(any(dac_v5, dac_v6, dac_v7))]
411        ch1.set_hfsel();
412        ch1.enable();
413
414        let mut ch2 = DacCh2 {
415            phantom: PhantomData,
416            dma: dma_ch2,
417        };
418        #[cfg(any(dac_v5, dac_v6, dac_v7))]
419        ch2.set_hfsel();
420        ch2.enable();
421
422        Self { ch1, ch2 }
423    }
424
425    /// Create a new `Dac` instance where the external output pins are not used,
426    /// so the DAC can only be used to generate internal signals but the GPIO
427    /// pins remain available for other functions.
428    ///
429    /// This struct allows you to access both channels of the DAC, where available. You can either
430    /// call `split()` to obtain separate `DacChannel`s, or use methods on `Dac` to use the two
431    /// channels together.
432    ///
433    /// The channels are set to [`Mode::NormalInternalUnbuffered`] and enabled on creation.
434    /// Note that some methods, such as `set_trigger()` and `set_mode()`, will disable the
435    /// channel; you must re-enable them with `enable()`.
436    ///
437    /// By default, triggering is disabled, but it can be enabled using the `set_trigger()`
438    /// method on the underlying channels.
439    #[cfg(all(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7), not(any(stm32h56x, stm32h57x))))]
440    pub fn new_internal(
441        _peri: impl Peripheral<P = T> + 'd,
442        dma_ch1: impl Peripheral<P = DMACh1> + 'd,
443        dma_ch2: impl Peripheral<P = DMACh2> + 'd,
444    ) -> Self {
445        into_ref!(dma_ch1, dma_ch2);
446        // Enable twice to increment the DAC refcount for each channel.
447        rcc::enable_and_reset::<T>();
448        rcc::enable_and_reset::<T>();
449
450        let mut ch1 = DacCh1 {
451            phantom: PhantomData,
452            dma: dma_ch1,
453        };
454        #[cfg(any(dac_v5, dac_v6, dac_v7))]
455        ch1.set_hfsel();
456        ch1.set_mode(Mode::NormalInternalUnbuffered);
457        ch1.enable();
458
459        let mut ch2 = DacCh2 {
460            phantom: PhantomData,
461            dma: dma_ch2,
462        };
463        #[cfg(any(dac_v5, dac_v6, dac_v7))]
464        ch2.set_hfsel();
465        ch2.set_mode(Mode::NormalInternalUnbuffered);
466        ch2.enable();
467
468        Self { ch1, ch2 }
469    }
470
471    /// Split this `Dac` into separate channels.
472    ///
473    /// You can access and move the channels around separately after splitting.
474    pub fn split(self) -> (DacCh1<'d, T, DMACh1>, DacCh2<'d, T, DMACh2>) {
475        (self.ch1, self.ch2)
476    }
477
478    /// Temporarily access channel 1.
479    pub fn ch1(&mut self) -> &mut DacCh1<'d, T, DMACh1> {
480        &mut self.ch1
481    }
482
483    /// Temporarily access channel 2.
484    pub fn ch2(&mut self) -> &mut DacCh2<'d, T, DMACh2> {
485        &mut self.ch2
486    }
487
488    /// Simultaneously update channels 1 and 2 with a new value.
489    ///
490    /// If triggering is not enabled, the new values are immediately output;
491    /// otherwise, they will be output after the next trigger.
492    pub fn set(&mut self, values: DualValue) {
493        match values {
494            DualValue::Bit8(v1, v2) => T::regs().dhr8rd().write(|reg| {
495                reg.set_dhr(0, v1);
496                reg.set_dhr(1, v2);
497            }),
498            DualValue::Bit12Left(v1, v2) => T::regs().dhr12ld().write(|reg| {
499                reg.set_dhr(0, v1);
500                reg.set_dhr(1, v2);
501            }),
502            DualValue::Bit12Right(v1, v2) => T::regs().dhr12rd().write(|reg| {
503                reg.set_dhr(0, v1);
504                reg.set_dhr(1, v2);
505            }),
506        }
507    }
508}
509
510trait SealedInstance {
511    fn regs() -> crate::pac::dac::Dac;
512}
513
514/// DAC instance.
515#[allow(private_bounds)]
516pub trait Instance: SealedInstance + RccPeripheral + 'static {}
517dma_trait!(DacDma1, Instance);
518dma_trait!(DacDma2, Instance);
519
520/// Marks a pin that can be used with the DAC
521pub trait DacPin<T: Instance, const C: u8>: crate::gpio::Pin + 'static {}
522
523foreach_peripheral!(
524    (dac, $inst:ident) => {
525        impl crate::dac::SealedInstance for peripherals::$inst {
526            fn regs() -> crate::pac::dac::Dac {
527                crate::pac::$inst
528            }
529        }
530
531        impl crate::dac::Instance for peripherals::$inst {}
532    };
533);
534
535macro_rules! impl_dac_pin {
536    ($inst:ident, $pin:ident, $ch:expr) => {
537        impl crate::dac::DacPin<peripherals::$inst, $ch> for crate::peripherals::$pin {}
538    };
539}