aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2021-12-09 12:42:13 +0000
committerGitHub <[email protected]>2021-12-09 12:42:13 +0000
commit45a82cfc4319a2351c08ff0b3de1639d901ec651 (patch)
tree3440133aa395daebb4dbb19feda35a9fb371a26d
parent08c84761453b3d5f3b13849b4923ce1f36fdabe7 (diff)
parent484c356c030d074ef0339cee66440bcc252ff1d4 (diff)
Merge #490
490: DCMI r=matoushybl a=matoushybl Co-authored-by: Matous Hybl <[email protected]>
-rw-r--r--embassy-stm32/src/dcmi.rs481
-rw-r--r--embassy-stm32/src/lib.rs2
-rw-r--r--embassy-stm32/src/rcc/h7/mod.rs5
-rw-r--r--examples/stm32h7/Cargo.toml40
-rw-r--r--examples/stm32h7/src/bin/camera.rs339
5 files changed, 866 insertions, 1 deletions
diff --git a/embassy-stm32/src/dcmi.rs b/embassy-stm32/src/dcmi.rs
new file mode 100644
index 000000000..2d2ad72e4
--- /dev/null
+++ b/embassy-stm32/src/dcmi.rs
@@ -0,0 +1,481 @@
1use core::marker::PhantomData;
2use core::task::Poll;
3
4use crate::gpio::sealed::Pin as __GpioPin;
5use crate::gpio::Pin as GpioPin;
6use embassy::interrupt::{Interrupt, InterruptExt};
7use embassy::util::Unborrow;
8use embassy::waitqueue::AtomicWaker;
9use embassy_hal_common::unborrow;
10use futures::future::poll_fn;
11
12/// The level on the VSync pin when the data is not valid on the parallel interface.
13#[derive(Clone, Copy, PartialEq)]
14pub enum VSyncDataInvalidLevel {
15 Low,
16 High,
17}
18
19/// The level on the VSync pin when the data is not valid on the parallel interface.
20#[derive(Clone, Copy, PartialEq)]
21pub enum HSyncDataInvalidLevel {
22 Low,
23 High,
24}
25
26#[derive(Clone, Copy, PartialEq)]
27pub enum PixelClockPolarity {
28 RisingEdge,
29 FallingEdge,
30}
31
32pub struct State {
33 waker: AtomicWaker,
34}
35impl State {
36 const fn new() -> State {
37 State {
38 waker: AtomicWaker::new(),
39 }
40 }
41}
42
43static STATE: State = State::new();
44
45#[derive(Debug, Eq, PartialEq, Copy, Clone)]
46#[cfg_attr(feature = "defmt", derive(defmt::Format))]
47#[non_exhaustive]
48pub enum Error {
49 Overrun,
50 PeripheralError,
51}
52
53pub struct Dcmi<'d, T: Instance, Dma: FrameDma> {
54 inner: T,
55 dma: Dma,
56 phantom: PhantomData<&'d mut T>,
57}
58
59impl<'d, T, Dma> Dcmi<'d, T, Dma>
60where
61 T: Instance,
62 Dma: FrameDma,
63{
64 pub fn new(
65 peri: impl Unborrow<Target = T> + 'd,
66 dma: impl Unborrow<Target = Dma> + 'd,
67 vsync_level: VSyncDataInvalidLevel,
68 hsync_level: HSyncDataInvalidLevel,
69 pixclk_polarity: PixelClockPolarity,
70 use_embedded_synchronization: bool,
71 irq: impl Unborrow<Target = T::Interrupt> + 'd,
72 d0: impl Unborrow<Target = impl D0Pin> + 'd,
73 d1: impl Unborrow<Target = impl D1Pin> + 'd,
74 d2: impl Unborrow<Target = impl D2Pin> + 'd,
75 d3: impl Unborrow<Target = impl D3Pin> + 'd,
76 d4: impl Unborrow<Target = impl D4Pin> + 'd,
77 d5: impl Unborrow<Target = impl D5Pin> + 'd,
78 d6: impl Unborrow<Target = impl D6Pin> + 'd,
79 d7: impl Unborrow<Target = impl D7Pin> + 'd,
80 d8: impl Unborrow<Target = impl D8Pin> + 'd,
81 d9: impl Unborrow<Target = impl D9Pin> + 'd,
82 d10: impl Unborrow<Target = impl D10Pin> + 'd,
83 d11: impl Unborrow<Target = impl D11Pin> + 'd,
84 d12: impl Unborrow<Target = impl D12Pin> + 'd,
85 d13: impl Unborrow<Target = impl D13Pin> + 'd,
86 v_sync: impl Unborrow<Target = impl VSyncPin> + 'd,
87 h_sync: impl Unborrow<Target = impl HSyncPin> + 'd,
88 pixclk: impl Unborrow<Target = impl PixClkPin> + 'd,
89 ) -> Self {
90 T::reset();
91 T::enable();
92
93 unborrow!(
94 peri, dma, irq, d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, v_sync,
95 h_sync, pixclk
96 );
97
98 d0.configure();
99 d1.configure();
100 d2.configure();
101 d3.configure();
102 d4.configure();
103 d5.configure();
104 d6.configure();
105 d7.configure();
106 d8.configure();
107 d9.configure();
108 d10.configure();
109 d11.configure();
110 d12.configure();
111 d13.configure();
112
113 v_sync.configure();
114 h_sync.configure();
115 pixclk.configure();
116
117 let edm = match (
118 d8.pin().is_some(),
119 d9.pin().is_some(),
120 d10.pin().is_some(),
121 d11.pin().is_some(),
122 d12.pin().is_some(),
123 d13.pin().is_some(),
124 ) {
125 (true, true, true, true, true, true) => 0b11, // 14 bits
126 (true, true, true, true, false, false) => 0b10, // 12 bits
127 (true, true, false, false, false, false) => 0b01, // 10 bits
128 (false, false, false, false, false, false) => 0b00, // 8 bits
129 _ => {
130 panic!("Invalid pin configuration.");
131 }
132 };
133
134 unsafe {
135 peri.regs().cr().modify(|r| {
136 r.set_cm(true); // disable continuous mode (snapshot mode)
137 r.set_ess(use_embedded_synchronization);
138 r.set_pckpol(pixclk_polarity == PixelClockPolarity::RisingEdge);
139 r.set_vspol(vsync_level == VSyncDataInvalidLevel::High);
140 r.set_hspol(hsync_level == HSyncDataInvalidLevel::High);
141 r.set_fcrc(0x00); // capture every frame
142 r.set_edm(edm); // extended data mode
143 });
144 }
145
146 irq.set_handler(Self::on_interrupt);
147 irq.unpend();
148 irq.enable();
149
150 Self {
151 inner: peri,
152 dma,
153 phantom: PhantomData,
154 }
155 }
156
157 unsafe fn on_interrupt(_: *mut ()) {
158 let ris = crate::pac::DCMI.ris().read();
159 if ris.err_ris() {
160 error!("DCMI IRQ: Error.");
161 crate::pac::DCMI.ier().modify(|ier| ier.set_err_ie(false));
162 }
163 if ris.ovr_ris() {
164 error!("DCMI IRQ: Overrun.");
165 crate::pac::DCMI.ier().modify(|ier| ier.set_ovr_ie(false));
166 }
167 if ris.frame_ris() {
168 info!("DCMI IRQ: Frame captured.");
169 crate::pac::DCMI.ier().modify(|ier| ier.set_frame_ie(false));
170 }
171 STATE.waker.wake();
172 }
173
174 unsafe fn toggle(enable: bool) {
175 crate::pac::DCMI.cr().modify(|r| {
176 r.set_enable(enable);
177 r.set_capture(enable);
178 })
179 }
180
181 fn enable_irqs() {
182 unsafe {
183 crate::pac::DCMI.ier().modify(|r| {
184 r.set_err_ie(true);
185 r.set_ovr_ie(true);
186 r.set_frame_ie(true);
187 });
188 }
189 }
190
191 fn clear_interrupt_flags() {
192 unsafe {
193 crate::pac::DCMI.icr().write(|r| {
194 r.set_ovr_isc(true);
195 r.set_err_isc(true);
196 r.set_frame_isc(true);
197 })
198 }
199 }
200
201 /// This method starts the capture and finishes when both the dma transfer and DCMI finish the frame transfer.
202 /// The implication is that the input buffer size must be exactly the size of the captured frame.
203 pub async fn capture(&mut self, buffer: &mut [u32]) -> Result<(), Error> {
204 let channel = &mut self.dma;
205 let request = channel.request();
206
207 let r = self.inner.regs();
208 let src = r.dr().ptr() as *mut u32;
209 let dma_read = crate::dma::read(channel, request, src, buffer);
210
211 Self::clear_interrupt_flags();
212 Self::enable_irqs();
213
214 unsafe { Self::toggle(true) };
215
216 let result = poll_fn(|cx| {
217 STATE.waker.register(cx.waker());
218
219 let ris = unsafe { crate::pac::DCMI.ris().read() };
220 if ris.err_ris() {
221 unsafe {
222 crate::pac::DCMI.icr().write(|r| {
223 r.set_err_isc(true);
224 })
225 };
226 Poll::Ready(Err(Error::PeripheralError))
227 } else if ris.ovr_ris() {
228 unsafe {
229 crate::pac::DCMI.icr().write(|r| {
230 r.set_ovr_isc(true);
231 })
232 };
233 Poll::Ready(Err(Error::Overrun))
234 } else if ris.frame_ris() {
235 unsafe {
236 crate::pac::DCMI.icr().write(|r| {
237 r.set_frame_isc(true);
238 })
239 };
240 Poll::Ready(Ok(()))
241 } else {
242 Poll::Pending
243 }
244 });
245
246 let (_, result) = futures::future::join(dma_read, result).await;
247
248 unsafe { Self::toggle(false) };
249
250 result
251 }
252}
253
254mod sealed {
255 use super::*;
256 use crate::rcc::RccPeripheral;
257
258 pub trait Instance: RccPeripheral {
259 fn regs(&self) -> crate::pac::dcmi::Dcmi;
260 }
261
262 pub trait FrameDma {
263 fn request(&self) -> crate::dma::Request;
264 }
265
266 macro_rules! pin {
267 ($name:ident) => {
268 pub trait $name: GpioPin {
269 fn configure(&mut self);
270 }
271 };
272 }
273
274 macro_rules! optional_pin {
275 ($name:ident) => {
276 pub trait $name: crate::gpio::OptionalPin {
277 fn configure(&mut self);
278 }
279 };
280 }
281
282 pin!(D0Pin);
283 pin!(D1Pin);
284 pin!(D2Pin);
285 pin!(D3Pin);
286 pin!(D4Pin);
287 pin!(D5Pin);
288 pin!(D6Pin);
289 pin!(D7Pin);
290 optional_pin!(D8Pin);
291 optional_pin!(D9Pin);
292 optional_pin!(D10Pin);
293 optional_pin!(D11Pin);
294 optional_pin!(D12Pin);
295 optional_pin!(D13Pin);
296
297 optional_pin!(HSyncPin);
298 optional_pin!(VSyncPin);
299 pin!(PixClkPin);
300}
301
302pub trait Instance: sealed::Instance + 'static {
303 type Interrupt: Interrupt;
304}
305
306pub trait FrameDma: sealed::FrameDma + crate::dma::Channel {}
307
308macro_rules! pin {
309 ($name:ident) => {
310 pub trait $name: sealed::$name + 'static {}
311 };
312}
313
314pin!(D0Pin);
315pin!(D1Pin);
316pin!(D2Pin);
317pin!(D3Pin);
318pin!(D4Pin);
319pin!(D5Pin);
320pin!(D6Pin);
321pin!(D7Pin);
322pin!(D8Pin);
323pin!(D9Pin);
324pin!(D10Pin);
325pin!(D11Pin);
326pin!(D12Pin);
327pin!(D13Pin);
328
329pin!(HSyncPin);
330pin!(VSyncPin);
331pin!(PixClkPin);
332
333// allow unused as U5 sources do not contain interrupt nor dma data
334#[allow(unused)]
335macro_rules! impl_peripheral {
336 ($inst:ident, $irq:ident) => {
337 impl sealed::Instance for crate::peripherals::$inst {
338 fn regs(&self) -> crate::pac::dcmi::Dcmi {
339 crate::pac::$inst
340 }
341 }
342
343 impl Instance for crate::peripherals::$inst {
344 type Interrupt = crate::interrupt::$irq;
345 }
346 };
347}
348
349crate::pac::interrupts! {
350 ($inst:ident, dcmi, $block:ident, GLOBAL, $irq:ident) => {
351 impl_peripheral!($inst, $irq);
352 };
353}
354
355// allow unused as U5 sources do not contain interrupt nor dma data
356#[allow(unused)]
357macro_rules! impl_dma {
358 ($inst:ident, {dmamux: $dmamux:ident}, $signal:ident, $request:expr) => {
359 impl<T> sealed::$signal for T
360 where
361 T: crate::dma::MuxChannel<Mux = crate::dma::$dmamux>,
362 {
363 fn request(&self) -> crate::dma::Request {
364 $request
365 }
366 }
367
368 impl<T> $signal for T where T: crate::dma::MuxChannel<Mux = crate::dma::$dmamux> {}
369 };
370 ($inst:ident, {channel: $channel:ident}, $signal:ident, $request:expr) => {
371 impl sealed::$signal for crate::peripherals::$channel {
372 fn request(&self) -> crate::dma::Request {
373 $request
374 }
375 }
376
377 impl $signal for crate::peripherals::$channel {}
378 };
379}
380
381crate::pac::peripheral_dma_channels! {
382 ($peri:ident, dcmi, $kind:ident, PSSI, $channel:tt, $request:expr) => {
383 impl_dma!($peri, $channel, FrameDma, $request);
384 };
385 ($peri:ident, dcmi, $kind:ident, DCMI, $channel:tt, $request:expr) => {
386 impl_dma!($peri, $channel, FrameDma, $request);
387 };
388}
389
390macro_rules! impl_pin {
391 ($pin:ident, $signal:ident, $af:expr) => {
392 impl sealed::$signal for crate::peripherals::$pin {
393 fn configure(&mut self) {
394 // NOTE(unsafe) Exclusive access to the registers
395 critical_section::with(|_| unsafe {
396 self.set_as_af($af, crate::gpio::sealed::AFType::Input);
397 self.block().ospeedr().modify(|w| {
398 w.set_ospeedr(
399 self.pin() as usize,
400 crate::pac::gpio::vals::Ospeedr::VERYHIGHSPEED,
401 )
402 });
403 })
404 }
405 }
406
407 impl $signal for crate::peripherals::$pin {}
408 };
409}
410
411macro_rules! impl_no_pin {
412 ($signal:ident) => {
413 impl sealed::$signal for crate::gpio::NoPin {
414 fn configure(&mut self) {}
415 }
416 impl $signal for crate::gpio::NoPin {}
417 };
418}
419
420impl_no_pin!(D8Pin);
421impl_no_pin!(D9Pin);
422impl_no_pin!(D10Pin);
423impl_no_pin!(D11Pin);
424impl_no_pin!(D12Pin);
425impl_no_pin!(D13Pin);
426impl_no_pin!(HSyncPin);
427impl_no_pin!(VSyncPin);
428
429crate::pac::peripheral_pins!(
430 ($inst:ident, dcmi, DCMI, $pin:ident, D0, $af:expr) => {
431 impl_pin!($pin, D0Pin, $af);
432 };
433 ($inst:ident, dcmi, DCMI, $pin:ident, D1, $af:expr) => {
434 impl_pin!($pin, D1Pin, $af);
435 };
436 ($inst:ident, dcmi, DCMI, $pin:ident, D2, $af:expr) => {
437 impl_pin!($pin, D2Pin, $af);
438 };
439 ($inst:ident, dcmi, DCMI, $pin:ident, D3, $af:expr) => {
440 impl_pin!($pin, D3Pin, $af);
441 };
442 ($inst:ident, dcmi, DCMI, $pin:ident, D4, $af:expr) => {
443 impl_pin!($pin, D4Pin, $af);
444 };
445 ($inst:ident, dcmi, DCMI, $pin:ident, D5, $af:expr) => {
446 impl_pin!($pin, D5Pin, $af);
447 };
448 ($inst:ident, dcmi, DCMI, $pin:ident, D6, $af:expr) => {
449 impl_pin!($pin, D6Pin, $af);
450 };
451 ($inst:ident, dcmi, DCMI, $pin:ident, D7, $af:expr) => {
452 impl_pin!($pin, D7Pin, $af);
453 };
454 ($inst:ident, dcmi, DCMI, $pin:ident, D8, $af:expr) => {
455 impl_pin!($pin, D8Pin, $af);
456 };
457 ($inst:ident, dcmi, DCMI, $pin:ident, D9, $af:expr) => {
458 impl_pin!($pin, D9Pin, $af);
459 };
460 ($inst:ident, dcmi, DCMI, $pin:ident, D10, $af:expr) => {
461 impl_pin!($pin, D10Pin, $af);
462 };
463 ($inst:ident, dcmi, DCMI, $pin:ident, D11, $af:expr) => {
464 impl_pin!($pin, D11Pin, $af);
465 };
466 ($inst:ident, dcmi, DCMI, $pin:ident, D12, $af:expr) => {
467 impl_pin!($pin, D12Pin, $af);
468 };
469 ($inst:ident, dcmi, DCMI, $pin:ident, D13, $af:expr) => {
470 impl_pin!($pin, D13Pin, $af);
471 };
472 ($inst:ident, dcmi, DCMI, $pin:ident, HSYNC, $af:expr) => {
473 impl_pin!($pin, HSyncPin, $af);
474 };
475 ($inst:ident, dcmi, DCMI, $pin:ident, VSYNC, $af:expr) => {
476 impl_pin!($pin, VSyncPin, $af);
477 };
478 ($inst:ident, dcmi, DCMI, $pin:ident, PIXCLK, $af:expr) => {
479 impl_pin!($pin, PixClkPin, $af);
480 };
481);
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs
index 649b25f10..425516a3f 100644
--- a/embassy-stm32/src/lib.rs
+++ b/embassy-stm32/src/lib.rs
@@ -32,6 +32,8 @@ pub mod can;
32pub mod dac; 32pub mod dac;
33#[cfg(dbgmcu)] 33#[cfg(dbgmcu)]
34pub mod dbgmcu; 34pub mod dbgmcu;
35#[cfg(dcmi)]
36pub mod dcmi;
35#[cfg(all(eth, feature = "net"))] 37#[cfg(all(eth, feature = "net"))]
36pub mod eth; 38pub mod eth;
37#[cfg(exti)] 39#[cfg(exti)]
diff --git a/embassy-stm32/src/rcc/h7/mod.rs b/embassy-stm32/src/rcc/h7/mod.rs
index dc458a8a3..be7f440a1 100644
--- a/embassy-stm32/src/rcc/h7/mod.rs
+++ b/embassy-stm32/src/rcc/h7/mod.rs
@@ -73,6 +73,7 @@ pub struct Config {
73 pub pll2: PllConfig, 73 pub pll2: PllConfig,
74 pub pll3: PllConfig, 74 pub pll3: PllConfig,
75 pub enable_dma1: bool, 75 pub enable_dma1: bool,
76 pub enable_dma2: bool,
76} 77}
77 78
78pub struct Rcc<'d> { 79pub struct Rcc<'d> {
@@ -334,6 +335,10 @@ impl<'d> Rcc<'d> {
334 RCC.ahb1enr().modify(|w| w.set_dma1en(true)); 335 RCC.ahb1enr().modify(|w| w.set_dma1en(true));
335 } 336 }
336 337
338 if self.config.enable_dma2 {
339 RCC.ahb1enr().modify(|w| w.set_dma2en(true));
340 }
341
337 CoreClocks { 342 CoreClocks {
338 hclk: Hertz(rcc_hclk), 343 hclk: Hertz(rcc_hclk),
339 pclk1: Hertz(rcc_pclk1), 344 pclk1: Hertz(rcc_pclk1),
diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml
index dc31c6b52..393e779e4 100644
--- a/examples/stm32h7/Cargo.toml
+++ b/examples/stm32h7/Cargo.toml
@@ -30,4 +30,42 @@ micromath = "2.0.0"
30git = "https://github.com/smoltcp-rs/smoltcp" 30git = "https://github.com/smoltcp-rs/smoltcp"
31rev = "3644b94b82d9433313c75281fdc78942c2450bdf" 31rev = "3644b94b82d9433313c75281fdc78942c2450bdf"
32default-features = false 32default-features = false
33features = ["defmt"] \ No newline at end of file 33features = ["defmt"]
34
35# cargo build/run
36[profile.dev]
37codegen-units = 1
38debug = 2
39debug-assertions = true # <-
40incremental = false
41opt-level = 3 # <-
42overflow-checks = true # <-
43
44# cargo test
45[profile.test]
46codegen-units = 1
47debug = 2
48debug-assertions = true # <-
49incremental = false
50opt-level = 3 # <-
51overflow-checks = true # <-
52
53# cargo build/run --release
54[profile.release]
55codegen-units = 1
56debug = 2
57debug-assertions = false # <-
58incremental = false
59lto = 'fat'
60opt-level = 3 # <-
61overflow-checks = false # <-
62
63# cargo test --release
64[profile.bench]
65codegen-units = 1
66debug = 2
67debug-assertions = false # <-
68incremental = false
69lto = 'fat'
70opt-level = 3 # <-
71overflow-checks = false # <- \ No newline at end of file
diff --git a/examples/stm32h7/src/bin/camera.rs b/examples/stm32h7/src/bin/camera.rs
new file mode 100644
index 000000000..2fa742b83
--- /dev/null
+++ b/examples/stm32h7/src/bin/camera.rs
@@ -0,0 +1,339 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use embassy::executor::Spawner;
6use embassy::time::{Duration, Timer};
7use embassy_stm32::dcmi::*;
8use embassy_stm32::gpio::{Level, NoPin, Output, Speed};
9use embassy_stm32::i2c::I2c;
10use embassy_stm32::interrupt;
11use embassy_stm32::rcc::{Mco, Mco1Source, McoClock};
12use embassy_stm32::time::U32Ext;
13use embassy_stm32::Peripherals;
14use embedded_hal::digital::v2::OutputPin;
15
16use defmt_rtt as _; // global logger
17use panic_probe as _;
18
19use core::sync::atomic::{AtomicUsize, Ordering};
20use embassy_stm32::Config;
21
22defmt::timestamp! {"{=u64}", {
23 static COUNT: AtomicUsize = AtomicUsize::new(0);
24 // NOTE(no-CAS) `timestamps` runs with interrupts disabled
25 let n = COUNT.load(Ordering::Relaxed);
26 COUNT.store(n + 1, Ordering::Relaxed);
27 n as u64
28 }
29}
30
31#[allow(unused)]
32pub fn config() -> Config {
33 let mut config = Config::default();
34 config.rcc.sys_ck = Some(400.mhz().into());
35 config.rcc.hclk = Some(400.mhz().into());
36 config.rcc.pll1.q_ck = Some(100.mhz().into());
37 config.rcc.enable_dma1 = true;
38 config.rcc.enable_dma2 = true;
39 config.rcc.pclk1 = Some(100.mhz().into());
40 config.rcc.pclk2 = Some(100.mhz().into());
41 config.rcc.pclk3 = Some(100.mhz().into());
42 config.rcc.pclk4 = Some(100.mhz().into());
43 config
44}
45
46use ov7725::*;
47
48const WIDTH: usize = 100;
49const HEIGHT: usize = 100;
50
51static mut FRAME: [u32; WIDTH * HEIGHT / 2] = [0u32; WIDTH * HEIGHT / 2];
52
53#[embassy::main(config = "config()")]
54async fn main(_spawner: Spawner, p: Peripherals) {
55 defmt::info!("Hello World!");
56 let mco = Mco::new(p.MCO1, p.PA8, Mco1Source::Hsi, McoClock::Divided(3));
57
58 let mut led = Output::new(p.PE3, Level::High, Speed::Low);
59 let i2c_irq = interrupt::take!(I2C1_EV);
60 let cam_i2c = I2c::new(
61 p.I2C1,
62 p.PB8,
63 p.PB9,
64 i2c_irq,
65 p.DMA1_CH1,
66 p.DMA1_CH2,
67 100u32.khz(),
68 );
69
70 let mut camera = Ov7725::new(cam_i2c, mco);
71
72 defmt::unwrap!(camera.init().await);
73
74 let manufacturer_id = defmt::unwrap!(camera.read_manufacturer_id().await);
75 let camera_id = defmt::unwrap!(camera.read_product_id().await);
76
77 defmt::info!(
78 "manufacturer: 0x{:x}, pid: 0x{:x}",
79 manufacturer_id,
80 camera_id
81 );
82
83 let dcmi_irq = interrupt::take!(DCMI);
84 let mut dcmi = Dcmi::new(
85 p.DCMI,
86 p.DMA1_CH0,
87 VSyncDataInvalidLevel::High,
88 HSyncDataInvalidLevel::Low,
89 PixelClockPolarity::RisingEdge,
90 false,
91 dcmi_irq,
92 p.PC6,
93 p.PC7,
94 p.PE0,
95 p.PE1,
96 p.PE4,
97 p.PD3,
98 p.PE5,
99 p.PE6,
100 NoPin,
101 NoPin,
102 NoPin,
103 NoPin,
104 NoPin,
105 NoPin,
106 p.PB7,
107 p.PA4,
108 p.PA6,
109 );
110
111 defmt::info!("attempting capture");
112 defmt::unwrap!(dcmi.capture(unsafe { &mut FRAME }).await);
113
114 defmt::info!("captured frame: {:x}", unsafe { &FRAME });
115
116 defmt::info!("main loop running");
117 loop {
118 defmt::info!("high");
119 defmt::unwrap!(led.set_high());
120 Timer::after(Duration::from_millis(500)).await;
121
122 defmt::info!("low");
123 defmt::unwrap!(led.set_low());
124 Timer::after(Duration::from_millis(500)).await;
125 }
126}
127
128mod ov7725 {
129 use core::marker::PhantomData;
130
131 use defmt::Format;
132 use embassy::time::{Duration, Timer};
133 use embassy_stm32::rcc::{Mco, McoInstance};
134 use embassy_traits::i2c::I2c;
135
136 #[repr(u8)]
137 pub enum RgbFormat {
138 Gbr422 = 0,
139 RGB565 = 1,
140 RGB555 = 2,
141 RGB444 = 3,
142 }
143 pub enum PixelFormat {
144 Yuv,
145 ProcessedRawBayer,
146 Rgb(RgbFormat),
147 RawBayer,
148 }
149
150 impl From<PixelFormat> for u8 {
151 fn from(raw: PixelFormat) -> Self {
152 match raw {
153 PixelFormat::Yuv => 0,
154 PixelFormat::ProcessedRawBayer => 1,
155 PixelFormat::Rgb(mode) => 2 | ((mode as u8) << 2),
156 PixelFormat::RawBayer => 3,
157 }
158 }
159 }
160
161 #[derive(Clone, Copy)]
162 #[repr(u8)]
163 #[allow(unused)]
164 pub enum Register {
165 Gain = 0x00,
166 Blue = 0x01,
167 Red = 0x02,
168 Green = 0x03,
169 BAvg = 0x05,
170 GAvg = 0x06,
171 RAvg = 0x07,
172 Aech = 0x08,
173 Com2 = 0x09,
174 PId = 0x0a,
175 Ver = 0x0b,
176 Com3 = 0x0c,
177 Com4 = 0x0d,
178 Com5 = 0x0e,
179 Com6 = 0x0f,
180 Aec = 0x10,
181 ClkRc = 0x11,
182 Com7 = 0x12,
183 Com8 = 0x13,
184 Com9 = 0x14,
185 Com10 = 0x15,
186 Reg16 = 0x16,
187 HStart = 0x17,
188 HSize = 0x18,
189 VStart = 0x19,
190 VSize = 0x1a,
191 PShift = 0x1b,
192 MidH = 0x1c,
193 MidL = 0x1d,
194 Laec = 0x1f,
195 Com11 = 0x20,
196 BdBase = 0x22,
197 BdMStep = 0x23,
198 Aew = 0x24,
199 Aeb = 0x25,
200 Vpt = 0x26,
201 Reg28 = 0x28,
202 HOutSize = 0x29,
203 EXHCH = 0x2a,
204 EXHCL = 0x2b,
205 VOutSize = 0x2c,
206 Advfl = 0x2d,
207 Advfh = 0x2e,
208 Yave = 0x2f,
209 LumHTh = 0x30,
210 LumLTh = 0x31,
211 HRef = 0x32,
212 DspCtrl4 = 0x67,
213 DspAuto = 0xac,
214 }
215
216 const CAM_ADDR: u8 = 0x21;
217
218 #[derive(Format)]
219 pub enum Error<I2cError: Format> {
220 I2c(I2cError),
221 }
222
223 pub struct Ov7725<'d, Bus: I2c> {
224 phantom: PhantomData<&'d ()>,
225 bus: Bus,
226 }
227
228 impl<'d, Bus> Ov7725<'d, Bus>
229 where
230 Bus: I2c,
231 Bus::Error: Format,
232 {
233 pub fn new<T>(bus: Bus, _mco: Mco<T>) -> Self
234 where
235 T: McoInstance,
236 {
237 Self {
238 phantom: PhantomData,
239 bus,
240 }
241 }
242
243 pub async fn init(&mut self) -> Result<(), Error<Bus::Error>> {
244 Timer::after(Duration::from_millis(500)).await;
245 self.reset_regs().await?;
246 Timer::after(Duration::from_millis(500)).await;
247 self.set_pixformat().await?;
248 self.set_resolution().await?;
249 Ok(())
250 }
251
252 pub async fn read_manufacturer_id(&mut self) -> Result<u16, Error<Bus::Error>> {
253 Ok(u16::from_le_bytes([
254 self.read(Register::MidL).await?,
255 self.read(Register::MidH).await?,
256 ]))
257 }
258
259 pub async fn read_product_id(&mut self) -> Result<u16, Error<Bus::Error>> {
260 Ok(u16::from_le_bytes([
261 self.read(Register::Ver).await?,
262 self.read(Register::PId).await?,
263 ]))
264 }
265
266 async fn reset_regs(&mut self) -> Result<(), Error<Bus::Error>> {
267 self.write(Register::Com7, 0x80).await
268 }
269
270 async fn set_pixformat(&mut self) -> Result<(), Error<Bus::Error>> {
271 self.write(Register::DspCtrl4, 0).await?;
272 let mut com7 = self.read(Register::Com7).await?;
273 com7 |= u8::from(PixelFormat::Rgb(RgbFormat::RGB565));
274 self.write(Register::Com7, com7).await?;
275 Ok(())
276 }
277
278 async fn set_resolution(&mut self) -> Result<(), Error<Bus::Error>> {
279 let horizontal: u16 = super::WIDTH as u16;
280 let vertical: u16 = super::HEIGHT as u16;
281
282 let h_high = (horizontal >> 2) as u8;
283 let v_high = (vertical >> 1) as u8;
284 let h_low = (horizontal & 0x03) as u8;
285 let v_low = (vertical & 0x01) as u8;
286
287 self.write(Register::HOutSize, h_high).await?;
288 self.write(Register::VOutSize, v_high).await?;
289 self.write(Register::EXHCH, h_low | (v_low << 2)).await?;
290
291 self.write(Register::Com3, 0xd1).await?;
292
293 let com3 = self.read(Register::Com3).await?;
294 let vflip = com3 & 0x80 > 0;
295
296 self.modify(Register::HRef, |reg| {
297 reg & 0xbf | if vflip { 0x40 } else { 0x40 }
298 })
299 .await?;
300
301 if horizontal <= 320 || vertical <= 240 {
302 self.write(Register::HStart, 0x3f).await?;
303 self.write(Register::HSize, 0x50).await?;
304 self.write(Register::VStart, 0x02).await?; // TODO vflip is subtracted in the original code
305 self.write(Register::VSize, 0x78).await?;
306 } else {
307 defmt::panic!("VGA resolutions not yet supported.");
308 }
309
310 Ok(())
311 }
312
313 async fn read(&mut self, register: Register) -> Result<u8, Error<Bus::Error>> {
314 let mut buffer = [0u8; 1];
315 self.bus
316 .write_read(CAM_ADDR, &[register as u8], &mut buffer[..1])
317 .await
318 .map_err(Error::I2c)?;
319 Ok(buffer[0])
320 }
321
322 async fn write(&mut self, register: Register, value: u8) -> Result<(), Error<Bus::Error>> {
323 self.bus
324 .write(CAM_ADDR, &[register as u8, value])
325 .await
326 .map_err(Error::I2c)
327 }
328
329 async fn modify<F: FnOnce(u8) -> u8>(
330 &mut self,
331 register: Register,
332 f: F,
333 ) -> Result<(), Error<Bus::Error>> {
334 let value = self.read(register).await?;
335 let value = f(value);
336 self.write(register, value).await
337 }
338 }
339}