Skip to content

Commit 464c9fe

Browse files
Dominaezzzrursprung
andcommitted
I2S camera - WIP
TODOs: * proper commit-msg * add missing docs for `DataFormat` * add missing example to docs * should the qa-test example be adapted to the ESP32-CAM instead? which board is more prevalent? Co-Authored-By: Ralph Ursprung <[email protected]>
1 parent f0191a0 commit 464c9fe

File tree

4 files changed

+1075
-0
lines changed

4 files changed

+1075
-0
lines changed

esp-hal/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111

1212
- Support for `rand_core` 0.9 (#3211)
1313
- `ESP_HAL_CONFIG_STACK_GUARD_OFFSET` and `ESP_HAL_CONFIG_STACK_GUARD_VALUE` to configure Rust's [Stack smashing protection](https://doc.rust-lang.org/rustc/exploit-mitigations.html#stack-smashing-protection) (#3203)
14+
- ESP32: I2S Camera mode driver (#3219)
1415

1516
### Changed
1617

esp-hal/src/i2s/master/camera.rs

Lines changed: 369 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,369 @@
1+
//! # I2S in Camera Slave receiving mode.
2+
//!
3+
//! ## Overview
4+
//! The I2S peripheral supports a camera slave mode for high-speed data
5+
//! transfer from external camera modules.
6+
//! The driver mandates DMA for efficient data transfer.
7+
//!
8+
//! ## Examples
9+
//! ```rust, no_run
10+
//! // FIXME: add example
11+
//! ```
12+
13+
use core::{
14+
mem::ManuallyDrop,
15+
ops::{Deref, DerefMut},
16+
};
17+
18+
use crate::{
19+
dma::{ChannelRx, DmaEligible, DmaError, DmaRxBuffer, PeripheralRxChannel, Rx, RxChannelFor},
20+
gpio::{InputPin, Level, Pull},
21+
i2s::master::{Error, ExtendedSignals, RegisterAccess},
22+
peripheral::{Peripheral, PeripheralRef},
23+
system::PeripheralClockControl,
24+
Blocking,
25+
};
26+
27+
/// Supported data formats
28+
#[derive(Debug, Clone, Copy, PartialEq)]
29+
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
30+
pub enum DataFormat {
31+
/// FIXME: docs
32+
DualChannel16 = 0,
33+
/// FIXME: docs
34+
SingleChannel16 = 1,
35+
/// FIXME: docs
36+
DualChannel32 = 2,
37+
/// FIXME: docs
38+
SingleChannel32 = 3,
39+
}
40+
41+
/// Represents the camera interface.
42+
pub struct Camera<'d, I2S: DmaEligible> {
43+
_i2s: PeripheralRef<'d, I2S>,
44+
rx_channel: ChannelRx<'d, Blocking, PeripheralRxChannel<I2S>>,
45+
}
46+
47+
impl<'d, I2S> Camera<'d, I2S>
48+
where
49+
I2S: RegisterAccess + ExtendedSignals,
50+
{
51+
/// Creates a new `Camera` instance with DMA support.
52+
pub fn new<CH: RxChannelFor<I2S>>(
53+
i2s: impl Peripheral<P = I2S> + 'd,
54+
data_format: DataFormat,
55+
channel: impl Peripheral<P = CH> + 'd,
56+
) -> Self {
57+
crate::into_ref!(i2s);
58+
59+
PeripheralClockControl::enable(i2s.peripheral());
60+
61+
let regs = i2s.regs();
62+
63+
// Configuration and start/stop bits
64+
regs.conf().modify(|_, w| {
65+
// Enable slave receiver mode
66+
w.rx_slave_mod()
67+
.set_bit()
68+
// Receive left-channel data first.
69+
.rx_right_first()
70+
.clear_bit()
71+
// Place left-channel data at the MSB in the FIFO
72+
.rx_msb_right()
73+
.clear_bit()
74+
// Do not enable receiver in Philips standard mode
75+
.rx_msb_shift()
76+
.clear_bit()
77+
// Do not enable receiver’s mono mode in PCM standard mode
78+
.rx_mono()
79+
.clear_bit()
80+
// Do not enable receiver in PCM standard mode
81+
.rx_short_sync()
82+
.clear_bit()
83+
});
84+
85+
// ADC/LCD/camera configuration register
86+
regs.conf2().modify(|_, w| {
87+
// Enable LCD mode.
88+
w.lcd_en()
89+
.set_bit()
90+
// Enable camera mode.
91+
.camera_en()
92+
.set_bit()
93+
});
94+
95+
// Configure clock divider
96+
regs.clkm_conf().modify(|_, w| unsafe {
97+
w.clkm_div_a()
98+
.bits(0) // Fractional clock divider’s denominator value (6 bits)
99+
.clkm_div_b()
100+
.bits(0) // Fractional clock divider’s numerator value. (6 bits)
101+
.clkm_div_num()
102+
.bits(2) // I2S clock divider’s integral value. (8 bits)
103+
});
104+
105+
regs.fifo_conf().modify(|_, w| unsafe {
106+
// Enable I2S DMA mode.
107+
w.dscr_en()
108+
.set_bit()
109+
// Receive FIFO mode configuration bit. (3 bits)
110+
.rx_fifo_mod()
111+
.bits(data_format as _)
112+
// The bit should always be set to 1.
113+
.rx_fifo_mod_force_en()
114+
.set_bit()
115+
});
116+
117+
regs.conf_chan().modify(|_, w| unsafe {
118+
// I2S receiver channel mode configuration bit. (2 bits)
119+
w.rx_chan_mod().bits(1)
120+
});
121+
122+
// Configure the bit length of I2S receiver channel. (6 bits)
123+
regs.sample_rate_conf()
124+
.modify(|_, w| unsafe { w.rx_bits_mod().bits(16) });
125+
126+
// Synchronize signals into the receiver in double sync method.
127+
regs.timing().write(|w| w.rx_dsync_sw().set_bit());
128+
129+
// Default these signals to high in case user doesn't provide them.
130+
I2S::v_sync_signal().connect_to(Level::High);
131+
I2S::h_sync_signal().connect_to(Level::High);
132+
I2S::h_enable_signal().connect_to(Level::High);
133+
134+
let rx_channel = ChannelRx::new(channel.map(|ch| ch.degrade()));
135+
136+
Self {
137+
_i2s: i2s,
138+
rx_channel,
139+
}
140+
}
141+
}
142+
143+
impl<I2S> Camera<'_, I2S>
144+
where
145+
I2S: RegisterAccess + ExtendedSignals,
146+
{
147+
/// Configures the data pins for the camera interface.
148+
#[allow(clippy::too_many_arguments)]
149+
pub fn with_data_pins<
150+
D0: InputPin,
151+
D1: InputPin,
152+
D2: InputPin,
153+
D3: InputPin,
154+
D4: InputPin,
155+
D5: InputPin,
156+
D6: InputPin,
157+
D7: InputPin,
158+
>(
159+
self,
160+
d0: impl Peripheral<P = D0>,
161+
d1: impl Peripheral<P = D1>,
162+
d2: impl Peripheral<P = D2>,
163+
d3: impl Peripheral<P = D3>,
164+
d4: impl Peripheral<P = D4>,
165+
d5: impl Peripheral<P = D5>,
166+
d6: impl Peripheral<P = D6>,
167+
d7: impl Peripheral<P = D7>,
168+
) -> Self {
169+
crate::into_mapped_ref!(d0);
170+
crate::into_mapped_ref!(d1);
171+
crate::into_mapped_ref!(d2);
172+
crate::into_mapped_ref!(d3);
173+
crate::into_mapped_ref!(d4);
174+
crate::into_mapped_ref!(d5);
175+
crate::into_mapped_ref!(d6);
176+
crate::into_mapped_ref!(d7);
177+
178+
let pairs = [
179+
(d0, I2S::din0_signal()),
180+
(d1, I2S::din1_signal()),
181+
(d2, I2S::din2_signal()),
182+
(d3, I2S::din3_signal()),
183+
(d4, I2S::din4_signal()),
184+
(d5, I2S::din5_signal()),
185+
(d6, I2S::din6_signal()),
186+
(d7, I2S::din7_signal()),
187+
];
188+
189+
for (pin, signal) in pairs {
190+
pin.init_input(Pull::None);
191+
signal.connect_to(pin);
192+
}
193+
194+
self
195+
}
196+
197+
/// Configures the pixel clock pin for the camera interface.
198+
pub fn with_ws<PIN: InputPin>(self, pin: impl Peripheral<P = PIN>) -> Self {
199+
crate::into_mapped_ref!(pin);
200+
pin.init_input(Pull::None);
201+
I2S::ws_in_signal().connect_to(pin);
202+
self
203+
}
204+
205+
/// Configures the vertical sync (VSYNC) pin for the camera interface.
206+
pub fn with_vsync<PIN: InputPin>(self, pin: impl Peripheral<P = PIN>) -> Self {
207+
crate::into_mapped_ref!(pin);
208+
pin.init_input(Pull::None);
209+
I2S::v_sync_signal().connect_to(pin);
210+
self
211+
}
212+
213+
/// Configures the horizontal sync (HSYNC) pin for the camera interface.
214+
pub fn with_hsync<PIN: InputPin>(self, pin: impl Peripheral<P = PIN>) -> Self {
215+
crate::into_mapped_ref!(pin);
216+
pin.init_input(Pull::None);
217+
I2S::h_sync_signal().connect_to(pin);
218+
self
219+
}
220+
221+
/// Configures the vertical sync enable pin for the camera interface.
222+
pub fn with_henable<PIN: InputPin>(self, pin: impl Peripheral<P = PIN>) -> Self {
223+
crate::into_mapped_ref!(pin);
224+
pin.init_input(Pull::None);
225+
I2S::h_enable_signal().connect_to(pin);
226+
self
227+
}
228+
}
229+
230+
impl<'d, I2S> Camera<'d, I2S>
231+
where
232+
I2S: RegisterAccess,
233+
{
234+
fn start_rx_transfer<'t, RXBUF>(
235+
&'t mut self,
236+
buf: &'t mut RXBUF,
237+
len: usize,
238+
) -> Result<(), Error>
239+
where
240+
RXBUF: DmaRxBuffer,
241+
{
242+
if len % 4 != 0 {
243+
return Err(Error::IllegalArgument);
244+
}
245+
246+
// Reset RX unit and RX FIFO
247+
self._i2s.reset_rx();
248+
249+
// Enable corresponding interrupts if needed
250+
251+
// configure DMA outlink
252+
unsafe {
253+
self.rx_channel
254+
.prepare_transfer(self._i2s.dma_peripheral(), buf)
255+
.and_then(|_| self.rx_channel.start_transfer())?;
256+
}
257+
258+
// set I2S_RX_STOP_EN if needed
259+
260+
// start: set I2S_RX_START
261+
self._i2s.rx_start(len);
262+
Ok(())
263+
}
264+
265+
/// Starts a DMA transfer to receive data from the camera peripheral.
266+
pub fn receive<BUF: DmaRxBuffer>(
267+
mut self,
268+
mut buf: BUF,
269+
len: usize,
270+
) -> Result<CameraTransfer<'d, I2S, BUF>, Error> {
271+
self.start_rx_transfer(&mut buf, len)?;
272+
Ok(CameraTransfer {
273+
camera: ManuallyDrop::new(self),
274+
buffer_view: ManuallyDrop::new(buf.into_view()),
275+
})
276+
}
277+
}
278+
279+
/// Represents an ongoing (or potentially stopped) transfer from the Camera to a
280+
/// DMA buffer.
281+
pub struct CameraTransfer<'d, I2S: DmaEligible + RegisterAccess, BUF: DmaRxBuffer> {
282+
camera: ManuallyDrop<Camera<'d, I2S>>,
283+
buffer_view: ManuallyDrop<BUF::View>,
284+
}
285+
286+
impl<'d, I2S: RegisterAccess, BUF: DmaRxBuffer> CameraTransfer<'d, I2S, BUF> {
287+
/// Returns true when [Self::wait] will not block.
288+
pub fn is_done(&self) -> bool {
289+
self.camera.rx_channel.has_dscr_empty_error() // IN_DSCR_EMPTY (i.e. No more buffer space)
290+
|| self.camera.rx_channel.has_error() // IN_DSCR_ERR (i.e. bad
291+
// descriptor)
292+
}
293+
294+
/// Stops this transfer on the spot and returns the peripheral and buffer.
295+
pub fn stop(mut self) -> (Camera<'d, I2S>, BUF) {
296+
self.stop_peripherals();
297+
let (camera, view) = self.release();
298+
(camera, BUF::from_view(view))
299+
}
300+
301+
/// Waits for the transfer to stop and returns the peripheral and buffer.
302+
///
303+
/// Note: The camera doesn't really "finish" its transfer, so what you're
304+
/// really waiting for here is a DMA Error. You typically just want to
305+
/// call [Self::stop] once you have the data you need.
306+
pub fn wait(mut self) -> (Result<(), DmaError>, Camera<'d, I2S>, BUF) {
307+
while !self.is_done() {}
308+
309+
// Stop the DMA as it doesn't know that the camera has stopped.
310+
self.camera.rx_channel.stop_transfer();
311+
312+
// Note: There is no "done" interrupt to clear.
313+
314+
let (camera, view) = self.release();
315+
316+
let result = if camera.rx_channel.has_error() {
317+
Err(DmaError::DescriptorError)
318+
} else {
319+
Ok(())
320+
};
321+
322+
(result, camera, BUF::from_view(view))
323+
}
324+
325+
fn release(mut self) -> (Camera<'d, I2S>, BUF::View) {
326+
// SAFETY: Since forget is called on self, we know that self.camera and
327+
// self.buffer_view won't be touched again.
328+
let result = unsafe {
329+
let camera = ManuallyDrop::take(&mut self.camera);
330+
let view = ManuallyDrop::take(&mut self.buffer_view);
331+
(camera, view)
332+
};
333+
core::mem::forget(self);
334+
result
335+
}
336+
337+
fn stop_peripherals(&mut self) {
338+
self.camera._i2s.rx_stop();
339+
}
340+
}
341+
342+
impl<I2S: DmaEligible + RegisterAccess, BUF: DmaRxBuffer> Deref for CameraTransfer<'_, I2S, BUF> {
343+
type Target = BUF::View;
344+
345+
fn deref(&self) -> &Self::Target {
346+
&self.buffer_view
347+
}
348+
}
349+
350+
impl<I2S: DmaEligible + RegisterAccess, BUF: DmaRxBuffer> DerefMut
351+
for CameraTransfer<'_, I2S, BUF>
352+
{
353+
fn deref_mut(&mut self) -> &mut Self::Target {
354+
&mut self.buffer_view
355+
}
356+
}
357+
358+
impl<I2S: DmaEligible + RegisterAccess, BUF: DmaRxBuffer> Drop for CameraTransfer<'_, I2S, BUF> {
359+
fn drop(&mut self) {
360+
self.stop_peripherals();
361+
362+
// SAFETY: This is Drop, we know that self.camera and self.buffer_view
363+
// won't be touched again.
364+
unsafe {
365+
ManuallyDrop::drop(&mut self.camera);
366+
ManuallyDrop::drop(&mut self.buffer_view);
367+
}
368+
}
369+
}

0 commit comments

Comments
 (0)