Skip to content

Commit 3cca769

Browse files
committed
Add sigma-delta modulation driver
Solution for issue esp-rs#2370
1 parent d32a733 commit 3cca769

File tree

9 files changed

+273
-0
lines changed

9 files changed

+273
-0
lines changed

esp-hal/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,8 @@ pub mod rom;
216216
pub mod rsa;
217217
#[cfg(any(lp_clkrst, rtc_cntl))]
218218
pub mod rtc_cntl;
219+
#[cfg(sdm)]
220+
pub mod sdm;
219221
#[cfg(sha)]
220222
pub mod sha;
221223
#[cfg(any(spi0, spi1, spi2, spi3))]

esp-hal/src/sdm.rs

Lines changed: 264 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,264 @@
1+
//! Sigma-Delta modulation peripheral driver.
2+
//!
3+
4+
use crate::{
5+
clock::Clocks,
6+
gpio::{OutputSignal, PeripheralOutput},
7+
peripheral::{Peripheral, PeripheralRef},
8+
peripherals,
9+
private,
10+
};
11+
use fugit::HertzU32;
12+
13+
/// Sigma-Delta modulation peripheral driver.
14+
pub struct Sdm<'d, SD> {
15+
_sd: PeripheralRef<'d, SD>,
16+
channels_usage: u8,
17+
}
18+
19+
/// Channel errors
20+
#[derive(Debug, Clone, Copy, PartialEq)]
21+
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
22+
pub enum Error {
23+
/// Prescale out of range
24+
PrescaleRange,
25+
/// No free channels to use
26+
NoChannels,
27+
}
28+
29+
impl<'d, SD> Sdm<'d, SD>
30+
where
31+
SD: RegisterAccess,
32+
{
33+
/// Initialize driver using a given SD instance.
34+
pub fn new(sd_instance: impl crate::peripheral::Peripheral<P = SD> + 'd) -> Self {
35+
Self {
36+
_sd: sd_instance.into_ref(),
37+
channels_usage: 0,
38+
}
39+
}
40+
41+
/// Configure and acquire channel.
42+
pub fn enable_pin<PIN: PeripheralOutput>(
43+
&mut self,
44+
pin: impl Peripheral<P = PIN> + 'd,
45+
frequency: HertzU32,
46+
) -> Result<Channel<'_, SD, PIN>, Error> {
47+
crate::into_ref!(pin);
48+
49+
let chs = self.channels_usage;
50+
let chidx = self.alloc_channel()?;
51+
let signal = CHANNELS[chidx as usize];
52+
53+
if chs == 0 {
54+
SD::enable_clock(true);
55+
}
56+
57+
pin.connect_peripheral_to_output(signal, private::Internal);
58+
59+
let mut channel = Channel {
60+
_sdm: self,
61+
pin,
62+
chidx,
63+
};
64+
65+
channel.set_frequency(frequency)?;
66+
67+
Ok(channel)
68+
}
69+
70+
/// Deconfigure and release channel.
71+
pub fn disable_pin<PIN: PeripheralOutput>(&mut self, channel: Channel<'d, SD, PIN>) {
72+
let Channel { mut pin, chidx, .. } = channel;
73+
74+
let signal = CHANNELS[chidx as usize];
75+
76+
pin.disconnect_from_peripheral_output(signal, private::Internal);
77+
78+
self.dealloc_channel(chidx);
79+
80+
if self.channels_usage == 0 {
81+
SD::enable_clock(false);
82+
}
83+
}
84+
85+
fn alloc_channel(&mut self) -> Result<u8, Error> {
86+
let mut usage = self.channels_usage;
87+
let mut chidx: u8 = 0;
88+
while usage & 1 != 0 && chidx < CHANNELS.len() as u8 {
89+
usage >>= 1;
90+
chidx += 1;
91+
}
92+
if chidx < CHANNELS.len() as u8 {
93+
self.channels_usage |= 1 << chidx;
94+
Ok(chidx)
95+
} else {
96+
Err(Error::NoChannels)
97+
}
98+
}
99+
100+
fn dealloc_channel(&mut self, chidx: u8) {
101+
self.channels_usage &= !(1 << chidx);
102+
}
103+
}
104+
105+
/// Sigma-Delta modulation channel handle.
106+
pub struct Channel<'d, SD, PIN> {
107+
_sdm: &'d Sdm<'d, SD>,
108+
pin: PeripheralRef<'d, PIN>,
109+
chidx: u8,
110+
}
111+
112+
impl<'d, SD, PIN> Channel<'d, SD, PIN>
113+
where
114+
SD: RegisterAccess,
115+
{
116+
/// Set raw pulse density
117+
///
118+
/// Sigma-delta quantized density of one channel, the value ranges from -128
119+
/// to 127, recommended range is -90 ~ 90. The waveform is more like a
120+
/// random one in this range.
121+
pub fn set_pulse_density(&mut self, density: i8) {
122+
SD::set_pulse_density(self.chidx, density);
123+
}
124+
125+
/// Set duty cycle
126+
pub fn set_duty(&mut self, duty: u8) {
127+
let density = duty as i16 - 128;
128+
self.set_pulse_density(density as i8)
129+
}
130+
131+
/// Set raw prescale
132+
///
133+
/// The divider of source clock, ranges from 1 to 256
134+
pub fn set_prescale(&mut self, prescale: u16) -> Result<(), Error> {
135+
if prescale < 1 || prescale > 256 {
136+
Err(Error::PrescaleRange)
137+
} else {
138+
SD::set_prescale(self.chidx, prescale);
139+
Ok(())
140+
}
141+
}
142+
143+
/// Set prescale using frequency
144+
pub fn set_frequency(&mut self, frequency: HertzU32) -> Result<(), Error> {
145+
let clocks = Clocks::get();
146+
let clock_frequency = clocks.apb_clock.to_Hz();
147+
let frequency = frequency.to_Hz();
148+
149+
let prescale = prescale_from_frequency(clock_frequency, frequency);
150+
151+
self.set_prescale(prescale)
152+
}
153+
}
154+
155+
mod ehal1 {
156+
use embedded_hal::pwm::{Error as PwmError, ErrorKind, ErrorType, SetDutyCycle};
157+
use crate::gpio::OutputPin;
158+
use super::{Channel, RegisterAccess, Error};
159+
160+
impl PwmError for Error {
161+
fn kind(&self) -> ErrorKind {
162+
ErrorKind::Other
163+
}
164+
}
165+
166+
impl<'d, SD: RegisterAccess, PIN: OutputPin> ErrorType for Channel<'d, SD, PIN> {
167+
type Error = Error;
168+
}
169+
170+
impl<'d, SD: RegisterAccess, PIN: OutputPin> SetDutyCycle for Channel<'d, SD, PIN> {
171+
fn max_duty_cycle(&self) -> u16 {
172+
255 as u16
173+
}
174+
175+
fn set_duty_cycle(&mut self, mut duty: u16) -> Result<(), Self::Error> {
176+
let max = self.max_duty_cycle();
177+
duty = if duty > max { max } else { duty };
178+
self.set_duty(duty as u8);
179+
Ok(())
180+
}
181+
}
182+
}
183+
184+
#[cfg(any(esp32, esp32s2, esp32s3))]
185+
const CHANNELS: [OutputSignal; 8] = [
186+
OutputSignal::GPIO_SD0,
187+
OutputSignal::GPIO_SD1,
188+
OutputSignal::GPIO_SD2,
189+
OutputSignal::GPIO_SD3,
190+
OutputSignal::GPIO_SD4,
191+
OutputSignal::GPIO_SD5,
192+
OutputSignal::GPIO_SD6,
193+
OutputSignal::GPIO_SD7,
194+
];
195+
196+
#[cfg(any(esp32c3, esp32c6, esp32h2))]
197+
const CHANNELS: [OutputSignal; 4] = [
198+
OutputSignal::GPIO_SD0,
199+
OutputSignal::GPIO_SD1,
200+
OutputSignal::GPIO_SD2,
201+
OutputSignal::GPIO_SD3,
202+
];
203+
204+
#[doc(hidden)]
205+
pub trait RegisterAccess {
206+
/// Enable/disable sigma/delta clock
207+
fn enable_clock(en: bool);
208+
209+
/// Set channel pulse density
210+
fn set_pulse_density(ch: u8, density: i8);
211+
212+
/// Set channel clock pre-scale
213+
fn set_prescale(ch: u8, prescale: u16);
214+
}
215+
216+
impl RegisterAccess for peripherals::GPIO_SD {
217+
fn enable_clock(en: bool) {
218+
// The clk enable register does not exist on ESP32.
219+
#[cfg(not(esp32))]
220+
{
221+
let sd = unsafe { &*Self::PTR };
222+
223+
sd.sigmadelta_misc()
224+
.modify(|_, w| w.function_clk_en().bit(en));
225+
}
226+
}
227+
228+
fn set_pulse_density(ch: u8, density: i8) {
229+
let sd = unsafe { &*Self::PTR };
230+
231+
sd.sigmadelta(ch as _)
232+
.modify(|_, w| unsafe { w.in_().bits(density as _) });
233+
}
234+
235+
fn set_prescale(ch: u8, prescale: u16) {
236+
let sd = unsafe { &*Self::PTR };
237+
238+
sd.sigmadelta(ch as _)
239+
.modify(|_, w| unsafe { w.prescale().bits((prescale - 1) as _) });
240+
}
241+
}
242+
243+
fn prescale_from_frequency(clk_freq: u32, req_freq: u32) -> u16 {
244+
let pre = clk_freq / req_freq;
245+
let err = clk_freq % req_freq;
246+
247+
// Do the normal rounding and error >= (src/n + src/(n+1)) / 2,
248+
// then carry the bit
249+
let pre = if err >= clk_freq / (2 * pre * (pre + 1)) {
250+
pre + 1
251+
} else {
252+
pre
253+
};
254+
255+
//pre.max(1).min(256) as _
256+
257+
/*if pre < 1 || pre > 256 {
258+
Err(Error::PrescaleRange)
259+
} else {
260+
pre as _
261+
}*/
262+
263+
pre as _
264+
}

esp-metadata/devices/esp32.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ peripherals = [
3333
"rtc_io",
3434
"sdhost",
3535
"sens",
36+
"sdm",
3637
"sha",
3738
"slc",
3839
"slchost",

esp-metadata/devices/esp32c3.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ peripherals = [
2626
"rsa",
2727
"rtc_cntl",
2828
"sensitive",
29+
"sdm",
2930
"sha",
3031
"spi0",
3132
"spi1",

esp-metadata/devices/esp32c6.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ peripherals = [
5050
"rmt",
5151
"rng",
5252
"rsa",
53+
"sdm",
5354
"sha",
5455
"slchost",
5556
"soc_etm",

esp-metadata/devices/esp32h2.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ peripherals = [
4444
"rmt",
4545
"rng",
4646
"rsa",
47+
"sdm",
4748
"sha",
4849
"soc_etm",
4950
"spi0",

esp-metadata/devices/esp32p4.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ peripherals = [
7171
# "rmt",
7272
# "rsa",
7373
# "sdhost",
74+
# "sdm",
7475
# "sha",
7576
# "soc_etm",
7677
# "spi0",

esp-metadata/devices/esp32s2.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ peripherals = [
2929
"rtc_i2c",
3030
"rtc_io",
3131
"sens",
32+
"sdm",
3233
"sha",
3334
"spi0",
3435
"spi1",

esp-metadata/devices/esp32s3.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ peripherals = [
3737
"rtc_io",
3838
"sens",
3939
"sensitive",
40+
"sdm",
4041
"sha",
4142
"spi0",
4243
"spi1",

0 commit comments

Comments
 (0)