From e86d5c5f75dfe544dd56c223945ad24b24c1b559 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Wed, 7 Aug 2019 10:17:11 +0000 Subject: [PATCH] Use DMAC for display everywhere MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- rust/accelerometer/src/main.rs | 4 +- rust/dvp-ov/src/main.rs | 4 +- rust/game-of-life/src/main.rs | 4 +- rust/glyph-mapping/src/main.rs | 4 +- rust/k210-console/src/main.rs | 4 +- rust/k210-shared/src/board/lcd.rs | 102 +++++++---------------- rust/k210-shared/src/board/lcd_render.rs | 19 ----- rust/mandelbrot/src/main.rs | 8 +- rust/rgbcontrol/src/main.rs | 4 +- rust/term-server/src/main.rs | 4 +- rust/weather/src/main.rs | 4 +- 11 files changed, 58 insertions(+), 103 deletions(-) diff --git a/rust/accelerometer/src/main.rs b/rust/accelerometer/src/main.rs index 6c1a692..8de24d0 100644 --- a/rust/accelerometer/src/main.rs +++ b/rust/accelerometer/src/main.rs @@ -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); diff --git a/rust/dvp-ov/src/main.rs b/rust/dvp-ov/src/main.rs index 14e678b..23cc89c 100644 --- a/rust/dvp-ov/src/main.rs +++ b/rust/dvp-ov/src/main.rs @@ -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); diff --git a/rust/game-of-life/src/main.rs b/rust/game-of-life/src/main.rs index 5e3c031..1cc4be6 100644 --- a/rust/game-of-life/src/main.rs +++ b/rust/game-of-life/src/main.rs @@ -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); diff --git a/rust/glyph-mapping/src/main.rs b/rust/glyph-mapping/src/main.rs index da4b5bd..c8cec13 100644 --- a/rust/glyph-mapping/src/main.rs +++ b/rust/glyph-mapping/src/main.rs @@ -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); diff --git a/rust/k210-console/src/main.rs b/rust/k210-console/src/main.rs index 43109c4..07960ec 100644 --- a/rust/k210-console/src/main.rs +++ b/rust/k210-console/src/main.rs @@ -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); diff --git a/rust/k210-shared/src/board/lcd.rs b/rust/k210-shared/src/board/lcd.rs index ad7c710..7e10813 100644 --- a/rust/k210-shared/src/board/lcd.rs +++ b/rust/k210-shared/src/board/lcd.rs @@ -108,8 +108,10 @@ pub enum direction { pub const DIR_XY_MASK: u8 = 0x20; pub const DIR_MASK: u8 = 0xE0; -pub struct LCD { +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 { 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 LCD { - 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 LCD { 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 LCD { } /** Low-level functions */ -impl LCDLL for LCD { +impl LCDLL for LCD<'_, X> { fn hard_init(&self) { self.init_dcx(); self.init_rst(); @@ -222,10 +225,10 @@ impl LCDLL for LCD { 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 LCDLL for LCD { 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 LCDLL for LCD { 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 LCDLL for LCD { 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 LCDLL for LCD { 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 LCDHL for LCD { +impl LCDHL for LCD<'_, X> { fn init(&mut self) { self.hard_init(); /*soft reset*/ @@ -351,7 +322,7 @@ impl LCDHL for LCD { } 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 LCDHL for LCD { 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); - } } diff --git a/rust/k210-shared/src/board/lcd_render.rs b/rust/k210-shared/src/board/lcd_render.rs index a1636d9..9fbabc0 100644 --- a/rust/k210-shared/src/board/lcd_render.rs +++ b/rust/k210-shared/src/board/lcd_render.rs @@ -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(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); -} diff --git a/rust/mandelbrot/src/main.rs b/rust/mandelbrot/src/main.rs index 1fc91bf..62541f7 100644 --- a/rust/mandelbrot/src/main.rs +++ b/rust/mandelbrot/src/main.rs @@ -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); diff --git a/rust/rgbcontrol/src/main.rs b/rust/rgbcontrol/src/main.rs index 8ff50bb..abb554a 100644 --- a/rust/rgbcontrol/src/main.rs +++ b/rust/rgbcontrol/src/main.rs @@ -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| { diff --git a/rust/term-server/src/main.rs b/rust/term-server/src/main.rs index ab3598b..a1e7838 100644 --- a/rust/term-server/src/main.rs +++ b/rust/term-server/src/main.rs @@ -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(); diff --git a/rust/weather/src/main.rs b/rust/weather/src/main.rs index 6f52a37..ec9c965 100644 --- a/rust/weather/src/main.rs +++ b/rust/weather/src/main.rs @@ -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();