esp_hal/analog/
dac.rs

1//! # Digital to Analog Converter (DAC)
2//!
3//! ## Overview
4//! Espressif devices usually have multiple DAC channels. Each DAC channel can
5//! convert the digital value 0~255 to the analog voltage 0~Vref (The reference
6//! voltage 'Vref' here is input from an input pin)
7//!
8//! The DAC peripheral supports outputting analog signal in the multiple ways.
9//!
10//! Two 8-bit DAC channels are available.
11//!
12//! ## Configuration
13//! Developers can choose the  DAC channel they want to use based on the GPIO
14//! pin assignments for each channel.
15//!
16//! ## Examples
17//! ### Write a value to a DAC channel
18//! ```rust, no_run
19#![doc = crate::before_snippet!()]
20//! # use esp_hal::analog::dac::Dac;
21//! # use esp_hal::delay::Delay;
22//! # use embedded_hal::delay::DelayNs;
23#![cfg_attr(esp32, doc = "let dac1_pin = peripherals.GPIO25;")]
24#![cfg_attr(esp32s2, doc = "let dac1_pin = peripherals.GPIO17;")]
25//! let mut dac1 = Dac::new(peripherals.DAC1, dac1_pin);
26//!
27//! let mut delay = Delay::new();
28//!
29//! let mut voltage_dac1 = 200u8;
30//!
31//! // Change voltage on the pins using write function:
32//! loop {
33//!     voltage_dac1 = voltage_dac1.wrapping_add(1);
34//!     dac1.write(voltage_dac1);
35//!
36//!     delay.delay_ms(50u32);
37//! }
38//! # }
39//! ```
40
41use crate::gpio::AnalogPin;
42
43// Only specific pins can be used with each DAC peripheral, and of course
44// these pins are different depending on which chip you are using; for this
45// reason, we will type alias the pins for ease of use later in this module:
46cfg_if::cfg_if! {
47    if #[cfg(esp32)] {
48        type Dac1Gpio<'d> = crate::peripherals::GPIO25<'d>;
49        type Dac2Gpio<'d> = crate::peripherals::GPIO26<'d>;
50    } else if #[cfg(esp32s2)] {
51        type Dac1Gpio<'d> = crate::peripherals::GPIO17<'d>;
52        type Dac2Gpio<'d> = crate::peripherals::GPIO18<'d>;
53    }
54}
55
56/// Digital-to-Analog Converter (DAC) Channel
57pub struct Dac<'d, T>
58where
59    T: Instance + 'd,
60    T::Pin: AnalogPin + 'd,
61{
62    _inner: T,
63    _lifetime: core::marker::PhantomData<&'d mut ()>,
64}
65
66impl<'d, T> Dac<'d, T>
67where
68    T: Instance + 'd,
69    T::Pin: AnalogPin + 'd,
70{
71    /// Construct a new instance of [`Dac`].
72    pub fn new(dac: T, pin: T::Pin) -> Self {
73        // TODO: Revert on drop.
74        pin.set_analog(crate::private::Internal);
75
76        #[cfg(esp32s2)]
77        crate::peripherals::SENS::regs()
78            .sar_dac_ctrl1()
79            .modify(|_, w| w.dac_clkgate_en().set_bit());
80
81        T::enable_xpd();
82
83        Self {
84            _inner: dac,
85            _lifetime: core::marker::PhantomData,
86        }
87    }
88
89    /// Writes the given value.
90    ///
91    /// For each DAC channel, the output analog voltage can be calculated as
92    /// follows: DACn_OUT = VDD3P3_RTC * PDACn_DAC/256
93    pub fn write(&mut self, value: u8) {
94        T::set_pad_source();
95        T::write_byte(value);
96    }
97}
98
99#[doc(hidden)]
100pub trait Instance: crate::private::Sealed {
101    const INDEX: usize;
102
103    type Pin;
104
105    fn enable_xpd() {
106        crate::peripherals::RTC_IO::regs()
107            .pad_dac(Self::INDEX)
108            .modify(|_, w| w.dac_xpd_force().set_bit().xpd_dac().set_bit());
109    }
110
111    fn set_pad_source() {
112        crate::peripherals::SENS::regs()
113            .sar_dac_ctrl2()
114            .modify(|_, w| w.dac_cw_en(Self::INDEX as u8).clear_bit());
115    }
116
117    fn write_byte(value: u8) {
118        crate::peripherals::RTC_IO::regs()
119            .pad_dac(Self::INDEX)
120            .modify(|_, w| unsafe { w.dac().bits(value) });
121    }
122}
123
124impl<'d> Instance for crate::peripherals::DAC1<'d> {
125    const INDEX: usize = 0;
126
127    type Pin = Dac1Gpio<'d>;
128}
129
130impl<'d> Instance for crate::peripherals::DAC2<'d> {
131    const INDEX: usize = 1;
132
133    type Pin = Dac2Gpio<'d>;
134}