1 | //! Digital to Analog Converter (DAC) |
2 | #![macro_use ] |
3 | |
4 | use core::marker::PhantomData; |
5 | |
6 | use embassy_hal_internal::{into_ref, PeripheralRef}; |
7 | |
8 | use crate::dma::NoDma; |
9 | #[cfg (any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))] |
10 | use crate::pac::dac; |
11 | use crate::rcc::{self, RccPeripheral}; |
12 | use crate::{peripherals, Peripheral}; |
13 | |
14 | mod tsel; |
15 | pub 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))] |
21 | pub 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))] |
44 | impl 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. |
64 | pub 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. |
78 | pub 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`]. |
90 | pub 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. |
103 | pub 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. |
110 | pub type DacCh1<'d, T, DMA = NoDma> = DacChannel<'d, T, 1, DMA>; |
111 | /// DAC channel 2 type alias. |
112 | pub type DacCh2<'d, T, DMA = NoDma> = DacChannel<'d, T, 2, DMA>; |
113 | |
114 | impl<'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 | |
282 | macro_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 | |
354 | impl_dma_methods!(1, DacDma1); |
355 | impl_dma_methods!(2, DacDma2); |
356 | |
357 | impl<'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 | /// ``` |
373 | pub 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 | |
378 | impl<'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 | |
510 | trait SealedInstance { |
511 | fn regs() -> crate::pac::dac::Dac; |
512 | } |
513 | |
514 | /// DAC instance. |
515 | #[allow (private_bounds)] |
516 | pub trait Instance: SealedInstance + RccPeripheral + 'static {} |
517 | dma_trait!(DacDma1, Instance); |
518 | dma_trait!(DacDma2, Instance); |
519 | |
520 | /// Marks a pin that can be used with the DAC |
521 | pub trait DacPin<T: Instance, const C: u8>: crate::gpio::Pin + 'static {} |
522 | |
523 | foreach_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 | |
535 | macro_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 | } |
540 | |