From f09403d09245c3092dcbcfe13cd45c2cd6a862bd Mon Sep 17 00:00:00 2001 From: Vadim Kaushan Date: Tue, 28 Jan 2020 19:45:23 +0300 Subject: [PATCH 1/8] Add Interface trait --- src/lib.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 03d35ed..a86bce1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,6 +13,24 @@ use hal::spi::{Mode, Phase, Polarity}; use core::fmt::Debug; use core::iter::IntoIterator; +/// Trait representing the interface to the hardware. +/// +/// Intended to abstract the various buses (SPI, MPU 8/9/16-bit) from the Controller code. +pub trait Interface { + type Error; + + /// Sends a command with a sequence of 8-bit arguments + /// + /// Mostly used for sending configuration commands + fn write(&mut self, command: u8, data: &[u8]) -> Result<(), Self::Error>; + + /// Sends a command with a sequence of 16-bit data words + /// + /// Mostly used for sending MemoryWrite command and other commands + /// with 16-bit arguments + fn write_iter(&mut self, command: u8, data: impl IntoIterator) -> Result<(), Self::Error>; +} + /// SPI mode pub const MODE: Mode = Mode { polarity: Polarity::IdleLow, From 5b03a86bb07db96eb44a18712dd1afe42efba1fd Mon Sep 17 00:00:00 2001 From: Vadim Kaushan Date: Tue, 28 Jan 2020 19:56:12 +0300 Subject: [PATCH 2/8] Move SPI interface implementation into a separate file --- src/lib.rs | 66 ++++++++++------------------------------------------- src/spi.rs | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 54 deletions(-) create mode 100644 src/spi.rs diff --git a/src/lib.rs b/src/lib.rs index a86bce1..c937edd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,13 +6,15 @@ extern crate embedded_hal as hal; extern crate embedded_graphics; use hal::blocking::delay::DelayMs; -use hal::blocking::spi; +use hal::blocking::spi::{Write, Transfer}; use hal::digital::v2::OutputPin; -use hal::spi::{Mode, Phase, Polarity}; use core::fmt::Debug; use core::iter::IntoIterator; +pub mod spi; +use spi::SpiInterface; + /// Trait representing the interface to the hardware. /// /// Intended to abstract the various buses (SPI, MPU 8/9/16-bit) from the Controller code. @@ -31,12 +33,6 @@ pub trait Interface { fn write_iter(&mut self, command: u8, data: impl IntoIterator) -> Result<(), Self::Error>; } -/// SPI mode -pub const MODE: Mode = Mode { - polarity: Polarity::IdleLow, - phase: Phase::CaptureOnFirstTransition, -}; - const WIDTH: usize = 240; const HEIGHT: usize = 320; @@ -71,9 +67,7 @@ pub enum Orientation { /// and the next word will fill the next pixel (the adjacent on the right, or /// the first of the next row if the row ended) pub struct Ili9341 { - spi: SPI, - cs: CS, - dc: DC, + interface: SpiInterface, reset: RESET, width: usize, height: usize, @@ -81,7 +75,7 @@ pub struct Ili9341 { impl Ili9341 where - SPI: spi::Transfer + spi::Write, + SPI: Transfer + Write, CS: OutputPin, DC: OutputPin, RESET: OutputPin, @@ -93,10 +87,9 @@ where reset: RESET, delay: &mut DELAY, ) -> Result> { + let interface = SpiInterface::new(spi, cs, dc); let mut ili9341 = Ili9341 { - spi, - cs, - dc, + interface, reset, width: WIDTH, height: HEIGHT, @@ -159,51 +152,16 @@ where Ok(()) } fn command(&mut self, cmd: Command, args: &[u8]) -> Result<(), Error> { - self.cs.set_low().map_err(Error::OutputPin)?; - - self.dc.set_low().map_err(Error::OutputPin)?; - self.spi.write(&[cmd as u8]).map_err(Error::Spi)?; - - self.dc.set_high().map_err(Error::OutputPin)?; - self.spi.write(args).map_err(Error::Spi)?; - - self.cs.set_high().map_err(Error::OutputPin)?; - Ok(()) + self.interface.write(cmd as u8, args) } fn write_iter>( &mut self, data: I, ) -> Result<(), Error> { - self.cs.set_low().map_err(Error::OutputPin)?; - - self.dc.set_low().map_err(Error::OutputPin)?; - self.spi - .write(&[Command::MemoryWrite as u8]) - .map_err(Error::Spi)?; - - self.dc.set_high().map_err(Error::OutputPin)?; - for d in data.into_iter() { - self.spi - .write(&[(d >> 8) as u8, (d & 0xff) as u8]) - .map_err(Error::Spi)?; - } - - self.cs.set_high().map_err(Error::OutputPin)?; - Ok(()) + self.interface.write_iter(Command::MemoryWrite as u8, data) } fn write_raw(&mut self, data: &[u8]) -> Result<(), Error> { - self.cs.set_low().map_err(Error::OutputPin)?; - - self.dc.set_low().map_err(Error::OutputPin)?; - self.spi - .write(&[Command::MemoryWrite as u8]) - .map_err(Error::Spi)?; - - self.dc.set_high().map_err(Error::OutputPin)?; - self.spi.write(data).map_err(Error::Spi)?; - - self.cs.set_high().map_err(Error::OutputPin)?; - Ok(()) + self.interface.write(Command::MemoryWrite as u8, data) } fn set_window(&mut self, x0: u16, y0: u16, x1: u16, y1: u16) -> Result<(), Error> { self.command( @@ -310,7 +268,7 @@ use embedded_graphics::{drawable::Pixel, pixelcolor::Rgb565, Drawing}; #[cfg(feature = "graphics")] impl Drawing for Ili9341 where - SPI: spi::Transfer + spi::Write, + SPI: Transfer + Write, CS: OutputPin, DC: OutputPin, RESET: OutputPin, diff --git a/src/spi.rs b/src/spi.rs new file mode 100644 index 0000000..8eac1a2 --- /dev/null +++ b/src/spi.rs @@ -0,0 +1,67 @@ +use super::hal::blocking::spi; +use super::hal::spi::{Mode, Phase, Polarity}; +use super::hal::digital::v2::OutputPin; +use super::{Interface, Error}; + +/// SPI mode +pub const MODE: Mode = Mode { + polarity: Polarity::IdleLow, + phase: Phase::CaptureOnFirstTransition, +}; + +/// `Interface` implementation for SPI interfaces +pub struct SpiInterface { + spi: SPI, + cs: CS, + dc: DC, +} + +impl SpiInterface + where SPI: spi::Transfer + spi::Write, + CS: OutputPin, + DC: OutputPin, +{ + pub fn new(spi: SPI, cs: CS, dc: DC) -> Self { + Self { + spi, + cs, + dc, + } + } +} + +impl Interface for SpiInterface + where SPI: spi::Transfer + spi::Write, + CS: OutputPin, + DC: OutputPin, +{ + type Error = Error; + + fn write(&mut self, command: u8, data: &[u8]) -> Result<(), Self::Error> { + self.cs.set_low().map_err(Error::OutputPin)?; + + self.dc.set_low().map_err(Error::OutputPin)?; + self.spi.write(&[command]).map_err(Error::Spi)?; + + self.dc.set_high().map_err(Error::OutputPin)?; + self.spi.write(data).map_err(Error::Spi)?; + + self.cs.set_high().map_err(Error::OutputPin)?; + Ok(()) + } + + fn write_iter(&mut self, command: u8, data: impl IntoIterator) -> Result<(), Self::Error> { + self.cs.set_low().map_err(Error::OutputPin)?; + + self.dc.set_low().map_err(Error::OutputPin)?; + self.spi.write(&[command]).map_err(Error::Spi)?; + + self.dc.set_high().map_err(Error::OutputPin)?; + for w in data.into_iter() { + self.spi.write(&w.to_be_bytes()).map_err(Error::Spi)?; + } + + self.cs.set_high().map_err(Error::OutputPin)?; + Ok(()) + } +} From d9ab0601b7d82a5de376e553207ec1a58b5727a0 Mon Sep 17 00:00:00 2001 From: Vadim Kaushan Date: Tue, 28 Jan 2020 20:02:35 +0300 Subject: [PATCH 3/8] Split Ili9341 constructor in two --- src/lib.rs | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index c937edd..644546d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -66,21 +66,21 @@ pub enum Orientation { /// - As soon as a pixel is received, an internal counter is incremented, /// and the next word will fill the next pixel (the adjacent on the right, or /// the first of the next row if the row ended) -pub struct Ili9341 { - interface: SpiInterface, +pub struct Ili9341 { + interface: IFACE, reset: RESET, width: usize, height: usize, } -impl Ili9341 +impl Ili9341, RESET> where SPI: Transfer + Write, CS: OutputPin, DC: OutputPin, RESET: OutputPin, { - pub fn new>( + pub fn new_spi>( spi: SPI, cs: CS, dc: DC, @@ -88,6 +88,20 @@ where delay: &mut DELAY, ) -> Result> { let interface = SpiInterface::new(spi, cs, dc); + Self::new(interface, reset, delay) + } +} + +impl Ili9341 + where + IFACE: Interface>, + RESET: OutputPin, +{ + pub fn new>( + interface: IFACE, + reset: RESET, + delay: &mut DELAY, + ) -> Result> { let mut ili9341 = Ili9341 { interface, reset, @@ -266,11 +280,9 @@ use embedded_graphics::drawable; use embedded_graphics::{drawable::Pixel, pixelcolor::Rgb565, Drawing}; #[cfg(feature = "graphics")] -impl Drawing for Ili9341 +impl Drawing for Ili9341 where - SPI: Transfer + Write, - CS: OutputPin, - DC: OutputPin, + IFACE: Interface>, RESET: OutputPin, SpiE: Debug, PinE: Debug, From 6056b3db2275ad607de0ecada33d839918f49fc0 Mon Sep 17 00:00:00 2001 From: Vadim Kaushan Date: Tue, 28 Jan 2020 20:03:35 +0300 Subject: [PATCH 4/8] Fix formatting --- src/lib.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 644546d..99fd38d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -165,18 +165,22 @@ impl Ili9341 delay.delay_ms(200); Ok(()) } + fn command(&mut self, cmd: Command, args: &[u8]) -> Result<(), Error> { self.interface.write(cmd as u8, args) } + fn write_iter>( &mut self, data: I, ) -> Result<(), Error> { self.interface.write_iter(Command::MemoryWrite as u8, data) } + fn write_raw(&mut self, data: &[u8]) -> Result<(), Error> { self.interface.write(Command::MemoryWrite as u8, data) } + fn set_window(&mut self, x0: u16, y0: u16, x1: u16, y1: u16) -> Result<(), Error> { self.command( Command::ColumnAddressSet, @@ -198,6 +202,7 @@ impl Ili9341 )?; Ok(()) } + /// Draw a rectangle on the screen, represented by top-left corner (x0, y0) /// and bottom-right corner (x1, y1). /// @@ -218,6 +223,7 @@ impl Ili9341 self.set_window(x0, y0, x1, y1)?; self.write_iter(data) } + /// Draw a rectangle on the screen, represented by top-left corner (x0, y0) /// and bottom-right corner (x1, y1). /// @@ -239,6 +245,7 @@ impl Ili9341 self.set_window(x0, y0, x1, y1)?; self.write_raw(data) } + /// Change the orientation of the screen pub fn set_orientation(&mut self, mode: Orientation) -> Result<(), Error> { match mode { @@ -264,10 +271,12 @@ impl Ili9341 } } } + /// Get the current screen width. It can change based on the current orientation pub fn width(&self) -> usize { self.width } + /// Get the current screen heighth. It can change based on the current orientation pub fn height(&self) -> usize { self.height From 459545a77705ed789074608a0e30befd1541c666 Mon Sep 17 00:00:00 2001 From: Vadim Kaushan Date: Tue, 28 Jan 2020 20:27:04 +0300 Subject: [PATCH 5/8] Update Error type --- src/lib.rs | 51 ++++++++++++++++++++++++++++++--------------------- src/spi.rs | 8 ++++---- 2 files changed, 34 insertions(+), 25 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 99fd38d..2e5094d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -37,11 +37,17 @@ const WIDTH: usize = 240; const HEIGHT: usize = 320; #[derive(Debug)] -pub enum Error { - Spi(SpiE), +pub enum Error { + Interface(IfaceE), OutputPin(PinE), } +impl From for Error { + fn from(e: IfaceE) -> Self { + Error::Interface(e) + } +} + /// The default orientation is Portrait pub enum Orientation { Portrait, @@ -88,20 +94,23 @@ where delay: &mut DELAY, ) -> Result> { let interface = SpiInterface::new(spi, cs, dc); - Self::new(interface, reset, delay) + Self::new(interface, reset, delay).map_err(|e| match e { + Error::Interface(inner) => inner, + Error::OutputPin(inner) => Error::OutputPin(inner), + }) } } -impl Ili9341 +impl Ili9341 where - IFACE: Interface>, + IFACE: Interface, RESET: OutputPin, { pub fn new>( interface: IFACE, reset: RESET, delay: &mut DELAY, - ) -> Result> { + ) -> Result> { let mut ili9341 = Ili9341 { interface, reset, @@ -109,7 +118,7 @@ impl Ili9341 height: HEIGHT, }; - ili9341.hard_reset(delay)?; + ili9341.hard_reset(delay).map_err(Error::OutputPin)?; ili9341.command(Command::SoftwareReset, &[])?; delay.delay_ms(200); @@ -153,35 +162,35 @@ impl Ili9341 fn hard_reset>( &mut self, delay: &mut DELAY, - ) -> Result<(), Error> { + ) -> Result<(), PinE> { // set high if previously low - self.reset.set_high().map_err(Error::OutputPin)?; + self.reset.set_high()?; delay.delay_ms(200); // set low for reset - self.reset.set_low().map_err(Error::OutputPin)?; + self.reset.set_low()?; delay.delay_ms(200); // set high for normal operation - self.reset.set_high().map_err(Error::OutputPin)?; + self.reset.set_high()?; delay.delay_ms(200); Ok(()) } - fn command(&mut self, cmd: Command, args: &[u8]) -> Result<(), Error> { + fn command(&mut self, cmd: Command, args: &[u8]) -> Result<(), IFACE::Error> { self.interface.write(cmd as u8, args) } fn write_iter>( &mut self, data: I, - ) -> Result<(), Error> { + ) -> Result<(), IFACE::Error> { self.interface.write_iter(Command::MemoryWrite as u8, data) } - fn write_raw(&mut self, data: &[u8]) -> Result<(), Error> { + fn write_raw(&mut self, data: &[u8]) -> Result<(), IFACE::Error> { self.interface.write(Command::MemoryWrite as u8, data) } - fn set_window(&mut self, x0: u16, y0: u16, x1: u16, y1: u16) -> Result<(), Error> { + fn set_window(&mut self, x0: u16, y0: u16, x1: u16, y1: u16) -> Result<(), IFACE::Error> { self.command( Command::ColumnAddressSet, &[ @@ -219,7 +228,7 @@ impl Ili9341 x1: u16, y1: u16, data: I, - ) -> Result<(), Error> { + ) -> Result<(), IFACE::Error> { self.set_window(x0, y0, x1, y1)?; self.write_iter(data) } @@ -241,13 +250,13 @@ impl Ili9341 x1: u16, y1: u16, data: &[u8], - ) -> Result<(), Error> { + ) -> Result<(), IFACE::Error> { self.set_window(x0, y0, x1, y1)?; self.write_raw(data) } /// Change the orientation of the screen - pub fn set_orientation(&mut self, mode: Orientation) -> Result<(), Error> { + pub fn set_orientation(&mut self, mode: Orientation) -> Result<(), IFACE::Error> { match mode { Orientation::Portrait => { self.width = WIDTH; @@ -289,11 +298,11 @@ use embedded_graphics::drawable; use embedded_graphics::{drawable::Pixel, pixelcolor::Rgb565, Drawing}; #[cfg(feature = "graphics")] -impl Drawing for Ili9341 +impl Drawing for Ili9341 where - IFACE: Interface>, + IFACE: Interface, RESET: OutputPin, - SpiE: Debug, + IfaceE: Debug, PinE: Debug, { fn draw(&mut self, item_pixels: T) diff --git a/src/spi.rs b/src/spi.rs index 8eac1a2..0956ab8 100644 --- a/src/spi.rs +++ b/src/spi.rs @@ -41,10 +41,10 @@ impl Interface for SpiInterface self.cs.set_low().map_err(Error::OutputPin)?; self.dc.set_low().map_err(Error::OutputPin)?; - self.spi.write(&[command]).map_err(Error::Spi)?; + self.spi.write(&[command]).map_err(Error::Interface)?; self.dc.set_high().map_err(Error::OutputPin)?; - self.spi.write(data).map_err(Error::Spi)?; + self.spi.write(data).map_err(Error::Interface)?; self.cs.set_high().map_err(Error::OutputPin)?; Ok(()) @@ -54,11 +54,11 @@ impl Interface for SpiInterface self.cs.set_low().map_err(Error::OutputPin)?; self.dc.set_low().map_err(Error::OutputPin)?; - self.spi.write(&[command]).map_err(Error::Spi)?; + self.spi.write(&[command]).map_err(Error::Interface)?; self.dc.set_high().map_err(Error::OutputPin)?; for w in data.into_iter() { - self.spi.write(&w.to_be_bytes()).map_err(Error::Spi)?; + self.spi.write(&w.to_be_bytes()).map_err(Error::Interface)?; } self.cs.set_high().map_err(Error::OutputPin)?; From 11c4a5ca34b7fc07a533630ff3a55ee96c7bbb1d Mon Sep 17 00:00:00 2001 From: Vadim Kaushan Date: Tue, 28 Jan 2020 20:35:30 +0300 Subject: [PATCH 6/8] Use u16 for pixel data --- src/lib.rs | 35 +++++++++-------------------------- 1 file changed, 9 insertions(+), 26 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 2e5094d..59cac28 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -186,10 +186,6 @@ impl Ili9341 self.interface.write_iter(Command::MemoryWrite as u8, data) } - fn write_raw(&mut self, data: &[u8]) -> Result<(), IFACE::Error> { - self.interface.write(Command::MemoryWrite as u8, data) - } - fn set_window(&mut self, x0: u16, y0: u16, x1: u16, y1: u16) -> Result<(), IFACE::Error> { self.command( Command::ColumnAddressSet, @@ -238,21 +234,20 @@ impl Ili9341 /// /// The border is included. /// - /// This method accepts a raw buffer of bytes that will be copied to the screen + /// This method accepts a raw buffer of words that will be copied to the screen /// video memory. /// - /// The expected format is rgb565, and the two bytes for a pixel - /// are in big endian order. + /// The expected format is rgb565. pub fn draw_raw( &mut self, x0: u16, y0: u16, x1: u16, y1: u16, - data: &[u8], + data: &[u16], ) -> Result<(), IFACE::Error> { self.set_window(x0, y0, x1, y1)?; - self.write_raw(data) + self.write_iter(data.iter().cloned()) } /// Change the orientation of the screen @@ -311,7 +306,7 @@ where { const BUF_SIZE: usize = 64; - let mut row: [u8; BUF_SIZE] = [0; BUF_SIZE]; + let mut row: [u16; BUF_SIZE] = [0; BUF_SIZE]; let mut i = 0; let mut lasty = 0; let mut startx = 0; @@ -333,14 +328,8 @@ where startx = pos.x; } // Add pixel color to buffer - for b in embedded_graphics::pixelcolor::raw::RawU16::from(color) - .into_inner() - .to_be_bytes() - .iter() - { - row[i] = *b; - i += 1; - } + row[i] = embedded_graphics::pixelcolor::raw::RawU16::from(color).into_inner(); + i += 1; lasty = pos.y; endx = pos.x; } else { @@ -357,14 +346,8 @@ where // Start new line of contiguous pixels i = 0; startx = pos.x; - for b in embedded_graphics::pixelcolor::raw::RawU16::from(color) - .into_inner() - .to_be_bytes() - .iter() - { - row[i] = *b; - i += 1; - } + row[i] = embedded_graphics::pixelcolor::raw::RawU16::from(color).into_inner(); + i += 1; lasty = pos.y; endx = pos.x; } From f958482f98cc2a2d32f6c90fa8d1052701b5472c Mon Sep 17 00:00:00 2001 From: Vadim Kaushan Date: Tue, 28 Jan 2020 20:50:58 +0300 Subject: [PATCH 7/8] Shrink row buffer to the previous capacity --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 59cac28..a2f4bd1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -304,7 +304,7 @@ where where T: IntoIterator>, { - const BUF_SIZE: usize = 64; + const BUF_SIZE: usize = 32; let mut row: [u16; BUF_SIZE] = [0; BUF_SIZE]; let mut i = 0; From c51eaa4ffa99659ee5e114aeb3b9e06f789af12d Mon Sep 17 00:00:00 2001 From: Vadim Kaushan Date: Tue, 28 Jan 2020 20:53:24 +0300 Subject: [PATCH 8/8] Edition 2018 --- Cargo.toml | 1 + src/lib.rs | 8 +++----- src/spi.rs | 8 ++++---- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ba780e9..e97d3bd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ categories = ["embedded", "hardware-support", "no-std"] keywords = ["embedded-hal-driver", "display", "LCD"] license = "MIT OR Apache-2.0" repository = "https://github.com/yuri91/ili9341-rs" +edition = "2018" [dependencies] diff --git a/src/lib.rs b/src/lib.rs index a2f4bd1..29808ea 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,13 +1,11 @@ #![no_std] -extern crate embedded_hal as hal; - #[cfg(feature = "graphics")] extern crate embedded_graphics; -use hal::blocking::delay::DelayMs; -use hal::blocking::spi::{Write, Transfer}; -use hal::digital::v2::OutputPin; +use embedded_hal::blocking::delay::DelayMs; +use embedded_hal::blocking::spi::{Write, Transfer}; +use embedded_hal::digital::v2::OutputPin; use core::fmt::Debug; use core::iter::IntoIterator; diff --git a/src/spi.rs b/src/spi.rs index 0956ab8..c168074 100644 --- a/src/spi.rs +++ b/src/spi.rs @@ -1,7 +1,7 @@ -use super::hal::blocking::spi; -use super::hal::spi::{Mode, Phase, Polarity}; -use super::hal::digital::v2::OutputPin; -use super::{Interface, Error}; +use embedded_hal::blocking::spi; +use embedded_hal::spi::{Mode, Phase, Polarity}; +use embedded_hal::digital::v2::OutputPin; +use crate::{Interface, Error}; /// SPI mode pub const MODE: Mode = Mode {