Skip to main content

ra_hal/
dac.rs

1//! 12-bit Digital-to-Analog Converter (`DAC12`).
2//!
3//! # Notes
4//!
5//! The driver will turn the module off (`MSTPD20=1`) when it is dropped.
6
7use core::marker::PhantomData;
8
9use embassy_hal_internal::{Peri, PeripheralType};
10use embassy_time::{Duration, block_for};
11
12use crate::{
13    gpio::{Basic, Flex, Pin},
14    module_stop::ModuleStop,
15    pac::dac12::vals::Ref,
16};
17
18/// `DAC12` driver.
19#[allow(private_bounds)]
20pub struct Dac<'d, I: Instance> {
21    _output_pin: Flex<'d, Basic>,
22    _phantom: PhantomData<&'d I>,
23}
24
25/// Alignment of output values within a 16-bit variable.
26#[cfg_attr(feature = "defmt", derive(defmt::Format))]
27#[derive(Copy, Clone, Default, Debug)]
28pub enum Align {
29    /// The value is padded with trailing zeros.
30    Left,
31
32    /// The value is padded with leading zeros.
33    #[default]
34    Right,
35}
36
37/// Reference voltage for `DAC12`. §35.1.
38pub enum Vref {
39    /// `Vrefh0`, analog reference voltage
40    AnalogReference,
41
42    /// `Avcc0`
43    AnalogSupply,
44
45    /// Internal ref ≈ 1.43V
46    Internal,
47
48    /// No reference, turn off `DAC12`.
49    None,
50}
51
52/// For the physical GPIO pins connected to `DAC12` channels.
53#[allow(private_bounds)]
54pub trait DacOutputPin: SealedDacOutputPin {}
55
56/// For the physical GPIO pins connected to `DAC12` channels.
57pub(crate) trait SealedDacOutputPin: Pin + PeripheralType {}
58
59/// `DAC12` peripheral instance.
60#[allow(private_bounds)]
61pub trait Instance: SealedInstance + ModuleStop + PeripheralType {}
62
63pub(crate) trait SealedInstance: PeripheralType {
64    fn regs() -> crate::pac::dac12::Dac12;
65}
66
67impl<'d, I: Instance> Dac<'d, I> {
68    /// Creates a new `DAC12` driver and configures `Vref` to use the analog block power supply (`Avcc0`).
69    pub fn new<P: DacOutputPin>(peri: Peri<'d, I>, output_pin: Peri<'d, P>) -> Self {
70        let _ = peri;
71
72        I::start_module();
73        output_pin.set_as_analog();
74
75        let mut this = Self {
76            _output_pin: Flex::new(output_pin),
77            _phantom: PhantomData,
78        };
79
80        this.set_vref(Vref::AnalogSupply);
81
82        this
83    }
84
85    /// Returns a mutable pointer to the output register.
86    ///
87    /// This should probably go away and/or be made not-pub.
88    #[inline(always)]
89    pub fn buffer(&self) -> *mut u16 {
90        let dac = I::regs();
91        dac.dadr0().as_ptr()
92    }
93
94    /// Sets the data alignment.
95    #[inline(always)]
96    pub fn set_align(&mut self, align: Align) {
97        let dac = I::regs();
98        match align {
99            Align::Left => dac.dadpr().modify(|r| r.set_dpsel(true)),
100            Align::Right => dac.dadpr().modify(|r| r.set_dpsel(false)),
101        }
102    }
103
104    /// Sets the reference voltage source and enables DAC output when voltage has stabilized.
105    #[inline(always)]
106    pub fn set_vref(&mut self, vref: Vref) {
107        // AVCC0 ≈ 4.8V on Arduino
108        // Internal ref ≈ 1.43V
109        // VREFH = unstable, slowly grows from ≈ 1V?
110
111        let dac = I::regs();
112
113        // §36.2.5, §36.3.2
114        dac.davrefcr().modify(|r| r.set_ref_(Ref::None));
115        dac.dadr0().write_value(0);
116        dac.dacr().modify(|r| r.set_daoe0(false));
117
118        block_for(Duration::from_micros(10));
119
120        let vref = match vref {
121            Vref::AnalogReference => Ref::Vref,
122            Vref::AnalogSupply => Ref::Avcc0,
123            Vref::Internal => Ref::Internal,
124            Vref::None => return,
125        };
126
127        dac.davrefcr().modify(|r| r.set_ref_(vref));
128        dac.dacr().modify(|r| r.set_daoe0(true));
129        block_for(Duration::from_micros(5));
130    }
131
132    /// Sets the output value.
133    #[inline(always)]
134    pub fn set(&mut self, value: u16) {
135        trace!("Set {}", value);
136        let dac = I::regs();
137
138        dac.dadr0().write_value(value);
139    }
140
141    /// Starts DAC output.
142    #[inline(always)]
143    pub fn start(&mut self) {
144        let dac = I::regs();
145        dac.dacr().write(|w| w.set_daoe0(true));
146    }
147
148    /// Stops DAC output.
149    #[inline(always)]
150    pub fn stop(&mut self) {
151        let dac = I::regs();
152        dac.dacr().write(|w| w.set_daoe0(false));
153    }
154}
155
156impl<'d, I: Instance> Drop for Dac<'d, I> {
157    /// Turns off `DAC12` output and sets `MSTPD20=true`.
158    fn drop(&mut self) {
159        warn!("DAC12: Drop");
160        let dac = I::regs();
161        dac.dacr().modify(|r| r.set_daoe0(false));
162        I::stop_module();
163    }
164}
165
166impl Instance for crate::peripherals::DAC12_0 {}
167impl SealedInstance for crate::peripherals::DAC12_0 {
168    #[inline(always)]
169    fn regs() -> crate::pac::dac12::Dac12 {
170        crate::pac::DAC12_0
171    }
172}
173
174macro_rules! dac_pin {
175    ($pin:ident) => {
176        impl crate::dac::DacOutputPin for crate::peripherals::$pin {}
177        impl crate::dac::SealedDacOutputPin for crate::peripherals::$pin {}
178    };
179}
180pub(crate) use dac_pin;