Compare commits

...

91 commits

Author SHA1 Message Date
Valentin Trophime 7fa69abd72 avoid some conversion slice to iterator where possible 2024-03-06 20:03:52 +01:00
Valentin Trophime 693f0bdd8f fix semantic versionning 2024-02-13 12:24:24 +01:00
Valentin Trophime 7318f971cf update dependencies 2024-02-13 12:24:24 +01:00
Yuri Iozzelli ac73c3ba40 Merge branch 'chemicstry-master' 2022-10-07 15:17:58 +02:00
chemicstry aa0003d0a4 Bump embedded-hal to 1.0.0-alpha.9 2022-10-07 15:01:55 +03:00
Yuri Iozzelli 32ca78087c
Merge pull request #38 from VersBinarii/readme
Add badges to README
2022-10-06 10:00:10 +02:00
VersBinarii 0cdd4e7c2e Add badges to README 2022-08-26 21:09:24 +02:00
Yuri Iozzelli df5ea50076
Merge pull request #37 from ashutosh-arm/upgrade_embedded_hal_to_alpha
Bumping up embedded-hal version to 1.0.0-alpha.8
2022-06-24 10:12:52 +02:00
Ashutosh Parkhi 00a50a13cc Bumping up embedded-hal version to 1.0.0-alpha.8 2022-06-23 17:20:18 +01:00
Yuri Iozzelli 99f6e63918
Merge pull request #35 from VersBinarii/commands
Add idle mode, brightness and frame rate control
2022-04-24 13:26:02 +02:00
VersBinarii a5e7f6498a Add idle mode, brightness and framerate control 2022-04-23 21:25:22 +02:00
Yuri Iozzelli bfb77647d5
Merge pull request #34 from VersBinarii/more_commands
Add Invert, display and sleep control commands
2022-04-21 16:17:52 +02:00
VersBinarii 1f47f5797e Add Invert, display and sleep control commands 2022-04-21 13:43:06 +02:00
Yuri Iozzelli d896467aec
Merge pull request #33 from VersBinarii/graphics
Add clear_screen function
2022-04-19 07:51:18 +02:00
VersBinarii 6e8c814156 Add clear_screen function 2022-04-18 18:46:30 +02:00
Yuri Iozzelli d933649c19
Merge pull request #32 from VersBinarii/examples
Add RTIC based example for stm32f411 BlackPill
2022-04-18 10:48:01 +02:00
VersBinarii d4e883ce8d Add RTIC based example for stm32f411 BlackPill 2022-04-18 10:30:28 +02:00
Yuri Iozzelli 6e1fab6da7
Merge pull request #31 from VersBinarii/bump_eh_alpha
Bump embedded-hal to alpha7
2022-04-17 10:04:49 +02:00
VersBinarii b0f3b14664 Bump embedded-hal to alpha7 2022-04-16 22:29:50 +02:00
Yuri Iozzelli 8aeefc4abc
Merge pull request #30 from plaes/min-doc-example
Add basic usage example to the index screen
2021-11-19 11:23:14 +01:00
Priit Laes 11661b20b6 Add basic usage example to the index screen 2021-11-18 22:32:37 +02:00
Yuri Iozzelli c3d0d08a3c
Merge pull request #29 from ivmarkov/master
Support for boards that need a non-standard initialization command
2021-08-07 10:31:07 +02:00
imarkov 8a4aee95c2 Support for boards that need a non-standard initialization command 2021-07-14 21:34:55 +03:00
Yuri Iozzelli 19efa445bf Bump version to 0.5.0 2021-06-10 18:40:31 +02:00
Yuri Iozzelli 634558b18e
Merge pull request #28 from yuri91/embedded-graphics-0.7
Embedded graphics 0.7
2021-06-10 18:39:42 +02:00
Yuri Iozzelli e33b10c885 Update embedded-hal version 2021-06-10 18:36:21 +02:00
Yuri Iozzelli ddffec49c6 Remove support for embedded-graphics 0.6 2021-06-10 18:35:09 +02:00
Yuri Iozzelli 7c2e3e27fb
Merge pull request #26 from GrantM11235/e-g-core
Add support for embedded-graphics-core 0.3
2021-05-13 18:14:35 +02:00
Grant Miller 115fec7703 Add support for embedded-graphics-core 0.3 2021-05-12 17:01:59 -05:00
Grant Miller 47d4a07469 Move mod graphics to top and delete extern crate 2021-05-12 15:37:04 -05:00
Grant Miller c93637abfd Rename draw methods
- Rename `draw_iter` to `draw_raw_iter` to avoid conflict with
`embedded_graphics_core::DrawTarget::draw_iter`

- Rename `draw_raw` to `draw_raw_slice` for consistency
2021-05-12 15:33:38 -05:00
Yuri Iozzelli 510d0c9a03
Merge pull request #25 from GrantM11235/display-error
Use `display_interface::DisplayError`
2021-05-11 20:24:46 +02:00
Grant Miller 535feb236c Loosen trait bounds 2021-05-11 11:53:39 -05:00
Grant Miller be44c24eea Use Result alias 2021-05-11 11:53:39 -05:00
Grant Miller 954d111959 Use display_interface::DisplayError 2021-05-11 11:50:51 -05:00
Yuri Iozzelli d9f35775d6
Merge pull request #24 from GrantM11235/remove-old-interface-trait
Remove old `Interface` trait
2021-05-11 09:17:57 +02:00
Yuri Iozzelli a68c0530ec
Merge pull request #23 from GrantM11235/spi-mode
Move `::spi::MODE` to `::SPI_MODE`
2021-05-11 09:17:11 +02:00
Grant Miller ea9cb59bff Remove old Interface trait 2021-05-10 16:02:56 -05:00
Grant Miller 8884885ca0 Move ::spi::MODE to ::SPI_MODE
We don't need a whole module for just one const
2021-05-10 15:57:26 -05:00
Yuri Iozzelli 2b7ee112fb
Merge pull request #22 from almusil/fix_scrolling
Fix underflow in scrolling
2021-01-21 16:51:51 +01:00
Ales Musil e350936d82 Fix underflow in scrolling
When window was smaller than whole display
scrolling by small step could cause underflow
in the offset computation.
2021-01-21 13:20:30 +01:00
Yuri Iozzelli 509060bb5d
Merge pull request #21 from almusil/do_not_hardcode_size
Do not hardcode display size
2020-11-07 12:46:48 +01:00
Ales Musil b5077900be Do not hardcode display size
ili driver family is pretty generic with support
of multiple display sizes. By having width and size
as parameter this driver can support broader range
of ili displays.
2020-10-30 08:28:47 +01:00
Yuri Iozzelli 64963398c7 Bump version to 0.4.1 2020-10-25 09:51:07 +01:00
Yuri Iozzelli 6adc0191d1
Update readme with scrolling feature 2020-10-21 18:41:37 +02:00
Yuri Iozzelli 81377dfddf
Merge pull request #20 from twitchyliquid64/master
Implement hardware vertical scroll
2020-10-21 18:40:45 +02:00
Tom afbfd98f9d Implement hardware vertical scroll 2020-10-17 00:36:58 -07:00
Yuri Iozzelli e45feea807 Bump version to 0.4.0 2020-09-01 22:19:08 +02:00
Yuri Iozzelli d4afd398f5 Update dependencies 2020-09-01 22:18:05 +02:00
Yuri Iozzelli eff270ab2d Merge branch 'GrantM11235-init-cleanup' 2020-09-01 21:43:05 +02:00
Grant Miller 61531412cf Add comment to pixel format set 2020-08-28 15:35:08 -05:00
Grant Miller 427127fe2d Use set_orientation in initialization 2020-08-28 15:35:08 -05:00
Grant Miller 780a5f0c25 Remove gamma set command, 0x01 is the default 2020-08-28 15:35:08 -05:00
Grant Miller bc1282b0ca Remove extended commands 2020-08-28 15:35:08 -05:00
Grant Miller 2342655799 Simplify reset and reduce delays 2020-08-28 15:35:08 -05:00
Yuri Iozzelli 332aa05bc4
Merge pull request #16 from therealprof/display-interface
Rough first conversion to display-interface
2020-06-06 09:55:03 +02:00
Yuri Iozzelli 0b1ba5c7de
Merge pull request #17 from KarlK90/clear
Implement optimized DrawTarget::clear
2020-06-06 09:54:06 +02:00
Stefan Kerkmann ed2b2eeed4 Improve readability 2020-06-05 20:12:33 +02:00
Stefan Kerkmann 8c45de7e90 Implement optimized DrawTarget::clear 2020-06-05 18:07:45 +02:00
Daniel Egger dc9a389b79 Rough first conversion to display-interface
Tested with SPI display

Signed-off-by: Daniel Egger <daniel@eggers-club.de>
2020-06-01 14:30:03 +02:00
Yuri Iozzelli 7f678b7e9f Bump version to 0.3.0 2020-04-15 18:16:14 +02:00
Yuri Iozzelli c6e4e6394c Merge branch 'jamwaffles-upgrade-e-g' 2020-04-15 18:15:32 +02:00
James Waples 7f8892c910 Upgrade e-g to 0.6.1 2020-04-15 13:13:59 +01:00
Yuri Iozzelli 9fd0457aa7 mention embedded-graphics in the readme 2020-03-01 16:51:09 +01:00
Yuri Iozzelli f80f2a3e93 Bump version to 0.3.0-beta.1 2020-03-01 16:46:40 +01:00
Yuri Iozzelli 799dc143b8 cargo fmt 2020-03-01 16:45:59 +01:00
Yuri Iozzelli 651aba971b Move embedded_graphics code in graphics.rs 2020-03-01 16:44:41 +01:00
Yuri Iozzelli 6bdd8e6e37 Add fast rectangle drawing 2020-03-01 16:34:32 +01:00
Yuri Iozzelli fe16080383 Update to embedded_graphics 0.6.0-beta.1 2020-03-01 15:29:25 +01:00
Yuri Iozzelli 080bb714b9
Merge pull request #8 from flaminggoat/parallel_gpio
Parallel gpio
2020-02-12 19:07:17 +01:00
Theo Hussey 5cf5cd7d73 Merge remote-tracking branch 'upstream/master' into parallel_gpio 2020-02-06 23:13:16 +00:00
Theo Hussey 30eb8755a4 added parallel gpio interface 2020-02-06 23:11:33 +00:00
Theo Hussey 97999343fd added parallel gpio interface 2020-02-06 23:10:53 +00:00
Yuri Iozzelli df4331a8d6
Merge pull request #7 from Disasm/generic-interface
Generic interface
2020-02-03 21:55:47 +01:00
Vadim Kaushan c51eaa4ffa
Edition 2018 2020-01-28 20:53:24 +03:00
Vadim Kaushan f958482f98
Shrink row buffer to the previous capacity 2020-01-28 20:50:58 +03:00
Vadim Kaushan 11c4a5ca34
Use u16 for pixel data 2020-01-28 20:35:46 +03:00
Vadim Kaushan 459545a777
Update Error type 2020-01-28 20:27:04 +03:00
Vadim Kaushan 6056b3db22
Fix formatting 2020-01-28 20:03:35 +03:00
Vadim Kaushan d9ab0601b7
Split Ili9341 constructor in two 2020-01-28 20:02:35 +03:00
Vadim Kaushan 5b03a86bb0
Move SPI interface implementation into a separate file 2020-01-28 19:56:12 +03:00
Vadim Kaushan f09403d092
Add Interface trait 2020-01-28 19:45:23 +03:00
Yuri Iozzelli 1956825a8e
Merge pull request #6 from flaminggoat/master
buffer contiguous pixels before sending to dislay for speed boost
2020-01-12 12:13:54 +01:00
Yuri Iozzelli ff62a152af Use exact embedded-graphics version 2020-01-12 12:04:12 +01:00
Yuri Iozzelli 539eeb459e Update embedded-hal version 2020-01-12 12:03:54 +01:00
Theo Hussey 20c22260a6 buffer contiguous pixels before sending to dislay for speed boost 2019-12-19 14:49:16 +00:00
Yuri Iozzelli 26933b663c
Merge pull request #5 from flaminggoat/master
Digital v2 output pin, corrected embedded-graphics endianness
2019-12-19 09:44:47 +01:00
Theo Hussey 363d7a0650 changed to use digital v2 output pin, corrected embedded-graphics endianness 2019-12-19 00:23:12 +00:00
Yuri Iozzelli 490ab33d13
Merge pull request #4 from dflemstr/master
Upgrade to embedded-graphics 0.6.0-alpha.2
2019-12-18 06:31:05 +01:00
David Flemström 6d82f813bc Add color support 2019-12-17 11:06:54 +01:00
David Flemström 905a1c8406 Upgrade to embedded-graphics 0.6.0-alpha.2 2019-12-17 10:55:56 +01:00
5 changed files with 578 additions and 215 deletions

View file

@ -1,21 +1,39 @@
# Copyright 2022 Arm Limited and/or its affiliates <open-source-office@arm.com>
[package]
name = "ili9341"
version = "0.2.0"
version = "0.6.0"
description = "A platform agnostic driver to interface with the ILI9341 (ald ILI9340C) TFT LCD display"
authors = ["Yuri Iozzelli <y.iozzelli@gmail.com>"]
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]
embedded-hal = "0.2.2"
display-interface = "0.5"
embedded-hal = "1.0.0"
[dependencies.embedded-graphics]
[dependencies.embedded-graphics-core]
optional = true
version = "0.4.7"
version = "0.4"
[dev-dependencies]
cortex-m-rtic = "1.0.0"
cortex-m = "0.7.3"
cortex-m-rt = "0.7.0"
defmt-rtt = "0.3.0"
panic-semihosting = "0.6"
[dev-dependencies.stm32f4xx-hal]
version = "0.12.0"
features = ["stm32f411"]
[features]
default = ["graphics"]
graphics = ["embedded-graphics"]
graphics = ["embedded-graphics-core"]
[[example]]
name = "rtic"

View file

@ -1,5 +1,9 @@
# `ili9341`
[![Crates.io](https://img.shields.io/crates/d/ili9341.svg)](https://crates.io/crates/ili9341)
[![Crates.io](https://img.shields.io/crates/v/ili9341.svg)](https://crates.io/crates/ili9341)
[![Released API docs](https://docs.rs/ili9341/badge.svg)](https://docs.rs/ili9341)
> A platform agnostic driver to interface with the ILI9341 (and ILI9340C) TFT
> LCD display
@ -7,11 +11,12 @@
- Putting pixels on the screen
- Change the screen orientation
- Hardware scrolling
- Compatible with [embedded-graphics](https://docs.rs/embedded-graphics)
## TODO
- [ ] Expose more configuration options
- [ ] Scrolling
- [ ] Read video memory
- [ ] DMA API
- ???

109
examples/rtic.rs Normal file
View file

@ -0,0 +1,109 @@
//! cortex-m-rtic example
//! Tested on BlackPill dev board with stm32f411ceu microcontroller
//! The LCD RESET pin was hard puled to Vcc therefore
//! DummyOutputPin was used as the reset pin
#![no_main]
#![no_std]
#[rtic::app(device = stm32f4xx_hal::pac)]
mod app {
use display_interface_spi::SPIInterface;
use embedded_graphics::{
mono_font::{ascii::FONT_6X10, MonoTextStyle},
pixelcolor::Rgb565,
prelude::*,
text::{Alignment, Text},
};
use embedded_hal::digital::{blocking::OutputPin, ErrorType, PinState};
use ili9341::{DisplaySize240x320, Ili9341, Orientation};
use stm32f4xx_hal::{
prelude::*,
spi::{Mode, NoMiso, Phase, Polarity},
timer::Channel,
};
#[derive(Default)]
pub struct DummyOutputPin;
impl ErrorType for DummyOutputPin {
type Error = ();
}
impl OutputPin for DummyOutputPin {
fn set_low(&mut self) -> Result<(), Self::Error> {
Ok(())
}
fn set_high(&mut self) -> Result<(), Self::Error> {
Ok(())
}
fn set_state(&mut self, _state: PinState) -> Result<(), Self::Error> {
Ok(())
}
}
#[shared]
struct Shared {}
#[local]
struct Local {}
#[init]
fn init(ctx: init::Context) -> (Shared, Local, init::Monotonics) {
let dp = ctx.device;
let rcc = dp.RCC.constrain();
let clocks = rcc.cfgr.use_hse(25.MHz()).sysclk(100.MHz()).freeze();
let gpioa = dp.GPIOA.split();
let gpiob = dp.GPIOB.split();
/*
* The ILI9341 driver
*/
let lcd_clk = gpiob.pb0.into_alternate();
let lcd_miso = NoMiso {};
let lcd_mosi = gpioa.pa10.into_alternate().internal_pull_up(true);
let lcd_dc = gpiob.pb1.into_push_pull_output();
let lcd_cs = gpiob.pb2.into_push_pull_output();
let mode = Mode {
polarity: Polarity::IdleLow,
phase: Phase::CaptureOnFirstTransition,
};
let lcd_spi = dp
.SPI5
.spi((lcd_clk, lcd_miso, lcd_mosi), mode, 2.MHz(), &clocks);
let spi_iface = SPIInterface::new(lcd_spi, lcd_dc, lcd_cs);
let dummy_reset = DummyOutputPin::default();
let mut delay = dp.TIM1.delay_us(&clocks);
let mut lcd = Ili9341::new(
spi_iface,
dummy_reset,
&mut delay,
Orientation::PortraitFlipped,
DisplaySize240x320,
)
.unwrap();
// Create a new character style
let style = MonoTextStyle::new(&FONT_6X10, Rgb565::RED);
// Create a text at position (20, 30) and draw it using the previously defined style
Text::with_alignment(
"First line\nSecond line",
Point::new(20, 30),
style,
Alignment::Center,
)
.draw(&mut lcd)
.unwrap();
(Shared {}, Local {}, init::Monotonics())
}
#[idle(local = [])]
fn idle(cx: idle::Context) -> ! {
loop {
cortex_m::asm::nop();
}
}
}

82
src/graphics_core.rs Normal file
View file

@ -0,0 +1,82 @@
use crate::Ili9341;
use embedded_graphics_core::{
pixelcolor::{raw::RawU16, Rgb565},
prelude::*,
primitives::Rectangle,
};
impl<IFACE, RESET> OriginDimensions for Ili9341<IFACE, RESET> {
fn size(&self) -> Size {
Size::new(self.width() as u32, self.height() as u32)
}
}
impl<IFACE, RESET> DrawTarget for Ili9341<IFACE, RESET>
where
IFACE: display_interface::WriteOnlyDataCommand,
{
type Error = display_interface::DisplayError;
type Color = Rgb565;
fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
where
I: IntoIterator<Item = Pixel<Self::Color>>,
{
for Pixel(point, color) in pixels {
if self.bounding_box().contains(point) {
let x = point.x as u16;
let y = point.y as u16;
let color = RawU16::from(color).into_inner();
self.draw_raw_slice(x, y, x, y, &[color])?;
}
}
Ok(())
}
fn fill_contiguous<I>(&mut self, area: &Rectangle, colors: I) -> Result<(), Self::Error>
where
I: IntoIterator<Item = Self::Color>,
{
let drawable_area = area.intersection(&self.bounding_box());
if let Some(drawable_bottom_right) = drawable_area.bottom_right() {
let x0 = drawable_area.top_left.x as u16;
let y0 = drawable_area.top_left.y as u16;
let x1 = drawable_bottom_right.x as u16;
let y1 = drawable_bottom_right.y as u16;
if area == &drawable_area {
// All pixels are on screen
self.draw_raw_iter(
x0,
y0,
x1,
y1,
area.points()
.zip(colors)
.map(|(_, color)| RawU16::from(color).into_inner()),
)
} else {
// Some pixels are on screen
self.draw_raw_iter(
x0,
y0,
x1,
y1,
area.points()
.zip(colors)
.filter(|(point, _)| drawable_area.contains(*point))
.map(|(_, color)| RawU16::from(color).into_inner()),
)
}
} else {
// No pixels are on screen
Ok(())
}
}
fn clear(&mut self, color: Self::Color) -> Result<(), Self::Error> {
self.clear_screen(RawU16::from(color).into_inner())
}
}

View file

@ -1,33 +1,82 @@
#![no_std]
extern crate embedded_hal as hal;
//! ILI9341 Display Driver
//!
//! ### Usage
//!
//! To control the display you need to set up:
//!
//! * Interface for communicating with display ([display-interface-spi crate] for SPI)
//! * Configuration (reset pin, delay, orientation and size) for display
//!
//! ```ignore
//! let iface = SPIInterface::new(spi, dc, cs);
//!
//! let mut display = Ili9341::new(
//! iface,
//! reset_gpio,
//! &mut delay,
//! Orientation::Landscape,
//! ili9341::DisplaySize240x320,
//! )
//! .unwrap();
//!
//! display.clear(Rgb565::RED).unwrap()
//! ```
//!
//! [display-interface-spi crate]: https://crates.io/crates/display-interface-spi
use embedded_hal::delay::DelayNs;
use embedded_hal::digital::OutputPin;
use display_interface::DataFormat;
use display_interface::WriteOnlyDataCommand;
#[cfg(feature = "graphics")]
extern crate embedded_graphics;
mod graphics_core;
use hal::blocking::spi;
use hal::blocking::delay::DelayMs;
use hal::spi::{Mode, Phase, Polarity};
use hal::digital::OutputPin;
pub use embedded_hal::spi::MODE_0 as SPI_MODE;
use core::iter::IntoIterator;
use core::fmt::Debug;
pub use display_interface::DisplayError;
/// SPI mode
pub const MODE: Mode = Mode {
polarity: Polarity::IdleLow,
phase: Phase::CaptureOnFirstTransition,
};
type Result<T = (), E = DisplayError> = core::result::Result<T, E>;
const WIDTH: usize = 240;
const HEIGHT: usize = 320;
#[derive(Debug)]
pub enum Error<E> {
Spi(E),
/// Trait that defines display size information
pub trait DisplaySize {
/// Width in pixels
const WIDTH: usize;
/// Height in pixels
const HEIGHT: usize;
}
/// The default orientation is Portrait
/// Generic display size of 240x320 pixels
pub struct DisplaySize240x320;
impl DisplaySize for DisplaySize240x320 {
const WIDTH: usize = 240;
const HEIGHT: usize = 320;
}
/// Generic display size of 320x480 pixels
pub struct DisplaySize320x480;
impl DisplaySize for DisplaySize320x480 {
const WIDTH: usize = 320;
const HEIGHT: usize = 480;
}
/// For quite a few boards (ESP32-S2-Kaluga-1, M5Stack, M5Core2 and others),
/// the ILI9341 initialization command arguments are slightly different
///
/// This trait provides the flexibility for users to define their own
/// initialization command arguments suitable for the particular board they are using
pub trait Mode {
fn mode(&self) -> u8;
fn is_landscape(&self) -> bool;
}
/// The default implementation of the Mode trait from above
/// Should work for most (but not all) boards
pub enum Orientation {
Portrait,
PortraitFlipped,
@ -35,9 +84,32 @@ pub enum Orientation {
LandscapeFlipped,
}
impl Mode for Orientation {
fn mode(&self) -> u8 {
match self {
Self::Portrait => 0x40 | 0x08,
Self::Landscape => 0x20 | 0x08,
Self::PortraitFlipped => 0x80 | 0x08,
Self::LandscapeFlipped => 0x40 | 0x80 | 0x20 | 0x08,
}
}
fn is_landscape(&self) -> bool {
match self {
Self::Landscape | Self::LandscapeFlipped => true,
Self::Portrait | Self::PortraitFlipped => false,
}
}
}
/// Specify state of specific mode of operation
pub enum ModeState {
On,
Off,
}
/// There are two method for drawing to the screen:
/// [draw_raw](struct.Ili9341.html#method.draw_raw) and
/// [draw_iter](struct.Ili9341.html#method.draw_iter).
/// [Ili9341::draw_raw_iter] and [Ili9341::draw_raw_slice]
///
/// In both cases the expected pixel format is rgb565.
///
@ -51,135 +123,96 @@ 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<SPI, CS, DC, RESET> {
spi: SPI,
cs: CS,
dc: DC,
pub struct Ili9341<IFACE, RESET> {
interface: IFACE,
reset: RESET,
width: usize,
height: usize,
landscape: bool,
}
impl<E, SPI, CS, DC, RESET> Ili9341<SPI, CS, DC, RESET>
impl<IFACE, RESET> Ili9341<IFACE, RESET>
where
SPI: spi::Transfer<u8, Error = E> + spi::Write<u8, Error = E>,
CS: OutputPin,
DC: OutputPin,
IFACE: WriteOnlyDataCommand,
RESET: OutputPin,
{
pub fn new<DELAY: DelayMs<u16>>(
spi: SPI,
cs: CS,
dc: DC,
pub fn new<DELAY, SIZE, MODE>(
interface: IFACE,
reset: RESET,
delay: &mut DELAY,
) -> Result<Self, Error<E>> {
mode: MODE,
_display_size: SIZE,
) -> Result<Self>
where
DELAY: DelayNs,
SIZE: DisplaySize,
MODE: Mode,
{
let mut ili9341 = Ili9341 {
spi,
cs,
dc,
interface,
reset,
width: WIDTH,
height: HEIGHT,
width: SIZE::WIDTH,
height: SIZE::HEIGHT,
landscape: false,
};
ili9341.hard_reset(delay);
ili9341.command(Command::SoftwareReset, &[])?;
delay.delay_ms(200);
// Do hardware reset by holding reset low for at least 10us
ili9341.reset.set_low().map_err(|_| DisplayError::RSError)?;
let _ = delay.delay_ms(1);
// Set high for normal operation
ili9341
.reset
.set_high()
.map_err(|_| DisplayError::RSError)?;
ili9341.command(Command::PowerControlA, &[0x39, 0x2c, 0x00, 0x34, 0x02])?;
ili9341.command(Command::PowerControlB, &[0x00, 0xc1, 0x30])?;
ili9341.command(Command::DriverTimingControlA, &[0x85, 0x00, 0x78])?;
ili9341.command(Command::DriverTimingControlB, &[0x00, 0x00])?;
ili9341.command(Command::PowerOnSequenceControl, &[0x64, 0x03, 0x12, 0x81])?;
ili9341.command(Command::PumpRatioControl, &[0x20])?;
ili9341.command(Command::PowerControl1, &[0x23])?;
ili9341.command(Command::PowerControl2, &[0x10])?;
ili9341.command(Command::VCOMControl1, &[0x3e, 0x28])?;
ili9341.command(Command::VCOMControl2, &[0x86])?;
ili9341.command(Command::MemoryAccessControl, &[0x48])?;
// Wait 5ms after reset before sending commands
// and 120ms before sending Sleep Out
let _ = delay.delay_ms(5);
// Do software reset
ili9341.command(Command::SoftwareReset, &[])?;
// Wait 5ms after reset before sending commands
// and 120ms before sending Sleep Out
let _ = delay.delay_ms(120);
ili9341.set_orientation(mode)?;
// Set pixel format to 16 bits per pixel
ili9341.command(Command::PixelFormatSet, &[0x55])?;
ili9341.command(Command::FrameControlNormal, &[0x00, 0x18])?;
ili9341.command(Command::DisplayFunctionControl, &[0x08, 0x82, 0x27])?;
ili9341.command(Command::Enable3G, &[0x00])?;
ili9341.command(Command::GammaSet, &[0x01])?;
ili9341.command(
Command::PositiveGammaCorrection,
&[
0x0f, 0x31, 0x2b, 0x0c, 0x0e, 0x08, 0x4e, 0xf1, 0x37, 0x07, 0x10, 0x03, 0x0e, 0x09,
0x00,
],
)?;
ili9341.command(
Command::NegativeGammaCorrection,
&[
0x00, 0x0e, 0x14, 0x03, 0x11, 0x07, 0x31, 0xc1, 0x48, 0x08, 0x0f, 0x0c, 0x31, 0x36,
0x0f,
],
)?;
ili9341.command(Command::SleepOut, &[])?;
delay.delay_ms(120);
ili9341.command(Command::DisplayOn, &[])?;
ili9341.sleep_mode(ModeState::Off)?;
// Wait 5ms after Sleep Out before sending commands
let _ = delay.delay_ms(5);
ili9341.display_mode(ModeState::On)?;
Ok(ili9341)
}
}
fn hard_reset<DELAY: DelayMs<u16>>(&mut self, delay: &mut DELAY) {
// set high if previously low
self.reset.set_high();
delay.delay_ms(200);
// set low for reset
self.reset.set_low();
delay.delay_ms(200);
// set high for normal operation
self.reset.set_high();
delay.delay_ms(200);
impl<IFACE, RESET> Ili9341<IFACE, RESET>
where
IFACE: WriteOnlyDataCommand,
{
fn command(&mut self, cmd: Command, args: &[u8]) -> Result {
self.interface.send_commands(DataFormat::U8(&[cmd as u8]))?;
self.interface.send_data(DataFormat::U8(args))
}
fn command(&mut self, cmd: Command, args: &[u8]) -> Result<(), Error<E>> {
self.cs.set_low();
self.dc.set_low();
self.spi.write(&[cmd as u8]).map_err(Error::Spi)?;
self.dc.set_high();
self.spi.write(args).map_err(Error::Spi)?;
self.cs.set_high();
Ok(())
fn write_iter<I: IntoIterator<Item = u16>>(&mut self, data: I) -> Result {
self.command(Command::MemoryWrite, &[])?;
use DataFormat::U16BEIter;
self.interface.send_data(U16BEIter(&mut data.into_iter()))
}
fn write_iter<I: IntoIterator<Item = u16>>(&mut self, data: I) -> Result<(), Error<E>> {
self.cs.set_low();
self.dc.set_low();
self.spi
.write(&[Command::MemoryWrite as u8])
.map_err(Error::Spi)?;
self.dc.set_high();
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();
Ok(())
fn write_slice(&mut self, data: &[u16]) -> Result {
self.command(Command::MemoryWrite, &[])?;
self.interface.send_data(DataFormat::U16(data))
}
fn write_raw(&mut self, data: &[u8]) -> Result<(), Error<E>> {
self.cs.set_low();
self.dc.set_low();
self.spi
.write(&[Command::MemoryWrite as u8])
.map_err(Error::Spi)?;
self.dc.set_high();
self.spi.write(data).map_err(Error::Spi)?;
self.cs.set_high();
Ok(())
}
fn set_window(&mut self, x0: u16, y0: u16, x1: u16, y1: u16) -> Result<(), Error<E>> {
fn set_window(&mut self, x0: u16, y0: u16, x1: u16, y1: u16) -> Result {
self.command(
Command::ColumnAddressSet,
&[
@ -197,9 +230,53 @@ where
(y1 >> 8) as u8,
(y1 & 0xff) as u8,
],
)?;
Ok(())
)
}
/// Configures the screen for hardware-accelerated vertical scrolling.
pub fn configure_vertical_scroll(
&mut self,
fixed_top_lines: u16,
fixed_bottom_lines: u16,
) -> Result<Scroller> {
let height = if self.landscape {
self.width
} else {
self.height
} as u16;
let scroll_lines = height as u16 - fixed_top_lines - fixed_bottom_lines;
self.command(
Command::VerticalScrollDefine,
&[
(fixed_top_lines >> 8) as u8,
(fixed_top_lines & 0xff) as u8,
(scroll_lines >> 8) as u8,
(scroll_lines & 0xff) as u8,
(fixed_bottom_lines >> 8) as u8,
(fixed_bottom_lines & 0xff) as u8,
],
)?;
Ok(Scroller::new(fixed_top_lines, fixed_bottom_lines, height))
}
pub fn scroll_vertically(&mut self, scroller: &mut Scroller, num_lines: u16) -> Result {
scroller.top_offset += num_lines;
if scroller.top_offset > (scroller.height - scroller.fixed_bottom_lines) {
scroller.top_offset = scroller.fixed_top_lines
+ (scroller.top_offset + scroller.fixed_bottom_lines - scroller.height)
}
self.command(
Command::VerticalScrollAddr,
&[
(scroller.top_offset >> 8) as u8,
(scroller.top_offset & 0xff) as u8,
],
)
}
/// Draw a rectangle on the screen, represented by top-left corner (x0, y0)
/// and bottom-right corner (x1, y1).
///
@ -209,132 +286,204 @@ where
///
/// The iterator is useful to avoid wasting memory by holding a buffer for
/// the whole screen when it is not necessary.
pub fn draw_iter<I: IntoIterator<Item = u16>>(
pub fn draw_raw_iter<I: IntoIterator<Item = u16>>(
&mut self,
x0: u16,
y0: u16,
x1: u16,
y1: u16,
data: I,
) -> Result<(), Error<E>> {
) -> Result {
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).
///
/// 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.
pub fn draw_raw(
&mut self,
x0: u16,
y0: u16,
x1: u16,
y1: u16,
data: &[u8],
) -> Result<(), Error<E>> {
/// The expected format is rgb565.
pub fn draw_raw_slice(&mut self, x0: u16, y0: u16, x1: u16, y1: u16, data: &[u16]) -> Result {
self.set_window(x0, y0, x1, y1)?;
self.write_raw(data)
self.write_slice(data)
}
/// Change the orientation of the screen
pub fn set_orientation(&mut self, mode: Orientation) -> Result<(), Error<E>> {
pub fn set_orientation<MODE>(&mut self, mode: MODE) -> Result
where
MODE: Mode,
{
self.command(Command::MemoryAccessControl, &[mode.mode()])?;
if self.landscape ^ mode.is_landscape() {
core::mem::swap(&mut self.height, &mut self.width);
}
self.landscape = mode.is_landscape();
Ok(())
}
/// Fill entire screen with specfied color u16 value
pub fn clear_screen(&mut self, color: u16) -> Result {
let color = core::iter::repeat(color).take(self.width * self.height);
self.draw_raw_iter(0, 0, self.width as u16, self.height as u16, color)
}
/// Control the screen sleep mode:
pub fn sleep_mode(&mut self, mode: ModeState) -> Result {
match mode {
Orientation::Portrait => {
self.width = WIDTH;
self.height = HEIGHT;
self.command(Command::MemoryAccessControl, &[0x40 | 0x08])
}
Orientation::Landscape => {
self.width = HEIGHT;
self.height = WIDTH;
self.command(Command::MemoryAccessControl, &[0x20 | 0x08])
}
Orientation::PortraitFlipped => {
self.width = WIDTH;
self.height = HEIGHT;
self.command(Command::MemoryAccessControl, &[0x80 | 0x08])
}
Orientation::LandscapeFlipped => {
self.width = HEIGHT;
self.height = WIDTH;
self.command(Command::MemoryAccessControl, &[0x40 | 0x80 | 0x20 | 0x08])
}
ModeState::On => self.command(Command::SleepModeOn, &[]),
ModeState::Off => self.command(Command::SleepModeOff, &[]),
}
}
/// Control the screen display mode
pub fn display_mode(&mut self, mode: ModeState) -> Result {
match mode {
ModeState::On => self.command(Command::DisplayOn, &[]),
ModeState::Off => self.command(Command::DisplayOff, &[]),
}
}
/// Invert the pixel color on screen
pub fn invert_mode(&mut self, mode: ModeState) -> Result {
match mode {
ModeState::On => self.command(Command::InvertOn, &[]),
ModeState::Off => self.command(Command::InvertOff, &[]),
}
}
/// Idle mode reduces the number of colors to 8
pub fn idle_mode(&mut self, mode: ModeState) -> Result {
match mode {
ModeState::On => self.command(Command::IdleModeOn, &[]),
ModeState::Off => self.command(Command::IdleModeOff, &[]),
}
}
/// Set display brightness to the value between 0 and 255
pub fn brightness(&mut self, brightness: u8) -> Result {
self.command(Command::SetBrightness, &[brightness])
}
/// Set adaptive brightness value equal to [AdaptiveBrightness]
pub fn content_adaptive_brightness(&mut self, value: AdaptiveBrightness) -> Result {
self.command(Command::ContentAdaptiveBrightness, &[value as _])
}
/// Configure [FrameRateClockDivision] and [FrameRate] in normal mode
pub fn normal_mode_frame_rate(
&mut self,
clk_div: FrameRateClockDivision,
frame_rate: FrameRate,
) -> Result {
self.command(
Command::NormalModeFrameRate,
&[clk_div as _, frame_rate as _],
)
}
/// Configure [FrameRateClockDivision] and [FrameRate] in idle mode
pub fn idle_mode_frame_rate(
&mut self,
clk_div: FrameRateClockDivision,
frame_rate: FrameRate,
) -> Result {
self.command(Command::IdleModeFrameRate, &[clk_div as _, frame_rate as _])
}
}
impl<IFACE, RESET> Ili9341<IFACE, RESET> {
/// 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
}
}
#[cfg(feature = "graphics")]
use embedded_graphics::drawable;
#[cfg(feature = "graphics")]
use embedded_graphics::{drawable::Pixel, pixelcolor::PixelColorU16, Drawing};
/// Scroller must be provided in order to scroll the screen. It can only be obtained
/// by configuring the screen for scrolling.
pub struct Scroller {
top_offset: u16,
fixed_bottom_lines: u16,
fixed_top_lines: u16,
height: u16,
}
#[cfg(feature = "graphics")]
impl<E, SPI, CS, DC, RESET> Drawing<PixelColorU16> for Ili9341<SPI, CS, DC, RESET>
where
SPI: spi::Transfer<u8, Error = E> + spi::Write<u8, Error = E>,
CS: OutputPin,
DC: OutputPin,
RESET: OutputPin,
E: Debug,
{
fn draw<T>(&mut self, item_pixels: T)
where
T: Iterator<Item = drawable::Pixel<PixelColorU16>>,
{
for Pixel(pos, color) in item_pixels {
self.draw_raw(
pos.0 as u16,
pos.1 as u16,
pos.0 as u16,
pos.1 as u16,
if color == PixelColorU16(0) {
&[0xff, 0xff]
} else {
&[0, 0]
},
)
.expect("Failed to communicate with device");
impl Scroller {
fn new(fixed_top_lines: u16, fixed_bottom_lines: u16, height: u16) -> Scroller {
Scroller {
top_offset: fixed_top_lines,
fixed_top_lines,
fixed_bottom_lines,
height,
}
}
}
/// Available Adaptive Brightness values
pub enum AdaptiveBrightness {
Off = 0x00,
UserInterfaceImage = 0x01,
StillPicture = 0x02,
MovingImage = 0x03,
}
/// Available frame rate in Hz
pub enum FrameRate {
FrameRate119 = 0x10,
FrameRate112 = 0x11,
FrameRate106 = 0x12,
FrameRate100 = 0x13,
FrameRate95 = 0x14,
FrameRate90 = 0x15,
FrameRate86 = 0x16,
FrameRate83 = 0x17,
FrameRate79 = 0x18,
FrameRate76 = 0x19,
FrameRate73 = 0x1a,
FrameRate70 = 0x1b,
FrameRate68 = 0x1c,
FrameRate65 = 0x1d,
FrameRate63 = 0x1e,
FrameRate61 = 0x1f,
}
/// Frame rate clock division
pub enum FrameRateClockDivision {
Fosc = 0x00,
FoscDiv2 = 0x01,
FoscDiv4 = 0x02,
FoscDiv8 = 0x03,
}
#[derive(Clone, Copy)]
enum Command {
SoftwareReset = 0x01,
PowerControlA = 0xcb,
PowerControlB = 0xcf,
DriverTimingControlA = 0xe8,
DriverTimingControlB = 0xea,
PowerOnSequenceControl = 0xed,
PumpRatioControl = 0xf7,
PowerControl1 = 0xc0,
PowerControl2 = 0xc1,
VCOMControl1 = 0xc5,
VCOMControl2 = 0xc7,
MemoryAccessControl = 0x36,
PixelFormatSet = 0x3a,
FrameControlNormal = 0xb1,
DisplayFunctionControl = 0xb6,
Enable3G = 0xf2,
GammaSet = 0x26,
PositiveGammaCorrection = 0xe0,
NegativeGammaCorrection = 0xe1,
SleepOut = 0x11,
SleepModeOn = 0x10,
SleepModeOff = 0x11,
InvertOff = 0x20,
InvertOn = 0x21,
DisplayOff = 0x28,
DisplayOn = 0x29,
ColumnAddressSet = 0x2a,
PageAddressSet = 0x2b,
MemoryWrite = 0x2c,
VerticalScrollDefine = 0x33,
VerticalScrollAddr = 0x37,
IdleModeOff = 0x38,
IdleModeOn = 0x39,
SetBrightness = 0x51,
ContentAdaptiveBrightness = 0x55,
NormalModeFrameRate = 0xb1,
IdleModeFrameRate = 0xb2,
}