Add LCD_CAM Camera driver (#1483)

* Add LCD_CAM Camera driver

* comments

* 16 bit

* Expose most config

* Module documentation

* Remove GPIO generics

* Fix breaking change

* Remove Pin generics from Camera type

* Fix unsafe

* Another breaking change

* fmt
This commit is contained in:
Dominic Fischer 2024-04-29 15:23:16 +01:00 committed by GitHub
parent 157ac17be1
commit d5d3f1f46b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 1003 additions and 3 deletions

View File

@ -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

View File

@ -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>,
R: ChannelTypes + RegisterAccess,
R::P: LcdCamPeripheral,
{
pub fn new<P: RxPins>(
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<VsyncFilterThreshold>) -> &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<MCLK: OutputPin>(self, mclk: impl Peripheral<P = MCLK> + '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<VSYNC: InputPin, HENABLE: InputPin, PCLK: InputPin>(
self,
vsync: impl Peripheral<P = VSYNC> + 'd,
h_enable: impl Peripheral<P = HENABLE> + 'd,
pclk: impl Peripheral<P = PCLK> + '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<RXBUF: WriteBuffer>(
&mut self,
circular: bool,
buf: &mut RXBUF,
) -> Result<(), DmaError> {
let (ptr, len) = unsafe { buf.write_buffer() };
assert_eq!(self.bus_width, size_of::<RXBUF::Word>());
unsafe {
self.rx_channel.prepare_transfer_without_start(
circular,
DmaPeripheral::LcdCam,
ptr as _,
len * size_of::<RXBUF::Word>(),
)?;
}
self.rx_channel.start_transfer()
}
pub fn read_dma<'t, RXBUF: WriteBuffer>(
&'t mut self,
buf: &'t mut RXBUF,
) -> Result<Transfer<'t, 'd, RX>, 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<Transfer<'t, 'd, RX>, 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<usize, Error> {
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<usize, (DmaError, usize)> {
// 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<P = P0> + 'd,
pin_1: impl Peripheral<P = P1> + 'd,
pin_2: impl Peripheral<P = P2> + 'd,
pin_3: impl Peripheral<P = P3> + 'd,
pin_4: impl Peripheral<P = P4> + 'd,
pin_5: impl Peripheral<P = P5> + 'd,
pin_6: impl Peripheral<P = P6> + 'd,
pin_7: impl Peripheral<P = P7> + '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<P = P0> + 'd,
pin_1: impl Peripheral<P = P1> + 'd,
pin_2: impl Peripheral<P = P2> + 'd,
pin_3: impl Peripheral<P = P3> + 'd,
pin_4: impl Peripheral<P = P4> + 'd,
pin_5: impl Peripheral<P = P5> + 'd,
pin_6: impl Peripheral<P = P6> + 'd,
pin_7: impl Peripheral<P = P7> + 'd,
pin_8: impl Peripheral<P = P8> + 'd,
pin_9: impl Peripheral<P = P9> + 'd,
pin_10: impl Peripheral<P = P10> + 'd,
pin_11: impl Peripheral<P = P11> + 'd,
pin_12: impl Peripheral<P = P12> + 'd,
pin_13: impl Peripheral<P = P13> + 'd,
pin_14: impl Peripheral<P = P14> + 'd,
pin_15: impl Peripheral<P = P15> + '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;
}
}

View File

@ -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() },
},
}
}

View File

@ -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 <uart>.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<u8, i2c::Error> {
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),
];