Startseite bisherige Projekte Tools/Snippets Bücherempfehlungen Publikationen Impressum Datenschutzerklärung

E-Paper mit 3 Farben

Im August 2023

Bring etwas Farbe ins grau: Das
5.83inch E-Paper E-Ink Display Module (B) for Raspberry Pi Pico, 648×480, Red / Black / White, SPI
https://www.waveshare.com/5.83inch-e-paper-hat-b.htm
https://www.waveshare.com/wiki/5.83inch_e-Paper_HAT_(B)

Die Auflösung ist 648x480. Eigentlich ist es für den Raspberry Pi gedacht. Mit dem Pi Zero scheint es jedoch Timing-Probleme zu geben, und das Demoprogramm funktioniert nicht bzw. gibt nur Rauschen aus. Wie früher der Fernseher.
Mit dem Pi 4 klappt alles.
Das gleiche Display gibt es aber auch für den Pi Pico, und mit etwa Kabelstecken klappt das auch mit diesem Modell.

Wo bekommt man dreifarbige Bilder her? Das Waveshare-Demo benutzt zwei Bilder, ein Bild für rot und eines für schwarz. Erfreulicherweise sind Bilder im pbm-Format schon weitgehend in dem gleichen Format, das auch der ByteBuffer des micropython-Framebuffers verwendet.

Bildbearbeitung:

Und wie werden die Bilder dann angezeigt? Ein Microypthon Beispiel, abgeleitet vom Waveshare-Demo:

# Bilder laden und anzeigen
# Bilder müssen x_r.pbm und x_b.pbm heißen. Das Programm wählt
# zufällig Bilder aus

from machine import Pin, SPI
import framebuf
import utime
import os
import sys
import random
import gc

# Display resolution
EPD_WIDTH       = 648
EPD_HEIGHT      = 480

RST_PIN         = 12
DC_PIN          = 8
CS_PIN          = 9
BUSY_PIN        = 13


class EPD_5in83_B():
    def __init__(self):
        self.reset_pin = Pin(RST_PIN, Pin.OUT)
        
        self.busy_pin = Pin(BUSY_PIN, Pin.IN, Pin.PULL_UP)
        self.cs_pin = Pin(CS_PIN, Pin.OUT)
        self.width = EPD_WIDTH
        self.height = EPD_HEIGHT
        
        self.spi = SPI(1)
        self.spi.init(baudrate=4000_000)
        self.dc_pin = Pin(DC_PIN, Pin.OUT)
        
        self.buffer_black = bytearray(self.height * self.width // 8)
        self.buffer_red = bytearray(self.height * self.width // 8)
        self.imageblack = framebuf.FrameBuffer(self.buffer_black, self.width, self.height, framebuf.MONO_HLSB)
        self.imagered = framebuf.FrameBuffer(self.buffer_red, self.width, self.height, framebuf.MONO_HLSB)
        self.init()

    def digital_write(self, pin, value):
        pin.value(value)

    def digital_read(self, pin):
        return pin.value()

    def delay_ms(self, delaytime):
        utime.sleep(delaytime / 1000.0)

    def spi_writebyte(self, data):
        self.spi.write(bytearray(data))

    def module_exit(self):
        self.digital_write(self.reset_pin, 0)

    # Hardware reset
    def reset(self):
        self.digital_write(self.reset_pin, 1)
        self.delay_ms(50) 
        self.digital_write(self.reset_pin, 0)
        self.delay_ms(2)
        self.digital_write(self.reset_pin, 1)
        self.delay_ms(50)   

    def send_command(self, command):
        self.digital_write(self.dc_pin, 0)
        self.digital_write(self.cs_pin, 0)
        self.spi_writebyte([command])
        self.digital_write(self.cs_pin, 1)

    def send_data(self, data):
        self.digital_write(self.dc_pin, 1)
        self.digital_write(self.cs_pin, 0)
        self.spi_writebyte([data])
        self.digital_write(self.cs_pin, 1)
        
    def send_data1(self, data):
        self.digital_write(self.dc_pin, 1)
        self.digital_write(self.cs_pin, 0)
        self.spi_writebyte(data)
        self.digital_write(self.cs_pin, 1)

    def ReadBusy(self):
        print("e-Paper busy")
        while(self.digital_read(self.busy_pin) == 0):      #  1: idle, 0: busy
            self.delay_ms(10)
        print("e-Paper busy release")  

    def TurnOnDisplay(self):
        self.send_command(0x12) 
        self.delay_ms(100)
        self.ReadBusy()
        
    def init(self):
        # EPD hardware init start     
        self.reset()
        
        self.send_command(0x01)     #POWER SETTING
        self.send_data (0x07)
        self.send_data (0x07)       #VGH=20V,VGL=-20V
        self.send_data (0x3f)       #VDH=15V
        self.send_data (0x3f)       #VDL=-15V

        self.send_command(0x04) #POWER ON
        self.delay_ms(100)  
        self.ReadBusy()   #waiting for the electronic paper IC to release the idle signal

        self.send_command(0X00)     #PANNEL SETTING
        self.send_data(0x0F)        #KW-3f   KWR-2F    BWROTP 0f   BWOTP 1f

        self.send_command(0x61)     #tres
        self.send_data (0x02)       #source 648
        self.send_data (0x88)
        self.send_data (0x01)       #gate 480
        self.send_data (0xe0)

        self.send_command(0X15)
        self.send_data(0x00)

        self.send_command(0X50)     #VCOM AND DATA INTERVAL SETTING
        self.send_data(0x11)
        self.send_data(0x07)

        self.send_command(0X60)     #TCON SETTING
        self.send_data(0x22)
        # EPD hardware init end
        return 0

    def display(self, imageBlack, imageRed):
        if (imageBlack == None or imageRed == None):
            return    
        self.send_command(0x10) # WRITE_RAM
        self.send_data1(imageBlack)
        self.send_command(0x13) # WRITE_RAM
        self.send_data1(imageRed)
        self.TurnOnDisplay()

    def Clear(self, colorBalck, colorRed):
        self.send_command(0x10) # WRITE_RAM
        for i in range(0, int(self.width / 8)):
            self.send_data1([colorBalck] * self.height)
            
        self.send_command(0x13) # WRITE_RAM
        for i in range(0, int(self.width / 8)):
            self.send_data1([colorRed] * self.height)
        self.TurnOnDisplay()

    def sleep(self):
        self.send_command(0x02) # DEEP_SLEEP_MODE
        self.ReadBusy()
        self.send_command(0x07)
        self.send_data(0xa5)
        
        self.delay_ms(2000)
        self.module_exit()

if __name__=='__main__':
    epd = EPD_5in83_B()
    epd.Clear(0xff, 0x00)
    
    filename = ""
    
    # find suitable image (ending with _b.pbm)
    images = []
    for file in os.listdir():
        if file[-6:] == '_b.pbm':
            images.append(file)
            
    if not images:
        epd.imageblack.fill(0xff)
        epd.imagered.fill(0x00)
        epd.imageblack.text("Found no images", 0, 10, 0x00)
        epd.display(epd.buffer_black, epd.buffer_red)
        epd.delay_ms(2000)
        print("Found no images")
        sys.exit();
        
    # pick random image file
    filename = images[random.randint(0,len(images)-1)]
    filenamer = filename[0:-6] + '_r.pbm'

    # read and set black file
    print ("Using black image ", filename)
    with open(filename, mode="rb") as file:
        b = file.read()
    epd.buffer_black[:] = b[len(b)-(648*480//8):] # do some calculation to skip pbm header
    # my black file need to be inverted
    for i in range(0, len( epd.buffer_black)): 
        epd.buffer_black[i] = ~ epd.buffer_black[i]
    
    del b	# free some memory
    
    # read and set red file
    print ("Using red image ", filename)
    with open(filenamer, mode="rb") as file:
        b = file.read()
        
    epd.buffer_red[:] = b[len(b)-(648*480//8):] # do some calculation to skip pbm header

    epd.display(epd.buffer_black, epd.buffer_red)
    epd.delay_ms(2000)
    
    print("...")

#     print("sleep")
#     epd.sleep()


Schön schön, damit kann man das Display als Bilderrahmen verwenden.


Nett. Aber wie wäre es mit eigenem Text? Oder einer Art Sinnspruchanzeiger?

Für die Anzeige von Text kann folgendes Tool verwendet werden:
https://github.com/peterhinch/micropython-font-to-py/tree/master
Für das E-Paper Display wird dazu folgende Modifikation benötigt:
https://forum.micropython.org/viewtopic.php?t=6319&start=10

Hinweis: font-to-py funktioniert auf Linux-Systemen. Oder aur Windows unter WSL.
Aufruf z.B. mit
python font_to_py.py -x -k chars.txt BigShouldersInlineText-VariableFont_wght.ttf 96 bigs96.py
wobei chars.txt die verwendeten Zeichen enthält, inkl. äöüß.

Das folgende Programm erzeugt aus einem Font und einem Hintergrundbild (für den rot-Kanal) ein Bild mit Text:

# Text auf dem Display darstellen.
# Basiert auf dem Waveshare-Demoprogramm
# inkfree72h.py wurde mit font-to-py erstellt
# benötigt noch writer.py
#

from machine import Pin, SPI
import machine
import framebuf
import utime
import sys
import gc
import random

# Display resolution
EPD_WIDTH       = 648
EPD_HEIGHT      = 480

RST_PIN         = 12
DC_PIN          = 8
CS_PIN          = 9
BUSY_PIN        = 13

# Helper for Writer, see https://forum.micropython.org/viewtopic.php?t=6319&start=10
class DummyDisplay(framebuf.FrameBuffer):
    def __init__(self, buffer, width, height, format):
        self.height = height
        self.width = width
        self.buffer = buffer
        self.format = format
        super().__init__(buffer, width, height, format)

class EPD_5in83_B():
    def __init__(self):
        self.reset_pin = Pin(RST_PIN, Pin.OUT)
        
        self.busy_pin = Pin(BUSY_PIN, Pin.IN, Pin.PULL_UP)
        self.cs_pin = Pin(CS_PIN, Pin.OUT)
        self.width = EPD_WIDTH
        self.height = EPD_HEIGHT
        
        self.spi = SPI(1)
        self.spi.init(baudrate=4000_000)
        self.dc_pin = Pin(DC_PIN, Pin.OUT)
        
        self.buffer_black = bytearray(self.height * self.width // 8)
        self.buffer_red = bytearray(self.height * self.width // 8)
        self.imageblack = DummyDisplay(self.buffer_black, self.width, self.height, framebuf.MONO_HLSB)
        self.imagered = DummyDisplay(self.buffer_red, self.width, self.height, framebuf.MONO_HLSB)
        self.init()

    def digital_write(self, pin, value):
        pin.value(value)

    def digital_read(self, pin):
        return pin.value()

    def delay_ms(self, delaytime):
        utime.sleep(delaytime / 1000.0)

    def spi_writebyte(self, data):
        self.spi.write(bytearray(data))
        
    def spi_writebyte2(self, data):
        self.spi.write(data)

    def module_exit(self):
        self.digital_write(self.reset_pin, 0)

    # Hardware reset
    def reset(self):
        self.digital_write(self.reset_pin, 1)
        self.delay_ms(50) 
        self.digital_write(self.reset_pin, 0)
        self.delay_ms(2)
        self.digital_write(self.reset_pin, 1)
        self.delay_ms(50)   

    def send_command(self, command):
        self.digital_write(self.dc_pin, 0)
        self.digital_write(self.cs_pin, 0)
        self.spi_writebyte([command])
        self.digital_write(self.cs_pin, 1)

    def send_data(self, data):
        self.digital_write(self.dc_pin, 1)
        self.digital_write(self.cs_pin, 0)
        self.spi_writebyte([data])
        self.digital_write(self.cs_pin, 1)
        
    def send_data1(self, data):
        self.digital_write(self.dc_pin, 1)
        self.digital_write(self.cs_pin, 0)
        self.spi_writebyte(data)
        self.digital_write(self.cs_pin, 1)
        
    def send_data2(self, data):
        self.digital_write(self.dc_pin, 1)
        self.digital_write(self.cs_pin, 0)
        self.spi_writebyte2(data)
        self.digital_write(self.cs_pin, 1)

    def ReadBusy(self):
        print("e-Paper busy")
        while(self.digital_read(self.busy_pin) == 0):      #  1: idle, 0: busy
            self.delay_ms(10)
        print("e-Paper busy release")  

    def TurnOnDisplay(self):
        self.send_command(0x12) 
        self.delay_ms(100)
        self.ReadBusy()
        
    def init(self):
        # EPD hardware init start     
        self.reset()
        
        self.send_command(0x01)     #POWER SETTING
        self.send_data (0x07)
        self.send_data (0x07)       #VGH=20V,VGL=-20V
        self.send_data (0x3f)       #VDH=15V
        self.send_data (0x3f)       #VDL=-15V

        self.send_command(0x04) #POWER ON
        self.delay_ms(100)  
        self.ReadBusy()   #waiting for the electronic paper IC to release the idle signal

        self.send_command(0X00)     #PANNEL SETTING
        self.send_data(0x0F)        #KW-3f   KWR-2F    BWROTP 0f   BWOTP 1f

        self.send_command(0x61)     #tres
        self.send_data (0x02)       #source 648
        self.send_data (0x88)
        self.send_data (0x01)       #gate 480
        self.send_data (0xe0)

        self.send_command(0X15)
        self.send_data(0x00)

        self.send_command(0X50)     #VCOM AND DATA INTERVAL SETTING
        self.send_data(0x11)
        self.send_data(0x07)

        self.send_command(0X60)     #TCON SETTING
        self.send_data(0x22)
        # EPD hardware init end
        return 0

    def display(self, imageBlack, imageRed):
        if (imageBlack == None or imageRed == None):
            return    
        self.send_command(0x10) # WRITE_RAM
        self.send_data2(imageBlack)
        self.send_command(0x13) # WRITE_RAM
        self.send_data2(imageRed)
        self.TurnOnDisplay()

    def Clear(self, colorBalck, colorRed):
        self.send_command(0x10) # WRITE_RAM
        for i in range(0, int(self.width / 8)):
            self.send_data1([colorBalck] * self.height)
            
        self.send_command(0x13) # WRITE_RAM
        for i in range(0, int(self.width / 8)):
            self.send_data1([colorRed] * self.height)
        self.TurnOnDisplay()

    def sleep(self):
        self.send_command(0x02) # DEEP_SLEEP_MODE
        self.ReadBusy()
        self.send_command(0x07)
        self.send_data(0xa5)
        
        self.delay_ms(2000)
        self.module_exit()

def showRule(epd):
    epd.imageblack.fill(0x00)
    epd.imagered.fill(0x00)
    
    #epd.imageblack.text("Found no images", 0, 10, 0x00)
    #epd.display(epd.buffer_black, epd.buffer_red)
    #epd.delay_ms(2000)
    
    bsa = ['bg.pbm', 'bg2.pbm', 'bg3.pbm', 'bg4.pbm']
    bs = bsa[random.randint(0,len(bsa)-1)]
    
    with open(bs, mode="rb") as file:
        b = file.read()
    epd.buffer_red[:] = b[len(b)-(648*480//8):] # do some calculation to skip pbm header
    
    del b
    gc.collect()
    
    from writer import Writer

    # Font
   
    fi = random.randint(0,4)
    if (fi == 0):
        import inkfree72
        font = inkfree72
    elif (fi == 1):
        import alfa72
        font = alfa72
    elif (fi == 2):
        import bwpic72
        font = bwpic72
    elif (fi == 3):
        import acme72
        font = acme72
    else:
        import borel72
        font = borel72
        
    # Text
    texts = [
    'Lebe dein Leben nie ohne ein Lachen, denn es gibt Menschen, die von deinem Lachen leben.',
    'Vergiss nicht, man braucht nur wenig um ein glückliches Leben zu führen.',
    'Wer ein WARUM zum Leben hat, erträgt fast jedes WIE'
    
    ]
    
    text = texts[random.randint(0,len(texts)-1)]        
    
    # Writing text to create white background
    wri = Writer(epd.imagered, font)
    Writer.set_textpos(epd.imagered, 60, 50)  
    wri.printstring(text, invert=True)
     
    del wri
    gc.collect()
      
    
    wri = Writer(epd.imageblack, font)
    Writer.set_textpos(epd.imageblack, 60, 50)  
    wri.printstring(text)
    
    # my black file need to be inverted
    for i in range(0, len( epd.buffer_black)): 
        epd.buffer_black[i] = ~ epd.buffer_black[i]
        
    # remove black information from red buffer to make them visible
    for i in range(0, len( epd.buffer_black)):
        epd.buffer_red[i] = epd.buffer_red[i] & epd.buffer_black[i]
        
    del font
    gc.collect()
    
    epd.Clear(0xff, 0x00)
    epd.display(epd.buffer_black, epd.buffer_red)
    epd.delay_ms(2000)
    

if __name__=='__main__':
    epd = EPD_5in83_B()
    
    while True:
        showRule(epd)
        machine.lightsleep(1000 * 60 * 15) # 15min
    
    print("...")



Das Programm writer.py habe ich auch leicht angepasst:

# writer.py with small modifications

# writer.py Implements the Writer class.
# Handles colour, word wrap and tab stops

# V0.5.1 Dec 2022 Support 4-bit color display drivers.
# V0.5.0 Sep 2021 Color now requires firmware >= 1.17.
# V0.4.3 Aug 2021 Support for fast blit to color displays (PR7682).
# V0.4.0 Jan 2021 Improved handling of word wrap and line clip. Upside-down
# rendering no longer supported: delegate to device driver.
# V0.3.5 Sept 2020 Fast rendering option for color displays

# Released under the MIT License (MIT). See LICENSE.
# Copyright (c) 2019-2021 Peter Hinch

# A Writer supports rendering text to a Display instance in a given font.
# Multiple Writer instances may be created, each rendering a font to the
# same Display object.

# Timings were run on a pyboard D SF6W comparing slow and fast rendering
# and averaging over multiple characters. Proportional fonts were used.
# 20 pixel high font, timings were 5.44ms/467μs, gain 11.7 (freesans20).
# 10 pixel high font, timings were 1.76ms/396μs, gain 4.36 (arial10).

# https://github.com/peterhinch/micropython-font-to-py

import framebuf
from uctypes import bytearray_at, addressof
from sys import implementation

__version__ = (0, 5, 1)

fast_mode = True  # Does nothing. Kept to avoid breaking code.

class DisplayState():
    def __init__(self):
        self.text_row = 0
        self.text_col = 0

def _get_id(device):
    if not isinstance(device, framebuf.FrameBuffer):
        raise ValueError('Device must be derived from FrameBuffer.')
    return id(device)

# Basic Writer class for monochrome displays
class Writer():

    state = {}  # Holds a display state for each device

    @staticmethod
    def set_textpos(device, row=None, col=None):
        devid = _get_id(device)
        if devid not in Writer.state:
            Writer.state[devid] = DisplayState()
        s = Writer.state[devid]  # Current state
        if row is not None:
            if row < 0 or row >= device.height:
                raise ValueError('row is out of range')
            s.text_row = row
        if col is not None:
            if col < 0 or col >= device.width:
                raise ValueError('col is out of range')
            s.text_col = col
            s.text_start_col = col # hacked
        return s.text_row,  s.text_col

    def __init__(self, device, font, verbose=True):
        self.devid = _get_id(device)
        self.device = device
        if self.devid not in Writer.state:
            Writer.state[self.devid] = DisplayState()
        self.font = font
        if font.height() >= device.height or font.max_width() >= device.width:
            raise ValueError('Font too large for screen')
        # Allow to work with reverse or normal font mapping
        if font.hmap():
            self.map = framebuf.MONO_HMSB if font.reverse() else framebuf.MONO_HLSB
        else:
            raise ValueError('Font must be horizontally mapped.')
        if verbose:
            fstr = 'Orientation: Horizontal. Reversal: {}. Width: {}. Height: {}.'
            print(fstr.format(font.reverse(), device.width, device.height))
            print('Start row = {} col = {}'.format(self._getstate().text_row, self._getstate().text_col))
        self.screenwidth = device.width  # In pixels
        self.screenheight = device.height
        self.bgcolor = 0  # Monochrome background and foreground colors
        self.fgcolor = 1
        self.row_clip = False  # Clip or scroll when screen fullt
        self.col_clip = False  # Clip or new line when row is full
        self.wrap = True  # Word wrap
        self.cpos = 0
        self.tab = 4

        self.glyph = None  # Current char
        self.char_height = 0
        self.char_width = 0
        self.clip_width = 0
        self.padright = 60 # added

    def _getstate(self):
        return Writer.state[self.devid]

    def _newline(self):
        s = self._getstate()
        height = self.font.height()
        s.text_row += height
        s.text_col = s.text_start_col # 0 # hacked, remember left border
        margin = self.screenheight - (s.text_row + height)
        y = self.screenheight + margin
        if margin < 0:
            if not self.row_clip:
                self.device.scroll(0, margin)
                self.device.fill_rect(0, y, self.screenwidth, abs(margin), self.bgcolor)
                s.text_row += margin

    def set_clip(self, row_clip=None, col_clip=None, wrap=None):
        if row_clip is not None:
            self.row_clip = row_clip
        if col_clip is not None:
            self.col_clip = col_clip
        if wrap is not None:
            self.wrap = wrap
        return self.row_clip, self.col_clip, self.wrap

    @property
    def height(self):  # Property for consistency with device
        return self.font.height()

    def printstring(self, string, invert=False):
        # word wrapping. Assumes words separated by single space.
        q = string.split('\n')
        last = len(q) - 1
        for n, s in enumerate(q):
            if s:
                self._printline(s, invert)
            if n != last:
                self._printchar('\n')

    def _printline(self, string, invert):
        rstr = None
        if self.wrap and self.stringlen(string, True):  # Length > self.screenwidth
            pos = 0
            lstr = string[:]
            while self.stringlen(lstr, True):  # Length > self.screenwidth
                pos = lstr.rfind(' ')
                lstr = lstr[:pos].rstrip()
            if pos > 0:
                rstr = string[pos + 1:]
                string = lstr
                
        for char in string:
            self._printchar(char, invert)
        if rstr is not None:
            self._printchar('\n')
            self._printline(rstr, invert)  # Recurse

    def stringlen(self, string, oh=False):
        if not len(string):
            return 0
        sc = self._getstate().text_col  # Start column
        wd = self.screenwidth - self.padright
        l = 0
        for char in string[:-1]:
            _, _, char_width = self.font.get_ch(char)
            l += char_width
            if oh and l + sc > wd:
                return True  # All done. Save time.
        char = string[-1]
        _, _, char_width = self.font.get_ch(char)
        if oh and l + sc + char_width > wd:
            l += self._truelen(char)  # Last char might have blank cols on RHS
        else:
            l += char_width  # Public method. Return same value as old code.
        return l + sc > wd if oh else l

    # Return the printable width of a glyph less any blank columns on RHS
    def _truelen(self, char):
        glyph, ht, wd = self.font.get_ch(char)
        div, mod = divmod(wd, 8)
        gbytes = div + 1 if mod else div  # No. of bytes per row of glyph
        mc = 0  # Max non-blank column
        data = glyph[(wd - 1) // 8]  # Last byte of row 0
        for row in range(ht):  # Glyph row
            for col in range(wd -1, -1, -1):  # Glyph column
                gbyte, gbit = divmod(col, 8)
                if gbit == 0:  # Next glyph byte
                    data = glyph[row * gbytes + gbyte]
                if col <= mc:
                    break
                if data & (1 << (7 - gbit)):  # Pixel is lit (1)
                    mc = col  # Eventually gives rightmost lit pixel
                    break
            if mc + 1 == wd:
                break  # All done: no trailing space
        # print('Truelen', char, wd, mc + 1)  # TEST 
        return mc + 1

    def _get_char(self, char, recurse):
        if not recurse:  # Handle tabs
            if char == '\n':
                self.cpos = 0
            elif char == '\t':
                nspaces = self.tab - (self.cpos % self.tab)
                if nspaces == 0:
                    nspaces = self.tab
                while nspaces:
                    nspaces -= 1
                    self._printchar(' ', recurse=True)
                self.glyph = None  # All done
                return

        self.glyph = None  # Assume all done
        if char == '\n':
            self._newline()
            return
        glyph, char_height, char_width = self.font.get_ch(char)
        s = self._getstate()
        np = None  # Allow restriction on printable columns
        if s.text_row + char_height > self.screenheight:
            if self.row_clip:
                return
            self._newline()
        oh = s.text_col + char_width - self.screenwidth  # Overhang (+ve)
        
        if oh > 0:
            if self.col_clip or self.wrap:
                np = char_width - oh  # No. of printable columns
                if np <= 0:
                    return
            else:
                self._newline()
        self.glyph = glyph
        self.char_height = char_height
        self.char_width = char_width
        self.clip_width = char_width if np is None else np
        
    # Method using blitting. Efficient rendering for monochrome displays.
    # Tested on SSD1306. Invert is for black-on-white rendering.
    def _printchar(self, char, invert=False, recurse=False):
        s = self._getstate()
        self._get_char(char, recurse)
        if self.glyph is None:
            return  # All done
        buf = bytearray(self.glyph)
        if invert:
            for i, v in enumerate(buf):
                buf[i] = 0xFF & ~ v
        fbc = framebuf.FrameBuffer(buf, self.clip_width, self.char_height, self.map)
        if invert:
            # hacked. Invert now defines transparent white font (for outlines)
            for x in range(0,12,3):
                for y in range(0,12,3):
                    self.device.blit(fbc, s.text_col-5+y, s.text_row-5+x, 1) 
        else:
            self.device.blit(fbc, s.text_col, s.text_row)
        s.text_col += self.char_width
        self.cpos += 1

    def tabsize(self, value=None):
        if value is not None:
            self.tab = value
        return self.tab

    def setcolor(self, *_):
        return self.fgcolor, self.bgcolor

# Writer for colour displays.
class CWriter(Writer):

    @staticmethod
    def create_color(ssd, idx, r, g, b):
        c = ssd.rgb(r, g, b)
        if not hasattr(ssd, 'lut'):
            return c
        if not 0 <= idx <= 15:
            raise ValueError('Color nos must be 0..15')
        x = idx << 1
        ssd.lut[x] = c & 0xff
        ssd.lut[x + 1] = c >> 8
        return idx

    def __init__(self, device, font, fgcolor=None, bgcolor=None, verbose=True):
        if not hasattr(device, 'palette'):
            raise OSError('Incompatible device driver.')
        if implementation[1] < (1, 17, 0):
            raise OSError('Firmware must be >= 1.17.')

        super().__init__(device, font, verbose)
        if bgcolor is not None:  # Assume monochrome.
            self.bgcolor = bgcolor
        if fgcolor is not None:
            self.fgcolor = fgcolor
        self.def_bgcolor = self.bgcolor
        self.def_fgcolor = self.fgcolor

    def _printchar(self, char, invert=False, recurse=False):
        s = self._getstate()
        self._get_char(char, recurse)
        if self.glyph is None:
            return  # All done
        buf = bytearray_at(addressof(self.glyph), len(self.glyph))
        fbc = framebuf.FrameBuffer(buf, self.clip_width, self.char_height, self.map)
        palette = self.device.palette
        palette.bg(self.fgcolor if invert else self.bgcolor)
        palette.fg(self.bgcolor if invert else self.fgcolor)
        self.device.blit(fbc, s.text_col, s.text_row, -1, palette)
        s.text_col += self.char_width
        self.cpos += 1

    def setcolor(self, fgcolor=None, bgcolor=None):
        if fgcolor is None and bgcolor is None:
            self.fgcolor = self.def_fgcolor
            self.bgcolor = self.def_bgcolor
        else:
            if fgcolor is not None:
                self.fgcolor = fgcolor
            if bgcolor is not None:
                self.bgcolor = bgcolor
        return self.fgcolor, self.bgcolor




Erstmal schön. Allerdings wird der Speicher schnell knapp, und die 1,4 MB im Pi Pico Flash reichen nicht für allzuviele Bilder oder Fonts.


Impressum - Datenschutzerklärung