rgb_display_fbcp.py
1 import time 2 import os 3 import fcntl 4 import mmap 5 import struct 6 import digitalio 7 import board 8 from PIL import Image, ImageDraw 9 import adafruit_rgb_display.st7789 as st7789 10 11 # definitions from linux/fb.h 12 FBIOGET_VSCREENINFO = 0x4600 13 FBIOGET_FSCREENINFO = 0x4602 14 FBIOBLANK = 0x4611 15 16 FB_TYPE_PACKED_PIXELS = 0 17 FB_TYPE_PLANES = 1 18 FB_TYPE_INTERLEAVED_PLANES = 2 19 FB_TYPE_TEXT = 3 20 FB_TYPE_VGA_PLANES = 4 21 FB_TYPE_FOURCC = 5 22 23 FB_VISUAL_MONO01 = 0 24 FB_VISUAL_MONO10 = 1 25 FB_VISUAL_TRUECOLOR = 2 26 FB_VISUAL_PSEUDOCOLOR = 3 27 FB_VISUAL_DIRECTCOLOR = 4 28 FB_VISUAL_STATIC_PSEUDOCOLOR = 5 29 FB_VISUAL_FOURCC = 6 30 31 FB_BLANK_UNBLANK = 0 32 FB_BLANK_POWERDOWN = 4 33 34 35 class Bitfield: # pylint: disable=too-few-public-methods 36 def __init__(self, offset, length, msb_right): 37 self.offset = offset 38 self.length = length 39 self.msb_right = msb_right 40 41 42 # Kind of like a pygame Surface object, or not! 43 # http://www.pygame.org/docs/ref/surface.html 44 class Framebuffer: # pylint: disable=too-many-instance-attributes 45 def __init__(self, dev): 46 self.dev = dev 47 self.fbfd = os.open(dev, os.O_RDWR) 48 vinfo = struct.unpack( 49 "8I12I16I4I", 50 fcntl.ioctl(self.fbfd, FBIOGET_VSCREENINFO, " " * ((8 + 12 + 16 + 4) * 4)), 51 ) 52 finfo = struct.unpack( 53 "16cL4I3HI", fcntl.ioctl(self.fbfd, FBIOGET_FSCREENINFO, " " * 48) 54 ) 55 56 bytes_per_pixel = (vinfo[6] + 7) // 8 57 screensize = vinfo[0] * vinfo[1] * bytes_per_pixel 58 59 fbp = mmap.mmap( 60 self.fbfd, screensize, flags=mmap.MAP_SHARED, prot=mmap.PROT_READ 61 ) 62 63 self.fbp = fbp 64 self.xres = vinfo[0] 65 self.yres = vinfo[1] 66 self.xoffset = vinfo[4] 67 self.yoffset = vinfo[5] 68 self.bits_per_pixel = vinfo[6] 69 self.bytes_per_pixel = bytes_per_pixel 70 self.grayscale = vinfo[7] 71 self.red = Bitfield(vinfo[8], vinfo[9], vinfo[10]) 72 self.green = Bitfield(vinfo[11], vinfo[12], vinfo[13]) 73 self.blue = Bitfield(vinfo[14], vinfo[15], vinfo[16]) 74 self.transp = Bitfield(vinfo[17], vinfo[18], vinfo[19]) 75 self.nonstd = vinfo[20] 76 self.name = b"".join([x for x in finfo[0:15] if x != b"\x00"]) 77 self.type = finfo[18] 78 self.visual = finfo[20] 79 self.line_length = finfo[24] 80 self.screensize = screensize 81 82 def close(self): 83 self.fbp.close() 84 os.close(self.fbfd) 85 86 def blank(self, blank): 87 # Blanking is not supported by all drivers 88 try: 89 if blank: 90 fcntl.ioctl(self.fbfd, FBIOBLANK, FB_BLANK_POWERDOWN) 91 else: 92 fcntl.ioctl(self.fbfd, FBIOBLANK, FB_BLANK_UNBLANK) 93 except IOError: 94 pass 95 96 def __str__(self): 97 visual_list = [ 98 "MONO01", 99 "MONO10", 100 "TRUECOLOR", 101 "PSEUDOCOLOR", 102 "DIRECTCOLOR", 103 "STATIC PSEUDOCOLOR", 104 "FOURCC", 105 ] 106 type_list = [ 107 "PACKED_PIXELS", 108 "PLANES", 109 "INTERLEAVED_PLANES", 110 "TEXT", 111 "VGA_PLANES", 112 "FOURCC", 113 ] 114 visual_name = "unknown" 115 if self.visual < len(visual_list): 116 visual_name = visual_list[self.visual] 117 type_name = "unknown" 118 if self.type < len(type_list): 119 type_name = type_list[self.type] 120 121 return ( 122 'mode "%sx%s"\n' % (self.xres, self.yres) 123 + " nonstd %s\n" % self.nonstd 124 + " rgba %s/%s,%s/%s,%s/%s,%s/%s\n" 125 % ( 126 self.red.length, 127 self.red.offset, 128 self.green.length, 129 self.green.offset, 130 self.blue.length, 131 self.blue.offset, 132 self.transp.length, 133 self.transp.offset, 134 ) 135 + "endmode\n" 136 + "\n" 137 + "Frame buffer device information:\n" 138 + " Device : %s\n" % self.dev 139 + " Name : %s\n" % self.name 140 + " Size : (%d, %d)\n" % (self.xres, self.yres) 141 + " Length : %s\n" % self.screensize 142 + " BPP : %d\n" % self.bits_per_pixel 143 + " Type : %s\n" % type_name 144 + " Visual : %s\n" % visual_name 145 + " LineLength : %s\n" % self.line_length 146 ) 147 148 149 device = "/dev/fb0" 150 fb = Framebuffer(device) 151 print(fb) 152 153 # Configuration for CS and DC pins (these are FeatherWing defaults on M0/M4): 154 cs_pin = digitalio.DigitalInOut(board.CE0) 155 dc_pin = digitalio.DigitalInOut(board.D25) 156 reset_pin = None 157 158 # Config for display baudrate (default max is 24mhz): 159 BAUDRATE = 64000000 160 161 # Setup SPI bus using hardware SPI: 162 spi = board.SPI() 163 164 # Create the ST7789 display: 165 disp = st7789.ST7789( 166 spi, 167 cs=cs_pin, 168 dc=dc_pin, 169 rst=reset_pin, 170 baudrate=BAUDRATE, 171 width=135, 172 height=240, 173 x_offset=53, 174 y_offset=40, 175 ) 176 177 height = disp.width # we swap height/width to rotate it to landscape! 178 width = disp.height 179 image = Image.new("RGB", (width, height)) 180 rotation = 90 181 182 # Get drawing object to draw on image. 183 draw = ImageDraw.Draw(image) 184 185 # Draw a black filled box to clear the image. 186 draw.rectangle((0, 0, width, height), outline=0, fill=(0, 0, 0)) 187 disp.image(image, rotation) 188 189 while True: 190 t = time.monotonic() 191 fb.fbp.seek(0) 192 b = fb.fbp.read(fb.screensize) 193 fbimage = Image.frombytes("RGBA", (fb.xres, fb.yres), b, "raw") 194 b, g, r, a = fbimage.split() 195 fbimage = Image.merge("RGB", (r, g, b)) 196 fbimage = fbimage.resize((width, height)) 197 198 disp.image(fbimage, rotation) 199 print(1.0 / (time.monotonic() - t)) 200 fb.close()