diff options
| author | Curly <[email protected]> | 2025-02-23 07:33:58 -0800 |
|---|---|---|
| committer | Curly <[email protected]> | 2025-02-23 07:33:58 -0800 |
| commit | 3932835998802fc3abf7cce4f736e072858ebfd1 (patch) | |
| tree | 5dd714b99bc74a03556c58809237c88691c293bb /examples/rp235x/src/bin/spi_display.rs | |
| parent | c3c67db93e627a4fafe5e1a1123e5cbb4abafe47 (diff) | |
rename `rp23` (?) folder to `rp235x`; fix `ci.sh` to use `rp235x` folder
Diffstat (limited to 'examples/rp235x/src/bin/spi_display.rs')
| -rw-r--r-- | examples/rp235x/src/bin/spi_display.rs | 177 |
1 files changed, 177 insertions, 0 deletions
diff --git a/examples/rp235x/src/bin/spi_display.rs b/examples/rp235x/src/bin/spi_display.rs new file mode 100644 index 000000000..9c524ab25 --- /dev/null +++ b/examples/rp235x/src/bin/spi_display.rs | |||
| @@ -0,0 +1,177 @@ | |||
| 1 | //! This example shows how to use SPI (Serial Peripheral Interface) in the RP2350 chip. | ||
| 2 | //! | ||
| 3 | //! Example written for a display using the ST7789 chip. Possibly the Waveshare Pico-ResTouch | ||
| 4 | //! (https://www.waveshare.com/wiki/Pico-ResTouch-LCD-2.8) | ||
| 5 | |||
| 6 | #![no_std] | ||
| 7 | #![no_main] | ||
| 8 | |||
| 9 | use core::cell::RefCell; | ||
| 10 | |||
| 11 | use defmt::*; | ||
| 12 | use display_interface_spi::SPIInterface; | ||
| 13 | use embassy_embedded_hal::shared_bus::blocking::spi::SpiDeviceWithConfig; | ||
| 14 | use embassy_executor::Spawner; | ||
| 15 | use embassy_rp::gpio::{Level, Output}; | ||
| 16 | use embassy_rp::spi; | ||
| 17 | use embassy_rp::spi::{Blocking, Spi}; | ||
| 18 | use embassy_sync::blocking_mutex::raw::NoopRawMutex; | ||
| 19 | use embassy_sync::blocking_mutex::Mutex; | ||
| 20 | use embassy_time::Delay; | ||
| 21 | use embedded_graphics::image::{Image, ImageRawLE}; | ||
| 22 | use embedded_graphics::mono_font::ascii::FONT_10X20; | ||
| 23 | use embedded_graphics::mono_font::MonoTextStyle; | ||
| 24 | use embedded_graphics::pixelcolor::Rgb565; | ||
| 25 | use embedded_graphics::prelude::*; | ||
| 26 | use embedded_graphics::primitives::{PrimitiveStyleBuilder, Rectangle}; | ||
| 27 | use embedded_graphics::text::Text; | ||
| 28 | use mipidsi::models::ST7789; | ||
| 29 | use mipidsi::options::{Orientation, Rotation}; | ||
| 30 | use mipidsi::Builder; | ||
| 31 | use {defmt_rtt as _, panic_probe as _}; | ||
| 32 | |||
| 33 | use crate::touch::Touch; | ||
| 34 | |||
| 35 | const DISPLAY_FREQ: u32 = 64_000_000; | ||
| 36 | const TOUCH_FREQ: u32 = 200_000; | ||
| 37 | |||
| 38 | #[embassy_executor::main] | ||
| 39 | async fn main(_spawner: Spawner) { | ||
| 40 | let p = embassy_rp::init(Default::default()); | ||
| 41 | info!("Hello World!"); | ||
| 42 | |||
| 43 | let bl = p.PIN_13; | ||
| 44 | let rst = p.PIN_15; | ||
| 45 | let display_cs = p.PIN_9; | ||
| 46 | let dcx = p.PIN_8; | ||
| 47 | let miso = p.PIN_12; | ||
| 48 | let mosi = p.PIN_11; | ||
| 49 | let clk = p.PIN_10; | ||
| 50 | let touch_cs = p.PIN_16; | ||
| 51 | //let touch_irq = p.PIN_17; | ||
| 52 | |||
| 53 | // create SPI | ||
| 54 | let mut display_config = spi::Config::default(); | ||
| 55 | display_config.frequency = DISPLAY_FREQ; | ||
| 56 | display_config.phase = spi::Phase::CaptureOnSecondTransition; | ||
| 57 | display_config.polarity = spi::Polarity::IdleHigh; | ||
| 58 | let mut touch_config = spi::Config::default(); | ||
| 59 | touch_config.frequency = TOUCH_FREQ; | ||
| 60 | touch_config.phase = spi::Phase::CaptureOnSecondTransition; | ||
| 61 | touch_config.polarity = spi::Polarity::IdleHigh; | ||
| 62 | |||
| 63 | let spi: Spi<'_, _, Blocking> = Spi::new_blocking(p.SPI1, clk, mosi, miso, touch_config.clone()); | ||
| 64 | let spi_bus: Mutex<NoopRawMutex, _> = Mutex::new(RefCell::new(spi)); | ||
| 65 | |||
| 66 | let display_spi = SpiDeviceWithConfig::new(&spi_bus, Output::new(display_cs, Level::High), display_config); | ||
| 67 | let touch_spi = SpiDeviceWithConfig::new(&spi_bus, Output::new(touch_cs, Level::High), touch_config); | ||
| 68 | |||
| 69 | let mut touch = Touch::new(touch_spi); | ||
| 70 | |||
| 71 | let dcx = Output::new(dcx, Level::Low); | ||
| 72 | let rst = Output::new(rst, Level::Low); | ||
| 73 | // dcx: 0 = command, 1 = data | ||
| 74 | |||
| 75 | // Enable LCD backlight | ||
| 76 | let _bl = Output::new(bl, Level::High); | ||
| 77 | |||
| 78 | // display interface abstraction from SPI and DC | ||
| 79 | let di = SPIInterface::new(display_spi, dcx); | ||
| 80 | |||
| 81 | // Define the display from the display interface and initialize it | ||
| 82 | let mut display = Builder::new(ST7789, di) | ||
| 83 | .display_size(240, 320) | ||
| 84 | .reset_pin(rst) | ||
| 85 | .orientation(Orientation::new().rotate(Rotation::Deg90)) | ||
| 86 | .init(&mut Delay) | ||
| 87 | .unwrap(); | ||
| 88 | display.clear(Rgb565::BLACK).unwrap(); | ||
| 89 | |||
| 90 | let raw_image_data = ImageRawLE::new(include_bytes!("../../assets/ferris.raw"), 86); | ||
| 91 | let ferris = Image::new(&raw_image_data, Point::new(34, 68)); | ||
| 92 | |||
| 93 | // Display the image | ||
| 94 | ferris.draw(&mut display).unwrap(); | ||
| 95 | |||
| 96 | let style = MonoTextStyle::new(&FONT_10X20, Rgb565::GREEN); | ||
| 97 | Text::new( | ||
| 98 | "Hello embedded_graphics \n + embassy + RP2040!", | ||
| 99 | Point::new(20, 200), | ||
| 100 | style, | ||
| 101 | ) | ||
| 102 | .draw(&mut display) | ||
| 103 | .unwrap(); | ||
| 104 | |||
| 105 | loop { | ||
| 106 | if let Some((x, y)) = touch.read() { | ||
| 107 | let style = PrimitiveStyleBuilder::new().fill_color(Rgb565::BLUE).build(); | ||
| 108 | |||
| 109 | Rectangle::new(Point::new(x - 1, y - 1), Size::new(3, 3)) | ||
| 110 | .into_styled(style) | ||
| 111 | .draw(&mut display) | ||
| 112 | .unwrap(); | ||
| 113 | } | ||
| 114 | } | ||
| 115 | } | ||
| 116 | |||
| 117 | /// Driver for the XPT2046 resistive touchscreen sensor | ||
| 118 | mod touch { | ||
| 119 | use embedded_hal_1::spi::{Operation, SpiDevice}; | ||
| 120 | |||
| 121 | struct Calibration { | ||
| 122 | x1: i32, | ||
| 123 | x2: i32, | ||
| 124 | y1: i32, | ||
| 125 | y2: i32, | ||
| 126 | sx: i32, | ||
| 127 | sy: i32, | ||
| 128 | } | ||
| 129 | |||
| 130 | const CALIBRATION: Calibration = Calibration { | ||
| 131 | x1: 3880, | ||
| 132 | x2: 340, | ||
| 133 | y1: 262, | ||
| 134 | y2: 3850, | ||
| 135 | sx: 320, | ||
| 136 | sy: 240, | ||
| 137 | }; | ||
| 138 | |||
| 139 | pub struct Touch<SPI: SpiDevice> { | ||
| 140 | spi: SPI, | ||
| 141 | } | ||
| 142 | |||
| 143 | impl<SPI> Touch<SPI> | ||
| 144 | where | ||
| 145 | SPI: SpiDevice, | ||
| 146 | { | ||
| 147 | pub fn new(spi: SPI) -> Self { | ||
| 148 | Self { spi } | ||
| 149 | } | ||
| 150 | |||
| 151 | pub fn read(&mut self) -> Option<(i32, i32)> { | ||
| 152 | let mut x = [0; 2]; | ||
| 153 | let mut y = [0; 2]; | ||
| 154 | self.spi | ||
| 155 | .transaction(&mut [ | ||
| 156 | Operation::Write(&[0x90]), | ||
| 157 | Operation::Read(&mut x), | ||
| 158 | Operation::Write(&[0xd0]), | ||
| 159 | Operation::Read(&mut y), | ||
| 160 | ]) | ||
| 161 | .unwrap(); | ||
| 162 | |||
| 163 | let x = (u16::from_be_bytes(x) >> 3) as i32; | ||
| 164 | let y = (u16::from_be_bytes(y) >> 3) as i32; | ||
| 165 | |||
| 166 | let cal = &CALIBRATION; | ||
| 167 | |||
| 168 | let x = ((x - cal.x1) * cal.sx / (cal.x2 - cal.x1)).clamp(0, cal.sx); | ||
| 169 | let y = ((y - cal.y1) * cal.sy / (cal.y2 - cal.y1)).clamp(0, cal.sy); | ||
| 170 | if x == 0 && y == 0 { | ||
| 171 | None | ||
| 172 | } else { | ||
| 173 | Some((x, y)) | ||
| 174 | } | ||
| 175 | } | ||
| 176 | } | ||
| 177 | } | ||
