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)
|
||||
|
||||
rust/rgbcontrol
|
||||
----------------
|
||||
|
||||
Control the color of the RGB LED from the touch screen.
|
||||
|
||||
[README](rust/rgbcontrol/README.md)
|
||||
|
||||
ROM re'ing
|
||||
===========
|
||||
|
||||
|
@ -5,6 +5,7 @@ members = [
|
||||
"game-of-life",
|
||||
"accelerometer",
|
||||
"uart-passthrough",
|
||||
"rgbcontrol",
|
||||
]
|
||||
|
||||
[patch.crates-io]
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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::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;
|
||||
|
@ -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::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;
|
||||
|
@ -5,6 +5,7 @@ authors = ["W.J. van der Laan <laanwj@protonmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
riscv-rt = "0.5.0"
|
||||
k210-hal = "0.1.0"
|
||||
libm = "0.1"
|
||||
riscv = "0.5"
|
||||
riscv-rt = "0.5.0"
|
||||
|
@ -1,5 +1,6 @@
|
||||
pub mod def;
|
||||
pub mod lcd;
|
||||
pub mod lcd_colors;
|
||||
pub mod lcd_render;
|
||||
pub mod msa300;
|
||||
pub mod ns2009;
|
||||
|
@ -117,6 +117,25 @@ pub struct LCD<SPI> {
|
||||
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> {
|
||||
pub fn new(spi: X) -> Self {
|
||||
Self {
|
||||
@ -126,8 +145,6 @@ impl<X: SPI> LCD<X> {
|
||||
}
|
||||
}
|
||||
|
||||
/* 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<X: SPI> LCD<X> {
|
||||
fn set_rst(&self, val: bool) {
|
||||
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_rst();
|
||||
self.set_rst(false);
|
||||
@ -169,7 +189,7 @@ impl<X: SPI> LCD<X> {
|
||||
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<X: SPI> LCD<X> {
|
||||
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<X: SPI> LCD<X> {
|
||||
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<X: SPI> LCD<X> {
|
||||
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<X: SPI> LCD<X> {
|
||||
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<X: SPI> LCD<X> {
|
||||
);
|
||||
self.spi.fill_data(SPI_SLAVE_SELECT, data, length);
|
||||
}
|
||||
}
|
||||
|
||||
/* High-level functions */
|
||||
|
||||
pub fn init(&mut self) {
|
||||
/* High-level functions */
|
||||
impl<X: SPI> LCDHL for LCD<X> {
|
||||
fn init(&mut self) {
|
||||
self.hard_init();
|
||||
/*soft reset*/
|
||||
self.write_command(command::SOFTWARE_RESET);
|
||||
@ -268,7 +289,7 @@ impl<X: SPI> LCD<X> {
|
||||
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<X: SPI> LCD<X> {
|
||||
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<X: SPI> LCD<X> {
|
||||
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);
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
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 gpiohs;
|
||||
pub mod i2c;
|
||||
pub mod pwm;
|
||||
pub mod sleep;
|
||||
pub mod spi;
|
||||
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::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;
|
||||
}
|
||||
}
|
||||
|
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