mirror of
https://github.com/laanwj/k210-sdk-stuff.git
synced 2024-11-22 17:36:20 +04:00
rust: Add dvp-ov
This commit is contained in:
parent
cd7f72f03f
commit
1630dc2a31
10
README.md
10
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.
|
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
|
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)
|
[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
|
ROM re'ing
|
||||||
===========
|
===========
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ members = [
|
|||||||
"uart-passthrough",
|
"uart-passthrough",
|
||||||
"rgbcontrol",
|
"rgbcontrol",
|
||||||
"weather",
|
"weather",
|
||||||
|
"dvp-ov",
|
||||||
]
|
]
|
||||||
|
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
|
2
rust/dvp-ov/.gitignore
vendored
Normal file
2
rust/dvp-ov/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
/target
|
||||||
|
**/*.rs.bk
|
11
rust/dvp-ov/Cargo.toml
Normal file
11
rust/dvp-ov/Cargo.toml
Normal 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
7
rust/dvp-ov/README.md
Normal 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
102
rust/dvp-ov/src/main.rs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
@ -194,9 +194,9 @@ static CONFIG: &[(u8, u8)] = &[
|
|||||||
(0xe0, 0x00),
|
(0xe0, 0x00),
|
||||||
];
|
];
|
||||||
|
|
||||||
pub fn ov2640_init(dvp: &DVP) {
|
pub fn init(dvp: &DVP) {
|
||||||
let (_manuf_id, _device_id) = read_id(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);
|
// printf("manuf_id:0x%04x,device_id:0x%04x\n", v_manuf_id, v_device_id);
|
||||||
for &(register, value) in CONFIG {
|
for &(register, value) in CONFIG {
|
||||||
dvp.sccb_send_data(OV2640_ADDR, register.into(), value.into());
|
dvp.sccb_send_data(OV2640_ADDR, register.into(), value.into());
|
||||||
|
@ -36,7 +36,7 @@ pub struct DVP {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Borrow image_format enum from pac */
|
/** 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 {
|
impl DVP {
|
||||||
/** Set SCCB clock to a safe and deterministic value (as low as possible) */
|
/** Set SCCB clock to a safe and deterministic value (as low as possible) */
|
||||||
@ -132,15 +132,15 @@ impl DVP {
|
|||||||
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(2000); // what do the actual timings here need to be?
|
||||||
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(2000);
|
||||||
|
|
||||||
// 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(2000);
|
||||||
self.dvp.cmos_cfg.modify(|_,w| w.reset().set_bit());
|
self.dvp.cmos_cfg.modify(|_,w| w.reset().set_bit());
|
||||||
usleep(200_000);
|
usleep(2000);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Initialize DVP peripheral */
|
/** Initialize DVP peripheral */
|
||||||
@ -220,8 +220,8 @@ impl DVP {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Set address for 16-bit R5G6B5 output */
|
/** Set address for 16-bit R5G6B5 output packed into 32-bit units */
|
||||||
pub fn set_display_addr(&self, addr: Option<*mut u16>) {
|
pub fn set_display_addr(&self, addr: Option<*mut u32>) {
|
||||||
if let Some(addr) = addr {
|
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));
|
||||||
|
Loading…
Reference in New Issue
Block a user