stm32_hal2/
dac.rs

1//! Support for the digital to Analog converter (DAC) peripheral.
2
3use core::ops::Deref;
4
5#[cfg(not(any(feature = "f", feature = "l5", feature = "g4")))]
6use cortex_m::delay::Delay;
7
8use crate::{
9    error::Result,
10    pac::{self, RCC},
11    util::RccPeriph,
12};
13
14cfg_if! {
15    if #[cfg(any(feature = "f3", feature = "g4", feature = "h7b3"))] {
16        use pac::dac1 as p;
17    } else {
18        use pac::dac as p;
19    }
20}
21
22#[cfg(any(feature = "f3", feature = "l4"))]
23use crate::dma::DmaInput;
24#[cfg(not(any(feature = "f4", feature = "l552")))]
25use crate::dma::{self, ChannelCfg, DmaChannel};
26#[cfg(feature = "g0")]
27use crate::pac::DMA as DMA1;
28#[cfg(not(feature = "g0"))]
29use crate::pac::DMA1;
30
31#[derive(Clone, Copy)]
32#[repr(u8)]
33/// Sets the MCR register, Mode1 and Mode2 fields.
34pub enum DacMode {
35    /// DAC channel is connected to external pin with Buffer enabled
36    NormExternalOnlyBufEn = 0b000,
37    /// DAC channel is connected to external pin and to on chip peripherals with buffer
38    /// enabled
39    NormExternalAndPeriphBufEn = 0b001,
40    /// DAC channel is connected to external pin with buffer disabled
41    NormExternalOnlyBufDis = 0b010,
42    /// DAC channel is connected to on chip peripherals with Buffer disabled
43    NormExternalAndPeriphBufDis = 0b011,
44    /// DAC channel is connected to external pin with Buffer enabled. (Sample and Hold)
45    ShNormExternalOnlyBufEn = 0b100,
46    /// DAC channel is connected to external pin and to on chip peripherals with buffer. (Sample and Hold)
47    /// enabled
48    ShExternalAndPeriphBufEn = 0b101,
49    /// DAC channel is connected to external pin with buffer disabled. (Sample and Hold)
50    ShNormExternalOnlyBufDis = 0b110,
51    /// DAC channel is connected to on chip peripherals with Buffer disabled. (Sample and Hold)
52    ShNormExternalAndPeriphBufDis = 0b111,
53}
54
55use cfg_if::cfg_if;
56
57#[derive(Clone, Copy)]
58#[repr(u8)]
59/// Select the channel to output to. Most MCUs only use 2 channels.
60pub enum DacChannel {
61    C1 = 1,
62    #[cfg(not(any(feature = "wl", feature = "f410")))]
63    C2 = 2,
64}
65
66#[derive(Clone, Copy)]
67/// Three options are available to set DAC precision. Sets the DHR8R1 etc register contents.
68pub enum DacBits {
69    /// Eight bit precision, right-aligned.
70    EightR,
71    /// 12-bit precision, left-aligned.
72    TwelveL,
73    /// 12-bit precision, right-aligned.
74    TwelveR,
75}
76
77#[derive(Clone, Copy)]
78#[repr(u8)]
79#[cfg(not(any(feature = "h7", feature = "g4")))]
80/// Select a trigger, used by some features. Sets CR, TSEL1 and TSEL2 fields, for Channel 1
81/// and Channel 2 triggers respectively. See L44 RM, Table 75. DAC trigger selection.
82pub enum Trigger {
83    /// Timer 6
84    Tim6 = 0b000,
85    /// Timers 3 or 8
86    Tim3_8 = 0b001,
87    /// Timer 7
88    Tim7 = 0b010,
89    /// Timer 15
90    Tim5 = 0b011,
91    /// Timer 2
92    Tim2 = 0b100,
93    /// Timer 4
94    Tim4 = 0b101,
95    /// Eg, for interrupts
96    Exti9 = 0b110,
97    /// A software trigger
98    Swtrig = 0b111,
99}
100
101#[derive(Clone, Copy)]
102#[repr(u8)]
103#[cfg(feature = "g4")]
104/// Trigger selection on G4, used by TSELx and Sawtooth generation ST[INC|RST]TRIGSELx
105/// Section 11.3.4 Table 66 - DAC trigger interconnect table
106pub enum Trigger {
107    Software = 0,
108    /// DAC[1,2,4] use TIM8, DAC3 uses TIM1
109    Tim8_1 = 1,
110    Tim7 = 2,
111    Tim15 = 3,
112    Tim2 = 4,
113    Tim4 = 5,
114    /// Reset uses EXTI9, increment uses EXTI10
115    ExtI9_10 = 6,
116    Tim6 = 7,
117    Tim3 = 8,
118
119    /// HRTIM Reset and Increment triggers
120    /// hrtim_[reset|step]_trigX
121    HrtimTrigger1 = 9,
122    HrtimTrigger2 = 10,
123    HrtimTrigger3 = 11,
124    HrtimTrigger4 = 12,
125    HrtimTrigger5 = 13,
126    HrtimTrigger6 = 14,
127
128    /// Update/Reset Triggers from HRTIM
129    /// DAC1 - hrtim_trg1
130    /// DAC2 - hrtim_trg2
131    /// DAC3 - hrtim_trg3
132    /// DAC4 - hrtim_trg1
133    HrtimDacTrigger1_2_3 = 15,
134}
135
136#[derive(Clone, Copy)]
137#[repr(u8)]
138#[cfg(feature = "g4")]
139/// Select a waveform generation. Sets CR, WAVE1 and WAVE2 for Channel 1
140/// and Channel 2 repectively. See G4 RM, section 22.7.1 noise/triangle wave generation enable
141pub enum WaveGeneration {
142    /// No wave generation
143    Disabled = 0b00,
144    /// Noise wave generation mode
145    Noise = 0b01,
146    /// Triange wave generation mode
147    Triangle = 0b10,
148    /// Sawtooth wave generation mode
149    Sawtooth = 0b11,
150}
151
152#[derive(Clone, Copy)]
153#[repr(u8)]
154#[cfg(any(feature = "g4"))]
155pub enum SawtoothDirection {
156    Falling = 0b0,
157    Rising = 0b1,
158}
159
160#[derive(Clone, Copy)]
161#[cfg(any(feature = "g4"))]
162/// Sawtooth Generation configuration for the generate_sawtooth() method
163pub struct SawtoothConfig {
164    /// Specify the increment trigger source from the Trigger enum
165    /// when increment is triggered, the DAC will increment
166    /// incremenbt or decrement (depending on the configured direction)
167    /// the output by the increment value specified in the config.
168    pub increment_trigger: Trigger,
169    /// Specify the reset trigger source from the Trigger enum
170    /// when reset is triggered, the DAC will reset to the initial value
171    /// specified in the config.
172    pub reset_trigger: Trigger,
173    /// Initial value that is set on the DAC output each time a reset
174    /// event is triggered.
175    pub initial: u16,
176    /// Increment value that is added/subtracted from the DAC output
177    /// each time an increment event is triggered.
178    pub increment: u16,
179    /// The direction of the sawtooth wave to control if the wave
180    /// is incremented or decremented on each increment trigger event.
181    pub direction: SawtoothDirection,
182}
183
184#[derive(Clone, Copy)]
185#[repr(u8)]
186#[cfg(feature = "h7")]
187/// Select a trigger, used by some features. Sets CR, TSEL1 and TSEL2 fields, for Channel 1
188/// and Channel 2 triggers respectively. See H743 RM, Table 225. DAC interconnection.
189pub enum Trigger {
190    /// A software trigger
191    Swtrig = 0,
192    /// Timer 1
193    Tim1 = 1,
194    /// Timer 2
195    Tim2 = 2,
196    /// Timer 3
197    Tim4 = 3,
198    /// Timer 4
199    Tim5 = 4,
200    /// Timer 5
201    Tim6 = 5,
202    /// Timer 6
203    Tim7 = 6,
204    /// Timer 7
205    Tim8 = 7,
206    /// Timer 8
207    Tim15 = 8,
208    /// High resolution timer trigger 1
209    Hrtim1Trig1 = 9,
210    /// High resolution timer trigger 2
211    Hrtim1Trig2 = 10,
212    /// Low power timer 1
213    Lptim1 = 11,
214    /// Low power timer 2
215    Lptim2 = 12,
216    /// Eg, for interrupts
217    Exti9 = 13,
218}
219
220#[derive(Clone, Copy)]
221#[repr(u8)]
222#[cfg(feature = "g4")]
223/// High frequency interface mode selection.
224/// Used in MCR
225/// See G4 RM, section 22.7.16
226pub enum HighFrequencyMode {
227    // High frequency interface mode disabled
228    Disabled = 0b00,
229    // High frequency interface mode compatible to AHB > 80MHz
230    Medium = 0b01,
231    // High frequency interface mode compatible to AHB > 160MHz
232    High = 0b10,
233}
234
235#[derive(Clone)]
236pub struct DacConfig {
237    /// Mode: Ie buffer enabled or not, and connected to internal, external, or both. Defaults
238    /// to external only, buffer enabled.
239    pub mode: DacMode,
240    /// Output bit depth and alignment. Defaults to 12 bits, right-aligned.
241    pub bits: DacBits,
242
243    #[cfg(feature = "g4")]
244    pub hfsel: HighFrequencyMode,
245}
246
247impl Default for DacConfig {
248    fn default() -> Self {
249        Self {
250            mode: DacMode::NormExternalOnlyBufEn,
251            bits: DacBits::TwelveR,
252            #[cfg(feature = "g4")]
253            hfsel: HighFrequencyMode::High,
254        }
255    }
256}
257
258/// Represents a Digital to Analog Converter (DAC) peripheral.
259pub struct Dac<R> {
260    pub regs: R,
261    pub cfg: DacConfig,
262    vref: f32,
263}
264
265// todo: Calculate the VDDA vref, as you do with onboard ADCs!
266
267impl<R> Dac<R>
268where
269    R: Deref<Target = p::RegisterBlock> + RccPeriph,
270{
271    /// Initialize a DAC peripheral, including  enabling and resetting
272    /// its RCC peripheral clock. `vref` is in volts.
273    pub fn new(regs: R, cfg: DacConfig, vref: f32) -> Self {
274        let rcc = unsafe { &(*RCC::ptr()) };
275        R::en_reset(rcc);
276
277        // See H743 RM, Table 227 for info on the buffer.
278        cfg_if! {
279            if #[cfg(any(
280                // feature = "l5",
281                feature = "g4",
282            ))] {
283                regs.mcr().modify(|_, w| unsafe {
284                    w.mode1().bits(cfg.mode as u8);
285                    w.mode2().bits(cfg.mode as u8)
286                });
287            } else if #[cfg(any(feature = "h7", feature = "l4", feature = "l5", feature = "wl"))] {
288                regs.mcr().modify(|_, w| unsafe {
289                    #[cfg(not(feature = "wl"))]
290                    w.mode2().bits(cfg.mode as u8);
291                    return w.mode1().bits(cfg.mode as u8);
292
293                });
294            }
295            // Note: mode not present on on F3 and F4.
296        }
297
298        Self { regs, cfg, vref }
299    }
300
301    /// Calibrate the DAC output buffer by performing a "User
302    /// trimming" operation. It is useful when the VDDA/VREF+
303    /// voltage or temperature differ from the factory trimming
304    /// conditions.
305    ///
306    /// The calibration is only valid when the DAC channel is
307    /// operating with the buffer enabled. If applied in other
308    /// modes it has no effect.
309    ///
310    /// After the calibration operation, the DAC channel is
311    /// disabled.
312    #[cfg(not(any(feature = "f3", feature = "f4", feature = "l5", feature = "g4")))]
313    pub fn calibrate_buffer(
314        // This function taken from STM32H7xx-hal.
315        &mut self,
316        channel: DacChannel,
317        delay: &mut Delay,
318    ) {
319        self.disable(channel);
320
321        let mut trim = 0;
322
323        loop {
324            self.regs
325                .ccr()
326                .modify(|_, w| unsafe { w.otrim(channel as u8).bits(trim) });
327
328            delay.delay_us(64);
329
330            let cal_flag = self.regs.sr().read().cal_flag(channel as u8).bit_is_set();
331
332            if cal_flag {
333                break;
334            }
335            trim += 1;
336        }
337    }
338
339    /// Enable the DAC, for a specific channel.
340    pub fn enable(&mut self, channel: DacChannel) {
341        let cr = &self.regs.cr();
342
343        cr.modify(|_, w| w.en(channel as u8).bit(true));
344    }
345
346    /// Disable the DAC, for a specific channel.
347    pub fn disable(&mut self, channel: DacChannel) {
348        let cr = &self.regs.cr();
349
350        // Doesn't use the newer API.
351        match channel {
352            DacChannel::C1 => cr.modify(|_, w| w.en1().clear_bit()),
353            #[cfg(not(any(feature = "wl", feature = "f410")))]
354            DacChannel::C2 => cr.modify(|_, w| w.en2().clear_bit()),
355        };
356    }
357
358    /// Set the DAC output word.
359    pub fn write(&mut self, channel: DacChannel, val: u16) {
360        // RM: DAC conversion
361        // The DORx cannot be written directly and any data transfer to the DAC channelx must
362        // be performed by loading the DHRx register (write operation to DHR8Rx,
363        // DHR12Lx, DHR12Rx, DHR8RD, DHR12RD or DHR12LD).
364        // Data stored in the DHRx register are automatically transferred to the DORx
365        // register after one APB1 clock cycle, if no hardware trigger is selected (TENx bit in CR
366        // register is reset). However, when a hardware trigger is selected (TENx bit in CR
367        // register is set) and a trigger occurs, the transfer is performed three APB1 clock cycles after
368        // the trigger signal.
369        // When DORx is loaded with the DHRx contents, the analog output voltage
370        // becomes available after a time tSETTLING that depends on the power supply voltage and the
371        // analog output load.
372
373        // todo: Should we ensure the word doesn't overflow the set `bits` value?
374
375        // todo: Use the field accessors (Assuming not too diff among variants?). These
376        // todo let you write u16 directly instead of casting as u32.
377        let val = val as u32;
378
379        match self.cfg.bits {
380            DacBits::EightR => self
381                .regs
382                .dhr8r(channel as usize)
383                .modify(|_, w| unsafe { w.bits(val) }),
384            DacBits::TwelveL => self
385                .regs
386                .dhr12l(channel as usize)
387                .modify(|_, w| unsafe { w.bits(val) }),
388            DacBits::TwelveR => self
389                .regs
390                .dhr12r(channel as usize)
391                .modify(|_, w| unsafe { w.bits(val) }),
392        };
393    }
394
395    /// Send values to the DAC using DMA. Each trigger (Eg using a timer; the basic timers Tim6
396    /// and Tim7 are designed for DAC triggering) sends one word from the buffer to the DAC's
397    /// output.
398    /// Note that the `dma_channel` argument is unused on F3 and L4, since it is hard-coded,
399    /// and can't be configured using the DMAMUX peripheral. (`dma::mux()` fn).
400    #[cfg(not(any(feature = "f4", feature = "l552")))]
401    pub unsafe fn write_dma(
402        &mut self,
403        buf: &[u16],
404        channel: DacChannel,
405        dma_channel: DmaChannel,
406        channel_cfg: ChannelCfg,
407        dma_periph: dma::DmaPeriph,
408        // dma: &mut Dma<D>,
409    ) -> Result<()> {
410        // where
411        // D: Deref<Target = dma_p::RegisterBlock>,
412        // {
413        let (ptr, len) = (buf.as_ptr(), buf.len());
414
415        #[cfg(any(feature = "f3", feature = "l4"))]
416        let dma_channel = match channel {
417            DacChannel::C1 => DmaInput::Dac1Ch1.dma1_channel(),
418            DacChannel::C2 => DmaInput::Dac1Ch2.dma1_channel(),
419        };
420
421        #[cfg(feature = "l4")]
422        match dma_periph {
423            dma::DmaPeriph::Dma1 => {
424                let mut regs = unsafe { &(*DMA1::ptr()) };
425                match channel {
426                    DacChannel::C1 => dma::channel_select(&mut regs, DmaInput::Dac1Ch1),
427                    DacChannel::C2 => dma::channel_select(&mut regs, DmaInput::Dac1Ch2),
428                }
429            }
430            dma::DmaPeriph::Dma2 => {
431                let mut regs = unsafe { &(*pac::DMA2::ptr()) };
432                match channel {
433                    DacChannel::C1 => dma::channel_select(&mut regs, DmaInput::Dac1Ch1),
434                    DacChannel::C2 => dma::channel_select(&mut regs, DmaInput::Dac1Ch2),
435                };
436            }
437        }
438
439        // H743 RM, section 26.4.8: DMA requests
440        // Each DAC channel has a DMA capability. Two DMA channels are used to service DAC
441        // channel DMA requests.
442
443        // When an external trigger (but not a software trigger) occurs while the DMAENx bit is set, the
444        // value of the DHRx register is transferred into the DORx register when the
445        // transfer is complete, and a DMA request is generated.
446        #[cfg(feature = "g4")]
447        match channel {
448            DacChannel::C1 => self.regs.cr().modify(|_, w| w.dmaen1().bit(true)),
449            DacChannel::C2 => self.regs.cr().modify(|_, w| w.dmaen2().bit(true)),
450        };
451
452        #[cfg(not(feature = "g4"))]
453        match channel {
454            DacChannel::C1 => self.regs.cr().modify(|_, w| w.dmaen1().bit(true)),
455            #[cfg(not(any(feature = "wl", feature = "f410")))]
456            DacChannel::C2 => self.regs.cr().modify(|_, w| w.dmaen2().bit(true)),
457        };
458
459        // In dual mode, if both DMAENx bits are set, two DMA requests are generated. If only one
460        // DMA request is needed, only the corresponding DMAENx bit must be set. In this way, the
461        // application can manage both DAC channels in dual mode by using one DMA request and a
462        // unique DMA channel.
463
464        // As DHRx to DORx data transfer occurred before the DMA request, the very first
465        // data has to be written to the DHRx before the first trigger event occurs.
466
467        // DMA underrun
468        // The DAC DMA request is not queued so that if a second external trigger arrives before the
469        // acknowledgment for the first external trigger is received (first request), then no new request
470        // is issued and the DMA channelx underrun flag DMAUDRx in the SR register is set,
471        // reporting the error condition. The DAC channelx continues to convert old data.
472
473        // The software must clear the DMAUDRx flag by writing 1, clear the DMAEN bit of the used
474        // DMA stream and re-initialize both DMA and DAC channelx to restart the transfer correctly.
475        // The software must modify the DAC trigger conversion frequency or lighten the DMA
476        // workload to avoid a new DMA underrun. Finally, the DAC conversion could be resumed by
477        // enabling both DMA data transfer and conversion trigger.
478
479        // For each DAC channelx, an interrupt is also generated if its corresponding DMAUDRIEx bit
480        // in the CR register is enabled.
481
482        let periph_addr = match &self.cfg.bits {
483            DacBits::EightR => &self.regs.dhr8r(channel as usize) as *const _ as u32,
484            DacBits::TwelveL => &self.regs.dhr12l(channel as usize) as *const _ as u32,
485            DacBits::TwelveR => &self.regs.dhr12r(channel as usize) as *const _ as u32,
486        };
487
488        let len = len as u32;
489
490        match dma_periph {
491            dma::DmaPeriph::Dma1 => {
492                let mut regs = unsafe { &(*DMA1::ptr()) };
493                dma::cfg_channel(
494                    &mut regs,
495                    dma_channel,
496                    periph_addr,
497                    ptr as u32,
498                    len,
499                    dma::Direction::ReadFromMem,
500                    dma::DataSize::S16,
501                    dma::DataSize::S16,
502                    channel_cfg,
503                )
504            }
505            #[cfg(dma2)]
506            dma::DmaPeriph::Dma2 => {
507                let mut regs = unsafe { &(*pac::DMA2::ptr()) };
508                dma::cfg_channel(
509                    &mut regs,
510                    dma_channel,
511                    periph_addr,
512                    ptr as u32,
513                    len,
514                    dma::Direction::ReadFromMem,
515                    dma::DataSize::S16,
516                    dma::DataSize::S16,
517                    channel_cfg,
518                )
519            }
520        }
521    }
522
523    /// Set the DAC output voltage.
524    pub fn write_voltage(&mut self, channel: DacChannel, volts: f32) {
525        let max_word = match self.cfg.bits {
526            DacBits::EightR => 255.,
527            DacBits::TwelveL => 4_095.,
528            DacBits::TwelveR => 4_095.,
529        };
530
531        let val = ((volts / self.vref) * max_word) as u16;
532        self.write(channel, val);
533    }
534
535    // todo: Trouble finding right `tsel` fields for l5 and WL. RM shows same as others. PAC bug?
536    // todo Or is the PAC breaking the bits field into multiple bits?
537    #[cfg(not(any(feature = "l5", feature = "wl")))]
538    /// Select and activate a trigger. See f303 Reference manual, section 16.5.4.
539    /// Each time a DAC interface detects a rising edge on the selected trigger source (refer to the
540    /// table below), the last data stored into the DHRx register are transferred into the
541    /// DORx register. The DORx register is updated three pclk cycles after the
542    /// trigger occurs.
543    pub fn set_trigger(&mut self, channel: DacChannel, trigger: Trigger) {
544        let cr = &self.regs.cr();
545
546        // Note: tsel1 didn't update with the new approach, so repetition here.
547        match channel {
548            DacChannel::C1 => cr.modify(|_, w| unsafe {
549                w.ten(channel as u8).bit(true);
550                w.tsel1().bits(trigger as u8)
551            }),
552            #[cfg(not(feature = "f410"))]
553            DacChannel::C2 => cr.modify(|_, w| unsafe {
554                w.ten(channel as u8).bit(true);
555                w.tsel2().bits(trigger as u8)
556            }),
557        };
558    }
559
560    #[cfg(not(any(feature = "l5", feature = "wl")))] // See note on `set_trigger`.
561    /// Independent trigger with single LFSR generation
562    /// See f303 Reference Manual section 16.5.2
563    pub fn trigger_lfsr(&mut self, channel: DacChannel, trigger: Trigger, data: u16) {
564        let cr = &self.regs.cr();
565
566        // todo: This may not be correct.
567        cr.modify(|_, w| unsafe {
568            w.mamp(channel as u8).bits(0b01);
569            w.wave(channel as u8).bits(0b01)
570        });
571
572        self.set_trigger(channel, trigger);
573        self.write(channel, data);
574    }
575
576    #[cfg(not(any(feature = "l5", feature = "wl")))] // See note on `set_trigger`.
577    /// Independent trigger with single triangle generation
578    /// See f303 Reference Manual section 16.5.2
579    pub fn trigger_triangle(&mut self, channel: DacChannel, trigger: Trigger, data: u16) {
580        let cr = &self.regs.cr();
581
582        cr.modify(|_, w| unsafe {
583            w.wave(channel as u8).bits(0b10);
584            w.mamp(channel as u8).bits(0b10)
585        });
586
587        self.set_trigger(channel, trigger);
588        self.write(channel, data);
589    }
590
591    // todo: Put back. PAC 0.16 API change. (July 2025)
592    // // #[cfg(any(feature = "g4"))]
593    // pub fn generate_sawtooth(&self, channel: DacChannel, config: SawtoothConfig) {
594    //     let cr = &self.regs.cr();
595    //
596    //     self.regs.str1().modify(|_, w| {
597    //         w.strstdata(channel as u8).variant(config.initial);
598    //         w.stincdata(channel as u8).variant(config.increment);
599    //         w.stdir(channel as u8).variant(config.direction as u8 == 1)
600    //     });
601    //
602    //     self.regs.stmodr().modify(|_, w| {
603    //         w.stinctrigsel(channel as u8).variant(config.increment_trigger as u8);
604    //         w.strsttrigsel(channel as u8).variant(config.reset_trigger as u8)
605    //     });
606    //
607    //     cr.modify(|_, w| w.wave(channel as u8).variant(WaveGeneration::Sawtooth as u8));
608    // }
609
610    /// Enable the DMA Underrun interrupt - the only interrupt available.
611    pub fn enable_interrupt(&mut self, channel: DacChannel) {
612        let cr = &self.regs.cr();
613
614        cr.modify(|_, w| w.dmaudrie(channel as u8).bit(true));
615    }
616
617    #[cfg(not(feature = "g4"))] // todo: PAC ommission? SR missing on G4? In RM. (may not affect all G4 variants)
618    /// Clear the DMA Underrun interrupt - the only interrupt available.
619    pub fn clear_interrupt(&mut self, channel: DacChannel) {
620        self.regs.sr().write(|w| match channel {
621            DacChannel::C1 => w.dmaudr1().bit(true),
622            #[cfg(not(any(feature = "wl", feature = "f410")))]
623            DacChannel::C2 => w.dmaudr2().bit(true),
624        });
625    }
626}