aboutsummaryrefslogtreecommitdiff
path: root/examples/stm32h7/src/bin/camera.rs
diff options
context:
space:
mode:
authorMatous Hybl <[email protected]>2021-11-16 11:44:05 +0100
committerMatous Hybl <[email protected]>2021-12-09 12:56:39 +0100
commit484c356c030d074ef0339cee66440bcc252ff1d4 (patch)
tree824a1e332664d21ad0df21ca14349c115774c60e /examples/stm32h7/src/bin/camera.rs
parent1dd5a71c07d2ab9aec1d9af7b4c87d5b1b969bd8 (diff)
Add DCMI example.
Diffstat (limited to 'examples/stm32h7/src/bin/camera.rs')
-rw-r--r--examples/stm32h7/src/bin/camera.rs339
1 files changed, 339 insertions, 0 deletions
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}