rust: Add rust version of glyph mapping demo

This commit is contained in:
Wladimir J. van der Laan 2019-06-02 12:51:13 +00:00
parent 1531cc8fe0
commit ec7ce47dff
8 changed files with 177 additions and 4 deletions

View File

@ -195,6 +195,13 @@ SDK: read frames from the OV2640 image sensor and display them on the LCD.
[README](rust/dvp-ov/README.md)
rust/glyph-mapping
------------------
Rust port of the glyph mapping demo.
[README](rust/glyph-mapping/README.md)
ROM re'ing
===========

View File

@ -8,6 +8,7 @@ members = [
"rgbcontrol",
"weather",
"dvp-ov",
"glyph-mapping",
]
[patch.crates-io]

View File

@ -59,8 +59,6 @@ fn io_init() {
#[entry]
fn main() -> ! {
let p = pac::Peripherals::take().unwrap();
// Configure clocks (TODO)
let clocks = k210_hal::clock::Clocks::new();
usleep(200000);

2
rust/glyph-mapping/.gitignore vendored Normal file
View File

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

View File

@ -0,0 +1,12 @@
[package]
name = "glyph-mapping"
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" }
k210-console = { path = "../k210-console" }

View File

@ -0,0 +1,5 @@
# `glyph-mapping`
Display the real-time image from ov5640 passed through a glyph-mapping algorithm.
This samples the camera in planar R8G8B8 format.

View File

@ -0,0 +1,146 @@
#![allow(dead_code)]
#![allow(non_snake_case)]
#![allow(non_camel_case_types)]
#![no_std]
#![no_main]
use k210_console::console::{Console, ScreenImage};
use k210_console::cp437_8x8::GLYPH_BY_FILL;
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::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;
/** 64-byte aligned planar RAM */
#[repr(align(64))]
struct PlanarScreenRAM {
pub r: [u8; DISP_WIDTH * DISP_HEIGHT],
pub g: [u8; DISP_WIDTH * DISP_HEIGHT],
pub b: [u8; DISP_WIDTH * DISP_HEIGHT],
}
impl PlanarScreenRAM {
fn as_mut_ptrs(&mut self) -> (*mut u8, *mut u8, *mut u8) {
(self.r.as_mut_ptr(),self.g.as_mut_ptr(),self.b.as_mut_ptr())
}
}
static mut FRAME_AI: PlanarScreenRAM = PlanarScreenRAM {
r: [0; DISP_WIDTH * DISP_HEIGHT],
g: [0; DISP_WIDTH * DISP_HEIGHT],
b: [0; DISP_WIDTH * DISP_HEIGHT],
};
/** Connect pins to internal functions */
fn io_init() {
/* Init DVP IO map and function settings */
fpioa::set_function(io::DVP_RST, fpioa::function::CMOS_RST);
fpioa::set_function(io::DVP_PWDN, fpioa::function::CMOS_PWDN);
fpioa::set_function(io::DVP_XCLK, fpioa::function::CMOS_XCLK);
fpioa::set_function(io::DVP_VSYNC, fpioa::function::CMOS_VSYNC);
fpioa::set_function(io::DVP_HSYNC, fpioa::function::CMOS_HREF);
fpioa::set_function(io::DVP_PCLK, fpioa::function::CMOS_PCLK);
fpioa::set_function(io::DVP_SCL, fpioa::function::SCCB_SCLK);
fpioa::set_function(io::DVP_SDA, fpioa::function::SCCB_SDA);
/* Init SPI IO map and function settings */
fpioa::set_function(io::LCD_RST, fpioa::function::gpiohs(lcd::RST_GPIONUM));
fpioa::set_io_pull(io::LCD_RST, fpioa::pull::DOWN); // outputs must be pull-down
fpioa::set_function(io::LCD_DC, fpioa::function::gpiohs(lcd::DCX_GPIONUM));
fpioa::set_io_pull(io::LCD_DC, fpioa::pull::DOWN);
fpioa::set_function(io::LCD_CS, fpioa::function::SPI0_SS3);
fpioa::set_function(io::LCD_WR, 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();
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);
dvp.set_xclk_rate(24000000);
dvp.set_image_format(image_format::RGB);
dvp.set_image_size(true, 320, 240);
ov2640::init(&dvp);
writeln!(stdout, "OV2640: init done").unwrap();
// use planar output for convenient sampling
dvp.set_ai_addr(Some(unsafe { FRAME_AI.as_mut_ptrs() }));
dvp.set_display_addr(None);
dvp.set_auto(false);
let mut image: ScreenImage = [0; DISP_WIDTH * DISP_HEIGHT / 2];
let mut console: Console = Console::new();
writeln!(stdout, "Starting frame loop").unwrap();
loop {
dvp.get_image();
for y in 0..console.height() {
for x in 0..console.width() {
// compute average over cell: could use some kernel that emphasizes the center
// but this works fairly okay and is nice and fast
// /
// need to mirror x and y here so that characters are right side up
// compared to camera image
let mut r = 0;
let mut g = 0;
let mut b = 0;
for iy in 0..8 {
for ix in 0..8 {
let cx = 319 - (x * 8 + ix) as usize;
let cy = 239 - (y * 8 + iy) as usize;
let addr = (cy as usize)*320+(cx as usize);
r += unsafe { FRAME_AI.r[addr] } as u32;
g += unsafe { FRAME_AI.g[addr] } as u32;
b += unsafe { FRAME_AI.b[addr] } as u32;
}
}
r /= 8*8;
g /= 8*8;
b /= 8*8;
let i = (77*r + 150*g + 29*b) / 256;
console.put_raw(
x, y,
lcd_colors::rgb565(r as u8, g as u8, b as u8),
0,
GLYPH_BY_FILL[i as usize].into(),
);
}
}
console.render(&mut image);
lcd.draw_picture(0, 0, DISP_WIDTH as u16, DISP_HEIGHT as u16, &image);
}
}

View File

@ -200,8 +200,10 @@ impl DVP {
}
}
/** Set address for planar RGB output for KPU
* `Option<(r_addr, g_addr, b_addr)>`
/** Set address for planar R8G8B8 output `Option<(r_addr, g_addr, b_addr)>`.
* This format is meant for the KPU as input but it's also usable from normal memory,
* it's simply an alternative output format. Both `display_addr` and `ai_addr` can be active at
* the same time.
*/
pub fn set_ai_addr(&self, addr: Option<(*mut u8, *mut u8, *mut u8)>) {
if let Some((r_addr, g_addr, b_addr)) = addr {