Skip to content

Commit

Permalink
Prepare driver for embedded-graphics 0.8.1
Browse files Browse the repository at this point in the history
- Enforce driver use of `Rgb565` to fit with v0.4-0.5 changes
- Add `FrameBuffer` from 0.7+
- Remove `graphic` crate as an option
  • Loading branch information
IniterWorker committed Sep 4, 2024
1 parent 00d85cc commit 134dd86
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 70 deletions.
6 changes: 1 addition & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ targets = [ "thumbv7m-none-eabi", "thumbv7em-none-eabihf" ]
# Embedded HAL abstraction
# We use this layer to abstract hardware i2c/spi
embedded-hal = { version = "1.0.0" }
embedded-graphics-core = { version = "0.4.0", optional = true }
embedded-graphics = { version = "0.8.0" }

# This Rust crate contains a no_std compatible interface in form of traits
# to bridge between a bus driver and a display driver.
Expand All @@ -33,10 +33,6 @@ embedded-graphics-core = { version = "0.4.0", optional = true }
display-interface = "0.5.0"
display-interface-spi = "0.5.0"

[features]
default = ["graphics"]
graphics = ["embedded-graphics-core"]

[dev-dependencies.cargo-husky]
version = "1"
features = ["user-hooks"]
Expand Down
33 changes: 19 additions & 14 deletions src/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@
//! Reference all screen hardware definition
use display_interface::{DisplayError, WriteOnlyDataCommand};
use embedded_graphics::{framebuffer::buffer_size, pixelcolor::Bgr565};
use embedded_hal::delay::DelayNs;

use crate::command::{
Command, DINVMode, Dbi, Dpi, GSMode, Gamma1, Gamma2, Gamma3, Gamma4, Logical, SSMode,
use crate::{
command::{
Command, DINVMode, Dbi, Dpi, GSMode, Gamma1, Gamma2, Gamma3, Gamma4, Logical, SSMode,
},
Gc9a01Framebuffer,
};

/// Screen information
Expand All @@ -31,8 +35,8 @@ pub trait DisplayDefinition {
/// The driver maximum rows
const ROWS: u16 = 240;

/// Buffer type Sized
type Buffer: AsMut<[u16]> + NewZeroed;
/// Buffer data frame buffer
type Buffer;

/// Configuration hook to configure model-dependent configuration
///
Expand All @@ -44,6 +48,8 @@ pub trait DisplayDefinition {
iface: &mut impl WriteOnlyDataCommand,
delay: &mut impl DelayNs,
) -> Result<(), DisplayError>;

fn new_buffer() -> Self::Buffer;
}

/// Screen Definition
Expand All @@ -55,7 +61,13 @@ impl DisplayDefinition for DisplayResolution240x240 {
const WIDTH: u16 = 240;
const HEIGHT: u16 = 240;

type Buffer = [u16; Self::WIDTH as usize * Self::HEIGHT as usize];
//type Buffer = [u16; Self::WIDTH as usize * Self::HEIGHT as usize];

type Buffer = Gc9a01Framebuffer<
{ Self::WIDTH as usize },
{ Self::HEIGHT as usize },
{ buffer_size::<Bgr565>(Self::WIDTH as usize, Self::HEIGHT as usize) },
>;

fn configure(
&self,
Expand Down Expand Up @@ -159,15 +171,8 @@ impl DisplayDefinition for DisplayResolution240x240 {

Ok(())
}
}

pub trait NewZeroed {
/// Creates a new value with its memory set to zero
fn new_zeroed() -> Self;
}

impl<const N: usize> NewZeroed for [u16; N] {
fn new_zeroed() -> Self {
[0u16; N]
fn new_buffer() -> Self::Buffer {
Self::Buffer::new()
}
}
11 changes: 11 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,14 @@
clippy::indexing_slicing
)]

use embedded_graphics::{
framebuffer::Framebuffer,
pixelcolor::{
raw::{LittleEndian, RawU16},
Rgb565,
},
};

// export commands
pub mod command;
// export screen configuration
Expand All @@ -85,3 +93,6 @@ mod spi;
// export the driver and interface
pub use driver::Gc9a01;
pub use spi::SPIDisplayInterface;

type Gc9a01Framebuffer<const WIDTH: usize, const HEIGHT: usize, const N: usize> =
Framebuffer<Rgb565, RawU16, LittleEndian, WIDTH, HEIGHT, N>;
98 changes: 47 additions & 51 deletions src/mode/graphics.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
//! Buffered Graphic Implementation
use core::slice;

use display_interface::{DisplayError, WriteOnlyDataCommand};
use embedded_graphics_core::{pixelcolor::raw::RawU16, prelude::RawData};
use embedded_graphics::geometry::Dimensions;
use embedded_graphics::{pixelcolor::Rgb565, prelude::*, Pixel};
use embedded_hal::delay::DelayNs;

use crate::{
display::{DisplayDefinition, NewZeroed},
rotation::DisplayRotation,
Gc9a01,
};
use crate::{display::DisplayDefinition, rotation::DisplayRotation, Gc9a01, Gc9a01Framebuffer};

/// Buffered Graphic Implementation
///
Expand All @@ -35,7 +34,7 @@ where
/// Create a new buffered graphics mode instance.
pub(crate) fn new() -> Self {
Self {
buffer: NewZeroed::new_zeroed(),
buffer: D::new_buffer(),
min_x: u16::MAX,
max_x: u16::MIN,
min_y: u16::MAX,
Expand All @@ -44,10 +43,11 @@ where
}
}

impl<I, D, DELAY> DisplayConfiguration<DELAY> for Gc9a01<I, D, BufferedGraphics<D>>
impl<const WIDTH: usize, const HEIGHT: usize, const N: usize, I, D, DELAY>
DisplayConfiguration<DELAY> for Gc9a01<I, D, BufferedGraphics<D>>
where
I: WriteOnlyDataCommand,
D: DisplayDefinition,
D: DisplayDefinition<Buffer = Gc9a01Framebuffer<WIDTH, HEIGHT, N>>,
DELAY: DelayNs,
{
type Error = DisplayError;
Expand All @@ -64,17 +64,17 @@ where
}
}

impl<I, D> Gc9a01<I, D, BufferedGraphics<D>>
impl<const WIDTH: usize, const HEIGHT: usize, const N: usize, I, D>
Gc9a01<I, D, BufferedGraphics<D>>
where
I: WriteOnlyDataCommand,
D: DisplayDefinition,
D: DisplayDefinition<Buffer = Gc9a01Framebuffer<WIDTH, HEIGHT, N>>,
{
/// Clear the display buffer
/// NOTE: Must use `flush` to apply changes
pub fn clear(&mut self) {
for b in self.mode.buffer.as_mut() {
*b = 0;
}
#[allow(clippy::let_underscore_must_use)]
let _ = self.mode.buffer.clear(Rgb565::BLACK);

let (max_x, max_y) = self.dimensions();
self.mode.min_x = u16::MIN;
Expand All @@ -83,10 +83,9 @@ where
self.mode.max_y = max_y;
}

pub fn fill(&mut self, color: u16) {
for b in self.mode.buffer.as_mut() {
*b = color;
}
pub fn fill(&mut self, color: Rgb565) {
#[allow(clippy::let_underscore_must_use)]
let _ = self.mode.buffer.clear(color);

let (max_x, max_y) = self.dimensions();
self.mode.min_x = u16::MIN;
Expand All @@ -95,6 +94,18 @@ where
self.mode.max_y = max_y;
}

#[allow(clippy::cast_ptr_alignment)]
fn convert_u8_to_u16_slice(input: &[u8]) -> &[u16] {
// Ensure the length is even
assert!(input.len() % 2 == 0);

// Convert &[u8] to &[u16] safely
let ptr: *const u16 = input.as_ptr().cast::<u16>();
let len = input.len() / 2;

unsafe { slice::from_raw_parts(ptr, len) }
}

/// Write the display buffer
///
/// # Errors
Expand Down Expand Up @@ -137,7 +148,7 @@ where

Self::flush_buffer_chunks(
&mut self.interface,
self.mode.buffer.as_mut(),
Self::convert_u8_to_u16_slice(self.mode.buffer.data()),
width as usize,
(disp_min_x, disp_min_y),
(disp_max_x, disp_max_y),
Expand All @@ -151,7 +162,7 @@ where

Self::flush_buffer_chunks(
&mut self.interface,
self.mode.buffer.as_mut(),
Self::convert_u8_to_u16_slice(self.mode.buffer.data()),
height as usize,
(disp_min_y, disp_min_x),
(disp_max_y, disp_max_x),
Expand All @@ -161,59 +172,46 @@ where
}

// Turn a pixel on or off
pub fn set_pixel(&mut self, x: u32, y: u32, value: u16) {
pub fn set_pixel(&mut self, x: u32, y: u32, value: Rgb565) {
let rotation = self.display_rotation;

let idx = match rotation {
DisplayRotation::Rotate0 | DisplayRotation::Rotate180 => {
((y as usize) * D::WIDTH as usize) + (x as usize)
}
#[allow(clippy::cast_possible_wrap)] // for efficient
let pos = match rotation {
DisplayRotation::Rotate0 | DisplayRotation::Rotate180 => Point::new(x as i32, y as i32),
DisplayRotation::Rotate90 | DisplayRotation::Rotate270 => {
((x as usize) * D::HEIGHT as usize) + (y as usize)
Point::new(y as i32, x as i32)
}
};

if let Some(byte) = self.mode.buffer.as_mut().get_mut(idx) {
self.mode.min_x = self.mode.min_x.min(x as u16);
self.mode.max_x = self.mode.max_x.max(x as u16);
self.mode.min_y = self.mode.min_y.min(y as u16);
self.mode.max_y = self.mode.max_y.max(y as u16);
self.mode.buffer.set_pixel(pos, value);

*byte = (value >> 8) & 0xFF | (value << 8) & 0xFF00;
}
self.mode.min_x = self.mode.min_x.min(x as u16);
self.mode.max_x = self.mode.max_x.max(x as u16);
self.mode.min_y = self.mode.min_y.min(y as u16);
self.mode.max_y = self.mode.max_y.max(y as u16);
}
}

#[cfg(feature = "graphics")]
use embedded_graphics_core::{
draw_target::DrawTarget,
geometry::Size,
geometry::{Dimensions, OriginDimensions},
pixelcolor::Rgb565,
Pixel,
};

use super::DisplayConfiguration;

#[cfg(feature = "graphics")]
impl<I, D> OriginDimensions for Gc9a01<I, D, BufferedGraphics<D>>
impl<const WIDTH: usize, const HEIGHT: usize, const N: usize, I, D> OriginDimensions
for Gc9a01<I, D, BufferedGraphics<D>>
where
I: WriteOnlyDataCommand,
D: DisplayDefinition,
D: DisplayDefinition<Buffer = Gc9a01Framebuffer<WIDTH, HEIGHT, N>>,
{
fn size(&self) -> Size {
let (w, h) = self.dimensions();
Size::new(w.into(), h.into())
}
}

#[cfg(feature = "graphics")]
impl<I, D> DrawTarget for Gc9a01<I, D, BufferedGraphics<D>>
impl<const WIDTH: usize, const HEIGHT: usize, const N: usize, I, D> DrawTarget
for Gc9a01<I, D, BufferedGraphics<D>>
where
I: WriteOnlyDataCommand,
D: DisplayDefinition,
D: DisplayDefinition<Buffer = Gc9a01Framebuffer<WIDTH, HEIGHT, N>>,
{
// TODO: figure out a way to handle all case
type Color = Rgb565;
type Error = DisplayError;

Expand All @@ -227,8 +225,6 @@ where
.into_iter()
.filter(|&Pixel(pos, _color)| bb.contains(pos))
.for_each(|Pixel(pos, color)| {
let color: RawU16 = color.into();
let color: u16 = color.into_inner();
#[allow(clippy::cast_sign_loss)]
self.set_pixel(pos.x as u32, pos.y as u32, color);
});
Expand Down

0 comments on commit 134dd86

Please sign in to comment.