Skip to content

Commit feb9db6

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 <[email protected]>
1 parent 7fd2659 commit feb9db6

File tree

3 files changed

+1075
-0
lines changed

3 files changed

+1075
-0
lines changed

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

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

0 commit comments

Comments
 (0)