diff --git a/README.md b/README.md index 69dccbf..32818cb 100644 --- a/README.md +++ b/README.md @@ -100,6 +100,8 @@ Projects ========= This is a general random sandbox with silly projects for me to play around with the Maix Go, some are in C and some are in Rust. +It turns out that this cheap board is great for playing around with Rust embedded in an environment that +has a fair amount of memory and number of peripherals available by default! glyph_mapping ------------- @@ -185,6 +187,14 @@ Uses the ESP8285 WiFi chip of the Maix Go to fetch weather data from [README](rust/weather/README.md) +rust/dvp-ov +----------- + +A straightforward passthrough test for video handling, based on `dvp_ov` in the +SDK: read frames from the OV2640 image sensor and display them on the LCD. + +[README](rust/dvp-ov/README.md) + ROM re'ing =========== diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 96a2d6d..273ed87 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -7,6 +7,7 @@ members = [ "uart-passthrough", "rgbcontrol", "weather", + "dvp-ov", ] [patch.crates-io] diff --git a/rust/dvp-ov/.gitignore b/rust/dvp-ov/.gitignore new file mode 100644 index 0000000..f0e3bca --- /dev/null +++ b/rust/dvp-ov/.gitignore @@ -0,0 +1,2 @@ +/target +**/*.rs.bk \ No newline at end of file diff --git a/rust/dvp-ov/Cargo.toml b/rust/dvp-ov/Cargo.toml new file mode 100644 index 0000000..21d9b36 --- /dev/null +++ b/rust/dvp-ov/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "dvp-ov" +version = "0.1.0" +authors = ["W.J. van der Laan "] +edition = "2018" + +[dependencies] +riscv-rt = "0.5.0" +k210-hal = "0.1.0" +riscv = "0.5" +k210-shared = { path = "../k210-shared" } diff --git a/rust/dvp-ov/README.md b/rust/dvp-ov/README.md new file mode 100644 index 0000000..9b8388d --- /dev/null +++ b/rust/dvp-ov/README.md @@ -0,0 +1,7 @@ +# `dvp-ov` + +A straightforward passthrough test for video handling, based on `dvp_ov` in the +SDK: read frames from the OV2640 image sensor and display them on the LCD. + +The performance is likely worse (don't know by how much) than the C +implementation because currently, no interrupts and DMA are used. diff --git a/rust/dvp-ov/src/main.rs b/rust/dvp-ov/src/main.rs new file mode 100644 index 0000000..1cce437 --- /dev/null +++ b/rust/dvp-ov/src/main.rs @@ -0,0 +1,102 @@ +#![allow(dead_code)] +#![allow(non_snake_case)] +#![allow(non_camel_case_types)] +#![no_std] +#![no_main] + +use k210_hal::pac; +use k210_hal::prelude::*; +use k210_hal::stdout::Stdout; +use k210_shared::board::def::{io,DISP_WIDTH,DISP_HEIGHT}; +use k210_shared::board::lcd::{LCD,LCDHL,self}; +use k210_shared::board::lcd_colors; +use k210_shared::board::lcd_render::render_image; +use k210_shared::soc::fpioa; +use k210_shared::soc::sleep::usleep; +use k210_shared::soc::spi::SPIExt; +use k210_shared::soc::sysctl; +use riscv_rt::entry; +use k210_shared::soc::dvp::{DVPExt,sccb_addr_len,image_format}; +use k210_shared::board::ov2640; + +pub type ScreenImage = [u32; DISP_WIDTH * DISP_HEIGHT / 2]; + +/** Connect pins to internal functions */ +fn io_init() { + /* Init DVP IO map and function settings */ + fpioa::set_function(io::DVP_RST.into(), fpioa::function::CMOS_RST); + fpioa::set_function(io::DVP_PWDN.into(), fpioa::function::CMOS_PWDN); + fpioa::set_function(io::DVP_XCLK.into(), fpioa::function::CMOS_XCLK); + fpioa::set_function(io::DVP_VSYNC.into(), fpioa::function::CMOS_VSYNC); + fpioa::set_function(io::DVP_HSYNC.into(), fpioa::function::CMOS_HREF); + fpioa::set_function(io::DVP_PCLK.into(), fpioa::function::CMOS_PCLK); + fpioa::set_function(io::DVP_SCL.into(), fpioa::function::SCCB_SCLK); + fpioa::set_function(io::DVP_SDA.into(), fpioa::function::SCCB_SDA); + + /* Init SPI IO map and function settings */ + fpioa::set_function(io::LCD_RST.into(), fpioa::function::gpiohs(lcd::RST_GPIONUM)); + fpioa::set_io_pull(io::LCD_RST.into(), fpioa::pull::DOWN); // outputs must be pull-down + fpioa::set_function(io::LCD_DC.into(), fpioa::function::gpiohs(lcd::DCX_GPIONUM)); + fpioa::set_io_pull(io::LCD_DC.into(), fpioa::pull::DOWN); + fpioa::set_function(io::LCD_CS.into(), fpioa::function::SPI0_SS3); + fpioa::set_function(io::LCD_WR.into(), fpioa::function::SPI0_SCLK); + + sysctl::set_spi0_dvp_data(true); + + /* Set DVP and SPI pin to 1.8V */ + sysctl::set_power_mode(sysctl::power_bank::BANK6, sysctl::io_power_mode::V18); + sysctl::set_power_mode(sysctl::power_bank::BANK7, sysctl::io_power_mode::V18); +} + +#[entry] +fn main() -> ! { + let p = pac::Peripherals::take().unwrap(); + + // Configure clocks (TODO) + let clocks = k210_hal::clock::Clocks::new(); + + usleep(200000); + + // Configure UART + let serial = p.UARTHS.constrain(115_200.bps(), &clocks); + let (mut tx, _) = serial.split(); + + let mut stdout = Stdout(&mut tx); + + io_init(); + + let spi = p.SPI0.constrain(); + let mut lcd = LCD::new(spi); + lcd.init(); + lcd.set_direction(lcd::direction::YX_LRUD); + lcd.clear(lcd_colors::PURPLE); + + let mut dvp = p.DVP.constrain(); + writeln!(stdout, "OV2640: init").unwrap(); + dvp.init(sccb_addr_len::W8); + writeln!(stdout, "OV2640: set xclk rate").unwrap(); + dvp.set_xclk_rate(24000000); + writeln!(stdout, "OV2640: set image format").unwrap(); + dvp.set_image_format(image_format::RGB); + writeln!(stdout, "OV2640: set image size").unwrap(); + dvp.set_image_size(true, 320, 240); + let (manuf_id, device_id) = ov2640::read_id(&dvp); + writeln!(stdout, "OV2640: manuf 0x{:04x} device 0x{:04x}", manuf_id, device_id).unwrap(); + if manuf_id != 0x7fa2 || device_id != 0x2642 { + writeln!(stdout, "Warning: unknown chip").unwrap(); + } + ov2640::init(&dvp); + writeln!(stdout, "OV2640: init done").unwrap(); + + let mut image: ScreenImage = [0; DISP_WIDTH * DISP_HEIGHT / 2]; + + dvp.set_ai_addr(None); + dvp.set_display_addr(Some(image.as_mut_ptr())); + dvp.set_auto(false); + + writeln!(stdout, "OV2640: starting frame loop").unwrap(); + loop { + dvp.get_image(); + lcd.draw_picture(0, 0, DISP_WIDTH as u16, DISP_HEIGHT as u16, &image); + } +} diff --git a/rust/k210-shared/src/board/ov2640.rs b/rust/k210-shared/src/board/ov2640.rs index e6e625c..7e53512 100644 --- a/rust/k210-shared/src/board/ov2640.rs +++ b/rust/k210-shared/src/board/ov2640.rs @@ -194,9 +194,9 @@ static CONFIG: &[(u8, u8)] = &[ (0xe0, 0x00), ]; -pub fn ov2640_init(dvp: &DVP) { +pub fn init(dvp: &DVP) { let (_manuf_id, _device_id) = read_id(dvp); - // TODO: do something with the IDs + // TODO: do something with the IDs (like check it against expected and fail if different?) // 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()); diff --git a/rust/k210-shared/src/soc/dvp.rs b/rust/k210-shared/src/soc/dvp.rs index 0f6c3f8..9bea799 100644 --- a/rust/k210-shared/src/soc/dvp.rs +++ b/rust/k210-shared/src/soc/dvp.rs @@ -36,7 +36,7 @@ pub struct DVP { } /** Borrow image_format enum from pac */ -pub type image_format = dvp::dvp_cfg::FORMATW; +pub use dvp::dvp_cfg::FORMATW as image_format; impl DVP { /** Set SCCB clock to a safe and deterministic value (as low as possible) */ @@ -132,15 +132,15 @@ impl DVP { fn reset(&self) { // First power down self.dvp.cmos_cfg.modify(|_,w| w.power_down().set_bit()); - usleep(200_000); + usleep(2000); // what do the actual timings here need to be? self.dvp.cmos_cfg.modify(|_,w| w.power_down().clear_bit()); - usleep(200_000); + usleep(2000); // Second reset self.dvp.cmos_cfg.modify(|_,w| w.reset().clear_bit()); - usleep(200_000); + usleep(2000); self.dvp.cmos_cfg.modify(|_,w| w.reset().set_bit()); - usleep(200_000); + usleep(2000); } /** Initialize DVP peripheral */ @@ -220,8 +220,8 @@ impl DVP { } } - /** Set address for 16-bit R5G6B5 output */ - pub fn set_display_addr(&self, addr: Option<*mut u16>) { + /** Set address for 16-bit R5G6B5 output packed into 32-bit units */ + pub fn set_display_addr(&self, addr: Option<*mut u32>) { if let Some(addr) = addr { unsafe { self.dvp.rgb_addr.write(|w| w.bits(((addr as usize) & 0xffffffff) as u32));