mirror of
https://github.com/laanwj/k210-sdk-stuff.git
synced 2024-11-22 01:16:20 +04:00
Use DMAC for display everywhere
This mimics the way the Kendryte SDK does things—it is better because the MMIO based SPI tends to sometimes lose synchonization when the CPU doesn't keep up providing data fast enough.
This commit is contained in:
parent
2906c8c895
commit
e86d5c5f75
@ -12,6 +12,7 @@ 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::dmac::{DMACExt, dma_channel};
|
||||
use k210_shared::soc::fpioa;
|
||||
use k210_shared::soc::i2c::{I2C,I2CExt};
|
||||
use k210_shared::soc::sleep::usleep;
|
||||
@ -71,8 +72,9 @@ fn main() -> ! {
|
||||
io_mux_init();
|
||||
io_set_power();
|
||||
|
||||
let dmac = p.DMAC.configure();
|
||||
let spi = p.SPI0.constrain();
|
||||
let mut lcd = LCD::new(spi);
|
||||
let mut lcd = LCD::new(spi, &dmac, dma_channel::CHANNEL0);
|
||||
lcd.init();
|
||||
lcd.set_direction(lcd::direction::YX_LRUD);
|
||||
lcd.clear(lcd_colors::PURPLE);
|
||||
|
@ -10,6 +10,7 @@ use k210_hal::stdout::Stdout;
|
||||
use k210_shared::board::def::{io,DISP_WIDTH,DISP_HEIGHT};
|
||||
use k210_shared::board::lcd::{LCD,LCDHL,self};
|
||||
use k210_shared::board::lcd_colors;
|
||||
use k210_shared::soc::dmac::{DMACExt, dma_channel};
|
||||
use k210_shared::soc::fpioa;
|
||||
use k210_shared::soc::sleep::usleep;
|
||||
use k210_shared::soc::spi::SPIExt;
|
||||
@ -71,8 +72,9 @@ fn main() -> ! {
|
||||
|
||||
io_init();
|
||||
|
||||
let dmac = p.DMAC.configure();
|
||||
let spi = p.SPI0.constrain();
|
||||
let mut lcd = LCD::new(spi);
|
||||
let mut lcd = LCD::new(spi, &dmac, dma_channel::CHANNEL0);
|
||||
lcd.init();
|
||||
lcd.set_direction(lcd::direction::YX_RLDU);
|
||||
lcd.clear(lcd_colors::PURPLE);
|
||||
|
@ -11,6 +11,7 @@ use k210_shared::board::def::{io,DISP_WIDTH,DISP_HEIGHT,NS2009_SLV_ADDR,NS2009_C
|
||||
use k210_shared::board::lcd::{LCD,LCDHL,self};
|
||||
use k210_shared::board::lcd_colors;
|
||||
use k210_shared::board::ns2009::TouchScreen;
|
||||
use k210_shared::soc::dmac::{DMACExt, dma_channel};
|
||||
use k210_shared::soc::fpioa;
|
||||
use k210_shared::soc::i2c::{I2C,I2CExt};
|
||||
use k210_shared::soc::sleep::usleep;
|
||||
@ -154,8 +155,9 @@ fn main() -> ! {
|
||||
io_mux_init();
|
||||
io_set_power();
|
||||
|
||||
let dmac = p.DMAC.configure();
|
||||
let spi = p.SPI0.constrain();
|
||||
let mut lcd = LCD::new(spi);
|
||||
let mut lcd = LCD::new(spi, &dmac, dma_channel::CHANNEL0);
|
||||
lcd.init();
|
||||
lcd.set_direction(lcd::direction::YX_LRUD);
|
||||
lcd.clear(lcd_colors::PURPLE);
|
||||
|
@ -12,6 +12,7 @@ use k210_hal::stdout::Stdout;
|
||||
use k210_shared::board::def::{io,DISP_WIDTH,DISP_HEIGHT};
|
||||
use k210_shared::board::lcd::{LCD,LCDHL,self};
|
||||
use k210_shared::board::lcd_colors;
|
||||
use k210_shared::soc::dmac::{DMACExt, dma_channel};
|
||||
use k210_shared::soc::fpioa;
|
||||
use k210_shared::soc::sleep::usleep;
|
||||
use k210_shared::soc::spi::SPIExt;
|
||||
@ -81,8 +82,9 @@ fn main() -> ! {
|
||||
|
||||
io_init();
|
||||
|
||||
let dmac = p.DMAC.configure();
|
||||
let spi = p.SPI0.constrain();
|
||||
let mut lcd = LCD::new(spi);
|
||||
let mut lcd = LCD::new(spi, &dmac, dma_channel::CHANNEL0);
|
||||
lcd.init();
|
||||
lcd.set_direction(lcd::direction::YX_LRUD);
|
||||
lcd.clear(lcd_colors::PURPLE);
|
||||
|
@ -12,6 +12,7 @@ use k210_hal::stdout::Stdout;
|
||||
use k210_shared::board::def::io;
|
||||
use k210_shared::board::lcd::{LCD,LCDHL,self};
|
||||
use k210_shared::board::lcd_colors;
|
||||
use k210_shared::soc::dmac::{DMACExt, dma_channel};
|
||||
use k210_shared::soc::fpioa;
|
||||
use k210_shared::soc::sleep::usleep;
|
||||
use k210_shared::soc::spi::SPIExt;
|
||||
@ -114,8 +115,9 @@ fn main() -> ! {
|
||||
.unwrap();
|
||||
|
||||
/* LCD init */
|
||||
let dmac = p.DMAC.configure();
|
||||
let spi = p.SPI0.constrain();
|
||||
let mut lcd = LCD::new(spi);
|
||||
let mut lcd = LCD::new(spi, &dmac, dma_channel::CHANNEL0);
|
||||
lcd.init();
|
||||
lcd.set_direction(lcd::direction::YX_LRUD);
|
||||
lcd.clear(lcd_colors::PURPLE);
|
||||
|
@ -108,8 +108,10 @@ pub enum direction {
|
||||
pub const DIR_XY_MASK: u8 = 0x20;
|
||||
pub const DIR_MASK: u8 = 0xE0;
|
||||
|
||||
pub struct LCD<SPI> {
|
||||
pub struct LCD<'a, SPI> {
|
||||
spi: SPI,
|
||||
dmac: &'a DMAC,
|
||||
channel: dma_channel,
|
||||
pub width: u16,
|
||||
pub height: u16,
|
||||
}
|
||||
@ -118,12 +120,13 @@ pub struct LCD<SPI> {
|
||||
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]);
|
||||
/** Write bytes. These are provided as 32-bit units (ignoring the upper 24 bits) for efficient DMA */
|
||||
fn write_byte(&self, data_buf: &[u32]);
|
||||
/** Write halfs. These are provided as 32-bit units (ignoring the upper 16 bits) for efficient DMA */
|
||||
fn write_half(&self, data_buf: &[u32]);
|
||||
/** Write words. */
|
||||
fn write_word(&self, data_buf: &[u32]);
|
||||
fn write_word_dma(&self, dmac: &DMAC, channel: dma_channel, data_buf: &[u32]);
|
||||
fn fill_data(&self, data: u32, length: usize);
|
||||
fn fill_data_dma(&self, dmac: &DMAC, channel: dma_channel, data: u32, length: usize);
|
||||
}
|
||||
|
||||
/** High-level interface */
|
||||
@ -131,15 +134,15 @@ pub trait LCDHL {
|
||||
fn init(&mut self);
|
||||
fn set_direction(&mut self, dir: direction);
|
||||
fn clear(&self, color: u16);
|
||||
fn clear_dma(&self, dmac: &DMAC, channel_num: dma_channel, color: u16);
|
||||
fn draw_picture(&self, x1: u16, y1: u16, width: u16, height: u16, data: &[u32]);
|
||||
fn draw_picture_dma(&self, dmac: &DMAC, channel_num: dma_channel, x1: u16, y1: u16, width: u16, height: u16, data: &[u32]);
|
||||
}
|
||||
|
||||
impl<X: SPI> LCD<X> {
|
||||
pub fn new(spi: X) -> Self {
|
||||
impl<'a, X: SPI> LCD<'a, X> {
|
||||
pub fn new(spi: X, dmac: &'a DMAC, channel: dma_channel) -> Self {
|
||||
Self {
|
||||
spi,
|
||||
dmac,
|
||||
channel,
|
||||
width: 0,
|
||||
height: 0,
|
||||
}
|
||||
@ -170,18 +173,18 @@ impl<X: SPI> LCD<X> {
|
||||
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,
|
||||
(x1 & 0xff) as u8,
|
||||
(x2 >> 8) as u8,
|
||||
(x2 & 0xff) as u8,
|
||||
(x1 >> 8) as u32,
|
||||
(x1 & 0xff) as u32,
|
||||
(x2 >> 8) as u32,
|
||||
(x2 & 0xff) as u32,
|
||||
]);
|
||||
|
||||
self.write_command(command::VERTICAL_ADDRESS_SET);
|
||||
self.write_byte(&[
|
||||
(y1 >> 8) as u8,
|
||||
(y1 & 0xff) as u8,
|
||||
(y2 >> 8) as u8,
|
||||
(y2 & 0xff) as u8,
|
||||
(y1 >> 8) as u32,
|
||||
(y1 & 0xff) as u32,
|
||||
(y2 >> 8) as u32,
|
||||
(y2 & 0xff) as u32,
|
||||
]);
|
||||
|
||||
self.write_command(command::MEMORY_WRITE);
|
||||
@ -189,7 +192,7 @@ impl<X: SPI> LCD<X> {
|
||||
}
|
||||
|
||||
/** Low-level functions */
|
||||
impl<X: SPI> LCDLL for LCD<X> {
|
||||
impl<X: SPI> LCDLL for LCD<'_, X> {
|
||||
fn hard_init(&self) {
|
||||
self.init_dcx();
|
||||
self.init_rst();
|
||||
@ -222,10 +225,10 @@ impl<X: SPI> LCDLL for LCD<X> {
|
||||
aitm::AS_FRAME_FORMAT,
|
||||
tmod::TRANS,
|
||||
);
|
||||
self.spi.send_data(SPI_SLAVE_SELECT, &[cmd as u8]);
|
||||
self.spi.send_data_dma(self.dmac, self.channel, SPI_SLAVE_SELECT, &[cmd as u32]);
|
||||
}
|
||||
|
||||
fn write_byte(&self, data_buf: &[u8]) {
|
||||
fn write_byte(&self, data_buf: &[u32]) {
|
||||
self.set_dcx_data();
|
||||
self.spi.configure(
|
||||
work_mode::MODE0,
|
||||
@ -238,10 +241,10 @@ impl<X: SPI> LCDLL for LCD<X> {
|
||||
aitm::AS_FRAME_FORMAT,
|
||||
tmod::TRANS,
|
||||
);
|
||||
self.spi.send_data(SPI_SLAVE_SELECT, data_buf);
|
||||
self.spi.send_data_dma(self.dmac, self.channel, SPI_SLAVE_SELECT, data_buf);
|
||||
}
|
||||
|
||||
fn write_half(&self, data_buf: &[u16]) {
|
||||
fn write_half(&self, data_buf: &[u32]) {
|
||||
self.set_dcx_data();
|
||||
self.spi.configure(
|
||||
work_mode::MODE0,
|
||||
@ -254,7 +257,7 @@ impl<X: SPI> LCDLL for LCD<X> {
|
||||
aitm::AS_FRAME_FORMAT,
|
||||
tmod::TRANS,
|
||||
);
|
||||
self.spi.send_data(SPI_SLAVE_SELECT, data_buf);
|
||||
self.spi.send_data_dma(self.dmac, self.channel, SPI_SLAVE_SELECT, data_buf);
|
||||
}
|
||||
|
||||
fn write_word(&self, data_buf: &[u32]) {
|
||||
@ -270,23 +273,7 @@ impl<X: SPI> LCDLL for LCD<X> {
|
||||
aitm::AS_FRAME_FORMAT,
|
||||
tmod::TRANS,
|
||||
);
|
||||
self.spi.send_data(SPI_SLAVE_SELECT, data_buf);
|
||||
}
|
||||
|
||||
fn write_word_dma(&self, dmac: &DMAC, channel: dma_channel, data_buf: &[u32]) {
|
||||
self.set_dcx_data();
|
||||
self.spi.configure(
|
||||
work_mode::MODE0,
|
||||
frame_format::OCTAL,
|
||||
32,
|
||||
0,
|
||||
0, /*instruction length*/
|
||||
32, /*address length*/
|
||||
0, /*wait cycles*/
|
||||
aitm::AS_FRAME_FORMAT,
|
||||
tmod::TRANS,
|
||||
);
|
||||
self.spi.send_data_dma(dmac, channel, SPI_SLAVE_SELECT, data_buf);
|
||||
self.spi.send_data_dma(self.dmac, self.channel, SPI_SLAVE_SELECT, data_buf);
|
||||
}
|
||||
|
||||
fn fill_data(&self, data: u32, length: usize) {
|
||||
@ -302,28 +289,12 @@ impl<X: SPI> LCDLL for LCD<X> {
|
||||
aitm::AS_FRAME_FORMAT,
|
||||
tmod::TRANS,
|
||||
);
|
||||
self.spi.fill_data(SPI_SLAVE_SELECT, data, length);
|
||||
}
|
||||
|
||||
fn fill_data_dma(&self, dmac: &DMAC, channel: dma_channel, data: u32, length: usize) {
|
||||
self.set_dcx_data();
|
||||
self.spi.configure(
|
||||
work_mode::MODE0,
|
||||
frame_format::OCTAL,
|
||||
32,
|
||||
0,
|
||||
0, /*instruction length*/
|
||||
32, /*address length*/
|
||||
0, /*wait cycles*/
|
||||
aitm::AS_FRAME_FORMAT,
|
||||
tmod::TRANS,
|
||||
);
|
||||
self.spi.fill_data_dma(dmac, channel, SPI_SLAVE_SELECT, data, length);
|
||||
self.spi.fill_data_dma(self.dmac, self.channel, SPI_SLAVE_SELECT, data, length);
|
||||
}
|
||||
}
|
||||
|
||||
/* High-level functions */
|
||||
impl<X: SPI> LCDHL for LCD<X> {
|
||||
impl<X: SPI> LCDHL for LCD<'_, X> {
|
||||
fn init(&mut self) {
|
||||
self.hard_init();
|
||||
/*soft reset*/
|
||||
@ -351,7 +322,7 @@ impl<X: SPI> LCDHL for LCD<X> {
|
||||
}
|
||||
|
||||
self.write_command(command::MEMORY_ACCESS_CTL);
|
||||
self.write_byte(&[dir as u8]);
|
||||
self.write_byte(&[dir as u32]);
|
||||
}
|
||||
|
||||
fn clear(&self, color: u16) {
|
||||
@ -361,22 +332,9 @@ impl<X: SPI> LCDHL for LCD<X> {
|
||||
self.fill_data(data, (LCD_X_MAX as usize) * (LCD_Y_MAX as usize) / 2);
|
||||
}
|
||||
|
||||
fn clear_dma(&self, dmac: &DMAC, channel_num: dma_channel, 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_dma(dmac, channel_num, data, (LCD_X_MAX as usize) * (LCD_Y_MAX as usize) / 2);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
fn draw_picture_dma(&self, dmac: &DMAC, channel_num: dma_channel, 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_dma(dmac, channel_num, data);
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,6 @@
|
||||
// TODO: switch this over to embedded-graphics probably
|
||||
use crate::board::def::{DISP_HEIGHT, DISP_WIDTH};
|
||||
use crate::board::lcd::LCDHL;
|
||||
use crate::soc::dmac::{DMAC, dma_channel};
|
||||
|
||||
/** Array for representing an image of the entire screen.
|
||||
* This is an array of DISP_WIDTH / 2 × DISP_HEIGHT, each two horizontally consecutive
|
||||
@ -33,21 +32,3 @@ where
|
||||
// isn't that.
|
||||
lcd.draw_picture(0, 0, DISP_WIDTH as u16, DISP_HEIGHT as u16, &idata);
|
||||
}
|
||||
|
||||
pub fn render_image_dma<L, I>(dmac: &DMAC, channel_num: dma_channel, 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);
|
||||
});
|
||||
|
||||
lcd.draw_picture_dma(dmac, channel_num, 0, 0, DISP_WIDTH as u16, DISP_HEIGHT as u16, &idata);
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ use k210_hal::stdout::Stdout;
|
||||
use k210_shared::board::def::{io,DISP_WIDTH,DISP_HEIGHT};
|
||||
use k210_shared::board::lcd::{LCD,LCDHL,self};
|
||||
use k210_shared::board::lcd_colors;
|
||||
use k210_shared::board::lcd_render::render_image_dma;
|
||||
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;
|
||||
@ -77,17 +77,17 @@ fn main() -> ! {
|
||||
dmac.read_id(), dmac.read_version(), dmac.read_channel_id(chan)).unwrap();
|
||||
|
||||
let spi = p.SPI0.constrain();
|
||||
let mut lcd = LCD::new(spi);
|
||||
let mut lcd = LCD::new(spi, &dmac, chan);
|
||||
lcd.init();
|
||||
lcd.set_direction(lcd::direction::YX_RLDU);
|
||||
lcd.clear_dma(&dmac, chan, lcd_colors::PURPLE);
|
||||
lcd.clear(lcd_colors::PURPLE);
|
||||
|
||||
writeln!(stdout, "First frame").unwrap();
|
||||
let mut zoom = 5.0f32;
|
||||
let ofsx = 0.02997f32;
|
||||
let ofsy = 0.80386f32;
|
||||
loop {
|
||||
render_image_dma(&dmac, chan, &mut lcd, |x,y| {
|
||||
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);
|
||||
|
@ -15,6 +15,7 @@ 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::dmac::{DMACExt, dma_channel};
|
||||
use k210_shared::soc::fpioa;
|
||||
use k210_shared::soc::i2c::{I2CExt, I2C};
|
||||
use k210_shared::soc::pwm::{TimerExt, PWM, Channel};
|
||||
@ -89,8 +90,9 @@ fn main() -> ! {
|
||||
};
|
||||
|
||||
writeln!(stdout, "LCD init").unwrap();
|
||||
let dmac = p.DMAC.configure();
|
||||
let spi = p.SPI0.constrain();
|
||||
let mut lcd = LCD::new(spi);
|
||||
let mut lcd = LCD::new(spi, &dmac, dma_channel::CHANNEL0);
|
||||
lcd.init();
|
||||
lcd.set_direction(lcd::direction::YX_LRUD);
|
||||
render_image(&mut lcd, |x, y| {
|
||||
|
@ -14,6 +14,7 @@ use k210_hal::prelude::*;
|
||||
use k210_hal::stdout::Stdout;
|
||||
use k210_shared::board::def::io;
|
||||
use k210_shared::board::lcd::{self, LCD, LCDHL};
|
||||
use k210_shared::soc::dmac::{DMACExt, dma_channel};
|
||||
use k210_shared::soc::fpioa;
|
||||
use k210_shared::soc::gpio;
|
||||
use k210_shared::soc::gpiohs;
|
||||
@ -107,8 +108,9 @@ fn main() -> ! {
|
||||
let mut sh = SerialNetworkHandler::new(&mut wa, config::APNAME.as_bytes(), config::APPASS.as_bytes());
|
||||
|
||||
// LCD ini
|
||||
let dmac = p.DMAC.configure();
|
||||
let spi = p.SPI0.constrain();
|
||||
let mut lcd = LCD::new(spi);
|
||||
let mut lcd = LCD::new(spi, &dmac, dma_channel::CHANNEL0);
|
||||
lcd.init();
|
||||
lcd.set_direction(lcd::direction::YX_LRUD);
|
||||
let mut console: Console = Console::new();
|
||||
|
@ -14,6 +14,7 @@ use k210_hal::prelude::*;
|
||||
use k210_hal::stdout::Stdout;
|
||||
use k210_shared::board::def::io;
|
||||
use k210_shared::board::lcd::{self, LCD, LCDHL};
|
||||
use k210_shared::soc::dmac::{DMACExt, dma_channel};
|
||||
use k210_shared::soc::fpioa;
|
||||
use k210_shared::soc::gpio;
|
||||
use k210_shared::soc::gpiohs;
|
||||
@ -107,8 +108,9 @@ fn main() -> ! {
|
||||
let mut sh = SerialNetworkHandler::new(&mut wa, config::APNAME.as_bytes(), config::APPASS.as_bytes());
|
||||
|
||||
// LCD ini
|
||||
let dmac = p.DMAC.configure();
|
||||
let spi = p.SPI0.constrain();
|
||||
let mut lcd = LCD::new(spi);
|
||||
let mut lcd = LCD::new(spi, &dmac, dma_channel::CHANNEL0);
|
||||
lcd.init();
|
||||
lcd.set_direction(lcd::direction::YX_LRUD);
|
||||
let mut console: Console = Console::new();
|
||||
|
Loading…
Reference in New Issue
Block a user