RP2040 MCU Board with 1.28inch Round LCD

From Sketching with Hardware at LMU Wiki
Revision as of 10:45, 6 July 2024 by Skwhadmin (talk | contribs)
Jump to navigation Jump to search

RP2040 Board with LCD

https://www.waveshare.com/rp2040-lcd-1.28.htm

https://www.waveshare.com/wiki/RP2040-LCD-1.28

Firmware with Display Driver: the regular micropython formware should work, too

here is a firmware with a build in driver

Display example

 1 """
 2 lines.py
 3 
 4     Draws lines and rectangles in random colors at random locations on the
 5     display.
 6 
 7 """
 8 import random
 9 from machine import Pin, SPI
10 import gc9a01py as gc9a01
11 
12 
13 def main():
14     spi = SPI(1, baudrate=60000000, sck=Pin(10), mosi=Pin(11))
15     tft = gc9a01.GC9A01(
16         spi,
17         dc=Pin(8, Pin.OUT),
18         cs=Pin(9, Pin.OUT),
19         reset=Pin(12, Pin.OUT),
20         backlight=Pin(25, Pin.OUT),
21         rotation=0)
22 
23     tft.fill(gc9a01.WHITE)
24 
25     while True:
26         tft.line(
27             random.randint(0, tft.width),
28             random.randint(0, tft.height),
29             random.randint(0, tft.width),
30             random.randint(0, tft.height),
31             gc9a01.color565(
32                 random.getrandbits(8),
33                 random.getrandbits(8),
34                 random.getrandbits(8)
35                 )
36             )
37 
38         width = random.randint(0, tft.width // 2)
39         height = random.randint(0, tft.height // 2)
40         col = random.randint(0, tft.width - width)
41         row = random.randint(0, tft.height - height)
42         tft.fill_rect(
43             col,
44             row,
45             width,
46             height,
47             gc9a01.color565(
48                 random.getrandbits(8),
49                 random.getrandbits(8),
50                 random.getrandbits(8)
51             )
52         )
53 
54 
55 main()

Display Driver

downloaded from: https://github.com/russhughes/gc9a01py/tree/main

Filename: gc9a01py.py

  1 """
  2 Copyright (c) 2020, 2021 Russ Hughes
  3 
  4 This file incorporates work covered by the following copyright and
  5 permission notice and is licensed under the same terms:
  6 
  7 The MIT License (MIT)
  8 
  9 Copyright (c) 2019 Ivan Belokobylskiy
 10 
 11 Permission is hereby granted, free of charge, to any person obtaining a copy
 12 of this software and associated documentation files (the "Software"), to deal
 13 in the Software without restriction, including without limitation the rights
 14 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 15 copies of the Software, and to permit persons to whom the Software is
 16 furnished to do so, subject to the following conditions:
 17 
 18 The above copyright notice and this permission notice shall be included in
 19 all copies or substantial portions of the Software.
 20 
 21 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 22 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 23 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 24 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 25 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 26 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 27 THE SOFTWARE.
 28 
 29 
 30 GC9A01 Display driver in MicroPython based on devbis st7789py_mpy module from
 31 https://github.com/devbis/st7789py_mpy modified to drive 240x240 pixel GC9A01
 32 displays.
 33 
 34 The driver supports display rotation, mirroring, scrolling and drawing text
 35 using 8 and 16 bit wide bitmap fonts with heights that are multiples of 8.
 36 Included are 12 bitmap fonts derived from classic pc text mode fonts and a
 37 couple of example programs.
 38 
 39 This is a work in progress. Documentation can be found at
 40 https://penfold.owt.com/gc9a01py/.
 41 
 42 If you are looking for a faster driver with additional features, check out the
 43 C version of the driver at https://github.com/russhughes/gc9a01_mpy
 44 
 45 """
 46 
 47 # pylint: disable=invalid-name,import-error
 48 
 49 import time
 50 from micropython import const
 51 import ustruct as struct
 52 
 53 # commands
 54 GC9A01_SWRESET = const(0x01)
 55 GC9A01_SLPIN = const(0x10)
 56 GC9A01_SLPOUT = const(0x11)
 57 GC9A01_INVOFF = const(0x20)
 58 GC9A01_INVON = const(0x21)
 59 GC9A01_DISPOFF = const(0x28)
 60 GC9A01_DISPON = const(0x29)
 61 GC9A01_CASET = const(0x2A)
 62 GC9A01_RASET = const(0x2B)
 63 GC9A01_RAMWR = const(0x2C)
 64 GC9A01_VSCRDEF = const(0x33)
 65 GC9A01_COLMOD = const(0x3A)
 66 GC9A01_MADCTL = const(0x36)
 67 GC9A01_VSCSAD = const(0x37)
 68 
 69 # Color definitions
 70 BLACK = const(0x0000)
 71 BLUE = const(0x001F)
 72 RED = const(0xF800)
 73 GREEN = const(0x07E0)
 74 CYAN = const(0x07FF)
 75 MAGENTA = const(0xF81F)
 76 YELLOW = const(0xFFE0)
 77 WHITE = const(0xFFFF)
 78 
 79 _ENCODE_PIXEL = ">H"
 80 _ENCODE_POS = ">HH"
 81 _DECODE_PIXEL = ">BBB"
 82 
 83 _BUFFER_SIZE = const(256)
 84 
 85 _BIT7 = const(0x80)
 86 _BIT6 = const(0x40)
 87 _BIT5 = const(0x20)
 88 _BIT4 = const(0x10)
 89 _BIT3 = const(0x08)
 90 _BIT2 = const(0x04)
 91 _BIT1 = const(0x02)
 92 _BIT0 = const(0x01)
 93 
 94 ROTATIONS = [
 95     0x48,   # 0 - PORTRAIT
 96     0x28,   # 1 - LANDSCAPE
 97     0x88,   # 2 - INVERTED_PORTRAIT
 98     0xe8,   # 3 - INVERTED_LANDSCAPE
 99     0x08,   # 4 - PORTRAIT_MIRRORED
100     0x68,   # 5 - LANDSCAPE_MIRRORED
101     0xc8,   # 6 - INVERTED_PORTRAIT_MIRRORED
102     0xa8]   # 7 - INVERTED_LANDSCAPE_MIRRORED]
103 
104 
105 def color565(red, green=0, blue=0):
106     """
107     Convert red, green and blue values (0-255) into a 16-bit 565 encoded color.
108     """
109     try:
110         red, green, blue = red  # see if the first var is a tuple/list
111     except TypeError:
112         pass
113     return (red & 0xf8) << 8 | (green & 0xfc) << 3 | blue >> 3
114 
115 
116 def _encode_pos(x, y):
117     """Encode a postion into bytes."""
118     return struct.pack(_ENCODE_POS, x, y)
119 
120 
121 def _encode_pixel(color):
122     """Encode a pixel color into bytes."""
123     return struct.pack(_ENCODE_PIXEL, color)
124 
125 
126 class GC9A01():
127     """
128     GC9A01 driver class
129 
130     Args:
131         spi (spi): spi object (Required)
132         dc (pin): dc pin (Required)
133         cs (pin): cs pin {optional}
134         reset (pin): reset pin
135         backlight(pin): backlight pin
136         rotation (int): display rotation
137     """
138 
139     def __init__(
140             self,
141             spi=None,
142             dc=None,
143             cs=None,
144             reset=None,
145             backlight=None,
146             rotation=0):
147         """
148         Initialize display.
149         """
150         if spi is None:
151             raise ValueError("SPI object is required.")
152 
153         if dc is None:
154             raise ValueError("dc pin is required.")
155 
156         self.width = 240
157         self.height = 240
158         self.spi = spi
159         self.reset = reset
160         self.dc = dc
161         self.cs = cs
162         self.backlight = backlight
163         self._rotation = rotation % 8
164 
165         self.hard_reset()
166         time.sleep_ms(100)
167 
168         self._write(0xEF)
169         self._write(0xEB, b'\x14')
170         self._write(0xFE)
171         self._write(0xEF)
172         self._write(0xEB, b'\x14')
173         self._write(0x84, b'\x40')
174         self._write(0x85, b'\xFF')
175         self._write(0x86, b'\xFF')
176         self._write(0x87, b'\xFF')
177         self._write(0x88, b'\x0A')
178         self._write(0x89, b'\x21')
179         self._write(0x8A, b'\x00')
180         self._write(0x8B, b'\x80')
181         self._write(0x8C, b'\x01')
182         self._write(0x8D, b'\x01')
183         self._write(0x8E, b'\xFF')
184         self._write(0x8F, b'\xFF')
185         self._write(0xB6, b'\x00\x00')
186         self._write(0x3A, b'\x55')
187         self._write(0x90, b'\x08\x08\x08\x08')
188         self._write(0xBD, b'\x06')
189         self._write(0xBC, b'\x00')
190         self._write(0xFF, b'\x60\x01\x04')
191         self._write(0xC3, b'\x13')
192         self._write(0xC4, b'\x13')
193         self._write(0xC9, b'\x22')
194         self._write(0xBE, b'\x11')
195         self._write(0xE1, b'\x10\x0E')
196         self._write(0xDF, b'\x21\x0c\x02')
197         self._write(0xF0, b'\x45\x09\x08\x08\x26\x2A')
198         self._write(0xF1, b'\x43\x70\x72\x36\x37\x6F')
199         self._write(0xF2, b'\x45\x09\x08\x08\x26\x2A')
200         self._write(0xF3, b'\x43\x70\x72\x36\x37\x6F')
201         self._write(0xED, b'\x1B\x0B')
202         self._write(0xAE, b'\x77')
203         self._write(0xCD, b'\x63')
204         self._write(0x70, b'\x07\x07\x04\x0E\x0F\x09\x07\x08\x03')
205         self._write(0xE8, b'\x34')
206 
207         self._write(
208             0x62,
209             b'\x18\x0D\x71\xED\x70\x70\x18\x0F\x71\xEF\x70\x70')
210 
211         self._write(
212             0x63,
213             b'\x18\x11\x71\xF1\x70\x70\x18\x13\x71\xF3\x70\x70')
214 
215         self._write(0x64, b'\x28\x29\xF1\x01\xF1\x00\x07')
216         self._write(
217             0x66,
218             b'\x3C\x00\xCD\x67\x45\x45\x10\x00\x00\x00')
219 
220         self._write(
221             0x67,
222             b'\x00\x3C\x00\x00\x00\x01\x54\x10\x32\x98')
223 
224         self._write(0x74, b'\x10\x85\x80\x00\x00\x4E\x00')
225         self._write(0x98, b'\x3e\x07')
226         self._write(0x35)
227         self._write(0x21)
228         self._write(0x11)
229         time.sleep_ms(120)
230 
231         self._write(0x29)
232         time.sleep_ms(20)
233 
234         self.rotation(self._rotation)
235 
236         if backlight is not None:
237             backlight.value(1)
238 
239     def _write(self, command=None, data=None):
240         """SPI write to the device: commands and data."""
241         if self.cs:
242             self.cs.off()
243 
244         if command is not None:
245             self.dc.off()
246             self.spi.write(bytes([command]))
247         if data is not None:
248             self.dc.on()
249             self.spi.write(data)
250 
251         if self.cs:
252             self.cs.on()
253 
254     def hard_reset(self):
255         """Hard reset display."""
256         if self.reset:
257             if self.cs:
258                 self.cs.off()
259 
260             self.reset.on()
261             time.sleep_ms(50)
262             self.reset.off()
263             time.sleep_ms(50)
264             self.reset.on()
265             time.sleep_ms(150)
266 
267             if self.cs:
268                 self.cs.on()
269 
270     def soft_reset(self):
271         """Soft reset display."""
272         self._write(GC9A01_SWRESET)
273         time.sleep_ms(150)
274 
275     def sleep_mode(self, value):
276         """
277         Enable or disable display sleep mode.
278 
279         Args:
280             value (bool): if True enable sleep mode.
281                 if False disable sleep mode
282         """
283         if value:
284             self._write(GC9A01_SLPIN)
285         else:
286             self._write(GC9A01_SLPOUT)
287 
288     def inversion_mode(self, value):
289         """
290         Enable or disable display inversion mode.
291 
292         Args:
293             value (bool): if True enable inversion mode.
294                 if False disable inversion mode
295         """
296         if value:
297             self._write(GC9A01_INVON)
298         else:
299             self._write(GC9A01_INVOFF)
300 
301     def rotation(self, rotation):
302         """
303         Set display rotation.
304 
305         Args:
306             rotation (int):
307 
308                 - 0 - PORTRAIT
309                 - 1 - LANDSCAPE
310                 - 2 - INVERTED PORTRAIT
311                 - 3 - INVERTED LANDSCAPE
312                 - 4 - PORTRAIT MIRRORED
313                 - 5 - LANDSCAPE MIRRORED
314                 - 6 - INVERTED PORTRAIT MIRRORED
315                 - 7 - INVERTED LANDSCAPE MIRRORED
316 
317         """
318 
319         self._rotation = rotation % 8
320         self._write(GC9A01_MADCTL, bytes([ROTATIONS[self._rotation]]))
321 
322     def _set_columns(self, start, end):
323         """
324         Send CASET (column address set) command to display.
325 
326         Args:
327             start (int): column start address
328             end (int): column end address
329         """
330         if start <= end <= self.width:
331             self._write(GC9A01_CASET, _encode_pos(
332                 start, end))
333 
334     def _set_rows(self, start, end):
335         """
336         Send RASET (row address set) command to display.
337 
338         Args:
339             start (int): row start address
340             end (int): row end address
341        """
342         if start <= end <= self.height:
343             self._write(GC9A01_RASET, _encode_pos(
344                 start, end))
345 
346     def _set_window(self, x0, y0, x1, y1):
347         """
348         Set window to column and row address.
349 
350         Args:
351             x0 (int): column start address
352             y0 (int): row start address
353             x1 (int): column end address
354             y1 (int): row end address
355         """
356         self._set_columns(x0, x1)
357         self._set_rows(y0, y1)
358         self._write(GC9A01_RAMWR)
359 
360     def vline(self, x, y, length, color):
361         """
362         Draw vertical line at the given location and color.
363 
364         Args:
365             x (int): x coordinate
366             Y (int): y coordinate
367             length (int): length of line
368             color (int): 565 encoded color
369         """
370         self.fill_rect(x, y, 1, length, color)
371 
372     def hline(self, x, y, length, color):
373         """
374         Draw horizontal line at the given location and color.
375 
376         Args:
377             x (int): x coordinate
378             Y (int): y coordinate
379             length (int): length of line
380             color (int): 565 encoded color
381         """
382         self.fill_rect(x, y, length, 1, color)
383 
384     def pixel(self, x, y, color):
385         """
386         Draw a pixel at the given location and color.
387 
388         Args:
389             x (int): x coordinate
390             Y (int): y coordinate
391             color (int): 565 encoded color
392         """
393         self._set_window(x, y, x, y)
394         self._write(None, _encode_pixel(color))
395 
396     def blit_buffer(self, buffer, x, y, width, height):
397         """
398         Copy buffer to display at the given location.
399 
400         Args:
401             buffer (bytes): Data to copy to display
402             x (int): Top left corner x coordinate
403             Y (int): Top left corner y coordinate
404             width (int): Width
405             height (int): Height
406         """
407         self._set_window(x, y, x + width - 1, y + height - 1)
408         self._write(None, buffer)
409 
410     def rect(self, x, y, w, h, color):
411         """
412         Draw a rectangle at the given location, size and color.
413 
414         Args:
415             x (int): Top left corner x coordinate
416             y (int): Top left corner y coordinate
417             width (int): Width in pixels
418             height (int): Height in pixels
419             color (int): 565 encoded color
420         """
421         self.hline(x, y, w, color)
422         self.vline(x, y, h, color)
423         self.vline(x + w - 1, y, h, color)
424         self.hline(x, y + h - 1, w, color)
425 
426     def fill_rect(self, x, y, width, height, color):
427         """
428         Draw a rectangle at the given location, size and filled with color.
429 
430         Args:
431             x (int): Top left corner x coordinate
432             y (int): Top left corner y coordinate
433             width (int): Width in pixels
434             height (int): Height in pixels
435             color (int): 565 encoded color
436         """
437         self._set_window(x, y, x + width - 1, y + height - 1)
438         chunks, rest = divmod(width * height, _BUFFER_SIZE)
439         pixel = _encode_pixel(color)
440         self.dc.on()
441         if chunks:
442             data = pixel * _BUFFER_SIZE
443             for _ in range(chunks):
444                 self._write(None, data)
445         if rest:
446             self._write(None, pixel * rest)
447 
448     def fill(self, color):
449         """
450         Fill the entire FrameBuffer with the specified color.
451 
452         Args:
453             color (int): 565 encoded color
454         """
455         self.fill_rect(0, 0, self.width, self.height, color)
456 
457     def line(self, x0, y0, x1, y1, color):
458         """
459         Draw a single pixel wide line starting at x0, y0 and ending at x1, y1.
460 
461         Args:
462             x0 (int): Start point x coordinate
463             y0 (int): Start point y coordinate
464             x1 (int): End point x coordinate
465             y1 (int): End point y coordinate
466             color (int): 565 encoded color
467         """
468         steep = abs(y1 - y0) > abs(x1 - x0)
469         if steep:
470             x0, y0 = y0, x0
471             x1, y1 = y1, x1
472         if x0 > x1:
473             x0, x1 = x1, x0
474             y0, y1 = y1, y0
475         dx = x1 - x0
476         dy = abs(y1 - y0)
477         err = dx // 2
478         if y0 < y1:
479             ystep = 1
480         else:
481             ystep = -1
482         while x0 <= x1:
483             if steep:
484                 self.pixel(y0, x0, color)
485             else:
486                 self.pixel(x0, y0, color)
487             err -= dy
488             if err < 0:
489                 y0 += ystep
490                 err += dx
491             x0 += 1
492 
493     def vscrdef(self, tfa, vsa, bfa):
494         """
495         Set Vertical Scrolling Definition.
496 
497         To scroll a 135x240 display these values should be 40, 240, 40.
498         There are 40 lines above the display that are not shown followed by
499         240 lines that are shown followed by 40 more lines that are not shown.
500         You could write to these areas off display and scroll them into view by
501         changing the TFA, VSA and BFA values.
502 
503         Args:
504             tfa (int): Top Fixed Area
505             vsa (int): Vertical Scrolling Area
506             bfa (int): Bottom Fixed Area
507         """
508         struct.pack(">HHH")
509         self._write(GC9A01_VSCRDEF, struct.pack(">HHH", tfa, vsa, bfa))
510 
511     def vscsad(self, vssa):
512         """
513         Set Vertical Scroll Start Address of RAM.
514 
515         Defines which line in the Frame Memory will be written as the first
516         line after the last line of the Top Fixed Area on the display
517 
518         Example:
519 
520             for line in range(40, 280, 1):
521                 tft.vscsad(line)
522                 utime.sleep(0.01)
523 
524         Args:
525             vssa (int): Vertical Scrolling Start Address
526 
527         """
528         self._write(GC9A01_VSCSAD, struct.pack(">H", vssa))
529 
530     def _text8(self, font, text, x0, y0, color=WHITE, background=BLACK):
531         """
532         Internal method to write characters with width of 8 and
533         heights of 8 or 16.
534 
535         Args:
536             font (module): font module to use
537             text (str): text to write
538             x0 (int): column to start drawing at
539             y0 (int): row to start drawing at
540             color (int): 565 encoded color to use for characters
541             background (int): 565 encoded color to use for background
542         """
543         for char in text:
544             ch = ord(char)
545             if (font.FIRST <= ch < font.LAST
546                     and x0+font.WIDTH <= self.width
547                     and y0+font.HEIGHT <= self.height):
548 
549                 if font.HEIGHT == 8:
550                     passes = 1
551                     size = 8
552                     each = 0
553                 else:
554                     passes = 2
555                     size = 16
556                     each = 8
557 
558                 for line in range(passes):
559                     idx = (ch-font.FIRST)*size+(each*line)
560                     #
561                     # Yes, this looks bad, but it is fast
562                     #
563                     buffer = struct.pack(
564                         '>64H',
565                         color if font.FONT[idx] & _BIT7 else background,
566                         color if font.FONT[idx] & _BIT6 else background,
567                         color if font.FONT[idx] & _BIT5 else background,
568                         color if font.FONT[idx] & _BIT4 else background,
569                         color if font.FONT[idx] & _BIT3 else background,
570                         color if font.FONT[idx] & _BIT2 else background,
571                         color if font.FONT[idx] & _BIT1 else background,
572                         color if font.FONT[idx] & _BIT0 else background,
573                         color if font.FONT[idx+1] & _BIT7 else background,
574                         color if font.FONT[idx+1] & _BIT6 else background,
575                         color if font.FONT[idx+1] & _BIT5 else background,
576                         color if font.FONT[idx+1] & _BIT4 else background,
577                         color if font.FONT[idx+1] & _BIT3 else background,
578                         color if font.FONT[idx+1] & _BIT2 else background,
579                         color if font.FONT[idx+1] & _BIT1 else background,
580                         color if font.FONT[idx+1] & _BIT0 else background,
581                         color if font.FONT[idx+2] & _BIT7 else background,
582                         color if font.FONT[idx+2] & _BIT6 else background,
583                         color if font.FONT[idx+2] & _BIT5 else background,
584                         color if font.FONT[idx+2] & _BIT4 else background,
585                         color if font.FONT[idx+2] & _BIT3 else background,
586                         color if font.FONT[idx+2] & _BIT2 else background,
587                         color if font.FONT[idx+2] & _BIT1 else background,
588                         color if font.FONT[idx+2] & _BIT0 else background,
589                         color if font.FONT[idx+3] & _BIT7 else background,
590                         color if font.FONT[idx+3] & _BIT6 else background,
591                         color if font.FONT[idx+3] & _BIT5 else background,
592                         color if font.FONT[idx+3] & _BIT4 else background,
593                         color if font.FONT[idx+3] & _BIT3 else background,
594                         color if font.FONT[idx+3] & _BIT2 else background,
595                         color if font.FONT[idx+3] & _BIT1 else background,
596                         color if font.FONT[idx+3] & _BIT0 else background,
597                         color if font.FONT[idx+4] & _BIT7 else background,
598                         color if font.FONT[idx+4] & _BIT6 else background,
599                         color if font.FONT[idx+4] & _BIT5 else background,
600                         color if font.FONT[idx+4] & _BIT4 else background,
601                         color if font.FONT[idx+4] & _BIT3 else background,
602                         color if font.FONT[idx+4] & _BIT2 else background,
603                         color if font.FONT[idx+4] & _BIT1 else background,
604                         color if font.FONT[idx+4] & _BIT0 else background,
605                         color if font.FONT[idx+5] & _BIT7 else background,
606                         color if font.FONT[idx+5] & _BIT6 else background,
607                         color if font.FONT[idx+5] & _BIT5 else background,
608                         color if font.FONT[idx+5] & _BIT4 else background,
609                         color if font.FONT[idx+5] & _BIT3 else background,
610                         color if font.FONT[idx+5] & _BIT2 else background,
611                         color if font.FONT[idx+5] & _BIT1 else background,
612                         color if font.FONT[idx+5] & _BIT0 else background,
613                         color if font.FONT[idx+6] & _BIT7 else background,
614                         color if font.FONT[idx+6] & _BIT6 else background,
615                         color if font.FONT[idx+6] & _BIT5 else background,
616                         color if font.FONT[idx+6] & _BIT4 else background,
617                         color if font.FONT[idx+6] & _BIT3 else background,
618                         color if font.FONT[idx+6] & _BIT2 else background,
619                         color if font.FONT[idx+6] & _BIT1 else background,
620                         color if font.FONT[idx+6] & _BIT0 else background,
621                         color if font.FONT[idx+7] & _BIT7 else background,
622                         color if font.FONT[idx+7] & _BIT6 else background,
623                         color if font.FONT[idx+7] & _BIT5 else background,
624                         color if font.FONT[idx+7] & _BIT4 else background,
625                         color if font.FONT[idx+7] & _BIT3 else background,
626                         color if font.FONT[idx+7] & _BIT2 else background,
627                         color if font.FONT[idx+7] & _BIT1 else background,
628                         color if font.FONT[idx+7] & _BIT0 else background
629                     )
630                     self.blit_buffer(buffer, x0, y0+8*line, 8, 8)
631 
632                 x0 += 8
633 
634     def _text16(self, font, text, x0, y0, color=WHITE, background=BLACK):
635         """
636         Internal method to draw characters with width of 16 and heights of 16
637         or 32.
638 
639         Args:
640             font (module): font module to use
641             text (str): text to write
642             x0 (int): column to start drawing at
643             y0 (int): row to start drawing at
644             color (int): 565 encoded color to use for characters
645             background (int): 565 encoded color to use for background
646         """
647         for char in text:
648             ch = ord(char)
649             if (font.FIRST <= ch < font.LAST
650                     and x0+font.WIDTH <= self.width
651                     and y0+font.HEIGHT <= self.height):
652 
653                 if font.HEIGHT == 16:
654                     passes = 2
655                     size = 32
656                     each = 16
657                 else:
658                     passes = 4
659                     size = 64
660                     each = 16
661 
662                 for line in range(passes):
663                     idx = (ch-font.FIRST)*size+(each*line)
664                     #
665                     # And this looks even worse, but it is fast
666                     #
667                     buffer = struct.pack(
668                         '>128H',
669                         color if font.FONT[idx] & _BIT7 else background,
670                         color if font.FONT[idx] & _BIT6 else background,
671                         color if font.FONT[idx] & _BIT5 else background,
672                         color if font.FONT[idx] & _BIT4 else background,
673                         color if font.FONT[idx] & _BIT3 else background,
674                         color if font.FONT[idx] & _BIT2 else background,
675                         color if font.FONT[idx] & _BIT1 else background,
676                         color if font.FONT[idx] & _BIT0 else background,
677                         color if font.FONT[idx+1] & _BIT7 else background,
678                         color if font.FONT[idx+1] & _BIT6 else background,
679                         color if font.FONT[idx+1] & _BIT5 else background,
680                         color if font.FONT[idx+1] & _BIT4 else background,
681                         color if font.FONT[idx+1] & _BIT3 else background,
682                         color if font.FONT[idx+1] & _BIT2 else background,
683                         color if font.FONT[idx+1] & _BIT1 else background,
684                         color if font.FONT[idx+1] & _BIT0 else background,
685                         color if font.FONT[idx+2] & _BIT7 else background,
686                         color if font.FONT[idx+2] & _BIT6 else background,
687                         color if font.FONT[idx+2] & _BIT5 else background,
688                         color if font.FONT[idx+2] & _BIT4 else background,
689                         color if font.FONT[idx+2] & _BIT3 else background,
690                         color if font.FONT[idx+2] & _BIT2 else background,
691                         color if font.FONT[idx+2] & _BIT1 else background,
692                         color if font.FONT[idx+2] & _BIT0 else background,
693                         color if font.FONT[idx+3] & _BIT7 else background,
694                         color if font.FONT[idx+3] & _BIT6 else background,
695                         color if font.FONT[idx+3] & _BIT5 else background,
696                         color if font.FONT[idx+3] & _BIT4 else background,
697                         color if font.FONT[idx+3] & _BIT3 else background,
698                         color if font.FONT[idx+3] & _BIT2 else background,
699                         color if font.FONT[idx+3] & _BIT1 else background,
700                         color if font.FONT[idx+3] & _BIT0 else background,
701                         color if font.FONT[idx+4] & _BIT7 else background,
702                         color if font.FONT[idx+4] & _BIT6 else background,
703                         color if font.FONT[idx+4] & _BIT5 else background,
704                         color if font.FONT[idx+4] & _BIT4 else background,
705                         color if font.FONT[idx+4] & _BIT3 else background,
706                         color if font.FONT[idx+4] & _BIT2 else background,
707                         color if font.FONT[idx+4] & _BIT1 else background,
708                         color if font.FONT[idx+4] & _BIT0 else background,
709                         color if font.FONT[idx+5] & _BIT7 else background,
710                         color if font.FONT[idx+5] & _BIT6 else background,
711                         color if font.FONT[idx+5] & _BIT5 else background,
712                         color if font.FONT[idx+5] & _BIT4 else background,
713                         color if font.FONT[idx+5] & _BIT3 else background,
714                         color if font.FONT[idx+5] & _BIT2 else background,
715                         color if font.FONT[idx+5] & _BIT1 else background,
716                         color if font.FONT[idx+5] & _BIT0 else background,
717                         color if font.FONT[idx+6] & _BIT7 else background,
718                         color if font.FONT[idx+6] & _BIT6 else background,
719                         color if font.FONT[idx+6] & _BIT5 else background,
720                         color if font.FONT[idx+6] & _BIT4 else background,
721                         color if font.FONT[idx+6] & _BIT3 else background,
722                         color if font.FONT[idx+6] & _BIT2 else background,
723                         color if font.FONT[idx+6] & _BIT1 else background,
724                         color if font.FONT[idx+6] & _BIT0 else background,
725                         color if font.FONT[idx+7] & _BIT7 else background,
726                         color if font.FONT[idx+7] & _BIT6 else background,
727                         color if font.FONT[idx+7] & _BIT5 else background,
728                         color if font.FONT[idx+7] & _BIT4 else background,
729                         color if font.FONT[idx+7] & _BIT3 else background,
730                         color if font.FONT[idx+7] & _BIT2 else background,
731                         color if font.FONT[idx+7] & _BIT1 else background,
732                         color if font.FONT[idx+7] & _BIT0 else background,
733                         color if font.FONT[idx+8] & _BIT7 else background,
734                         color if font.FONT[idx+8] & _BIT6 else background,
735                         color if font.FONT[idx+8] & _BIT5 else background,
736                         color if font.FONT[idx+8] & _BIT4 else background,
737                         color if font.FONT[idx+8] & _BIT3 else background,
738                         color if font.FONT[idx+8] & _BIT2 else background,
739                         color if font.FONT[idx+8] & _BIT1 else background,
740                         color if font.FONT[idx+8] & _BIT0 else background,
741                         color if font.FONT[idx+9] & _BIT7 else background,
742                         color if font.FONT[idx+9] & _BIT6 else background,
743                         color if font.FONT[idx+9] & _BIT5 else background,
744                         color if font.FONT[idx+9] & _BIT4 else background,
745                         color if font.FONT[idx+9] & _BIT3 else background,
746                         color if font.FONT[idx+9] & _BIT2 else background,
747                         color if font.FONT[idx+9] & _BIT1 else background,
748                         color if font.FONT[idx+9] & _BIT0 else background,
749                         color if font.FONT[idx+10] & _BIT7 else background,
750                         color if font.FONT[idx+10] & _BIT6 else background,
751                         color if font.FONT[idx+10] & _BIT5 else background,
752                         color if font.FONT[idx+10] & _BIT4 else background,
753                         color if font.FONT[idx+10] & _BIT3 else background,
754                         color if font.FONT[idx+10] & _BIT2 else background,
755                         color if font.FONT[idx+10] & _BIT1 else background,
756                         color if font.FONT[idx+10] & _BIT0 else background,
757                         color if font.FONT[idx+11] & _BIT7 else background,
758                         color if font.FONT[idx+11] & _BIT6 else background,
759                         color if font.FONT[idx+11] & _BIT5 else background,
760                         color if font.FONT[idx+11] & _BIT4 else background,
761                         color if font.FONT[idx+11] & _BIT3 else background,
762                         color if font.FONT[idx+11] & _BIT2 else background,
763                         color if font.FONT[idx+11] & _BIT1 else background,
764                         color if font.FONT[idx+11] & _BIT0 else background,
765                         color if font.FONT[idx+12] & _BIT7 else background,
766                         color if font.FONT[idx+12] & _BIT6 else background,
767                         color if font.FONT[idx+12] & _BIT5 else background,
768                         color if font.FONT[idx+12] & _BIT4 else background,
769                         color if font.FONT[idx+12] & _BIT3 else background,
770                         color if font.FONT[idx+12] & _BIT2 else background,
771                         color if font.FONT[idx+12] & _BIT1 else background,
772                         color if font.FONT[idx+12] & _BIT0 else background,
773                         color if font.FONT[idx+13] & _BIT7 else background,
774                         color if font.FONT[idx+13] & _BIT6 else background,
775                         color if font.FONT[idx+13] & _BIT5 else background,
776                         color if font.FONT[idx+13] & _BIT4 else background,
777                         color if font.FONT[idx+13] & _BIT3 else background,
778                         color if font.FONT[idx+13] & _BIT2 else background,
779                         color if font.FONT[idx+13] & _BIT1 else background,
780                         color if font.FONT[idx+13] & _BIT0 else background,
781                         color if font.FONT[idx+14] & _BIT7 else background,
782                         color if font.FONT[idx+14] & _BIT6 else background,
783                         color if font.FONT[idx+14] & _BIT5 else background,
784                         color if font.FONT[idx+14] & _BIT4 else background,
785                         color if font.FONT[idx+14] & _BIT3 else background,
786                         color if font.FONT[idx+14] & _BIT2 else background,
787                         color if font.FONT[idx+14] & _BIT1 else background,
788                         color if font.FONT[idx+14] & _BIT0 else background,
789                         color if font.FONT[idx+15] & _BIT7 else background,
790                         color if font.FONT[idx+15] & _BIT6 else background,
791                         color if font.FONT[idx+15] & _BIT5 else background,
792                         color if font.FONT[idx+15] & _BIT4 else background,
793                         color if font.FONT[idx+15] & _BIT3 else background,
794                         color if font.FONT[idx+15] & _BIT2 else background,
795                         color if font.FONT[idx+15] & _BIT1 else background,
796                         color if font.FONT[idx+15] & _BIT0 else background
797                     )
798                     self.blit_buffer(buffer, x0, y0+8*line, 16, 8)
799             x0 += font.WIDTH
800 
801     def text(self, font, text, x0, y0, color=WHITE, background=BLACK):
802         """
803         Draw text on display in specified font and colors. 8 and 16 bit wide
804         fonts are supported.
805 
806         Args:
807             font (module): font module to use.
808             text (str): text to write
809             x0 (int): column to start drawing at
810             y0 (int): row to start drawing at
811             color (int): 565 encoded color to use for characters
812             background (int): 565 encoded color to use for background
813         """
814         if font.WIDTH == 8:
815             self._text8(font, text, x0, y0, color, background)
816         else:
817             self._text16(font, text, x0, y0, color, background)
818 
819     def bitmap(self, bitmap, x, y, index=0):
820         """
821         Draw a bitmap on display at the specified column and row
822 
823         Args:
824             bitmap (bitmap_module): The module containing the bitmap to draw
825             x (int): column to start drawing at
826             y (int): row to start drawing at
827             index (int): Optional index of bitmap to draw from multiple bitmap
828                 module
829 
830         """
831         bitmap_size = bitmap.HEIGHT * bitmap.WIDTH
832         buffer_len = bitmap_size * 2
833         buffer = bytearray(buffer_len)
834         bs_bit = bitmap.BPP * bitmap_size * index if index > 0 else 0
835 
836         for i in range(0, buffer_len, 2):
837             color_index = 0
838             for bit in range(bitmap.BPP):
839                 color_index <<= 1
840                 color_index |= (bitmap.BITMAP[bs_bit // 8]
841                                 & 1 << (7 - (bs_bit % 8))) > 0
842                 bs_bit += 1
843 
844             color = bitmap.PALETTE[color_index]
845             buffer[i] = color & 0xff00 >> 8
846             buffer[i + 1] = color_index & 0xff
847 
848         self.blit_buffer(buffer, x, y, bitmap.WIDTH, bitmap.HEIGHT)
849 
850     # @micropython.native
851     def write(self, font, string, x, y, fg=WHITE, bg=BLACK):
852         """
853         Write a string using a converted true-type font on the display starting
854         at the specified column and row
855 
856         Args:
857             font (font): The module containing the converted true-type font
858             s (string): The string to write
859             x (int): column to start writing
860             y (int): row to start writing
861             fg (int): foreground color, optional, defaults to WHITE
862             bg (int): background color, optional, defaults to BLACK
863         """
864         buffer_len = font.HEIGHT * font.MAX_WIDTH * 2
865         buffer = bytearray(buffer_len)
866         fg_hi = (fg & 0xff00) >> 8
867         fg_lo = fg & 0xff
868 
869         bg_hi = (bg & 0xff00) >> 8
870         bg_lo = bg & 0xff
871 
872         for character in string:
873             try:
874                 char_index = font.MAP.index(character)
875                 offset = char_index * font.OFFSET_WIDTH
876                 bs_bit = font.OFFSETS[offset]
877                 if font.OFFSET_WIDTH > 1:
878                     bs_bit = (bs_bit << 8) + font.OFFSETS[offset + 1]
879 
880                 if font.OFFSET_WIDTH > 2:
881                     bs_bit = (bs_bit << 8) + font.OFFSETS[offset + 2]
882 
883                 char_width = font.WIDTHS[char_index]
884                 buffer_needed = char_width * font.HEIGHT * 2
885 
886                 for i in range(0, buffer_needed, 2):
887                     if font.BITMAPS[bs_bit // 8] & 1 << (7 - (bs_bit % 8)) > 0:
888                         buffer[i] = fg_hi
889                         buffer[i + 1] = fg_lo
890                     else:
891                         buffer[i] = bg_hi
892                         buffer[i + 1] = bg_lo
893 
894                     bs_bit += 1
895 
896                 to_col = x + char_width - 1
897                 to_row = y + font.HEIGHT - 1
898                 if self.width > to_col and self.height > to_row:
899                     self._set_window(x, y, to_col, to_row)
900                     self._write(None, buffer[0:buffer_needed])
901 
902                 x += char_width
903 
904             except ValueError:
905                 pass
906 
907     def write_width(self, font, string):
908         """
909         Returns the width in pixels of the string if it was written with the
910         specified font
911 
912         Args:
913             font (font): The module containing the converted true-type font
914             string (string): The string to measure
915         """
916         width = 0
917         for character in string:
918             try:
919                 char_index = font.MAP.index(character)
920                 width += font.WIDTHS[char_index]
921 
922             except ValueError:
923                 pass
924 
925         return width