【micropython】esp32 framebuf通过pixel的形式在墨水屏上展示中文

部署运行你感兴趣的模型镜像

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

micropython使用framebuf库来实现中文展示


提示:以下是本篇文章正文内容,下面案例可供参考
在这里插入图片描述

一、hzk字库

不过多叙述,自行查找
资源的话,可以上网找,一找一大把

二、使用步骤

1.引入相应的驱动

可以使用墨水屏、oled,原则上,使用framebuf这个的都可以

使用的是CircuitPython下的framebuf库,可以旋转,比较方便
cframebuf库如下

cframebuf.py

// An highlighted block
# SPDX-FileCopyrightText: <text> 2018 Kattni Rembor, Melissa LeBlanc-Williams
# and Tony DiCola, for Adafruit Industries.
# Original file created by Damien P. George </text>
#
# SPDX-License-Identifier: MIT

"""
`adafruit_framebuf`
====================================================
CircuitPython pure-python framebuf module, based on the micropython framebuf module.
Implementation Notes
--------------------
**Hardware:**
* `Adafruit SSD1306 OLED displays <https://www.adafruit.com/?q=ssd1306>`_
**Software and Dependencies:**
* Adafruit CircuitPython firmware for the supported boards:
  https://github.com/adafruit/circuitpython/releases
"""

__version__ = "0.0.0-auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_framebuf.git"

import os
import struct

# Framebuf format constants:
MVLSB = 0  # Single bit displays (like SSD1306 OLED)
RGB565 = 1  # 16-bit color displays
GS4_HMSB = 2  # Unimplemented!
MHMSB = 3  # Single bit displays like the Sharp Memory
RGB888 = 4  # Neopixels and Dotstars


class MHMSBFormat:
    """MHMSBFormat"""

    @staticmethod
    def set_pixel(framebuf, x, y, color):
        """Set a given pixel to a color."""
        index = (y * framebuf.stride + x) // 8
        offset = 7 - x & 0x07
        framebuf.buf[index] = (framebuf.buf[index] & ~(0x01 << offset)) | (
                (color != 0) << offset
        )

    @staticmethod
    def get_pixel(framebuf, x, y):
        """Get the color of a given pixel"""
        index = (y * framebuf.stride + x) // 8
        offset = 7 - x & 0x07
        return (framebuf.buf[index] >> offset) & 0x01

    @staticmethod
    def fill(framebuf, color):
        """completely fill/clear the buffer with a color"""
        if color:
            fill = 0xFF
        else:
            fill = 0x00
        for i in range(len(framebuf.buf)):  # pylint: disable=consider-using-enumerate
            framebuf.buf[i] = fill

    @staticmethod
    def fill_rect(framebuf, x, y, width, height, color):
        """Draw a rectangle at the given location, size and color. The ``fill_rect`` method draws
        both the outline and interior."""
        # pylint: disable=too-many-arguments
        for _x in range(x, x + width):
            offset = 7 - _x & 0x07
            for _y in range(y, y + height):
                index = (_y * framebuf.stride + _x) // 8
                framebuf.buf[index] = (framebuf.buf[index] & ~(0x01 << offset)) | (
                        (color != 0) << offset
                )


class MVLSBFormat:
    """MVLSBFormat"""

    @staticmethod
    def set_pixel(framebuf, x, y, color):
        """Set a given pixel to a color."""
        index = (y >> 3) * framebuf.stride + x
        offset = y & 0x07
        framebuf.buf[index] = (framebuf.buf[index] & ~(0x01 << offset)) | (
                (color != 0) << offset
        )

    @staticmethod
    def get_pixel(framebuf, x, y):
        """Get the color of a given pixel"""
        index = (y >> 3) * framebuf.stride + x
        offset = y & 0x07
        return (framebuf.buf[index] >> offset) & 0x01

    @staticmethod
    def fill(framebuf, color):
        """completely fill/clear the buffer with a color"""
        if color:
            fill = 0xFF
        else:
            fill = 0x00
        for i in range(len(framebuf.buf)):  # pylint: disable=consider-using-enumerate
            framebuf.buf[i] = fill

    @staticmethod
    def fill_rect(framebuf, x, y, width, height, color):
        """Draw a rectangle at the given location, size and color. The ``fill_rect`` method draws
        both the outline and interior."""
        # pylint: disable=too-many-arguments
        while height > 0:
            index = (y >> 3) * framebuf.stride + x
            offset = y & 0x07
            for w_w in range(width):
                framebuf.buf[index + w_w] = (
                                                    framebuf.buf[index + w_w] & ~(0x01 << offset)
                                            ) | ((color != 0) << offset)
            y += 1
            height -= 1


class RGB888Format:
    """RGB888Format"""

    @staticmethod
    def set_pixel(framebuf, x, y, color):
        """Set a given pixel to a color."""
        index = (y * framebuf.stride + x) * 3
        if isinstance(color, tuple):
            framebuf.buf[index: index + 3] = bytes(color)
        else:
            framebuf.buf[index: index + 3] = bytes(
                ((color >> 16) & 255, (color >> 8) & 255, color & 255)
            )

    @staticmethod
    def get_pixel(framebuf, x, y):
        """Get the color of a given pixel"""
        index = (y * framebuf.stride + x) * 3
        return (
                (framebuf.buf[index] << 16)
                | (framebuf.buf[index + 1] << 8)
                | framebuf.buf[index + 2]
        )

    @staticmethod
    def fill(framebuf, color):
        """completely fill/clear the buffer with a color"""
        fill = (color >> 16) & 255, (color >> 8) & 255, color & 255
        for i in range(0, len(framebuf.buf), 3):
            framebuf.buf[i: i + 3] = bytes(fill)

    @staticmethod
    def fill_rect(framebuf, x, y, width, height, color):
        """Draw a rectangle at the given location, size and color. The ``fill_rect`` method draws
        both the outline and interior."""
        # pylint: disable=too-many-arguments
        fill = (color >> 16) & 255, (color >> 8) & 255, color & 255
        for _x in range(x, x + width):
            for _y in range(y, y + height):
                index = (_y * framebuf.stride + _x) * 3
                framebuf.buf[index: index + 3] = bytes(fill)


class FrameBuffer:
    """FrameBuffer object.
    :param buf: An object with a buffer protocol which must be large enough to contain every
                pixel defined by the width, height and format of the FrameBuffer.
    :param width: The width of the FrameBuffer in pixel
    :param height: The height of the FrameBuffer in pixel
    :param buf_format: Specifies the type of pixel used in the FrameBuffer; permissible values
                        are listed under Constants below. These set the number of bits used to
                        encode a color value and the layout of these bits in ``buf``. Where a
                        color value c is passed to a method, c is  a small integer with an encoding
                        that is dependent on the format of the FrameBuffer.
    :param stride: The number of pixels between each horizontal line of pixels in the
                   FrameBuffer. This defaults to ``width`` but may need adjustments when
                   implementing a FrameBuffer within another larger FrameBuffer or screen. The
                   ``buf`` size must accommodate an increased step size.
    """

    def __init__(self, buf, width, height, buf_format=MVLSB, stride=None):
        # pylint: disable=too-many-arguments
        self.buf = buf
        self.width = width
        self.height = height
        self.stride = stride
        self._font = None
        if self.stride is None:
            self.stride = width
        if buf_format == MVLSB:
            self.format = MVLSBFormat()
        elif buf_format == MHMSB:
            self.format = MHMSBFormat()
        elif buf_format == RGB888:
            self.format = RGB888Format()
        else:
            raise ValueError("invalid format")
        self._rotation = 0

    @property
    def rotation(self):
        """The rotation setting of the display, can be one of (0, 1, 2, 3)"""
        return self._rotation

    @rotation.setter
    def rotation(self, val):
        if not val in (0, 1, 2, 3):
            raise RuntimeError("Bad rotation setting")
        self._rotation = val

    def fill(self, color):
        """Fill the entire FrameBuffer with the specified color."""
        self.format.fill(self, color)

    def fill_rect(self, x, y, width, height, color):
        """Draw a rectangle at the given location, size and color. The ``fill_rect`` method draws
        both the outline and interior."""
        # pylint: disable=too-many-arguments, too-many-boolean-expressions
        self.rect(x, y, width, height, color, fill=True)

    def pixel(self, x, y, color=None):
        """If ``color`` is not given, get the color value of the specified pixel. If ``color`` is
        given, set the specified pixel to the given color."""
        if self.rotation == 1:
            x, y = y, x
            x = self.width - x - 1
        if self.rotation == 2:
            x = self.width - x - 1
            y = self.height - y - 1
        if self.rotation == 3:
            x, y = y, x
            y = self.height - y - 1

        if x < 0 or x >= self.width or y < 0 or y >= self.height:
            return None
        if color is None:
            return self.format.get_pixel(self, x, y)
        self.format.set_pixel(self, x, y, color)
        return None

    def hline(self, x, y, width, color):
        """Draw a horizontal line up to a given length."""
        self.rect(x, y, width, 1, color, fill=True)

    def vline(self, x, y, height, color):
        """Draw a vertical line up to a given length."""
        self.rect(x, y, 1, height, color, fill=True)

    def circle(self, center_x, center_y, radius, color):
        """Draw a circle at the given midpoint location, radius and color.
        The ```circle```method draws only a 1 pixel outline."""
        x = radius - 1
        y = 0
        d_x = 1
        d_y = 1
        err = d_x - (radius << 1)
        while x >= y:
            self.pixel(center_x + x, center_y + y, color)
            self.pixel(center_x + y, center_y + x, color)
            self.pixel(center_x - y, center_y + x, color)
            self.pixel(center_x - x, center_y + y, color)
            self.pixel(center_x - x, center_y - y, color)
            self.pixel(center_x - y, center_y - x, color)
            self.pixel(center_x + y, center_y - x, color)
            self.pixel(center_x + x, center_y - y, color)
            if err <= 0:
                y += 1
                err += d_y
                d_y += 2
            if err > 0:
                x -= 1
                d_x += 2
                err += d_x - (radius << 1)

    def rect(self, x, y, width, height, color, *, fill=False):
        """Draw a rectangle at the given location, size and color. The ```rect```method draws only
        a 1 pixel outline."""
        # pylint: disable=too-many-arguments
        if self.rotation == 1:
            x, y = y, x
            width, height = height, width
            x = self.width - x - width
        if self.rotation == 2:
            x = self.width - x - width
            y = self.height - y - height
        if self.rotation == 3:
            x, y = y, x
            width, height = height, width
            y = self.height - y - height

        # pylint: disable=too-many-boolean-expressions
        if (
                width < 1
                or height < 1
                or (x + width) <= 0
                or (y + height) <= 0
                or y >= self.height
                or x >= self.width
        ):
            return
        x_end = min(self.width - 1, x + width - 1)
        y_end = min(self.height - 1, y + height - 1)
        x = max(x, 0)
        y = max(y, 0)
        if fill:
            self.format.fill_rect(self, x, y, x_end - x + 1, y_end - y + 1, color)
        else:
            self.format.fill_rect(self, x, y, x_end - x + 1, 1, color)
            self.format.fill_rect(self, x, y, 1, y_end - y + 1, color)
            self.format.fill_rect(self, x, y_end, x_end - x + 1, 1, color)
            self.format.fill_rect(self, x_end, y, 1, y_end - y + 1, color)

    def line(self, x_0, y_0, x_1, y_1, color):
        # pylint: disable=too-many-arguments
        """Bresenham's line algorithm"""
        d_x = abs(x_1 - x_0)
        d_y = abs(y_1 - y_0)
        x, y = x_0, y_0
        s_x = -1 if x_0 > x_1 else 1
        s_y = -1 if y_0 > y_1 else 1
        if d_x > d_y:
            err = d_x / 2.0
            while x != x_1:
                self.pixel(x, y, color)
                err -= d_y
                if err < 0:
                    y += s_y
                    err += d_x
                x += s_x
        else:
            err = d_y / 2.0
            while y != y_1:
                self.pixel(x, y, color)
                err -= d_x
                if err < 0:
                    x += s_x
                    err += d_y
                y += s_y
        self.pixel(x, y, color)

    def blit(self):
        """blit is not yet implemented"""
        # raise NotImplementedError()

    def blit111111(self, source, x, y, alpha_color=None):
        """
        将源帧缓冲区绘制到目标帧缓冲区的指定位置。

        :param source: 源帧缓冲区,必须具有 width 和 height 属性
        :param x: 目标位置的 x 坐标
        :param y: 目标位置的 y 坐标
        :param alpha_color: 可选,透明色,若源像素等于该值则不绘制
        """
        source_width = 296  # source.width
        source_height = 128  # source.height
        source_buffer = source

        for src_y in range(source_height):
            for src_x in range(source_width):
                # 计算目标位置
                target_x = x + src_x
                target_y = y + src_y

                # 确保目标位置在范围内
                if 0 <= target_x < self.width and 0 <= target_y < self.height:
                    # 获取源像素
                    pixel = source_buffer[src_y * source_width + src_x]

                    # 处理透明度
                    if alpha_color is not None and pixel == alpha_color:
                        continue  # 跳过透明像素

                    # 设置目标像素
                    self.buffer[target_y * self.width + target_x] = pixel

    def scroll(self, delta_x, delta_y):
        """shifts framebuf in x and y direction"""
        if delta_x < 0:
            shift_x = 0
            xend = self.width + delta_x
            dt_x = 1
        else:
            shift_x = self.width - 1
            xend = delta_x - 1
            dt_x = -1
        if delta_y < 0:
            y = 0
            yend = self.height + delta_y
            dt_y = 1
        else:
            y = self.height - 1
            yend = delta_y - 1
            dt_y = -1
        while y != yend:
            x = shift_x
            while x != xend:
                self.format.set_pixel(
                    self, x, y, self.format.get_pixel(self, x - delta_x, y - delta_y)
                )
                x += dt_x
            y += dt_y

    # pylint: disable=too-many-arguments
    def text(self, string, x, y, color, *, font_name="/data/font5x8.bin", size=1):
        """Place text on the screen in variables sizes. Breaks on \n to next line.
        Does not break on line going off screen.
        """
        # determine our effective width/height, taking rotation into account
        frame_width = self.width
        frame_height = self.height
        if self.rotation in (1, 3):
            frame_width, frame_height = frame_height, frame_width

        for chunk in string.split("\n"):
            if not self._font or self._font.font_name != font_name:
                # load the font!
                self._font = BitmapFont(font_name)
            width = self._font.font_width
            height = self._font.font_height
            for i, char in enumerate(chunk):
                char_x = x + (i * (width + 1)) * size
                if (
                        char_x + (width * size) > 0
                        and char_x < frame_width
                        and y + (height * size) > 0
                        and y < frame_height
                ):
                    self._font.draw_char(char, char_x, y, self, color, size=size)
            y += height * size

    # pylint: enable=too-many-arguments

    def image(self, img):
        """Set buffer to value of Python Imaging Library image.  The image should
        be in 1 bit mode and a size equal to the display size."""
        # determine our effective width/height, taking rotation into account
        width = self.width
        height = self.height
        if self.rotation in (1, 3):
            width, height = height, width

        if isinstance(self.format, RGB888Format) and img.mode != "RGB":
            raise ValueError("Image must be in mode RGB.")
        if isinstance(self.format, (MHMSBFormat, MVLSBFormat)) and img.mode != "1":
            raise ValueError("Image must be in mode 1.")

        imwidth, imheight = img.size
        if imwidth != width or imheight != height:
            raise ValueError(
                "Image must be same dimensions as display ({0}x{1}).".format(
                    width, height
                )
            )
        # Grab all the pixels from the image, faster than getpixel.
        pixels = img.load()
        # Clear buffer
        for i in range(len(self.buf)):  # pylint: disable=consider-using-enumerate
            self.buf[i] = 0
        # Iterate through the pixels
        for x in range(width):  # yes this double loop is slow,
            for y in range(height):  # but these displays are small!
                if img.mode == "RGB":
                    self.pixel(x, y, pixels[(x, y)])
                elif pixels[(x, y)]:
                    self.pixel(x, y, 1)  # only write if pixel is true


# MicroPython basic bitmap font renderer.
# Author: Tony DiCola
# License: MIT License (https://opensource.org/licenses/MIT)
class BitmapFont:
    """A helper class to read binary font tiles and 'seek' through them as a
    file to display in a framebuffer. We use file access so we dont waste 1KB
    of RAM on a font!"""

    def __init__(self, font_name="font5x8.bin"):
        # Specify the drawing area width and height, and the pixel function to
        # call when drawing pixels (should take an x and y param at least).
        # Optionally specify font_name to override the font file to use (default
        # is font5x8.bin).  The font format is a binary file with the following
        # format:
        # - 1 unsigned byte: font character width in pixels
        # - 1 unsigned byte: font character height in pixels
        # - x bytes: font data, in ASCII order covering all 255 characters.
        #            Each character should have a byte for each pixel column of
        #            data (i.e. a 5x8 font has 5 bytes per character).
        self.font_name = font_name

        # Open the font file and grab the character width and height values.
        # Note that only fonts up to 8 pixels tall are currently supported.
        try:
            self._font = open(  # pylint: disable=consider-using-with
                self.font_name, "rb"
            )
            self.font_width, self.font_height = struct.unpack("BB", self._font.read(2))
            # simple font file validation check based on expected file size
            if 2 + 256 * self.font_width != os.stat(font_name)[6]:
                raise RuntimeError("Invalid font file: " + font_name)
        except OSError:
            print("Could not find font file", font_name)
            raise
        except OverflowError:
            # os.stat can throw this on boards without long int support
            # just hope the font file is valid and press on
            pass

    def deinit(self):
        """Close the font file as cleanup."""
        self._font.close()

    def __enter__(self):
        """Initialize/open the font file"""
        self.__init__()
        return self

    def __exit__(self, exception_type, exception_value, traceback):
        """cleanup on exit"""
        self.deinit()

    def draw_char(
            self, char, x, y, framebuffer, color, size=1
    ):  # pylint: disable=too-many-arguments
        """Draw one character at position (x,y) to a framebuffer in a given color"""
        size = max(size, 1)
        # Don't draw the character if it will be clipped off the visible area.
        # if x < -self.font_width or x >= framebuffer.width or \
        #   y < -self.font_height or y >= framebuffer.height:
        #    return
        # Go through each column of the character.
        for char_x in range(self.font_width):
            # Grab the byte for the current column of font data.
            self._font.seek(2 + (ord(char) * self.font_width) + char_x)
            try:
                line = struct.unpack("B", self._font.read(1))[0]
            except RuntimeError:
                continue  # maybe character isnt there? go to next
            # Go through each row in the column byte.
            for char_y in range(self.font_height):
                # Draw a pixel for each bit that's flipped on.
                if (line >> char_y) & 0x1:
                    framebuffer.fill_rect(
                        x + char_x * size, y + char_y * size, size, size, color
                    )

    def width(self, text):
        """Return the pixel width of the specified text message."""
        return len(text) * (self.font_width + 1)


class FrameBuffer1(FrameBuffer):  # pylint: disable=abstract-method
    """FrameBuffer1 object. Inherits from FrameBuffer."""
    pass





2.9寸墨水屏驱动,使用的是汉朔2.9寸墨水屏
支持局刷

from micropython import const
from time import sleep_ms
import ustruct
import cframebuf
from machine import Pin, SPI, RTC
# Display resolution
EPD_WIDTH  = const(128)
EPD_HEIGHT = const(296)

# Display commands
DRIVER_OUTPUT_CONTROL                = const(0x01)
BOOSTER_SOFT_START_CONTROL           = const(0x0C)
#GATE_SCAN_START_POSITION             = const(0x0F)
DEEP_SLEEP_MODE                      = const(0x10)
DATA_ENTRY_MODE_SETTING              = const(0x11)
#SW_RESET                             = const(0x12)
#TEMPERATURE_SENSOR_CONTROL           = const(0x1A)
MASTER_ACTIVATION                    = const(0x20)
#DISPLAY_UPDATE_CONTROL_1             = const(0x21)
DISPLAY_UPDATE_CONTROL_2             = const(0x22)
WRITE_RAM                            = const(0x24)
WRITE_VCOM_REGISTER                  = const(0x2C)
WRITE_LUT_REGISTER                   = const(0x32)
SET_DUMMY_LINE_PERIOD                = const(0x3A)
SET_GATE_TIME                        = const(0x3B)
#BORDER_WAVEFORM_CONTROL              = const(0x3C)
SET_RAM_X_ADDRESS_START_END_POSITION = const(0x44)
SET_RAM_Y_ADDRESS_START_END_POSITION = const(0x45)
SET_RAM_X_ADDRESS_COUNTER            = const(0x4E)
SET_RAM_Y_ADDRESS_COUNTER            = const(0x4F)
TERMINATE_FRAME_READ_WRITE           = const(0xFF)

BUSY = const(1)  # 1=busy, 0=idle

mosi = Pin(3)
sck = Pin(13)
cs = Pin(15)
dc = Pin(16)
rst = Pin(8)
busy = Pin(1)



class EPD(cframebuf.FrameBuffer):
    # 30 bytes (look up tables)
    # original waveshare example
    LUT_FULL_UPDATE    = bytearray(b'\x02\x02\x01\x11\x12\x12\x22\x22\x66\x69\x69\x59\x58\x99\x99\x88\x00\x00\x00\x00\xF8\xB4\x13\x51\x35\x51\x51\x19\x01\x00')
    LUT_PARTIAL_UPDATE = bytearray(b'\x10\x18\x18\x08\x18\x18\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x13\x14\x44\x12\x00\x00\x00\x00\x00\x00')

    # https://github.com/ZinggJM/GxEPD/blob/master/GxGDEH029A1/GxGDEH029A1.cpp
    #LUT_FULL_UPDATE    = bytearray(b'\x50\xAA\x55\xAA\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\x1F\x00\x00\x00\x00\x00\x00\x00')
    #LUT_PARTIAL_UPDATE = bytearray(b'\x10\x18\x18\x08\x18\x18\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x13\x14\x44\x12\x00\x00\x00\x00\x00\x00')
    
    def __init__(self):
        
        
        self.cs = Pin(cs, Pin.OUT, value=1)
        self.dc = Pin(dc, Pin.OUT, value=0)
        self.rst = Pin(rst, Pin.OUT, value=0)
        self.busy = Pin(busy, Pin.IN)
        self.width = EPD_WIDTH
        self.height = EPD_HEIGHT
        self.spi =  SPI(1, baudrate=4000000, polarity=0, phase=0, sck=sck, mosi=mosi)
        
        self.pages = self.height // 8
        self.buffer = bytearray(self.width * self.pages)
        super().__init__(self.buffer, self.width, self.height,
                         cframebuf.MHMSB)
        self.init()
        

    def clear(self):
        self.buffer = (b'\xff' * self.width * self.pages)

    def show(self,buffer):
        self.set_frame_memory(buffer, 0, 0, 128, 296)
        self.display_frame()
    def _command(self, command, data=None):
        self.dc.value(0)
        self.cs.value(0)
        self.spi.write(command.to_bytes(1, 'big'))
        #111self.spi.write(bytearray([command]))
        self.cs.value(1)
        if data is not None:
            self._data(data)

    def _data(self, data):
        self.dc.value(1)
        self.cs.value(0)
        self.spi.write(data)
        self.cs.value(1)

    def init(self):
        self.reset()
        self._command(DRIVER_OUTPUT_CONTROL, ustruct.pack("<HB", EPD_HEIGHT-1, 0x00))
        self._command(BOOSTER_SOFT_START_CONTROL, b'\xD7\xD6\x9D')
        self._command(WRITE_VCOM_REGISTER, b'\xA8') # VCOM 7C
        self._command(SET_DUMMY_LINE_PERIOD, b'\x1A') # 4 dummy lines per gate
        self._command(SET_GATE_TIME, b'\x08') # 2us per line
        self._command(DATA_ENTRY_MODE_SETTING, b'\x03') # X increment Y increment
        self.set_lut(self.LUT_FULL_UPDATE)

    def wait_until_idle(self):
        while self.busy.value() == BUSY:
            
            sleep_ms(100)

    def reset(self):
        self.rst.value(0)
        sleep_ms(200)
        self.rst.value(1)
        sleep_ms(200)

    def set_lut(self, lut):
        self._command(WRITE_LUT_REGISTER, lut)

    # put an image in the frame memory
    def set_frame_memory(self, image, x, y, w, h):
        # x point must be the multiple of 8 or the last 3 bits will be ignored
        x = x & 0xF8
        w = w & 0xF8

        if (x + w >= self.width):
            x_end = self.width - 1
        else:
            x_end = x + w - 1

        if (y + h >= self.height):
            y_end = self.height - 1
        else:
            y_end = y + h - 1

        self.set_memory_area(x, y, x_end, y_end)
        self.set_memory_pointer(x, y)
        self._command(WRITE_RAM, image)

    # replace the frame memory with the specified color
    def clear_frame_memory(self, color):
        self.set_memory_area(0, 0, self.width - 1, self.height - 1)
        self.set_memory_pointer(0, 0)
        self._command(WRITE_RAM)
        # send the color data
        for i in range(0, self.width // 8 * self.height):
            #111self._data(bytearray([color]))
            self._data(bytearray(color))

    # draw the current frame memory and switch to the next memory area
    def display_frame(self):
        self._command(DISPLAY_UPDATE_CONTROL_2, b'\xC4')
        self._command(MASTER_ACTIVATION)
        self._command(TERMINATE_FRAME_READ_WRITE)
        self.wait_until_idle()

    # specify the memory area for data R/W
    def set_memory_area(self, x_start, y_start, x_end, y_end):
        self._command(SET_RAM_X_ADDRESS_START_END_POSITION)
        # x point must be the multiple of 8 or the last 3 bits will be ignored
        self._data(bytearray([(x_start >> 3) & 0xFF]))
        self._data(bytearray([(x_end >> 3) & 0xFF]))
        self._command(SET_RAM_Y_ADDRESS_START_END_POSITION, ustruct.pack("<HH", y_start, y_end))

    # specify the start point for data R/W
    def set_memory_pointer(self, x, y):
        self._command(SET_RAM_X_ADDRESS_COUNTER)
        # x point must be the multiple of 8 or the last 3 bits will be ignored
        self._data(bytearray([(x >> 3) & 0xFF]))
        self._command(SET_RAM_Y_ADDRESS_COUNTER, ustruct.pack("<H", y))
        self.wait_until_idle()

    # to wake call reset() or init()
    def sleep(self):
        self._command(DEEP_SLEEP_MODE, b'\x01')
        self.wait_until_idle()
    def set_refresh(self, full_update=True):
        self.set_lut(self.LUT_FULL_UPDATE) if full_update else self.set_lut(self.LUT_PARTIAL_UPDATE)
if __name__ == "__main__":

    buf = bytearray(128 * 296 // 8)
    fb = cframebuf.FrameBuffer(buf, 128, 296, cframebuf.MHMSB)
    fb.rotation=3
    fb.fill_rect(0, 0, 296, 128, 0xff)

    epd = EPD()

    fb.text("U_U", 2, 3, 0x00, size=2)
    fb.text("test", 50, 3, 0x00, size=2)
    fb.text("1", 1000, 3, 0x00, size=2)
    epd.show(buf)




代码如下(示例):

目前只使用16X16的字体,其他尺寸字体暂时不兼容,后续有时间更新

更新目前已支持32*32的字体,其他尺寸的类似,主要更改的就是分几批for循环。–2024/10/22
displayHzk.py


'''
库,逐行式,高位在前

'''

"""汉字字模(95个ascii+6763个汉字+93个汉字符号)读取示例"""
class HZK:
    def __init__(self,HZKPath,FontPix):
        """HZKPath字库路径,FontPix字体大小"""
        self.fhzk=open(HZKPath,"rb")
        #是否使用RAM缓存映射
        
        self.sheet={}

        #计算单个汉字占用字节
        self.hzSize=int(FontPix*FontPix/8)
        #计算ascii占用字节,这里主要计算入12x12、12x24等比较特殊的字库
        if int(FontPix/2)%8!=0:
            col=(int(FontPix/2)//8)+1 
        else:
            col=int(FontPix/2)//8
        self.ascSize=col*FontPix
    
    def __del__(self):
        self.fhzk.close()
    
    def binarySearch(self, char):
        """二分法查找映射数据"""
        start, end = 0, 6951 - 1 
        while start <= end:
            mid = (start + end) // 2
            self.fhzk.seek(mid * 4)
            buf = self.fhzk.read(4)
            current_char = int.from_bytes(buf[0:2], "big")

            if current_char == ord(char):
                return int.from_bytes(buf[2:4], "big")
            elif current_char < ord(char):
                start = mid + 1
            else:
                end = mid - 1
        return None

    def getCharPos(self, _char):
        """计算指定字符在数据区域的位置"""
        pos = None
        
        pos = self.binarySearch(_char)
        return pos
                    
    def get(self,_char):
        """获取指定字符的点阵数据"""
        data=b""
        pos=self.getCharPos(_char)
        if pos!=None:
            self.fhzk.seek(pos*self.hzSize+6951*4)
            if ord(_char)<128:
                data=self.fhzk.read(self.ascSize)
            else:
                data=self.fhzk.read(self.hzSize)
        else:
            if ord(_char)<128:
                data=bytes(self.ascSize)
            else:
                data=bytes(self.hzSize)
        #print("data:%s"%data)        
        mylist=[]
        for i in range(len(data)):

            mylist.append(data[i])
        data=mylist
        
        return data


'''
=======================================
OLED显示汉字

思路:按字模软件的数据,每次显示一个数据,即8个点,1为亮,0为灭。
不断循环显示整个汉字,这种方式显示速度显然不会太快。
但其思路清晰易懂,先尝试。
=======================================
'''
def displayHzk(ch_str, x_axis, y_axis,fb,reverse=False,font='HZK16_宋体'):
    
    size=16
    if '32' in font:
        size=32

    hzk=HZK("/data/%s"%font,size)
    words = 0 #每描完一个字,换位置描下一个字
    for k in ch_str: #取出某个字符或汉字=k
        byte_data=hzk.get(k)
        if k==',':
            byte_data=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,48,0,48,0,16,0,32,0,0,0]
        if k=='!':
            byte_data=[0,0,16,0,16,0,16,0,16,0,16,0,16,0,16,0,16,0,16,0,0,0,0,0,16,0,16,0,0,0,0,0]

        #print(k,"对应:",byte_data)
        #byte_data1 = fonts[k] #按k取出字模数据
        if reverse:
            byte_data = [(~byte) & 0xff for byte in byte_data]

        #print(byte_data)
        data_len=len(byte_data) #字模数据长度,即数据个数        
        ch_size=0 #字号初值=0
        
        for i in range(8,65): #因为此OLED屏最高是64行,即最大字号可设为64。最小定为8号字
            num=int((i-1)/8)+1 # i=字号,num=扫描多少轮,能否理解这个规律?
            
            if((data_len/num)==i): # 数据量/扫描次数=字号,想想为什么?
                ch_size=i
                break
        #print("扫描多少轮num:",num)
        #print("字号:",ch_size)
        #print(k,data_len,ch_size)
        
        if ch_size==0:
            #非中文,特殊处理
            num=size//16
            for i in range(0,num):
                for y in range(i*data_len//2,(i+1)*data_len//num):
                    a = '{:0>8b}'.format(byte_data[y])
                    #print(a,'---',y,i)
                    for x in range(0, 8): #每个数据是8个点,查看其亮或灭
                        fb.pixel(x_axis+words +x+8*(y%num), y//num+y_axis ,int(a[x]))#逐行式
                        #print(x_axis + words+ x ,y  + y_axis ,int(a[x]))
                    
            words += len(byte_data)//(2*num)
        else:
            for i in range(0,num):
                for y in range(ch_size*i,ch_size*(i+1)): #分区块取出数据,该字被扫描几轮,就有几块数据,而每一次只取一个数据y来处理。
                    a = '{:0>8b}'.format(byte_data[y]) #python,进制转换补全。>表示右对齐,b即二进制,a字符串:8位二进制,左侧用0补足。
                    #print('取到的值=',a)
                    for x in range(0, 8): #每个数据是8个点,查看其亮或灭
                        fb.pixel( x_axis+words +x+8*(y%num), y//num+y_axis ,int(a[x]))#逐行式
                        
                        #fb.pixel(x_axis + words + x+8*i, y % ch_size + y_axis, int(a[x])) #行列式
                        #上式中 % 表示求余数,为什么要这样做,y % ch_size ?另,为什么8*i?
                        #a是字符串,a[x]能直接将字符串中x位置的字符取出,x从0开始,int转为整型。pixel最后一个参数:1时亮,0时灭
            words += ch_size #下一个字


if __name__ == "__main__":
    from driver import epaper2in9
    from lib import cframebuf
    from machine import Pin, I2C,SPI
    

    buf = bytearray(128 * 296 // 8)
    fb = cframebuf.FrameBuffer(buf, 128, 296, cframebuf.MHMSB)

    fb.rotation=3
    fb.fill_rect(0, 0, 296, 128, 0xff)

    epd = epaper2in9.EPD()

    epd.set_refresh(False)

    displayHzk('我看就是登记卡ASDASF123124231?',0,40,fb,True,font='HZK16_幼圆')
    displayHzk('ASD31。?',0,60,fb,True,font='HZK32_幼圆')
    #displayHzk('法国德国梵蒂冈订个蛋糕搭嘎搭嘎',40,60,fb,True,font='HZK16_宋体')
    #displayHzk('法国德国梵蒂冈订个蛋糕搭嘎搭嘎',40,80,fb,True,font='HZK16_隶书')

    #chinese('雪',60,60,fb)
    epd.show(buf)
  






  




您可能感兴趣的与本文相关的镜像

Python3.10

Python3.10

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值