#![cfg_attr(not(test), no_std)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#[cfg(not(any(feature = "3mp", feature = "5mp")))]
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};
const WRITE_REGISTER_MASK: u8 = 0x80;
const SENSOR_STATE_MASK: u8 = 0x03;
const SENSOR_STATE_IDLE: u8 = 0x02;
const SENSOR_RESET_ENABLE: u8 = 0x40;
const CAPTURE_FINISHED_MASK: u8 = 0x04;
const FIFO_READ_BURST: u8 = 0x3c;
const FIFO_READ_SINGLE: u8 = 0x3d;
#[derive(Debug, Clone, PartialEq)]
#[repr(u8)]
pub enum CameraType {
OV5640 = 0x81,
OV3640 = 0x82,
Unknown(u8),
}
impl From<u8> for CameraType {
fn from(id: u8) -> Self {
match id {
0x81 => CameraType::OV5640,
0x82 => CameraType::OV3640,
id => CameraType::Unknown(id),
}
}
}
#[derive(Debug, Clone)]
#[repr(u8)]
enum CameraControl {
Gain = 0x00, Exposure = 0x01,
WhiteBalance = 0x02,
}
#[derive(Debug, Clone)]
#[repr(u8)]
enum RegisterAddress {
Power = 0x02,
ArduchipFifo = 0x04,
SensorReset = 0x07,
DebugDeviceAddress = 0x0a,
Format = 0x20,
Resolution = 0x21,
Brightness = 0x22,
Contrast = 0x23,
Saturation = 0x24,
Exposure = 0x25,
WhiteBalanceMode = 0x26,
ColorEffect = 0x27,
#[cfg(feature = "3mp")]
Sharpness = 0x28,
#[cfg(feature = "5mp")]
AutoFocus = 0x29,
GainExposureWhiteBalance = 0x2a,
SensorId = 0x40,
SensorState = 0x44,
FifoSize1 = 0x45,
FifoSize2 = 0x46,
FifoSize3 = 0x47,
}
#[cfg_attr(docsrs, doc(cfg(feature = "3mp")))]
#[cfg(feature = "3mp")]
#[derive(Debug, Clone, Default)]
#[repr(u8)]
pub enum SharpnessLevel {
#[default]
Auto = 0x00,
One = 0x01,
Two = 0x02,
Three = 0x03,
Four = 0x04,
Five = 0x05,
Six = 0x06,
Seven = 0x07,
Eight = 0x08,
}
#[derive(Debug, Clone, Default)]
#[repr(u8)]
pub enum ColorEffect {
#[default]
None = 0x00,
Blueish = 0x01,
Redish = 0x02,
BlackWhite = 0x03,
Sepia = 0x04,
Negative = 0x05,
GrassGreen = 0x06,
OverExposure = 0x07,
Solarize = 0x08,
}
#[derive(Debug, Clone, Default)]
#[repr(u8)]
pub enum Level {
#[default]
Default = 0x00,
PlusOne = 0x01,
MinusOne = 0x02,
PlusTwo = 0x03,
MinusTwo = 0x04,
PlusThree = 0x05,
MinusThree = 0x06,
}
#[derive(Debug, Clone, Default)]
#[repr(u8)]
pub enum BrightnessLevel {
#[default]
Default = 0x00,
PlusOne = 0x01,
MinusOne = 0x02,
PlusTwo = 0x03,
MinusTwo = 0x04,
PlusThree = 0x05,
MinusThree = 0x06,
PlusFour = 0x07,
MinusFour = 0x08,
}
#[derive(Debug, Clone)]
#[repr(u8)]
enum PowerMode {
LowPower = 0x07,
Normal = 0x05,
}
#[derive(Debug, Clone)]
#[repr(u8)]
enum ControlValue {
Disable = 0x00,
Enable = 0x80,
}
#[derive(Debug, Clone, Default)]
#[repr(u8)]
pub enum Format {
#[default]
Jpeg = 0x01,
Rgb = 0x02,
Yuv = 0x03,
}
#[derive(Debug, Clone, Default)]
#[repr(u8)]
pub enum Resolution {
#[cfg_attr(docsrs, doc(cfg(feature = "3mp")))]
#[cfg(feature = "3mp")]
Qqvga = 0x00,
Qvga = 0x01,
Vga = 0x02,
#[cfg_attr(docsrs, doc(cfg(feature = "3mp")))]
#[cfg(feature = "3mp")]
Svga = 0x03,
Hd = 0x04,
#[cfg_attr(docsrs, doc(cfg(feature = "3mp")))]
#[cfg(feature = "3mp")]
Sxgam = 0x05,
Uxga = 0x06,
#[cfg_attr(all(feature = "3mp", feature = "5mp"), default)]
Fhd = 0x07,
#[cfg_attr(docsrs, doc(cfg(feature = "3mp")))]
#[cfg(feature = "3mp")]
#[cfg_attr(not(feature = "5mp"), default)]
Qxga = 0x08,
#[cfg_attr(docsrs, doc(cfg(feature = "5mp")))]
#[cfg(feature = "5mp")]
#[cfg_attr(not(feature = "3mp"), default)]
Wqxga2 = 0x09,
Res96x96 = 0x0a,
Res128x128 = 0x0b,
Res320x320 = 0x0c,
}
#[derive(Debug, Clone)]
#[repr(u8)]
enum ArduchipCommand {
Clear = 0x01,
Start = 0x02,
}
#[derive(Debug, Clone, Default)]
#[repr(u8)]
pub enum WhiteBalanceMode {
#[default]
Auto = 0x00,
Sunny = 0x01,
Office = 0x02,
Cloudy = 0x03,
Home = 0x04,
}
pub struct ArducamMega<SPI, Delay> {
spi: SPI,
delay: Delay,
}
impl<SPI, Delay> ArducamMega<SPI, Delay>
where
SPI: SpiDevice,
SPI::Bus: SpiBus,
Delay: DelayUs,
{
pub fn new(spi: SPI, delay: Delay) -> Self {
Self { spi, delay }
}
fn read_reg(&mut self, addr: RegisterAddress) -> Result<u8, Error<SPI::Error, Delay::Error>> {
let out: [u8; 1] = [addr as u8];
let mut buf = [0; 3];
self.spi
.transfer(&mut buf[..], &out[..])
.map_err(Error::Spi)?;
Ok(buf[2])
}
fn write_reg(
&mut self,
addr: RegisterAddress,
value: u8,
) -> Result<&mut Self, Error<SPI::Error, Delay::Error>> {
let out: [u8; 2] = [addr as u8 | WRITE_REGISTER_MASK, value];
self.spi.write(&out[..]).map_err(Error::Spi)?;
Ok(self)
}
fn wait_idle(&mut self) -> Result<&mut Self, Error<SPI::Error, Delay::Error>> {
while (self.read_reg(RegisterAddress::SensorState)? & SENSOR_STATE_MASK)
!= SENSOR_STATE_IDLE
{
self.delay.delay_us(500u32).map_err(Error::Delay)?;
}
Ok(self)
}
pub fn reset(&mut self) -> Result<&mut Self, Error<SPI::Error, Delay::Error>> {
self.write_reg(RegisterAddress::SensorReset, SENSOR_RESET_ENABLE)?;
self.wait_idle()
}
pub fn get_camera_type(&mut self) -> Result<CameraType, Error<SPI::Error, Delay::Error>> {
let id = self.read_reg(RegisterAddress::SensorId)?;
Ok(id.into())
}
#[cfg_attr(docsrs, doc(cfg(feature = "5mp")))]
#[cfg(feature = "5mp")]
pub fn set_auto_focus(
&mut self,
value: u8,
) -> Result<&mut Self, Error<SPI::Error, Delay::Error>> {
self.write_reg(RegisterAddress::AutoFocus, value)?;
self.wait_idle()
}
pub fn set_format(
&mut self,
format: Format,
) -> Result<&mut Self, Error<SPI::Error, Delay::Error>> {
self.write_reg(RegisterAddress::Format, format as u8)?;
self.wait_idle()
}
pub fn set_resolution(
&mut self,
resolution: Resolution,
) -> Result<&mut Self, Error<SPI::Error, Delay::Error>> {
self.write_reg(
RegisterAddress::Resolution,
resolution as u8 | WRITE_REGISTER_MASK,
)?;
self.wait_idle()
}
pub fn set_debug_device_address(
&mut self,
addr: u8,
) -> Result<&mut Self, Error<SPI::Error, Delay::Error>> {
self.write_reg(RegisterAddress::DebugDeviceAddress, addr)?;
self.wait_idle()
}
fn clear_fifo(&mut self) -> Result<&mut Self, Error<SPI::Error, Delay::Error>> {
self.write_reg(RegisterAddress::ArduchipFifo, ArduchipCommand::Clear as u8)
}
pub fn capture_finished(&mut self) -> Result<bool, Error<SPI::Error, Delay::Error>> {
let sensor_state = self.read_reg(RegisterAddress::SensorState)?;
Ok((sensor_state & CAPTURE_FINISHED_MASK) != 0)
}
pub fn capture(&mut self) -> Result<&mut Self, Error<SPI::Error, Delay::Error>> {
self.capture_noblock()?;
while !self.capture_finished()? {
self.delay.delay_us(500u32).map_err(Error::Delay)?;
}
Ok(self)
}
pub fn capture_noblock(&mut self) -> Result<&mut Self, Error<SPI::Error, Delay::Error>> {
self.clear_fifo()?;
self.write_reg(RegisterAddress::ArduchipFifo, ArduchipCommand::Start as u8)
}
pub fn read_fifo_length(&mut self) -> Result<usize, Error<SPI::Error, Delay::Error>> {
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;
let length = (size3 << 16) | (size2 << 8) | size1;
Ok(length)
}
pub fn read_fifo_byte(&mut self) -> Result<u8, Error<SPI::Error, Delay::Error>> {
let output: [u8; 1] = [FIFO_READ_SINGLE];
let mut data: [u8; 3] = [0; 3];
self.spi
.transfer(&mut data[..], &output[..])
.map_err(Error::Spi)?;
Ok(data[2])
}
pub fn read_fifo_full<T>(
&mut self,
data: &mut T,
) -> Result<&mut Self, Error<SPI::Error, Delay::Error>>
where
T: AsMut<[u8]>,
{
let data = data.as_mut();
let length = data.len();
let output: [u8; 1] = [FIFO_READ_BURST];
let mut buffer: [u8; 65] = [0; 65];
let mut i = 0;
self.spi
.transaction(|bus| {
while i + 63 < length {
bus.transfer(&mut buffer, &output)?;
data[i..i + 63].copy_from_slice(&buffer[2..]);
i += 63;
}
bus.transfer(&mut buffer[..(length - i) + 2], &output)?;
data[i..].copy_from_slice(&buffer[2..(length - i) + 2]);
Ok(())
})
.map_err(Error::Spi)?;
Ok(self)
}
fn set_auto_camera_control(
&mut self,
cc: CameraControl,
value: ControlValue,
) -> Result<&mut Self, Error<SPI::Error, Delay::Error>> {
self.write_reg(
RegisterAddress::GainExposureWhiteBalance,
cc as u8 | value as u8,
)?;
self.wait_idle()
}
#[inline]
pub fn enable_auto_white_balance(
&mut self,
) -> Result<&mut Self, Error<SPI::Error, Delay::Error>> {
self.set_auto_camera_control(CameraControl::WhiteBalance, ControlValue::Enable)
}
#[inline]
pub fn disable_auto_white_balance(
&mut self,
) -> Result<&mut Self, Error<SPI::Error, Delay::Error>> {
self.set_auto_camera_control(CameraControl::WhiteBalance, ControlValue::Disable)
}
#[inline]
pub fn enable_auto_iso(&mut self) -> Result<&mut Self, Error<SPI::Error, Delay::Error>> {
self.set_auto_camera_control(CameraControl::Gain, ControlValue::Enable)
}
#[inline]
pub fn disable_auto_iso(&mut self) -> Result<&mut Self, Error<SPI::Error, Delay::Error>> {
self.set_auto_camera_control(CameraControl::Gain, ControlValue::Disable)
}
#[inline]
pub fn enable_auto_exposure(&mut self) -> Result<&mut Self, Error<SPI::Error, Delay::Error>> {
self.set_auto_camera_control(CameraControl::Exposure, ControlValue::Enable)
}
#[inline]
pub fn disable_auto_exposure(&mut self) -> Result<&mut Self, Error<SPI::Error, Delay::Error>> {
self.set_auto_camera_control(CameraControl::Exposure, ControlValue::Disable)
}
pub fn set_white_balance_mode(
&mut self,
mode: WhiteBalanceMode,
) -> Result<&mut Self, Error<SPI::Error, Delay::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<SPI::Error, Delay::Error>> {
self.write_reg(RegisterAddress::Power, mode as u8)
}
#[inline]
pub fn enable_low_power_mode(&mut self) -> Result<&mut Self, Error<SPI::Error, Delay::Error>> {
self.set_power_mode(PowerMode::LowPower)
}
#[inline]
pub fn disable_low_power_mode(&mut self) -> Result<&mut Self, Error<SPI::Error, Delay::Error>> {
self.set_power_mode(PowerMode::Normal)
}
pub fn set_brightness_bias(
&mut self,
level: BrightnessLevel,
) -> Result<&mut Self, Error<SPI::Error, Delay::Error>> {
self.write_reg(RegisterAddress::Brightness, level as u8)?;
self.wait_idle()
}
pub fn set_contrast(
&mut self,
level: Level,
) -> Result<&mut Self, Error<SPI::Error, Delay::Error>> {
self.write_reg(RegisterAddress::Contrast, level as u8)?;
self.wait_idle()
}
pub fn set_saturation(
&mut self,
level: Level,
) -> Result<&mut Self, Error<SPI::Error, Delay::Error>> {
self.write_reg(RegisterAddress::Saturation, level as u8)?;
self.wait_idle()
}
pub fn set_exposure(
&mut self,
level: Level,
) -> Result<&mut Self, Error<SPI::Error, Delay::Error>> {
self.write_reg(RegisterAddress::Exposure, level as u8)?;
self.wait_idle()
}
pub fn set_color_effect(
&mut self,
effect: ColorEffect,
) -> Result<&mut Self, Error<SPI::Error, Delay::Error>> {
self.write_reg(RegisterAddress::ColorEffect, effect as u8)?;
self.wait_idle()
}
#[cfg_attr(docsrs, doc(cfg(feature = "3mp")))]
#[cfg(feature = "3mp")]
pub fn set_sharpness(
&mut self,
level: SharpnessLevel,
) -> Result<&mut Self, Error<SPI::Error, Delay::Error>> {
self.write_reg(RegisterAddress::Sharpness, level as u8)?;
self.wait_idle()
}
}
#[derive(Copy, Clone, Debug)]
pub enum Error<SPI, Delay> {
Spi(SPI),
Delay(Delay),
}
pub fn find_jpeg_eof(data: &[u8]) -> Option<usize> {
data.windows(2)
.enumerate()
.filter_map(|(i, p)| match p {
[0xff, 0xd9] => Some(i + 2),
_ => None,
})
.last()
}
#[cfg(test)]
mod tests {
use super::*;
use embedded_hal_mock::{
delay,
spi::{self, Transaction},
};
#[test]
fn find_jpeg_eof_finds_first_byte() {
assert_eq!(find_jpeg_eof(&[0xff, 0xd9]).unwrap(), 2);
}
#[test]
fn find_jpeg_eof_finds_not_2_aligned_byte() {
assert_eq!(find_jpeg_eof(&[0x00, 0xff, 0xd9]).unwrap(), 3);
}
#[test]
fn find_jpeg_eof_returns_last_match() {
assert_eq!(find_jpeg_eof(&[0xff, 0xd9, 0xff, 0xd9]).unwrap(), 4);
}
#[test]
fn find_jpeg_eof_identifies_missing_tag() {
assert!(find_jpeg_eof(&[]).is_none());
}
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());
};
}
macro_rules! expect {
(send $send:expr, receive $recv:expr) => {
[
Transaction::transaction_start(),
Transaction::transfer($send, $recv),
Transaction::transaction_end(),
]
};
(send $send:expr, receive $recv:expr, wait_idle) => {
[
Transaction::transaction_start(),
Transaction::transfer($send, $recv),
Transaction::transaction_end(),
Transaction::transaction_start(),
Transaction::transfer(vec![0x44], vec![0x00, 0x00, 0x02]),
Transaction::transaction_end(),
]
};
(send $send:expr) => {
[
Transaction::transaction_start(),
Transaction::write_vec($send),
Transaction::transaction_end(),
]
};
(send $send:expr, wait_idle) => {
[
Transaction::transaction_start(),
Transaction::write_vec($send),
Transaction::transaction_end(),
Transaction::transaction_start(),
Transaction::transfer(vec![0x44], vec![0x00, 0x00, 0x02]),
Transaction::transaction_end(),
]
};
}
#[test]
fn read_reg_returns_third_byte() {
let expectations = expect!(send vec![0x45], receive vec![0x00, 0x00, 0x01]);
harness!(expectations, spi, cam);
assert_eq!(cam.read_reg(RegisterAddress::FifoSize1).unwrap(), 0x01);
spi.done();
}
#[test]
fn write_reg_transforms_addr() {
let expectations = expect!(send vec![0xc5, 0x02]);
harness!(expectations, spi, cam);
cam.write_reg(RegisterAddress::FifoSize1, 0x02).unwrap();
spi.done();
}
#[test]
fn reset_sends_correct_data() {
let expectations = expect!(send vec![0x87, 0x40], wait_idle);
harness!(expectations, spi, cam);
cam.reset().unwrap();
spi.done();
}
#[test]
fn wait_idle_returns_immediately_if_idle() {
let expectations = expect!(send vec![0x44], receive vec![0x00, 0x00, 0x02]);
harness!(expectations, spi, cam);
cam.wait_idle().unwrap();
spi.done();
}
#[test]
fn wait_idle_waits_until_idle() {
let expectations = [
Transaction::transaction_start(),
Transaction::transfer(vec![0x44], vec![0x00, 0x00, 0x01]),
Transaction::transaction_end(),
Transaction::transaction_start(),
Transaction::transfer(vec![0x44], vec![0x00, 0x00, 0x01]),
Transaction::transaction_end(),
Transaction::transaction_start(),
Transaction::transfer(vec![0x44], vec![0x00, 0x00, 0x01]),
Transaction::transaction_end(),
Transaction::transaction_start(),
Transaction::transfer(vec![0x44], vec![0x01, 0x01, 0x02]),
Transaction::transaction_end(),
];
harness!(expectations, spi, cam);
cam.wait_idle().unwrap();
spi.done();
}
#[test]
fn get_camera_type_reads_a_register_and_returns_an_enum() {
let expectations = [
Transaction::transaction_start(),
Transaction::transfer(vec![0x40], vec![0x00, 0x00, 0x81]),
Transaction::transaction_end(),
Transaction::transaction_start(),
Transaction::transfer(vec![0x40], vec![0x00, 0x00, 0x82]),
Transaction::transaction_end(),
Transaction::transaction_start(),
Transaction::transfer(vec![0x40], vec![0x00, 0x00, 0x33]),
Transaction::transaction_end(),
];
harness!(expectations, spi, cam);
assert_eq!(cam.get_camera_type().unwrap(), CameraType::OV5640);
assert_eq!(cam.get_camera_type().unwrap(), CameraType::OV3640);
assert_eq!(cam.get_camera_type().unwrap(), CameraType::Unknown(0x33));
spi.done();
}
#[cfg(feature = "5mp")]
#[test]
fn set_auto_focus_writes_a_register() {
let expectations = expect!(send vec![0xa9, 0x33], wait_idle);
harness!(expectations, spi, cam);
cam.set_auto_focus(0x33).unwrap();
spi.done();
}
#[test]
fn set_format_writes_a_register() {
let expectations = expect!(send vec![0xa0, 0x01], wait_idle);
harness!(expectations, spi, cam);
cam.set_format(Format::default()).unwrap();
spi.done();
}
#[test]
fn set_resolution_writes_a_register() {
let expectations = expect!(send vec![0xa1, 0x84], wait_idle);
harness!(expectations, spi, cam);
cam.set_resolution(Resolution::Hd).unwrap();
spi.done();
}
#[test]
fn set_debug_device_address_writes_a_register() {
let expectations = expect!(send vec![0x8a, 0x33], wait_idle);
harness!(expectations, spi, cam);
cam.set_debug_device_address(0x33).unwrap();
spi.done();
}
#[test]
fn clear_fifo_writes_a_register() {
let expectations = expect!(send vec![0x84, 0x01]);
harness!(expectations, spi, cam);
cam.clear_fifo().unwrap();
spi.done();
}
#[test]
fn capture_finished_detects_unfinished() {
let expectations = expect!(send vec![0x44], receive vec![0x00, 0x00, 0xfb]);
harness!(expectations, spi, cam);
assert!(!cam.capture_finished().unwrap());
spi.done();
}
#[test]
fn capture_finished_detects_finished() {
let expectations = expect!(send vec![0x44], receive vec![0x00, 0x00, 0x04]);
harness!(expectations, spi, cam);
assert!(cam.capture_finished().unwrap());
spi.done();
}
#[test]
fn capture_no_block_clears_fifo_and_writes_reg() {
let expectations = [
Transaction::transaction_start(),
Transaction::write_vec(vec![0x84, 0x01]),
Transaction::transaction_end(),
Transaction::transaction_start(),
Transaction::write_vec(vec![0x84, 0x02]),
Transaction::transaction_end(),
];
harness!(expectations, spi, cam);
cam.capture_noblock().unwrap();
spi.done();
}
#[test]
fn capture_returns_instantly_after_capture_finishes() {
let expectations = [
Transaction::transaction_start(),
Transaction::write_vec(vec![0x84, 0x01]),
Transaction::transaction_end(),
Transaction::transaction_start(),
Transaction::write_vec(vec![0x84, 0x02]),
Transaction::transaction_end(),
Transaction::transaction_start(),
Transaction::transfer(vec![0x44], vec![0x00, 0x00, 0x04]),
Transaction::transaction_end(),
];
harness!(expectations, spi, cam);
cam.capture().unwrap();
spi.done();
}
#[test]
fn capture_blocks_until_capture_finished() {
let expectations = [
Transaction::transaction_start(),
Transaction::write_vec(vec![0x84, 0x01]),
Transaction::transaction_end(),
Transaction::transaction_start(),
Transaction::write_vec(vec![0x84, 0x02]),
Transaction::transaction_end(),
Transaction::transaction_start(),
Transaction::transfer(vec![0x44], vec![0x00, 0x00, 0x00]),
Transaction::transaction_end(),
Transaction::transaction_start(),
Transaction::transfer(vec![0x44], vec![0x00, 0x00, 0x00]),
Transaction::transaction_end(),
Transaction::transaction_start(),
Transaction::transfer(vec![0x44], vec![0x00, 0x00, 0x00]),
Transaction::transaction_end(),
Transaction::transaction_start(),
Transaction::transfer(vec![0x44], vec![0x00, 0x00, 0x04]),
Transaction::transaction_end(),
];
harness!(expectations, spi, cam);
cam.capture().unwrap();
spi.done();
}
#[test]
fn read_fifo_length_reads_three_regs() {
let expectations = [
Transaction::transaction_start(),
Transaction::transfer(vec![0x45], vec![0x00, 0x00, 0xab]),
Transaction::transaction_end(),
Transaction::transaction_start(),
Transaction::transfer(vec![0x46], vec![0x00, 0x00, 0xbc]),
Transaction::transaction_end(),
Transaction::transaction_start(),
Transaction::transfer(vec![0x47], vec![0x00, 0x00, 0xcd]),
Transaction::transaction_end(),
];
harness!(expectations, spi, cam);
assert_eq!(cam.read_fifo_length().unwrap(), 13483179);
spi.done();
}
#[test]
fn read_fifo_byte_reads_a_register() {
let expectations = expect!(send vec![0x3d], receive vec![0x00, 0x00, 0x33]);
harness!(expectations, spi, cam);
assert_eq!(cam.read_fifo_byte().unwrap(), 0x33);
spi.done();
}
#[test]
fn read_fifo_full_skips_first_two_bytes() {
let mut buffer = [0; 2];
let expectations = expect!(send vec![0x3c], receive vec![0x00, 0x00, 0x33, 0x01]);
harness!(expectations, spi, cam);
cam.read_fifo_full(&mut buffer).unwrap();
assert_eq!(buffer, [0x33, 0x01]);
spi.done();
}
#[test]
fn read_fifo_full_skips_first_two_bytes_on_long_transfers() {
let mut buffer = [0; 126];
let expectations = [
Transaction::transaction_start(),
Transaction::transfer(vec![0x3c], vec![0x11; 65]),
Transaction::transfer(vec![0x3c], vec![0x22; 65]),
Transaction::transaction_end(),
];
harness!(expectations, spi, cam);
cam.read_fifo_full(&mut buffer).unwrap();
assert_eq!(
buffer.iter().map(|i| *i as u32).sum::<u32>(),
63 * 0x11 + 63 * 0x22
);
spi.done();
}
#[test]
fn read_fifo_full_hands_partial_reads() {
let mut buffer = [0; 100];
let expectations = [
Transaction::transaction_start(),
Transaction::transfer(vec![0x3c], vec![0x11; 65]),
Transaction::transfer(vec![0x3c], vec![0x22; 39]),
Transaction::transaction_end(),
];
harness!(expectations, spi, cam);
cam.read_fifo_full(&mut buffer).unwrap();
assert_eq!(
buffer.iter().map(|i| *i as u32).sum::<u32>(),
63 * 0x11 + 37 * 0x22
);
spi.done();
}
#[test]
fn set_auto_camera_control_writes_a_register() {
let expectations = expect!(send vec![0xaa, 0x81], wait_idle);
harness!(expectations, spi, cam);
cam.set_auto_camera_control(CameraControl::Exposure, ControlValue::Enable)
.unwrap();
spi.done();
}
#[test]
fn enable_auto_white_balance_writes_a_register() {
let expectations = expect!(send vec![0xaa, 0x82], wait_idle);
harness!(expectations, spi, cam);
cam.enable_auto_white_balance().unwrap();
spi.done();
}
#[test]
fn disable_auto_white_balance_writes_a_register() {
let expectations = expect!(send vec![0xaa, 0x02], wait_idle);
harness!(expectations, spi, cam);
cam.disable_auto_white_balance().unwrap();
spi.done();
}
#[test]
fn enable_auto_iso_writes_a_register() {
let expectations = expect!(send vec![0xaa, 0x80], wait_idle);
harness!(expectations, spi, cam);
cam.enable_auto_iso().unwrap();
spi.done();
}
#[test]
fn disable_auto_iso_writes_a_register() {
let expectations = expect!(send vec![0xaa, 0x00], wait_idle);
harness!(expectations, spi, cam);
cam.disable_auto_iso().unwrap();
spi.done();
}
#[test]
fn enable_auto_exposure_writes_a_register() {
let expectations = expect!(send vec![0xaa, 0x81], wait_idle);
harness!(expectations, spi, cam);
cam.enable_auto_exposure().unwrap();
spi.done();
}
#[test]
fn disable_auto_exposure_writes_a_register() {
let expectations = expect!(send vec![0xaa, 0x01], wait_idle);
harness!(expectations, spi, cam);
cam.disable_auto_exposure().unwrap();
spi.done();
}
#[test]
fn set_white_balance_mode_disables_autowb_and_writes_a_reg() {
let expectations = [
Transaction::transaction_start(),
Transaction::write_vec(vec![0xaa, 0x02]),
Transaction::transaction_end(),
Transaction::transaction_start(),
Transaction::transfer(vec![0x44], vec![0x00, 0x00, 0x02]),
Transaction::transaction_end(),
Transaction::transaction_start(),
Transaction::write_vec(vec![0xa6, 0x04]),
Transaction::transaction_end(),
Transaction::transaction_start(),
Transaction::transfer(vec![0x44], vec![0x00, 0x00, 0x02]),
Transaction::transaction_end(),
];
harness!(expectations, spi, cam);
cam.set_white_balance_mode(WhiteBalanceMode::Home).unwrap();
spi.done();
}
#[test]
fn enable_low_power_mode_writes_a_reg() {
let expectations = expect!(send vec![0x82, 0x07]);
harness!(expectations, spi, cam);
cam.enable_low_power_mode().unwrap();
spi.done();
}
#[test]
fn disable_low_power_mode_writes_a_reg() {
let expectations = expect!(send vec![0x82, 0x05]);
harness!(expectations, spi, cam);
cam.disable_low_power_mode().unwrap();
spi.done();
}
#[test]
fn set_brightness_bias_writes_a_reg() {
let expectations = expect!(send vec![0xa2, 0x03], wait_idle);
harness!(expectations, spi, cam);
cam.set_brightness_bias(BrightnessLevel::PlusTwo).unwrap();
spi.done();
}
#[test]
fn set_contrast_writes_a_reg() {
let expectations = expect!(send vec![0xa3, 0x03], wait_idle);
harness!(expectations, spi, cam);
cam.set_contrast(Level::PlusTwo).unwrap();
spi.done();
}
#[test]
fn set_saturation_writes_a_reg() {
let expectations = expect!(send vec![0xa4, 0x03], wait_idle);
harness!(expectations, spi, cam);
cam.set_saturation(Level::PlusTwo).unwrap();
spi.done();
}
#[test]
fn set_exposure_writes_a_reg() {
let expectations = expect!(send vec![0xa5, 0x03], wait_idle);
harness!(expectations, spi, cam);
cam.set_exposure(Level::PlusTwo).unwrap();
spi.done();
}
#[test]
fn set_color_effect_writes_a_reg() {
let expectations = expect!(send vec![0xa7, 0x03], wait_idle);
harness!(expectations, spi, cam);
cam.set_color_effect(ColorEffect::BlackWhite).unwrap();
spi.done();
}
#[cfg(feature = "3mp")]
#[test]
fn set_sharpness_writes_a_reg() {
let expectations = expect!(send vec![0xa8, 0x03], wait_idle);
harness!(expectations, spi, cam);
cam.set_sharpness(SharpnessLevel::Three).unwrap();
spi.done();
}
}