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 offset_x: u16,
42 offset_y: u16,
43 orientation: Orientation,
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
120 orientation: Orientation::default(),
121 }
122 }
123 pub fn set_offset(&mut self, offset_x: u16, offset_y: u16) {
124 self.offset_x = offset_x;
125 self.offset_y = offset_y;
126 }
127
128 pub fn init(&mut self, delay_source: &mut impl DelayUs<u32>) -> Result<(), Error<PinE>> {
136 self.hard_reset(delay_source)?;
137 if let Some(bl) = self.bl.as_mut() {
138 bl.set_low().map_err(Error::Pin)?;
139 delay_source.delay_us(10_000);
140 bl.set_high().map_err(Error::Pin)?;
141 }
142
143 self.write_command(Instruction::SWRESET)?; delay_source.delay_us(150_000);
145 self.write_command(Instruction::SLPOUT)?; delay_source.delay_us(10_000);
147 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])?;
152 self.write_command(Instruction::COLMOD)?; self.write_data(&[0b0101_0101])?;
154 self.write_command(Instruction::INVON)?; delay_source.delay_us(10_000);
156 self.write_command(Instruction::NORON)?; delay_source.delay_us(10_000);
158 self.write_command(Instruction::DISPON)?; delay_source.delay_us(10_000);
160 Ok(())
161 }
162
163 pub fn hard_reset(&mut self, delay_source: &mut impl DelayUs<u32>) -> Result<(), Error<PinE>> {
171 if let Some(rst) = self.rst.as_mut() {
172 rst.set_high().map_err(Error::Pin)?;
173 delay_source.delay_us(10); rst.set_low().map_err(Error::Pin)?;
175 delay_source.delay_us(10); rst.set_high().map_err(Error::Pin)?;
177 delay_source.delay_us(10); }
179
180 Ok(())
181 }
182
183 pub fn set_backlight(
184 &mut self,
185 state: BacklightState,
186 delay_source: &mut impl DelayUs<u32>,
187 ) -> Result<(), Error<PinE>> {
188 if let Some(bl) = self.bl.as_mut() {
189 match state {
190 BacklightState::On => bl.set_high().map_err(Error::Pin)?,
191 BacklightState::Off => bl.set_low().map_err(Error::Pin)?,
192 }
193 delay_source.delay_us(10); }
195 Ok(())
196 }
197
198 pub fn orientation(&self) -> Orientation {
202 self.orientation
203 }
204
205 pub fn set_orientation(&mut self, orientation: Orientation) -> Result<(), Error<PinE>> {
209 self.write_command(Instruction::MADCTL)?;
210 self.write_data(&[orientation as u8])?;
211 self.orientation = orientation;
212 Ok(())
213 }
214
215 pub fn set_pixel(&mut self, x: u16, y: u16, color: u16) -> Result<(), Error<PinE>> {
225 self.set_address_window(x, y, x, y)?;
226 self.write_command(Instruction::RAMWR)?;
227 self.di
228 .send_data(U16BEIter(&mut once(color)))
229 .map_err(|_| Error::DisplayError)?;
230
231 Ok(())
232 }
233
234 pub fn set_pixels<T>(
246 &mut self,
247 sx: u16,
248 sy: u16,
249 ex: u16,
250 ey: u16,
251 colors: T,
252 ) -> Result<(), Error<PinE>>
253 where
254 T: IntoIterator<Item = u16>,
255 {
256 self.set_address_window(sx, sy, ex, ey)?;
257 self.write_command(Instruction::RAMWR)?;
258 self.di
259 .send_data(U16BEIter(&mut colors.into_iter()))
260 .map_err(|_| Error::DisplayError)
261 }
262
263 pub fn set_scroll_offset(&mut self, offset: u16) -> Result<(), Error<PinE>> {
270 self.write_command(Instruction::VSCAD)?;
271 self.write_data(&offset.to_be_bytes())
272 }
273
274 pub fn release(self) -> (DI, Option<RST>, Option<BL>) {
279 (self.di, self.rst, self.bl)
280 }
281
282 fn write_command(&mut self, command: Instruction) -> Result<(), Error<PinE>> {
283 self.di
284 .send_commands(U8Iter(&mut once(command as u8)))
285 .map_err(|_| Error::DisplayError)?;
286 Ok(())
287 }
288
289 fn write_data(&mut self, data: &[u8]) -> Result<(), Error<PinE>> {
290 self.di
291 .send_data(U8Iter(&mut data.iter().cloned()))
292 .map_err(|_| Error::DisplayError)
293 }
294
295 fn set_address_window(
297 &mut self,
298 sx: u16,
299 sy: u16,
300 ex: u16,
301 ey: u16,
302 ) -> Result<(), Error<PinE>> {
303 let mut offset = (self.offset_x, self.offset_y);
304 match self.orientation {
307 Orientation::Portrait | Orientation::PortraitSwapped => {
308 offset = (offset.0, offset.1);
309 }
310 Orientation::Landscape | Orientation::LandscapeSwapped => {
311 offset = (offset.1, offset.0);
313 }
314 }
315 let (sx, sy, ex, ey) = (sx + offset.0, sy + offset.1, ex + offset.0, ey + offset.1);
316 self.write_command(Instruction::CASET)?;
317 self.write_data(&sx.to_be_bytes())?;
318 self.write_data(&ex.to_be_bytes())?;
319 self.write_command(Instruction::RASET)?;
320 self.write_data(&sy.to_be_bytes())?;
321 self.write_data(&ey.to_be_bytes())
322 }
323
324 pub fn set_tearing_effect(&mut self, tearing_effect: TearingEffect) -> Result<(), Error<PinE>> {
328 match tearing_effect {
329 TearingEffect::Off => self.write_command(Instruction::TEOFF),
330 TearingEffect::Vertical => {
331 self.write_command(Instruction::TEON)?;
332 self.write_data(&[0])
333 }
334 TearingEffect::HorizontalAndVertical => {
335 self.write_command(Instruction::TEON)?;
336 self.write_data(&[1])
337 }
338 }
339 }
340}