rust: Add rgbcontrol

This commit is contained in:
Wladimir J. van der Laan 2019-05-23 09:22:50 +00:00
parent 8ca44a288f
commit d14c640356
30 changed files with 362 additions and 140 deletions

View File

@ -132,6 +132,13 @@ Pass through UART from host to the ESP8285 WIFI chip.
[README](rust/uart-passthrough/README.md) [README](rust/uart-passthrough/README.md)
rust/rgbcontrol
----------------
Control the color of the RGB LED from the touch screen.
[README](rust/rgbcontrol/README.md)
ROM re'ing ROM re'ing
=========== ===========

View File

@ -5,6 +5,7 @@ members = [
"game-of-life", "game-of-life",
"accelerometer", "accelerometer",
"uart-passthrough", "uart-passthrough",
"rgbcontrol",
] ]
[patch.crates-io] [patch.crates-io]

View File

@ -8,8 +8,9 @@ use k210_hal::pac;
use k210_hal::prelude::*; use k210_hal::prelude::*;
use k210_hal::stdout::Stdout; use k210_hal::stdout::Stdout;
use k210_shared::board::def::{io,DISP_WIDTH,DISP_HEIGHT,MSA300_SLV_ADDR,MSA300_ADDR_BITS,MSA300_CLK}; use k210_shared::board::def::{io,DISP_WIDTH,DISP_HEIGHT,MSA300_SLV_ADDR,MSA300_ADDR_BITS,MSA300_CLK};
use k210_shared::board::lcd::{LCD,self}; use k210_shared::board::lcd::{LCD,LCDHL,self};
use k210_shared::board::lcd_colors; use k210_shared::board::lcd_colors;
use k210_shared::board::lcd_render::render_image;
use k210_shared::board::msa300::Accelerometer; use k210_shared::board::msa300::Accelerometer;
use k210_shared::soc::fpioa; use k210_shared::soc::fpioa;
use k210_shared::soc::i2c::{I2C,I2CExt}; use k210_shared::soc::i2c::{I2C,I2CExt};
@ -23,12 +24,6 @@ pub const BLK_SIZE: usize = 8;
pub const GRID_WIDTH: usize = DISP_WIDTH / BLK_SIZE; pub const GRID_WIDTH: usize = DISP_WIDTH / BLK_SIZE;
pub const GRID_HEIGHT: usize = DISP_HEIGHT / BLK_SIZE; pub const GRID_HEIGHT: usize = DISP_HEIGHT / BLK_SIZE;
/** Array for representing an image of the entire screen.
* This is an array of DISP_WIDTH / 2 × DISP_HEIGHT, each two horizontally consecutive
* pixels are encoded in a u32 with `(a << 16)|b`.
*/
pub type ScreenImage = [u32; DISP_WIDTH * DISP_HEIGHT / 2];
/** Connect pins to internal functions */ /** Connect pins to internal functions */
fn io_mux_init() { fn io_mux_init() {
/* Init SPI IO map and function settings */ /* Init SPI IO map and function settings */
@ -82,8 +77,6 @@ fn main() -> ! {
lcd.set_direction(lcd::direction::YX_LRUD); lcd.set_direction(lcd::direction::YX_LRUD);
lcd.clear(lcd_colors::PURPLE); lcd.clear(lcd_colors::PURPLE);
let mut image: ScreenImage = [0; DISP_WIDTH * DISP_HEIGHT / 2];
writeln!(stdout, "MSA300 init").unwrap(); writeln!(stdout, "MSA300 init").unwrap();
let i2c = p.I2C0.constrain(); let i2c = p.I2C0.constrain();
i2c.init(MSA300_SLV_ADDR, MSA300_ADDR_BITS, MSA300_CLK); i2c.init(MSA300_SLV_ADDR, MSA300_ADDR_BITS, MSA300_CLK);
@ -99,17 +92,13 @@ fn main() -> ! {
let cy = ((x/8.5 + 1.0) * ((DISP_HEIGHT / 2) as f32)) as i32; let cy = ((x/8.5 + 1.0) * ((DISP_HEIGHT / 2) as f32)) as i32;
let r = (1.5*mag) as i32; let r = (1.5*mag) as i32;
let rr = r * r; let rr = r * r;
let mut idx = 0; render_image(&mut lcd, |x,y| {
for y in 0..DISP_HEIGHT as i32 { if sample_cirle(x as i32, y as i32, cx, cy, r, rr) {
for x2 in 0..(DISP_WIDTH/2) as i32 { 0xffff
let x = x2 * 2; } else {
image[idx] = (if sample_cirle(x + 0, y, cx, cy, r, rr) { 0xffff } else { 0 } << 16) | 0
if sample_cirle(x + 1, y, cx, cy, r, rr) { 0xffff } else { 0 };
idx += 1;
} }
} });
lcd.draw_picture(0, 0, DISP_WIDTH as u16, DISP_HEIGHT as u16, &image);
// usleep(10000); // usleep(10000);
} }
} }

View File

@ -1,9 +0,0 @@
[target.riscv64gc-unknown-none-elf]
runner = "riscv64-unknown-elf-gdb -x gdb_init"
rustflags = [
"-C", "link-arg=-Tmemory.x",
"-C", "link-arg=-Tlink.x",
]
[build]
target = "riscv64gc-unknown-none-elf"

View File

@ -1,3 +0,0 @@
target remote :3333
load
c

View File

@ -1,11 +0,0 @@
transport select jtag
adapter_khz 1000
set _CHIPNAME riscv
jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x04e4796b
set _TARGETNAME $_CHIPNAME.cpu
target create $_TARGETNAME riscv -chain-position $_TARGETNAME
init
halt

View File

@ -8,7 +8,7 @@ use k210_hal::pac;
use k210_hal::prelude::*; use k210_hal::prelude::*;
use k210_hal::stdout::Stdout; use k210_hal::stdout::Stdout;
use k210_shared::board::def::{io,DISP_WIDTH,DISP_HEIGHT,NS2009_SLV_ADDR,NS2009_CAL,NS2009_ADDR_BITS,NS2009_CLK}; use k210_shared::board::def::{io,DISP_WIDTH,DISP_HEIGHT,NS2009_SLV_ADDR,NS2009_CAL,NS2009_ADDR_BITS,NS2009_CLK};
use k210_shared::board::lcd::{LCD,self}; use k210_shared::board::lcd::{LCD,LCDHL,self};
use k210_shared::board::lcd_colors; use k210_shared::board::lcd_colors;
use k210_shared::board::ns2009::TouchScreen; use k210_shared::board::ns2009::TouchScreen;
use k210_shared::soc::fpioa; use k210_shared::soc::fpioa;

View File

@ -1,9 +0,0 @@
[target.riscv64gc-unknown-none-elf]
runner = "riscv64-unknown-elf-gdb -x gdb_init"
rustflags = [
"-C", "link-arg=-Tmemory.x",
"-C", "link-arg=-Tlink.x",
]
[build]
target = "riscv64gc-unknown-none-elf"

View File

@ -1,3 +0,0 @@
target remote :3333
load
c

View File

@ -1,11 +0,0 @@
transport select jtag
adapter_khz 1000
set _CHIPNAME riscv
jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x04e4796b
set _TARGETNAME $_CHIPNAME.cpu
target create $_TARGETNAME riscv -chain-position $_TARGETNAME
init
halt

View File

@ -14,7 +14,7 @@ use k210_hal::pac;
use k210_hal::prelude::*; use k210_hal::prelude::*;
use k210_hal::stdout::Stdout; use k210_hal::stdout::Stdout;
use k210_shared::board::def::io; use k210_shared::board::def::io;
use k210_shared::board::lcd::{LCD,self}; use k210_shared::board::lcd::{LCD,LCDHL,self};
use k210_shared::board::lcd_colors; use k210_shared::board::lcd_colors;
use k210_shared::soc::fpioa; use k210_shared::soc::fpioa;
use k210_shared::soc::sleep::usleep; use k210_shared::soc::sleep::usleep;

View File

@ -5,6 +5,7 @@ authors = ["W.J. van der Laan <laanwj@protonmail.com>"]
edition = "2018" edition = "2018"
[dependencies] [dependencies]
riscv-rt = "0.5.0"
k210-hal = "0.1.0" k210-hal = "0.1.0"
libm = "0.1"
riscv = "0.5" riscv = "0.5"
riscv-rt = "0.5.0"

View File

@ -1,5 +1,6 @@
pub mod def; pub mod def;
pub mod lcd; pub mod lcd;
pub mod lcd_colors; pub mod lcd_colors;
pub mod lcd_render;
pub mod msa300; pub mod msa300;
pub mod ns2009; pub mod ns2009;

View File

@ -117,6 +117,25 @@ pub struct LCD<SPI> {
pub height: u16, pub height: u16,
} }
/** Low-level interface */
pub trait LCDLL {
fn hard_init(&self);
fn write_command(&self, cmd: command);
fn write_byte(&self, data_buf: &[u8]);
fn write_half(&self, data_buf: &[u16]);
fn write_word(&self, data_buf: &[u32]);
fn fill_data(&self, data: u32, length: usize);
}
/** High-level interface */
pub trait LCDHL {
fn init(&mut self);
fn set_direction(&mut self, dir: direction);
fn set_area(&self, x1: u16, y1: u16, x2: u16, y2: u16);
fn clear(&self, color: u16);
fn draw_picture(&self, x1: u16, y1: u16, width: u16, height: u16, data: &[u32]);
}
impl<X: SPI> LCD<X> { impl<X: SPI> LCD<X> {
pub fn new(spi: X) -> Self { pub fn new(spi: X) -> Self {
Self { Self {
@ -126,8 +145,6 @@ impl<X: SPI> LCD<X> {
} }
} }
/* Low-level functions */
fn init_dcx(&self) { fn init_dcx(&self) {
gpiohs::set_direction(DCX_GPIONUM, gpio::direction::OUTPUT); gpiohs::set_direction(DCX_GPIONUM, gpio::direction::OUTPUT);
gpiohs::set_pin(DCX_GPIONUM, true); gpiohs::set_pin(DCX_GPIONUM, true);
@ -149,8 +166,11 @@ impl<X: SPI> LCD<X> {
fn set_rst(&self, val: bool) { fn set_rst(&self, val: bool) {
gpiohs::set_pin(RST_GPIONUM, val); gpiohs::set_pin(RST_GPIONUM, val);
} }
}
pub fn hard_init(&self) { /** Low-level functions */
impl<X: SPI> LCDLL for LCD<X> {
fn hard_init(&self) {
self.init_dcx(); self.init_dcx();
self.init_rst(); self.init_rst();
self.set_rst(false); self.set_rst(false);
@ -169,7 +189,7 @@ impl<X: SPI> LCD<X> {
self.set_rst(true); self.set_rst(true);
} }
pub fn write_command(&self, cmd: command) { fn write_command(&self, cmd: command) {
self.set_dcx_control(); self.set_dcx_control();
self.spi.configure( self.spi.configure(
ctrlr0::WORK_MODEW::MODE0, ctrlr0::WORK_MODEW::MODE0,
@ -185,7 +205,7 @@ impl<X: SPI> LCD<X> {
self.spi.send_data(SPI_SLAVE_SELECT, &[cmd as u8]); self.spi.send_data(SPI_SLAVE_SELECT, &[cmd as u8]);
} }
pub fn write_byte(&self, data_buf: &[u8]) { fn write_byte(&self, data_buf: &[u8]) {
self.set_dcx_data(); self.set_dcx_data();
self.spi.configure( self.spi.configure(
ctrlr0::WORK_MODEW::MODE0, ctrlr0::WORK_MODEW::MODE0,
@ -201,7 +221,7 @@ impl<X: SPI> LCD<X> {
self.spi.send_data(SPI_SLAVE_SELECT, data_buf); self.spi.send_data(SPI_SLAVE_SELECT, data_buf);
} }
pub fn write_half(&self, data_buf: &[u16]) { fn write_half(&self, data_buf: &[u16]) {
self.set_dcx_data(); self.set_dcx_data();
self.spi.configure( self.spi.configure(
ctrlr0::WORK_MODEW::MODE0, ctrlr0::WORK_MODEW::MODE0,
@ -217,7 +237,7 @@ impl<X: SPI> LCD<X> {
self.spi.send_data(SPI_SLAVE_SELECT, data_buf); self.spi.send_data(SPI_SLAVE_SELECT, data_buf);
} }
pub fn write_word(&self, data_buf: &[u32]) { fn write_word(&self, data_buf: &[u32]) {
self.set_dcx_data(); self.set_dcx_data();
self.spi.configure( self.spi.configure(
ctrlr0::WORK_MODEW::MODE0, ctrlr0::WORK_MODEW::MODE0,
@ -233,7 +253,7 @@ impl<X: SPI> LCD<X> {
self.spi.send_data(SPI_SLAVE_SELECT, data_buf); self.spi.send_data(SPI_SLAVE_SELECT, data_buf);
} }
pub fn fill_data(&self, data: u32, length: usize) { fn fill_data(&self, data: u32, length: usize) {
self.set_dcx_data(); self.set_dcx_data();
self.spi.configure( self.spi.configure(
ctrlr0::WORK_MODEW::MODE0, ctrlr0::WORK_MODEW::MODE0,
@ -248,10 +268,11 @@ impl<X: SPI> LCD<X> {
); );
self.spi.fill_data(SPI_SLAVE_SELECT, data, length); self.spi.fill_data(SPI_SLAVE_SELECT, data, length);
} }
}
/* High-level functions */ /* High-level functions */
impl<X: SPI> LCDHL for LCD<X> {
pub fn init(&mut self) { fn init(&mut self) {
self.hard_init(); self.hard_init();
/*soft reset*/ /*soft reset*/
self.write_command(command::SOFTWARE_RESET); self.write_command(command::SOFTWARE_RESET);
@ -268,7 +289,7 @@ impl<X: SPI> LCD<X> {
self.write_command(command::DISPLAY_ON); self.write_command(command::DISPLAY_ON);
} }
pub fn set_direction(&mut self, dir: direction) { fn set_direction(&mut self, dir: direction) {
if ((dir as u8) & DIR_XY_MASK) != 0 { if ((dir as u8) & DIR_XY_MASK) != 0 {
self.width = LCD_Y_MAX; self.width = LCD_Y_MAX;
self.height = LCD_X_MAX; self.height = LCD_X_MAX;
@ -281,7 +302,7 @@ impl<X: SPI> LCD<X> {
self.write_byte(&[dir as u8]); self.write_byte(&[dir as u8]);
} }
pub fn set_area(&self, x1: u16, y1: u16, x2: u16, y2: u16) { fn set_area(&self, x1: u16, y1: u16, x2: u16, y2: u16) {
self.write_command(command::HORIZONTAL_ADDRESS_SET); self.write_command(command::HORIZONTAL_ADDRESS_SET);
self.write_byte(&[ self.write_byte(&[
(x1 >> 8) as u8, (x1 >> 8) as u8,
@ -301,14 +322,14 @@ impl<X: SPI> LCD<X> {
self.write_command(command::MEMORY_WRITE); self.write_command(command::MEMORY_WRITE);
} }
pub fn clear(&self, color: u16) { fn clear(&self, color: u16) {
let data = ((color as u32) << 16) | (color as u32); let data = ((color as u32) << 16) | (color as u32);
self.set_area(0, 0, self.width - 1, self.height - 1); self.set_area(0, 0, self.width - 1, self.height - 1);
self.fill_data(data, (LCD_X_MAX as usize) * (LCD_Y_MAX as usize) / 2); self.fill_data(data, (LCD_X_MAX as usize) * (LCD_Y_MAX as usize) / 2);
} }
pub fn draw_picture(&self, x1: u16, y1: u16, width: u16, height: u16, data: &[u32]) { fn draw_picture(&self, x1: u16, y1: u16, width: u16, height: u16, data: &[u32]) {
self.set_area(x1, y1, x1 + width - 1, y1 + height - 1); self.set_area(x1, y1, x1 + width - 1, y1 + height - 1);
assert!(data.len() == (width as usize) * (height as usize) / 2); assert!(data.len() == (width as usize) * (height as usize) / 2);
self.write_word(data); self.write_word(data);

View File

@ -1,3 +1,5 @@
use libm::F32Ext;
/** Some convenient RGB565 colors */ /** Some convenient RGB565 colors */
pub const BLACK: u16 = 0x0000; pub const BLACK: u16 = 0x0000;
pub const NAVY: u16 = 0x000F; pub const NAVY: u16 = 0x000F;
@ -21,5 +23,55 @@ pub const PINK: u16 = 0xF81F;
/** Truncate 8 bit RGB to RBG565 */ /** Truncate 8 bit RGB to RBG565 */
pub const fn rgb565(r: u8, g: u8, b: u8) -> u16 { pub const fn rgb565(r: u8, g: u8, b: u8) -> u16 {
return (((r as u16) >> 3) << 11) | (((g as u16) >> 2) << 5) | ((b as u16) >> 3); (((r as u16) >> 3) << 11) | (((g as u16) >> 2) << 5) | ((b as u16) >> 3)
}
/** 32.0 minus 1ulp */
const ALMOST_32: f32 = 31.999998f32;
/** 64.0 minus 1ulp */
const ALMOST_64: f32 = 63.999996f32;
/** Truncate 32 bit RGB to RBG565 */
pub fn rgbf565(r: f32, g: f32, b: f32) -> u16 {
(((r * ALMOST_32) as u16) << 11) |
(((g * ALMOST_64) as u16) << 5) |
((b * ALMOST_32) as u16)
}
/** HSV to RGB. `h` is 0.0..360.0, `s` and `v` are 0.0..1.0 output RGB will be 0.0..1.0 (all ranges
* inclusive)
*/
pub fn hsv2rgb(h: f32, s: f32, v: f32) -> (f32, f32, f32) {
let h = h / 60.0;
let i = h.trunc();
let f = h - i;
let c = v * (1.0 - s * f);
let b = v * (1.0 - s + s * f);
let o = v * (1.0 - s);
match i as u32 {
// yellow to green
1 => (c, v, o),
// green to cyan
2 => (o, v, b),
// cyan to blue
3 => (o, c, v),
// blue to magenta
4 => (b, o, v),
// magenta to red
5 => (v, o, c),
// red to yellow
_ => (v, b, o),
}
}
/** Clamp a float between 0 and 1 */
pub fn clampf(v: f32) -> f32 {
if v < 0.0 {
0.0
} else if v > 1.0 {
1.0
} else {
v
}
} }

View File

@ -0,0 +1,33 @@
/** Efficient(?) full-image rendering */
use crate::board::def::{DISP_HEIGHT, DISP_WIDTH};
use crate::board::lcd::LCDHL;
/** Array for representing an image of the entire screen.
* This is an array of DISP_WIDTH / 2 × DISP_HEIGHT, each two horizontally consecutive
* pixels are encoded in a u32 with `(a << 16)|b`.
*/
pub type ScreenImage = [u32; DISP_WIDTH * DISP_HEIGHT / 2];
pub fn render_image<L, I>(lcd: &mut L, mut image: I)
where
L: LCDHL,
I: FnMut(u16, u16) -> u16,
{
// Theoretically this initialization could be avoided by directly initializing from an
// iterator, however, rust doesn't have built-in functionality for this. There's a crate
// (array_init) but it doesn't work for large arrays.
let mut idata: ScreenImage = [0; DISP_WIDTH * DISP_HEIGHT / 2];
let yx = (0..DISP_HEIGHT)
.flat_map(|y| core::iter::repeat(y as u16).zip(0 as u16..(DISP_WIDTH / 2) as u16));
idata.iter_mut().zip(yx).for_each(|(v, (y, x))| {
*v = ((image(x * 2 + 0, y) as u32) << 16) | (image(x * 2 + 1, y) as u32);
});
// It would be possible to make draw_picture take an iterator directly
// instead of rendering to an array first, however, this means that the
// computation has to keep up with the SPI clock speed or there will be
// glitches -- also it means that DMA cannot be used -- whereas a sufficiently
// advanced DMA engine is indistinguishable from a GPU, the one in K210
// isn't that.
lcd.draw_picture(0, 0, DISP_WIDTH as u16, DISP_HEIGHT as u16, &idata);
}

View File

@ -2,6 +2,7 @@ pub mod fpioa;
pub mod gpio; pub mod gpio;
pub mod gpiohs; pub mod gpiohs;
pub mod i2c; pub mod i2c;
pub mod pwm;
pub mod sleep; pub mod sleep;
pub mod spi; pub mod spi;
pub mod sysctl; pub mod sysctl;

View File

@ -0,0 +1,56 @@
use k210_hal::pac;
// TODO: generalize over other timers than TIMER0
// use pac::{timer0,TIMER0,TIMER1,TIMER2};
use crate::soc::sysctl;
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum Channel {
CH1 = 0,
CH2,
CH3,
CH4,
}
/** Start a PWM channel */
pub fn pwm_start(ch: Channel) {
unsafe {
let ptr = pac::TIMER0::ptr();
use pac::timer0::channel::control::MODEW;
// set a deterministic value for load counts
(*ptr).channel[ch as usize].load_count.write(|w| w.bits(1));
(*ptr).load_count2[ch as usize].write(|w| w.bits(1));
// start channel
(*ptr).channel[ch as usize].control.write(
|w| w.interrupt().set_bit()
.pwm_enable().set_bit()
.mode().variant(MODEW::USER)
.enable().set_bit());
}
}
/** Stop a PWM channel */
pub fn pwm_stop(ch: Channel) {
unsafe {
let ptr = pac::TIMER0::ptr();
(*ptr).channel[ch as usize].control.write(
|w| w.interrupt().set_bit());
}
}
/** Set frequency and value for a PWM channel */
pub fn pwm_set(ch: Channel, freq: u32, value: f32) -> u32 {
let clk_freq = sysctl::clock_get_freq(sysctl::clock::TIMER0);
let periods = clk_freq / freq;
let percent = (value * (periods as f32)) as u32;
unsafe {
let ptr = pac::TIMER0::ptr();
(*ptr).channel[ch as usize].load_count.write(|w| w.bits(periods - percent));
(*ptr).load_count2[ch as usize].write(|w| w.bits(percent));
}
clk_freq / periods
}

View File

@ -1,9 +0,0 @@
[target.riscv64gc-unknown-none-elf]
runner = "riscv64-unknown-elf-gdb -x gdb_init"
rustflags = [
"-C", "link-arg=-Tmemory.x",
"-C", "link-arg=-Tlink.x",
]
[build]
target = "riscv64gc-unknown-none-elf"

View File

@ -1,3 +0,0 @@
target remote :3333
load
c

View File

@ -1,11 +0,0 @@
transport select jtag
adapter_khz 1000
set _CHIPNAME riscv
jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x04e4796b
set _TARGETNAME $_CHIPNAME.cpu
target create $_TARGETNAME riscv -chain-position $_TARGETNAME
init
halt

View File

@ -10,8 +10,9 @@ use k210_hal::pac;
use k210_hal::prelude::*; use k210_hal::prelude::*;
use k210_hal::stdout::Stdout; use k210_hal::stdout::Stdout;
use k210_shared::board::def::{io,DISP_WIDTH,DISP_HEIGHT}; use k210_shared::board::def::{io,DISP_WIDTH,DISP_HEIGHT};
use k210_shared::board::lcd::{LCD,self}; use k210_shared::board::lcd::{LCD,LCDHL,self};
use k210_shared::board::lcd_colors; use k210_shared::board::lcd_colors;
use k210_shared::board::lcd_render::render_image;
use k210_shared::soc::fpioa; use k210_shared::soc::fpioa;
use k210_shared::soc::sleep::usleep; use k210_shared::soc::sleep::usleep;
use k210_shared::soc::spi::SPIExt; use k210_shared::soc::spi::SPIExt;
@ -20,8 +21,6 @@ use riscv_rt::entry;
use crate::palette::PALETTE; use crate::palette::PALETTE;
pub type ScreenImage = [u32; DISP_WIDTH * DISP_HEIGHT / 2];
/** Connect pins to internal functions */ /** Connect pins to internal functions */
fn io_mux_init() { fn io_mux_init() {
/* Init SPI IO map and function settings */ /* Init SPI IO map and function settings */
@ -52,16 +51,6 @@ fn mandelbrot(cx: f32, cy: f32, iterations: u32) -> u32 {
i i
} }
fn compute(x: u16, y: u16, zoom: f32) -> u16 {
let ofsx = 0.02997f32;
let ofsy = 0.80386f32;
let xx = 2.0 * (x as f32) / ((DISP_WIDTH-1) as f32) - 1.0;
let yy = 2.0 * (y as f32) / ((DISP_HEIGHT-1) as f32) - 1.0;
let i = mandelbrot(xx * zoom + ofsx, yy * zoom + ofsy, 20);
PALETTE[i as usize]
}
#[entry] #[entry]
fn main() -> ! { fn main() -> ! {
let p = pac::Peripherals::take().unwrap(); let p = pac::Peripherals::take().unwrap();
@ -86,22 +75,19 @@ fn main() -> ! {
lcd.set_direction(lcd::direction::YX_RLDU); lcd.set_direction(lcd::direction::YX_RLDU);
lcd.clear(lcd_colors::PURPLE); lcd.clear(lcd_colors::PURPLE);
let mut image: ScreenImage = [0; DISP_WIDTH * DISP_HEIGHT / 2];
writeln!(stdout, "First frame").unwrap(); writeln!(stdout, "First frame").unwrap();
let mut frame = 0;
let mut zoom = 5.0f32; let mut zoom = 5.0f32;
let ofsx = 0.02997f32;
let ofsy = 0.80386f32;
loop { loop {
let mut ofs = 0; render_image(&mut lcd, |x,y| {
for y in 0..DISP_HEIGHT as u16 { let xx = 2.0 * (x as f32) / ((DISP_WIDTH-1) as f32) - 1.0;
for x in 0..(DISP_WIDTH/2) as u16 { let yy = 2.0 * (y as f32) / ((DISP_HEIGHT-1) as f32) - 1.0;
image[ofs] = ((compute(x*2+0, y, zoom) as u32)<<16) | (compute(x*2+1, y, zoom) as u32); let i = mandelbrot(xx * zoom + ofsx, yy * zoom + ofsy, 20);
ofs += 1;
} PALETTE[i as usize]
} });
lcd.draw_picture(0, 0, DISP_WIDTH as u16, DISP_HEIGHT as u16, &image);
frame += 1;
zoom *= 0.98f32; zoom *= 0.98f32;
} }
} }

2
rust/rgbcontrol/.gitignore vendored Normal file
View File

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

View File

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

10
rust/rgbcontrol/README.md Normal file
View File

@ -0,0 +1,10 @@
# `rgbcontrol`
Control the color of the RGB LED from the touch screen.
Displays a HSV color picker and sets sets the color of the LED based on the
position that is touched, with the intensity derived from the touch pressure.
Exercises the PWM functionality. Channel 1-3 of TIMER0 are assigned to the
R, G and B pins.

128
rust/rgbcontrol/src/main.rs Normal file
View File

@ -0,0 +1,128 @@
#![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_HEIGHT, DISP_WIDTH, NS2009_ADDR_BITS, NS2009_CAL, NS2009_CLK, NS2009_SLV_ADDR,
};
use k210_shared::board::lcd::{self, LCD, LCDHL};
use k210_shared::board::lcd_colors::{clampf, hsv2rgb, rgbf565};
use k210_shared::board::lcd_render::render_image;
use k210_shared::board::ns2009::TouchScreen;
use k210_shared::soc::fpioa;
use k210_shared::soc::i2c::{I2CExt, I2C};
use k210_shared::soc::pwm::{pwm_set, pwm_start, Channel};
use k210_shared::soc::sleep::usleep;
use k210_shared::soc::spi::SPIExt;
use k210_shared::soc::sysctl;
use riscv_rt::entry;
// Factor for equalizing relative brightness of R/G/B leds
const R_SCALE: f32 = 1.0;
const G_SCALE: f32 = 0.16;
const B_SCALE: f32 = 0.33;
fn io_init() {
/* 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);
/* Route PWM outputs of TIMER0 to RGB leds */
fpioa::set_function(io::LED_R as u8, fpioa::function::TIMER0_TOGGLE1);
fpioa::set_function(io::LED_G as u8, fpioa::function::TIMER0_TOGGLE2);
fpioa::set_function(io::LED_B as u8, fpioa::function::TIMER0_TOGGLE3);
sysctl::set_spi0_dvp_data(true);
/* I2C0 for touch-screen */
fpioa::set_function(io::I2C1_SCL.into(), fpioa::function::I2C0_SCLK);
fpioa::set_function(io::I2C1_SDA.into(), fpioa::function::I2C0_SDA);
/* Set DVP and SPI pins 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);
}
/** Color picker */
fn color_from_xy(x: u16, y: u16, v: f32) -> (f32, f32, f32) {
hsv2rgb(
360.0 * (x as f32) / (DISP_WIDTH as f32),
(y as f32) / ((DISP_HEIGHT - 1) as f32),
v,
)
}
#[entry]
fn main() -> ! {
let p = pac::Peripherals::take().unwrap();
let clocks = k210_hal::clock::Clocks::new();
usleep(200000);
let serial = p.UARTHS.constrain(115_200.bps(), &clocks);
let (mut tx, _) = serial.split();
let mut stdout = Stdout(&mut tx);
io_init();
writeln!(stdout, "NS2009 init").unwrap();
let i2c = p.I2C0.constrain();
i2c.init(NS2009_SLV_ADDR, NS2009_ADDR_BITS, NS2009_CLK);
let mut ts = if let Some(ts) = TouchScreen::init(i2c, NS2009_CAL) {
ts
} else {
writeln!(stdout, "NS2009 init failure").unwrap();
panic!("Fatal error");
};
writeln!(stdout, "LCD init").unwrap();
let spi = p.SPI0.constrain();
let mut lcd = LCD::new(spi);
lcd.init();
lcd.set_direction(lcd::direction::YX_LRUD);
render_image(&mut lcd, |x, y| {
let (r, g, b) = color_from_xy(x, y, 1.0);
rgbf565(r, g, b)
});
writeln!(stdout, "start PWM").unwrap();
sysctl::clock_enable(sysctl::clock::TIMER0);
pwm_start(Channel::CH1);
pwm_start(Channel::CH2);
pwm_start(Channel::CH3);
let freq = 10000;
loop {
if let Some(ev) = ts.poll() {
if ev.z > 0
&& ev.x >= 0
&& ev.x < (DISP_WIDTH as i32)
&& ev.y >= 0
&& ev.y < (DISP_HEIGHT as i32)
{
//writeln!(stdout, "{:?}", ev).unwrap();
let (r, g, b) =
color_from_xy(ev.x as u16, ev.y as u16, clampf(ev.z as f32 / 1000.0));
pwm_set(Channel::CH1, freq, 1.0 - r * R_SCALE);
pwm_set(Channel::CH2, freq, 1.0 - g * G_SCALE);
pwm_set(Channel::CH3, freq, 1.0 - b * B_SCALE);
}
}
usleep(10000);
}
}

View File

@ -1,8 +0,0 @@
[target.riscv64gc-unknown-none-elf]
rustflags = [
"-C", "link-arg=-Tmemory.x",
"-C", "link-arg=-Tlink.x",
]
[build]
target = "riscv64gc-unknown-none-elf"