From b1e9960e10ad6553b11a15f3a5cff9539486eb07 Mon Sep 17 00:00:00 2001 From: Kevin Hamacher Date: Wed, 17 Oct 2018 18:38:14 +0200 Subject: [PATCH] Initial commit --- Cargo.toml | 14 +++ src/lib.rs | 312 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 326 insertions(+) create mode 100644 Cargo.toml create mode 100644 src/lib.rs diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..fea372b --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "ili9225" +version = "0.1.0" +authors = ["Kevin Hamacher "] + +[dependencies] +embedded-graphics = { version = "0.4.0", optional = true } + +[dependencies.embedded-hal] +version = "0.2.1" + +[features] +default = ["graphics"] +graphics = ["embedded-graphics"] diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..d68ac22 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,312 @@ +#![no_std] +extern crate embedded_hal as hal; + +use hal::digital::OutputPin; + +pub const WIDTH: usize = 176; +pub const HEIGHT: usize = 220; + +enum Register { + DriverOutputControl = 0x01, // Driver Output Control + LcdACDrivingControl = 0x02, // LCD AC Driving Control + EntryMode = 0x03, // Entry Mode + DisplayControl1 = 0x07, // Display Control 1 + BlankPeriodControl = 0x08, // Blank Period Control + FrameCycleControl = 0x0B, // Frame Cycle Control + InterfaceControl = 0x0C, // Interface Control + OscControl = 0x0F, // Osc Control + PowerControl1 = 0x10, // Power Control 1 + PowerControl2 = 0x11, // Power Control 2 + PowerControl3 = 0x12, // Power Control 3 + PowerControl4 = 0x13, // Power Control 4 + PowerControl5 = 0x14, // Power Control 5 + VciRecycling = 0x15, // VCI Recycling + RamAddressSet1 = 0x20, // Horizontal GRAM Address Set + RamAddressSet2 = 0x21, // Vertical GRAM Address Set + GramData = 0x22, // GRAM Data Register + GateScanControl = 0x30, // Gate Scan Control Register + VerticalScrollControl1 = 0x31, // Vertical Scroll Control 1 Register + VerticalScrollControl2 = 0x32, // Vertical Scroll Control 2 Register + VerticalScrollControl3 = 0x33, // Vertical Scroll Control 3 Register + PartialDrivingPosition1 = 0x34, // Partial Driving Position 1 Register + PartialDrivingPosition2 = 0x35, // Partial Driving Position 2 Register + HorizontalWindowStart = 0x36, // Horizontal Address Start Position + HorizontalWindowEnd = 0x37, // Horizontal Address End Position + VerticalWindowStart = 0x38, // Vertical Address Start Position + VerticalWindowEnd = 0x39, // Vertical Address End Position + GammaControl1 = 0x50, // Gamma Control 1 + GammaControl2 = 0x51, // Gamma Control 2 + GammaControl3 = 0x52, // Gamma Control 3 + GammaControl4 = 0x53, // Gamma Control 4 + GammaControl5 = 0x54, // Gamma Control 5 + GammaControl6 = 0x55, // Gamma Control 6 + GammaControl7 = 0x56, // Gamma Control 7 + GammaControl8 = 0x57, // Gamma Control 8 + GammaControl9 = 0x58, // Gamma Control 9 + GammaControl10 = 0x59, // Gamma Control 10 + + // TODO + UnknownTodo = 0x00FF, +} + +const InvertOnCommand: u16 = 0x20; +const InvertOffCommand: u16 = 0x21; + +/* +// autoincrement modes (register ILI9225_ENTRY_MODE, bit 5..3 ) + +enum autoIncMode_t { R2L_BottomUp, BottomUp_R2L, L2R_BottomUp, BottomUp_L2R, R2L_TopDown, TopDown_R2L, L2R_TopDown, TopDown_L2R }; +*/ + +pub enum Orientation { + Portrait = 0, + Landscape = 1, + InvertedPortrait = 2, + InvertedLandscape = 3, +} + +pub fn to_rgb565(r: u8, g: u8, b: u8) -> u16 { + (((r >> 3) as u16) << 11) | (((g >> 2) as u16) << 5) | (b >> 3) as u16 +} + +// TODO: Do we need a CS? +pub struct ILI9225 { + spi: SPI, + dc: DC, + reset: RESET, + delay: DELAY, +} + +impl ILI9225 +where + SPI: hal::blocking::spi::Write, + DC: OutputPin, + RESET: OutputPin, + DELAY: hal::blocking::delay::DelayMs, +{ + pub fn new(spi: SPI, dc: DC, reset: RESET, delay: DELAY) -> Self { + Self { + spi, + dc, + reset, + delay, + } + } + + fn write_command8(&mut self, cmd: u8) { + self.dc.set_low(); + //CS? + self.spi.write(&[cmd]).ok(); // Ignore result (TODO) + } + + fn write_command16(&mut self, cmd: u16) { + self.write_command8((cmd >> 8) as u8); + self.write_command8(cmd as u8); + } + + fn write_data8(&mut self, val: u8) { + self.dc.set_high(); + //CS? + self.spi.write(&[val]).ok(); // Ignore result (TODO) + } + + fn write_data16(&mut self, val: u16) { + self.write_data8((val >> 8) as u8); + self.write_data8(val as u8); + } + + fn write_register(&mut self, reg: Register, value: u16) { + self.write_command16(reg as u16); + self.write_data16(value); + } + + pub fn init(&mut self) { + // Reset device. + self.reset.set_low(); + self.delay.delay_ms(50); + self.reset.set_high(); + self.delay.delay_ms(50); + + // Assume device is ready now. + // startWrite(); + // Reset power control before power-on. + self.write_register(Register::PowerControl1, 0); + self.write_register(Register::PowerControl2, 0); + self.write_register(Register::PowerControl3, 0); + self.write_register(Register::PowerControl4, 0); + self.write_register(Register::PowerControl5, 0); + + //endWrite(), repeat for below? + self.delay.delay_ms(40); + self.write_register(Register::PowerControl2, 0x0018); + self.write_register(Register::PowerControl3, 0x6121); + self.write_register(Register::PowerControl4, 0x006F); + self.write_register(Register::PowerControl5, 0x495F); + self.write_register(Register::PowerControl1, 0x0800); + self.delay.delay_ms(10); + + self.write_register(Register::PowerControl2, 0x103B); + + self.delay.delay_ms(50); + self.write_register(Register::DriverOutputControl, 0x011C); // set the display line number and display direction + self.write_register(Register::LcdACDrivingControl, 0x0100); // set 1 line inversion + self.write_register(Register::EntryMode, 0x1038); // set GRAM write direction and BGR=1. + self.write_register(Register::DisplayControl1, 0x0000); // Display off + self.write_register(Register::BlankPeriodControl, 0x0808); // set the back porch and front porch + self.write_register(Register::FrameCycleControl, 0x1100); // set the clocks number per line + self.write_register(Register::InterfaceControl, 0x0000); // CPU interface + self.write_register(Register::OscControl, 0x0D01); // Set Osc /*0e01*/ + self.write_register(Register::VciRecycling, 0x0020); // Set VCI recycling + self.write_register(Register::RamAddressSet1, 0x0000); // RAM Address + self.write_register(Register::RamAddressSet2, 0x0000); // RAM Address + + // Set GRam stuff. + self.write_register(Register::GateScanControl, 0x0000); + self.write_register(Register::VerticalScrollControl1, 0x00DB); + self.write_register(Register::VerticalScrollControl2, 0x0000); + self.write_register(Register::VerticalScrollControl3, 0x0000); + self.write_register(Register::PartialDrivingPosition1, 0x00DB); + self.write_register(Register::PartialDrivingPosition2, 0x0000); + self.write_register(Register::HorizontalWindowStart, 0x00AF); + self.write_register(Register::HorizontalWindowEnd, 0x0000); + self.write_register(Register::VerticalWindowStart, 0x00DB); + self.write_register(Register::VerticalWindowEnd, 0x0000); + + // Gamma curve. + self.write_register(Register::GammaControl1, 0x0000); + self.write_register(Register::GammaControl2, 0x0808); + self.write_register(Register::GammaControl3, 0x080A); + self.write_register(Register::GammaControl4, 0x000A); + self.write_register(Register::GammaControl5, 0x0A08); + self.write_register(Register::GammaControl6, 0x0808); + self.write_register(Register::GammaControl7, 0x0000); + self.write_register(Register::GammaControl8, 0x0A00); + self.write_register(Register::GammaControl9, 0x0710); + self.write_register(Register::GammaControl10, 0x0710); + + // Display on? + self.write_register(Register::DisplayControl1, 0x0012); + self.delay.delay_ms(50); + self.write_register(Register::DisplayControl1, 0x1017); + } + + //pub fn clear(&mut self) {} + //pub fn enableBacklight(&mut self, enable: bool) {} + //pub fn setBacklightBrightness(&mut self, brightness: u8) {} + pub fn enable_display(&mut self, enable: bool) { + self.write_register(Register::UnknownTodo, 0); + if enable { + self.write_register(Register::PowerControl1, 0); + self.delay.delay_ms(50); + self.write_register(Register::DisplayControl1, 0x1017); + } else { + self.write_register(Register::DisplayControl1, 0); + self.delay.delay_ms(50); + self.write_register(Register::PowerControl1, 3); + } + } + + pub fn invert_display(&mut self, invert: bool) { + self.write_command16(if invert { + InvertOnCommand + } else { + InvertOffCommand + }); + } + + pub fn set_orientation(&mut self, orientation: Orientation) {} + // getOrientation? + // getSize? + pub fn set_pixel(&mut self, pos: (u16, u16), color: u16) { + // TODO: Bounds checks + // TODO: Orientation + self.write_register(Register::RamAddressSet1, pos.0); + self.write_register(Register::RamAddressSet2, pos.1); + self.write_register(Register::GramData, color); + } +} + +#[cfg(feature = "graphics")] +extern crate embedded_graphics; +#[cfg(feature = "graphics")] +use self::embedded_graphics::drawable; +#[cfg(feature = "graphics")] +use self::embedded_graphics::pixelcolor::PixelColorU16; +#[cfg(feature = "graphics")] +use self::embedded_graphics::unsignedcoord::UnsignedCoord; +#[cfg(feature = "graphics")] +use self::embedded_graphics::Drawing; + +#[cfg(feature = "graphics")] +impl Drawing for ILI9225 +where + SPI: hal::blocking::spi::Write, + DC: OutputPin, + RESET: OutputPin, + DELAY: hal::blocking::delay::DelayMs, +{ + fn draw(&mut self, item_pixels: T) + where + T: Iterator>, + { + //let (width, height) = self.display.get_size().dimensions(); + for drawable::Pixel(UnsignedCoord(x, y), color) in item_pixels { + //if x <= width.into() && y <= height.into() { + //let color = to_rgb565(color.into(), 0, 0); + // TODO: Is pixelcoloru16 already rgb565? + self.set_pixel((x as u16, y as u16), color.into_inner()); + //} + } + } +} + +#[cfg(test)] +mod tests { + #[test] + fn color_conversion_works() { + use to_rgb565; + for (res, (r, g, b)) in [ + (0x0000, (0, 0, 0)), + (0x0000, (7, 3, 7)), + (0x000F, (0, 0, 127)), + (0x0011, (0, 0, 139)), + (0x001F, (0, 0, 255)), + (0x03E0, (0, 127, 0)), + (0x03EF, (0, 127, 127)), + (0x07E0, (0, 255, 0)), + (0x07FF, (0, 255, 255)), + (0x471A, (64, 224, 208)), + (0x4810, (75, 0, 130)), + (0x7BE0, (127, 127, 0)), + (0x7BEF, (127, 127, 127)), + (0x8000, (128, 0, 0)), + (0x8410, (128, 128, 128)), + (0x867D, (135, 206, 235)), + (0x895C, (138, 43, 226)), + (0x901A, (148, 0, 211)), + (0x9772, (144, 238, 144)), + (0x9E66, (154, 205, 50)), + (0xA145, (165, 42, 42)), + (0xA285, (160, 82, 45)), + (0xAEDC, (172, 216, 230)), + (0xAFE5, (173, 255, 47)), + (0xC618, (192, 192, 192)), + (0xE7FF, (224, 255, 255)), + (0xEC1D, (238, 130, 238)), + (0xF7BB, (245, 245, 220)), + (0xF7FF, (240, 255, 255)), + (0xF800, (255, 0, 0)), + (0xF81F, (255, 0, 255)), + (0xFB08, (255, 99, 71)), + (0xFD20, (255, 165, 0)), + (0xFEA0, (255, 215, 0)), + (0xFFDF, (255, 250, 250)), + (0xFFE0, (255, 255, 0)), + (0xFFFF, (255, 255, 255)), + ] + .iter() + { + assert_eq!(to_rgb565(*r, *g, *b), *res); + } + } +}