arducam_legacy/
lib.rs

1//! This library aims to provide support for older legacy Arducam cameras such as ArduCAM Mini 2MP Plus
2//! It provides `embedded-hal` compatible API
3//! 
4//! # Example
5//! ```rust
6//! #![no_std]
7//! #![no_main]
8//! 
9//! use stm32_hal2::{pac, gpio::{Pin, Port, PinMode, OutputType}, spi::{Spi, BaudRate}, i2c::I2c, timer::Timer};
10//! use cortex_m::delay::Delay;
11//! 
12//! use arducam_legacy::Arducam;
13//! 
14//! fn main() -> ! {
15//!     let cp = cortex_m::Peripherals::take().unwrap();
16//!     let dp = pac::Peripherals::take().unwrap();
17//! 
18//!     // Clocks setup
19//!     let clock_cfg = stm32_hal2::clocks::Clocks::default();
20//!     clock_cfg.setup().unwrap();
21//!     let mut delay = Delay::new(cp.SYST, clock_cfg.systick());
22//!     let mut mono_timer = Timer::new_tim2(dp.TIM2, 100.0, Default::default(), &clock_cfg);
23//! 
24//!     // Example pinout configuration
25//!     // Adapt to your HAL crate
26//!     let _arducam_spi_mosi = Pin::new(Port::D, 4, PinMode::Alt(5));
27//!     let _arducam_spi_miso = Pin::new(Port::D, 3, PinMode::Alt(5));
28//!     let _arducam_spi_sck = Pin::new(Port::D, 1, PinMode::Alt(5));
29//!     let arducam_cs = Pin::new(Port::D, 0, PinMode::Output);
30//!     let arducam_spi = Spi::new(dp.SPI2, Default::default(), BaudRate::Div32);
31//!     let mut arducam_i2c_sda = Pin::new(Port::F, 0, PinMode::Alt(4));
32//!     arducam_i2c_sda.output_type(OutputType::OpenDrain);
33//!     let mut arducam_i2c_scl = Pin::new(Port::F, 1, PinMode::Alt(4));
34//!     arducam_i2c_scl.output_type(OutputType::OpenDrain);
35//!     let arducam_i2c = I2c::new(dp.I2C2, Default::default(), &clock_cfg);
36//! 
37//!     let mut arducam = Arducam::new(
38//!         arducam_spi,
39//!         arducam_i2c,
40//!         arducam_cs,
41//!         arducam_legacy::Resolution::Res320x240, arducam_legacy::ImageFormat::JPEG
42//!         );
43//!     arducam.init(&mut delay).unwrap();
44//!     
45//!     arducam.start_capture().unwrap();
46//!     while !arducam.is_capture_done().unwrap() { delay.delay_ms(1) }
47//!     let mut image = [0; 8192];
48//!     let length = arducam.get_fifo_length().unwrap();
49//!     let final_length = arducam.read_captured_image(&mut image).unwrap();
50//! 
51//!     loop {}
52//! }
53//! ```
54
55#![no_std]
56#![no_main]
57
58use core::{fmt, slice::IterMut};
59
60use embedded_hal::{blocking::{spi::{self, Transfer}, i2c, delay::DelayMs}, digital::v2::OutputPin};
61use ov2640_registers::*;
62
63mod ov2640_registers;
64
65const ARDUCHIP_TEST1: u8 = 0x00;
66const ARDUCHIP_FIFO: u8 = 0x04;
67const ARDUCHIP_TRIG: u8 = 0x41;
68const OV2640_ADDR: u8 = 0x60 >> 1;
69const OV2640_CHIPID_HIGH: u8 = 0x0A;
70const OV2640_CHIPID_LOW: u8 = 0x0B;
71const FIFO_CLEAR_MASK: u8 = 0x01;
72const FIFO_START_MASK: u8 = 0x02;
73const FIFO_BURST: u8 = 0x3C;
74const FIFO_SIZE1: u8 = 0x42;
75const FIFO_SIZE2: u8 = 0x43;
76const FIFO_SIZE3: u8 = 0x44;
77const CAP_DONE_MASK: u8 = 0x08;
78
79#[derive(fmt::Debug)]
80/// Possible errors which can happen during communication
81pub enum Error<SpiErr, I2cErr, PinErr> {
82    Spi(SpiErr),
83    I2c(I2cErr),
84    Pin(PinErr),
85    OutOfBounds
86}
87
88#[derive(Debug)]
89// Image resolutions
90pub enum Resolution {
91    Res160x120,
92    Res176x144,
93    Res320x240,
94    Res352x288,
95    Res640x480,
96    Res800x600,
97    Res1024x768,
98    Res1280x1024,
99    Res1600x1200
100}
101
102#[derive(PartialEq, Eq, Debug)]
103/// Image formats which Arducam can handle
104pub enum ImageFormat {
105    // BMP,
106    // RAW,
107    JPEG
108}
109
110/// Main struct responsible for communicating with Arducam
111pub struct Arducam<SPI, I2C, CS> {
112    spi: SPI,
113    spi_cs: CS,
114    i2c: I2C,
115    format: ImageFormat,
116    resolution: Resolution
117}
118
119impl<SPI, I2C, CS, SpiErr, I2cErr, PinErr> Arducam<SPI, I2C, CS>
120where
121    SPI: Transfer<u8, Error = SpiErr> + spi::Write<u8, Error = SpiErr>,
122    I2C: i2c::Write<Error = I2cErr> + i2c::WriteRead<Error = I2cErr>,
123    CS: OutputPin<Error = PinErr>
124{
125    /// Creates a new Arducam instance but doesn't initialize it
126    pub fn new(spi: SPI, i2c: I2C, cs_pin: CS, resolution: Resolution, format: ImageFormat) -> Arducam<SPI, I2C, CS> {
127        Arducam {
128            spi,
129            spi_cs: cs_pin,
130            i2c,
131            format,
132            resolution,
133        }
134    }
135
136    /// Initializes Arducam to resetted state
137    pub fn init<D>(&mut self, delay: &mut D) -> Result<(), Error<SpiErr, I2cErr, PinErr>>
138    where
139        D: DelayMs<u32>
140    {
141        self.arduchip_write_reg(0x07, 0x80)?;
142        delay.delay_ms(100);
143        self.arduchip_write_reg(0x07, 0x00)?;
144        delay.delay_ms(100);
145        self.sensor_writereg8_8(0xFF, 0x01)?;
146        self.sensor_writereg8_8(0x12, 0x80)?;
147        delay.delay_ms(100);
148
149        // if self.format == ImageFormat::JPEG {
150            unsafe {
151                self.sensor_writeregs8_8(&OV2640_JPEG_INIT)?;
152                self.sensor_writeregs8_8(&OV2640_YUV422)?;
153            }
154            self.sensor_writereg8_8(0xFF, 0x01)?;
155            self.sensor_writereg8_8(0x15, 0x00)?;
156            self.send_resolution()?;
157        // }
158        // else {
159        //     unsafe { self.sensor_writeregs8_8(&OV2640_QVGA)?; }
160        // }
161
162        Ok(())
163    }
164
165    /// Sets camera resolution
166    pub fn set_resolution(&mut self, resolution: Resolution) -> Result<(), Error<SpiErr, I2cErr, PinErr>> {
167        self.resolution = resolution;
168        self.send_resolution()?;
169        Ok(())
170    }
171
172    /// Checks if Arducam is still connected to SPI bus
173    pub fn is_connected(&mut self) -> Result<bool, Error<SpiErr, I2cErr, PinErr>> {
174        let test_value = 0x52;
175        self.arduchip_write_reg(ARDUCHIP_TEST1, test_value)?;
176        let result = self.arduchip_read_reg(ARDUCHIP_TEST1)?;
177
178        let valid_ov2640_chipid1 = [0x26, 0x41];
179        let valid_ov2640_chipid2 = [0x26, 0x42];
180        let chipid = self.get_sensor_chipid()?;
181
182        if test_value == result && chipid == valid_ov2640_chipid1 || chipid == valid_ov2640_chipid2 {
183            Ok(true)
184        }
185        else {
186            Ok(false)
187        }
188    }
189
190    /// Sends image capture request
191    pub fn start_capture(&mut self) -> Result<(), Error<SpiErr, I2cErr, PinErr>> {
192        self.flush_fifo()?;
193        self.start_fifo()?;
194        Ok(())
195    }
196
197    /// Checks if image capture is done
198    pub fn is_capture_done(&mut self) -> Result<bool, Error<SpiErr, I2cErr, PinErr>> {
199        self.arduchip_read_reg(ARDUCHIP_TRIG).map(|result| { result & CAP_DONE_MASK != 0 })
200    }
201
202    /// Saves captured image to provided mutable slice
203    /// It is important to be sure if that slice will be big enough for image data
204    /// otherwise data will be cut
205    /// 
206    /// # Returns
207    /// Actual image size
208    pub fn read_captured_image(&mut self, data_out: IterMut<u8>) -> Result<usize, Error<SpiErr, I2cErr, PinErr>>
209    {
210        let length = self.get_fifo_length()?;
211        let mut final_length = 0;
212        self.spi_cs.set_low().map_err(Error::Pin)?;
213        self.set_fifo_burst()?;
214        let mut curr_byte = 0;
215        #[allow(unused_assignments)]
216        let mut prev_byte = 0;
217        for (i, b) in data_out.enumerate() {
218            prev_byte = curr_byte;
219            curr_byte = self.spi.transfer(&mut [0x00]).map_err(Error::Spi)?[0];
220            *b = curr_byte;
221            if prev_byte == 0xFF && curr_byte == 0xD9 || i as u32 > length {
222                final_length = i;
223                break;
224            }
225        }
226        self.flush_fifo()?;
227        Ok(final_length)
228    }
229
230    /// Returns image length reported by arduchip in FIFO
231    pub fn get_fifo_length(&mut self) -> Result<u32, Error<SpiErr, I2cErr, PinErr>> {
232        let mut len_builder = (0u32, 0u32, 0u32);
233        len_builder.0 = self.arduchip_read_reg(FIFO_SIZE1)?.into();
234        len_builder.1 = self.arduchip_read_reg(FIFO_SIZE2)?.into();
235        len_builder.2 = (self.arduchip_read_reg(FIFO_SIZE3)? & 0x7F).into();
236        Ok((len_builder.2 << 16 | len_builder.1 << 8 | len_builder.0) as u32 & 0x7FFFFFu32)
237    }
238
239    /// Returns sensor vendor and product ID
240    pub fn get_sensor_chipid(&mut self) -> Result<[u8; 2], Error<SpiErr, I2cErr, PinErr>> {
241        let mut chipid: [u8; 2] = [0; 2];
242        self.sensor_writereg8_8(0xFF, 0x01)?;
243        self.sensor_readreg8_8(OV2640_CHIPID_HIGH, &mut chipid[0..1])?;
244        self.sensor_readreg8_8(OV2640_CHIPID_LOW, &mut chipid[1..2])?;
245        Ok(chipid)
246    }
247
248    fn send_resolution(&mut self) -> Result<(), Error<SpiErr, I2cErr, PinErr>> {
249        unsafe {
250            match self.resolution {
251                Resolution::Res160x120 => { self.sensor_writeregs8_8(&OV2640_160x120_JPEG)? },
252                Resolution::Res1024x768 => { self.sensor_writeregs8_8(&OV2640_1024x768_JPEG)? },
253                Resolution::Res1280x1024 => { self.sensor_writeregs8_8(&OV2640_1280x1024_JPEG)? },
254                Resolution::Res1600x1200 => { self.sensor_writeregs8_8(&OV2640_1600x1200_JPEG)? },
255                Resolution::Res176x144 => { self.sensor_writeregs8_8(&OV2640_176x144_JPEG)? },
256                Resolution::Res320x240 => { self.sensor_writeregs8_8(&OV2640_320x240_JPEG)? },
257                Resolution::Res352x288 => { self.sensor_writeregs8_8(&OV2640_352x288_JPEG)? },
258                Resolution::Res640x480 => { self.sensor_writeregs8_8(&OV2640_640x480_JPEG)? },
259                Resolution::Res800x600 => { self.sensor_writeregs8_8(&OV2640_800x600_JPEG)? },
260            }
261        }
262
263        Ok(())
264    }
265
266    fn flush_fifo(&mut self) -> Result<(), Error<SpiErr, I2cErr, PinErr>> {
267        self.arduchip_write_reg(ARDUCHIP_FIFO, FIFO_CLEAR_MASK)
268    }
269
270    fn start_fifo(&mut self) -> Result<(), Error<SpiErr, I2cErr, PinErr>> {
271        self.arduchip_write_reg(ARDUCHIP_FIFO, FIFO_START_MASK)
272    }
273
274    fn set_fifo_burst(&mut self) -> Result<(), Error<SpiErr, I2cErr, PinErr>> {
275        self.spi.write(&[FIFO_BURST]).map_err(Error::Spi)
276    }
277
278    fn arduchip_write(&mut self, addr: u8, data: u8) -> Result<(), Error<SpiErr, I2cErr, PinErr>> {
279        self.spi_cs.set_low().map_err(Error::Pin)?;
280        self.spi.write(&[addr; 1]).map_err(Error::Spi)?;
281        self.spi.write(&[data; 1]).map_err(Error::Spi)?;
282        self.spi_cs.set_high().map_err(Error::Pin)?;
283        Ok(())
284    }
285
286    fn arduchip_read(&mut self, addr: u8) -> Result<u8, Error<SpiErr, I2cErr, PinErr>> {
287        self.spi_cs.set_low().map_err(Error::Pin)?;
288        self.spi.transfer(&mut [addr; 1]).map_err(Error::Spi)?;
289        let value = self.spi.transfer(&mut [0; 1]).map_err(Error::Spi)?[0];
290        self.spi_cs.set_high().map_err(Error::Pin)?;
291        Ok(value)
292    }
293
294    fn arduchip_write_reg(&mut self, addr: u8, data: u8) -> Result<(), Error<SpiErr, I2cErr, PinErr>> {
295        self.arduchip_write(addr | 0x80, data)
296    }
297
298    fn arduchip_read_reg(&mut self, addr: u8) -> Result<u8, Error<SpiErr, I2cErr, PinErr>> {
299        self.arduchip_read(addr & 0x7F)
300    }
301
302    fn sensor_writeregs8_8(&mut self, regs: &[[u8; 2]]) -> Result<(), Error<SpiErr, I2cErr, PinErr>> {
303        for reg in regs {
304            self.sensor_writereg8_8(reg[0], reg[1])?;
305        }
306        Ok(())
307    }
308
309    fn sensor_writereg8_8(&mut self, reg: u8, data: u8) -> Result<(), Error<SpiErr, I2cErr, PinErr>> {
310        self.i2c.write(OV2640_ADDR, &[reg & 0xFF, data & 0xFF]).map_err(Error::I2c)
311    }
312
313    fn sensor_readreg8_8(&mut self, reg: u8, out: &mut [u8]) -> Result<(), Error<SpiErr, I2cErr, PinErr>> {
314        self.i2c.write_read(OV2640_ADDR, &[reg & 0xFF], out).map_err(Error::I2c)
315    }
316}
317
318impl<SPI, I2C, CS> fmt::Debug for Arducam<SPI, I2C, CS>
319where
320    SPI: fmt::Debug,
321    I2C: fmt::Debug,
322    CS: fmt::Debug,
323{
324    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
325        f.debug_struct("Arducam")
326            .field("Spi", &self.spi)
327            .field("I2C", &self.i2c)
328            .field("Resolution", &self.resolution)
329            .field("Image format", &self.format)
330            .finish()
331    }
332}