diff --git a/src/clock.rs b/src/clock.rs index d833527..12ed8ea 100644 --- a/src/clock.rs +++ b/src/clock.rs @@ -1,6 +1,6 @@ //! Clock configuration //use crate::pac::PRCI; -use crate::sysctl::ACLK; +// use crate::sysctl::ACLK; use crate::time::Hertz; /// Frozen clock frequencies @@ -11,6 +11,8 @@ use crate::time::Hertz; pub struct Clocks { pub(crate) aclk: Hertz, pub(crate) apb0: Hertz, + pub(crate) apb1: Hertz, + pub(crate) apb2: Hertz, } impl Clocks { @@ -28,6 +30,8 @@ impl Clocks { Self { aclk: Hertz(390_000_000), apb0: Hertz(195_000_000), + apb1: Hertz(195_000_000), + apb2: Hertz(195_000_000), } } @@ -40,4 +44,14 @@ impl Clocks { pub fn apb0(&self) -> Hertz { self.apb0 } + + /// Returns APB1 frequency + pub fn apb1(&self) -> Hertz { + self.apb1 + } + + /// Returns APB2 frequency + pub fn apb2(&self) -> Hertz { + self.apb2 + } } diff --git a/src/dvp.rs b/src/dvp.rs new file mode 100644 index 0000000..1b14385 --- /dev/null +++ b/src/dvp.rs @@ -0,0 +1,214 @@ +use crate::clock::Clocks; +use crate::sysctl; +use crate::time::Hertz; +use k210_pac as pac; + +pub trait DvpExt: Sized { + fn constrain(self) -> Dvp; +} + +impl DvpExt for pac::DVP { + fn constrain(self) -> Dvp { + Dvp { dvp: self } + } +} + +pub struct Dvp { + pub dvp: pac::DVP, +} + +pub type ImageFormat = pac::dvp::dvp_cfg::FORMAT_A; + +impl Dvp { + pub fn sccb_clk_init(&self) { + unsafe { + self.dvp + .sccb_cfg + .modify(|_, w| w.scl_lcnt().bits(255).scl_hcnt().bits(255)) + } + } + + pub fn sccb_clk_set_rate(&self, clk_rate: Hertz, clock: &Clocks) -> Hertz { + let sccb_freq = clock.apb1(); + let period_clk_cnt = (sccb_freq.0 / clk_rate.0 / 2).max(0).min(255) as u8; + unsafe { + self.dvp.sccb_cfg.modify(|_, w| { + w.scl_lcnt() + .bits(period_clk_cnt) + .scl_hcnt() + .bits(period_clk_cnt) + }) + } + return Hertz(clock.cpu().0 / period_clk_cnt as u32 / 2); + } + + 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 + } + } + + pub fn sccb_send_data(&self, dev_addr: u8, reg_addr: u8, reg_data: u8) { + use pac::dvp::sccb_cfg::BYTE_NUM_A::*; + unsafe { + 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) + .wdata_byte0() + .bits(reg_data) + }) + } + self.sccb_start_transfer(); + } + + pub fn sccb_receive_data(&self, dev_addr: u8, reg_addr: u8) -> u8 { + use pac::dvp::sccb_cfg::BYTE_NUM_A::*; + unsafe { + 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) + }); + } + self.sccb_start_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() + } + + pub fn reset(&self) { + self.dvp.cmos_cfg.modify(|_, w| w.power_down().set_bit()); + self.dvp.cmos_cfg.modify(|_, w| w.power_down().clear_bit()); + self.dvp.cmos_cfg.modify(|_, w| w.reset().clear_bit()); + self.dvp.cmos_cfg.modify(|_, w| w.reset().set_bit()); + } + + pub fn init(&self) { + // Consider borrowing i.s.o. using global instance of sysctl? + sysctl::clk_en_peri().modify(|_, w| w.dvp_clk_en().set_bit()); + sysctl::peri_reset().modify(|_, w| w.dvp_reset().set_bit()); + sysctl::peri_reset().modify(|_, w| w.dvp_reset().clear_bit()); + + unsafe { + self.dvp + .cmos_cfg + .modify(|_, w| w.clk_div().bits(3).clk_enable().set_bit()); + } + + self.sccb_clk_init(); + self.reset(); + } + + pub fn set_xclk_rate(&self, xclk_rate: Hertz, clock: &Clocks) -> Hertz { + let apb1_clk = clock.apb1().0; + let period = if apb1_clk > xclk_rate.0 * 2 { + apb1_clk / xclk_rate.0 / 2 - 1 as u32 + } else { + 0 + }; + + let period = period.min(255); + unsafe { + self.dvp + .cmos_cfg + .modify(|_, w| w.clk_div().bits(period as u8).clk_enable().set_bit()) + } + + self.reset(); + Hertz(apb1_clk / (period + 1) / 2) + } + + pub fn set_image_size(&self, burst_mode: bool, width: u16, height: u16) { + use pac::dvp::axi::GM_MLEN_A::*; + 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(BYTE4)); + width / 8 / 4 + } else { + self.dvp + .dvp_cfg + .modify(|_, w| w.burst_size_4beats().clear_bit()); + self.dvp.axi.modify(|_, w| w.gm_mlen().variant(BYTE1)); + width / 8 / 1 + }; + + let burst_num = burst_num.min(255).max(0) as u8; + + unsafe { + self.dvp + .dvp_cfg + .modify(|_, w| w.href_burst_num().bits(burst_num).line_num().bits(height)) + } + } + + pub fn set_image_format(&self, format: ImageFormat) { + self.dvp.dvp_cfg.modify(|_, w| w.format().variant(format)); + } + + pub fn set_display_addr(&self, addr: Option<*mut u32>) { + unsafe { + if let Some(addr) = addr { + self.dvp + .rgb_addr + .write(|w| w.bits((addr as usize & 0xffff_ffff) 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()); + } + } + } + + pub fn set_auto(&self, status: bool) { + self.dvp.dvp_cfg.modify(|_, w| w.auto_enable().bit(status)); + } + + 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 + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 4309e86..eeb7d82 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,29 +13,30 @@ pub mod cache; pub mod clint; pub mod clock; pub mod dmac; +pub mod dvp; pub mod fft; pub mod fpioa; pub mod gpio; pub mod gpiohs; pub mod plic; pub mod serial; -pub mod spi; pub mod sha256; +pub mod spi; pub mod stdout; pub mod sysctl; pub mod time; /// Prelude pub mod prelude { - pub use embedded_hal::prelude::*; - pub use crate::serial::SerialExt as _k210_hal_serial_SerialExt; - pub use crate::stdout::Write as _k210_hal_stdout_Write; - pub use crate::time::U32Ext as _k210_hal_time_U32Ext; pub use crate::fpioa::FpioaExt as _k210_hal_fpioa_FpioaExt; - pub use crate::sysctl::SysctlExt as _k210_hal_sysctl_SysctlExt; pub use crate::gpio::GpioExt as _k210_hal_gpio_GpioExt; pub use crate::gpiohs::GpiohsExt as _k210_hal_gpiohs_GpiohsExt; pub use crate::plic::PlicExt as _k210_hal_plic_PlicExt; + pub use crate::serial::SerialExt as _k210_hal_serial_SerialExt; + pub use crate::stdout::Write as _k210_hal_stdout_Write; + pub use crate::sysctl::SysctlExt as _k210_hal_sysctl_SysctlExt; + pub use crate::time::U32Ext as _k210_hal_time_U32Ext; + pub use embedded_hal::prelude::*; } mod bit_utils { diff --git a/src/sysctl.rs b/src/sysctl.rs index 2440436..3ef6ab5 100644 --- a/src/sysctl.rs +++ b/src/sysctl.rs @@ -74,6 +74,8 @@ impl SysctlExt for SYSCTL { Parts { aclk: ACLK { _ownership: () }, apb0: APB0 { _ownership: () }, + apb1: APB1 { _ownership: () }, + apb2: APB2 { _ownership: () }, pll0: PLL0 { _ownership: () }, } } @@ -87,7 +89,11 @@ pub struct Parts { pub pll0: PLL0, /// entry for controlling the enable/disable/frequency of apb0 pub apb0: APB0, - // todo: SRAM, APB-bus, ROM, DMA, AI, PLL1, PLL2, APB1, APB2 + /// entry for controlling the enable/disable/frequency of apb1 + pub apb1: APB1, + /// entry for controlling the enable/disable/frequency of apb2 + pub apb2: APB2, + // todo: SRAM, APB-bus, ROM, DMA, AI, PLL1, PLL2 } impl Parts { @@ -95,6 +101,8 @@ impl Parts { Clocks { aclk: self.aclk.get_frequency(), apb0: self.apb0.get_frequency(), + apb1: self.apb1.get_frequency(), + apb2: self.apb2.get_frequency(), } } } @@ -131,13 +139,67 @@ impl APB0 { } } -// pub struct APB1 { -// _ownership: () -// } +pub struct APB1 { + _ownership: (), +} + +impl APB1 { + pub fn enable(&mut self) { + clk_en_cent().modify(|_, w| w.apb1_clk_en().set_bit()) + } + + pub fn set_frequency(&mut self, expected_freq: impl Into) -> Hertz { + let aclk = ACLK::steal(); + let aclk_frequency = aclk.get_frequency().0 as i64; + let apb1_clk_sel = (aclk_frequency / expected_freq.into().0 as i64 + 1) + .max(0) + .min(0b111) as u8; + unsafe { + sysctl() + .clk_sel0 + .modify(|_, w| w.apb1_clk_sel().bits(apb1_clk_sel)) + } + Hertz(aclk_frequency as u32 / (apb1_clk_sel as u32 + 1)) + } + + pub fn get_frequency(&self) -> Hertz { + let aclk = ACLK::steal(); + let aclk_frequency = aclk.get_frequency().0 as i64; + let apb1_clk_sel = sysctl().clk_sel0.read().apb1_clk_sel().bits(); + Hertz(aclk_frequency as u32 / (apb1_clk_sel as u32 + 1)) + } +} -// pub struct APB2 { -// _ownership: () -// } +pub struct APB2 { + _ownership: (), +} + +impl APB2 { + pub fn enable(&mut self) { + clk_en_cent().modify(|_, w| w.apb2_clk_en().set_bit()) + } + + pub fn set_frequency(&mut self, expected_freq: impl Into) -> Hertz { + let aclk = ACLK::steal(); + let aclk_frequency = aclk.get_frequency().0 as i64; + let apb2_clk_sel = (aclk_frequency / expected_freq.into().0 as i64 + 1) + .max(0) + .min(0b111) as u8; + unsafe { + sysctl() + .clk_sel0 + .modify(|_, w| w.apb2_clk_sel().bits(apb2_clk_sel)) + } + Hertz(aclk_frequency as u32 / (apb2_clk_sel as u32 + 1)) + } + + pub fn get_frequency(&self) -> Hertz { + let aclk = ACLK::steal(); + let aclk_frequency = aclk.get_frequency().0 as i64; + let apb2_clk_sel = sysctl().clk_sel0.read().apb2_clk_sel().bits(); + Hertz(aclk_frequency as u32 / (apb2_clk_sel as u32 + 1)) + } +} /// PLL0, which source is CLOCK_FREQ_IN0, /// and the output can be used on ACLK(CPU), SPIs, etc.