1#![no_std]
2#![allow(clippy::type_complexity)]
4
5pub mod instruction;
8
9use crate::instruction::Instruction;
10use core::iter::once;
11
12use display_interface::DataFormat::{U16BEIter, U8Iter};
13use display_interface::WriteOnlyDataCommand;
14use embedded_hal::blocking::delay::DelayUs;
15use embedded_hal::digital::v2::OutputPin;
16
17#[cfg(feature = "graphics")]
18mod graphics;
19
20#[cfg(feature = "batch")]
21mod batch;
22
23pub struct ST7789<DI, RST, BL>
27where
28 DI: WriteOnlyDataCommand,
29 RST: OutputPin,
30 BL: OutputPin,
31{
32 di: DI,
34 rst: Option<RST>,
36 bl: Option<BL>,
38 size_x: u16,
40 size_y: u16,
41 orientation: Orientation,
43 offset_x: u16,
44 offset_y: u16,
45}
46
47#[repr(u8)]
51#[derive(Copy, Clone)]
52pub enum Orientation {
53 Portrait = 0b0000_0000, Landscape = 0b0110_0000, PortraitSwapped = 0b1100_0000, LandscapeSwapped = 0b1010_0000, }
58
59impl Default for Orientation {
60 fn default() -> Self {
61 Self::Portrait
62 }
63}
64
65#[derive(Copy, Clone)]
69pub enum TearingEffect {
70 Off,
72 Vertical,
74 HorizontalAndVertical,
76}
77
78#[derive(Copy, Clone, Debug)]
79pub enum BacklightState {
80 On,
81 Off,
82}
83
84#[derive(Debug)]
88pub enum Error<PinE> {
89 DisplayError,
90 Pin(PinE),
91}
92
93impl<DI, RST, BL, PinE> ST7789<DI, RST, BL>
94where
95 DI: WriteOnlyDataCommand,
96 RST: OutputPin<Error = PinE>,
97 BL: OutputPin<Error = PinE>,
98{
99 pub fn new(di: DI, rst: Option<RST>, bl: Option<BL>, size_x: u16, size_y: u16) -> Self {
111 Self {
112 di,
113 rst,
114 bl,
115 size_x,
116 size_y,
117 offset_x: 0,
118 offset_y: 0,
119 orientation: Orientation::default(),
120 }
121 }
122 pub fn set_offset(&mut self, offset_x: u16, offset_y: u16) {
123 self.offset_x = offset_x;
124 self.offset_y = offset_y;
125 }
126
127 pub fn init(&mut self, delay_source: &mut impl DelayUs<u32>) -> Result<(), Error<PinE>> {
135 self.hard_reset(delay_source)?;
136 if let Some(bl) = self.bl.as_mut() {
137 bl.set_low().map_err(Error::Pin)?;
138 delay_source.delay_us(10_000);
139 bl.set_high().map_err(Error::Pin)?;
140 }
141
142 self.write_command(Instruction::SWRESET)?; delay_source.delay_us(150_000);
144 self.write_command(Instruction::SLPOUT)?; delay_source.delay_us(10_000);
146 self.write_command(Instruction::INVOFF)?; self.write_command(Instruction::VSCRDER)?; self.write_data(&[0u8, 0u8, 0x14u8, 0u8, 0u8, 0u8])?; self.write_command(Instruction::MADCTL)?; self.write_data(&[0b0000_0000])?;
151 self.write_command(Instruction::COLMOD)?; self.write_data(&[0b0101_0101])?;
153 self.write_command(Instruction::INVON)?; delay_source.delay_us(10_000);
155 self.write_command(Instruction::NORON)?; delay_source.delay_us(10_000);
157 self.write_command(Instruction::DISPON)?; delay_source.delay_us(10_000);
159 Ok(())
160 }
161
162 pub fn hard_reset(&mut self, delay_source: &mut impl DelayUs<u32>) -> Result<(), Error<PinE>> {
170 if let Some(rst) = self.rst.as_mut() {
171 rst.set_high().map_err(Error::Pin)?;
172 delay_source.delay_us(10); rst.set_low().map_err(Error::Pin)?;
174 delay_source.delay_us(10); rst.set_high().map_err(Error::Pin)?;
176 delay_source.delay_us(10); }
178
179 Ok(())
180 }
181
182 pub fn set_backlight(
183 &mut self,
184 state: BacklightState,
185 delay_source: &mut impl DelayUs<u32>,
186 ) -> Result<(), Error<PinE>> {
187 if let Some(bl) = self.bl.as_mut() {
188 match state {
189 BacklightState::On => bl.set_high().map_err(Error::Pin)?,
190 BacklightState::Off => bl.set_low().map_err(Error::Pin)?,
191 }
192 delay_source.delay_us(10); }
194 Ok(())
195 }
196
197 pub fn orientation(&self) -> Orientation {
201 self.orientation
202 }
203
204 pub fn set_orientation(&mut self, orientation: Orientation) -> Result<(), Error<PinE>> {
208 self.write_command(Instruction::MADCTL)?;
209 self.write_data(&[orientation as u8])?;
210 self.orientation = orientation;
211 Ok(())
212 }
213
214 pub fn set_pixel(&mut self, x: u16, y: u16, color: u16) -> Result<(), Error<PinE>> {
224 self.set_address_window(x, y, x, y)?;
225 self.write_command(Instruction::RAMWR)?;
226 self.di
227 .send_data(U16BEIter(&mut once(color)))
228 .map_err(|_| Error::DisplayError)?;
229
230 Ok(())
231 }
232
233 pub fn set_pixels<T>(
245 &mut self,
246 sx: u16,
247 sy: u16,
248 ex: u16,
249 ey: u16,
250 colors: T,
251 ) -> Result<(), Error<PinE>>
252 where
253 T: IntoIterator<Item = u16>,
254 {
255 self.set_address_window(sx, sy, ex, ey)?;
256 self.write_command(Instruction::RAMWR)?;
257 self.di
258 .send_data(U16BEIter(&mut colors.into_iter()))
259 .map_err(|_| Error::DisplayError)
260 }
261
262 pub fn set_scroll_offset(&mut self, offset: u16) -> Result<(), Error<PinE>> {
269 self.write_command(Instruction::VSCAD)?;
270 self.write_data(&offset.to_be_bytes())
271 }
272
273 pub fn release(self) -> (DI, Option<RST>, Option<BL>) {
278 (self.di, self.rst, self.bl)
279 }
280
281 fn write_command(&mut self, command: Instruction) -> Result<(), Error<PinE>> {
282 self.di
283 .send_commands(U8Iter(&mut once(command as u8)))
284 .map_err(|_| Error::DisplayError)?;
285 Ok(())
286 }
287
288 fn write_data(&mut self, data: &[u8]) -> Result<(), Error<PinE>> {
289 self.di
290 .send_data(U8Iter(&mut data.iter().cloned()))
291 .map_err(|_| Error::DisplayError)
292 }
293
294 fn set_address_window(
296 &mut self,
297 sx: u16,
298 sy: u16,
299 ex: u16,
300 ey: u16,
301 ) -> Result<(), Error<PinE>> {
302 let (sx, sy, ex, ey) = match self.orientation() {
303 Orientation::Landscape | Orientation::LandscapeSwapped => (
304 sx + self.offset_x,
305 sy + self.offset_y,
306 ex + self.offset_x,
307 ey + self.offset_y,
308 ),
309 Orientation::Portrait | Orientation::PortraitSwapped => (
310 sy + self.offset_x,
311 sx + self.offset_y,
312 ey + self.offset_x,
313 ex + self.offset_y,
314 ),
315 };
316
317 self.write_command(Instruction::CASET)?;
318 self.write_data(&sx.to_be_bytes())?;
319 self.write_data(&ex.to_be_bytes())?;
320 self.write_command(Instruction::RASET)?;
321 self.write_data(&sy.to_be_bytes())?;
322 self.write_data(&ey.to_be_bytes())
323 }
324
325 pub fn set_tearing_effect(&mut self, tearing_effect: TearingEffect) -> Result<(), Error<PinE>> {
329 match tearing_effect {
330 TearingEffect::Off => self.write_command(Instruction::TEOFF),
331 TearingEffect::Vertical => {
332 self.write_command(Instruction::TEON)?;
333 self.write_data(&[0])
334 }
335 TearingEffect::HorizontalAndVertical => {
336 self.write_command(Instruction::TEON)?;
337 self.write_data(&[1])
338 }
339 }
340 }
341}