diff --git a/Cargo.toml b/Cargo.toml index 244bf98..92dd33e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] authors = ["Sebastian Lauwers "] name = "arducam-mega" -version = "0.4.0" +version = "1.0.0" edition = "2021" license = "MIT OR Apache-2.0" repository = "https://github.com/unticks/arducam-mega/" @@ -17,7 +17,7 @@ maintenance = { status = "experimental" } # This dependency will be relaxed once embedded-hal is released as 1.0.0. Until # then, we target a specific alpha as the previous stable release breaks too # much. -embedded-hal = { version = "1.0.0-alpha.9" } +embedded-hal = { version = "1.0.0" } [features] default = ["3mp", "5mp"] @@ -25,7 +25,7 @@ default = ["3mp", "5mp"] 5mp = [] [dev-dependencies] -embedded-hal-mock = { git = "https://github.com/dbrgn/embedded-hal-mock", rev = "3d5ecf6" } +embedded-hal-mock = { git = "https://github.com/dbrgn/embedded-hal-mock"} # docs.rs-specific configuration [package.metadata.docs.rs] diff --git a/src/lib.rs b/src/lib.rs index ab38105..13b2d6f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -61,7 +61,7 @@ compile_error!( "at least one of the `arducam-mega/3mp` or `arducam-mega/5mp` features needs to be enabled" ); -use embedded_hal::{delay::DelayUs, spi::SpiBus, spi::SpiDevice}; +use embedded_hal::{delay::DelayNs, spi::SpiDevice}; const WRITE_REGISTER_MASK: u8 = 0x80; const SENSOR_STATE_MASK: u8 = 0x03; @@ -375,40 +375,33 @@ pub struct ArducamMega { impl ArducamMega where SPI: SpiDevice, - SPI::Bus: SpiBus, - Delay: DelayUs, + Delay: DelayNs, { pub fn new(spi: SPI, delay: Delay) -> Self { Self { spi, delay } } - fn read_reg(&mut self, addr: RegisterAddress) -> Result> { + fn read_reg(&mut self, addr: RegisterAddress) -> Result { let out: [u8; 1] = [addr as u8]; let mut buf = [0; 3]; - self.spi - .transfer(&mut buf[..], &out[..]) - .map_err(Error::Spi)?; + self.spi.transfer(&mut buf[..], &out[..])?; Ok(buf[2]) } - fn write_reg( - &mut self, - addr: RegisterAddress, - value: u8, - ) -> Result<&mut Self, Error> { + fn write_reg(&mut self, addr: RegisterAddress, value: u8) -> Result<&mut Self, SPI::Error> { let out: [u8; 2] = [addr as u8 | WRITE_REGISTER_MASK, value]; - self.spi.write(&out[..]).map_err(Error::Spi)?; + self.spi.write(&out[..])?; Ok(self) } - fn wait_idle(&mut self) -> Result<&mut Self, Error> { + fn wait_idle(&mut self) -> Result<&mut Self, SPI::Error> { while (self.read_reg(RegisterAddress::SensorState)? & SENSOR_STATE_MASK) != SENSOR_STATE_IDLE { - self.delay.delay_us(500u32).map_err(Error::Delay)?; + self.delay.delay_ns(500_000u32); } Ok(self) @@ -418,7 +411,7 @@ where /// /// This command sends a reset command to the camera. The Arducam SDK uses this after /// initialisation to ensure that the sensor is in a known-good state. - pub fn reset(&mut self) -> Result<&mut Self, Error> { + pub fn reset(&mut self) -> Result<&mut Self, SPI::Error> { self.write_reg(RegisterAddress::SensorReset, SENSOR_RESET_ENABLE)?; self.wait_idle() } @@ -427,7 +420,7 @@ where /// /// This function uses the `SensorId` register in the camera to obtain information about the /// sensor type. See [`CameraType`](CameraType) for more information. - pub fn get_camera_type(&mut self) -> Result> { + pub fn get_camera_type(&mut self) -> Result { let id = self.read_reg(RegisterAddress::SensorId)?; Ok(id.into()) @@ -440,10 +433,7 @@ where /// image data. More testing welcome. #[cfg_attr(docsrs, doc(cfg(feature = "5mp")))] #[cfg(feature = "5mp")] - pub fn set_auto_focus( - &mut self, - value: u8, - ) -> Result<&mut Self, Error> { + pub fn set_auto_focus(&mut self, value: u8) -> Result<&mut Self, SPI::Error> { self.write_reg(RegisterAddress::AutoFocus, value)?; self.wait_idle() } @@ -453,10 +443,7 @@ where /// This function allows you to control what format the camera captures pictures in. /// `Format::Jpeg` provides a good mix between image size and quality, and is the /// default. - pub fn set_format( - &mut self, - format: Format, - ) -> Result<&mut Self, Error> { + pub fn set_format(&mut self, format: Format) -> Result<&mut Self, SPI::Error> { self.write_reg(RegisterAddress::Format, format as u8)?; self.wait_idle() } @@ -466,10 +453,7 @@ where /// This function allows you to control the resolution of the pictures that are captured by the /// camera. Both the 3MP and 5MP cameras have two different default resolutions. See /// [`Resolution`](Resolution) for more details. - pub fn set_resolution( - &mut self, - resolution: Resolution, - ) -> Result<&mut Self, Error> { + pub fn set_resolution(&mut self, resolution: Resolution) -> Result<&mut Self, SPI::Error> { self.write_reg( RegisterAddress::Resolution, resolution as u8 | WRITE_REGISTER_MASK, @@ -481,10 +465,7 @@ where /// /// The Arducam SDK uses this command as part of the camera initialisation with the address /// `0x78`, however this does not appear to be necessary for the camera to function properly. - pub fn set_debug_device_address( - &mut self, - addr: u8, - ) -> Result<&mut Self, Error> { + pub fn set_debug_device_address(&mut self, addr: u8) -> Result<&mut Self, SPI::Error> { self.write_reg(RegisterAddress::DebugDeviceAddress, addr)?; self.wait_idle() } @@ -493,7 +474,7 @@ where /// /// This command empties the contents of the camera's FIFO buffer. This is used as part of the /// [`capture()`](ArducamMega::capture) function. - fn clear_fifo(&mut self) -> Result<&mut Self, Error> { + fn clear_fifo(&mut self) -> Result<&mut Self, SPI::Error> { self.write_reg(RegisterAddress::ArduchipFifo, ArduchipCommand::Clear as u8) } @@ -502,7 +483,7 @@ where /// This command reads a register in the camera to check if the sensor has finished taking a /// picture. You should not attempt to read the FIFO buffer length nor read any FIFO buffer /// data prior to this returning `true`. - pub fn capture_finished(&mut self) -> Result> { + pub fn capture_finished(&mut self) -> Result { let sensor_state = self.read_reg(RegisterAddress::SensorState)?; Ok((sensor_state & CAPTURE_FINISHED_MASK) != 0) } @@ -513,18 +494,18 @@ where /// to capture an image. The image will be captured using the currently-configured settings /// (white balance, gain, exposure, colour filters, etc). This command blocks until the sensor /// has finished capturing the image. - pub fn capture(&mut self) -> Result<&mut Self, Error> { + pub fn capture(&mut self) -> Result<&mut Self, SPI::Error> { self.capture_noblock()?; while !self.capture_finished()? { - self.delay.delay_us(500u32).map_err(Error::Delay)?; + self.delay.delay_ns(500_000u32); } Ok(self) } /// Non-blocking version of [`capture()`](ArducamMega::capture) - pub fn capture_noblock(&mut self) -> Result<&mut Self, Error> { + pub fn capture_noblock(&mut self) -> Result<&mut Self, SPI::Error> { self.clear_fifo()?; self.write_reg(RegisterAddress::ArduchipFifo, ArduchipCommand::Start as u8) } @@ -534,7 +515,7 @@ where /// This function reads out the size of the FIFO buffer length. This (roughly) represents the /// size of the picture. It appears that in some cases, the reported buffer is larger than the /// actual picture data. - pub fn read_fifo_length(&mut self) -> Result> { + pub fn read_fifo_length(&mut self) -> Result { let size1 = self.read_reg(RegisterAddress::FifoSize1)? as usize; let size2 = self.read_reg(RegisterAddress::FifoSize2)? as usize; let size3 = self.read_reg(RegisterAddress::FifoSize3)? as usize; @@ -554,14 +535,12 @@ where /// Reading out the entire image data like this will be relatively slow, as each byte transfer /// will require an SPI transaction to be setup and ended. For faster transfers, please see /// [`read_fifo_full`](ArducamMega::read_fifo_full). - pub fn read_fifo_byte(&mut self) -> Result> { + pub fn read_fifo_byte(&mut self) -> Result { let output: [u8; 1] = [FIFO_READ_SINGLE]; - let mut data: [u8; 3] = [0; 3]; - self.spi - .transfer(&mut data[..], &output[..]) - .map_err(Error::Spi)?; + let mut data: [u8; 2] = [0; 2]; + self.spi.transfer(&mut data[..], &output[..])?; - Ok(data[2]) + Ok(data[1]) } /// Reads out the entire FIFO buffer @@ -581,10 +560,7 @@ where /// [`find_jpeg_eof()`](find_jpeg_eof) to help trim the data stream. /// /// This function is not currently tested with other data formats (YUV, RGB). - pub fn read_fifo_full( - &mut self, - data: &mut T, - ) -> Result<&mut Self, Error> + pub fn read_fifo_full(&mut self, data: &mut T) -> Result<&mut Self, SPI::Error> where T: AsMut<[u8]>, { @@ -595,25 +571,51 @@ where let mut i = 0; + //Can no longer use a single spi.transaction, since length isnt known at compile time. + + // there are more than 63 bytes to be read + while i + 63 < length { + // send read burst command, read 65 bytes + self.spi.transfer(&mut buffer, &output)?; + // copy buffer into data array, skipping first two bytes + data[i..i + 63].copy_from_slice(&buffer[2..]); + i += 63; + } + // Send another read burst command and read remaining bytes self.spi - .transaction(|bus| { - // there are more than 63 bytes to be read - while i + 63 < length { - // Send the read burst command and read 65 bytes - bus.transfer(&mut buffer, &output)?; - // Copy buffer contents into data array, skipping first two bytes - data[i..i + 63].copy_from_slice(&buffer[2..]); - i += 63; - } - - // Send another read burst command and read remaining bytes - bus.transfer(&mut buffer[..(length - i) + 2], &output)?; - // Copy buffer contents into data array, skipping first two bytes - data[i..].copy_from_slice(&buffer[2..(length - i) + 2]); - - Ok(()) - }) - .map_err(Error::Spi)?; + .transfer(&mut buffer[..(length - i) + 2], &output)?; + // Copy buffer contents into data array, skipping first two bytes + data[i..].copy_from_slice(&buffer[2..(length - i) + 2]); + + Ok(self) + } + + pub fn read_fifo_burst(&mut self, data: &mut T) -> Result<&mut Self, SPI::Error> + where + T: AsMut<[u8]>, + { + let data = data.as_mut(); + assert!(data.len() >= 63); + + let output: [u8; 1] = [FIFO_READ_BURST]; + let mut buffer: [u8; 65] = [0; 65]; + + self.spi.transfer(&mut buffer, &output)?; + data[0..63].copy_from_slice(&buffer[2..]); + + Ok(self) + } + + /// An intentionally extremely thin wrapper over the raw SPI transfer, to be used for debugging + pub fn read_fifo_sized(&mut self, data: &mut T) -> Result<&mut Self, SPI::Error> + where + T: AsMut<[u8]>, + { + let data = data.as_mut(); + + let output: [u8; 1] = [FIFO_READ_BURST]; + + self.spi.transfer(data, &output)?; Ok(self) } @@ -622,7 +624,7 @@ where &mut self, cc: CameraControl, value: ControlValue, - ) -> Result<&mut Self, Error> { + ) -> Result<&mut Self, SPI::Error> { self.write_reg( RegisterAddress::GainExposureWhiteBalance, cc as u8 | value as u8, @@ -635,9 +637,7 @@ where /// **Note**: This appears to not work on the ArducamMega 5MP, where pictures are severely /// green-tinted when using AWB. #[inline] - pub fn enable_auto_white_balance( - &mut self, - ) -> Result<&mut Self, Error> { + pub fn enable_auto_white_balance(&mut self) -> Result<&mut Self, SPI::Error> { self.set_auto_camera_control(CameraControl::WhiteBalance, ControlValue::Enable) } @@ -646,33 +646,31 @@ where /// This function is automatically called by /// [`set_white_balance_mode()`](ArducamMega::set_white_balance_mode). #[inline] - pub fn disable_auto_white_balance( - &mut self, - ) -> Result<&mut Self, Error> { + pub fn disable_auto_white_balance(&mut self) -> Result<&mut Self, SPI::Error> { self.set_auto_camera_control(CameraControl::WhiteBalance, ControlValue::Disable) } /// Enables the camera's automatic gain adjustment #[inline] - pub fn enable_auto_iso(&mut self) -> Result<&mut Self, Error> { + pub fn enable_auto_iso(&mut self) -> Result<&mut Self, SPI::Error> { self.set_auto_camera_control(CameraControl::Gain, ControlValue::Enable) } /// Disables the camera's automatic gain adjustment #[inline] - pub fn disable_auto_iso(&mut self) -> Result<&mut Self, Error> { + pub fn disable_auto_iso(&mut self) -> Result<&mut Self, SPI::Error> { self.set_auto_camera_control(CameraControl::Gain, ControlValue::Disable) } /// Enables the camera's automatic exposure control #[inline] - pub fn enable_auto_exposure(&mut self) -> Result<&mut Self, Error> { + pub fn enable_auto_exposure(&mut self) -> Result<&mut Self, SPI::Error> { self.set_auto_camera_control(CameraControl::Exposure, ControlValue::Enable) } /// Disables the camera's automatic exposure control #[inline] - pub fn disable_auto_exposure(&mut self) -> Result<&mut Self, Error> { + pub fn disable_auto_exposure(&mut self) -> Result<&mut Self, SPI::Error> { self.set_auto_camera_control(CameraControl::Exposure, ControlValue::Disable) } @@ -685,73 +683,55 @@ where pub fn set_white_balance_mode( &mut self, mode: WhiteBalanceMode, - ) -> Result<&mut Self, Error> { + ) -> Result<&mut Self, SPI::Error> { self.disable_auto_white_balance()?; self.write_reg(RegisterAddress::WhiteBalanceMode, mode as u8)?; self.wait_idle() } #[inline] - fn set_power_mode( - &mut self, - mode: PowerMode, - ) -> Result<&mut Self, Error> { + fn set_power_mode(&mut self, mode: PowerMode) -> Result<&mut Self, SPI::Error> { self.write_reg(RegisterAddress::Power, mode as u8) } /// Turns on the camera's low power mode #[inline] - pub fn enable_low_power_mode(&mut self) -> Result<&mut Self, Error> { + pub fn enable_low_power_mode(&mut self) -> Result<&mut Self, SPI::Error> { self.set_power_mode(PowerMode::LowPower) } /// Turns off the camera's low power mode #[inline] - pub fn disable_low_power_mode(&mut self) -> Result<&mut Self, Error> { + pub fn disable_low_power_mode(&mut self) -> Result<&mut Self, SPI::Error> { self.set_power_mode(PowerMode::Normal) } /// Sets the camera's brightness bias - pub fn set_brightness_bias( - &mut self, - level: BrightnessLevel, - ) -> Result<&mut Self, Error> { + pub fn set_brightness_bias(&mut self, level: BrightnessLevel) -> Result<&mut Self, SPI::Error> { self.write_reg(RegisterAddress::Brightness, level as u8)?; self.wait_idle() } /// Sets the camera's contrast - pub fn set_contrast( - &mut self, - level: Level, - ) -> Result<&mut Self, Error> { + pub fn set_contrast(&mut self, level: Level) -> Result<&mut Self, SPI::Error> { self.write_reg(RegisterAddress::Contrast, level as u8)?; self.wait_idle() } /// Sets the camera's saturation - pub fn set_saturation( - &mut self, - level: Level, - ) -> Result<&mut Self, Error> { + pub fn set_saturation(&mut self, level: Level) -> Result<&mut Self, SPI::Error> { self.write_reg(RegisterAddress::Saturation, level as u8)?; self.wait_idle() } /// Sets the camera's exposure - pub fn set_exposure( - &mut self, - level: Level, - ) -> Result<&mut Self, Error> { + pub fn set_exposure(&mut self, level: Level) -> Result<&mut Self, SPI::Error> { self.write_reg(RegisterAddress::Exposure, level as u8)?; self.wait_idle() } /// Sets the camera's color effect - pub fn set_color_effect( - &mut self, - effect: ColorEffect, - ) -> Result<&mut Self, Error> { + pub fn set_color_effect(&mut self, effect: ColorEffect) -> Result<&mut Self, SPI::Error> { self.write_reg(RegisterAddress::ColorEffect, effect as u8)?; self.wait_idle() } @@ -759,10 +739,7 @@ where /// Sets the camera's sharpness #[cfg_attr(docsrs, doc(cfg(feature = "3mp")))] #[cfg(feature = "3mp")] - pub fn set_sharpness( - &mut self, - level: SharpnessLevel, - ) -> Result<&mut Self, Error> { + pub fn set_sharpness(&mut self, level: SharpnessLevel) -> Result<&mut Self, SPI::Error> { self.write_reg(RegisterAddress::Sharpness, level as u8)?; self.wait_idle() } @@ -807,7 +784,7 @@ pub fn find_jpeg_eof(data: &[u8]) -> Option { #[cfg(test)] mod tests { use super::*; - use embedded_hal_mock::{ + use embedded_hal_mock::eh1::{ delay, spi::{self, Transaction}, }; @@ -835,7 +812,7 @@ mod tests { macro_rules! harness { ($e:ident, $s:ident, $c:ident) => { let mut $s = spi::Mock::new(&$e); - let mut $c = ArducamMega::new(&mut $s, delay::MockNoop::new()); + let mut $c = ArducamMega::new(&mut $s, delay::NoopDelay::new()); }; } @@ -1109,8 +1086,11 @@ mod tests { fn read_fifo_full_skips_first_two_bytes_on_long_transfers() { let mut buffer = [0; 126]; let expectations = [ + //Updated since read_fifo_full requires multiple transactions now Transaction::transaction_start(), Transaction::transfer(vec![0x3c], vec![0x11; 65]), + Transaction::transaction_end(), + Transaction::transaction_start(), Transaction::transfer(vec![0x3c], vec![0x22; 65]), Transaction::transaction_end(), ]; @@ -1127,8 +1107,11 @@ mod tests { fn read_fifo_full_hands_partial_reads() { let mut buffer = [0; 100]; let expectations = [ + //Updated since read_fifo_full requires multiple transactions now Transaction::transaction_start(), Transaction::transfer(vec![0x3c], vec![0x11; 65]), + Transaction::transaction_end(), + Transaction::transaction_start(), Transaction::transfer(vec![0x3c], vec![0x22; 39]), Transaction::transaction_end(), ];