rust: Improve DVP interface

Clean up interface a bit.
This commit is contained in:
Wladimir J. van der Laan 2019-06-01 20:33:12 +00:00
parent 8cc5ff53f5
commit cd7f72f03f

View File

@ -5,6 +5,7 @@ use pac::dvp;
use crate::soc::sleep::usleep; use crate::soc::sleep::usleep;
use crate::soc::sysctl; use crate::soc::sysctl;
/** Extension trait for adding constrain() to DVP peripheral */
pub trait DVPExt: Sized { pub trait DVPExt: Sized {
/// Constrains DVP peripheral /// Constrains DVP peripheral
fn constrain(self) -> DVP; fn constrain(self) -> DVP;
@ -14,13 +15,6 @@ impl DVPExt for pac::DVP {
fn constrain(self) -> DVP { DVP { dvp: self, sccb_addr_len: sccb_addr_len::W8 } } 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 */ /** SCCB register address width */
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub enum sccb_addr_len { pub enum sccb_addr_len {
@ -28,18 +22,20 @@ pub enum sccb_addr_len {
W16, W16,
} }
/** Interrupt */ /** Enumeration for different DVP interrupts */
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub enum interrupt { pub enum interrupt {
frame_start, frame_start,
frame_finish, frame_finish,
} }
/** DVP peripheral abstraction */
pub struct DVP { pub struct DVP {
dvp: pac::DVP, dvp: pac::DVP,
sccb_addr_len: sccb_addr_len, sccb_addr_len: sccb_addr_len,
} }
/** Borrow image_format enum from pac */
pub type image_format = dvp::dvp_cfg::FORMATW; pub type image_format = dvp::dvp_cfg::FORMATW;
impl DVP { impl DVP {
@ -134,13 +130,13 @@ impl DVP {
/** Reset DVP-connected device */ /** Reset DVP-connected device */
fn reset(&self) { fn reset(&self) {
/* First power down */ // First power down
self.dvp.cmos_cfg.modify(|_,w| w.power_down().set_bit()); self.dvp.cmos_cfg.modify(|_,w| w.power_down().set_bit());
usleep(200_000); usleep(200_000);
self.dvp.cmos_cfg.modify(|_,w| w.power_down().clear_bit()); self.dvp.cmos_cfg.modify(|_,w| w.power_down().clear_bit());
usleep(200_000); usleep(200_000);
/* Second reset */ // Second reset
self.dvp.cmos_cfg.modify(|_,w| w.reset().clear_bit()); self.dvp.cmos_cfg.modify(|_,w| w.reset().clear_bit());
usleep(200_000); usleep(200_000);
self.dvp.cmos_cfg.modify(|_,w| w.reset().set_bit()); self.dvp.cmos_cfg.modify(|_,w| w.reset().set_bit());
@ -183,29 +179,18 @@ impl DVP {
self.dvp.dvp_cfg.modify(|_,w| w.format().variant(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 /** Set image size and burst mode. If burst mode is enabled the maximum configurable size is
// 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. * 8160x1023, without burst mode it is 2040x1023.
*/ */
pub fn set_image_size(&self, width: u16, height: u16) { pub fn set_image_size(&self, burst_mode: bool, width: u16, height: u16) {
// Note: this uses state written in enable/disable_burst, so that needs to be configured before this // 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() { let burst_num = if burst_mode {
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));
width / 8 / 4 width / 8 / 4
} else { } else {
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));
width / 8 / 1 width / 8 / 1
}; };
assert!(burst_num < 256); assert!(burst_num < 256);
@ -216,8 +201,11 @@ impl DVP {
} }
} }
/** Set address for planar RGB output */ /** Set address for planar RGB output for KPU
pub fn set_ai_addr(&self, r_addr: *mut u8, g_addr: *mut u8, b_addr: *mut u8) { * `Option<(r_addr, g_addr, b_addr)>`
*/
pub fn set_ai_addr(&self, addr: Option<(*mut u8, *mut u8, *mut u8)>) {
if let Some((r_addr, g_addr, b_addr)) = addr {
// Makes use of the fact that // Makes use of the fact that
// a) physical memory is the same as virtual memory on the K210 // a) physical memory is the same as virtual memory on the K210
// b) memory wraps around every 2^32 // b) memory wraps around every 2^32
@ -226,13 +214,22 @@ impl DVP {
self.dvp.g_addr.write(|w| w.bits(((g_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)); self.dvp.b_addr.write(|w| w.bits(((b_addr as usize) & 0xffffffff) as u32));
} }
self.dvp.dvp_cfg.modify(|_,w| w.ai_output_enable().set_bit());
} else {
self.dvp.dvp_cfg.modify(|_,w| w.ai_output_enable().clear_bit());
}
} }
/** Set address for 16-bit R5G6B5 output */ /** Set address for 16-bit R5G6B5 output */
pub fn set_display_addr(&self, addr: *mut u16) { pub fn set_display_addr(&self, addr: Option<*mut u16>) {
if let Some(addr) = addr {
unsafe { unsafe {
self.dvp.rgb_addr.write(|w| w.bits(((addr as usize) & 0xffffffff) as u32)); self.dvp.rgb_addr.write(|w| w.bits(((addr as usize) & 0xffffffff) as u32));
} }
self.dvp.dvp_cfg.modify(|_,w| w.display_output_enable().set_bit());
} else {
self.dvp.dvp_cfg.modify(|_,w| w.display_output_enable().clear_bit());
}
} }
/** Start a frame */ /** Start a frame */
@ -315,29 +312,9 @@ impl DVP {
} }
} }
/** Enable automatic frame mode */ /** Enable/disable automatic frame mode */
pub fn enable_auto(&self) { pub fn set_auto(&self, enable: bool) {
self.dvp.dvp_cfg.modify(|_,w| w.auto_enable().set_bit()); self.dvp.dvp_cfg.modify(|_,w| w.auto_enable().bit(enable));
}
/** 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));
}
} }
} }
}