Skip to main content

rusty_esp_image_core/
sccb.rs

1//! SCCB — the camera control bus. It is I²C without a repeated start: a
2//! register read is a write transaction (the address) followed by a separate
3//! read transaction. Sensors use 8-bit (OV2640, OV7670) or 16-bit (OV5640,
4//! OV3660) register addresses.
5
6use embedded_hal::i2c::I2c;
7use rusty_esp_core::error::{Error, Result};
8
9use crate::sensor::{RegOp, Register, SensorDesc};
10
11/// Register address width of a sensor.
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum RegWidth {
14    /// One address byte.
15    U8,
16    /// Two address bytes, big-endian.
17    U16,
18}
19
20/// A sensor's control interface over an I²C bus.
21#[derive(Debug)]
22pub struct Sccb<I> {
23    i2c: I,
24    addr: u8,
25    width: RegWidth,
26}
27
28impl<I: I2c> Sccb<I> {
29    /// Talk to the sensor at 7-bit address `addr` with `width` register addresses.
30    pub fn new(i2c: I, addr: u8, width: RegWidth) -> Self {
31        Sccb { i2c, addr, width }
32    }
33
34    /// Talk to the sensor `desc` describes.
35    pub fn for_sensor(i2c: I, desc: &SensorDesc) -> Self {
36        Sccb::new(i2c, desc.sccb_addr, desc.reg_width)
37    }
38
39    /// The 7-bit bus address.
40    #[must_use]
41    pub fn addr(&self) -> u8 {
42        self.addr
43    }
44
45    /// Give the bus back.
46    pub fn release(self) -> I {
47        self.i2c
48    }
49
50    fn addr_bytes(&self, reg: u16) -> ([u8; 2], usize) {
51        match self.width {
52            RegWidth::U8 => ([reg as u8, 0], 1),
53            RegWidth::U16 => (reg.to_be_bytes(), 2),
54        }
55    }
56
57    /// Write one register.
58    pub fn write(&mut self, reg: u16, value: u8) -> Result<()> {
59        let (a, n) = self.addr_bytes(reg);
60        let mut buf = [0u8; 3];
61        buf[..n].copy_from_slice(&a[..n]);
62        buf[n] = value;
63        self.i2c
64            .write(self.addr, &buf[..=n])
65            .map_err(|_| Error::Hardware)
66    }
67
68    /// Write one [`Register`].
69    pub fn write_reg(&mut self, reg: Register) -> Result<()> {
70        self.write(reg.addr, reg.value)
71    }
72
73    /// Read one register (write the address, then read one byte).
74    pub fn read(&mut self, reg: u16) -> Result<u8> {
75        let (a, n) = self.addr_bytes(reg);
76        self.i2c
77            .write(self.addr, &a[..n])
78            .map_err(|_| Error::Hardware)?;
79        let mut v = [0u8; 1];
80        self.i2c
81            .read(self.addr, &mut v)
82            .map_err(|_| Error::Hardware)?;
83        Ok(v[0])
84    }
85
86    /// Run a register sequence. `delay_ms` is called for each
87    /// [`RegOp::DelayMs`] step (the caller owns the clock). Returns the number
88    /// of registers written.
89    pub fn apply(&mut self, table: &[RegOp], mut delay_ms: impl FnMut(u16)) -> Result<usize> {
90        let mut written = 0;
91        for op in table {
92            match *op {
93                RegOp::Write { addr, value } => {
94                    self.write(addr, value)?;
95                    written += 1;
96                }
97                RegOp::DelayMs(ms) => delay_ms(ms),
98            }
99        }
100        Ok(written)
101    }
102
103    /// Read the product id and compare with `desc`.
104    pub fn probe(&mut self, desc: &SensorDesc) -> Result<bool> {
105        let pid = match desc.pid_len {
106            1 => u16::from(self.read(desc.pid_reg)?),
107            2 => {
108                let hi = self.read(desc.pid_reg)?;
109                let lo = self.read(desc.pid_reg + 1)?;
110                u16::from_be_bytes([hi, lo])
111            }
112            _ => return Err(Error::InvalidFormat),
113        };
114        Ok(pid == desc.pid)
115    }
116}
117
118#[cfg(test)]
119pub(crate) mod fake {
120    //! A register-map I²C device for tests: remembers the last address
121    //! written and answers reads from a small map.
122
123    use core::convert::Infallible;
124
125    use embedded_hal::i2c::{ErrorType, I2c, Operation, SevenBitAddress};
126
127    #[derive(Debug, Default)]
128    pub struct FakeSensor {
129        pub regs: std::collections::BTreeMap<u16, u8>,
130        pub last_addr: Option<u16>,
131        pub writes: std::vec::Vec<(u16, u8)>,
132        pub width16: bool,
133    }
134
135    impl ErrorType for FakeSensor {
136        type Error = Infallible;
137    }
138
139    impl I2c<SevenBitAddress> for FakeSensor {
140        fn transaction(
141            &mut self,
142            _address: SevenBitAddress,
143            operations: &mut [Operation<'_>],
144        ) -> Result<(), Infallible> {
145            for op in operations {
146                match op {
147                    Operation::Write(bytes) => {
148                        let (reg, rest) = if self.width16 {
149                            (u16::from_be_bytes([bytes[0], bytes[1]]), &bytes[2..])
150                        } else {
151                            (u16::from(bytes[0]), &bytes[1..])
152                        };
153                        self.last_addr = Some(reg);
154                        if let Some(&v) = rest.first() {
155                            self.regs.insert(reg, v);
156                            self.writes.push((reg, v));
157                        }
158                    }
159                    Operation::Read(buf) => {
160                        let reg = self.last_addr.unwrap_or(0);
161                        for (i, b) in buf.iter_mut().enumerate() {
162                            *b = *self.regs.get(&(reg + i as u16)).unwrap_or(&0);
163                        }
164                    }
165                }
166            }
167            Ok(())
168        }
169    }
170}
171
172#[cfg(test)]
173mod tests {
174    use super::fake::FakeSensor;
175    use super::*;
176    use crate::sensor::{SensorId, describe, ov2640, ov5640};
177
178    #[test]
179    fn write_read_apply_probe_8bit() {
180        let desc = describe(SensorId::Ov2640).unwrap();
181        let mut bus = Sccb::for_sensor(FakeSensor::default(), desc);
182        assert_eq!(bus.addr(), 0x30);
183        bus.write(0x12, 0x80).unwrap();
184        assert_eq!(bus.read(0x12).unwrap(), 0x80);
185        let mut delays = 0;
186        let n = bus
187            .apply(
188                &[
189                    RegOp::Write {
190                        addr: 0x0A,
191                        value: 0x26,
192                    },
193                    RegOp::DelayMs(5),
194                    RegOp::Write {
195                        addr: 0x0B,
196                        value: 0x42,
197                    },
198                ],
199                |_| delays += 1,
200            )
201            .unwrap();
202        assert_eq!((n, delays), (2, 1));
203        assert!(bus.probe(desc).unwrap());
204        bus.write(0x0A, 0x00).unwrap();
205        assert!(!bus.probe(desc).unwrap());
206        // the real init table applies without error and writes every step
207        let n = bus.apply(ov2640::SETTINGS_CIF, |_| {}).unwrap();
208        assert_eq!(n, ov2640::SETTINGS_CIF.len());
209        let inner = bus.release();
210        assert!(inner.writes.len() > 100);
211    }
212
213    #[test]
214    fn sixteen_bit_addresses() {
215        let desc = describe(SensorId::Ov5640).unwrap();
216        let mut bus = Sccb::for_sensor(
217            FakeSensor {
218                width16: true,
219                ..FakeSensor::default()
220            },
221            desc,
222        );
223        bus.write(0x300A, 0x56).unwrap();
224        bus.write(0x300B, 0x40).unwrap();
225        assert_eq!(bus.read(0x300A).unwrap(), 0x56);
226        assert!(bus.probe(desc).unwrap());
227        let mut delays = std::vec::Vec::new();
228        let n = bus
229            .apply(ov5640::DEFAULT_REGS, |ms| delays.push(ms))
230            .unwrap();
231        let writes = ov5640::DEFAULT_REGS
232            .iter()
233            .filter(|op| matches!(op, RegOp::Write { .. }))
234            .count();
235        assert_eq!(n, writes);
236        assert_eq!(
237            delays.len(),
238            ov5640::DEFAULT_REGS.len() - writes,
239            "every delay step reached the caller's clock"
240        );
241    }
242}