From e7b021dfbf7b363c6fdf4da022cab1950e1df1d7 Mon Sep 17 00:00:00 2001 From: Simon Thornington Date: Sun, 13 Dec 2020 14:28:36 -0800 Subject: [PATCH] initial draft of hw accel primitives --- src/command.rs | 68 +++++++++++++++++++++++++++++++++++++++----------- src/display.rs | 23 ++++++++++++++++- 2 files changed, 75 insertions(+), 16 deletions(-) diff --git a/src/command.rs b/src/command.rs index 7832d33..50443f8 100644 --- a/src/command.rs +++ b/src/command.rs @@ -47,6 +47,25 @@ pub enum Command { VcomhDeselect(VcomhLevel), /// NOOP Noop, + /// Draw a line (col start, col end, row start, row end, 16-bit color) + DrawLine(u8, u8, u8, u8, u16), + /// Draw a rectangle (col start, col end, row start, row end, line color, fill color) + /// Note: fill color will have no use if EnableFill was sent with false + DrawRect(u8, u8, u8, u8, u16, u16), + /// Enable filling of drawn rectangles + EnableFill(bool), +} + +/// This is a raw converter from Rgb565 u16 to the bytes that +/// ssd1331 expects for rgb in accelerated graphics commands. +fn raw16_to_ssd1331_accel(raw: u16) -> (u8, u8, u8) { + const RED_MASK: u16 = 0b11111_000000_00000; + const GREEN_MASK: u16 = 0b00000_111111_00000; + const BLUE_MASK: u16 = 0b00000_000000_11111; + let a = ((raw & BLUE_MASK) << 1) as u8; + let b = ((raw & GREEN_MASK) >> 5) as u8; + let c = ((raw & RED_MASK) >> 10) as u8; + (a, b, c) } impl Command { @@ -60,16 +79,16 @@ impl Command { SPI: hal::blocking::spi::Write, DC: OutputPin, { - // Transform command into a fixed size array of 7 u8 and the real length for sending + // Transform command into a fixed size array of 11 u8 and the real length for sending let (data, len) = match self { - Command::Contrast(a, b, c) => ([0x81, a, 0x82, b, 0x83, c, 0], 6), + Command::Contrast(a, b, c) => ([0x81, a, 0x82, b, 0x83, c, 0, 0, 0, 0, 0], 6), // TODO: Collapse AllOn and Invert commands into new DisplayMode cmd with enum - Command::AllOn(on) => ([if on { 0xA5 } else { 0xA6 }, 0, 0, 0, 0, 0, 0], 1), - Command::Invert(inv) => ([if inv { 0xA7 } else { 0xA4 }, 0, 0, 0, 0, 0, 0], 1), - Command::DisplayOn(on) => ([0xAE | (on as u8), 0, 0, 0, 0, 0, 0], 1), - Command::ColumnAddress(start, end) => ([0x15, start, end, 0, 0, 0, 0], 3), - Command::RowAddress(start, end) => ([0x75, start, end, 0, 0, 0, 0], 3), - Command::StartLine(line) => ([0xA1, (0x3F & line), 0, 0, 0, 0, 0], 2), + Command::AllOn(on) => ([if on { 0xA5 } else { 0xA6 }, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 1), + Command::Invert(inv) => ([if inv { 0xA7 } else { 0xA4 }, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 1), + Command::DisplayOn(on) => ([0xAE | (on as u8), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 1), + Command::ColumnAddress(start, end) => ([0x15, start, end, 0, 0, 0, 0, 0, 0, 0, 0], 3), + Command::RowAddress(start, end) => ([0x75, start, end, 0, 0, 0, 0, 0, 0, 0, 0], 3), + Command::StartLine(line) => ([0xA1, (0x3F & line), 0, 0, 0, 0, 0, 0, 0, 0, 0], 2), Command::RemapAndColorDepth(hremap, vremap, cmode, addr_inc_mode) => ( [ 0xA0, @@ -82,12 +101,16 @@ impl Command { 0, 0, 0, + 0, + 0, + 0, + 0, ], 2, ), - Command::Multiplex(ratio) => ([0xA8, ratio, 0, 0, 0, 0, 0], 2), - Command::ReverseComDir(rev) => ([0xC0 | ((rev as u8) << 3), 0, 0, 0, 0, 0, 0], 1), - Command::DisplayOffset(offset) => ([0xA2, offset, 0, 0, 0, 0, 0], 2), + Command::Multiplex(ratio) => ([0xA8, ratio, 0, 0, 0, 0, 0, 0, 0, 0, 0], 2), + Command::ReverseComDir(rev) => ([0xC0 | ((rev as u8) << 3), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 1), + Command::DisplayOffset(offset) => ([0xA2, offset, 0, 0, 0, 0, 0, 0, 0, 0, 0], 2), Command::ComPinConfig(alt, lr) => ( [ 0xDA, @@ -97,18 +120,33 @@ impl Command { 0, 0, 0, + 0, + 0, + 0, + 0, ], 2, ), Command::DisplayClockDiv(fosc, div) => { - ([0xB3, ((0xF & fosc) << 4) | (0xF & div), 0, 0, 0, 0, 0], 2) + ([0xB3, ((0xF & fosc) << 4) | (0xF & div), 0, 0, 0, 0, 0, 0, 0, 0, 0], 2) } Command::PreChargePeriod(phase1, phase2) => ( - [0x3e, ((0xF & phase2) << 4) | (0xF & phase1), 0, 0, 0, 0, 0], + [0x3e, ((0xF & phase2) << 4) | (0xF & phase1), 0, 0, 0, 0, 0, 0, 0, 0, 0], 2, ), - Command::VcomhDeselect(level) => ([0xBE, (level as u8) << 1, 0, 0, 0, 0, 0], 2), - Command::Noop => ([0xE3, 0, 0, 0, 0, 0, 0], 1), + Command::VcomhDeselect(level) => ([0xBE, (level as u8) << 1, 0, 0, 0, 0, 0, 0, 0, 0, 0], 2), + Command::Noop => ([0xE3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 1), + Command::DrawLine(c1, r1, c2, r2, color_raw16) => { + // do it by hand since graphics is an optional feature + let (a, b, c) = raw16_to_ssd1331_accel(color_raw16); + ([0x21, c1, r1, c2, r2, c, b, a, 0, 0, 0], 8) + }, + Command::DrawRect(c1, r1, c2, r2, line_raw16, fill_raw16) => { + let (al, bl, cl) = raw16_to_ssd1331_accel(line_raw16); + let (af, bf, cf) = raw16_to_ssd1331_accel(fill_raw16); + ([0x22, c1, r1, c2, r2, cl, bl, al, cf, bf, af], 11) + }, + Command::EnableFill(on) => ([0x26, if on { 0x01 } else { 0x00 }, 0, 0, 0, 0, 0, 0, 0, 0, 0], 2), }; // Command mode. 1 = data, 0 = command diff --git a/src/display.rs b/src/display.rs index a50207d..1402afd 100644 --- a/src/display.rs +++ b/src/display.rs @@ -1,4 +1,4 @@ -use hal::{blocking::delay::DelayMs, digital::v2::OutputPin}; +use hal::{blocking::delay::DelayMs, blocking::delay::DelayUs, digital::v2::OutputPin}; use crate::{ command::{AddressIncrementMode, ColorMode, Command, VcomhLevel}, @@ -233,6 +233,27 @@ where self.buffer[idx + 1] = low; } + /// Draw a line directly into the ssd1331 (bypassing the frame buffer) using it's + /// hardware acceleration. This does no bounds checking. + pub fn draw_hw_line(&mut self, x1: u32, y1: u32, x2: u32, y2: u32, value: u16 + ) -> Result<(), Error> { + // TODO: should we try to clip the line to the display here? that could be tricky. + Command::DrawLine(x1 as u8, y1 as u8, x2 as u8, y2 as u8, value) + .send(&mut self.spi, &mut self.dc) + } + + /// Draw a line directly into the ssd1331 (bypassing the frame buffer) using it's + /// hardware acceleration. This does no bounds checking. + pub fn draw_hw_rect(&mut self, x1: u32, y1: u32, x2: u32, y2: u32, line: u16, fill: Option, delay: &mut DELAY + ) -> Result<(), Error> + where DELAY: DelayUs, + { + Command::EnableFill(fill.is_some()).send(&mut self.spi, &mut self.dc)?; + Command::DrawRect(x1 as u8, y1 as u8, x2 as u8, y2 as u8, line, fill.unwrap_or(0)) + .send(&mut self.spi, &mut self.dc) + .map(|r| if fill.is_some() { delay.delay_us(10) }) // delay 10us if we had to do filling + } + /// Initialise display, setting sensible defaults and rotation pub fn init(&mut self) -> Result<(), Error> { let display_rotation = self.display_rotation;