diff --git a/esp-hal/CHANGELOG.md b/esp-hal/CHANGELOG.md index d00e000d727..3cb831770c2 100644 --- a/esp-hal/CHANGELOG.md +++ b/esp-hal/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - ESP32-PICO-V3-02: Initial support (#1155) - `time::current_time` API (#1503) +- ESP32-S3: Add LCD_CAM Camera driver (#1483) ### Fixed diff --git a/esp-hal/src/lcd_cam/cam.rs b/esp-hal/src/lcd_cam/cam.rs index 7c2381316e1..c27ee4ea90d 100644 --- a/esp-hal/src/lcd_cam/cam.rs +++ b/esp-hal/src/lcd_cam/cam.rs @@ -1,5 +1,581 @@ -use crate::{peripheral::PeripheralRef, peripherals::LCD_CAM}; +//! # Camera - Master or Slave Mode +//! +//! ## Overview +//! The LCD_CAM peripheral supports receiving 8/16 bit DVP signals in either +//! master or slave mode. In master mode, the peripheral provides the master +//! clock to drive the camera, in slave mode it does not. This is configured +//! with the `with_master_clock` method on the camera driver. The driver (due to +//! the peripheral) mandates DMA (Direct Memory Access) for efficient data +//! transfer. +//! +//! ## Examples +//! Following code shows how to receive some bytes from an 8 bit DVP stream in +//! master mode. +//! +//! ```no_run +//! let mclk_pin = io.pins.gpio15; +//! let vsync_pin = io.pins.gpio6; +//! let href_pin = io.pins.gpio7; +//! let pclk_pin = io.pins.gpio13; +//! let data_pins = RxEightBits::new( +//! io.pins.gpio11, +//! io.pins.gpio9, +//! io.pins.gpio8, +//! io.pins.gpio10, +//! io.pins.gpio12, +//! io.pins.gpio18, +//! io.pins.gpio17, +//! io.pins.gpio16, +//! ); +//! +//! let lcd_cam = LcdCam::new(peripherals.LCD_CAM); +//! let mut camera = Camera::new(lcd_cam.cam, channel.rx, data_pins, 20u32.MHz(), &clocks) +//! .with_master_clock(mclk_pin) // Remove this for slave mode. +//! .with_ctrl_pins(vsync_pin, href_pin, pclk_pin); +//! ``` + +use core::mem::size_of; + +use embedded_dma::WriteBuffer; +use fugit::HertzU32; + +use crate::{ + clock::Clocks, + dma::{ + ChannelRx, + ChannelTypes, + DmaError, + DmaPeripheral, + DmaTransfer, + LcdCamPeripheral, + RegisterAccess, + Rx, + RxChannel, + RxPrivate, + }, + gpio::{InputPin, InputSignal, OutputPin, OutputSignal}, + i2s::Error, + lcd_cam::{cam::private::RxPins, private::calculate_clkm, BitOrder, ByteOrder}, + peripheral::{Peripheral, PeripheralRef}, + peripherals::LCD_CAM, +}; + +/// Generation of GDMA SUC EOF +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum EofMode { + /// Generate GDMA SUC EOF by data byte length + ByteLen, + /// Generate GDMA SUC EOF by the vsync signal + VsyncSignal, +} + +/// Vsync Filter Threshold +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum VsyncFilterThreshold { + One, + Two, + Three, + Four, + Five, + Six, + Seven, + Eight, +} pub struct Cam<'d> { - pub(crate) _lcd_cam: PeripheralRef<'d, LCD_CAM>, + pub(crate) lcd_cam: PeripheralRef<'d, LCD_CAM>, +} + +pub struct Camera<'d, RX> { + lcd_cam: PeripheralRef<'d, LCD_CAM>, + rx_channel: RX, + // 1 or 2 + bus_width: usize, +} + +impl<'d, T, R> Camera<'d, ChannelRx<'d, T, R>> +where + T: RxChannel, + R: ChannelTypes + RegisterAccess, + R::P: LcdCamPeripheral, +{ + pub fn new( + cam: Cam<'d>, + mut channel: ChannelRx<'d, T, R>, + _pins: P, + frequency: HertzU32, + clocks: &Clocks, + ) -> Self { + let lcd_cam = cam.lcd_cam; + + let (i, divider) = calculate_clkm( + frequency.to_Hz() as _, + &[ + clocks.xtal_clock.to_Hz() as _, + clocks.cpu_clock.to_Hz() as _, + clocks.crypto_pwm_clock.to_Hz() as _, + ], + ); + + lcd_cam.cam_ctrl().write(|w| { + // Force enable the clock for all configuration registers. + unsafe { + w.cam_clk_sel() + .bits((i + 1) as _) + .cam_clkm_div_num() + .bits(divider.div_num as _) + .cam_clkm_div_b() + .bits(divider.div_b as _) + .cam_clkm_div_a() + .bits(divider.div_a as _) + .cam_vsync_filter_thres() + .bits(0) + .cam_vs_eof_en() + .set_bit() + .cam_line_int_en() + .clear_bit() + .cam_stop_en() + .clear_bit() + } + }); + lcd_cam.cam_ctrl1().write(|w| unsafe { + w.cam_vh_de_mode_en() + .set_bit() + .cam_rec_data_bytelen() + .bits(0) + .cam_line_int_num() + .bits(0) + .cam_vsync_filter_en() + .clear_bit() + .cam_2byte_en() + .clear_bit() + .cam_clk_inv() + .clear_bit() + .cam_de_inv() + .clear_bit() + .cam_hsync_inv() + .clear_bit() + .cam_vsync_inv() + .clear_bit() + }); + + lcd_cam + .cam_rgb_yuv() + .write(|w| w.cam_conv_bypass().clear_bit()); + + lcd_cam.cam_ctrl().modify(|_, w| w.cam_update().set_bit()); + + channel.init_channel(); + + Self { + lcd_cam, + rx_channel: channel, + bus_width: P::BUS_WIDTH, + } + } +} + +impl<'d, RX: Rx> Camera<'d, RX> { + pub fn set_byte_order(&mut self, byte_order: ByteOrder) -> &mut Self { + self.lcd_cam + .cam_ctrl() + .modify(|_, w| w.cam_byte_order().bit(byte_order != ByteOrder::default())); + self + } + + pub fn set_bit_order(&mut self, bit_order: BitOrder) -> &mut Self { + self.lcd_cam + .cam_ctrl() + .modify(|_, w| w.cam_bit_order().bit(bit_order != BitOrder::default())); + self + } + + pub fn set_vsync_filter(&mut self, threshold: Option) -> &mut Self { + if let Some(threshold) = threshold { + let value = match threshold { + VsyncFilterThreshold::One => 0, + VsyncFilterThreshold::Two => 1, + VsyncFilterThreshold::Three => 2, + VsyncFilterThreshold::Four => 3, + VsyncFilterThreshold::Five => 4, + VsyncFilterThreshold::Six => 5, + VsyncFilterThreshold::Seven => 6, + VsyncFilterThreshold::Eight => 7, + }; + + self.lcd_cam + .cam_ctrl() + .modify(|_, w| unsafe { w.cam_vsync_filter_thres().bits(value) }); + self.lcd_cam + .cam_ctrl1() + .modify(|_, w| w.cam_vsync_filter_en().set_bit()); + } else { + self.lcd_cam + .cam_ctrl1() + .modify(|_, w| w.cam_vsync_filter_en().clear_bit()); + } + self + } + + pub fn with_master_clock(self, mclk: impl Peripheral

+ 'd) -> Self { + crate::into_ref!(mclk); + mclk.set_to_push_pull_output() + .connect_peripheral_to_output(OutputSignal::CAM_CLK); + self + } + + pub fn with_ctrl_pins( + self, + vsync: impl Peripheral

+ 'd, + h_enable: impl Peripheral

+ 'd, + pclk: impl Peripheral

+ 'd, + ) -> Self { + crate::into_ref!(vsync); + crate::into_ref!(h_enable); + crate::into_ref!(pclk); + + vsync + .set_to_input() + .connect_input_to_peripheral(InputSignal::CAM_V_SYNC); + h_enable + .set_to_input() + .connect_input_to_peripheral(InputSignal::CAM_H_ENABLE); + pclk.set_to_input() + .connect_input_to_peripheral(InputSignal::CAM_PCLK); + + self + } + + // Reset Camera control unit and Async Rx FIFO + fn reset_unit_and_fifo(&self) { + self.lcd_cam + .cam_ctrl1() + .modify(|_, w| w.cam_reset().set_bit()); + self.lcd_cam + .cam_ctrl1() + .modify(|_, w| w.cam_reset().clear_bit()); + self.lcd_cam + .cam_ctrl1() + .modify(|_, w| w.cam_afifo_reset().set_bit()); + self.lcd_cam + .cam_ctrl1() + .modify(|_, w| w.cam_afifo_reset().clear_bit()); + } + + // Start the Camera unit to listen for incoming DVP stream. + fn start_unit(&self) { + self.lcd_cam + .cam_ctrl() + .modify(|_, w| w.cam_update().set_bit()); + self.lcd_cam + .cam_ctrl1() + .modify(|_, w| w.cam_start().set_bit()); + } + + fn start_dma( + &mut self, + circular: bool, + buf: &mut RXBUF, + ) -> Result<(), DmaError> { + let (ptr, len) = unsafe { buf.write_buffer() }; + + assert_eq!(self.bus_width, size_of::()); + + unsafe { + self.rx_channel.prepare_transfer_without_start( + circular, + DmaPeripheral::LcdCam, + ptr as _, + len * size_of::(), + )?; + } + self.rx_channel.start_transfer() + } + + pub fn read_dma<'t, RXBUF: WriteBuffer>( + &'t mut self, + buf: &'t mut RXBUF, + ) -> Result, DmaError> { + self.reset_unit_and_fifo(); + // Start DMA to receive incoming transfer. + self.start_dma(false, buf)?; + self.start_unit(); + + Ok(Transfer { instance: self }) + } + + pub fn read_dma_circular<'t, RXBUF: WriteBuffer>( + &'t mut self, + buf: &'t mut RXBUF, + ) -> Result, DmaError> { + self.reset_unit_and_fifo(); + // Start DMA to receive incoming transfer. + self.start_dma(true, buf)?; + self.start_unit(); + + Ok(Transfer { instance: self }) + } +} + +/// An in-progress transfer +#[must_use] +pub struct Transfer<'t, 'd, RX: Rx> { + instance: &'t mut Camera<'d, RX>, +} + +impl<'t, 'd, RX: Rx> Transfer<'t, 'd, RX> { + /// Amount of bytes which can be popped + pub fn available(&mut self) -> usize { + self.instance.rx_channel.available() + } + + pub fn pop(&mut self, data: &mut [u8]) -> Result { + Ok(self.instance.rx_channel.pop(data)?) + } + + /// Wait for the DMA transfer to complete. + /// Length of the received data is returned + #[allow(clippy::type_complexity)] + pub fn wait_receive(self, dst: &mut [u8]) -> Result { + // Wait for DMA transfer to finish. + while !self.is_done() {} + + let len = self + .instance + .rx_channel + .drain_buffer(dst) + .map_err(|e| (e, 0))?; + + if self.instance.rx_channel.has_error() { + Err((DmaError::DescriptorError, len)) + } else { + Ok(len) + } + } +} + +impl<'t, 'd, RX: Rx> DmaTransfer for Transfer<'t, 'd, RX> { + fn wait(self) -> Result<(), DmaError> { + // Wait for DMA transfer to finish. + while !self.is_done() {} + + let ch = &self.instance.rx_channel; + if ch.has_error() { + Err(DmaError::DescriptorError) + } else { + Ok(()) + } + } + + fn is_done(&self) -> bool { + let ch = &self.instance.rx_channel; + // Wait for IN_SUC_EOF (i.e. VSYNC) + ch.is_done() || + // Or for IN_DSCR_EMPTY (i.e. No more buffer space) + ch.has_dscr_empty_error() || + // Or for IN_DSCR_ERR (i.e. bad descriptor) + ch.has_error() + } +} + +impl<'t, 'd, RX: Rx> Drop for Transfer<'t, 'd, RX> { + fn drop(&mut self) { + self.instance + .lcd_cam + .cam_ctrl1() + .modify(|_, w| w.cam_start().clear_bit()); + // TODO: Stop DMA?? self.instance.rx_channel.stop_transfer(); + } +} + +pub struct RxEightBits { + _pins: (), +} + +impl RxEightBits { + #[allow(clippy::too_many_arguments)] + pub fn new<'d, P0, P1, P2, P3, P4, P5, P6, P7>( + pin_0: impl Peripheral

+ 'd, + pin_1: impl Peripheral

+ 'd, + pin_2: impl Peripheral

+ 'd, + pin_3: impl Peripheral

+ 'd, + pin_4: impl Peripheral

+ 'd, + pin_5: impl Peripheral

+ 'd, + pin_6: impl Peripheral

+ 'd, + pin_7: impl Peripheral

+ 'd, + ) -> Self + where + P0: InputPin, + P1: InputPin, + P2: InputPin, + P3: InputPin, + P4: InputPin, + P5: InputPin, + P6: InputPin, + P7: InputPin, + { + crate::into_ref!(pin_0); + crate::into_ref!(pin_1); + crate::into_ref!(pin_2); + crate::into_ref!(pin_3); + crate::into_ref!(pin_4); + crate::into_ref!(pin_5); + crate::into_ref!(pin_6); + crate::into_ref!(pin_7); + + pin_0 + .set_to_input() + .connect_input_to_peripheral(InputSignal::CAM_DATA_0); + pin_1 + .set_to_input() + .connect_input_to_peripheral(InputSignal::CAM_DATA_1); + pin_2 + .set_to_input() + .connect_input_to_peripheral(InputSignal::CAM_DATA_2); + pin_3 + .set_to_input() + .connect_input_to_peripheral(InputSignal::CAM_DATA_3); + pin_4 + .set_to_input() + .connect_input_to_peripheral(InputSignal::CAM_DATA_4); + pin_5 + .set_to_input() + .connect_input_to_peripheral(InputSignal::CAM_DATA_5); + pin_6 + .set_to_input() + .connect_input_to_peripheral(InputSignal::CAM_DATA_6); + pin_7 + .set_to_input() + .connect_input_to_peripheral(InputSignal::CAM_DATA_7); + + Self { _pins: () } + } +} + +impl RxPins for RxEightBits { + const BUS_WIDTH: usize = 1; +} + +pub struct RxSixteenBits { + _pins: (), +} + +impl RxSixteenBits { + #[allow(clippy::too_many_arguments)] + pub fn new<'d, P0, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15>( + pin_0: impl Peripheral

+ 'd, + pin_1: impl Peripheral

+ 'd, + pin_2: impl Peripheral

+ 'd, + pin_3: impl Peripheral

+ 'd, + pin_4: impl Peripheral

+ 'd, + pin_5: impl Peripheral

+ 'd, + pin_6: impl Peripheral

+ 'd, + pin_7: impl Peripheral

+ 'd, + pin_8: impl Peripheral

+ 'd, + pin_9: impl Peripheral

+ 'd, + pin_10: impl Peripheral

+ 'd, + pin_11: impl Peripheral

+ 'd, + pin_12: impl Peripheral

+ 'd, + pin_13: impl Peripheral

+ 'd, + pin_14: impl Peripheral

+ 'd, + pin_15: impl Peripheral

+ 'd, + ) -> Self + where + P0: InputPin, + P1: InputPin, + P2: InputPin, + P3: InputPin, + P4: InputPin, + P5: InputPin, + P6: InputPin, + P7: InputPin, + P8: InputPin, + P9: InputPin, + P10: InputPin, + P11: InputPin, + P12: InputPin, + P13: InputPin, + P14: InputPin, + P15: InputPin, + { + crate::into_ref!(pin_0); + crate::into_ref!(pin_1); + crate::into_ref!(pin_2); + crate::into_ref!(pin_3); + crate::into_ref!(pin_4); + crate::into_ref!(pin_5); + crate::into_ref!(pin_6); + crate::into_ref!(pin_7); + crate::into_ref!(pin_8); + crate::into_ref!(pin_9); + crate::into_ref!(pin_10); + crate::into_ref!(pin_11); + crate::into_ref!(pin_12); + crate::into_ref!(pin_13); + crate::into_ref!(pin_14); + crate::into_ref!(pin_15); + + pin_0 + .set_to_input() + .connect_input_to_peripheral(InputSignal::CAM_DATA_0); + pin_1 + .set_to_input() + .connect_input_to_peripheral(InputSignal::CAM_DATA_1); + pin_2 + .set_to_input() + .connect_input_to_peripheral(InputSignal::CAM_DATA_2); + pin_3 + .set_to_input() + .connect_input_to_peripheral(InputSignal::CAM_DATA_3); + pin_4 + .set_to_input() + .connect_input_to_peripheral(InputSignal::CAM_DATA_4); + pin_5 + .set_to_input() + .connect_input_to_peripheral(InputSignal::CAM_DATA_5); + pin_6 + .set_to_input() + .connect_input_to_peripheral(InputSignal::CAM_DATA_6); + pin_7 + .set_to_input() + .connect_input_to_peripheral(InputSignal::CAM_DATA_7); + pin_8 + .set_to_input() + .connect_input_to_peripheral(InputSignal::CAM_DATA_8); + pin_9 + .set_to_input() + .connect_input_to_peripheral(InputSignal::CAM_DATA_9); + pin_10 + .set_to_input() + .connect_input_to_peripheral(InputSignal::CAM_DATA_10); + pin_11 + .set_to_input() + .connect_input_to_peripheral(InputSignal::CAM_DATA_11); + pin_12 + .set_to_input() + .connect_input_to_peripheral(InputSignal::CAM_DATA_12); + pin_13 + .set_to_input() + .connect_input_to_peripheral(InputSignal::CAM_DATA_13); + pin_14 + .set_to_input() + .connect_input_to_peripheral(InputSignal::CAM_DATA_14); + pin_15 + .set_to_input() + .connect_input_to_peripheral(InputSignal::CAM_DATA_15); + + Self { _pins: () } + } +} + +impl RxPins for RxSixteenBits { + const BUS_WIDTH: usize = 2; +} + +mod private { + pub trait RxPins { + const BUS_WIDTH: usize; + } } diff --git a/esp-hal/src/lcd_cam/mod.rs b/esp-hal/src/lcd_cam/mod.rs index 9afcf996be0..527df73370b 100644 --- a/esp-hal/src/lcd_cam/mod.rs +++ b/esp-hal/src/lcd_cam/mod.rs @@ -31,7 +31,7 @@ impl<'d> LcdCam<'d> { lcd_cam: unsafe { lcd_cam.clone_unchecked() }, }, cam: Cam { - _lcd_cam: unsafe { lcd_cam.clone_unchecked() }, + lcd_cam: unsafe { lcd_cam.clone_unchecked() }, }, } } diff --git a/examples/src/bin/lcd_cam_ov2640.rs b/examples/src/bin/lcd_cam_ov2640.rs new file mode 100644 index 00000000000..b1c9860936e --- /dev/null +++ b/examples/src/bin/lcd_cam_ov2640.rs @@ -0,0 +1,423 @@ +//! Drives the camera on a Freenove ESP32-S3 WROOM (also works as is on the ESP32S3-EYE) +//! +//! This example reads a JPEG from an OV2640 and writes it to the console as hex. +//! +//! Pins used: +//! SIOD GPIO4 +//! SIOC GPIO5 +//! XCLK GPIO15 +//! VSYNC GPIO6 +//! HREF GPIO7 +//! PCLK GPIO13 +//! D2 GPIO11 +//! D3 GPIO9 +//! D4 GPIO8 +//! D5 GPIO10 +//! D6 GPIO12 +//! D7 GPIO18 +//! D8 GPIO17 +//! D9 GPIO16 + +//% CHIPS: esp32s3 + +#![no_std] +#![no_main] + +use esp_backtrace as _; +use esp_hal::{ + clock::ClockControl, + delay::Delay, + dma::{Dma, DmaPriority}, + dma_buffers, + gpio::Io, + i2c, + i2c::I2C, + lcd_cam::{ + cam::{Camera, RxEightBits}, + LcdCam, + }, + peripherals::Peripherals, + prelude::*, + system::SystemControl, + Blocking, +}; +use esp_println::println; + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take(); + let system = SystemControl::new(peripherals.SYSTEM); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); + + let dma = Dma::new(peripherals.DMA); + let channel = dma.channel0; + + let (_, mut tx_descriptors, rx_buffer, mut rx_descriptors) = dma_buffers!(0, 32678); + + let channel = channel.configure( + false, + &mut tx_descriptors, + &mut rx_descriptors, + DmaPriority::Priority0, + ); + + let cam_siod = io.pins.gpio4; + let cam_sioc = io.pins.gpio5; + let cam_xclk = io.pins.gpio15; + let cam_vsync = io.pins.gpio6; + let cam_href = io.pins.gpio7; + let cam_pclk = io.pins.gpio13; + let cam_data_pins = RxEightBits::new( + io.pins.gpio11, + io.pins.gpio9, + io.pins.gpio8, + io.pins.gpio10, + io.pins.gpio12, + io.pins.gpio18, + io.pins.gpio17, + io.pins.gpio16, + ); + + let lcd_cam = LcdCam::new(peripherals.LCD_CAM); + let mut camera = Camera::new(lcd_cam.cam, channel.rx, cam_data_pins, 20u32.MHz(), &clocks) + .with_master_clock(cam_xclk) + .with_ctrl_pins(cam_vsync, cam_href, cam_pclk); + + let delay = Delay::new(&clocks); + + let mut buffer = rx_buffer; + buffer.fill(0u8); + + delay.delay_millis(500u32); + + let i2c = I2C::new( + peripherals.I2C0, + cam_siod, + cam_sioc, + 100u32.kHz(), + &clocks, + None, + ); + + let mut sccb = Sccb::new(i2c); + + // Checking camera address + sccb.probe(OV2640_ADDRESS).unwrap(); + println!("Probe successful!"); + + sccb.write(OV2640_ADDRESS, 0xFF, 0x01).unwrap(); // bank sensor + let pid = sccb.read(OV2640_ADDRESS, 0x0A).unwrap(); + println!("Found PID of {:#02X}, and was expecting 0x26", pid); + + // Start waiting for camera before initialising it to prevent missing the first few bytes. + // This can be improved with a VSYNC interrupt but would complicate this example. + let transfer = camera.read_dma(&mut buffer).unwrap(); + + for (reg, value) in FIRST_BLOCK { + sccb.write(OV2640_ADDRESS, *reg, *value).unwrap(); + } + delay.delay_millis(10u32); + for (reg, value) in SECOND_BLOCK { + sccb.write(OV2640_ADDRESS, *reg, *value).unwrap(); + if *reg == 0xDD && *value == 0x7F { + delay.delay_millis(10u32); + } + } + + transfer.wait().unwrap(); + + // Note: JPEGs starts with "FF, D8, FF, E0" and end with "FF, D9" + + let index_of_end = buffer.windows(2).position(|c| c[0] == 0xFF && c[1] == 0xD9); + let index_of_end = if let Some(idx) = index_of_end { + idx + 2 + } else { + println!("Failed to find JPEG terminator"); + buffer.len() + }; + + println!("Frame data (parse with `xxd -r -p .txt image.jpg`):"); + println!("{:02X?}", &buffer[..index_of_end]); + + loop {} +} + +pub const OV2640_ADDRESS: u8 = 0x30; + +pub struct Sccb<'d, T> { + i2c: I2C<'d, T, Blocking>, +} + +impl<'d, T> Sccb<'d, T> +where + T: i2c::Instance, +{ + pub fn new(i2c: I2C<'d, T, Blocking>) -> Self { + Self { i2c } + } + + pub fn probe(&mut self, address: u8) -> Result<(), i2c::Error> { + self.i2c.write(address, &[]) + } + + pub fn read(&mut self, address: u8, reg: u8) -> Result { + self.i2c.write(address, &[reg])?; + + let mut bytes = [0u8; 1]; + self.i2c.read(address, &mut bytes)?; + Ok(bytes[0]) + } + + pub fn write(&mut self, address: u8, reg: u8, data: u8) -> Result<(), i2c::Error> { + self.i2c.write(address, &[reg, data]) + } +} + +const FIRST_BLOCK: &[(u8, u8)] = &[(0xFF, 0x01), (0x12, 0x80)]; + +const SECOND_BLOCK: &[(u8, u8)] = &[ + (0xFF, 0x00), + (0x2C, 0xFF), + (0x2E, 0xDF), + (0xFF, 0x01), + (0x3C, 0x32), + (0x11, 0x01), + (0x09, 0x02), + (0x04, 0x28), + (0x13, 0xE5), + (0x14, 0x48), + (0x2C, 0x0C), + (0x33, 0x78), + (0x3A, 0x33), + (0x3B, 0xFB), + (0x3E, 0x00), + (0x43, 0x11), + (0x16, 0x10), + (0x39, 0x92), + (0x35, 0xDA), + (0x22, 0x1A), + (0x37, 0xC3), + (0x23, 0x00), + (0x34, 0xC0), + (0x06, 0x88), + (0x07, 0xC0), + (0x0D, 0x87), + (0x0E, 0x41), + (0x4C, 0x00), + (0x4A, 0x81), + (0x21, 0x99), + (0x24, 0x40), + (0x25, 0x38), + (0x26, 0x82), + (0x5C, 0x00), + (0x63, 0x00), + (0x61, 0x70), + (0x62, 0x80), + (0x7C, 0x05), + (0x20, 0x80), + (0x28, 0x30), + (0x6C, 0x00), + (0x6D, 0x80), + (0x6E, 0x00), + (0x70, 0x02), + (0x71, 0x94), + (0x73, 0xC1), + (0x3D, 0x34), + (0x5A, 0x57), + (0x4F, 0xBB), + (0x50, 0x9C), + (0x12, 0x20), + (0x17, 0x11), + (0x18, 0x43), + (0x19, 0x00), + (0x1A, 0x25), + (0x32, 0x89), + (0x37, 0xC0), + (0x4F, 0xCA), + (0x50, 0xA8), + (0x6D, 0x00), + (0x3D, 0x38), + (0xFF, 0x00), + (0xE5, 0x7F), + (0xF9, 0xC0), + (0x41, 0x24), + (0xE0, 0x14), + (0x76, 0xFF), + (0x33, 0xA0), + (0x42, 0x20), + (0x43, 0x18), + (0x4C, 0x00), + (0x87, 0x50), + (0x88, 0x3F), + (0xD7, 0x03), + (0xD9, 0x10), + (0xD3, 0x82), + (0xC8, 0x08), + (0xC9, 0x80), + (0x7C, 0x00), + (0x7D, 0x00), + (0x7C, 0x03), + (0x7D, 0x48), + (0x7D, 0x48), + (0x7C, 0x08), + (0x7D, 0x20), + (0x7D, 0x10), + (0x7D, 0x0E), + (0x90, 0x00), + (0x91, 0x0E), + (0x91, 0x1A), + (0x91, 0x31), + (0x91, 0x5A), + (0x91, 0x69), + (0x91, 0x75), + (0x91, 0x7E), + (0x91, 0x88), + (0x91, 0x8F), + (0x91, 0x96), + (0x91, 0xA3), + (0x91, 0xAF), + (0x91, 0xC4), + (0x91, 0xD7), + (0x91, 0xE8), + (0x91, 0x20), + (0x92, 0x00), + (0x93, 0x06), + (0x93, 0xE3), + (0x93, 0x05), + (0x93, 0x05), + (0x93, 0x00), + (0x93, 0x04), + (0x93, 0x00), + (0x93, 0x00), + (0x93, 0x00), + (0x93, 0x00), + (0x93, 0x00), + (0x93, 0x00), + (0x93, 0x00), + (0x96, 0x00), + (0x97, 0x08), + (0x97, 0x19), + (0x97, 0x02), + (0x97, 0x0C), + (0x97, 0x24), + (0x97, 0x30), + (0x97, 0x28), + (0x97, 0x26), + (0x97, 0x02), + (0x97, 0x98), + (0x97, 0x80), + (0x97, 0x00), + (0x97, 0x00), + (0xA4, 0x00), + (0xA8, 0x00), + (0xC5, 0x11), + (0xC6, 0x51), + (0xBF, 0x80), + (0xC7, 0x10), + (0xB6, 0x66), + (0xB8, 0xA5), + (0xB7, 0x64), + (0xB9, 0x7C), + (0xB3, 0xAF), + (0xB4, 0x97), + (0xB5, 0xFF), + (0xB0, 0xC5), + (0xB1, 0x94), + (0xB2, 0x0F), + (0xC4, 0x5C), + (0xC3, 0xFD), + (0x7F, 0x00), + (0xE5, 0x1F), + (0xE1, 0x67), + (0xDD, 0x7F), + (0xDA, 0x00), + (0xE0, 0x00), + (0x05, 0x00), + (0x05, 0x01), + (0xFF, 0x01), + (0x12, 0x40), + (0x03, 0x0A), + (0x32, 0x09), + (0x17, 0x11), + (0x18, 0x43), + (0x19, 0x00), + (0x1A, 0x4B), + (0x37, 0xC0), + (0x4F, 0xCA), + (0x50, 0xA8), + (0x5A, 0x23), + (0x6D, 0x00), + (0x3D, 0x38), + (0x39, 0x92), + (0x35, 0xDA), + (0x22, 0x1A), + (0x37, 0xC3), + (0x23, 0x00), + (0x34, 0xC0), + (0x06, 0x88), + (0x07, 0xC0), + (0x0D, 0x87), + (0x0E, 0x41), + (0x42, 0x03), + (0x4C, 0x00), + (0xFF, 0x00), + (0xE0, 0x04), + (0xC0, 0x64), + (0xC1, 0x4B), + (0x8C, 0x00), + (0x51, 0xC8), + (0x52, 0x96), + (0x53, 0x00), + (0x54, 0x00), + (0x55, 0x00), + (0x57, 0x00), + (0x86, 0x3D), + (0x50, 0x80), + (0x51, 0xC8), + (0x52, 0x96), + (0x53, 0x00), + (0x54, 0x00), + (0x55, 0x00), + (0x57, 0x00), + (0x5A, 0xA0), + (0x5B, 0x78), + (0x5C, 0x00), + (0xFF, 0x01), + (0x11, 0x00), + (0xFF, 0x00), + (0xD3, 0x10), + (0x05, 0x00), + (0xE0, 0x14), + (0xDA, 0x12), + (0xD7, 0x03), + (0xE1, 0x77), + (0xE5, 0x1F), + (0xD9, 0x10), + (0xDF, 0x80), + (0x33, 0x80), + (0x3C, 0x10), + (0xEB, 0x30), + (0xDD, 0x7F), + (0xE0, 0x00), + (0xE0, 0x14), + (0xDA, 0x12), + (0xD7, 0x03), + (0xE1, 0x77), + (0xE5, 0x1F), + (0xD9, 0x10), + (0xDF, 0x80), + (0x33, 0x80), + (0x3C, 0x10), + (0xEB, 0x30), + (0xDD, 0x7F), + (0xE0, 0x00), + (0xFF, 0x01), + (0x14, 0x08), + (0xFF, 0x00), + (0x87, 0x50), + (0x87, 0x10), + (0xC3, 0xFD), + (0x44, 0x0C), +];