Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

initial draft of hw accel primitives #8

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 53 additions & 15 deletions src/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -60,16 +79,16 @@ impl Command {
SPI: hal::blocking::spi::Write<u8, Error = CommE>,
DC: OutputPin<Error = PinE>,
{
// 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,
Expand All @@ -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,
Expand All @@ -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
Expand Down
23 changes: 22 additions & 1 deletion src/display.rs
Original file line number Diff line number Diff line change
@@ -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},
Expand Down Expand Up @@ -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<CommE, PinE>> {
// TODO: should we try to clip the line to the display here? that could be tricky.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What behaviour does the display have in clipping cases? If it goes offscreen and doesn't do anything like wrapping around, it should be fine to not bother clipping. Presumably this also applies to draw_hw_rect too.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok do we need to clip, or just abort, if any of it is off-screen? I am unfamiliar with what is the standard for these drivers.

Also, generally, do you think this is going to be helpful? I am still on the fence, since mixing blit and hw accel causes noticeable flashing (at least on my softcore).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't done anything using both blitting and HW accel, so I can't comment on that. The draw_hw_rect method may be useful for the new embedded-graphics 0.7 features so would be worth keeping in IMO, although I can see complications arising when using both HW and the software buffer in this driver.

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<DELAY>(&mut self, x1: u32, y1: u32, x2: u32, y2: u32, line: u16, fill: Option<u16>, delay: &mut DELAY
) -> Result<(), Error<CommE, PinE>>
where DELAY: DelayUs<u32>,
{
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<CommE, PinE>> {
let display_rotation = self.display_rotation;
Expand Down