rust: Add dvp-ov

This commit is contained in:
Wladimir J. van der Laan 2019-06-01 21:00:26 +00:00
parent cd7f72f03f
commit 1630dc2a31
8 changed files with 142 additions and 9 deletions

View File

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

View File

@ -7,6 +7,7 @@ members = [
"uart-passthrough",
"rgbcontrol",
"weather",
"dvp-ov",
]
[patch.crates-io]

2
rust/dvp-ov/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/target
**/*.rs.bk

11
rust/dvp-ov/Cargo.toml Normal file
View File

@ -0,0 +1,11 @@
[package]
name = "dvp-ov"
version = "0.1.0"
authors = ["W.J. van der Laan <laanwj@protonmail.com>"]
edition = "2018"
[dependencies]
riscv-rt = "0.5.0"
k210-hal = "0.1.0"
riscv = "0.5"
k210-shared = { path = "../k210-shared" }

7
rust/dvp-ov/README.md Normal file
View File

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

102
rust/dvp-ov/src/main.rs Normal file
View File

@ -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);
}
}

View File

@ -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());

View File

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