mirror of
https://github.com/laanwj/k210-sdk-stuff.git
synced 2025-01-18 21:17:09 +04:00
rust: DVP
Add support for DVP peripheral.
This commit is contained in:
parent
5f2c94fa01
commit
8cc5ff53f5
@ -30,7 +30,7 @@ Maix Go
|
|||||||
|
|
||||||
- MSA300 - Accelerometer (I2C, address 0x26)
|
- MSA300 - Accelerometer (I2C, address 0x26)
|
||||||
- STM32F103C8 - JTAG & UART, debug M1 without extra Jlink, this bypasses the CH340C on the module (USB to host, serial to K210)
|
- STM32F103C8 - JTAG & UART, debug M1 without extra Jlink, this bypasses the CH340C on the module (USB to host, serial to K210)
|
||||||
- OV2640 - Color CMOS UXGA (2.0 MegaPixel) CAMERA C HI (DVP)
|
- OV2640 - Color CMOS UXGA (2.0 MegaPixel) CAMERA C HI (DVP, SCCB address 0x60)
|
||||||
|
|
||||||
### External
|
### External
|
||||||
|
|
||||||
|
@ -4,3 +4,4 @@ pub mod lcd_colors;
|
|||||||
pub mod lcd_render;
|
pub mod lcd_render;
|
||||||
pub mod msa300;
|
pub mod msa300;
|
||||||
pub mod ns2009;
|
pub mod ns2009;
|
||||||
|
pub mod ov2640;
|
||||||
|
@ -26,6 +26,9 @@ pub const MSA300_ADDR_BITS: u32 = 7;
|
|||||||
/** I2C clock speed for MSA300 */
|
/** I2C clock speed for MSA300 */
|
||||||
pub const MSA300_CLK: u32 = 500000;
|
pub const MSA300_CLK: u32 = 500000;
|
||||||
|
|
||||||
|
/** DVP SCCB address for OV2640 */
|
||||||
|
pub const OV2640_ADDR: u8 = 0x60;
|
||||||
|
|
||||||
/** I/O pins for FPIOA */
|
/** I/O pins for FPIOA */
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
#[repr(u32)]
|
#[repr(u32)]
|
||||||
|
217
rust/k210-shared/src/board/ov2640.rs
Normal file
217
rust/k210-shared/src/board/ov2640.rs
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
use crate::soc::dvp::DVP;
|
||||||
|
use crate::board::def::OV2640_ADDR;
|
||||||
|
|
||||||
|
/** Initial configuration for OV2640 chip */
|
||||||
|
static CONFIG: &[(u8, u8)] = &[
|
||||||
|
(0xff, 0x01), // Bank: Sensor Address
|
||||||
|
(0x12, 0x80), // COM7 SRST: Initiates system reset
|
||||||
|
|
||||||
|
(0xff, 0x00), // Bank: DSP Address
|
||||||
|
(0x2c, 0xff), // ??
|
||||||
|
(0x2e, 0xdf), // ??
|
||||||
|
|
||||||
|
(0xff, 0x01), // Bank: Sensor Address
|
||||||
|
(0x3c, 0x32), // ??
|
||||||
|
(0x11, 0x00),
|
||||||
|
(0x09, 0x02),
|
||||||
|
(0x04, 0x88),
|
||||||
|
(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),
|
||||||
|
(0x36, 0x1a),
|
||||||
|
(0x06, 0x88),
|
||||||
|
(0x07, 0xc0),
|
||||||
|
(0x0d, 0x87),
|
||||||
|
(0x0e, 0x41),
|
||||||
|
(0x4c, 0x00),
|
||||||
|
(0x48, 0x00),
|
||||||
|
(0x5b, 0x00),
|
||||||
|
(0x42, 0x03),
|
||||||
|
(0x4a, 0x81),
|
||||||
|
(0x21, 0x99),
|
||||||
|
(0x24, 0x40),
|
||||||
|
(0x25, 0x38),
|
||||||
|
(0x26, 0x82),
|
||||||
|
(0x5c, 0x00),
|
||||||
|
(0x63, 0x00),
|
||||||
|
(0x46, 0x22),
|
||||||
|
(0x0c, 0x3c),
|
||||||
|
(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),
|
||||||
|
(0x12, 0x40),
|
||||||
|
(0x17, 0x11),
|
||||||
|
(0x18, 0x43),
|
||||||
|
(0x19, 0x00),
|
||||||
|
(0x1a, 0x4b),
|
||||||
|
(0x32, 0x09),
|
||||||
|
(0x37, 0xc0),
|
||||||
|
(0x4f, 0xca),
|
||||||
|
(0x50, 0xa8),
|
||||||
|
(0x5a, 0x23),
|
||||||
|
(0x6d, 0x00),
|
||||||
|
(0x3d, 0x38),
|
||||||
|
|
||||||
|
(0xff, 0x00), // Bank: DSP Address
|
||||||
|
(0xe5, 0x7f),
|
||||||
|
(0xf9, 0xc0),
|
||||||
|
(0x41, 0x24),
|
||||||
|
(0xe0, 0x14),
|
||||||
|
(0x76, 0xff),
|
||||||
|
(0x33, 0xa0),
|
||||||
|
(0x42, 0x20),
|
||||||
|
(0x43, 0x18),
|
||||||
|
(0x4c, 0x00),
|
||||||
|
(0x87, 0xd5),
|
||||||
|
(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),
|
||||||
|
(0xc3, 0xed),
|
||||||
|
(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),
|
||||||
|
(0xc0, 0x64),
|
||||||
|
(0xc1, 0x4b),
|
||||||
|
(0x8c, 0x00),
|
||||||
|
(0x86, 0x3d),
|
||||||
|
(0x50, 0x00),
|
||||||
|
(0x51, 0xc8),
|
||||||
|
(0x52, 0x96),
|
||||||
|
(0x53, 0x00),
|
||||||
|
(0x54, 0x00),
|
||||||
|
(0x55, 0x00),
|
||||||
|
(0x5a, 0xc8),
|
||||||
|
(0x5b, 0x96),
|
||||||
|
(0x5c, 0x00),
|
||||||
|
(0xd3, 0x02),
|
||||||
|
(0xc3, 0xed),
|
||||||
|
(0x7f, 0x00),
|
||||||
|
(0xda, 0x08),
|
||||||
|
(0xe5, 0x1f),
|
||||||
|
(0xe1, 0x67),
|
||||||
|
(0xe0, 0x00),
|
||||||
|
(0xdd, 0x7f),
|
||||||
|
(0x05, 0x00),
|
||||||
|
|
||||||
|
(0xff, 0x00), // Bank: DSP Address
|
||||||
|
(0xe0, 0x04),
|
||||||
|
(0x5a, 0x50),
|
||||||
|
(0x5b, 0x3c),
|
||||||
|
(0x5c, 0x00),
|
||||||
|
(0xe0, 0x00),
|
||||||
|
];
|
||||||
|
|
||||||
|
pub fn ov2640_init(dvp: &DVP) {
|
||||||
|
let (_manuf_id, _device_id) = read_id(dvp);
|
||||||
|
// TODO: do something with the IDs
|
||||||
|
// printf("manuf_id:0x%04x,device_id:0x%04x\n", v_manuf_id, v_device_id);
|
||||||
|
for &(register, value) in CONFIG {
|
||||||
|
dvp.sccb_send_data(OV2640_ADDR, register.into(), value.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Return (manuf_id, device_id) */
|
||||||
|
pub fn read_id(dvp: &DVP) -> (u16, u16) {
|
||||||
|
// 0xFF RA_DLMT - Register Bank Select: Sensor address
|
||||||
|
dvp.sccb_send_data(OV2640_ADDR, 0xFF, 0x01);
|
||||||
|
// 0x1C MIDH - Manufacturer ID Byte – High (Read only = 0x7F)
|
||||||
|
// 0x1D MIDL - Manufacturer ID Byte – Low (Read only = 0xA2)
|
||||||
|
let manuf_id = ((dvp.sccb_receive_data(OV2640_ADDR, 0x1C) as u16) << 8) | (dvp.sccb_receive_data(OV2640_ADDR, 0x1D) as u16);
|
||||||
|
// 0x0A PIDH - Product ID Number MSB (Read only)
|
||||||
|
// 0x0B PIDL - Product ID Number LSB (Read only)
|
||||||
|
let device_id = ((dvp.sccb_receive_data(OV2640_ADDR, 0x0A) as u16) << 8) | (dvp.sccb_receive_data(OV2640_ADDR, 0x0B) as u16);
|
||||||
|
(manuf_id, device_id)
|
||||||
|
}
|
@ -1,3 +1,4 @@
|
|||||||
|
pub mod dvp;
|
||||||
pub mod fpioa;
|
pub mod fpioa;
|
||||||
pub mod gpio;
|
pub mod gpio;
|
||||||
pub mod gpiohs;
|
pub mod gpiohs;
|
||||||
|
343
rust/k210-shared/src/soc/dvp.rs
Normal file
343
rust/k210-shared/src/soc/dvp.rs
Normal file
@ -0,0 +1,343 @@
|
|||||||
|
use core::cmp;
|
||||||
|
use k210_hal::pac;
|
||||||
|
use pac::dvp;
|
||||||
|
|
||||||
|
use crate::soc::sleep::usleep;
|
||||||
|
use crate::soc::sysctl;
|
||||||
|
|
||||||
|
pub trait DVPExt: Sized {
|
||||||
|
/// Constrains DVP peripheral
|
||||||
|
fn constrain(self) -> DVP;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DVPExt for pac::DVP {
|
||||||
|
fn constrain(self) -> DVP { DVP { dvp: self, sccb_addr_len: sccb_addr_len::W8 } }
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Output mode (RGB565 for display, or for planar for AI) */
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum output_mode {
|
||||||
|
AI,
|
||||||
|
DISPLAY,
|
||||||
|
}
|
||||||
|
|
||||||
|
/** SCCB register address width */
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum sccb_addr_len {
|
||||||
|
W8,
|
||||||
|
W16,
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Interrupt */
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum interrupt {
|
||||||
|
frame_start,
|
||||||
|
frame_finish,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DVP {
|
||||||
|
dvp: pac::DVP,
|
||||||
|
sccb_addr_len: sccb_addr_len,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type image_format = dvp::dvp_cfg::FORMATW;
|
||||||
|
|
||||||
|
impl DVP {
|
||||||
|
/** Set SCCB clock to a safe and deterministic value (as low as possible) */
|
||||||
|
fn sccb_clk_init(&self) {
|
||||||
|
unsafe {
|
||||||
|
self.dvp.sccb_cfg.modify(|_,w|
|
||||||
|
w.scl_lcnt().bits(255)
|
||||||
|
.scl_hcnt().bits(255)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Set SCCB clock rate */
|
||||||
|
pub fn sccb_set_clk_rate(&self, clk_rate: u32) -> u32 {
|
||||||
|
let v_sccb_freq = sysctl::clock_get_freq(sysctl::clock::APB1);
|
||||||
|
let v_period_clk_cnt = v_sccb_freq / clk_rate / 2; // TODO: round() i.s.o. truncate?
|
||||||
|
|
||||||
|
if v_period_clk_cnt > 255 {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
self.dvp.sccb_cfg.modify(|_,w|
|
||||||
|
w.scl_lcnt().bits(v_period_clk_cnt as u8)
|
||||||
|
.scl_hcnt().bits(v_period_clk_cnt as u8)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// confused: why does this use clock::DVP but the period_clk_cnt uses clock::APB1?
|
||||||
|
return sysctl::clock_get_freq(sysctl::clock::DVP) / (v_period_clk_cnt * 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Perform, and wait for a SCCB transfer (read or write) */
|
||||||
|
fn sccb_start_transfer(&self) {
|
||||||
|
while self.dvp.sts.read().sccb_en().bit() {
|
||||||
|
// IDLE
|
||||||
|
}
|
||||||
|
self.dvp.sts.write(|w| w.sccb_en().set_bit()
|
||||||
|
.sccb_en_we().set_bit());
|
||||||
|
while self.dvp.sts.read().sccb_en().bit() {
|
||||||
|
// IDLE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Set a register value through SCCB */
|
||||||
|
pub fn sccb_send_data(&self, dev_addr: u8, reg_addr: u16, reg_data: u8) {
|
||||||
|
use dvp::sccb_cfg::BYTE_NUMW::*;
|
||||||
|
unsafe {
|
||||||
|
match self.sccb_addr_len {
|
||||||
|
sccb_addr_len::W8 => {
|
||||||
|
self.dvp.sccb_cfg.modify(|_,w| w.byte_num().variant(NUM3));
|
||||||
|
self.dvp.sccb_ctl.write(|w| w.device_address().bits(dev_addr | 1)
|
||||||
|
.reg_address().bits(reg_addr as u8)
|
||||||
|
.wdata_byte0().bits(reg_data));
|
||||||
|
},
|
||||||
|
sccb_addr_len::W16 => {
|
||||||
|
self.dvp.sccb_cfg.modify(|_,w| w.byte_num().variant(NUM4));
|
||||||
|
self.dvp.sccb_ctl.write(|w| w.device_address().bits(dev_addr | 1)
|
||||||
|
.reg_address().bits((reg_addr >> 8) as u8)
|
||||||
|
.wdata_byte0().bits((reg_addr & 0xff) as u8)
|
||||||
|
.wdata_byte1().bits(reg_data));
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.sccb_start_transfer();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Receive register value through SCCB */
|
||||||
|
pub fn sccb_receive_data(&self, dev_addr: u8, reg_addr: u16) -> u8 {
|
||||||
|
// Write read request
|
||||||
|
use dvp::sccb_cfg::BYTE_NUMW::*;
|
||||||
|
unsafe {
|
||||||
|
match self.sccb_addr_len {
|
||||||
|
sccb_addr_len::W8 => {
|
||||||
|
self.dvp.sccb_cfg.modify(|_,w| w.byte_num().variant(NUM2));
|
||||||
|
self.dvp.sccb_ctl.write(|w| w.device_address().bits(dev_addr | 1)
|
||||||
|
.reg_address().bits(reg_addr as u8));
|
||||||
|
},
|
||||||
|
sccb_addr_len::W16 => {
|
||||||
|
self.dvp.sccb_cfg.modify(|_,w| w.byte_num().variant(NUM3));
|
||||||
|
self.dvp.sccb_ctl.write(|w| w.device_address().bits(dev_addr | 1)
|
||||||
|
.reg_address().bits((reg_addr >> 8) as u8)
|
||||||
|
.wdata_byte0().bits((reg_addr & 0xff) as u8));
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.sccb_start_transfer();
|
||||||
|
// Start read transfer
|
||||||
|
unsafe { self.dvp.sccb_ctl.write(|w| w.device_address().bits(dev_addr)); }
|
||||||
|
self.sccb_start_transfer();
|
||||||
|
self.dvp.sccb_cfg.read().rdata().bits()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Reset DVP-connected device */
|
||||||
|
fn reset(&self) {
|
||||||
|
/* First power down */
|
||||||
|
self.dvp.cmos_cfg.modify(|_,w| w.power_down().set_bit());
|
||||||
|
usleep(200_000);
|
||||||
|
self.dvp.cmos_cfg.modify(|_,w| w.power_down().clear_bit());
|
||||||
|
usleep(200_000);
|
||||||
|
|
||||||
|
/* Second reset */
|
||||||
|
self.dvp.cmos_cfg.modify(|_,w| w.reset().clear_bit());
|
||||||
|
usleep(200_000);
|
||||||
|
self.dvp.cmos_cfg.modify(|_,w| w.reset().set_bit());
|
||||||
|
usleep(200_000);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Initialize DVP peripheral */
|
||||||
|
pub fn init(&mut self, sccb_addr_len: sccb_addr_len) {
|
||||||
|
self.sccb_addr_len = sccb_addr_len;
|
||||||
|
sysctl::clock_enable(sysctl::clock::DVP);
|
||||||
|
sysctl::reset(sysctl::reset::DVP);
|
||||||
|
// Set XCLK to hardcoded divider (3+1)*2=8
|
||||||
|
unsafe {
|
||||||
|
self.dvp.cmos_cfg.modify(|_,w| w.clk_div().bits(3)
|
||||||
|
.clk_enable().set_bit());
|
||||||
|
}
|
||||||
|
self.sccb_clk_init();
|
||||||
|
self.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Set XCLK clock rate */
|
||||||
|
pub fn set_xclk_rate(&self, xclk_rate: u32) -> u32 {
|
||||||
|
// Taken directly from SDK: it's strange that this clock is relative to APB1 not DVP clock
|
||||||
|
let v_apb1_clk = sysctl::clock_get_freq(sysctl::clock::APB1);
|
||||||
|
let v_period = if v_apb1_clk > xclk_rate * 2 {
|
||||||
|
cmp::min((v_apb1_clk / (xclk_rate * 2)) - 1, 255) // TODO round instead of trunc?
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
unsafe {
|
||||||
|
self.dvp.cmos_cfg.modify(|_,w| w.clk_div().bits(v_period as u8)
|
||||||
|
.clk_enable().set_bit());
|
||||||
|
}
|
||||||
|
self.reset();
|
||||||
|
v_apb1_clk / ((v_period + 1) * 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Set input image format */
|
||||||
|
pub fn set_image_format(&self, format: image_format) {
|
||||||
|
self.dvp.dvp_cfg.modify(|_,w| w.format().variant(format));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not sure it's consistent interface to have two separate functions here, but just going with
|
||||||
|
// the SDK for now... might even want to merge these into set_image_size, or force configuring
|
||||||
|
// the entire peripheral (register size, burst mode, image size) at once
|
||||||
|
/** Enable burst mode */
|
||||||
|
pub fn enable_burst(&self) {
|
||||||
|
self.dvp.dvp_cfg.modify(|_,w| w.burst_size_4beats().set_bit());
|
||||||
|
self.dvp.axi.modify(|_,w| w.gm_mlen().variant(dvp::axi::GM_MLENW::BYTE4));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Disable burst mode */
|
||||||
|
pub fn disable_burst(&self) {
|
||||||
|
self.dvp.dvp_cfg.modify(|_,w| w.burst_size_4beats().clear_bit());
|
||||||
|
self.dvp.axi.modify(|_,w| w.gm_mlen().variant(dvp::axi::GM_MLENW::BYTE1));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Set image size. If burst mode is enabled the maximum configurable size is
|
||||||
|
* 8160x1023, without burst mode it is 2040x1023.
|
||||||
|
*/
|
||||||
|
pub fn set_image_size(&self, width: u16, height: u16) {
|
||||||
|
// Note: this uses state written in enable/disable_burst, so that needs to be configured before this
|
||||||
|
let burst_num = if self.dvp.dvp_cfg.read().burst_size_4beats().bit() {
|
||||||
|
width / 8 / 4
|
||||||
|
} else {
|
||||||
|
width / 8 / 1
|
||||||
|
};
|
||||||
|
assert!(burst_num < 256);
|
||||||
|
assert!(height < 1024);
|
||||||
|
unsafe {
|
||||||
|
self.dvp.dvp_cfg.modify(|_,w| w.href_burst_num().bits(burst_num as u8)
|
||||||
|
.line_num().bits(height));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Set address for planar RGB output */
|
||||||
|
pub fn set_ai_addr(&self, r_addr: *mut u8, g_addr: *mut u8, b_addr: *mut u8) {
|
||||||
|
// Makes use of the fact that
|
||||||
|
// a) physical memory is the same as virtual memory on the K210
|
||||||
|
// b) memory wraps around every 2^32
|
||||||
|
unsafe {
|
||||||
|
self.dvp.r_addr.write(|w| w.bits(((r_addr as usize) & 0xffffffff) as u32));
|
||||||
|
self.dvp.g_addr.write(|w| w.bits(((g_addr as usize) & 0xffffffff) as u32));
|
||||||
|
self.dvp.b_addr.write(|w| w.bits(((b_addr as usize) & 0xffffffff) as u32));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Set address for 16-bit R5G6B5 output */
|
||||||
|
pub fn set_display_addr(&self, addr: *mut u16) {
|
||||||
|
unsafe {
|
||||||
|
self.dvp.rgb_addr.write(|w| w.bits(((addr as usize) & 0xffffffff) as u32));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Start a frame */
|
||||||
|
pub fn start_frame(&self) {
|
||||||
|
while !self.dvp.sts.read().frame_start().bit() {
|
||||||
|
// IDLE
|
||||||
|
}
|
||||||
|
self.dvp.sts.write(|w| w.frame_start().set_bit()
|
||||||
|
.frame_start_we().set_bit());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Start conversion of frame */
|
||||||
|
pub fn start_convert(&self) {
|
||||||
|
self.dvp.sts.write(|w| w.dvp_en().set_bit()
|
||||||
|
.dvp_en_we().set_bit());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Finish conversion of frame */
|
||||||
|
pub fn finish_convert(&self) {
|
||||||
|
while !self.dvp.sts.read().frame_finish().bit() {
|
||||||
|
// IDLE
|
||||||
|
}
|
||||||
|
self.dvp.sts.write(|w| w.frame_finish().set_bit()
|
||||||
|
.frame_finish_we().set_bit());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Wait for an entire frame to complete */
|
||||||
|
pub fn get_image(&self) {
|
||||||
|
while !self.dvp.sts.read().frame_start().bit() {
|
||||||
|
// IDLE
|
||||||
|
}
|
||||||
|
self.dvp.sts.write(|w| w.frame_start().set_bit()
|
||||||
|
.frame_start_we().set_bit());
|
||||||
|
while !self.dvp.sts.read().frame_start().bit() {
|
||||||
|
// IDLE
|
||||||
|
}
|
||||||
|
self.dvp.sts.write(|w| w.frame_finish().set_bit()
|
||||||
|
.frame_finish_we().set_bit()
|
||||||
|
.frame_start().set_bit()
|
||||||
|
.frame_start_we().set_bit()
|
||||||
|
.dvp_en().set_bit()
|
||||||
|
.dvp_en_we().set_bit());
|
||||||
|
while !self.dvp.sts.read().frame_finish().bit() {
|
||||||
|
// IDLE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Configure interrupt */
|
||||||
|
pub fn config_interrupt(&self, interrupt: interrupt, enable: bool) {
|
||||||
|
match interrupt {
|
||||||
|
interrupt::frame_start => {
|
||||||
|
self.dvp.dvp_cfg.modify(|_,w| w.start_int_enable().bit(enable));
|
||||||
|
}
|
||||||
|
interrupt::frame_finish => {
|
||||||
|
self.dvp.dvp_cfg.modify(|_,w| w.finish_int_enable().bit(enable));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get status of an interrupt */
|
||||||
|
pub fn get_interrupt(&self, interrupt: interrupt) -> bool {
|
||||||
|
let sts = self.dvp.sts.read();
|
||||||
|
match interrupt {
|
||||||
|
interrupt::frame_start => { sts.frame_start().bit() }
|
||||||
|
interrupt::frame_finish => { sts.frame_finish().bit() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Clear an interrupt */
|
||||||
|
pub fn clear_interrupt(&self, interrupt: interrupt) {
|
||||||
|
match interrupt {
|
||||||
|
interrupt::frame_start => {
|
||||||
|
self.dvp.sts.modify(|_,w| w.frame_start().set_bit()
|
||||||
|
.frame_start_we().set_bit());
|
||||||
|
}
|
||||||
|
interrupt::frame_finish => {
|
||||||
|
self.dvp.sts.modify(|_,w| w.frame_finish().set_bit()
|
||||||
|
.frame_finish_we().set_bit());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Enable automatic frame mode */
|
||||||
|
pub fn enable_auto(&self) {
|
||||||
|
self.dvp.dvp_cfg.modify(|_,w| w.auto_enable().set_bit());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Disable automatic frame mode */
|
||||||
|
pub fn disable_auto(&self) {
|
||||||
|
self.dvp.dvp_cfg.modify(|_,w| w.auto_enable().clear_bit());
|
||||||
|
}
|
||||||
|
|
||||||
|
// The following function could be merged into setting the address?
|
||||||
|
// set_display_addr / set_ai_addr with an Option maybe?
|
||||||
|
/** Enable/disable an output */
|
||||||
|
pub fn set_output_enable(&self, index: output_mode, enable: bool) {
|
||||||
|
match index {
|
||||||
|
output_mode::AI => {
|
||||||
|
self.dvp.dvp_cfg.modify(|_,w| w.ai_output_enable().bit(enable));
|
||||||
|
}
|
||||||
|
output_mode::DISPLAY => {
|
||||||
|
self.dvp.dvp_cfg.modify(|_,w| w.display_output_enable().bit(enable));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user