diff --git a/Cargo.lock b/Cargo.lock index ad6ec8f..03d6720 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,18 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c583acf993cf4245c4acb0a2cc2ab1f9cc097de73411bb6d3647ff6af2b1013d" +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "az" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973" + [[package]] name = "base64" version = "0.13.1" @@ -196,12 +208,38 @@ dependencies = [ "litrs", ] +[[package]] +name = "embassy-embedded-hal" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0641612053b2f34fc250bb63f6630ae75de46e02ade7f457268447081d709ce" +dependencies = [ + "embassy-futures", + "embassy-hal-internal", + "embassy-sync 0.8.0", + "embedded-hal 0.2.7", + "embedded-hal 1.0.0", + "embedded-hal-async", + "embedded-storage", + "embedded-storage-async", + "nb 1.1.0", +] + [[package]] name = "embassy-futures" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc2d050bdc5c21e0862a89256ed8029ae6c290a93aecefc73084b3002cdebb01" +[[package]] +name = "embassy-hal-internal" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f10ce10a4dfdf6402d8e9bd63128986b96a736b1a0a6680547ed2ac55d55dba" +dependencies = [ + "num-traits", +] + [[package]] name = "embassy-sync" version = "0.6.2" @@ -244,6 +282,48 @@ dependencies = [ "heapless 0.9.3", ] +[[package]] +name = "embedded-can" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9d2e857f87ac832df68fa498d18ddc679175cf3d2e4aa893988e5601baf9438" +dependencies = [ + "nb 1.1.0", +] + +[[package]] +name = "embedded-graphics" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e8da660bb0c829b34a56a965490597f82a55e767b91f9543be80ce8ccb416fe" +dependencies = [ + "az", + "byteorder", + "embedded-graphics-core", + "float-cmp", + "micromath", +] + +[[package]] +name = "embedded-graphics-core" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95743bef3ff70fcba3930246c4e6872882bbea0dcc6da2ca860112e0cd4bd09f" +dependencies = [ + "az", + "byteorder", +] + +[[package]] +name = "embedded-hal" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff" +dependencies = [ + "nb 0.1.3", + "void", +] + [[package]] name = "embedded-hal" version = "1.0.0" @@ -256,7 +336,17 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c4c685bbef7fe13c3c6dd4da26841ed3980ef33e841cddfa15ce8a8fb3f1884" dependencies = [ - "embedded-hal", + "embedded-hal 1.0.0", +] + +[[package]] +name = "embedded-hal-bus" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "513e0b3a8fb7d3013a8ae17a834283f170deaf7d0eeab0a7c1a36ad4dd356d22" +dependencies = [ + "critical-section", + "embedded-hal 1.0.0", ] [[package]] @@ -295,6 +385,15 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a21dea9854beb860f3062d10228ce9b976da520a73474aed3171ec276bc0c032" +[[package]] +name = "embedded-storage-async" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1763775e2323b7d5f0aa6090657f5e21cfa02ede71f5dc40eead06d64dcd15cc" +dependencies = [ + "embedded-storage", +] + [[package]] name = "enumset" version = "1.1.12" @@ -398,11 +497,18 @@ dependencies = [ "cfg-if", "critical-section", "delegate", + "digest", "document-features", + "embassy-embedded-hal", "embassy-futures", "embassy-sync 0.8.0", - "embedded-hal", + "embedded-can", + "embedded-hal 1.0.0", "embedded-hal-async", + "embedded-io 0.6.1", + "embedded-io 0.7.1", + "embedded-io-async 0.6.1", + "embedded-io-async 0.7.0", "enumset", "esp-config", "esp-hal-procmacros", @@ -420,12 +526,17 @@ dependencies = [ "fugit", "instability", "log", + "nb 1.1.0", "paste", "portable-atomic", + "rand_core 0.10.1", + "rand_core 0.6.4", + "rand_core 0.9.5", "riscv", "sha1", "sha2", "strum", + "ufmt-write", "xtensa-lx", "xtensa-lx-rt", ] @@ -516,12 +627,16 @@ name = "esp32-phone" version = "0.1.0" dependencies = [ "critical-section", + "embedded-graphics", + "embedded-hal 1.0.0", + "embedded-hal-bus", "esp-alloc", "esp-backtrace", "esp-bootloader-esp-idf", "esp-hal", "esp-println", "log", + "mipidsi", ] [[package]] @@ -578,6 +693,15 @@ dependencies = [ "vcell", ] +[[package]] +name = "float-cmp" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" +dependencies = [ + "num-traits", +] + [[package]] name = "fnv" version = "1.0.7" @@ -777,6 +901,47 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" +[[package]] +name = "micromath" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3c8dda44ff03a2f238717214da50f65d5a53b45cd213a7370424ffdb6fae815" + +[[package]] +name = "mipidsi" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790ebd28bd67addbccf41b1c0c188c26bb9f5bdcd91d4d6da9bd558e20d97a1d" +dependencies = [ + "embedded-graphics-core", + "embedded-hal 1.0.0", + "heapless 0.8.0", +] + +[[package]] +name = "nb" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f" +dependencies = [ + "nb 1.1.0", +] + +[[package]] +name = "nb" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + [[package]] name = "paste" version = "1.0.15" @@ -831,6 +996,24 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" + +[[package]] +name = "rand_core" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" + [[package]] name = "riscv" version = "0.15.0" @@ -838,7 +1021,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b05cfa3f7b30c84536a9025150d44d26b8e1cc20ddf436448d74cd9591eefb25" dependencies = [ "critical-section", - "embedded-hal", + "embedded-hal 1.0.0", "paste", "riscv-macros", "riscv-pac", @@ -1114,6 +1297,12 @@ version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" +[[package]] +name = "ufmt-write" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e87a2ed6b42ec5e28cc3b94c09982969e9227600b2e3dcbc1db927a84c06bd69" + [[package]] name = "unicode-ident" version = "1.0.24" @@ -1144,6 +1333,12 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + [[package]] name = "winapi-util" version = "0.1.11" diff --git a/Cargo.toml b/Cargo.toml index 0541c02..20ef82c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,8 +5,8 @@ rust-version = "1.88" version = "0.1.0" [dependencies] -esp-hal = { version = "~1.1.0", features = ["esp32", "log-04"] } - +esp-hal = { version = "~1.1.0", features = ["esp32", "log-04", "unstable"] } +embedded-hal = "*" esp-bootloader-esp-idf = { version = "0.5.0", features = ["esp32", "log-04"] } log = "0.4.27" @@ -20,6 +20,10 @@ esp-backtrace = { version = "0.19.0", features = [ ] } esp-println = { version = "0.17.0", features = ["esp32", "log-04"] } +mipidsi = "0.10.0" +embedded-hal-bus = "*" +embedded-graphics="*" + # For fine tuning these settings, please refer to https://doc.rust-lang.org/cargo/reference/profiles.html [profile.dev] diff --git a/src/display.rs b/src/display.rs new file mode 100644 index 0000000..f6ae92d --- /dev/null +++ b/src/display.rs @@ -0,0 +1,293 @@ +use core::ops::Mul; + +use embedded_hal::{delay::DelayNs, spi::SpiBus}; +use esp_hal::{ + DriverMode, + gpio::{Input, Output}, + spi::master::Spi, +}; + +// use atmega_hal::{ +// Spi, +// port::{self, Pin, PinOps, mode}, +// spi::ChipSelectPin, +// }; +// use embedded_hal::{delay::DelayNs, digital::OutputPin, spi::SpiBus}; + +#[derive(Clone, Copy)] +pub struct Rgb565(pub u8, pub u8, pub u8); + +impl Rgb565 { + pub fn as_color(&self) -> Color { + let r = ((self.0 / 8) as u16) << 11; + let g = ((self.1 / 4) as u16) << 5; + let b = (self.2 / 8) as u16; + + Color::from(r | b | g) + } + + pub fn red() -> Rgb565 { + Rgb565(255, 0, 0) + } + + pub fn green() -> Rgb565 { + Rgb565(0, 255, 0) + } + + pub fn blue() -> Rgb565 { + Rgb565(0, 0, 255) + } + + pub fn yellow() -> Rgb565 { + Rgb565(255, 255, 0) + } + + pub fn magenta() -> Rgb565 { + Rgb565(255, 0, 255) + } + + pub fn cyan() -> Rgb565 { + Rgb565(0, 255, 255) + } + + pub fn qoi_hash(&self) -> usize { + ((self.0 as u32 * 3 + self.1 as u32 * 5 + self.2 as u32 * 7 + 255 * 11) % 64) as usize + } +} + +impl Mul for Rgb565 { + type Output = Rgb565; + + fn mul(self, rhs: f32) -> Self::Output { + Rgb565( + (self.0 as f32 * rhs) as u8, + (self.1 as f32 * rhs) as u8, + (self.2 as f32 * rhs) as u8, + ) + } +} + +#[derive(Default, Clone, Copy)] +pub struct Position { + pub x: u16, + pub y: u16, +} + +impl Position { + pub fn new(x: u16, y: u16) -> Position { + Position { x, y } + } +} + +#[derive(Default, Clone, Copy)] +pub struct Color { + pub bytes: [u8; 2], +} + +impl Color { + pub fn from(number: u16) -> Color { + Color { + bytes: number.to_be_bytes(), + } + } +} + +pub struct Display<'d, DM: DriverMode, T: DelayNs> { + pub spi: Spi<'d, DM>, + pub cs: Output<'d>, + pub dc: Output<'d>, + pub rst: Output<'d>, + pub delay: T, +} + +pub enum Command { + NOP = 0x0, + SoftReset = 0x1, + RddID = 0x4, + RddST = 0x9, + + SleepIn = 0x10, + SleepOut = 0x11, + PTLON = 0x12, + NoRon = 0x13, + + InversionOff = 0x20, + InversionOn = 0x21, + DisplayOff = 0x28, + DisplayOn = 0x29, + ColumnAlignmentSet = 0x2A, + RowAlignmentSet = 0x2B, + RamWR = 0x2C, + RamRD = 0x2E, + + PTLAR = 0x30, + VSCRDEF = 0x33, + ColorMode = 0x3A, + MADCTL = 0x36, + VSCSAD = 0x37, + // MadCTLMY = 0x80, + // MadCTLMX = 0x40, + // MadCTLMV = 0x20, + // MadCTLML = 0x10, + // MadCTLBGR = 0x08, + // MadCTLMH = 0x04, + // MadCTLRGB = 0x00, + + // RDID1 = 0xDA, + // RDID2 = 0xDB, + // RDID3 = 0xDC, + // RDID4 = 0xDD, + + // ColorMode65K = 0x50, + // ColorMode262K = 0x60, + // ColorMode12BIT = 0x03, + // ColorMode16BIT = 0x05, + // ColorMode18BIT = 0x06, + // ColorMode16M = 0x07, +} + +enum ColorMode { + ColorMode65K = 0x50, + ColorMode262K = 0x60, + ColorMode12BIT = 0x03, + ColorMode16BIT = 0x05, + ColorMode18BIT = 0x06, + ColorMode16M = 0x07, +} + +pub enum Writeable<'d> { + Command(Command), + Data(&'d [u8]), +} + +pub enum Rotation { + Portrait = 0x00, + Landscape = 0x60, + InvertedPortrait = 0xc0, + InvertedLandscape = 0xa0, +} + +impl<'d, DM: DriverMode, T: DelayNs> Display<'d, DM, T> { + pub fn write(&mut self, writeable: Writeable) { + self.cs.set_low(); + match writeable { + Writeable::Command(cmd) => { + self.dc.set_low(); + SpiBus::write(&mut self.spi, &[cmd as u8]).unwrap() + } + Writeable::Data(data) => { + self.dc.set_high(); + SpiBus::write(&mut self.spi, data).unwrap(); + self.cs.set_high(); + } + } + } + + pub fn init(&mut self) { + self.hard_reset(); + self.soft_reset(); + self.set_sleep(false); + self.set_color_mode((ColorMode::ColorMode65K as u8) | (ColorMode::ColorMode16BIT as u8)); + self.delay.delay_ms(50); + self.set_inversion(true); + self.write(Writeable::Command(Command::NoRon)); + self.draw_rect( + Position { x: 0, y: 0 }, + Position { x: 240, y: 240 }, + Color { bytes: [255, 255] }, + ); + + self.write(Writeable::Command(Command::DisplayOn)); + + self.delay.delay_ms(500); + } + + pub fn hard_reset(&mut self) { + self.cs.set_low(); + self.rst.set_high(); + self.delay.delay_ms(50); + + self.rst.set_low(); + self.delay.delay_ms(50); + + self.rst.set_high(); + self.delay.delay_ms(150); + + self.cs.set_high(); + } + + pub fn soft_reset(&mut self) { + self.write(Writeable::Command(Command::SoftReset)); + self.delay.delay_ms(150); + } + + pub fn set_sleep(&mut self, sleep: bool) { + match sleep { + true => self.write(Writeable::Command(Command::SleepIn)), + false => self.write(Writeable::Command(Command::SleepOut)), + } + } + + pub fn set_inversion(&mut self, inversion: bool) { + match inversion { + true => self.write(Writeable::Command(Command::InversionOn)), + false => self.write(Writeable::Command(Command::InversionOff)), + } + } + + pub fn set_rotation(&mut self, rotation: Rotation) { + self.write(Writeable::Command(Command::MADCTL)); + self.write(Writeable::Data(&[rotation as u8])); + } + + pub fn set_color_mode(&mut self, mode: u8) { + self.write(Writeable::Command(Command::ColorMode)); + self.write(Writeable::Data(&[mode & 0x77])); + } + + fn set_columns(&mut self, start: u16, end: u16) { + let [start1, start2] = start.to_be_bytes(); + let [end1, end2] = end.to_be_bytes(); + self.write(Writeable::Command(Command::ColumnAlignmentSet)); + self.write(Writeable::Data(&[start1, start2, end1, end2])); + } + + fn set_rows(&mut self, start: u16, end: u16) { + let [start1, start2] = start.to_be_bytes(); + let [end1, end2] = end.to_be_bytes(); + self.write(Writeable::Command(Command::RowAlignmentSet)); + self.write(Writeable::Data(&[start1, start2, end1, end2])); + } + + pub fn set_window(&mut self, pos0: Position, pos1: Position) { + self.set_columns(pos0.x, pos1.x); + self.set_rows(pos0.y, pos1.y); + self.write(Writeable::Command(Command::RamWR)); + } + + pub fn pixel(&mut self, position: Position, color: Color) { + self.set_window(position, position); + self.write(Writeable::Data(&color.bytes)); + } + + pub fn draw_rect(&mut self, pos0: Position, pos1: Position, color: Color) { + self.set_window(pos0, pos1); + self.dc.set_high(); + let width = pos1.x - pos0.x; + let height = pos1.y - pos0.y; + let pixels = width * height; + let chunks = pixels / 256; + let mut full_buf = [0; 512]; + let [col1, col2] = color.bytes; + for i in 0..256 { + full_buf[i * 2] = col1; + full_buf[i * 2 + 1] = col2; + } + for _ in 0..chunks { + self.write(Writeable::Data(&full_buf)); + } + for _ in 0..(pixels % 256) { + self.write(Writeable::Data(&color.bytes)); + } + } +} diff --git a/src/main.rs b/src/main.rs index 4a2caea..8fe4301 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,17 +7,26 @@ )] #![deny(clippy::large_stack_frames)] +use embedded_graphics::{pixelcolor::Rgb565, prelude::DrawTarget}; +use embedded_hal_bus::spi::ExclusiveDevice; use esp_hal::{ clock::CpuClock, - gpio::{Output, OutputConfig}, + delay::Delay, + gpio::{Level, Output, OutputConfig}, main, - time::{Duration, Instant}, + spi::master::{Config, Spi}, + time::{Duration, Instant, Rate}, }; use esp_backtrace as _; +use mipidsi::{Builder, interface::SpiInterface, models::ST7789}; + +// use crate::display::{Color, Display, Position, Rgb565, Rotation}; extern crate alloc; +mod display; + // This creates a default app-descriptor required by the esp-idf bootloader. // For more information see: esp_bootloader_esp_idf::esp_app_desc!(); @@ -55,17 +64,68 @@ fn main() -> ! { esp_alloc::heap_allocator!(#[esp_hal::ram(reclaimed)] size: 98768); - let mut led = Output::new( - peripherals.GPIO21, - esp_hal::gpio::Level::High, - OutputConfig::default(), - ); + let spi = Spi::new( + peripherals.SPI2, + Config::default() + .with_frequency(Rate::from_mhz(40)) + .with_mode(esp_hal::spi::Mode::_2) + .with_read_bit_order(esp_hal::spi::BitOrder::MsbFirst) + .with_write_bit_order(esp_hal::spi::BitOrder::MsbFirst), + ) + .unwrap() + .with_sck(peripherals.GPIO5) + .with_mosi(peripherals.GPIO18) + .with_miso(peripherals.GPIO19); + + let rst = Output::new(peripherals.GPIO14, Level::Low, OutputConfig::default()); + let dc = Output::new(peripherals.GPIO32, Level::Low, OutputConfig::default()); + let cs = Output::new(peripherals.GPIO21, Level::Low, OutputConfig::default()); + + // let mut display = Display { + // spi, + // cs, + // dc, + // rst, + // delay: Delay::new(), + // }; + + // display.init(); + // display.draw_rect( + // Position::new(0, 0), + // Position::new(32, 32), + // Rgb565::yellow().as_color(), + // ); + + // let mut led = Output::new(peripherals.GPIO5, Level::Low, OutputConfig::default()); + + let mut test_delay = Delay::new(); + + let mut buffer = [0u8; 512]; + let spi_device = ExclusiveDevice::new_no_delay(spi, cs).unwrap(); + let di = SpiInterface::new(spi_device, dc, &mut buffer); + let mut display = Builder::new(ST7789, di) + .reset_pin(rst) + .init(&mut test_delay) + .unwrap(); // delay provider from your MCU + + let mut color = 0; loop { - log::info!("Hello world!"); - led.toggle(); - let delay_start = Instant::now(); - while delay_start.elapsed() < Duration::from_millis(500) {} + log::info!("Hello world: {}", color); + + color = (color + 50) % 200; + + display.clear(Rgb565::new(color, color, color)).unwrap(); + + // display.draw_rect( + // Position::new(0, 0), + // Position::new(color as u16, color as u16), + // Rgb565::red().as_color(), + // ); + + // let delay_start = Instant::now(); + // while delay_start.elapsed() < Duration::from_millis(500) {} + test_delay.delay_millis(1000); } // for inspiration have a look at the examples at https://github.com/esp-rs/esp-hal/tree/esp-hal-v1.1.0/examples