61浏览
查看: 61|回复: 1

[K10教程] 行空板基于micropython实现实现英文,中文和日文显示

[复制链接]
本帖最后由 PY学习笔记 于 2024-12-1 20:01 编辑

行空板基于micropython-easydisplay和micropython-ili9341库实现英文,中文和日文显示。
首先,是easydisplay的代码:
  1. # Github: https://github.com/funnygeeker/micropython-easydisplay
  2. # Author: funnygeeker
  3. # Licence: MIT
  4. # Date: 2023/2/18
  5. #
  6. # 参考项目:
  7. # https://github.com/AntonVanke/micropython-ufont
  8. # https://github.com/boochow/MicroPython-ST7735/blob/master/tftbmp.py
  9. #
  10. # 参考资料:
  11. # PBM图像显示:https://www.bilibili.com/video/av798158808
  12. # PBM文件格式:https://www.cnblogs.com/SeekHit/p/7055748.html
  13. # PBM文件转换:https://blog.csdn.net/jd3096/article/details/121319042
  14. # 灰度化、二值化:https://blog.csdn.net/li_wen01/article/details/72867057
  15. # Framebuffer 的 Palette: https://forum.micropython.org/viewtopic.php?t=12857
  16. from io import BytesIO
  17. from struct import unpack
  18. from framebuf import FrameBuffer, MONO_HLSB, RGB565
  19. class EasyDisplay:
  20.     READ_SIZE = 32  # Limit the picture read size to prevent memory errors in low-performance development boards
  21.     def __init__(self, display,
  22.                  color_type,
  23.                  font: str = None,
  24.                  key: int = -1,
  25.                  show: bool = None,
  26.                  clear: bool = None,
  27.                  invert: bool = False,
  28.                  color: int = 0xFFFF,
  29.                  bg_color: int = 0,
  30.                  size: int = None,
  31.                  auto_wrap: bool = False,
  32.                  half_char: bool = True,
  33.                  line_spacing: int = 0,
  34.                  *args, **kwargs):
  35.         """
  36.         初始化 EasyDisplay
  37.         Args:
  38.             display: The display instance
  39.                 表示显示的实例
  40.             color_type: Color type of screen, "MONO" or "RGB565"
  41.                 屏幕的颜色类型,"MONO" 或者 “RGB565”
  42.             font: The location of the font file
  43.                 字体文件位置
  44.             key: The specified color will be treated as transparent (only applicable for Framebuffer mode)
  45.                 指定的颜色将被视为透明(仅适用于 Framebuffer 模式)
  46.             show: Show immediately (only applicable for Framebuffer mode)
  47.                 立即显示(仅适用于 Framebuffer 模式)
  48.             clear: Clear the screen
  49.                 清理屏幕
  50.             invert: Invert colors
  51.                 反转颜色
  52.             color_type: Image format, "RGB565" for RGB565 screen, "MONO" for black and white screen
  53.                 图像格式,RGB565 屏幕用 "RGB565",黑白 屏幕用 "MONO"
  54.             color: The main color of the image (only effective when displaying black and white images on a color screen)
  55.                 图像主体颜色(仅彩色屏幕显示黑白图像时生效)
  56.             bg_color: The background color of the image (only effective when displaying black and white images on a color screen)
  57.                 图像背景颜色(仅彩色屏幕显示黑白图像时生效)
  58.             size: Font size
  59.                 文本字体大小
  60.             auto_wrap: Automatically wrap text
  61.                 文本自动换行
  62.             half_char: Display ASCII characters in half width
  63.                 半宽显示 ASCII 字符
  64.             line_spacing: Line spacing for text
  65.                 文本行间距
  66.         """
  67.         self.display = display
  68.         self._buffer = hasattr(display, 'buffer')  # buffer: 驱动是否使用了帧缓冲区,False(SPI 直接驱动) / True(Framebuffer)
  69.         self._font = None
  70.         self._key = key
  71.         self._show = show
  72.         self._clear = clear
  73.         self.invert = invert
  74.         self.color_type = color_type
  75.         self.color = color
  76.         self.bg_color = bg_color
  77.         self.size = size
  78.         self.auto_wrap = auto_wrap
  79.         self.half_char = half_char
  80.         self.line_spacing = line_spacing
  81.         self.font_size = None
  82.         self.font_bmf_info = None
  83.         self.font_version = None
  84.         self.font_file = None
  85.         self.font_map_mode = None
  86.         self.font_start_bitmap = None
  87.         self.font_bitmap_size = None
  88.         if font:
  89.             self.load_font(font)
  90.     # Framebuffer Function: https://docs.micropython.org/en/latest/library/framebuf.html
  91.     def fill(self, *args, **kwargs):
  92.         self.display.fill(*args, **kwargs)
  93.     def pixel(self, *args, **kwargs):
  94.         return self.display.pixel(*args, **kwargs)
  95.     def hline(self, *args, **kwargs):
  96.         self.display.hline(*args, **kwargs)
  97.     def vline(self, *args, **kwargs):
  98.         self.display.vline(*args, **kwargs)
  99.     def line(self, *args, **kwargs):
  100.         self.display.line(*args, **kwargs)
  101.     def rect(self, *args, **kwargs):
  102.         self.display.rect(*args, **kwargs)
  103.     def fill_rect(self, *args, **kwargs):
  104.         self.display.fill_rect(*args, **kwargs)
  105.     def scroll(self, *args, **kwargs):
  106.         self.display.scroll(*args, **kwargs)
  107.     def blit(self, *args, **kwargs):
  108.         self.display.blit(*args, **kwargs)
  109.     def ellipse(self, *args, **kwargs):
  110.         self.display.ellipse(*args, **kwargs)
  111.     def poly(self, *args, **kwargs):
  112.         self.display.poly(*args, **kwargs)
  113.     # Only partial screen driver support
  114.     def circle(self, *args, **kwargs):
  115.         self.display.circle(*args, **kwargs)
  116.     def fill_circle(self, *args, **kwargs):
  117.         self.display.fill_circle(*args, **kwargs)
  118.     def clear(self):
  119.         """
  120.         Clear screen
  121.         """
  122.         self.display.fill(0)
  123.     def show(self):
  124.         """
  125.         Display
  126.         """
  127.         try:
  128.             self.display.show()
  129.         except AttributeError:
  130.             pass
  131.     @staticmethod
  132.     def rgb565_color(r, g, b):
  133.         """
  134.         Convert red, green and blue values (0-255) into a 16-bit 565 encoding.
  135.         """
  136.         return (r & 0xf8) << 8 | (g & 0xfc) << 3 | b >> 3
  137.     def _get_index(self, word: str) -> int:
  138.         """
  139.         Get Text Index 获取文字索引
  140.         Args:
  141.             word: Character 字符
  142.         """
  143.         word_code = ord(word)
  144.         start = 0x10
  145.         end = self.font_start_bitmap
  146.         _seek = self._font.seek
  147.         _font_read = self._font.read
  148.         while start <= end:
  149.             mid = ((start + end) // 4) * 2
  150.             _seek(mid, 0)
  151.             target_code = unpack(">H", _font_read(2))[0]
  152.             if word_code == target_code:
  153.                 return (mid - 16) >> 1
  154.             elif word_code < target_code:
  155.                 end = mid - 2
  156.             else:
  157.                 start = mid + 2
  158.         return -1
  159.     # @timeit
  160.     @staticmethod
  161.     def _hlsb_font_size(bytearray_data: bytearray, new_size: int, old_size: int) -> bytearray:
  162.         """
  163.         Scale HLSB Characters 缩放字符
  164.         Args:
  165.             bytearray_data: Source char data 源字符数据
  166.             new_size: New char size 新字符大小
  167.             old_size: Old char size 旧字符大小
  168.         Returns:
  169.             Scaled character data 缩放后的数据
  170.         """
  171.         r = range(new_size)  # Preload functions to avoid repeated execution and improve efficiency
  172.         if old_size == new_size:
  173.             return bytearray_data
  174.         _t = bytearray(new_size * ((new_size >> 3) + 1))
  175.         _new_index = -1
  176.         for _col in r:
  177.             for _row in r:
  178.                 if _row % 8 == 0:
  179.                     _new_index += 1
  180.                 _old_index = int(_col / (new_size / old_size)) * old_size + int(_row / (new_size / old_size))
  181.                 _t[_new_index] = _t[_new_index] | (
  182.                         (bytearray_data[_old_index >> 3] >> (7 - _old_index % 8) & 1) << (7 - _row % 8))
  183.         return _t
  184.     def get_bitmap(self, word: str) -> bytes:
  185.         """
  186.         Get Dot Matrix Image 获取点阵图
  187.         Args:
  188.             word: Single character 单个字符
  189.         Returns:
  190.             Bytes representing the dot matrix image of the character 字符点阵
  191.         """
  192.         index = self._get_index(word)
  193.         if index == -1:
  194.             return b'\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x0f\xcf\xf3\xcf\xf3\xff\xf3\xff\xcf\xff?\xff?\xff\xff\xff' \
  195.                    b'?\xff?\xff\xff\xff\xff'  # Returns the question mark icon
  196.         self._font.seek(self.font_start_bitmap + index * self.font_bitmap_size, 0)
  197.         return self._font.read(self.font_bitmap_size)
  198.     def load_font(self, file: str):
  199.         """
  200.         Load Font File 加载字体文件
  201.         Args:
  202.             file: Path to the font file 文件路径
  203.         """
  204.         self.font_file = file
  205.         self._font = open(file, "rb")
  206.         # 获取字体文件信息
  207.         #  字体文件信息大小 16 byte ,按照顺序依次是
  208.         #   文件标识 2 byte
  209.         #   版本号 1 byte
  210.         #   映射方式 1 byte
  211.         #   位图开始字节 3 byte
  212.         #   字号 1 byte
  213.         #   单字点阵字节大小 1 byte
  214.         #   保留 7 byte
  215.         self.font_bmf_info = self._font.read(16)
  216.         # 判断字体是否正确,文件头和常用的图像格式 BMP 相同,需要添加版本验证来辅助验证
  217.         if self.font_bmf_info[0:2] != b"BM":
  218.             raise TypeError("Incorrect font file format: {}".format(file))
  219.         self.font_version = self.font_bmf_info[2]
  220.         if self.font_version != 3:
  221.             raise TypeError("Incorrect font file version: {}".format(self.font_version))
  222.         # 映射方式,目前映射方式并没有加以验证,原因是 MONO 最易于处理
  223.         self.font_map_mode = self.font_bmf_info[3]
  224.         # 位图开始字节,位图数据位于文件尾,需要通过位图开始字节来确定字体数据实际位置
  225.         self.font_start_bitmap = unpack(">I", b'\x00' + self.font_bmf_info[4:7])[0]
  226.         # 字体大小,默认的文字字号,用于缩放方面的处理
  227.         self.font_size = self.font_bmf_info[7]
  228.         if self.size is None:
  229.             self.size = int(self.font_size)
  230.         # 点阵所占字节,用来定位字体数据位置
  231.         self.font_bitmap_size = self.font_bmf_info[8]
  232.     def text(self, s: str, x: int, y: int,
  233.              color: int = None, bg_color: int = None, size: int = None,
  234.              half_char: bool = None, auto_wrap: bool = None, show: bool = None, clear: bool = None,
  235.              key: bool = None, invert: bool = None, line_spacing: int = None, *args, **kwargs):
  236.         """
  237.         Args:
  238.             s: String
  239.                 字符串
  240.             x: X-coordinate of the string
  241.                 x 坐标
  242.             y: Y-coordinate of the string
  243.                 y 坐标
  244.             color: Text color (RGB565 range is 0-65535, MONO range is 0 and greater than zero - typically use 1)
  245.                 文字颜色 (RGB565 范围为 0-65535,MONO 范围为 0 和 大于零-通常使用 1)
  246.             bg_color: Text background color (RGB565 range is 0-65535, MONO range is 0 and greater than zero - typically use 1)\
  247.                 文字背景颜色 (RGB565 范围为 0-65535,MONO 范围为 0 和 大于零-通常使用 1)
  248.             key: Transparent color, when color matches key, it becomes transparent (only applicable in Framebuffer mode)
  249.                 透明色,当颜色与 key 相同时则透明 (仅适用于 Framebuffer 模式)
  250.             size: Text size
  251.                 文字大小
  252.             show: Show immediately
  253.                 立即显示
  254.             clear: Clear buffer / Clear screen
  255.                 清理缓冲区 / 清理屏幕
  256.             invert: Invert (MONO)
  257.                 逆置(MONO)
  258.             auto_wrap: Enable auto wrap
  259.                 自动换行
  260.             half_char: Display ASCII characters in half width
  261.                 半宽显示 ASCII 字符
  262.             line_spacing: Line spacing
  263.                 行间距
  264.         """
  265.         if color is None:
  266.             color = self.color
  267.         if bg_color is None:
  268.             bg_color = self.bg_color
  269.         if key is None:
  270.             key = self._key
  271.         if size is None:
  272.             size = self.size
  273.         if show is None:
  274.             show = self._show
  275.         if clear is None:
  276.             clear = self._clear
  277.         if invert is None:
  278.             invert = self.invert
  279.         if auto_wrap is None:
  280.             auto_wrap = self.auto_wrap
  281.         if half_char is None:
  282.             half_char = self.half_char
  283.         color_type = self.color_type
  284.         if line_spacing is None:
  285.             line_spacing = self.line_spacing
  286.         # 如果没有指定字号则使用默认字号
  287.         font_size = size or self.font_size
  288.         # 记录初始的 x 位置
  289.         init_x = x
  290.         try:
  291.             _seek = self._font.seek
  292.         except AttributeError:
  293.             raise AttributeError("The font file is not loaded... Did you forgot?")
  294.         dp = self.display
  295.         font_offset = font_size // 2
  296.         # 颜色反转
  297.         if invert:
  298.             color, bg_color = bg_color, color
  299.         # 配置调色板
  300.         if color_type == "MONO":
  301.             palette = FrameBuffer(bytearray(1), 2, 1, MONO_HLSB)  # MONO pixels occupy 1 byte for every 8 pixels
  302.         elif color_type == "RGB565":
  303.             palette = FrameBuffer(bytearray(4), 2, 1, RGB565)  # RGB565 pixels occupy 2 bytes for every 1 pixel
  304.         else:
  305.             raise KeyError("Unsupported color_type: {}".format(color_type))
  306.         palette.pixel(1, 0, color)
  307.         palette.pixel(0, 0, bg_color)
  308.         # 清屏
  309.         if clear:
  310.             self.clear()
  311.         for char in s:
  312.             if auto_wrap and ((x + font_offset > dp.width and ord(char) < 128 and half_char) or
  313.                               (x + font_size > dp.width and (not half_char or ord(char) > 128))):
  314.                 y += font_size + line_spacing
  315.                 x = init_x
  316.             # 对控制字符的处理
  317.             if char == '\n':
  318.                 y += font_size + line_spacing
  319.                 x = init_x
  320.                 continue
  321.             elif char == '\t':
  322.                 x = ((x // font_size) + 1) * font_size + init_x % font_size
  323.                 continue
  324.             elif ord(char) < 16:
  325.                 continue
  326.             # 超过范围的字符不会显示
  327.             if x > 240 or y > 320:
  328.                 continue
  329.             # 获取字体的点阵数据
  330.             byte_data = self.get_bitmap(char)
  331.             # 准备并缩放字符数据
  332.             byte_data = bytearray(byte_data)
  333.             if font_size != self.font_size:
  334.                 byte_data = self._hlsb_font_size(byte_data, font_size, self.font_size)
  335.             # 显示字符
  336.             fbuf = FrameBuffer(byte_data, font_size, font_size, MONO_HLSB)
  337.             if self._buffer:  # FrameBuffer Driven
  338.                 dp.blit(fbuf, x, y, key, palette)
  339.             else:
  340.                 if color_type == "RGB565":
  341.                     n_fbuf = FrameBuffer(bytearray(font_size * font_size * 2), font_size, font_size, RGB565)
  342.                     n_fbuf.blit(fbuf, 0, 0, key, palette)  # Render black and white pixels to color
  343.                 elif color_type == "MONO":
  344.                     n_fbuf = fbuf  # Not tested
  345.                 else:
  346.                     raise ValueError("Unsupported color_type: {}".format(color_type))
  347.                 dp._writeblock(x, y, x + font_size - 1, y + font_size - 1)
  348.                 dp._data(n_fbuf)
  349.             # 英文字符半格显示
  350.             if ord(char) < 128 and half_char:
  351.                 x += font_offset
  352.             else:
  353.                 x += font_size
  354.         self.show() if show else 0
  355.     def ppm(self, *args, **kwargs):
  356.         self.pbm(*args, **kwargs)
  357.     def pbm(self, file, x, y, key: int = None, show: bool = None, clear: bool = None, invert: bool = False,
  358.             color: int = None, bg_color: int = None):
  359.         """
  360.         Display PBM / PPM Image
  361.         显示 pbm / ppm 图片
  362.         # You can use the Pillow library in python3 to convert the image to PBM format. For example:
  363.         # 您可以通过使用 python3 的 pillow 库将图片转换为 pbm 格式,比如:
  364.         # convert_type = "1"  # "1" for black and white image, "RGBA" for colored image
  365.         # convert_type = "1"  # 1 为黑白图像,RGBA 为彩色图像
  366.         #
  367.         # from PIL import Image
  368.         # with Image.open("filename.png", "r") as img:
  369.         #   img2 = img.convert(convert_type)
  370.         #   img2.save("filename.pbm")
  371.         Args:
  372.             file: PBM file
  373.                 pbm 文件
  374.                 File path (str)
  375.                 文件路径
  376.                 Raw data (BytesIO)
  377.                 原始数据
  378.             x: X-coordinate
  379.                  X 坐标
  380.             y: Y-coordinate
  381.                  Y 坐标
  382.             key: Specified color to be treated as transparent (only applicable in Framebuffer mode)
  383.                 指定的颜色将被视为透明(仅适用于 Framebuffer 模式)
  384.             show: Show immediately (only applicable in Framebuffer mode)
  385.                 立即显示(仅适用于 Framebuffer 模式)
  386.             clear: Clear screen
  387.                 清理屏幕
  388.             invert: Invert colors
  389.                 反转颜色
  390.             color: Image main color (only effective when displaying black and white image on a color screen)
  391.                 图像主体颜色(仅彩色屏幕显示黑白图像时生效)
  392.             bg_color: Image background color (only effective when displaying black and white image on a color screen)
  393.                 图像背景颜色(仅彩色屏幕显示黑白图像时生效)
  394.         """
  395.         if key is None:
  396.             key = self._key
  397.         if show is None:
  398.             show = self._show
  399.         if clear is None:
  400.             clear = self._clear
  401.         if invert is None:
  402.             invert = self.invert
  403.         color_type = self.color_type
  404.         if color is None:
  405.             color = self.color
  406.         if bg_color is None:
  407.             bg_color = self.bg_color
  408.         if clear:  # 清屏
  409.             self.clear()
  410.         dp = self.display
  411.         if isinstance(file, BytesIO):
  412.             func = file
  413.         else:
  414.             func = open(file, "rb")
  415.         with func as f:
  416.             file_format = f.readline()  # 获取文件格式
  417.             _width, _height = [int(value) for value in f.readline().split()]  # 获取图片的宽度和高度
  418.             f_read = f.read
  419.             if file_format == b"P4\n":  # P4 位图 二进制
  420.                 # 颜色反转
  421.                 if invert:
  422.                     color, bg_color = bg_color, color
  423.                 # 配置调色板
  424.                 if color_type == "MONO":
  425.                     palette = FrameBuffer(bytearray(1), 2, 1, MONO_HLSB)
  426.                 elif color_type == "RGB565":
  427.                     palette = FrameBuffer(bytearray(4), 2, 1, RGB565)
  428.                 else:
  429.                     raise KeyError("Unsupported color_type: {}".format(color_type))
  430.                 palette.pixel(1, 0, color)
  431.                 palette.pixel(0, 0, bg_color)
  432.                 if self._buffer:  # Framebuffer 模式
  433.                     data = bytearray(f_read())  # 读取并显示图像
  434.                     fbuf = FrameBuffer(data, _width, _height, MONO_HLSB)
  435.                     dp.blit(fbuf, x, y, key, palette)
  436.                 else:  # 直接驱动
  437.                     write_data = dp.write_data
  438.                     dp.set_window(x, y, x + _width - 1, y + _height - 1)  # 设置窗口
  439.                     buffer_size = self.READ_SIZE
  440.                     width = buffer_size * 8
  441.                     # Use different types of buffers according to different color types
  442.                     if color_type == "RGB565":
  443.                         data_fbuf = FrameBuffer(bytearray(buffer_size * 16), width, 1, RGB565)
  444.                     elif color_type == "MONO":
  445.                         data_fbuf = FrameBuffer(bytearray(buffer_size), width, 1, MONO_HLSB)  # Not tested
  446.                     else:
  447.                         raise ValueError("Unsupported color_type: {}".format(color_type))
  448.                     data_fbuf_blit = data_fbuf.blit
  449.                     # Read a picture several times, taking a part of it each time
  450.                     data = bytearray(f_read(buffer_size))
  451.                     while data:
  452.                         fbuf = FrameBuffer(data, width, 1, MONO_HLSB)
  453.                         data_fbuf_blit(fbuf, 0, 0, key, palette)  # Render MONO pixels into RGB565 pixels
  454.                         len_data = len(data)
  455.                         if len_data < buffer_size:  # Limit the data sent to no more than the Buffer size, so as to avoid data overflow and affect the display
  456.                             if color_type == "RGB565":
  457.                                 fbuf_data = bytearray(data_fbuf)[:len_data * 16]
  458.                             elif color_type == "MONO":
  459.                                 fbuf_data = bytearray(data_fbuf)[:len_data]
  460.                             else:
  461.                                 raise ValueError("Unsupported color_type: {}".format(color_type))
  462.                         else:
  463.                             fbuf_data = bytearray(data_fbuf)
  464.                         write_data(fbuf_data)
  465.                         data = bytearray(f_read(buffer_size))
  466.             elif file_format == b"P6\n":  # P6 像素图 二进制
  467.                 max_pixel_value = f.readline()  # 获取最大像素值
  468.                 r_height = range(_height)
  469.                 r_width = range(_width)
  470.                 color_bytearray = bytearray(3)  # 为变量预分配内存
  471.                 f_rinto = f.readinto
  472.                 try:
  473.                     dp_color = dp.color
  474.                 except AttributeError:
  475.                     dp_color = self.rgb565_color
  476.                 dp_pixel = dp.pixel
  477.                 if self._buffer:  # Framebuffer 模式
  478.                     if color_type == "RGB565":
  479.                         buffer = bytearray(_width * 2)
  480.                     for _y in r_height:  # 逐行显示图片
  481.                         for _x in r_width:
  482.                             f_rinto(color_bytearray)
  483.                             r, g, b = color_bytearray[0], color_bytearray[1], color_bytearray[2]
  484.                             if invert:
  485.                                 r = 255 - r
  486.                                 g = 255 - g
  487.                                 b = 255 - b
  488.                             if color_type == "RGB565":
  489.                                 buffer[_x * 2: (_x + 1) * 2] = dp_color(r, g, b).to_bytes(2, 'big')  # 通过索引赋值
  490.                             elif color_type == "MONO":
  491.                                 _color = int((r + g + b) / 3) >= 127
  492.                                 if _color:
  493.                                     _color = color
  494.                                 else:
  495.                                     _color = bg_color
  496.                                 if _color != key:  # 不显示指定颜色
  497.                                     dp_pixel(_x + x, _y + y, _color)
  498.                         if color_type == "RGB565":
  499.                             fbuf = FrameBuffer(buffer, _width, 1, RGB565)
  500.                             dp.blit(fbuf, x, y + _y, key)
  501.                 else:  # 直接驱动
  502.                     dp.set_window(x, y, x + _width - 1, y + _height - 1)  # 设置窗口
  503.                     buffer = bytearray(_width * 2)
  504.                     for _y in r_height:  # 逐行显示图片
  505.                         for _x in r_width:
  506.                             color_bytearray = f_read(3)
  507.                             r, g, b = color_bytearray[0], color_bytearray[1], color_bytearray[2]
  508.                             if invert:
  509.                                 r = 255 - r
  510.                                 g = 255 - g
  511.                                 b = 255 - b
  512.                             if color_type == "RGB565":
  513.                                 buffer[_x * 2: (_x + 1) * 2] = dp_color(
  514.                                     r, g, b).to_bytes(2, 'big')  # 通过索引赋值
  515.                             elif color_type == "MONO":
  516.                                 _color = int((r + g + b) / 3) >= 127
  517.                                 if _color:
  518.                                     _color = color
  519.                                 else:
  520.                                     _color = bg_color
  521.                                 if _color != key:  # 不显示指定颜色
  522.                                     dp_pixel(_x + x, _y + y, _color)
  523.                         if color_type == "RGB565":
  524.                             dp.write_data(buffer)
  525.             else:
  526.                 raise TypeError("Unsupported File Format Type.")
  527.             self.show() if show else 0  # 立即显示
  528.     def bmp(self, file, x, y, key: int = None, show: bool = None, clear: bool = None, invert: bool = False,
  529.             color: int = None, bg_color: int = None):
  530.         """
  531.         Display BMP Image  显示 bmp 图片
  532.         # You can convert the image to `24-bit` `bmp` format using the Paint application in Windows.
  533.         # Alternatively, you can use software like `Image2Lcd` to convert the image to `24-bit` `bmp` format (horizontal scan, includes image header data, 24-bit grayscale).
  534.         # 您可以通过使用 windows 的 画图 将图片转换为 `24-bit` 的 `bmp` 格式
  535.         # 也可以使用 `Image2Lcd` 这款软件将图片转换为 `24-bit` 的 `bmp` 格式(水平扫描,包含图像头数据,灰度二十四位)
  536.         Args:
  537.             file: bmp file
  538.                 bmp 文件
  539.                 File path (str)
  540.                 文件路径
  541.                 Raw data (BytesIO)
  542.                 原始数据
  543.             x: X-coordinate
  544.                 X 坐标
  545.             y: Y-coordinate
  546.                 Y 坐标
  547.             key: Specified color to be treated as transparent (only applicable in Framebuffer mode)
  548.                 指定的颜色将被视为透明(仅适用于 Framebuffer 模式)
  549.             show: Show immediately (only applicable in Framebuffer mode)
  550.                 立即显示(仅适用于 Framebuffer 模式)
  551.             clear: Clear screen
  552.                 清理屏幕
  553.             invert: Invert colors
  554.                 反转颜色
  555.             color: Image main color (only effective when displaying black and white image on a color screen)
  556.                    图像主体颜色(仅彩色图片显示以黑白形式显示时生效)
  557.             bg_color: Image background color (only effective when displaying black and white image on a color screen)
  558.                    图像背景颜色(仅彩色图片显示以黑白形式显示时生效)
  559.         """
  560.         if key is None:
  561.             key = self._key
  562.         if show is None:
  563.             show = self._show
  564.         if clear is None:
  565.             clear = self._clear
  566.         if invert is None:
  567.             invert = self.invert
  568.         color_type = self.color_type
  569.         if color is None:
  570.             color = self.color
  571.         if bg_color is None:
  572.             bg_color = self.bg_color
  573.         if isinstance(file, BytesIO):
  574.             func = file
  575.         else:
  576.             func = open(file, "rb")
  577.         with func as f:
  578.             f_read = f.read
  579.             f_rinto = f.readinto
  580.             f_seek = f.seek
  581.             f_tell = f.tell()
  582.             dp = self.display
  583.             try:
  584.                 dp_color = dp.color
  585.             except AttributeError:
  586.                 dp_color = self.rgb565_color
  587.             dp_pixel = dp.pixel
  588.             if f_read(2) == b'BM':  # 检查文件头
  589.                 dummy = f_read(8)  # 文件大小占四个字节,文件作者占四个字节,file size(4), creator bytes(4)
  590.                 int_fb = int.from_bytes
  591.                 offset = int_fb(f_read(4), 'little')  # 像素存储位置占四个字节
  592.                 hdrsize = int_fb(f_read(4), 'little')  # DIB header 占四个字节
  593.                 _width = int_fb(f_read(4), 'little')  # 图像宽度
  594.                 _height = int_fb(f_read(4), 'little')  # 图像高度
  595.                 if int_fb(f_read(2), 'little') == 1:  # 色彩平面数 planes must be 1
  596.                     depth = int_fb(f_read(2), 'little')  # 像素位数
  597.                     # 转换时只支持二十四位彩色,不压缩的图像
  598.                     if depth == 24 and int_fb(f_read(4), 'little') == 0:  # compress method == uncompressed
  599.                         row_size = (_width * 3 + 3) & ~3
  600.                         if _height < 0:
  601.                             _height = -_height
  602.                             flip = False
  603.                         else:
  604.                             flip = True
  605.                         if _width > dp.width:  # Limit the maximum size of image display
  606.                             _width = dp.width
  607.                         if _height > dp.height:
  608.                             _height = dp.height
  609.                         _color_bytearray = bytearray(3)  # 像素的二进制颜色
  610.                         if clear:  # 清屏
  611.                             self.clear()
  612.                         buffer = bytearray(_width * 2)
  613.                         self_buf = self._buffer
  614.                         if not self_buf:
  615.                             dp.set_window(x, y, x + _width - 1, y + _height - 1)  # 设置窗口
  616.                         r_width = range(_width)
  617.                         r_height = range(_height)
  618.                         for _y in r_height:
  619.                             if flip:
  620.                                 pos = offset + (_height - 1 - _y) * row_size
  621.                             else:
  622.                                 pos = offset + _y * row_size
  623.                             if f_tell != pos:
  624.                                 f_seek(pos)  # 调整指针位置
  625.                             for _x in r_width:
  626.                                 f_rinto(_color_bytearray)
  627.                                 r, g, b = _color_bytearray[2], _color_bytearray[1], _color_bytearray[0]
  628.                                 if invert:  # 颜色反转
  629.                                     r = 255 - r
  630.                                     g = 255 - g
  631.                                     b = 255 - b
  632.                                 if self_buf:  # Framebuffer 模式
  633.                                     if color_type == "RGB565":
  634.                                         buffer[_x * 2: (_x + 1) * 2] = dp_color(
  635.                                             r, g, b).to_bytes(2, 'big')  # 通过索引赋值
  636.                                     elif color_type == "MONO":
  637.                                         _color = int((r + g + b) / 3) >= 127
  638.                                         if _color:
  639.                                             _color = color
  640.                                         else:
  641.                                             _color = bg_color
  642.                                         if _color != key:  # 不显示指定颜色
  643.                                             dp_pixel(_x + x, _y + y, _color)
  644.                                 else:
  645.                                     if color_type == "RGB565":
  646.                                         buffer[_x * 2: (_x + 1) * 2] = dp_color(
  647.                                             r, g, b).to_bytes(2, 'big')  # 通过索引赋值
  648.                                     elif color_type == "MONO":
  649.                                         _color = int((r + g + b) / 3) >= 127
  650.                                         if _color:
  651.                                             _color = color
  652.                                         else:
  653.                                             _color = bg_color
  654.                                         if _color != key:  # 不显示指定颜色
  655.                                             dp_pixel(_x + x, _y + y, _color)
  656.                             if color_type == "RGB565":
  657.                                 if self_buf:
  658.                                     fbuf = FrameBuffer(buffer, _width, 1, RGB565)
  659.                                     dp.blit(fbuf, x, y + _y, key)
  660.                                 else:
  661.                                     dp.write_data(buffer)
  662.                         self.show() if show else 0  # 立即显示
  663.                     else:
  664.                         raise TypeError("Unsupported file type: only 24-bit uncompressed BMP images are supported.")
  665.             else:
  666.                 raise TypeError("Unsupported file type: only BMP images are supported.")
  667.     def dat(self, file, x, y, key=None):
  668.         """
  669.         Display screen raw data file, with extremely high efficiency, only supports RGB565 format.
  670.         显示表示屏幕原始数据的文件,拥有极高的效率,仅支持 RGB565 格式
  671.         Args:
  672.             file: dat file  dat 文件
  673.                 File path (str)  文件路径
  674.                 Raw data (BytesIO)  原始数据
  675.             x: X-coordinate  X坐标
  676.             y: Y-coordinate  Y 坐标
  677.             key: Specified color to be treated as transparent (only applicable in Framebuffer mode)
  678.                 指定的颜色将被视为透明(仅适用于 Framebuffer 模式)
  679.         """
  680.         if key is None:
  681.             key = self._key
  682.         if isinstance(file, BytesIO):
  683.             func = file
  684.         else:
  685.             func = open(file, "rb")
  686.         with func as f:
  687.             f_readline = f.readline
  688.             f_read = f.read
  689.             file_head = f_readline().rstrip(b'\n')
  690.             if file_head == b'EasyDisplay':  # 文件头
  691.                 version = f_readline().rstrip(b'\n')
  692.                 if version == b'V1':  # 文件格式版本
  693.                     _width, _height = f_readline().rstrip(b'\n').split(b' ')
  694.                     _width, _height = int(_width), int(_height)
  695.                     if self._buffer:  # Framebuffer 模式
  696.                         data = f_read(_width)
  697.                         dp_blit = self.display.blit
  698.                         y_offset = 0
  699.                         while data:
  700.                             buf = FrameBuffer(bytearray(data), _width, 1, RGB565)
  701.                             dp_blit(buf, x, y + y_offset, key)
  702.                             data = f_read(_width)
  703.                             y_offset += 1
  704.                     else:  # 直接驱动模式
  705.                         size = self.READ_SIZE * 10
  706.                         data = f_read(size)
  707.                         dp_write = self.display.write_data
  708.                         self.display.set_window(x, y, x + _width - 1, y + _height - 1)
  709.                         while data:
  710.                             dp_write(data)
  711.                             data = f_read(size)
  712.                 else:
  713.                     raise TypeError("Unsupported Version: {}".format(version))
  714.             else:
  715.                 try:
  716.                     raise TypeError("Unsupported File Type: {}".format(file_head))
  717.                 except:
  718.                     raise TypeError("Unsupported File Type!")
复制代码
然后是ILI9341屏幕的代码:
  1. import time
  2. import framebuf
  3. from machine import SPI, Pin , I2C
  4. i2c1 = I2C(0, scl=Pin(48), sda=Pin(47), freq=100000)
  5. '''
  6. class ILI9341:
  7.     def __init__(self, width, height):
  8.         self.width = width
  9.         self.height = height
  10.         self.pages = self.height // 8
  11.         self.buffer = bytearray(self.pages * self.width)
  12.         self.framebuf = framebuf.FrameBuffer(
  13.             self.buffer, self.width, self.height, framebuf.MONO_VLSB
  14.         )
  15.         self.spi = SPI(1,baudrate=40000000, phase=0, polarity=0, sck=Pin(12),mosi=Pin(21))
  16.         # chip select
  17.         self.cs = Pin(14, mode=Pin.OUT, pull=Pin.PULL_UP)
  18.         # command
  19.         self.dc = Pin(13, mode=Pin.OUT, pull=Pin.PULL_UP)
  20.         # initialize all pins high
  21.         self.cs.value(1)
  22.         self.dc.value(1)
  23.         #self.spi.init(baudrate=8000000, phase=0, polarity=0, sck=Pin(12),mosi=Pin(21),miso=-1)
  24.         self._i2c = i2c1
  25.         self._i2c.writeto(0x20,bytearray([0x02, 0x01]))
  26.         self._i2c.writeto(0x20,bytearray([0x06, 0xFE]))
  27.         self.init_display()
  28.     def init_display(self):
  29.         time.sleep_ms(500)
  30.         self.write_cmd(0x01)
  31.         time.sleep_ms(200)
  32.         self.write_cmd(0xCF)
  33.         self.write_data(bytearray([0x00, 0x8B, 0x30]))
  34.         self.write_cmd(0xED)
  35.         self.write_data(bytearray([0x67, 0x03, 0x12, 0x81]))
  36.         self.write_cmd(0xE8)
  37.         self.write_data(bytearray([0x85, 0x10, 0x7A]))
  38.         self.write_cmd(0xCB)
  39.         self.write_data(bytearray([0x39, 0x2C, 0x00, 0x34, 0x02]))
  40.         self.write_cmd(0xF7)
  41.         self.write_data(bytearray([0x20]))
  42.         self.write_cmd(0xEA)
  43.         self.write_data(bytearray([0x00, 0x00]))
  44.         # Power control
  45.         self.write_cmd(0xC0)
  46.         # VRH[5:0]
  47.         self.write_data(bytearray([0x1B]))
  48.         # Power control
  49.         self.write_cmd(0xC1)
  50.         # SAP[2:0];BT[3:0]
  51.         self.write_data(bytearray([0x10]))
  52.         # VCM control
  53.         self.write_cmd(0xC5)
  54.         self.write_data(bytearray([0x3F, 0x3C]))
  55.         # VCM control2
  56.         self.write_cmd(0xC7)
  57.         self.write_data(bytearray([0xB7]))
  58.         # Memory Access Control
  59.         self.write_cmd(0x36)
  60.         self.write_data(bytearray([0x08]))
  61.         self.write_cmd(0x3A)
  62.         self.write_data(bytearray([0x55]))
  63.         self.write_cmd(0xB1)
  64.         self.write_data(bytearray([0x00, 0x1B]))
  65.         # Display Function Control
  66.         self.write_cmd(0xB6)
  67.         self.write_data(bytearray([0x0A, 0xA2]))
  68.         # 3Gamma Function Disable
  69.         self.write_cmd(0xF2)
  70.         self.write_data(bytearray([0x00]))
  71.         # Gamma curve selected
  72.         self.write_cmd(0x26)
  73.         self.write_data(bytearray([0x01]))
  74.         # Set Gamma
  75.         self.write_cmd(0xE0)
  76.         self.write_data(
  77.             bytearray(
  78.                 [
  79.                     0x0F,
  80.                     0x2A,
  81.                     0x28,
  82.                     0x08,
  83.                     0x0E,
  84.                     0x08,
  85.                     0x54,
  86.                     0xA9,
  87.                     0x43,
  88.                     0x0A,
  89.                     0x0F,
  90.                     0x00,
  91.                     0x00,
  92.                     0x00,
  93.                     0x00,
  94.                 ]
  95.             )
  96.         )
  97.         # Set Gamma
  98.         self.write_cmd(0xE1)
  99.         self.write_data(
  100.             bytearray(
  101.                 [
  102.                     0x00,
  103.                     0x15,
  104.                     0x17,
  105.                     0x07,
  106.                     0x11,
  107.                     0x06,
  108.                     0x2B,
  109.                     0x56,
  110.                     0x3C,
  111.                     0x05,
  112.                     0x10,
  113.                     0x0F,
  114.                     0x3F,
  115.                     0x3F,
  116.                     0x0F,
  117.                 ]
  118.             )
  119.         )
  120.         # Exit Sleep
  121.         self.write_cmd(0x11)
  122.         time.sleep_ms(120)
  123.         # Display on
  124.         self.write_cmd(0x29)
  125.         time.sleep_ms(500)
  126.         
  127.         self.fill(0)
  128.     def show(self):
  129.         # set col
  130.         self.write_cmd(0x2A)
  131.         self.write_data(bytearray([0x00, 0x00]))
  132.         self.write_data(bytearray([0x00, 0xEF]))
  133.         # set page
  134.         self.write_cmd(0x2B)
  135.         self.write_data(bytearray([0x00, 0x00]))
  136.         self.write_data(bytearray([0x01, 0x3F]))
  137.         self.write_cmd(0x2C)
  138.         buffer = bytearray()
  139.         buffer_size = 4096*2
  140.         for row in range(0, self.pages):
  141.             for pixel_pos in range(0, 8):
  142.                 for col in range(0, self.width):
  143.                     compressed_pixel = self.buffer[row * 240 + col]
  144.                     if ((compressed_pixel >> pixel_pos) & 0x1) == 0:
  145.                         #self.write_data(bytearray([0x00, 0x00]))
  146.                         buffer.extend(bytearray([0x00, 0x00]))
  147.                     else:
  148.                         buffer.extend(bytearray([0xFF, 0xFF]))
  149.                         #self.write_data(bytearray([0xFF, 0xFF]))
  150.                     if len(buffer) >= buffer_size:
  151.                         self.write_data(buffer)
  152.                         buffer = bytearray()
  153.         if len(buffer) > 0:
  154.             self.write_data(buffer)
  155.     def fill(self, col):
  156.         self.framebuf.fill(col)
  157.     def pixel(self, x, y, col):
  158.         self.framebuf.pixel(x, y, col)
  159.     def scroll(self, dx, dy):
  160.         self.framebuf.scroll(dx, dy)
  161.     def text(self, string, x, y, col=1):
  162.         self.framebuf.text(string, x, y, col)
  163.     def write_cmd(self, cmd):
  164.         self.dc.value(0)
  165.         self.cs.value(0)
  166.         self.spi.write(bytearray([cmd]))
  167.         self.cs.value(1)
  168.     def write_data(self, buf):
  169.         self.dc.value(1)
  170.         self.cs.value(0)
  171.         self.spi.write(buf)
  172.         self.cs.value(1)
  173. '''
  174. # This is an adapted version of the ILI934X driver as below.
  175. # It works with multiple fonts and also works with the esp32 H/W SPI implementation
  176. # Also includes a word wrap print function
  177. # Proportional fonts are generated by Peter Hinch's Font-to-py
  178. # MIT License; Copyright (c) 2017 Jeffrey N. Magee
  179. # This file is part of MicroPython ILI934X driver
  180. # Copyright (c) 2016 - 2017 Radomir Dopieralski, Mika Tuupola
  181. #
  182. # Licensed under the MIT license:
  183. #   http://www.opensource.org/licenses/mit-license.php
  184. #
  185. # Project home:
  186. #   https://github.com/tuupola/micropython-ili934x
  187. import time
  188. import ustruct
  189. import glcdfont
  190. import framebuf
  191. from micropython import const
  192. _RDDSDR = const(0x0f) # Read Display Self-Diagnostic Result
  193. _SLPOUT = const(0x11) # Sleep Out
  194. _GAMSET = const(0x26) # Gamma Set
  195. _DISPOFF = const(0x28) # Display Off
  196. _DISPON = const(0x29) # Display On
  197. _CASET = const(0x2a) # Column Address Set
  198. _PASET = const(0x2b) # Page Address Set
  199. _RAMWR = const(0x2c) # Memory Write
  200. _RAMRD = const(0x2e) # Memory Read
  201. _MADCTL = const(0x36) # Memory Access Control
  202. _VSCRSADD = const(0x37) # Vertical Scrolling Start Address
  203. _PIXSET = const(0x3a) # Pixel Format Set
  204. _PWCTRLA = const(0xcb) # Power Control A
  205. _PWCRTLB = const(0xcf) # Power Control B
  206. _DTCTRLA = const(0xe8) # Driver Timing Control A
  207. _DTCTRLB = const(0xea) # Driver Timing Control B
  208. _PWRONCTRL = const(0xed) # Power on Sequence Control
  209. _PRCTRL = const(0xf7) # Pump Ratio Control
  210. _PWCTRL1 = const(0xc0) # Power Control 1
  211. _PWCTRL2 = const(0xc1) # Power Control 2
  212. _VMCTRL1 = const(0xc5) # VCOM Control 1
  213. _VMCTRL2 = const(0xc7) # VCOM Control 2
  214. _FRMCTR1 = const(0xb1) # Frame Rate Control 1
  215. _DISCTRL = const(0xb6) # Display Function Control
  216. _ENA3G = const(0xf2) # Enable 3G
  217. _PGAMCTRL = const(0xe0) # Positive Gamma Control
  218. _NGAMCTRL = const(0xe1) # Negative Gamma Control
  219. _CHUNK = const(1024) #maximum number of pixels per spi write
  220. def color565(r, g, b):
  221.     return (r & 0xf8) << 8 | (g & 0xfc) << 3 | b >> 3
  222. class ILI9341:
  223.     def __init__(self, spi, cs, dc, rst, w, h, r):
  224.         self.spi = spi
  225.         self.cs = cs
  226.         self.dc = dc
  227.         self.rst = rst
  228.         self._init_width = w
  229.         self._init_height = h
  230.         self.width = w
  231.         self.height = h
  232.         self.rotation = r
  233.         self.cs.init(self.cs.OUT, value=1)
  234.         self.dc.init(self.dc.OUT, value=0)
  235.         self.rst.init(self.rst.OUT, value=0)
  236.         self.reset()
  237.         self.init()
  238.         self._scroll = 0
  239.         self._buf = bytearray(_CHUNK * 2)
  240.         self._colormap = bytearray(b'\x00\x00\xFF\xFF') #default white foregraound, black background
  241.         self._x = 0
  242.         self._y = 0
  243.         self._font = glcdfont
  244.         self.scrolling = False
  245.     def set_color(self,fg,bg):
  246.         self._colormap[0] = bg>>8
  247.         self._colormap[1] = bg & 255
  248.         self._colormap[2] = fg>>8
  249.         self._colormap[3] = fg & 255
  250.     def set_pos(self,x,y):
  251.         self._x = x
  252.         self._y = y
  253.         #self.scroll(0)
  254.     def reset_scroll(self):
  255.         self.scrolling = False
  256.         self._scroll = 0
  257.         self.scroll(0)
  258.     def set_font(self, font):
  259.         self._font = font
  260.     def init(self):
  261.         for command, data in (
  262.             (_RDDSDR, b"\x03\x80\x02"),
  263.             (_PWCRTLB, b"\x00\xc1\x30"),
  264.             (_PWRONCTRL, b"\x64\x03\x12\x81"),
  265.             (_DTCTRLA, b"\x85\x00\x78"),
  266.             (_PWCTRLA, b"\x39\x2c\x00\x34\x02"),
  267.             (_PRCTRL, b"\x20"),
  268.             (_DTCTRLB, b"\x00\x00"),
  269.             (_PWCTRL1, b"\x23"),
  270.             (_PWCTRL2, b"\x10"),
  271.             (_VMCTRL1, b"\x3e\x28"),
  272.             (_VMCTRL2, b"\x86")):
  273.             self._write(command, data)
  274.         if self.rotation == 0:                  # 0 deg
  275.             self._write(_MADCTL, b"\x48")
  276.             self.width = self._init_height
  277.             self.height = self._init_width
  278.         elif self.rotation == 1:                # 90 deg
  279.             self._write(_MADCTL, b"\x28")
  280.             self.width = self._init_width
  281.             self.height = self._init_height
  282.         elif self.rotation == 2:                # 180 deg
  283.             self._write(_MADCTL, b"\x88")
  284.             self.width = self._init_height
  285.             self.height = self._init_width
  286.         elif self.rotation == 3:                # 270 deg
  287.             self._write(_MADCTL, b"\xE8")
  288.             self.width = self._init_width
  289.             self.height = self._init_height
  290.         elif self.rotation == 4:                # Mirrored + 0 deg
  291.             self._write(_MADCTL, b"\xC8")
  292.             self.width = self._init_height
  293.             self.height = self._init_width
  294.         elif self.rotation == 5:                # Mirrored + 90 deg
  295.             self._write(_MADCTL, b"\x68")
  296.             self.width = self._init_width
  297.             self.height = self._init_height
  298.         elif self.rotation == 6:                # Mirrored + 180 deg
  299.             self._write(_MADCTL, b"\x08")
  300.             self.width = self._init_height
  301.             self.height = self._init_width
  302.         elif self.rotation == 7:                # Mirrored + 270 deg
  303.             self._write(_MADCTL, b"\xA8")
  304.             self.width = self._init_width
  305.             self.height = self._init_height
  306.         else:
  307.             self._write(_MADCTL, b"\x08")
  308.         for command, data in (
  309.             (_PIXSET, b"\x55"),
  310.             (_FRMCTR1, b"\x00\x18"),
  311.             (_DISCTRL, b"\x08\x82\x27"),
  312.             (_ENA3G, b"\x00"),
  313.             (_GAMSET, b"\x01"),
  314.             (_PGAMCTRL, b"\x0f\x31\x2b\x0c\x0e\x08\x4e\xf1\x37\x07\x10\x03\x0e\x09\x00"),
  315.             (_NGAMCTRL, b"\x00\x0e\x14\x03\x11\x07\x31\xc1\x48\x08\x0f\x0c\x31\x36\x0f")):
  316.             self._write(command, data)
  317.         self._write(_SLPOUT)
  318.         time.sleep_ms(120)
  319.         self._write(_DISPON)
  320.         self._i2c = i2c1
  321.         self._i2c.writeto(0x20,bytearray([0x02, 0x01]))
  322.         self._i2c.writeto(0x20,bytearray([0x06, 0xFE]))
  323.     def reset(self):
  324.         self.rst(0)
  325.         time.sleep_ms(50)
  326.         self.rst(1)
  327.         time.sleep_ms(50)
  328.     def _write(self, command, data=None):
  329.         self.dc(0)
  330.         self.cs(0)
  331.         self.spi.write(bytearray([command]))
  332.         self.cs(1)
  333.         if data is not None:
  334.             self._data(data)
  335.     def _data(self, data):
  336.         self.dc(1)
  337.         self.cs(0)
  338.         self.spi.write(data)
  339.         self.cs(1)
  340.     def _writeblock(self, x0, y0, x1, y1, data=None):
  341.         self._write(_CASET, ustruct.pack(">HH", x0, x1))
  342.         self._write(_PASET, ustruct.pack(">HH", y0, y1))
  343.         self._write(_RAMWR, data)
  344.     def _readblock(self, x0, y0, x1, y1, data=None):
  345.         self._write(_CASET, ustruct.pack(">HH", x0, x1))
  346.         self._write(_PASET, ustruct.pack(">HH", y0, y1))
  347.         if data is None:
  348.             return self._read(_RAMRD, (x1 - x0 + 1) * (y1 - y0 + 1) * 3)
  349.     def _read(self, command, count):
  350.         self.dc(0)
  351.         self.cs(0)
  352.         self.spi.write(bytearray([command]))
  353.         data = self.spi.read(count)
  354.         self.cs(1)
  355.         return data
  356.     def pixel(self, x, y, color=None):
  357.         if color is None:
  358.             r, b, g = self._readblock(x, y, x, y)
  359.             return color565(r, g, b)
  360.         if not 0 <= x < self.width or not 0 <= y < self.height:
  361.             return
  362.         self._writeblock(x, y, x, y, ustruct.pack(">H", color))
  363.     def fill_rectangle(self, x, y, w, h, color=None):
  364.         x = min(self.width - 1, max(0, x))
  365.         y = min(self.height - 1, max(0, y))
  366.         w = min(self.width - x, max(1, w))
  367.         h = min(self.height - y, max(1, h))
  368.         if color:
  369.             color = ustruct.pack(">H", color)
  370.         else:
  371.             color = self._colormap[0:2] #background
  372.         for i in range(_CHUNK):
  373.             self._buf[2*i]=color[0]; self._buf[2*i+1]=color[1]
  374.         chunks, rest = divmod(w * h, _CHUNK)
  375.         self._writeblock(x, y, x + w - 1, y + h - 1, None)
  376.         if chunks:
  377.             for count in range(chunks):
  378.                 self._data(self._buf)
  379.         if rest != 0:
  380.             mv = memoryview(self._buf)
  381.             self._data(mv[:rest*2])
  382.     def fill(self,c):
  383.         self.fill_rectangle(0, 0, self.width, self.height,c)
  384.     def blit(self, bitbuff, x, y, w, h):
  385.         x = min(self.width - 1, max(0, x))
  386.         y = min(self.height - 1, max(0, y))
  387.         w = min(self.width - x, max(1, w))
  388.         h = min(self.height - y, max(1, h))
  389.         chunks, rest = divmod(w * h, _CHUNK)
  390.         self._writeblock(x, y, x + w - 1, y + h - 1, None)
  391.         written = 0
  392.         for iy in range(h):
  393.             for ix in range(w):
  394.                 index = ix+iy*w - written
  395.                 if index >=_CHUNK:
  396.                     self._data(self._buf)
  397.                     written += _CHUNK
  398.                     index   -= _CHUNK
  399.                 c = bitbuff.pixel(ix,iy)
  400.                 self._buf[index*2] = self._colormap[c*2]
  401.                 self._buf[index*2+1] = self._colormap[c*2+1]
  402.         rest = w*h - written
  403.         if rest != 0:
  404.             mv = memoryview(self._buf)
  405.             self._data(mv[:rest*2])
  406.     def chars(self, str, x, y):
  407.         str_w  = self._font.get_width(str)
  408.         if str_w == 0:
  409.             return x + 0
  410.         div, rem = divmod(self._font.height(),8)
  411.         nbytes = div+1 if rem else div
  412.         buf = bytearray(str_w * nbytes)
  413.         pos = 0
  414.         for ch in str:
  415.             glyph, char_w = self._font.get_ch(ch)
  416.             for row in range(nbytes):
  417.                 index = row*str_w + pos
  418.                 for i in range(char_w):
  419.                     buf[index+i] = glyph[nbytes*i+row]
  420.             pos += char_w
  421.         fb = framebuf.FrameBuffer(buf,str_w, self._font.height(), framebuf.MONO_VLSB)
  422.         self.blit(fb,x,y,str_w,self._font.height())
  423.         return x+str_w
  424.     def scroll(self, dy):
  425.         self._scroll = (self._scroll + dy) % self.height
  426.         #self._write(_VSCRSADD, ustruct.pack(">H", self._scroll))
  427.     def next_line(self, cury, char_h):
  428.         global scrolling
  429.         if not self.scrolling:
  430.             res = cury + char_h
  431.             self.scrolling = (res >= self.height)
  432.         if self.scrolling:
  433.             self.scroll(char_h)
  434.             res = (self.height - char_h + self._scroll)%self.height
  435.             self.fill_rectangle(0, res, self.width, self._font.height())
  436.         return res
  437.     def write(self, text): #does character wrap, compatible with stream output
  438.         curx = self._x; cury = self._y
  439.         char_h = self._font.height()
  440.         width = 0
  441.         written = 0
  442.         for pos, ch in enumerate(text):
  443.             if ch == '\n':
  444.                 if pos>0:
  445.                     self.chars(text[written:pos],curx,cury)
  446.                 curx = 0; written = pos+1; width = 0
  447.                 cury = self.next_line(cury,char_h)
  448.             else:
  449.                 char_w = self._font.get_width(ch)
  450.                 if curx + width + char_w >= self.width:
  451.                     self.chars(text[written:pos], curx,cury)
  452.                     curx = 0 ; written = pos; width = char_h
  453.                     cury = self.next_line(cury,char_h)
  454.                 else:
  455.                     width += char_w
  456.         if written<len(text):
  457.             curx = self.chars(text[written:], curx,cury)
  458.         self._x = curx; self._y = cury
  459.     def print(self, text): #does word wrap, leaves self._x unchanged
  460.         cury = self._y; curx = self._x
  461.         
  462.         char_h = self._font.height()
  463.         char_w = self._font.max_width()
  464.         lines = text.split('\n')
  465.         for line in lines:
  466.             words = line.split(' ')
  467.             for word in words:
  468.                 if curx + self._font.get_width(word) >= self.width:
  469.                     curx = self._x; cury = self.next_line(cury,char_h)
  470.                     while self._font.get_width(word) > self.width:
  471.                         self.chars(word[:self.width//char_w],curx,cury)
  472.                         word = word[self.width//char_w:]
  473.                         cury = self.next_line(cury,char_h)
  474.                 if len(word)>0:
  475.                     curx = self.chars(word+' ', curx,cury)
  476.             curx = self._x; cury = self.next_line(cury,char_h)
  477.         self._y = cury
复制代码
最后是测试代码:
  1. import time
  2. from easydisplay import EasyDisplay
  3. from machine import Pin,SPI
  4. from ILI9341 import ILI9341
  5. spi = SPI(1, baudrate=40000000, phase=0, polarity=0, sck=Pin(12), mosi=Pin(21))
  6. lcd = ILI9341(spi, cs=Pin(14), dc=Pin(13), rst=Pin(0), w=320, h=240, r=2)
  7. ed = EasyDisplay(lcd, "RGB565", font="/text_lite_16px_2312.v3.bmf", show=True, color=0xFFFF, clear=True)
  8. ed.text("你好,世界!\nHello World!\nこんにちは、世界!", 0, 0)
复制代码
  1. import time
  2. from easydisplay import EasyDisplay
  3. from machine import Pin,SPI
  4. from ILI9341 import ILI9341
  5. spi = SPI(1, baudrate=40000000, phase=0, polarity=0, sck=Pin(12), mosi=Pin(21))
  6. lcd = ILI9341(spi, cs=Pin(14), dc=Pin(13), rst=Pin(0), w=320, h=240, r=2)
  7. ed = EasyDisplay(lcd, "RGB565", font="/text_lite_16px_2312.v3.bmf", show=True, color=0xFFFF, clear=False,auto_wrap=True)
  8. ed.clear()
  9. ed.text("行空板K10简介", 40, 30,size=24,color=0x7E0)
  10. text_k10 = "行空板K10是一款专为快速体验物联网和学习人工智能而设计的开发学习板,100%采用国产芯片,知识产权自主可控,符合信息科技课程中编程学习、物联网及人工智能等教学需求。该板集成2.8寸LCD彩屏、WiFi蓝牙、摄像头、麦克风、扬声器、RGB指示灯、多种传感器及丰富的扩展接口。凭借高度集成的板载资源,教学过程中无需额外连接其他设备,便可轻松实现传感器控制、物联网应用以及人脸识别、语音识别、语音合成等AI人工智能项目。"
  11. ed.text(text_k10, 0, 80,0x0000FF,None,16)
复制代码
  1. import time
  2. from easydisplay import EasyDisplay
  3. from machine import Pin,SPI
  4. from ILI9341 import ILI9341
  5. spi = SPI(1, baudrate=40000000, phase=0, polarity=0, sck=Pin(12), mosi=Pin(21))
  6. lcd = ILI9341(spi, cs=Pin(14), dc=Pin(13), rst=Pin(0), w=320, h=240, r=2)
  7. ed = EasyDisplay(lcd, "RGB565", font="/text_lite_16px_2312.v3.bmf", show=True, color=0xFFFF, clear=False,auto_wrap=True)
  8. ed.clear()
  9. ed.text("PY学习笔记简介", 40, 30,size=24,color=0x7E0)
  10. text_pyxxbj = "微信公众号PY学习笔记:\n\n专注于python和Micropython的学习与分享。\n\n敬请关注!"
  11. ed.text(text_pyxxbj, 0, 80,0x0000FF,None,16)
复制代码
效果:
行空板基于micropython实现实现英文,中文和日文显示图1行空板基于micropython实现实现英文,中文和日文显示图2

PY学习笔记  学徒
 楼主|

发表于 3 天前

要刷educore库固件
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

为本项目制作心愿单
购买心愿单
心愿单 编辑
[[wsData.name]]

硬件清单

  • [[d.name]]
btnicon
我也要做!
点击进入购买页面
上海智位机器人股份有限公司 沪ICP备09038501号-4

© 2013-2024 Comsenz Inc. Powered by Discuz! X3.4 Licensed

mail