From 451e3429611710c145916b2e87f534493eae85a5 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 20 Jul 2021 10:06:57 +0200 Subject: [PATCH] rp/examples: add spi_display example --- examples/rp/Cargo.toml | 3 + examples/rp/assets/ferris.raw | Bin 0 -> 11008 bytes examples/rp/src/bin/spi_display.rs | 215 +++++++++++++++++++++++++++++ 3 files changed, 218 insertions(+) create mode 100644 examples/rp/assets/ferris.raw create mode 100644 examples/rp/src/bin/spi_display.rs diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index e45bf594..4c4f983f 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -30,3 +30,6 @@ cortex-m-rt = "0.6.13" embedded-hal = { version = "0.2.4" } panic-probe = { version = "0.2.0", features = ["print-defmt"] } futures = { version = "0.3.8", default-features = false, features = ["async-await", "cfg-target-has-atomic", "unstable"] } +display-interface-spi = "0.4.1" +embedded-graphics = "0.7.1" +st7789 = "0.6.1" diff --git a/examples/rp/assets/ferris.raw b/examples/rp/assets/ferris.raw new file mode 100644 index 0000000000000000000000000000000000000000..9733889c577bdcc9277858ce019abff5b7dd23d0 GIT binary patch literal 11008 zcmd5?F^ua(5VexV#V@WS1%*kP(1k9)^CeA06$+&At2oh@0@XE0K{Vfvgzh8?f}lvb zaTTFoMCc>A0LerGojwRfh{6R({wd%DqNSjK9Zxp1YdbzW$#K|Hylc$27uoSiXeOk>`2)ETl94!X5 z7Q>oZcLS)K_4`3-o24s~T)IN69FL)C0^2HN9bPjKDaXE$Ic$?^cUdyd0GqoOdUSk& z)m^jRz!NpcXI*DkTWzhvYD}(y;w}+V+oZYh0`w@b-S)J(p2N{9vWWl6CTvs4TZIEp zjPY$qVZOB%L?z8dQo~6~tV8R|HSw}(AjjO>R-?O3dK=P)$+xlX57w1NDRi&LI%rLY>sr5EE+juPZOcMX}yJB|mkDykmG+wG!3h1HTYe>d!M?e7lGC6 z*CPx~h==!G##uBQ12?+&w8cHc-QHX25Ibgg1deqZUEVF4{H^cPD!jixPnCv0%w84v z9`yfmyFr${f@Z6`o!)(89(aH!>$KBeG2&lFFpG5`z_}1?1`ZJ zzdYT*@mtKNoQ}5jtBFy_hZy}K?`K9&cQgmekZnQU4d+9MR8IK)kI#O-dPmdEiq%ZF zpLzJvAD{m8_@Sm-jp}rDQ>1;Q75OHC_^|Q+?)QJ9>x0*@9m*FGqCY=JnpFj8{1JW0 zS8~^s$3rQH&HtDFL>zB?h^=VaV5uOu3CESMkmkV36t9Zas@`UY{l z`&3|6_ht~||NRTn{O~@~!0ssjE$8fnjPL?;V)H*Oy51 z{1MWal_ECl1_MuCzYM*&PI;ehH2uL4aeVtwVAXWTr~bl<{`?Wpy!@-P%N`e>8<4ld ztEdGb7Tm+V{ra0ve>)c~@uz~-AHkQ$uYUH3kaC;$s5f~_nC(dSG`F{rzTY$ytm*sb zL$LB#R;SGK?dW0@H&$KJ=eB56X!lH+c$Kb*y^_pu9FD10w3K#Y!D6{Dd0rsmLo*|- z#X;k-^Er^&mMDtjgp%`EIYqZ>m$$oNcE0y}`1NwxaV@ zIM*6GS+|$vS-el~+KEmYWGwCF=CU@MB>EuJsO7=ZAC;~$ddT;hTmu=g4YlX4YqQNI z-1H7h125?X+yH6I)AnK_`N}x2Qa>OfQTQXPBAi@XEdIU4(sDsHhG;jJ_EfOaR>A|sbvyIQkX zDuS;z6DZt(=b6lTe>L7b**qyv%QKF2Ib60ET>3{q5ASMGAD|yHuIS4m!pf1(M^5LQ zdopRZ@HNzG2$@`lG8=?e!BfXiTKHP8pVyIdHFkW#>8>#v(e!F;)`3?l&*uHFNBq8k z-}z|XpZA=u)2T64?~V^=&&HYTgCKI4_fVqKQSrnx1@Q06y8nrpOg>(GZ03D6SCy83QFk90Az3DH8)x}RC72juAcQ zf!J1$kVgA!XE&1ja`G~Tv(3;r-Oc5>0Xoj>ulQbW8v*3&AY!`+ON%XiwnDdZz8Cc@AOcvBcOCKD8vW4f211 zh$8MU(vc*+U^-p8UQ6#Gb$U+-3zQHocz*Rg)KveKjsX76lBZ8x8JU zd?+d1uH*ioWE&N$VRu;1ABE$RUAltRlJy*a;0>0oMT=FZrK|9Q_3V!7m${Emo#U`Z zq+1{%luNMsrH?`KKO}ii)KOnF7U{a+dgXfp+HJ_F_LWT|eS|b!T{Ylz`L;O#GU9!t zf4syI*qyw+k8`Hg9cf(wJtf!HI2VtT1JbDa-?BLEY3pj-0T|*tT2)>=qR*yQc~4P` wR$bCnkn#T-omP-|F6|UZ^0i?#!;9#oVeR<*+&^_zgQd0@_=A$oddFDezjq(d^8f$< literal 0 HcmV?d00001 diff --git a/examples/rp/src/bin/spi_display.rs b/examples/rp/src/bin/spi_display.rs new file mode 100644 index 00000000..467cdf42 --- /dev/null +++ b/examples/rp/src/bin/spi_display.rs @@ -0,0 +1,215 @@ +#![no_std] +#![no_main] +#![feature(asm)] +#![feature(min_type_alias_impl_trait)] +#![feature(impl_trait_in_bindings)] +#![feature(type_alias_impl_trait)] +#![allow(incomplete_features)] + +#[path = "../example_common.rs"] +mod example_common; + +use core::cell::RefCell; +use core::fmt::Debug; + +use defmt::*; +use display_interface_spi::SPIInterfaceNoCS; +use embassy::executor::Spawner; +use embassy::time::Delay; +use embassy_rp::gpio::NoPin; +use embassy_rp::peripherals; +use embassy_rp::spi; +use embassy_rp::spi::Spi; +use embassy_rp::{gpio, Peripherals}; +use embedded_graphics::image::{Image, ImageRawLE}; +use embedded_graphics::mono_font::ascii::FONT_10X20; +use embedded_graphics::mono_font::MonoTextStyle; +use embedded_graphics::pixelcolor::Rgb565; +use embedded_graphics::prelude::*; +use embedded_graphics::primitives::{PrimitiveStyleBuilder, Rectangle}; +use embedded_graphics::text::Text; +use gpio::{Level, Output}; +use st7789::{Orientation, ST7789}; + +#[embassy::main] +async fn main(_spawner: Spawner, p: Peripherals) { + info!("Hello World!"); + + let bl = p.PIN_13; + let rst = p.PIN_15; + let display_cs = p.PIN_9; + let dcx = p.PIN_8; + let miso = p.PIN_12; + let mosi = p.PIN_11; + let clk = p.PIN_10; + let touch_cs = p.PIN_16; + //let touch_irq = p.PIN_17; + + // create SPI + let mut config = spi::Config::default(); + config.frequency = DISPLAY_FREQ; + config.phase = spi::Phase::CaptureOnSecondTransition; + config.polarity = spi::Polarity::IdleHigh; + + let spi = RefCell::new(SpiState { + last_mode: SpiMode::Display, + spi: Spi::new(p.SPI1, clk, mosi, miso, NoPin, config), + display_cs: Output::new(display_cs, Level::Low), + }); + + let mut touch = Touch::new(TouchSpi(&spi), Output::new(touch_cs, Level::High)); + + let dcx = Output::new(dcx, Level::Low); + let rst = Output::new(rst, Level::Low); + // dcx: 0 = command, 1 = data + + // Enable LCD backlight + let _bl = Output::new(bl, Level::High); + + // display interface abstraction from SPI and DC + let di = SPIInterfaceNoCS::new(DisplaySpi(&spi), dcx); + + // create driver + let mut display = ST7789::new(di, rst, 240, 320); + + // initialize + display.init(&mut Delay).unwrap(); + + // set default orientation + display.set_orientation(Orientation::Landscape).unwrap(); + + display.clear(Rgb565::BLACK).unwrap(); + + let raw_image_data = ImageRawLE::new(include_bytes!("../../assets/ferris.raw"), 86); + let ferris = Image::new(&raw_image_data, Point::new(34, 68)); + + // Display the image + ferris.draw(&mut display).unwrap(); + + let style = MonoTextStyle::new(&FONT_10X20, Rgb565::GREEN); + Text::new( + "Hello embedded_graphics \n + embassy + RP2040!", + Point::new(20, 200), + style, + ) + .draw(&mut display) + .unwrap(); + + loop { + if let Some((x, y)) = touch.read() { + let style = PrimitiveStyleBuilder::new() + .fill_color(Rgb565::BLUE) + .build(); + + Rectangle::new(Point::new(x - 1, y - 1), Size::new(3, 3)) + .into_styled(style) + .draw(&mut display) + .unwrap(); + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum SpiMode { + Display, + Touch, +} + +struct SpiState { + spi: Spi<'static, peripherals::SPI1>, + display_cs: Output<'static, peripherals::PIN_9>, + + last_mode: SpiMode, +} + +const DISPLAY_FREQ: u32 = 64_000_000; +const TOUCH_FREQ: u32 = 200_000; + +struct DisplaySpi<'a>(&'a RefCell); +impl<'a> embedded_hal::blocking::spi::Write for DisplaySpi<'a> { + type Error = core::convert::Infallible; + + fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> { + let this = &mut *self.0.borrow_mut(); + if this.last_mode != SpiMode::Display { + this.spi.set_frequency(DISPLAY_FREQ); + this.display_cs.set_low(); + this.last_mode = SpiMode::Display; + } + this.spi.write(words); + Ok(()) + } +} + +struct TouchSpi<'a>(&'a RefCell); +impl<'a> embedded_hal::blocking::spi::Transfer for TouchSpi<'a> { + type Error = core::convert::Infallible; + + fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Self::Error> { + let this = &mut *self.0.borrow_mut(); + if this.last_mode != SpiMode::Touch { + this.spi.set_frequency(TOUCH_FREQ); + this.display_cs.set_high(); + this.last_mode = SpiMode::Touch; + } + this.spi.transfer(words); + Ok(words) + } +} + +struct Calibration { + x1: i32, + x2: i32, + y1: i32, + y2: i32, + sx: i32, + sy: i32, +} + +const CALIBRATION: Calibration = Calibration { + x1: 3880, + x2: 340, + y1: 262, + y2: 3850, + sx: 320, + sy: 240, +}; + +struct Touch< + SPI: embedded_hal::blocking::spi::Transfer, + CS: embedded_hal::digital::v2::OutputPin, +> { + spi: SPI, + cs: CS, +} + +impl, CS: embedded_hal::digital::v2::OutputPin> + Touch +where + SPI::Error: Debug, + CS::Error: Debug, +{ + pub fn new(spi: SPI, cs: CS) -> Self { + Self { spi, cs } + } + + pub fn read(&mut self) -> Option<(i32, i32)> { + self.cs.set_low().unwrap(); + let mut buf = [0x90, 0x00, 0x00, 0xd0, 0x00, 0x00]; + self.spi.transfer(&mut buf).unwrap(); + self.cs.set_high().unwrap(); + + let x = ((buf[1] as u32) << 5 | (buf[2] as u32) >> 3) as i32; + let y = ((buf[4] as u32) << 5 | (buf[5] as u32) >> 3) as i32; + + let cal = &CALIBRATION; + + let x = ((x - cal.x1) * cal.sx / (cal.x2 - cal.x1)).clamp(0, cal.sx); + let y = ((y - cal.y1) * cal.sy / (cal.y2 - cal.y1)).clamp(0, cal.sy); + if x == 0 && y == 0 { + None + } else { + Some((x, y)) + } + } +}