diff --git a/README.md b/README.md index a648910..c0c93c8 100644 --- a/README.md +++ b/README.md @@ -132,6 +132,13 @@ Pass through UART from host to the ESP8285 WIFI chip. [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 =========== diff --git a/rust/accelerometer/.cargo/config b/rust/.cargo/config similarity index 100% rename from rust/accelerometer/.cargo/config rename to rust/.cargo/config diff --git a/rust/Cargo.toml b/rust/Cargo.toml index c68c5fa..1c9745a 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -5,6 +5,7 @@ members = [ "game-of-life", "accelerometer", "uart-passthrough", + "rgbcontrol", ] [patch.crates-io] diff --git a/rust/accelerometer/src/main.rs b/rust/accelerometer/src/main.rs index 1bc8cf2..04b2a33 100644 --- a/rust/accelerometer/src/main.rs +++ b/rust/accelerometer/src/main.rs @@ -8,8 +8,9 @@ use k210_hal::pac; use k210_hal::prelude::*; 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::lcd::{LCD,self}; +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::board::msa300::Accelerometer; use k210_shared::soc::fpioa; 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_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 */ fn io_mux_init() { /* Init SPI IO map and function settings */ @@ -82,8 +77,6 @@ fn main() -> ! { lcd.set_direction(lcd::direction::YX_LRUD); lcd.clear(lcd_colors::PURPLE); - let mut image: ScreenImage = [0; DISP_WIDTH * DISP_HEIGHT / 2]; - writeln!(stdout, "MSA300 init").unwrap(); let i2c = p.I2C0.constrain(); 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 r = (1.5*mag) as i32; let rr = r * r; - let mut idx = 0; - for y in 0..DISP_HEIGHT as i32 { - for x2 in 0..(DISP_WIDTH/2) as i32 { - let x = x2 * 2; - image[idx] = (if sample_cirle(x + 0, y, cx, cy, r, rr) { 0xffff } else { 0 } << 16) | - if sample_cirle(x + 1, y, cx, cy, r, rr) { 0xffff } else { 0 }; - idx += 1; + render_image(&mut lcd, |x,y| { + if sample_cirle(x as i32, y as i32, cx, cy, r, rr) { + 0xffff + } else { + 0 } - } - - lcd.draw_picture(0, 0, DISP_WIDTH as u16, DISP_HEIGHT as u16, &image); + }); // usleep(10000); } } diff --git a/rust/game-of-life/.cargo/config b/rust/game-of-life/.cargo/config deleted file mode 100644 index ab84f4b..0000000 --- a/rust/game-of-life/.cargo/config +++ /dev/null @@ -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" diff --git a/rust/game-of-life/gdb_init b/rust/game-of-life/gdb_init deleted file mode 100644 index e7d61f4..0000000 --- a/rust/game-of-life/gdb_init +++ /dev/null @@ -1,3 +0,0 @@ -target remote :3333 -load -c diff --git a/rust/game-of-life/openocd.cfg b/rust/game-of-life/openocd.cfg deleted file mode 100644 index 912647f..0000000 --- a/rust/game-of-life/openocd.cfg +++ /dev/null @@ -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 diff --git a/rust/game-of-life/src/main.rs b/rust/game-of-life/src/main.rs index dc8adcc..bdbca97 100644 --- a/rust/game-of-life/src/main.rs +++ b/rust/game-of-life/src/main.rs @@ -8,7 +8,7 @@ use k210_hal::pac; use k210_hal::prelude::*; 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::lcd::{LCD,self}; +use k210_shared::board::lcd::{LCD,LCDHL,self}; use k210_shared::board::lcd_colors; use k210_shared::board::ns2009::TouchScreen; use k210_shared::soc::fpioa; diff --git a/rust/accelerometer/gdb_init b/rust/gdb_init similarity index 100% rename from rust/accelerometer/gdb_init rename to rust/gdb_init diff --git a/rust/k210-console/.cargo/config b/rust/k210-console/.cargo/config deleted file mode 100644 index ab84f4b..0000000 --- a/rust/k210-console/.cargo/config +++ /dev/null @@ -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" diff --git a/rust/k210-console/gdb_init b/rust/k210-console/gdb_init deleted file mode 100644 index e7d61f4..0000000 --- a/rust/k210-console/gdb_init +++ /dev/null @@ -1,3 +0,0 @@ -target remote :3333 -load -c diff --git a/rust/k210-console/openocd.cfg b/rust/k210-console/openocd.cfg deleted file mode 100644 index 912647f..0000000 --- a/rust/k210-console/openocd.cfg +++ /dev/null @@ -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 diff --git a/rust/k210-console/src/main.rs b/rust/k210-console/src/main.rs index bb5857f..0a1a8e1 100644 --- a/rust/k210-console/src/main.rs +++ b/rust/k210-console/src/main.rs @@ -14,7 +14,7 @@ use k210_hal::pac; use k210_hal::prelude::*; use k210_hal::stdout::Stdout; 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::soc::fpioa; use k210_shared::soc::sleep::usleep; diff --git a/rust/k210-shared/Cargo.toml b/rust/k210-shared/Cargo.toml index 8d75bc0..5b404f5 100644 --- a/rust/k210-shared/Cargo.toml +++ b/rust/k210-shared/Cargo.toml @@ -5,6 +5,7 @@ authors = ["W.J. van der Laan "] edition = "2018" [dependencies] -riscv-rt = "0.5.0" k210-hal = "0.1.0" +libm = "0.1" riscv = "0.5" +riscv-rt = "0.5.0" diff --git a/rust/k210-shared/src/board.rs b/rust/k210-shared/src/board.rs index 7eb3664..7fbc030 100644 --- a/rust/k210-shared/src/board.rs +++ b/rust/k210-shared/src/board.rs @@ -1,5 +1,6 @@ pub mod def; pub mod lcd; pub mod lcd_colors; +pub mod lcd_render; pub mod msa300; pub mod ns2009; diff --git a/rust/k210-shared/src/board/lcd.rs b/rust/k210-shared/src/board/lcd.rs index 9551105..11c6516 100644 --- a/rust/k210-shared/src/board/lcd.rs +++ b/rust/k210-shared/src/board/lcd.rs @@ -117,6 +117,25 @@ pub struct LCD { 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 LCD { pub fn new(spi: X) -> Self { Self { @@ -126,8 +145,6 @@ impl LCD { } } - /* Low-level functions */ - fn init_dcx(&self) { gpiohs::set_direction(DCX_GPIONUM, gpio::direction::OUTPUT); gpiohs::set_pin(DCX_GPIONUM, true); @@ -149,8 +166,11 @@ impl LCD { fn set_rst(&self, val: bool) { gpiohs::set_pin(RST_GPIONUM, val); } +} - pub fn hard_init(&self) { +/** Low-level functions */ +impl LCDLL for LCD { + fn hard_init(&self) { self.init_dcx(); self.init_rst(); self.set_rst(false); @@ -169,7 +189,7 @@ impl LCD { self.set_rst(true); } - pub fn write_command(&self, cmd: command) { + fn write_command(&self, cmd: command) { self.set_dcx_control(); self.spi.configure( ctrlr0::WORK_MODEW::MODE0, @@ -185,7 +205,7 @@ impl LCD { 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.spi.configure( ctrlr0::WORK_MODEW::MODE0, @@ -201,7 +221,7 @@ impl LCD { 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.spi.configure( ctrlr0::WORK_MODEW::MODE0, @@ -217,7 +237,7 @@ impl LCD { 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.spi.configure( ctrlr0::WORK_MODEW::MODE0, @@ -233,7 +253,7 @@ impl LCD { 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.spi.configure( ctrlr0::WORK_MODEW::MODE0, @@ -248,10 +268,11 @@ impl LCD { ); self.spi.fill_data(SPI_SLAVE_SELECT, data, length); } +} - /* High-level functions */ - - pub fn init(&mut self) { +/* High-level functions */ +impl LCDHL for LCD { + fn init(&mut self) { self.hard_init(); /*soft reset*/ self.write_command(command::SOFTWARE_RESET); @@ -268,7 +289,7 @@ impl LCD { 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 { self.width = LCD_Y_MAX; self.height = LCD_X_MAX; @@ -281,7 +302,7 @@ impl LCD { 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_byte(&[ (x1 >> 8) as u8, @@ -301,14 +322,14 @@ impl LCD { 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); 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); } - 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); assert!(data.len() == (width as usize) * (height as usize) / 2); self.write_word(data); diff --git a/rust/k210-shared/src/board/lcd_colors.rs b/rust/k210-shared/src/board/lcd_colors.rs index d5c592d..f0eedf8 100644 --- a/rust/k210-shared/src/board/lcd_colors.rs +++ b/rust/k210-shared/src/board/lcd_colors.rs @@ -1,3 +1,5 @@ +use libm::F32Ext; + /** Some convenient RGB565 colors */ pub const BLACK: u16 = 0x0000; pub const NAVY: u16 = 0x000F; @@ -21,5 +23,55 @@ pub const PINK: u16 = 0xF81F; /** Truncate 8 bit RGB to RBG565 */ 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 + } } diff --git a/rust/k210-shared/src/board/lcd_render.rs b/rust/k210-shared/src/board/lcd_render.rs new file mode 100644 index 0000000..b573a12 --- /dev/null +++ b/rust/k210-shared/src/board/lcd_render.rs @@ -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(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); +} diff --git a/rust/k210-shared/src/soc.rs b/rust/k210-shared/src/soc.rs index d52d13b..504496b 100644 --- a/rust/k210-shared/src/soc.rs +++ b/rust/k210-shared/src/soc.rs @@ -2,6 +2,7 @@ pub mod fpioa; pub mod gpio; pub mod gpiohs; pub mod i2c; +pub mod pwm; pub mod sleep; pub mod spi; pub mod sysctl; diff --git a/rust/k210-shared/src/soc/pwm.rs b/rust/k210-shared/src/soc/pwm.rs new file mode 100644 index 0000000..f284abb --- /dev/null +++ b/rust/k210-shared/src/soc/pwm.rs @@ -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 +} + + diff --git a/rust/mandelbrot/.cargo/config b/rust/mandelbrot/.cargo/config deleted file mode 100644 index ab84f4b..0000000 --- a/rust/mandelbrot/.cargo/config +++ /dev/null @@ -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" diff --git a/rust/mandelbrot/gdb_init b/rust/mandelbrot/gdb_init deleted file mode 100644 index e7d61f4..0000000 --- a/rust/mandelbrot/gdb_init +++ /dev/null @@ -1,3 +0,0 @@ -target remote :3333 -load -c diff --git a/rust/mandelbrot/openocd.cfg b/rust/mandelbrot/openocd.cfg deleted file mode 100644 index 912647f..0000000 --- a/rust/mandelbrot/openocd.cfg +++ /dev/null @@ -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 diff --git a/rust/mandelbrot/src/main.rs b/rust/mandelbrot/src/main.rs index 239b51d..3060d31 100644 --- a/rust/mandelbrot/src/main.rs +++ b/rust/mandelbrot/src/main.rs @@ -10,8 +10,9 @@ 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,self}; +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; @@ -20,8 +21,6 @@ use riscv_rt::entry; use crate::palette::PALETTE; -pub type ScreenImage = [u32; DISP_WIDTH * DISP_HEIGHT / 2]; - /** Connect pins to internal functions */ fn io_mux_init() { /* Init SPI IO map and function settings */ @@ -52,16 +51,6 @@ fn mandelbrot(cx: f32, cy: f32, iterations: u32) -> u32 { 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] fn main() -> ! { let p = pac::Peripherals::take().unwrap(); @@ -86,22 +75,19 @@ fn main() -> ! { lcd.set_direction(lcd::direction::YX_RLDU); lcd.clear(lcd_colors::PURPLE); - let mut image: ScreenImage = [0; DISP_WIDTH * DISP_HEIGHT / 2]; - writeln!(stdout, "First frame").unwrap(); - let mut frame = 0; let mut zoom = 5.0f32; + let ofsx = 0.02997f32; + let ofsy = 0.80386f32; loop { - let mut ofs = 0; - for y in 0..DISP_HEIGHT as u16 { - for x in 0..(DISP_WIDTH/2) as u16 { - image[ofs] = ((compute(x*2+0, y, zoom) as u32)<<16) | (compute(x*2+1, y, zoom) as u32); - ofs += 1; - } - } - lcd.draw_picture(0, 0, DISP_WIDTH as u16, DISP_HEIGHT as u16, &image); + render_image(&mut lcd, |x,y| { + 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] + }); - frame += 1; zoom *= 0.98f32; } } diff --git a/rust/accelerometer/openocd.cfg b/rust/openocd.cfg similarity index 100% rename from rust/accelerometer/openocd.cfg rename to rust/openocd.cfg diff --git a/rust/rgbcontrol/.gitignore b/rust/rgbcontrol/.gitignore new file mode 100644 index 0000000..f0e3bca --- /dev/null +++ b/rust/rgbcontrol/.gitignore @@ -0,0 +1,2 @@ +/target +**/*.rs.bk \ No newline at end of file diff --git a/rust/rgbcontrol/Cargo.toml b/rust/rgbcontrol/Cargo.toml new file mode 100644 index 0000000..4ba0915 --- /dev/null +++ b/rust/rgbcontrol/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "rgbcontrol" +version = "0.1.0" +authors = ["W.J. van der Laan "] +edition = "2018" + +[dependencies] +k210-hal = "0.1.0" +k210-shared = { path = "../k210-shared" } +riscv = "0.5" +riscv-rt = "0.5.0" diff --git a/rust/rgbcontrol/README.md b/rust/rgbcontrol/README.md new file mode 100644 index 0000000..c5d4cb0 --- /dev/null +++ b/rust/rgbcontrol/README.md @@ -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. + diff --git a/rust/rgbcontrol/src/main.rs b/rust/rgbcontrol/src/main.rs new file mode 100644 index 0000000..1c30280 --- /dev/null +++ b/rust/rgbcontrol/src/main.rs @@ -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); + } +} diff --git a/rust/uart-passthrough/.cargo/config b/rust/uart-passthrough/.cargo/config deleted file mode 100644 index 8bcf92f..0000000 --- a/rust/uart-passthrough/.cargo/config +++ /dev/null @@ -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"