Skip to content

Commit fc079f9

Browse files
Dominaezzzrursprung
andcommitted
I2S camera - WIP: untested
TODOs: * test with hardware mentioned in `i2s_camera.rs` * test with ESP32-CAM board + OV2640 * fix FIXMEs (missing docs) * fix formatting (import order!) Co-Authored-By: Ralph Ursprung <ralph.ursprung@gmail.com>
1 parent fe2a126 commit fc079f9

File tree

3 files changed

+1068
-0
lines changed

3 files changed

+1068
-0
lines changed

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

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

0 commit comments

Comments
 (0)