oxstack_cardputer/
display.rs1use embassy_time::Delay;
8use embedded_hal_bus::spi::{DeviceError, ExclusiveDevice, NoDelay};
9use esp_hal::Async;
10use esp_hal::dma::{BurstConfig, DmaRxBuf, DmaTxBuf};
11use esp_hal::gpio::{Level, Output, OutputConfig};
12use esp_hal::peripherals::{DMA_CH0, GPIO33, GPIO34, GPIO35, GPIO36, GPIO37, SPI3};
13use esp_hal::spi::Mode;
14use esp_hal::spi::master::{Config, Spi, SpiDma};
15use esp_hal::time::Rate;
16use oxstack_drivers::st7789::{self, M5_ST7789V2_INIT, Panel, Rotation, St7789};
17use oxstack_gfx::Framebuffer;
18use static_cell::ConstStaticCell;
19
20pub const WIDTH: usize = 240;
22pub const HEIGHT: usize = 135;
24
25pub type Frame = Framebuffer<WIDTH, HEIGHT>;
27
28pub const PANEL: Panel = Panel {
30 width: 135,
31 height: 240,
32 offset_x: 52,
33 offset_y: 40,
34 memory_width: 240,
35 memory_height: 320,
36 bgr: true,
37 invert: true,
38};
39
40pub const ROTATION: Rotation = Rotation::Deg90;
42
43pub const SPI_FREQUENCY: Rate = Rate::from_mhz(40);
46
47pub fn try_framebuffer() -> Option<&'static mut Frame> {
51 static FRAME: ConstStaticCell<Frame> = ConstStaticCell::new(Frame::new());
52 FRAME.try_take()
53}
54
55pub fn framebuffer() -> &'static mut Frame {
57 try_framebuffer().expect("framebuffer already taken")
58}
59
60type Bus<'d> = ExclusiveDevice<SpiDma<'d, Async>, Output<'d>, NoDelay>;
61
62pub type Error = st7789::Error<
64 DeviceError<esp_hal::spi::Error, core::convert::Infallible>,
65 core::convert::Infallible,
66>;
67
68#[allow(missing_docs)]
70pub struct DisplayResources<'d> {
71 pub spi: SPI3<'d>,
72 pub dma: DMA_CH0<'d>,
73 pub sck: GPIO36<'d>,
74 pub mosi: GPIO35<'d>,
75 pub cs: GPIO37<'d>,
76 pub dc: GPIO34<'d>,
77 pub rst: GPIO33<'d>,
78}
79reborrow_group!(DisplayResources {
80 spi,
81 dma,
82 sck,
83 mosi,
84 cs,
85 dc,
86 rst
87});
88
89fn bounce_buffers() -> (DmaRxBuf, DmaTxBuf) {
93 const CHUNK: usize = BurstConfig::DEFAULT.max_compatible_chunk_size();
94 let (rx, rx_desc) = static_dma_memory!(32, CHUNK);
95 let (tx, tx_desc) = static_dma_memory!(256, CHUNK);
96 (
97 DmaRxBuf::new(rx_desc, rx).expect("valid DMA buffer"),
98 DmaTxBuf::new(tx_desc, tx).expect("valid DMA buffer"),
99 )
100}
101
102impl<'d> DisplayResources<'d> {
103 pub async fn init(&mut self) -> Result<Display<'_>, Error> {
109 let (rx, tx) = bounce_buffers();
110 let spi = Spi::new(
111 self.spi.reborrow(),
112 Config::default()
113 .with_frequency(SPI_FREQUENCY)
114 .with_mode(Mode::_0),
115 )
116 .expect("40 MHz mode 0 is a valid SPI configuration")
117 .with_sck(self.sck.reborrow())
118 .with_mosi(self.mosi.reborrow())
119 .with_dma(self.dma.reborrow())
120 .with_buffers(rx, tx)
121 .into_async();
122
123 let out = OutputConfig::default();
124 let cs = Output::new(self.cs.reborrow(), Level::High, out);
125 let dc = Output::new(self.dc.reborrow(), Level::Low, out);
126 let mut rst = Output::new(self.rst.reborrow(), Level::High, out);
127
128 let mut delay = Delay;
129 let Ok(()) = st7789::hard_reset(&mut rst, &mut delay).await;
130
131 let Ok(bus) = ExclusiveDevice::new_no_delay(spi, cs);
132 let mut lcd = St7789::new(bus, dc, PANEL, ROTATION);
133 lcd.init(M5_ST7789V2_INIT, &mut delay).await?;
134 Ok(Display { lcd, _rst: rst })
135 }
136}
137
138pub struct Display<'d> {
140 lcd: St7789<Bus<'d>, Output<'d>>,
141 _rst: Output<'d>,
143}
144
145impl<'d> Display<'d> {
146 pub async fn flush(&mut self, frame: &mut Frame) -> Result<bool, Error> {
153 let Some(dirty) = frame.take_dirty() else {
154 return Ok(false);
155 };
156 self.lcd.write_rows(dirty.y, dirty.bytes).await?;
157 Ok(true)
158 }
159
160 pub async fn set_display_on(&mut self, on: bool) -> Result<(), Error> {
162 self.lcd.set_display_on(on).await
163 }
164
165 pub async fn set_sleep(&mut self, sleep: bool) -> Result<(), Error> {
167 self.lcd.set_sleep(sleep, &mut Delay).await
168 }
169
170 pub fn controller(&mut self) -> &mut St7789<Bus<'d>, Output<'d>> {
172 &mut self.lcd
173 }
174}