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:
Wladimir J. van der Laan 2019-08-07 10:17:11 +00:00
parent 2906c8c895
commit e86d5c5f75
11 changed files with 58 additions and 103 deletions

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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| {

View File

@ -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();

View File

@ -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();