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}
44
45#[repr(u8)]
49#[derive(Copy, Clone)]
50pub enum Orientation {
51 Portrait = 0b0000_0000, Landscape = 0b0110_0000, PortraitSwapped = 0b1100_0000, LandscapeSwapped = 0b1010_0000, }
56
57impl Default for Orientation {
58 fn default() -> Self {
59 Self::Portrait
60 }
61}
62
63#[derive(Copy, Clone)]
67pub enum TearingEffect {
68 Off,
70 Vertical,
72 HorizontalAndVertical,
74}
75
76#[derive(Copy, Clone, Debug)]
77pub enum BacklightState {
78 On,
79 Off,
80}
81
82#[derive(Debug)]
86pub enum Error<PinE> {
87 DisplayError,
88 Pin(PinE),
89}
90
91impl<DI, RST, BL, PinE> ST7789<DI, RST, BL>
92where
93 DI: WriteOnlyDataCommand,
94 RST: OutputPin<Error = PinE>,
95 BL: OutputPin<Error = PinE>,
96{
97 pub fn new(di: DI, rst: Option<RST>, bl: Option<BL>, size_x: u16, size_y: u16) -> Self {
109 Self {
110 di,
111 rst,
112 bl,
113 size_x,
114 size_y,
115 orientation: Orientation::default(),
116 }
117 }
118
119 pub fn init(&mut self, delay_source: &mut impl DelayUs<u32>) -> Result<(), Error<PinE>> {
127 self.hard_reset(delay_source)?;
128 if let Some(bl) = self.bl.as_mut() {
129 bl.set_low().map_err(Error::Pin)?;
130 delay_source.delay_us(10_000);
131 bl.set_high().map_err(Error::Pin)?;
132 }
133
134 self.write_command(Instruction::SWRESET)?; delay_source.delay_us(150_000);
136 self.write_command(Instruction::SLPOUT)?; delay_source.delay_us(10_000);
138 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])?;
143 self.write_command(Instruction::COLMOD)?; self.write_data(&[0b0101_0101])?;
145 self.write_command(Instruction::INVON)?; delay_source.delay_us(10_000);
147 self.write_command(Instruction::NORON)?; delay_source.delay_us(10_000);
149 self.write_command(Instruction::DISPON)?; delay_source.delay_us(10_000);
151 Ok(())
152 }
153
154 pub fn hard_reset(&mut self, delay_source: &mut impl DelayUs<u32>) -> Result<(), Error<PinE>> {
162 if let Some(rst) = self.rst.as_mut() {
163 rst.set_high().map_err(Error::Pin)?;
164 delay_source.delay_us(10); rst.set_low().map_err(Error::Pin)?;
166 delay_source.delay_us(10); rst.set_high().map_err(Error::Pin)?;
168 delay_source.delay_us(10); }
170
171 Ok(())
172 }
173
174 pub fn set_backlight(
175 &mut self,
176 state: BacklightState,
177 delay_source: &mut impl DelayUs<u32>,
178 ) -> Result<(), Error<PinE>> {
179 if let Some(bl) = self.bl.as_mut() {
180 match state {
181 BacklightState::On => bl.set_high().map_err(Error::Pin)?,
182 BacklightState::Off => bl.set_low().map_err(Error::Pin)?,
183 }
184 delay_source.delay_us(10); }
186 Ok(())
187 }
188
189 pub fn orientation(&self) -> Orientation {
193 self.orientation
194 }
195
196 pub fn set_orientation(&mut self, orientation: Orientation) -> Result<(), Error<PinE>> {
200 self.write_command(Instruction::MADCTL)?;
201 self.write_data(&[orientation as u8])?;
202 self.orientation = orientation;
203 Ok(())
204 }
205
206 pub fn set_pixel(&mut self, x: u16, y: u16, color: u16) -> Result<(), Error<PinE>> {
216 self.set_address_window(x, y, x, y)?;
217 self.write_command(Instruction::RAMWR)?;
218 self.di
219 .send_data(U16BEIter(&mut once(color)))
220 .map_err(|_| Error::DisplayError)?;
221
222 Ok(())
223 }
224
225 pub fn set_pixels<T>(
237 &mut self,
238 sx: u16,
239 sy: u16,
240 ex: u16,
241 ey: u16,
242 colors: T,
243 ) -> Result<(), Error<PinE>>
244 where
245 T: IntoIterator<Item = u16>,
246 {
247 self.set_address_window(sx, sy, ex, ey)?;
248 self.write_command(Instruction::RAMWR)?;
249 self.di
250 .send_data(U16BEIter(&mut colors.into_iter()))
251 .map_err(|_| Error::DisplayError)
252 }
253
254 pub fn set_scroll_offset(&mut self, offset: u16) -> Result<(), Error<PinE>> {
261 self.write_command(Instruction::VSCAD)?;
262 self.write_data(&offset.to_be_bytes())
263 }
264
265 pub fn release(self) -> (DI, Option<RST>, Option<BL>) {
270 (self.di, self.rst, self.bl)
271 }
272
273 fn write_command(&mut self, command: Instruction) -> Result<(), Error<PinE>> {
274 self.di
275 .send_commands(U8Iter(&mut once(command as u8)))
276 .map_err(|_| Error::DisplayError)?;
277 Ok(())
278 }
279
280 fn write_data(&mut self, data: &[u8]) -> Result<(), Error<PinE>> {
281 self.di
282 .send_data(U8Iter(&mut data.iter().cloned()))
283 .map_err(|_| Error::DisplayError)
284 }
285
286 fn set_address_window(
288 &mut self,
289 sx: u16,
290 sy: u16,
291 ex: u16,
292 ey: u16,
293 ) -> Result<(), Error<PinE>> {
294 self.write_command(Instruction::CASET)?;
295 self.write_data(&sx.to_be_bytes())?;
296 self.write_data(&ex.to_be_bytes())?;
297 self.write_command(Instruction::RASET)?;
298 self.write_data(&sy.to_be_bytes())?;
299 self.write_data(&ey.to_be_bytes())
300 }
301
302 pub fn set_tearing_effect(&mut self, tearing_effect: TearingEffect) -> Result<(), Error<PinE>> {
306 match tearing_effect {
307 TearingEffect::Off => self.write_command(Instruction::TEOFF),
308 TearingEffect::Vertical => {
309 self.write_command(Instruction::TEON)?;
310 self.write_data(&[0])
311 }
312 TearingEffect::HorizontalAndVertical => {
313 self.write_command(Instruction::TEON)?;
314 self.write_data(&[1])
315 }
316 }
317 }
318}