Compare commits

..

1 commit

Author SHA1 Message Date
182529a41b WIP move matrix creation into click 2025-03-18 11:23:26 -05:00
14 changed files with 78 additions and 99 deletions

View file

@ -19,7 +19,7 @@ jobs:
- name: Set up repository
uses: actions/checkout@v4
with:
submodules: false
submodules: true
show-progress: false
fetch-depth: 1
persist-credentials: false

2
.gitignore vendored
View file

@ -1,3 +1,5 @@
/*.pio.h
/build
/dist
/docs/api
*.egg-info

View file

@ -0,0 +1 @@
../build/lib.linux-x86_64-cpython-311/adafruit_blinka_raspberry_pi5_piomatter/

View file

@ -17,7 +17,6 @@ import numpy as np
import adafruit_blinka_raspberry_pi5_piomatter as piomatter
import adafruit_blinka_raspberry_pi5_piomatter.click as piomatter_click
from adafruit_blinka_raspberry_pi5_piomatter.pixelmappers import simple_multilane_mapper
with open("/sys/class/graphics/fb0/virtual_size") as f:
screenx, screeny = [int(word) for word in f.read().split(",")]
@ -39,15 +38,10 @@ linux_framebuffer = np.memmap('/dev/fb0',mode='r', shape=(screeny, stride // byt
@click.option("--x-offset", "xoffset", type=int, help="The x offset of top left corner of the region to mirror", default=0)
@click.option("--y-offset", "yoffset", type=int, help="The y offset of top left corner of the region to mirror", default=0)
@piomatter_click.standard_options
def main(xoffset, yoffset, width, height, serpentine, rotation, pinout, n_planes, n_temporal_planes, n_addr_lines, n_lanes):
if n_lanes != 2:
pixelmap = simple_multilane_mapper(width, height, n_addr_lines, n_lanes)
geometry = piomatter.Geometry(width=width, height=height, n_planes=n_planes, n_addr_lines=n_addr_lines, n_temporal_planes=n_temporal_planes, n_lanes=n_lanes, map=pixelmap)
else:
geometry = piomatter.Geometry(width=width, height=height, n_planes=n_planes, n_addr_lines=n_addr_lines, n_temporal_planes=n_temporal_planes, rotation=rotation, serpentine=serpentine)
framebuffer = np.zeros(shape=(geometry.height, geometry.width), dtype=dtype)
matrix = piomatter.PioMatter(colorspace=piomatter.Colorspace.RGB565, pinout=pinout, framebuffer=framebuffer, geometry=geometry)
@piomatter_click.make_matrix(colorspace=piomatter.Colorspace.RGB565)
def main(xoffset, yoffset, matrix, framebuffer):
width = matrix.width
height = matrix.height
while True:
framebuffer[:,:] = linux_framebuffer[yoffset:yoffset+height, xoffset:xoffset+width]
matrix.show()

View file

@ -72,7 +72,7 @@ def main(xoffset, yoffset, scale, width, height, serpentine, rotation, pinout, n
pixelmap = simple_multilane_mapper(width, height, n_addr_lines, n_lanes)
geometry = piomatter.Geometry(width=width, height=height, n_planes=n_planes, n_addr_lines=n_addr_lines, n_temporal_planes=n_temporal_planes, n_lanes=n_lanes, map=pixelmap)
else:
geometry = piomatter.Geometry(width=width, height=height, n_planes=n_planes, n_temporal_planes=n_temporal_planes, n_addr_lines=n_addr_lines, rotation=rotation, serpentine=serpentine)
geometry = piomatter.Geometry(width=width, height=height, n_planes=n_planes, n_temporal_planes=n_temporal_planes, n_addr_lines=n_addr_lines, rotation=rotation)
matrix_framebuffer = np.zeros(shape=(geometry.height, geometry.width, 3), dtype=np.uint8)
matrix = piomatter.PioMatter(colorspace=piomatter.Colorspace.RGB888Packed, pinout=pinout, framebuffer=matrix_framebuffer, geometry=geometry)

View file

@ -30,7 +30,7 @@ pixelmap = simple_multilane_mapper(width, height, n_addr_lines, n_lanes)
geometry = piomatter.Geometry(width=width, height=height, n_addr_lines=n_addr_lines, n_planes=10, n_temporal_planes=4, map=pixelmap, n_lanes=n_lanes)
framebuffer = np.asarray(canvas) + 0 # Make a mutable copy
matrix = piomatter.PioMatter(colorspace=piomatter.Colorspace.RGB888Packed,
pinout=piomatter.Pinout.Active3BGR,
pinout=piomatter.Pinout.Active3,
framebuffer=framebuffer,
geometry=geometry)
@ -66,7 +66,7 @@ def darken_color(hex_color, darkness_factor):
return darkened_hex_color
step_count = 8
step_count = 4
darkness_factor = 0.5
clearing = False

View file

@ -1,46 +0,0 @@
#!/usr/bin/python3
# SPDX-FileCopyrightText: 2025 Tim Cocks for Adafruit Industries
#
# SPDX-License-Identifier: MIT
"""
Display a simple test pattern of 3 shapes on three 64x64 matrix panels
using Active3 compatible connections.
Run like this:
$ python triple_matrix_active3_simpletest.py
"""
import numpy as np
from PIL import Image, ImageDraw
import adafruit_blinka_raspberry_pi5_piomatter as piomatter
from adafruit_blinka_raspberry_pi5_piomatter.pixelmappers import simple_multilane_mapper
width = 64
n_lanes = 6
n_addr_lines = 5
height = n_lanes << n_addr_lines
pen_radius = 1
canvas = Image.new('RGB', (width, height), (0, 0, 0))
draw = ImageDraw.Draw(canvas)
pixelmap = simple_multilane_mapper(width, height, n_addr_lines, n_lanes)
geometry = piomatter.Geometry(width=width, height=height, n_addr_lines=n_addr_lines, n_planes=10, n_temporal_planes=4, map=pixelmap, n_lanes=n_lanes)
framebuffer = np.asarray(canvas) + 0 # Make a mutable copy
matrix = piomatter.PioMatter(colorspace=piomatter.Colorspace.RGB888Packed,
pinout=piomatter.Pinout.Active3,
framebuffer=framebuffer,
geometry=geometry)
draw.rectangle((8, 8, width-8, width-8), fill=0x008800)
draw.circle((32, 64+32), 22, fill=0x880000)
draw.polygon([(32, 136), (54, 180), (10, 180)], fill=0x000088)
framebuffer[:] = np.asarray(canvas)
matrix.show()
input("Press enter to exit")

View file

@ -30,7 +30,6 @@ from pyvirtualdisplay.smartdisplay import SmartDisplay
import adafruit_blinka_raspberry_pi5_piomatter as piomatter
import adafruit_blinka_raspberry_pi5_piomatter.click as piomatter_click
from adafruit_blinka_raspberry_pi5_piomatter.pixelmappers import simple_multilane_mapper
@click.command
@ -42,24 +41,18 @@ from adafruit_blinka_raspberry_pi5_piomatter.pixelmappers import simple_multilan
default=1.0, type=click.FloatRange(min=0.1, max=1.0))
@click.option("--use-xauth/--no-use-xauth", help="If a Xauthority file should be created", default=False)
@piomatter_click.standard_options
@piomatter_click.make_matrix(colorspace=piomatter.Colorspace.RGB888Packed)
@click.argument("command", nargs=-1)
def main(scale, backend, use_xauth, extra_args, rfbport, brightness, width, height, serpentine, rotation, pinout,
n_planes, n_temporal_planes, n_addr_lines, n_lanes, command):
def main(matrix, framebuffer, scale, backend, use_xauth, extra_args, rfbport, brightness, command):
kwargs = {}
if backend == "xvnc":
kwargs['rfbport'] = rfbport
if extra_args:
kwargs['extra_args'] = shlex.split(extra_args)
print("xauth", use_xauth)
if n_lanes != 2:
pixelmap = simple_multilane_mapper(width, height, n_addr_lines, n_lanes)
geometry = piomatter.Geometry(width=width, height=height, n_planes=n_planes, n_addr_lines=n_addr_lines,
n_temporal_planes=n_temporal_planes, n_lanes=n_lanes, map=pixelmap)
else:
geometry = piomatter.Geometry(width=width, height=height, n_planes=n_planes, n_addr_lines=n_addr_lines,
n_temporal_planes=n_temporal_planes, rotation=rotation, serpentine=serpentine)
framebuffer = np.zeros(shape=(geometry.height, geometry.width, 3), dtype=np.uint8)
matrix = piomatter.PioMatter(colorspace=piomatter.Colorspace.RGB888Packed, pinout=pinout, framebuffer=framebuffer, geometry=geometry)
width = matrix.width
height = matrix.height
with SmartDisplay(backend=backend, use_xauth=use_xauth, size=(round(width*scale),round(height*scale)), manage_global_env=False, **kwargs) as disp, Popen(command, env=disp.env()) as proc:
while proc.poll() is None:

View file

@ -52,7 +52,7 @@ def main(width, height, serpentine, rotation, pinout, n_planes,
n_temporal_planes=n_temporal_planes, n_lanes=n_lanes, map=pixelmap)
else:
geometry = piomatter.Geometry(width=width, height=height, n_planes=n_planes, n_addr_lines=n_addr_lines,
n_temporal_planes=n_temporal_planes, rotation=rotation, serpentine=serpentine)
n_temporal_planes=n_temporal_planes, rotation=rotation)
framebuffer = np.zeros(shape=(geometry.height, geometry.width, 3), dtype=np.uint8)
matrix = piomatter.PioMatter(colorspace=piomatter.Colorspace.RGB888Packed, pinout=pinout, framebuffer=framebuffer,

View file

@ -1,8 +1,9 @@
# Available at setup time due to pyproject.toml
from pybind11.setup_helpers import Pybind11Extension, build_ext
from setuptools import setup
from setuptools_scm import get_version
from pybind11.setup_helpers import Pybind11Extension, build_ext
__version__ = get_version()
# The main interface is through Pybind11Extension.

View file

@ -6,9 +6,39 @@ from collections.abc import Callable
from typing import Any
import click
import numpy as np
import adafruit_blinka_raspberry_pi5_piomatter as piomatter
from .pixelmappers import simple_multilane_mapper
def make_matrix(*, colorspace):
print("make_matrix", colorspace)
def do_make_matrix(f):
print("do_make_matrix", f)
def wrapper(width, height, serpentine, rotation, pinout, n_planes, n_temporal_planes, n_addr_lines, n_lanes, **click_args):
if colorspace == piomatter.Colorspace.RGB565:
framebuffer = np.zeros((height, width), dtype=np.uint16)
elif colorspace == piomatter.Colorspace.RGB888:
framebuffer = np.zeros((height, width), dtype=np.uint32)
elif colorspace == piomatter.Colorspace.RGB888Packed:
framebuffer = np.zeros((height, width, 3), dtype=np.uint8)
else:
raise ValueError(f"Unsupported colorspace {colorspace!r}")
if n_lanes != 2:
if serpentine:
raise ValueError("Serpentine is only avaialble with 2 lanes")
pixelmap = simple_multilane_mapper(width, height, n_addr_lines, n_lanes)
geometry = piomatter.Geometry(width=width, height=height, n_planes=n_planes, n_addr_lines=n_addr_lines, n_temporal_planes=n_temporal_planes, n_lanes=n_lanes, map=pixelmap)
else:
geometry = piomatter.Geometry(width=width, height=height, n_planes=n_planes, n_addr_lines=n_addr_lines, n_temporal_planes=n_temporal_planes, rotation=rotation)
matrix = piomatter.PioMatter(colorspace=colorspace, pinout=pinout, framebuffer=framebuffer, geometry=geometry)
return f(matrix=matrix, framebuffer=framebuffer, **click_args)
return wrapper
return do_make_matrix
class _PybindEnumChoice(click.Choice):
def __init__(self, enum, case_sensitive=False):

View file

@ -41,25 +41,6 @@ struct adafruit_matrix_bonnet_pinout_bgr {
};
struct active3_pinout {
static constexpr pin_t PIN_RGB[] = {11, 27, 7, 8, 9, 10, 12, 5, 6,
19, 13, 20, 14, 2, 3, 26, 16, 21};
static constexpr pin_t PIN_ADDR[] = {22, 23, 24, 25, 15};
static constexpr pin_t PIN_OE = 18; // /OE: output enable when LOW
static constexpr pin_t PIN_CLK = 17; // SRCLK: clocks on RISING edge
static constexpr pin_t PIN_LAT = 4; // RCLK: latches on RISING edge
static constexpr uint32_t clk_bit = 1u << PIN_CLK;
static constexpr uint32_t lat_bit = 1u << PIN_LAT;
static constexpr uint32_t oe_bit = 1u << PIN_OE;
static constexpr uint32_t oe_active = 0;
static constexpr uint32_t oe_inactive = oe_bit;
static constexpr uint32_t post_oe_delay = 0;
static constexpr uint32_t post_latch_delay = 0;
static constexpr uint32_t post_addr_delay = 5;
};
struct active3_pinout_bgr {
static constexpr pin_t PIN_RGB[] = {7, 27, 11, 10, 9, 8, 6, 5, 12,
20, 13, 19, 3, 2, 14, 21, 16, 26};
static constexpr pin_t PIN_ADDR[] = {22, 23, 24, 25, 15};
@ -78,4 +59,23 @@ struct active3_pinout_bgr {
static constexpr uint32_t post_addr_delay = 5;
};
struct active3_pinout_bgr {
static constexpr pin_t PIN_RGB[] = {11, 27, 7, 8, 9, 10, 12, 5, 6,
19, 13, 20, 14, 2, 3, 26, 16, 21};
static constexpr pin_t PIN_ADDR[] = {22, 23, 24, 25, 15};
static constexpr pin_t PIN_OE = 18; // /OE: output enable when LOW
static constexpr pin_t PIN_CLK = 17; // SRCLK: clocks on RISING edge
static constexpr pin_t PIN_LAT = 4; // RCLK: latches on RISING edge
static constexpr uint32_t clk_bit = 1u << PIN_CLK;
static constexpr uint32_t lat_bit = 1u << PIN_LAT;
static constexpr uint32_t oe_bit = 1u << PIN_OE;
static constexpr uint32_t oe_active = 0;
static constexpr uint32_t oe_inactive = oe_bit;
static constexpr uint32_t post_oe_delay = 0;
static constexpr uint32_t post_latch_delay = 0;
static constexpr uint32_t post_addr_delay = 5;
};
} // namespace piomatter

View file

@ -57,7 +57,7 @@ static uint64_t monotonicns64() {
constexpr size_t MAX_XFER = 65532;
struct piomatter_base {
piomatter_base() {}
piomatter_base(const matrix_geometry &geometry) : geometry{geometry} {}
piomatter_base(const piomatter_base &) = delete;
piomatter_base &operator=(const piomatter_base &) = delete;
@ -65,17 +65,18 @@ struct piomatter_base {
virtual int show() = 0;
double fps;
matrix_geometry geometry;
};
template <class pinout = adafruit_matrix_bonnet_pinout,
class colorspace = colorspace_rgb888>
struct piomatter : piomatter_base {
struct piomatter : public piomatter_base {
using buffer_type = std::vector<uint32_t>;
using bufseq_type = std::vector<buffer_type>;
piomatter(std::span<typename colorspace::data_type const> framebuffer,
const matrix_geometry &geometry)
: framebuffer(framebuffer), geometry{geometry}, converter{},
blitter_thread{} {
: piomatter_base{geometry},
framebuffer(framebuffer), converter{}, blitter_thread{} {
if (geometry.n_addr_lines > std::size(pinout::PIN_ADDR)) {
throw std::runtime_error("too many address lines requested");
}
@ -240,7 +241,6 @@ struct piomatter : piomatter_base {
std::span<typename colorspace::data_type const> framebuffer;
bufseq_type buffers[3];
buffer_manager manager{};
matrix_geometry geometry;
colorspace converter;
std::thread blitter_thread;
std::atomic<int> pending_error_errno;

View file

@ -27,6 +27,8 @@ struct PyPiomatter {
}
}
double fps() const { return matter->fps; }
int width() const { return matter->geometry.width; }
int height() const { return matter->geometry.height; }
};
template <typename pinout, typename colorspace>
@ -288,5 +290,7 @@ data is triple-buffered to prevent tearing.
)pbdoc")
.def_property_readonly("fps", &PyPiomatter::fps, R"pbdoc(
The approximate number of matrix refreshes per second.
)pbdoc");
)pbdoc")
.def_property_readonly("width", &PyPiomatter::width)
.def_property_readonly("height", &PyPiomatter::height);
}