1use std::{fs::File, cmp::min, thread::sleep, time::Duration};
2
3use color::Color;
4use image::{DynamicImage, ImageBuffer, imageops::overlay};
5use imageproc::{drawing::{draw_text_mut, draw_filled_rect_mut}, rect::Rect};
6use rppal::gpio::{Gpio, OutputPin};
7use rusttype::{Scale, Font};
8use spidev::{Spidev, SpidevOptions, SpiModeFlags, SpidevTransfer};
9
10use conv::bytes_from_img;
11use text::Text;
12
13pub mod conv;
14pub mod text;
15pub mod color;
16
17struct Consts {}
18
19#[allow(dead_code)]
20impl Consts {
21 const BG_SPI_CS_BACK: u8 = 0;
22 const BG_SPI_CS_FRONT: u8 = 1;
23
24 const SPI_CLOCK_HZ: u32 = 60_000_000;
25
26 const ST7789_NOP: u8 = 0x00;
27 const ST7789_SWRESET: u8 = 0x01;
28 const ST7789_RDDID: u8 = 0x04;
29 const ST7789_RDDST: u8 = 0x09;
30
31 const ST7789_SLPIN: u8 = 0x10;
32 const ST7789_SLPOUT: u8 = 0x11;
33 const ST7789_PTLON: u8 = 0x12;
34 const ST7789_NORON: u8 = 0x13;
35
36 const ST7789_INVOFF: u8 = 0x20;
37 const ST7789_INVON: u8 = 0x21;
38 const ST7789_DISPOFF: u8 = 0x28;
39 const ST7789_DISPON: u8 = 0x29;
40
41 const ST7789_CASET: u8 = 0x2A;
42 const ST7789_RASET: u8 = 0x2B;
43 const ST7789_RAMWR: u8 = 0x2C;
44 const ST7789_RAMRD: u8 = 0x2E;
45
46 const ST7789_PTLAR: u8 = 0x30;
47 const ST7789_MADCTL: u8 = 0x36;
48 const ST7789_COLMOD: u8 = 0x3A;
49
50 const ST7789_FRMCTR1: u8 = 0xB1;
51 const ST7789_FRMCTR2: u8 = 0xB2;
52 const ST7789_FRMCTR3: u8 = 0xB3;
53 const ST7789_INVCTR: u8 = 0xB4;
54 const ST7789_DISSET5: u8 = 0xB6;
55
56 const ST7789_GCTRL: u8 = 0xB7;
57 const ST7789_GTADJ: u8 = 0xB8;
58 const ST7789_VCOMS: u8 = 0xBB;
59
60 const ST7789_LCMCTRL: u8 = 0xC0;
61 const ST7789_IDSET: u8 = 0xC1;
62 const ST7789_VDVVRHEN: u8 = 0xC2;
63 const ST7789_VRHS: u8 = 0xC3;
64 const ST7789_VDVS: u8 = 0xC4;
65 const ST7789_VMCTR1: u8 = 0xC5;
66 const ST7789_FRCTRL2: u8 = 0xC6;
67 const ST7789_CABCCTRL: u8 = 0xC7;
68
69 const ST7789_RDID1: u8 = 0xDA;
70 const ST7789_RDID2: u8 = 0xDB;
71 const ST7789_RDID3: u8 = 0xDC;
72 const ST7789_RDID4: u8 = 0xDD;
73
74 const ST7789_GMCTRP1: u8 = 0xE0;
75 const ST7789_GMCTRN1: u8 = 0xE1;
76
77 const ST7789_PWCTR6: u8 = 0xFC;
78}
79
80
81pub enum DataType {
82 Data,
83 Command
84}
85
86#[allow(dead_code)]
87pub struct ST7789 {
88 gpio: Gpio,
89 spi: Spidev,
90
91 cs: OutputPin,
92 dc: OutputPin,
93 bl: OutputPin,
94 rst: Option<OutputPin>, width: i16, height: i16, rotation: f32, offset_x: i16, offset_y: i16, invert: bool,
103
104 display_buffer: DynamicImage,
105
106 chunk_size: Option<usize>
107}
108
109impl ST7789 {
110 pub fn new(
111 spi_no: u8,
112 dev_no: u8,
113 cs_no: u8,
114 dc_no: u8,
115 bl_no: u8,
116 speed_hz: u32
117 ) -> Self {
118 let mut spi = Spidev::new(File::open(format!("/dev/spidev{}.{}", spi_no, dev_no)).expect("COULDN'T OPEN SPI FILE!"));
119 let gpio = Gpio::new().unwrap();
120
121 let cs = gpio.get(cs_no).unwrap().into_output();
123 let dc = gpio.get(dc_no).unwrap().into_output();
124 let bl = gpio.get(bl_no).unwrap().into_output();
125
126 let opts = SpidevOptions::new()
128 .bits_per_word(0)
129 .max_speed_hz(speed_hz)
130 .lsb_first(false)
131 .mode(SpiModeFlags::SPI_MODE_0)
132 .build();
133
134 spi.configure(&opts).expect(
135 "Couldn't configure SPI! \
136 \nThis is likely because SPI is not set\
137 \nup on your pi."
138 );
139
140 return Self {
141 gpio: gpio,
142 spi: spi,
143 cs: cs,
146 dc: dc,
147 bl: bl,
148 rst: None,
149
150 width: 320,
151 height: 170,
152 rotation: 0_f32,
153 offset_x: 0,
154 offset_y: 0,
155
156 invert: true,
157 display_buffer: DynamicImage::ImageRgb8(ImageBuffer::new(320, 170)),
158
159 chunk_size: None
160 }
161 }
162
163 pub fn draw_image(&mut self, img: &DynamicImage, pos_x: i16, pos_y: i16) {
164 overlay(&mut self.display_buffer, img, pos_x as i64, pos_y as i64);
165 }
166
167 pub fn draw_text(&mut self, text: &str, font: &Font, pos_x: i16, pos_y: i16, color: &Color, size: f32) {
168 draw_text_mut(
169 &mut self.display_buffer,
170 color.get_rgba(),
171 pos_x as i32, pos_y as i32,
172 Scale::uniform(size),
173 font,
174 text
175 );
176 }
177
178 pub fn draw_text_obj(&mut self, text: &Text, pos_x: i16, pos_y: i16) {
179 self.draw_text(
180 text.content.as_str(),
181 &text.font,
182 pos_x, pos_y,
183 &text.color,
184 text.size
185 );
186 }
187
188 pub fn draw_rect(&mut self, rect: Rect, color: &Color) {
189 draw_filled_rect_mut(
190 &mut self.display_buffer,
191 rect,
192 color.get_rgba()
193 );
194 }
195
196 pub fn draw_clear(&mut self, color: &Color) {
197 draw_filled_rect_mut(
198 &mut self.display_buffer,
199 Rect::at(0, 0)
200 .of_size(self.width as u32, self.height as u32),
201 color.get_rgba()
202 );
203 }
204
205 pub fn display(&mut self) {
206 self.set_window(None, None, None, None);
207
208 let bytes_vec = bytes_from_img(&self.display_buffer);
209 let bytes = bytes_vec.as_slice();
210
211 self.send_cmd(Consts::ST7789_RAMWR);
212 self.send_datas(bytes);
213 }
214
215 pub fn set_window(&mut self, x1_o: Option<i16>, y1_o: Option<i16>, x2_o: Option<i16>, y2_o: Option<i16>) {
216 let mut x1 = match x1_o { Some(x) => x, None => 0 };
217 let mut y1 = match y1_o { Some(y) => y, None => 0 };
218 let mut x2 = match x2_o { Some(x) => x-1, None => self.width-1 };
219 let mut y2 = match y2_o { Some(y) => y-1, None => self.height-1 };
220
221 x1 += self.offset_x;
222 y1 += self.offset_y;
223 x2 += self.offset_x;
224 y2 += self.offset_y;
225
226 self.send_cmd(Consts::ST7789_CASET);
228 self.send_data((x1 >> 8) as u8); self.send_data((x1 & 0xff) as u8); self.send_data((x2 >> 8) as u8); self.send_data((x2 & 0xff) as u8); self.send_cmd(Consts::ST7789_RASET);
235 self.send_data((y1 >> 8) as u8); self.send_data((y1 & 0xff) as u8); self.send_data((y2 >> 8) as u8); self.send_data((y2 & 0xff) as u8); }
241
242 pub fn clear(&mut self, col: u16) {
243 let color_half = col.to_be_bytes();
244 let num_pixels = self.width as i32 * self.height as i32;
245 let mut pix_vec: Vec<u8> = Vec::new();
246
247 self.set_window(None, None, None, None);
248
249 for _ in 0..num_pixels {
250 pix_vec.push(color_half[0]);
251 pix_vec.push(color_half[1]);
252 }
253
254 self.send_cmd(Consts::ST7789_RAMWR);
255 self.send_datas(pix_vec.as_slice());
256 }
257
258 pub fn reset(&mut self) {
260 match &mut self.rst {
261 Some(p) => {
262 p.set_high();
263 sleep(Duration::from_millis(10));
264 p.set_low();
265 sleep(Duration::from_millis(10));
266 p.set_high();
267 sleep(Duration::from_millis(10));
268 }
269 None => {
270 println!("Reset pin not set.");
271 }
272 }
273 }
274
275 pub fn init(&mut self) {
276 self.bl.set_pwm_frequency(1000.0, 1.0).expect(
277 "Couldn't set PWM frequency! \
278 \nThis is likely because your PWM outputs \
279 \nare disabled."
280 );
281 self.reset();
282
283 self.send_cmd(Consts::ST7789_SWRESET);
284 sleep(Duration::from_millis(150));
285
286 self.send_cmd(Consts::ST7789_MADCTL);
298 self.send_data(0x70);
299
300 self.send_cmd(Consts::ST7789_FRMCTR2); self.send_data(0x0C);
302 self.send_data(0x0C);
303 self.send_data(0x00);
304 self.send_data(0x33);
305 self.send_data(0x33);
306
307 self.send_cmd(Consts::ST7789_COLMOD); self.send_data(0x05); self.send_cmd(Consts::ST7789_GCTRL); self.send_data(0x35);
312
313 self.send_cmd(Consts::ST7789_VCOMS);
314 self.send_data(0x13);
315
316 self.send_cmd(Consts::ST7789_LCMCTRL); self.send_data(0x2C);
318
319 self.send_cmd(Consts::ST7789_VDVVRHEN); self.send_data(0x01);
321
322 self.send_cmd(Consts::ST7789_VRHS); self.send_data(0x0B);
324
325 self.send_cmd(Consts::ST7789_VDVS); self.send_data(0x20);
327
328 self.send_cmd(0xd0); self.send_data(0xa4);
330 self.send_data(0xa1);
331
332 self.send_cmd(Consts::ST7789_FRCTRL2); self.send_data(0x0f);
334
335 self.send_cmd(Consts::ST7789_GMCTRP1); self.send_data(0x00);
337 self.send_data(0x03);
338 self.send_data(0x07);
339 self.send_data(0x08);
340 self.send_data(0x07);
341 self.send_data(0x15);
342 self.send_data(0x2A);
343 self.send_data(0x44);
344 self.send_data(0x42);
345 self.send_data(0x0A);
346 self.send_data(0x17);
347 self.send_data(0x18);
348 self.send_data(0x25);
349 self.send_data(0x27);
350
351 self.send_cmd(Consts::ST7789_GMCTRN1); self.send_data(0x00);
353 self.send_data(0x03);
354 self.send_data(0x08);
355 self.send_data(0x07);
356 self.send_data(0x07);
357 self.send_data(0x23);
358 self.send_data(0x2A);
359 self.send_data(0x43);
360 self.send_data(0x42);
361 self.send_data(0x09);
362 self.send_data(0x18);
363 self.send_data(0x17);
364 self.send_data(0x25);
365 self.send_data(0x27);
366
367 self.send_cmd(match self.invert {
368 true => Consts::ST7789_INVON,
369 false => Consts::ST7789_INVOFF
370 });
371
372 self.send_cmd(Consts::ST7789_SLPOUT);
373
374 self.send_cmd(Consts::ST7789_DISPON);
375 }
376
377 pub fn cleanup(&mut self) {
378 match &mut self.rst {
379 Some(pin) => {
380 pin.set_high();
381 self.dc.set_low();
382 sleep(Duration::from_millis(1));
383 self.bl.set_high();
384 },
385 None => return
386 }
387 }
388
389 pub fn send_data(&mut self, data: u8) {
390 self.send_datas(&[data]);
391 }
392
393 pub fn send_cmd(&mut self, data: u8) {
394 self.send_cmds(&[data]);
395 }
396
397 pub fn send_datas(&mut self, data: &[u8]) {
398 self.send(data, DataType::Data);
399 }
400
401 pub fn send_cmds(&mut self, data: &[u8]) {
402 self.send(data, DataType::Command);
403 }
404
405 pub fn send(&mut self, data: &[u8], data_type: DataType) {
406 match data_type {
408 DataType::Command => self.dc.set_low(),
409 DataType::Data => self.dc.set_high()
410 };
411
412 let step = match self.chunk_size {
414 Some(s) => s,
415 None => 4096
416 };
417
418 for start in (0..data.len()).step_by(step) {
420 let end = min(start + step, data.len());
421 let mut slice = SpidevTransfer::write(&data[start..end]);
422
423 match self.spi.transfer(&mut slice) {
424 Ok(_) => (),
425 Err(_) => println!("Error sending command/data starting with {:#04x}", data[start]),
426 }
427 }
428 }
429
430 pub fn with_reset(mut self, rst_no: u8) -> Self {
431 self.rst =Some(self.gpio.get(rst_no).unwrap().into_output());
432
433 return self;
434 }
435
436 pub fn with_dimensions(mut self, width: i16, height: i16) -> Self {
437 self.width = width;
438 self.height = height;
439
440 return self;
441 }
442
443 pub fn with_rotation(mut self, rot: f32) -> Self {
444 self.rotation = rot;
445
446 return self;
447 }
448
449 pub fn with_offset(mut self, off_x: i16, off_y: i16) -> Self {
450 self.offset_x = off_x;
451 self.offset_y = off_y;
452
453 return self;
454 }
455
456 pub fn with_chunk_size(mut self, size: usize) -> Self {
457 self.chunk_size = Some(size);
458
459 return self;
460 }
461
462 pub fn width(&self) -> i16 {
463 return self.width;
464 }
465
466 pub fn height(&self) -> i16 {
467 return self.height;
468 }
469}