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 - 1).bits(trim) });
327
328            delay.delay_us(64);
329
330            let cal_flag = self
331                .regs
332                .sr()
333                .read()
334                .cal_flag(channel as u8 - 1)
335                .bit_is_set();
336
337            if cal_flag {
338                break;
339            }
340            trim += 1;
341        }
342    }
343
344    /// Enable the DAC, for a specific channel.
345    pub fn enable(&mut self, channel: DacChannel) {
346        let cr = &self.regs.cr();
347
348        cr.modify(|_, w| w.en(channel as u8 - 1).bit(true));
349    }
350
351    /// Disable the DAC, for a specific channel.
352    pub fn disable(&mut self, channel: DacChannel) {
353        let cr = &self.regs.cr();
354
355        // Doesn't use the newer API.
356        match channel {
357            DacChannel::C1 => cr.modify(|_, w| w.en1().clear_bit()),
358            #[cfg(not(any(feature = "wl", feature = "f410")))]
359            DacChannel::C2 => cr.modify(|_, w| w.en2().clear_bit()),
360        };
361    }
362
363    /// Set the DAC output word.
364    pub fn write(&mut self, channel: DacChannel, val: u16) {
365        // RM: DAC conversion
366        // The DORx cannot be written directly and any data transfer to the DAC channelx must
367        // be performed by loading the DHRx register (write operation to DHR8Rx,
368        // DHR12Lx, DHR12Rx, DHR8RD, DHR12RD or DHR12LD).
369        // Data stored in the DHRx register are automatically transferred to the DORx
370        // register after one APB1 clock cycle, if no hardware trigger is selected (TENx bit in CR
371        // register is reset). However, when a hardware trigger is selected (TENx bit in CR
372        // register is set) and a trigger occurs, the transfer is performed three APB1 clock cycles after
373        // the trigger signal.
374        // When DORx is loaded with the DHRx contents, the analog output voltage
375        // becomes available after a time tSETTLING that depends on the power supply voltage and the
376        // analog output load.
377
378        // todo: Should we ensure the word doesn't overflow the set `bits` value?
379
380        // todo: Use the field accessors (Assuming not too diff among variants?). These
381        // todo let you write u16 directly instead of casting as u32.
382        let val = val as u32;
383
384        match self.cfg.bits {
385            DacBits::EightR => self
386                .regs
387                .dhr8r(channel as usize - 1)
388                .modify(|_, w| unsafe { w.bits(val) }),
389            DacBits::TwelveL => self
390                .regs
391                .dhr12l(channel as usize - 1)
392                .modify(|_, w| unsafe { w.bits(val) }),
393            DacBits::TwelveR => self
394                .regs
395                .dhr12r(channel as usize - 1)
396                .modify(|_, w| unsafe { w.bits(val) }),
397        };
398    }
399
400    /// Send values to the DAC using DMA. Each trigger (Eg using a timer; the basic timers Tim6
401    /// and Tim7 are designed for DAC triggering) sends one word from the buffer to the DAC's
402    /// output.
403    /// Note that the `dma_channel` argument is unused on F3 and L4, since it is hard-coded,
404    /// and can't be configured using the DMAMUX peripheral. (`dma::mux()` fn).
405    #[cfg(not(any(feature = "f4", feature = "l552")))]
406    pub unsafe fn write_dma(
407        &mut self,
408        buf: &[u16],
409        channel: DacChannel,
410        dma_channel: DmaChannel,
411        channel_cfg: ChannelCfg,
412        dma_periph: dma::DmaPeriph,
413        // dma: &mut Dma<D>,
414    ) -> Result<()> {
415        // where
416        // D: Deref<Target = dma_p::RegisterBlock>,
417        // {
418        let (ptr, len) = (buf.as_ptr(), buf.len());
419
420        #[cfg(any(feature = "f3", feature = "l4"))]
421        let dma_channel = match channel {
422            DacChannel::C1 => DmaInput::Dac1Ch1.dma1_channel(),
423            DacChannel::C2 => DmaInput::Dac1Ch2.dma1_channel(),
424        };
425
426        #[cfg(feature = "l4")]
427        match dma_periph {
428            dma::DmaPeriph::Dma1 => {
429                let mut regs = unsafe { &(*DMA1::ptr()) };
430                match channel {
431                    DacChannel::C1 => dma::channel_select(&mut regs, DmaInput::Dac1Ch1),
432                    DacChannel::C2 => dma::channel_select(&mut regs, DmaInput::Dac1Ch2),
433                }
434            }
435            dma::DmaPeriph::Dma2 => {
436                let mut regs = unsafe { &(*pac::DMA2::ptr()) };
437                match channel {
438                    DacChannel::C1 => dma::channel_select(&mut regs, DmaInput::Dac1Ch1),
439                    DacChannel::C2 => dma::channel_select(&mut regs, DmaInput::Dac1Ch2),
440                };
441            }
442        }
443
444        // H743 RM, section 26.4.8: DMA requests
445        // Each DAC channel has a DMA capability. Two DMA channels are used to service DAC
446        // channel DMA requests.
447
448        // When an external trigger (but not a software trigger) occurs while the DMAENx bit is set, the
449        // value of the DHRx register is transferred into the DORx register when the
450        // transfer is complete, and a DMA request is generated.
451        #[cfg(feature = "g4")]
452        match channel {
453            DacChannel::C1 => self.regs.cr().modify(|_, w| w.dmaen1().bit(true)),
454            DacChannel::C2 => self.regs.cr().modify(|_, w| w.dmaen2().bit(true)),
455        };
456
457        #[cfg(not(feature = "g4"))]
458        match channel {
459            DacChannel::C1 => self.regs.cr().modify(|_, w| w.dmaen1().bit(true)),
460            #[cfg(not(any(feature = "wl", feature = "f410")))]
461            DacChannel::C2 => self.regs.cr().modify(|_, w| w.dmaen2().bit(true)),
462        };
463
464        // In dual mode, if both DMAENx bits are set, two DMA requests are generated. If only one
465        // DMA request is needed, only the corresponding DMAENx bit must be set. In this way, the
466        // application can manage both DAC channels in dual mode by using one DMA request and a
467        // unique DMA channel.
468
469        // As DHRx to DORx data transfer occurred before the DMA request, the very first
470        // data has to be written to the DHRx before the first trigger event occurs.
471
472        // DMA underrun
473        // The DAC DMA request is not queued so that if a second external trigger arrives before the
474        // acknowledgment for the first external trigger is received (first request), then no new request
475        // is issued and the DMA channelx underrun flag DMAUDRx in the SR register is set,
476        // reporting the error condition. The DAC channelx continues to convert old data.
477
478        // The software must clear the DMAUDRx flag by writing 1, clear the DMAEN bit of the used
479        // DMA stream and re-initialize both DMA and DAC channelx to restart the transfer correctly.
480        // The software must modify the DAC trigger conversion frequency or lighten the DMA
481        // workload to avoid a new DMA underrun. Finally, the DAC conversion could be resumed by
482        // enabling both DMA data transfer and conversion trigger.
483
484        // For each DAC channelx, an interrupt is also generated if its corresponding DMAUDRIEx bit
485        // in the CR register is enabled.
486
487        let periph_addr = match &self.cfg.bits {
488            DacBits::EightR => &self.regs.dhr8r(channel as usize - 1) as *const _ as u32,
489            DacBits::TwelveL => &self.regs.dhr12l(channel as usize - 1) as *const _ as u32,
490            DacBits::TwelveR => &self.regs.dhr12r(channel as usize - 1) as *const _ as u32,
491        };
492
493        let len = len as u32;
494
495        match dma_periph {
496            dma::DmaPeriph::Dma1 => {
497                let mut regs = unsafe { &(*DMA1::ptr()) };
498                dma::cfg_channel(
499                    &mut regs,
500                    dma_channel,
501                    periph_addr,
502                    ptr as u32,
503                    len,
504                    dma::Direction::ReadFromMem,
505                    dma::DataSize::S16,
506                    dma::DataSize::S16,
507                    channel_cfg,
508                )
509            }
510            #[cfg(dma2)]
511            dma::DmaPeriph::Dma2 => {
512                let mut regs = unsafe { &(*pac::DMA2::ptr()) };
513                dma::cfg_channel(
514                    &mut regs,
515                    dma_channel,
516                    periph_addr,
517                    ptr as u32,
518                    len,
519                    dma::Direction::ReadFromMem,
520                    dma::DataSize::S16,
521                    dma::DataSize::S16,
522                    channel_cfg,
523                )
524            }
525        }
526    }
527
528    /// Set the DAC output voltage.
529    pub fn write_voltage(&mut self, channel: DacChannel, volts: f32) {
530        let max_word = match self.cfg.bits {
531            DacBits::EightR => 255.,
532            DacBits::TwelveL => 4_095.,
533            DacBits::TwelveR => 4_095.,
534        };
535
536        let val = ((volts / self.vref) * max_word) as u16;
537        self.write(channel, val);
538    }
539
540    // todo: Trouble finding right `tsel` fields for l5 and WL. RM shows same as others. PAC bug?
541    // todo Or is the PAC breaking the bits field into multiple bits?
542    #[cfg(not(any(feature = "l5", feature = "wl")))]
543    /// Select and activate a trigger. See f303 Reference manual, section 16.5.4.
544    /// Each time a DAC interface detects a rising edge on the selected trigger source (refer to the
545    /// table below), the last data stored into the DHRx register are transferred into the
546    /// DORx register. The DORx register is updated three pclk cycles after the
547    /// trigger occurs.
548    pub fn set_trigger(&mut self, channel: DacChannel, trigger: Trigger) {
549        let cr = &self.regs.cr();
550
551        // Note: tsel1 didn't update with the new approach, so repetition here.
552        match channel {
553            DacChannel::C1 => cr.modify(|_, w| unsafe {
554                w.ten(channel as u8 - 1).bit(true);
555                w.tsel1().bits(trigger as u8)
556            }),
557            #[cfg(not(feature = "f410"))]
558            DacChannel::C2 => cr.modify(|_, w| unsafe {
559                w.ten(channel as u8 - 1).bit(true);
560                w.tsel2().bits(trigger as u8)
561            }),
562        };
563    }
564
565    #[cfg(not(any(feature = "l5", feature = "wl")))] // See note on `set_trigger`.
566    /// Independent trigger with single LFSR generation
567    /// See f303 Reference Manual section 16.5.2
568    pub fn trigger_lfsr(&mut self, channel: DacChannel, trigger: Trigger, data: u16) {
569        let cr = &self.regs.cr();
570
571        // todo: This may not be correct.
572        cr.modify(|_, w| unsafe {
573            w.mamp(channel as u8 - 1).bits(0b01);
574            w.wave(channel as u8 - 1).bits(0b01)
575        });
576
577        self.set_trigger(channel, trigger);
578        self.write(channel, data);
579    }
580
581    #[cfg(not(any(feature = "l5", feature = "wl")))] // See note on `set_trigger`.
582    /// Independent trigger with single triangle generation
583    /// See f303 Reference Manual section 16.5.2
584    pub fn trigger_triangle(&mut self, channel: DacChannel, trigger: Trigger, data: u16) {
585        let cr = &self.regs.cr();
586
587        cr.modify(|_, w| unsafe {
588            w.wave(channel as u8 - 1).bits(0b10);
589            w.mamp(channel as u8 - 1).bits(0b10)
590        });
591
592        self.set_trigger(channel, trigger);
593        self.write(channel, data);
594    }
595
596    // todo: Put back. PAC 0.16 API change. (July 2025)
597    // // #[cfg(any(feature = "g4"))]
598    // pub fn generate_sawtooth(&self, channel: DacChannel, config: SawtoothConfig) {
599    //     let cr = &self.regs.cr();
600    //
601    //     self.regs.str1().modify(|_, w| {
602    //         w.strstdata(channel as u8).variant(config.initial);
603    //         w.stincdata(channel as u8).variant(config.increment);
604    //         w.stdir(channel as u8).variant(config.direction as u8 == 1)
605    //     });
606    //
607    //     self.regs.stmodr().modify(|_, w| {
608    //         w.stinctrigsel(channel as u8).variant(config.increment_trigger as u8);
609    //         w.strsttrigsel(channel as u8).variant(config.reset_trigger as u8)
610    //     });
611    //
612    //     cr.modify(|_, w| w.wave(channel as u8).variant(WaveGeneration::Sawtooth as u8));
613    // }
614
615    /// Enable the DMA Underrun interrupt - the only interrupt available.
616    pub fn enable_interrupt(&mut self, channel: DacChannel) {
617        let cr = &self.regs.cr();
618
619        cr.modify(|_, w| w.dmaudrie(channel as u8 - 1).bit(true));
620    }
621
622    #[cfg(not(feature = "g4"))] // todo: PAC ommission? SR missing on G4? In RM. (may not affect all G4 variants)
623    /// Clear the DMA Underrun interrupt - the only interrupt available.
624    pub fn clear_interrupt(&mut self, channel: DacChannel) {
625        self.regs.sr().write(|w| match channel {
626            DacChannel::C1 => w.dmaudr1().bit(true),
627            #[cfg(not(any(feature = "wl", feature = "f410")))]
628            DacChannel::C2 => w.dmaudr2().bit(true),
629        });
630    }
631}