mirror of
https://github.com/laanwj/k210-sdk-stuff.git
synced 2024-11-24 18:36:19 +04:00
rust: Add rgbcontrol
This commit is contained in:
parent
8ca44a288f
commit
d14c640356
@ -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
|
||||||
===========
|
===========
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ members = [
|
|||||||
"game-of-life",
|
"game-of-life",
|
||||||
"accelerometer",
|
"accelerometer",
|
||||||
"uart-passthrough",
|
"uart-passthrough",
|
||||||
|
"rgbcontrol",
|
||||||
]
|
]
|
||||||
|
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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"
|
|
@ -1,3 +0,0 @@
|
|||||||
target remote :3333
|
|
||||||
load
|
|
||||||
c
|
|
@ -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
|
|
@ -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;
|
||||||
|
@ -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"
|
|
@ -1,3 +0,0 @@
|
|||||||
target remote :3333
|
|
||||||
load
|
|
||||||
c
|
|
@ -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
|
|
@ -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;
|
||||||
|
@ -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"
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
33
rust/k210-shared/src/board/lcd_render.rs
Normal file
33
rust/k210-shared/src/board/lcd_render.rs
Normal 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);
|
||||||
|
}
|
@ -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;
|
||||||
|
56
rust/k210-shared/src/soc/pwm.rs
Normal file
56
rust/k210-shared/src/soc/pwm.rs
Normal 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
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -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"
|
|
@ -1,3 +0,0 @@
|
|||||||
target remote :3333
|
|
||||||
load
|
|
||||||
c
|
|
@ -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
|
|
@ -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
2
rust/rgbcontrol/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
/target
|
||||||
|
**/*.rs.bk
|
11
rust/rgbcontrol/Cargo.toml
Normal file
11
rust/rgbcontrol/Cargo.toml
Normal 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
10
rust/rgbcontrol/README.md
Normal 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
128
rust/rgbcontrol/src/main.rs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
@ -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"
|
|
Loading…
Reference in New Issue
Block a user