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}