Compare commits
66 commits
pyvirtuald
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
abd7f18fc5 | ||
|
|
b057531ef1 | ||
|
|
d5a979df18 | ||
|
|
5d46945596 | ||
|
|
263f530375 | ||
|
|
e9a07d4942 | ||
|
|
792955cf1a | ||
|
|
b35b3d35bb | ||
|
|
0225964f24 | ||
| 89dd515ae5 | |||
| fc7295eb74 | |||
| b19b6c7cb0 | |||
|
|
fe7c44be88 | ||
|
|
bf3409d92b | ||
|
|
f80ae1e772 | ||
| a2827b6e28 | |||
| db9a925103 | |||
| 8b9f7d7006 | |||
| 450e3b61d3 | |||
| a565d17654 | |||
| 57c66624ce | |||
| 8e69092434 | |||
|
|
9465e133b8 | ||
|
|
270c8b854c | ||
|
|
9a4ed78ca6 | ||
| 0a9e578944 | |||
| 499d5c56a7 | |||
| ac6b65f23b | |||
|
|
0e717476c4 | ||
|
|
5961685d45 | ||
|
|
8d3355fca3 | ||
| 6d3e236828 | |||
| b26f8dc48d | |||
| 34d8cd1e4d | |||
| 3fb93d310b | |||
| f7a247eeae | |||
| 908766eeb8 | |||
| 372a158bd7 | |||
| 8ae0fea913 | |||
| 7c26836a68 | |||
|
|
1d9d652cf3 | ||
| 9bd7813dc3 | |||
| 2442bee476 | |||
| e2c5bc3467 | |||
| 4f548a318c | |||
| a5f505241d | |||
| 7c7569236b | |||
| 68c24fcc00 | |||
| 39bc663b26 | |||
| 25c324dd7d | |||
| 4fba6a6ee3 | |||
|
|
2b7a9c6d61 | ||
| 65b8b6fbf6 | |||
| 296047a806 | |||
| 274adf1805 | |||
| cfec6060d8 | |||
| d008684e5d | |||
| d26bba84ba | |||
| 4ffb57c3d5 | |||
| 47226cf246 | |||
| 97d5a4454d | |||
| cab2d4293d | |||
|
|
f627e654fb | ||
|
|
86070bb49a | ||
|
|
8d967c26e1 | ||
|
|
749734085b |
46 changed files with 3106 additions and 2446 deletions
2
.github/workflows/wheels.yml
vendored
2
.github/workflows/wheels.yml
vendored
|
|
@ -50,7 +50,7 @@ jobs:
|
|||
submodules: true
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: pypa/cibuildwheel@v2.22.0
|
||||
- uses: pypa/cibuildwheel@v2.23.0
|
||||
env:
|
||||
CIBW_ARCHS_LINUX: ${{ matrix.arch_linux }}
|
||||
CIBW_BUILD: "cp311-manylinux* cp312-manylinux* cp313-manylinux*"
|
||||
|
|
|
|||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,4 +1,3 @@
|
|||
/*.pio.h
|
||||
protodemo
|
||||
/build
|
||||
*.egg-info
|
||||
|
|
|
|||
|
|
@ -12,6 +12,9 @@
|
|||
#
|
||||
# See https://github.com/pre-commit/pre-commit
|
||||
|
||||
# Don't re-format vendored files
|
||||
exclude: "^src/piolib/.*$"
|
||||
|
||||
ci:
|
||||
autoupdate_commit_msg: "chore: update pre-commit hooks"
|
||||
autofix_commit_msg: "style: pre-commit fixes"
|
||||
|
|
|
|||
8
docs/adafruit_blinka_raspberry_pi5_piomatter.rst
Normal file
8
docs/adafruit_blinka_raspberry_pi5_piomatter.rst
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
HUB75 matrix driver for Raspberry Pi 5 using PIO
|
||||
------------------------------------------------
|
||||
|
||||
.. autosummary::
|
||||
:toctree: _generate
|
||||
:recursive:
|
||||
|
||||
adafruit_blinka_raspberry_pi5_piomatter
|
||||
|
|
@ -1 +0,0 @@
|
|||
.. automodule:: adafruit_blinka_raspberry_pi5_piomatter
|
||||
20
docs/conf.py
20
docs/conf.py
|
|
@ -26,13 +26,27 @@
|
|||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = [
|
||||
"sphinx.ext.autodoc",
|
||||
"autoapi.extension",
|
||||
"sphinx.ext.intersphinx",
|
||||
"sphinx.ext.autosummary",
|
||||
"sphinx.ext.napoleon",
|
||||
]
|
||||
|
||||
autosummary_generate = True
|
||||
autoapi_keep_files = True
|
||||
autoapi_dirs = ["../src/adafruit_blinka_raspberry_pi5_piomatter"]
|
||||
autoapi_add_toctree_entry = True
|
||||
autoapi_options = [
|
||||
"members",
|
||||
"undoc-members",
|
||||
"show-inheritance",
|
||||
"special-members",
|
||||
"show-module-summary",
|
||||
]
|
||||
|
||||
autoapi_python_class_content = "both"
|
||||
autoapi_python_use_implicit_namespaces = True
|
||||
autoapi_template_dir = "autoapi/templates"
|
||||
autoapi_root = "api"
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ["_templates"]
|
||||
|
|
@ -50,7 +64,7 @@ master_doc = "index"
|
|||
|
||||
# General information about the project.
|
||||
project = "adafruit-blinka-pi5-piomatter"
|
||||
copyright = "2023 Jeff Epler"
|
||||
copyright = "2025 Jeff Epler"
|
||||
author = "Jeff Epler"
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
|
|
|
|||
|
|
@ -3,5 +3,6 @@
|
|||
# SPDX-License-Identifier: Unlicense
|
||||
|
||||
sphinx
|
||||
sphinx-autoapi
|
||||
sphinx-rtd-theme
|
||||
sphinxcontrib-jquery
|
||||
|
|
|
|||
|
|
@ -12,10 +12,12 @@ For help with commandline arguments, run `python fbmirror.py --help`
|
|||
"""
|
||||
|
||||
|
||||
import adafruit_blinka_raspberry_pi5_piomatter as piomatter
|
||||
import click
|
||||
import numpy as np
|
||||
import piomatter_click
|
||||
|
||||
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(",")]
|
||||
|
|
@ -37,8 +39,12 @@ 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_addr_lines):
|
||||
geometry = piomatter.Geometry(width=width, height=height, n_planes=n_planes, n_addr_lines=n_addr_lines, rotation=rotation)
|
||||
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)
|
||||
|
||||
|
|
|
|||
|
|
@ -37,11 +37,13 @@ or if `/boot/firmware/cmdline.txt` specifies a resolution such as
|
|||
`... video=HDMI-A-1:640x480M@60D`.
|
||||
"""
|
||||
|
||||
import adafruit_blinka_raspberry_pi5_piomatter as piomatter
|
||||
import click
|
||||
import numpy as np
|
||||
import PIL.Image as Image
|
||||
import piomatter_click
|
||||
|
||||
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(",")]
|
||||
|
|
@ -65,8 +67,12 @@ linux_framebuffer = np.memmap('/dev/fb0',mode='r', shape=(screeny, stride // byt
|
|||
@click.option("--y-offset", "yoffset", type=int, help="The y offset of top left corner of the region to mirror", default=0)
|
||||
@click.option("--scale", "scale", type=int, help="The scale factor to reduce the display down by.", default=3)
|
||||
@piomatter_click.standard_options
|
||||
def main(xoffset, yoffset, scale, width, height, serpentine, rotation, pinout, n_planes, n_addr_lines):
|
||||
geometry = piomatter.Geometry(width=width, height=height, n_planes=n_planes, n_addr_lines=n_addr_lines, rotation=rotation)
|
||||
def main(xoffset, yoffset, scale, 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_temporal_planes=n_temporal_planes, n_addr_lines=n_addr_lines, rotation=rotation, serpentine=serpentine)
|
||||
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)
|
||||
|
||||
|
|
|
|||
|
|
@ -11,10 +11,11 @@ The animated gif is played repeatedly until interrupted with ctrl-c.
|
|||
|
||||
import time
|
||||
|
||||
import adafruit_blinka_raspberry_pi5_piomatter as piomatter
|
||||
import numpy as np
|
||||
import PIL.Image as Image
|
||||
|
||||
import adafruit_blinka_raspberry_pi5_piomatter as piomatter
|
||||
|
||||
width = 64
|
||||
height = 32
|
||||
|
||||
|
|
|
|||
|
|
@ -13,10 +13,11 @@ import glob
|
|||
import sys
|
||||
import time
|
||||
|
||||
import adafruit_blinka_raspberry_pi5_piomatter as piomatter
|
||||
import numpy as np
|
||||
import PIL.Image as Image
|
||||
|
||||
import adafruit_blinka_raspberry_pi5_piomatter as piomatter
|
||||
|
||||
images = sorted(glob.glob(sys.argv[1]))
|
||||
|
||||
geometry = piomatter.Geometry(width=64, height=32, n_addr_lines=4, rotation=piomatter.Orientation.Normal)
|
||||
|
|
|
|||
|
|
@ -14,11 +14,12 @@ $ python quote_scroller.py
|
|||
|
||||
"""
|
||||
|
||||
import adafruit_blinka_raspberry_pi5_piomatter as piomatter
|
||||
import numpy as np
|
||||
import requests
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
|
||||
import adafruit_blinka_raspberry_pi5_piomatter as piomatter
|
||||
|
||||
# 128px for 2x1 matrices. Change to 64 if you're using a single matrix.
|
||||
total_width = 128
|
||||
total_height = 32
|
||||
|
|
|
|||
|
|
@ -10,11 +10,12 @@ Run like this:
|
|||
$ python rainbow_spiral.py
|
||||
|
||||
"""
|
||||
import adafruit_blinka_raspberry_pi5_piomatter as piomatter
|
||||
import numpy as np
|
||||
import rainbowio
|
||||
from PIL import Image, ImageDraw
|
||||
|
||||
import adafruit_blinka_raspberry_pi5_piomatter as piomatter
|
||||
|
||||
width = 64
|
||||
height = 32
|
||||
pen_radius = 1
|
||||
|
|
|
|||
114
examples/rainbow_spiral_active3.py
Normal file
114
examples/rainbow_spiral_active3.py
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
#!/usr/bin/python3
|
||||
# SPDX-FileCopyrightText: 2025 Tim Cocks for Adafruit Industries
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
"""
|
||||
Display a spiral around the display drawn with a rainbow color.
|
||||
|
||||
Run like this:
|
||||
|
||||
$ python rainbow_spiral.py
|
||||
|
||||
"""
|
||||
import numpy as np
|
||||
import rainbowio
|
||||
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.Active3BGR,
|
||||
framebuffer=framebuffer,
|
||||
geometry=geometry)
|
||||
|
||||
color_index = 0
|
||||
|
||||
update_interval = 3
|
||||
update_counter = 0
|
||||
def update_matrix():
|
||||
global update_counter
|
||||
if (update_counter := update_counter + 1) >= update_interval:
|
||||
framebuffer[:] = np.asarray(canvas)
|
||||
matrix.show()
|
||||
update_counter = 0
|
||||
|
||||
def darken_color(hex_color, darkness_factor):
|
||||
# Convert hex color number to RGB
|
||||
r = (hex_color >> 16) & 0xFF
|
||||
g = (hex_color >> 8) & 0xFF
|
||||
b = hex_color & 0xFF
|
||||
|
||||
# Apply darkness factor
|
||||
r = int(r * (1 - darkness_factor))
|
||||
g = int(g * (1 - darkness_factor))
|
||||
b = int(b * (1 - darkness_factor))
|
||||
|
||||
# Ensure values are within the valid range
|
||||
r = max(0, min(255, r))
|
||||
g = max(0, min(255, g))
|
||||
b = max(0, min(255, b))
|
||||
|
||||
# Convert RGB back to hex number
|
||||
darkened_hex_color = (r << 16) + (g << 8) + b
|
||||
|
||||
return darkened_hex_color
|
||||
|
||||
step_count = 8
|
||||
darkness_factor = 0.5
|
||||
|
||||
clearing = False
|
||||
|
||||
try:
|
||||
# step_down_size = pen_radius * 2 + 2
|
||||
|
||||
while True:
|
||||
for step in range(step_count):
|
||||
step_down_size = step * (pen_radius* 2) + (2 * step)
|
||||
for x in range(pen_radius + step_down_size, width - pen_radius - step_down_size - 1):
|
||||
color_index = (color_index + 2) % 256
|
||||
color = darken_color(rainbowio.colorwheel(color_index), darkness_factor) if not clearing else 0x000000
|
||||
draw.circle((x, pen_radius + step_down_size), pen_radius, color)
|
||||
update_matrix()
|
||||
for y in range(pen_radius + step_down_size, height - pen_radius - step_down_size - 1):
|
||||
color_index = (color_index + 2) % 256
|
||||
color = darken_color(rainbowio.colorwheel(color_index), darkness_factor) if not clearing else 0x000000
|
||||
draw.circle((width - pen_radius - step_down_size -1, y), pen_radius, color)
|
||||
update_matrix()
|
||||
for x in range(width - pen_radius - step_down_size - 1, pen_radius + step_down_size, -1):
|
||||
color_index = (color_index + 2) % 256
|
||||
color = darken_color(rainbowio.colorwheel(color_index), darkness_factor) if not clearing else 0x000000
|
||||
draw.circle((x, height - pen_radius - step_down_size - 1), pen_radius, color)
|
||||
update_matrix()
|
||||
for y in range(height - pen_radius - step_down_size - 1, pen_radius + ((step+1) * (pen_radius* 2) + (2 * (step+1))) -1, -1):
|
||||
color_index = (color_index + 2) % 256
|
||||
color = darken_color(rainbowio.colorwheel(color_index), darkness_factor) if not clearing else 0x000000
|
||||
draw.circle((pen_radius + step_down_size, y), pen_radius, color)
|
||||
update_matrix()
|
||||
|
||||
if step != step_count-1:
|
||||
# connect to next iter
|
||||
for x in range(pen_radius + step_down_size, pen_radius + ((step+1) * (pen_radius* 2) + (2 * (step+1)))):
|
||||
color_index = (color_index + 2) % 256
|
||||
color = darken_color(rainbowio.colorwheel(color_index),
|
||||
darkness_factor) if not clearing else 0x000000
|
||||
draw.circle((x, pen_radius + ((step+1) * (pen_radius* 2) + (2 * (step+1)))), pen_radius, color)
|
||||
update_matrix()
|
||||
|
||||
print(matrix.fps)
|
||||
clearing = not clearing
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print("Exiting")
|
||||
|
|
@ -13,10 +13,11 @@ The image is displayed until the user hits enter to exit.
|
|||
|
||||
import pathlib
|
||||
|
||||
import adafruit_blinka_raspberry_pi5_piomatter as piomatter
|
||||
import numpy as np
|
||||
import PIL.Image as Image
|
||||
|
||||
import adafruit_blinka_raspberry_pi5_piomatter as piomatter
|
||||
|
||||
geometry = piomatter.Geometry(width=64, height=64, n_addr_lines=4, rotation=piomatter.Orientation.Normal)
|
||||
framebuffer = np.asarray(Image.open(pathlib.Path(__file__).parent / "blinka64x64.png"))
|
||||
matrix = piomatter.PioMatter(colorspace=piomatter.Colorspace.RGB888Packed,
|
||||
|
|
|
|||
|
|
@ -13,10 +13,11 @@ The image is displayed until the user hits enter to exit.
|
|||
|
||||
import pathlib
|
||||
|
||||
import adafruit_blinka_raspberry_pi5_piomatter as piomatter
|
||||
import numpy as np
|
||||
import PIL.Image as Image
|
||||
|
||||
import adafruit_blinka_raspberry_pi5_piomatter as piomatter
|
||||
|
||||
geometry = piomatter.Geometry(width=64, height=64, n_addr_lines=5, rotation=piomatter.Orientation.Normal, n_planes=8)
|
||||
framebuffer = np.asarray(Image.open(pathlib.Path(__file__).parent / "blinka64x64.png"))
|
||||
matrix = piomatter.PioMatter(colorspace=piomatter.Colorspace.RGB888Packed, pinout=piomatter.Pinout.AdafruitMatrixBonnetBGR, framebuffer=framebuffer, geometry=geometry)
|
||||
|
|
|
|||
|
|
@ -11,10 +11,11 @@ $ python simpletest.py
|
|||
|
||||
"""
|
||||
|
||||
import adafruit_blinka_raspberry_pi5_piomatter as piomatter
|
||||
import numpy as np
|
||||
from PIL import Image, ImageDraw
|
||||
|
||||
import adafruit_blinka_raspberry_pi5_piomatter as piomatter
|
||||
|
||||
width = 64
|
||||
height = 32
|
||||
|
||||
|
|
|
|||
46
examples/triple_matrix_active3_simpletest.py
Normal file
46
examples/triple_matrix_active3_simpletest.py
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
#!/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")
|
||||
|
|
@ -20,43 +20,56 @@ Here's an example for running an emulator using a rom stored in "/tmp/snesrom.sm
|
|||
$ python virtualdisplay.py --pinout AdafruitMatrixHatBGR --scale 2 --backend xvfb --width 128 --height 128 --serpentine --num-address-lines 5 --num-planes 4 -- mednafen -snes.xscalefs 1 -snes.yscalefs 1 -snes.xres 128 -video.fs 1 -video.driver softfb /tmp/snesrom.smc
|
||||
"""
|
||||
|
||||
# To run a nice emulator:
|
||||
|
||||
|
||||
import shlex
|
||||
from subprocess import Popen
|
||||
|
||||
import adafruit_blinka_raspberry_pi5_piomatter as piomatter
|
||||
import click
|
||||
import numpy as np
|
||||
import piomatter_click
|
||||
from PIL import ImageEnhance
|
||||
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
|
||||
@click.option("--scale", type=float, help="The scale factor, larger numbers mean more virtual pixels", default=1)
|
||||
@click.option("--backend", help="The pyvirtualdisplay backend to use", default="xvfb")
|
||||
@click.option("--extra-args", help="Extra arguments to pass to the backend server", default="")
|
||||
@click.option("--rfbport", help="The port number for the --backend xvnc", default=None, type=int)
|
||||
@click.option("--brightness", help="The brightness factor of the image output to the matrix",
|
||||
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
|
||||
@click.argument("command", nargs=-1)
|
||||
def main(scale, backend, use_xauth, extra_args, rfbport, width, height, serpentine, rotation, pinout, n_planes, n_addr_lines, command):
|
||||
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):
|
||||
kwargs = {}
|
||||
if backend == "xvnc":
|
||||
kwargs['rfbport'] = rfbport
|
||||
if extra_args:
|
||||
kwargs['extra_args'] = shlex.split(extra_args)
|
||||
print("xauth", use_xauth)
|
||||
geometry = piomatter.Geometry(width=width, height=height, n_planes=n_planes, n_addr_lines=n_addr_lines, rotation=rotation)
|
||||
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)
|
||||
|
||||
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:
|
||||
img = disp.grab(autocrop=False)
|
||||
|
||||
if img is None:
|
||||
continue
|
||||
if brightness != 1.0:
|
||||
darkener = ImageEnhance.Brightness(img)
|
||||
img = darkener.enhance(brightness)
|
||||
img = img.resize((width, height))
|
||||
framebuffer[:, :] = np.array(img)
|
||||
matrix.show()
|
||||
|
|
|
|||
83
examples/xdisplay_mirror.py
Normal file
83
examples/xdisplay_mirror.py
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
#!/usr/bin/python3
|
||||
"""
|
||||
Display a (possibly scaled) X display to a matrix
|
||||
|
||||
The display runs until this script exits.
|
||||
|
||||
The display doesn't get a keyboard or mouse, so you have to use a program that
|
||||
will get its input in some other way, such as from a gamepad.
|
||||
|
||||
For help with commandline arguments, run `python xdisplay_mirror.py --help`
|
||||
|
||||
This example command will mirror the entire display scaled onto a 2x2 grid of 64px panels, total matrix size 128x128.
|
||||
|
||||
$ python xdisplay_mirror.py --pinout AdafruitMatrixHatBGR --width 128 --height 128 --serpentine --num-address-lines 5 --num-planes 8
|
||||
|
||||
This example command will mirror a 128x128 pixel square from the top left of the display at real size on the same matrix panels
|
||||
|
||||
$ python xdisplay_mirror.py --pinout AdafruitMatrixHatBGR --width 128 --height 128 --serpentine --num-address-lines 5 --num-planes 8 --mirror-region 0,0,128,128
|
||||
"""
|
||||
|
||||
import click
|
||||
import numpy as np
|
||||
from PIL import Image, ImageEnhance, ImageGrab
|
||||
|
||||
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
|
||||
|
||||
RESAMPLE_MAP = {
|
||||
"nearest": Image.NEAREST,
|
||||
"bilinear": Image.BILINEAR,
|
||||
"lanczos": Image.LANCZOS,
|
||||
"bicubic": Image.BICUBIC
|
||||
}
|
||||
|
||||
|
||||
@click.command
|
||||
@click.option("--mirror-region", help="Region of X display to mirror. Comma seperated x,y,w,h. "
|
||||
"Default will mirror entire display.", default="")
|
||||
@click.option("--x-display", help="The X display to mirror. Default is :0", default=":0")
|
||||
@click.option("--brightness", help="The brightness factor of the image output to the matrix",
|
||||
default=1.0, type=click.FloatRange(min=0.1, max=1.0))
|
||||
@click.option("--resample-method", type=click.Choice(RESAMPLE_MAP), default="nearest",
|
||||
help="The resample method for PIL to use when resizing the screen image. Default is nearest")
|
||||
@piomatter_click.standard_options(n_lanes=2, n_temporal_planes=0)
|
||||
def main(width, height, serpentine, rotation, pinout, n_planes,
|
||||
n_temporal_planes, n_addr_lines, n_lanes, mirror_region, x_display, resample_method, brightness):
|
||||
|
||||
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)
|
||||
|
||||
if mirror_region:
|
||||
mirror_region = tuple(int(_) for _ in mirror_region.split(','))
|
||||
else:
|
||||
mirror_region = None
|
||||
size_measure = ImageGrab.grab(xdisplay=":0")
|
||||
print(f"Mirroring full display: {size_measure.width}, {size_measure.height}")
|
||||
|
||||
while True:
|
||||
img = ImageGrab.grab(xdisplay=x_display)
|
||||
if mirror_region is not None:
|
||||
img = img.crop((mirror_region[0], mirror_region[1], # left,top
|
||||
mirror_region[0] + mirror_region[2], # right
|
||||
mirror_region[1] + mirror_region[3])) # bottom
|
||||
if brightness != 1.0:
|
||||
darkener = ImageEnhance.Brightness(img)
|
||||
img = darkener.enhance(brightness)
|
||||
img = img.resize((width, height), RESAMPLE_MAP[resample_method])
|
||||
|
||||
framebuffer[:, :] = np.array(img)
|
||||
matrix.show()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
@ -9,14 +9,15 @@ build-backend = "setuptools.build_meta"
|
|||
[tool.setuptools_scm]
|
||||
|
||||
[tool.ruff]
|
||||
extend-select = [
|
||||
lint.extend-select = [
|
||||
"B", # flake8-bugbear
|
||||
"I", # isort
|
||||
"PGH", # pygrep-hooks
|
||||
"RUF", # Ruff-specific
|
||||
"UP", # pyupgrade
|
||||
]
|
||||
extend-ignore = [
|
||||
lint.extend-ignore = [
|
||||
"E501", # Line too long
|
||||
"RUF002", # Yes I meant to type 'multiplication sign'!
|
||||
]
|
||||
target-version = "py311"
|
||||
|
|
|
|||
|
|
@ -3,5 +3,6 @@
|
|||
# SPDX-License-Identifier: Unlicense
|
||||
Adafruit-Blinka
|
||||
adafruit-circuitpython-pioasm
|
||||
click
|
||||
numpy
|
||||
pillow
|
||||
|
|
|
|||
4
setup.py
4
setup.py
|
|
@ -11,7 +11,7 @@ __version__ = get_version()
|
|||
# say from a submodule.
|
||||
|
||||
ext_modules = [
|
||||
Pybind11Extension("adafruit_blinka_raspberry_pi5_piomatter",
|
||||
Pybind11Extension("adafruit_blinka_raspberry_pi5_piomatter._piomatter",
|
||||
["src/pymain.cpp", "src/piolib/piolib.c", "src/piolib/pio_rp1.c"],
|
||||
define_macros = [('VERSION_INFO', __version__)],
|
||||
include_dirs = ['./src/include', './src/piolib/include'],
|
||||
|
|
@ -33,6 +33,8 @@ setup(
|
|||
cmdclass={"build_ext": build_ext},
|
||||
zip_safe=False,
|
||||
python_requires=">=3.11",
|
||||
packages=['adafruit_blinka_raspberry_pi5_piomatter'],
|
||||
package_dir={'adafruit_blinka_raspberry_pi5_piomatter': 'src/adafruit_blinka_raspberry_pi5_piomatter'},
|
||||
extras_require={
|
||||
'docs': ["sphinx", "sphinx-rtd-theme", "sphinxcontrib-jquery"],
|
||||
},
|
||||
|
|
|
|||
20
src/CMakeLists.txt
Normal file
20
src/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
cmake_minimum_required(VERSION 3.12)
|
||||
|
||||
project(protodemo C CXX)
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
|
||||
add_compile_options(-Wall -g3 -Og)
|
||||
add_executable(protodemo
|
||||
protodemo.cpp
|
||||
piolib/piolib.c
|
||||
piolib/pio_rp1.c
|
||||
)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/protomatter.pio.h
|
||||
COMMAND python ${CMAKE_CURRENT_SOURCE_DIR}/assemble.py ${CMAKE_CURRENT_SOURCE_DIR}/protomatter.pio ${CMAKE_CURRENT_BINARY_DIR}/protomatter.pio.h
|
||||
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/assemble.py ${CMAKE_CURRENT_SOURCE_DIR}/protomatter.pio
|
||||
)
|
||||
|
||||
target_include_directories(protodemo PRIVATE include piolib/include)
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
protodemo: protodemo.c piolib/*.c include/piomatter/*.h include/piomatter/protomatter.pio.h Makefile
|
||||
g++ -std=c++20 -O3 -ggdb -x c++ -Iinclude -Ipiolib/include -o $@ $(filter %.c, $^) -Wno-narrowing
|
||||
|
||||
matrixmap.h:
|
||||
|
||||
include/piomatter/protomatter.pio.h: protomatter.pio assemble.py
|
||||
python assemble.py $< $@
|
||||
33
src/adafruit_blinka_raspberry_pi5_piomatter/__init__.py
Normal file
33
src/adafruit_blinka_raspberry_pi5_piomatter/__init__.py
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
"""
|
||||
HUB75 matrix driver for Raspberry Pi 5 using PIO
|
||||
------------------------------------------------
|
||||
|
||||
.. currentmodule:: adafruit_blinka_raspberry_pi5_piomatter
|
||||
|
||||
.. autosummary::
|
||||
:toctree: _generate
|
||||
:recursive:
|
||||
:class: Orientation Pinout Colorspace Geometry PioMatter
|
||||
|
||||
Orientation
|
||||
Pinout
|
||||
Colorspace
|
||||
Geometry
|
||||
PioMatter
|
||||
"""
|
||||
|
||||
from ._piomatter import (
|
||||
Colorspace,
|
||||
Geometry,
|
||||
Orientation,
|
||||
Pinout,
|
||||
PioMatter,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
'Colorspace',
|
||||
'Geometry',
|
||||
'Orientation',
|
||||
'Pinout',
|
||||
'PioMatter',
|
||||
]
|
||||
|
|
@ -5,11 +5,12 @@
|
|||
from collections.abc import Callable
|
||||
from typing import Any
|
||||
|
||||
import adafruit_blinka_raspberry_pi5_piomatter as piomatter
|
||||
import click
|
||||
|
||||
import adafruit_blinka_raspberry_pi5_piomatter as piomatter
|
||||
|
||||
class PybindEnumChoice(click.Choice):
|
||||
|
||||
class _PybindEnumChoice(click.Choice):
|
||||
def __init__(self, enum, case_sensitive=False):
|
||||
self.enum = enum
|
||||
choices = [k for k, v in enum.__dict__.items() if isinstance(v, enum)]
|
||||
|
|
@ -25,6 +26,11 @@ class PybindEnumChoice(click.Choice):
|
|||
r = getattr(self.enum, value)
|
||||
return r
|
||||
|
||||
def _validate_temporal_planes(ctx, param, value):
|
||||
if value not in (0, 1, 2, 3, 4, 5):
|
||||
raise click.BadParameter("must be from 0 to 5 (0 and 1 both disable temporal planes)")
|
||||
return value
|
||||
|
||||
def standard_options(
|
||||
f: click.decorators.FC | None = None,
|
||||
*,
|
||||
|
|
@ -34,7 +40,9 @@ def standard_options(
|
|||
rotation=piomatter.Orientation.Normal,
|
||||
pinout=piomatter.Pinout.AdafruitMatrixBonnet,
|
||||
n_planes=10,
|
||||
n_temporal_planes=0,
|
||||
n_addr_lines=4,
|
||||
n_lanes=2,
|
||||
) -> Callable[[], None]:
|
||||
"""Add standard commandline flags, with the defaults given
|
||||
|
||||
|
|
@ -61,7 +69,7 @@ def standard_options(
|
|||
f = click.option(
|
||||
"--pinout",
|
||||
default=pinout,
|
||||
type=PybindEnumChoice(piomatter.Pinout),
|
||||
type=_PybindEnumChoice(piomatter.Pinout),
|
||||
help="The details of the electrical connection to the panels"
|
||||
)(f)
|
||||
if rotation is not None:
|
||||
|
|
@ -69,13 +77,17 @@ def standard_options(
|
|||
"--orientation",
|
||||
"rotation",
|
||||
default=rotation,
|
||||
type=PybindEnumChoice(piomatter.Orientation),
|
||||
type=_PybindEnumChoice(piomatter.Orientation),
|
||||
help="The overall orientation (rotation) of the panels"
|
||||
)(f)
|
||||
if n_planes is not None:
|
||||
f = click.option("--num-planes", "n_planes", default=n_planes, help="The number of bit planes (color depth. Lower values can improve refresh rate in frames per second")(f)
|
||||
f = click.option("--num-planes", "n_planes", default=n_planes, help="The number of bit planes (color depth). Lower values can improve refresh rate in frames per second")(f)
|
||||
if n_temporal_planes is not None:
|
||||
f = click.option("--num-temporal-planes", "n_temporal_planes", default=n_temporal_planes, callback=_validate_temporal_planes, help="The number of temporal bit-planes. May be 0, 2, or 4. Nonzero values improve frame rate but can cause some shimmer")(f)
|
||||
if n_addr_lines is not None:
|
||||
f = click.option("--num-address-lines", "n_addr_lines", default=n_addr_lines, help="The number of address lines used by the panels")(f)
|
||||
if n_lanes is not None:
|
||||
f = click.option("--num-lanes", "n_lanes", default=n_lanes, help="The number of lanes used by the panels. One 16-pin connector has two lanes (6 RGB pins)")(f)
|
||||
return f
|
||||
if f is None:
|
||||
return wrapper
|
||||
30
src/adafruit_blinka_raspberry_pi5_piomatter/pixelmappers.py
Normal file
30
src/adafruit_blinka_raspberry_pi5_piomatter/pixelmappers.py
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
"""Functions to define the layout of complex setups, particularly multi-connector matrices"""
|
||||
|
||||
def simple_multilane_mapper(width, height, n_addr_lines, n_lanes):
|
||||
"""A simple mapper for 4+ pixel lanes
|
||||
|
||||
A framebuffer (width × height) is mapped onto a matrix where the lanes are stacked
|
||||
top-to-bottom. Panels within a lane may be cascaded left-to-right.
|
||||
|
||||
Rotation is not supported, and neither are more complicated arrangements of panels
|
||||
within a single chain (no support for serpentine or stacked panels within a segment)
|
||||
|
||||
.. code-block::
|
||||
|
||||
0 -> [panel] -> [panel]
|
||||
1 -> [panel] -> [panel]
|
||||
2 -> [panel] -> [panel]
|
||||
"""
|
||||
|
||||
calc_height = n_lanes << n_addr_lines
|
||||
if height != calc_height:
|
||||
raise RuntimeError(f"Calculated height {calc_height} does not match requested height {height}")
|
||||
n_addr = 1 << n_addr_lines
|
||||
|
||||
m = []
|
||||
for addr in range(n_addr):
|
||||
for x in range(width):
|
||||
for lane in range(n_lanes):
|
||||
y = addr + lane * n_addr
|
||||
m.append(x + width * y)
|
||||
return m
|
||||
|
|
@ -48,7 +48,8 @@ matrix_map make_matrixmap(size_t width, size_t height, size_t n_addr_lines,
|
|||
|
||||
size_t panel_height = 2 << n_addr_lines;
|
||||
if (height % panel_height != 0) {
|
||||
throw std::range_error("Height does not evenly divide panel height");
|
||||
throw std::range_error(
|
||||
"Overall height does not evenly divide calculated panel height");
|
||||
}
|
||||
|
||||
size_t half_panel_height = 1u << n_addr_lines;
|
||||
|
|
@ -80,23 +81,116 @@ matrix_map make_matrixmap(size_t width, size_t height, size_t n_addr_lines,
|
|||
return result;
|
||||
}
|
||||
|
||||
struct schedule_entry {
|
||||
uint32_t shift, active_time;
|
||||
};
|
||||
|
||||
using schedule = std::vector<schedule_entry>;
|
||||
using schedule_sequence = std::vector<schedule>;
|
||||
|
||||
schedule_sequence rescale_schedule(schedule_sequence ss, size_t pixels_across) {
|
||||
uint32_t max_active_time = 0;
|
||||
for (auto &s : ss) {
|
||||
for (auto &ent : s) {
|
||||
max_active_time = std::max(ent.active_time, max_active_time);
|
||||
}
|
||||
}
|
||||
if (max_active_time == 0 || max_active_time >= pixels_across) {
|
||||
return ss;
|
||||
}
|
||||
int scale = (pixels_across + max_active_time - 1) / max_active_time;
|
||||
for (auto &s : ss) {
|
||||
for (auto &ent : s) {
|
||||
ent.active_time *= scale;
|
||||
}
|
||||
}
|
||||
return ss;
|
||||
}
|
||||
|
||||
schedule_sequence make_simple_schedule(int n_planes, size_t pixels_across) {
|
||||
if (n_planes < 1 || n_planes > 10) {
|
||||
throw std::range_error("n_planes out of range");
|
||||
}
|
||||
schedule result;
|
||||
for (int i = 0; i < n_planes; i++) {
|
||||
result.emplace_back(9 - i, (1 << (n_planes - i - 1)));
|
||||
}
|
||||
return rescale_schedule({result}, pixels_across);
|
||||
}
|
||||
|
||||
// Make a temporal dither schedule. All the top `n_planes` are shown everytime,
|
||||
// but the lowest planes are done in a cycle of `n_temporal_planes`:
|
||||
// 2: {0, 1}; 4: {0, 1, 2, 3}
|
||||
schedule_sequence make_temporal_dither_schedule(int n_planes,
|
||||
size_t pixels_across,
|
||||
int n_temporal_planes) {
|
||||
if (n_planes < 1 || n_planes > 10) {
|
||||
throw std::range_error("n_planes out of range");
|
||||
}
|
||||
if (n_temporal_planes < 2) {
|
||||
// either 0 or 1 temporal planes are not really temporal at all
|
||||
return make_simple_schedule(n_planes, pixels_across);
|
||||
}
|
||||
if (n_temporal_planes >= n_planes) {
|
||||
throw std::range_error("n_temporal_planes can't exceed n_planes");
|
||||
}
|
||||
|
||||
int n_real_planes = n_planes - n_temporal_planes;
|
||||
|
||||
schedule_sequence result;
|
||||
|
||||
auto add_sched = [&result, n_real_planes,
|
||||
n_temporal_planes](int i, int plane, int count) {
|
||||
schedule sched;
|
||||
for (int j = 0; j < n_real_planes; j++) {
|
||||
int k = 1 << (n_temporal_planes + n_real_planes - j - 1);
|
||||
sched.emplace_back(9 - j, (k + i) / n_temporal_planes);
|
||||
}
|
||||
sched.emplace_back(9 - plane, count);
|
||||
result.emplace_back(sched);
|
||||
};
|
||||
|
||||
for (int i = 0; i < n_temporal_planes; i++) {
|
||||
add_sched(i, n_real_planes + i, 1 << (n_temporal_planes - i - 1));
|
||||
}
|
||||
|
||||
return rescale_schedule(result, pixels_across);
|
||||
}
|
||||
|
||||
struct matrix_geometry {
|
||||
template <typename Cb>
|
||||
matrix_geometry(size_t pixels_across, size_t n_addr_lines, int n_planes,
|
||||
size_t width, size_t height, bool serpentine, const Cb &cb)
|
||||
int n_temporal_planes, size_t width, size_t height,
|
||||
bool serpentine, const Cb &cb)
|
||||
: matrix_geometry(
|
||||
pixels_across, n_addr_lines, n_planes, n_temporal_planes, width,
|
||||
height,
|
||||
make_matrixmap(width, height, n_addr_lines, serpentine, cb), 2) {}
|
||||
|
||||
matrix_geometry(size_t pixels_across, size_t n_addr_lines, int n_planes,
|
||||
int n_temporal_planes, size_t width, size_t height,
|
||||
matrix_map map, size_t n_lanes)
|
||||
: matrix_geometry(pixels_across, n_addr_lines, width, height, map,
|
||||
n_lanes,
|
||||
make_temporal_dither_schedule(n_planes, pixels_across,
|
||||
n_temporal_planes)) {}
|
||||
|
||||
matrix_geometry(size_t pixels_across, size_t n_addr_lines, size_t width,
|
||||
size_t height, matrix_map map, size_t n_lanes,
|
||||
const schedule_sequence &schedules)
|
||||
: pixels_across(pixels_across), n_addr_lines(n_addr_lines),
|
||||
n_planes(n_planes), width(width),
|
||||
height(height), map{make_matrixmap(width, height, n_addr_lines,
|
||||
serpentine, cb)} {
|
||||
size_t pixels_down = 2u << n_addr_lines;
|
||||
n_lanes(n_lanes), width(width), height(height),
|
||||
map(map), schedules{schedules} {
|
||||
size_t pixels_down = n_lanes << n_addr_lines;
|
||||
if (map.size() != pixels_down * pixels_across) {
|
||||
throw std::range_error(
|
||||
"map size does not match calculated pixel count");
|
||||
}
|
||||
}
|
||||
size_t pixels_across, n_addr_lines;
|
||||
int n_planes;
|
||||
|
||||
size_t pixels_across, n_addr_lines, n_lanes;
|
||||
size_t width, height;
|
||||
matrix_map map;
|
||||
schedule_sequence schedules;
|
||||
};
|
||||
} // namespace piomatter
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ struct adafruit_matrix_bonnet_pinout {
|
|||
|
||||
static constexpr uint32_t post_oe_delay = 0;
|
||||
static constexpr uint32_t post_latch_delay = 0;
|
||||
static constexpr uint32_t post_addr_delay = 500;
|
||||
static constexpr uint32_t post_addr_delay = 5;
|
||||
};
|
||||
|
||||
struct adafruit_matrix_bonnet_pinout_bgr {
|
||||
|
|
@ -37,7 +37,45 @@ struct adafruit_matrix_bonnet_pinout_bgr {
|
|||
|
||||
static constexpr uint32_t post_oe_delay = 0;
|
||||
static constexpr uint32_t post_latch_delay = 0;
|
||||
static constexpr uint32_t post_addr_delay = 500;
|
||||
static constexpr uint32_t post_addr_delay = 5;
|
||||
};
|
||||
|
||||
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};
|
||||
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
|
||||
|
|
|
|||
|
|
@ -12,6 +12,42 @@
|
|||
|
||||
namespace piomatter {
|
||||
|
||||
static int pio_sm_xfer_data_large(PIO pio, int sm, int direction, size_t size,
|
||||
uint32_t *databuf) {
|
||||
#if 0
|
||||
// it would be NICE to gracefully fall back to blocked transfer, but sadly
|
||||
// once the large xfer ioctl fails, future small xfers fail too.
|
||||
static enum { UNKNOWN, OK, BAD } large_xfer_status = UNKNOWN;
|
||||
printf("large_xfer_status=%d\n", large_xfer_status);
|
||||
if (large_xfer_status != BAD) {
|
||||
int r = pio_sm_xfer_data(pio, sm, direction, size, databuf);
|
||||
if (large_xfer_status == UNKNOWN && r != 0) {
|
||||
large_xfer_status = BAD;
|
||||
fprintf(stderr,
|
||||
"Transmission limit workaround engaged. May reduce quality of "
|
||||
"output.\nSee https://github.com/raspberrypi/utils/issues/123 "
|
||||
"for details.\n");
|
||||
} else {
|
||||
if (large_xfer_status == UNKNOWN && r == 0) {
|
||||
large_xfer_status = OK;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
constexpr size_t MAX_XFER = 65532;
|
||||
while (size) {
|
||||
size_t xfersize = std::min(size_t{MAX_XFER}, size);
|
||||
int r = pio_sm_xfer_data(pio, sm, direction, xfersize, databuf);
|
||||
if (r != 0) {
|
||||
return r;
|
||||
}
|
||||
size -= xfersize;
|
||||
databuf += xfersize / sizeof(*databuf);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint64_t monotonicns64() {
|
||||
struct timespec tp;
|
||||
clock_gettime(CLOCK_MONOTONIC, &tp);
|
||||
|
|
@ -20,27 +56,13 @@ static uint64_t monotonicns64() {
|
|||
|
||||
constexpr size_t MAX_XFER = 65532;
|
||||
|
||||
void pio_sm_xfer_data_large(PIO pio, int sm, int direction, size_t size,
|
||||
uint32_t *databuf) {
|
||||
while (size) {
|
||||
size_t xfersize = std::min(size_t{MAX_XFER}, size);
|
||||
int r = pio_sm_xfer_data(pio, sm, direction, xfersize, databuf);
|
||||
if (r) {
|
||||
throw std::runtime_error(
|
||||
"pio_sm_xfer_data (reboot may be required)");
|
||||
}
|
||||
size -= xfersize;
|
||||
databuf += xfersize / sizeof(*databuf);
|
||||
}
|
||||
}
|
||||
|
||||
struct piomatter_base {
|
||||
piomatter_base() {}
|
||||
piomatter_base(const piomatter_base &) = delete;
|
||||
piomatter_base &operator=(const piomatter_base &) = delete;
|
||||
|
||||
virtual ~piomatter_base() {}
|
||||
virtual void show() = 0;
|
||||
virtual int show() = 0;
|
||||
|
||||
double fps;
|
||||
};
|
||||
|
|
@ -49,23 +71,37 @@ template <class pinout = adafruit_matrix_bonnet_pinout,
|
|||
class colorspace = colorspace_rgb888>
|
||||
struct piomatter : 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::blit_thread, this} {
|
||||
blitter_thread{} {
|
||||
if (geometry.n_addr_lines > std::size(pinout::PIN_ADDR)) {
|
||||
throw std::runtime_error("too many address lines requested");
|
||||
}
|
||||
program_init();
|
||||
blitter_thread = std::move(std::thread{&piomatter::blit_thread, this});
|
||||
show();
|
||||
}
|
||||
|
||||
void show() override {
|
||||
int show() override {
|
||||
int err = pending_error_errno.exchange(0); // we're handling this error
|
||||
if (err != 0) {
|
||||
return err;
|
||||
}
|
||||
int buffer_idx = manager.get_free_buffer();
|
||||
auto &buffer = buffers[buffer_idx];
|
||||
auto &bufseq = buffers[buffer_idx];
|
||||
bufseq.resize(geometry.schedules.size());
|
||||
auto converted = converter.convert(framebuffer);
|
||||
protomatter_render_rgb10<pinout>(buffer, geometry, converted.data());
|
||||
auto old_active_time = geometry.schedules.back().back().active_time;
|
||||
for (size_t i = 0; i < geometry.schedules.size(); i++) {
|
||||
protomatter_render_rgb10<pinout>(bufseq[i], geometry,
|
||||
geometry.schedules[i],
|
||||
old_active_time, converted.data());
|
||||
old_active_time = geometry.schedules[i].back().active_time;
|
||||
}
|
||||
manager.put_filled_buffer(buffer_idx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
~piomatter() {
|
||||
|
|
@ -97,7 +133,7 @@ struct piomatter : piomatter_base {
|
|||
if (sm < 0) {
|
||||
throw std::runtime_error("pio_claim_unused_sm");
|
||||
}
|
||||
int r = pio_sm_config_xfer(pio, sm, PIO_DIR_TO_SM, MAX_XFER, 2);
|
||||
int r = pio_sm_config_xfer(pio, sm, PIO_DIR_TO_SM, MAX_XFER, 3);
|
||||
if (r) {
|
||||
throw std::runtime_error("pio_sm_config_xfer");
|
||||
}
|
||||
|
|
@ -160,26 +196,34 @@ struct piomatter : piomatter_base {
|
|||
}
|
||||
|
||||
void blit_thread() {
|
||||
const uint32_t *databuf = nullptr;
|
||||
size_t datasize = 0;
|
||||
int old_buffer_idx = buffer_manager::no_buffer;
|
||||
int cur_buffer_idx = buffer_manager::no_buffer;
|
||||
int buffer_idx;
|
||||
int seq_idx = -1;
|
||||
uint64_t t0, t1;
|
||||
t0 = monotonicns64();
|
||||
while ((buffer_idx = manager.get_filled_buffer()) !=
|
||||
buffer_manager::exit_request) {
|
||||
if (buffer_idx != buffer_manager::no_buffer) {
|
||||
const auto &buffer = buffers[buffer_idx];
|
||||
databuf = &buffer[0];
|
||||
datasize = buffer.size() * sizeof(*databuf);
|
||||
if (old_buffer_idx != buffer_manager::no_buffer) {
|
||||
manager.put_free_buffer(old_buffer_idx);
|
||||
if (cur_buffer_idx != buffer_manager::no_buffer) {
|
||||
manager.put_free_buffer(cur_buffer_idx);
|
||||
}
|
||||
old_buffer_idx = buffer_idx;
|
||||
cur_buffer_idx = buffer_idx;
|
||||
}
|
||||
if (datasize) {
|
||||
pio_sm_xfer_data_large(pio, sm, PIO_DIR_TO_SM, datasize,
|
||||
(uint32_t *)databuf);
|
||||
if (cur_buffer_idx != buffer_manager::no_buffer) {
|
||||
const auto &cur_buf = buffers[cur_buffer_idx];
|
||||
seq_idx = (seq_idx + 1) % cur_buf.size();
|
||||
const auto &data = cur_buf[seq_idx];
|
||||
auto datasize = sizeof(uint32_t) * data.size();
|
||||
auto dataptr = const_cast<uint32_t *>(&data[0]);
|
||||
// returns err = rp1_ioctl.... which seems to be a negative
|
||||
// errno value
|
||||
int r = pio_sm_xfer_data_large(pio, sm, PIO_DIR_TO_SM, datasize,
|
||||
dataptr);
|
||||
if (r != 0) {
|
||||
pending_error_errno.store(errno);
|
||||
printf("xfer_data() returned error %d (errno=%s)\n", r,
|
||||
strerror(errno));
|
||||
}
|
||||
t1 = monotonicns64();
|
||||
if (t0 != t1) {
|
||||
fps = 1e9 / (t1 - t0);
|
||||
|
|
@ -194,11 +238,12 @@ struct piomatter : piomatter_base {
|
|||
PIO pio = NULL;
|
||||
int sm = -1;
|
||||
std::span<typename colorspace::data_type const> framebuffer;
|
||||
buffer_type buffers[3];
|
||||
bufseq_type buffers[3];
|
||||
buffer_manager manager{};
|
||||
matrix_geometry geometry;
|
||||
colorspace converter;
|
||||
std::thread blitter_thread;
|
||||
std::atomic<int> pending_error_errno;
|
||||
};
|
||||
|
||||
} // namespace piomatter
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
const int protomatter_wrap = 4;
|
||||
const int protomatter_wrap_target = 0;
|
||||
const int protomatter_sideset_pin_count = 1;
|
||||
const bool protomatter_sideset_enable = true;
|
||||
const bool protomatter_sideset_enable = 1;
|
||||
const uint16_t protomatter[] = {
|
||||
// ; data format (out-shift-right):
|
||||
// ; MSB ... LSB
|
||||
|
|
|
|||
|
|
@ -132,6 +132,7 @@ struct colorspace_rgb10 {
|
|||
template <typename pinout>
|
||||
void protomatter_render_rgb10(std::vector<uint32_t> &result,
|
||||
const matrix_geometry &matrixmap,
|
||||
const schedule &sched, uint32_t old_active_time,
|
||||
const uint32_t *pixels) {
|
||||
result.clear();
|
||||
|
||||
|
|
@ -153,7 +154,7 @@ void protomatter_render_rgb10(std::vector<uint32_t> &result,
|
|||
data_count = n;
|
||||
};
|
||||
|
||||
int32_t active_time;
|
||||
int32_t active_time = old_active_time;
|
||||
|
||||
auto do_data_clk_active = [&active_time, &data_count, &result](uint32_t d) {
|
||||
bool active = active_time > 0;
|
||||
|
|
@ -183,68 +184,42 @@ void protomatter_render_rgb10(std::vector<uint32_t> &result,
|
|||
return data;
|
||||
};
|
||||
|
||||
auto add_pixels = [&do_data_clk_active,
|
||||
&result](uint32_t addr_bits, bool r0, bool g0, bool b0,
|
||||
bool r1, bool g1, bool b1) {
|
||||
uint32_t data = addr_bits;
|
||||
if (r0)
|
||||
data |= (1 << pinout::PIN_RGB[0]);
|
||||
if (g0)
|
||||
data |= (1 << pinout::PIN_RGB[1]);
|
||||
if (b0)
|
||||
data |= (1 << pinout::PIN_RGB[2]);
|
||||
if (r1)
|
||||
data |= (1 << pinout::PIN_RGB[3]);
|
||||
if (g1)
|
||||
data |= (1 << pinout::PIN_RGB[4]);
|
||||
if (b1)
|
||||
data |= (1 << pinout::PIN_RGB[5]);
|
||||
|
||||
do_data_clk_active(data);
|
||||
};
|
||||
|
||||
int last_bit = 0;
|
||||
// illuminate the right row for data in the shift register (the previous
|
||||
// address)
|
||||
|
||||
const size_t n_addr = 1u << matrixmap.n_addr_lines;
|
||||
const int n_planes = matrixmap.n_planes;
|
||||
constexpr size_t n_bits = 10u;
|
||||
unsigned offset = n_bits - n_planes;
|
||||
const size_t pixels_across = matrixmap.pixels_across;
|
||||
|
||||
size_t prev_addr = n_addr - 1;
|
||||
uint32_t addr_bits = calc_addr_bits(prev_addr);
|
||||
|
||||
for (size_t addr = 0; addr < n_addr; addr++) {
|
||||
// printf("addr=%zu/%zu\n", addr, n_addr);
|
||||
for (int bit = n_planes - 1; bit >= 0; bit--) {
|
||||
// printf("bit=%d/%d\n", bit, n_planes);
|
||||
uint32_t r = 1 << (20 + offset + bit);
|
||||
uint32_t g = 1 << (10 + offset + bit);
|
||||
uint32_t b = 1 << (0 + offset + bit);
|
||||
|
||||
// the shortest /OE we can do is one DATA_OVERHEAD...
|
||||
// TODO: should make sure desired duration of MSB is at least
|
||||
// `pixels_across`
|
||||
active_time = 1 << last_bit;
|
||||
last_bit = bit;
|
||||
for (auto &schedule_ent : sched) {
|
||||
uint32_t r_mask = 1 << (20 + schedule_ent.shift);
|
||||
uint32_t g_mask = 1 << (10 + schedule_ent.shift);
|
||||
uint32_t b_mask = 1 << (0 + schedule_ent.shift);
|
||||
|
||||
prep_data(pixels_across);
|
||||
auto mapiter = matrixmap.map.begin() + 2 * addr * pixels_across;
|
||||
auto mapiter = matrixmap.map.begin() +
|
||||
matrixmap.n_lanes * addr * pixels_across;
|
||||
for (size_t x = 0; x < pixels_across; x++) {
|
||||
assert(mapiter != matrixmap.map.end());
|
||||
auto pixel0 = pixels[*mapiter++];
|
||||
auto r0 = pixel0 & r;
|
||||
auto g0 = pixel0 & g;
|
||||
auto b0 = pixel0 & b;
|
||||
assert(mapiter != matrixmap.map.end());
|
||||
auto pixel1 = pixels[*mapiter++];
|
||||
auto r1 = pixel1 & r;
|
||||
auto g1 = pixel1 & g;
|
||||
auto b1 = pixel1 & b;
|
||||
uint32_t data = addr_bits;
|
||||
for (size_t px = 0; px < matrixmap.n_lanes; px++) {
|
||||
assert(mapiter != matrixmap.map.end());
|
||||
auto pixel0 = pixels[*mapiter++];
|
||||
auto r_bit = pixel0 & r_mask;
|
||||
auto g_bit = pixel0 & g_mask;
|
||||
auto b_bit = pixel0 & b_mask;
|
||||
|
||||
add_pixels(addr_bits, r0, g0, b0, r1, g1, b1);
|
||||
if (r_bit)
|
||||
data |= (1 << pinout::PIN_RGB[px * 3 + 0]);
|
||||
if (g_bit)
|
||||
data |= (1 << pinout::PIN_RGB[px * 3 + 1]);
|
||||
if (b_bit)
|
||||
data |= (1 << pinout::PIN_RGB[px * 3 + 2]);
|
||||
}
|
||||
|
||||
do_data_clk_active(data);
|
||||
}
|
||||
|
||||
do_data_delay(addr_bits | pinout::oe_active,
|
||||
|
|
@ -256,6 +231,8 @@ void protomatter_render_rgb10(std::vector<uint32_t> &result,
|
|||
do_data_delay(addr_bits | pinout::oe_inactive | pinout::lat_bit,
|
||||
pinout::post_latch_delay);
|
||||
|
||||
active_time = schedule_ent.active_time;
|
||||
|
||||
// with oe inactive, set address bits to illuminate THIS line
|
||||
if (addr != prev_addr) {
|
||||
addr_bits = calc_addr_bits(addr);
|
||||
|
|
|
|||
|
|
@ -8,16 +8,16 @@
|
|||
#define _HARDWARE_CLOCKS_H
|
||||
|
||||
enum clock_index {
|
||||
clk_gpout0 = 0, ///< GPIO Muxing 0
|
||||
clk_gpout1, ///< GPIO Muxing 1
|
||||
clk_gpout2, ///< GPIO Muxing 2
|
||||
clk_gpout3, ///< GPIO Muxing 3
|
||||
clk_ref, ///< Watchdog and timers reference clock
|
||||
clk_sys, ///< Processors, bus fabric, memory, memory mapped registers
|
||||
clk_peri, ///< Peripheral clock for UART and SPI
|
||||
clk_usb, ///< USB clock
|
||||
clk_adc, ///< ADC clock
|
||||
clk_rtc, ///< Real time clock
|
||||
clk_gpout0 = 0, ///< GPIO Muxing 0
|
||||
clk_gpout1, ///< GPIO Muxing 1
|
||||
clk_gpout2, ///< GPIO Muxing 2
|
||||
clk_gpout3, ///< GPIO Muxing 3
|
||||
clk_ref, ///< Watchdog and timers reference clock
|
||||
clk_sys, ///< Processors, bus fabric, memory, memory mapped registers
|
||||
clk_peri, ///< Peripheral clock for UART and SPI
|
||||
clk_usb, ///< USB clock
|
||||
clk_adc, ///< ADC clock
|
||||
clk_rtc, ///< Real time clock
|
||||
CLK_COUNT
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -39,16 +39,14 @@ enum gpio_irq_level {
|
|||
};
|
||||
|
||||
enum gpio_override {
|
||||
GPIO_OVERRIDE_NORMAL =
|
||||
0, ///< peripheral signal selected via \ref gpio_set_function
|
||||
GPIO_OVERRIDE_INVERT =
|
||||
1, ///< invert peripheral signal selected via \ref gpio_set_function
|
||||
GPIO_OVERRIDE_LOW = 2, ///< drive low/disable output
|
||||
GPIO_OVERRIDE_HIGH = 3, ///< drive high/enable output
|
||||
GPIO_OVERRIDE_NORMAL = 0, ///< peripheral signal selected via \ref gpio_set_function
|
||||
GPIO_OVERRIDE_INVERT = 1, ///< invert peripheral signal selected via \ref gpio_set_function
|
||||
GPIO_OVERRIDE_LOW = 2, ///< drive low/disable output
|
||||
GPIO_OVERRIDE_HIGH = 3, ///< drive high/enable output
|
||||
};
|
||||
enum gpio_slew_rate {
|
||||
GPIO_SLEW_RATE_SLOW = 0, ///< Slew rate limiting enabled
|
||||
GPIO_SLEW_RATE_FAST = 1 ///< Slew rate limiting disabled
|
||||
GPIO_SLEW_RATE_SLOW = 0, ///< Slew rate limiting enabled
|
||||
GPIO_SLEW_RATE_FAST = 1 ///< Slew rate limiting disabled
|
||||
};
|
||||
|
||||
enum gpio_drive_strength {
|
||||
|
|
|
|||
|
|
@ -9,21 +9,18 @@
|
|||
|
||||
//#include "pico.h"
|
||||
|
||||
/** \brief PIO instruction encoding
|
||||
/** \brief PIO instruction encoding
|
||||
* \defgroup pio_instructions pio_instructions
|
||||
* \ingroup hardware_pio
|
||||
*
|
||||
* Functions for generating PIO instruction encodings programmatically. In debug builds
|
||||
*`PARAM_ASSERTIONS_ENABLED_PIO_INSTRUCTIONS` can be set to 1 to enable validation of encoding function
|
||||
* parameters.
|
||||
*
|
||||
* Functions for generating PIO instruction encodings programmatically. In debug
|
||||
*builds `PARAM_ASSERTIONS_ENABLED_PIO_INSTRUCTIONS` can be set to 1 to enable
|
||||
*validation of encoding function parameters.
|
||||
*
|
||||
* For fuller descriptions of the instructions in question see the "RP2040
|
||||
*Datasheet"
|
||||
* For fuller descriptions of the instructions in question see the "RP2040 Datasheet"
|
||||
*/
|
||||
|
||||
// PICO_CONFIG: PARAM_ASSERTIONS_ENABLED_PIO_INSTRUCTIONS, Enable/disable
|
||||
// assertions in the PIO instructions, type=bool, default=0,
|
||||
// group=pio_instructions
|
||||
// PICO_CONFIG: PARAM_ASSERTIONS_ENABLED_PIO_INSTRUCTIONS, Enable/disable assertions in the PIO instructions, type=bool, default=0, group=pio_instructions
|
||||
#ifndef PARAM_ASSERTIONS_ENABLED_PIO_INSTRUCTIONS
|
||||
#define PARAM_ASSERTIONS_ENABLED_PIO_INSTRUCTIONS 0
|
||||
#endif
|
||||
|
|
@ -45,48 +42,44 @@ enum pio_instr_bits {
|
|||
};
|
||||
|
||||
#ifndef NDEBUG
|
||||
#define _PIO_INVALID_IN_SRC 0x08u
|
||||
#define _PIO_INVALID_IN_SRC 0x08u
|
||||
#define _PIO_INVALID_OUT_DEST 0x10u
|
||||
#define _PIO_INVALID_SET_DEST 0x20u
|
||||
#define _PIO_INVALID_MOV_SRC 0x40u
|
||||
#define _PIO_INVALID_MOV_SRC 0x40u
|
||||
#define _PIO_INVALID_MOV_DEST 0x80u
|
||||
#else
|
||||
#define _PIO_INVALID_IN_SRC 0u
|
||||
#define _PIO_INVALID_IN_SRC 0u
|
||||
#define _PIO_INVALID_OUT_DEST 0u
|
||||
#define _PIO_INVALID_SET_DEST 0u
|
||||
#define _PIO_INVALID_MOV_SRC 0u
|
||||
#define _PIO_INVALID_MOV_SRC 0u
|
||||
#define _PIO_INVALID_MOV_DEST 0u
|
||||
#endif
|
||||
|
||||
/*! \brief Enumeration of values to pass for source/destination args for
|
||||
* instruction encoding functions \ingroup pio_instructions
|
||||
/*! \brief Enumeration of values to pass for source/destination args for instruction encoding functions
|
||||
* \ingroup pio_instructions
|
||||
*
|
||||
* \note Not all values are suitable for all functions. Validity is only checked
|
||||
* in debug mode when `PARAM_ASSERTIONS_ENABLED_PIO_INSTRUCTIONS` is 1
|
||||
* \note Not all values are suitable for all functions. Validity is only checked in debug mode when
|
||||
* `PARAM_ASSERTIONS_ENABLED_PIO_INSTRUCTIONS` is 1
|
||||
*/
|
||||
enum pio_src_dest {
|
||||
pio_pins = 0u,
|
||||
pio_x = 1u,
|
||||
pio_y = 2u,
|
||||
pio_null = 3u | _PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_DEST,
|
||||
pio_pindirs =
|
||||
4u | _PIO_INVALID_IN_SRC | _PIO_INVALID_MOV_SRC | _PIO_INVALID_MOV_DEST,
|
||||
pio_exec_mov = 4u | _PIO_INVALID_IN_SRC | _PIO_INVALID_OUT_DEST |
|
||||
_PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_SRC,
|
||||
pio_status = 5u | _PIO_INVALID_IN_SRC | _PIO_INVALID_OUT_DEST |
|
||||
_PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_DEST,
|
||||
pio_pc =
|
||||
5u | _PIO_INVALID_IN_SRC | _PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_SRC,
|
||||
pio_pindirs = 4u | _PIO_INVALID_IN_SRC | _PIO_INVALID_MOV_SRC | _PIO_INVALID_MOV_DEST,
|
||||
pio_exec_mov = 4u | _PIO_INVALID_IN_SRC | _PIO_INVALID_OUT_DEST | _PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_SRC,
|
||||
pio_status = 5u | _PIO_INVALID_IN_SRC | _PIO_INVALID_OUT_DEST | _PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_DEST,
|
||||
pio_pc = 5u | _PIO_INVALID_IN_SRC | _PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_SRC,
|
||||
pio_isr = 6u | _PIO_INVALID_SET_DEST,
|
||||
pio_osr = 7u | _PIO_INVALID_OUT_DEST | _PIO_INVALID_SET_DEST,
|
||||
pio_exec_out = 7u | _PIO_INVALID_IN_SRC | _PIO_INVALID_SET_DEST |
|
||||
_PIO_INVALID_MOV_SRC | _PIO_INVALID_MOV_DEST,
|
||||
pio_exec_out = 7u | _PIO_INVALID_IN_SRC | _PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_SRC | _PIO_INVALID_MOV_DEST,
|
||||
};
|
||||
|
||||
static inline uint _pio_major_instr_bits(uint instr) { return instr & 0xe000u; }
|
||||
static inline uint _pio_major_instr_bits(uint instr) {
|
||||
return instr & 0xe000u;
|
||||
}
|
||||
|
||||
static inline uint _pio_encode_instr_and_args(enum pio_instr_bits instr_bits,
|
||||
uint arg1, uint arg2) {
|
||||
static inline uint _pio_encode_instr_and_args(enum pio_instr_bits instr_bits, uint arg1, uint arg2) {
|
||||
valid_params_if(PIO_INSTRUCTIONS, arg1 <= 0x7);
|
||||
#if PARAM_ASSERTIONS_ENABLED(PIO_INSTRUCTIONS)
|
||||
uint32_t major = _pio_major_instr_bits(instr_bits);
|
||||
|
|
@ -99,21 +92,17 @@ static inline uint _pio_encode_instr_and_args(enum pio_instr_bits instr_bits,
|
|||
return instr_bits | (arg1 << 5u) | (arg2 & 0x1fu);
|
||||
}
|
||||
|
||||
static inline uint
|
||||
_pio_encode_instr_and_src_dest(enum pio_instr_bits instr_bits,
|
||||
enum pio_src_dest dest, uint value) {
|
||||
static inline uint _pio_encode_instr_and_src_dest(enum pio_instr_bits instr_bits, enum pio_src_dest dest, uint value) {
|
||||
return _pio_encode_instr_and_args(instr_bits, dest & 7u, value);
|
||||
}
|
||||
|
||||
/*! \brief Encode just the delay slot bits of an instruction
|
||||
* \ingroup pio_instructions
|
||||
*
|
||||
* \note This function does not return a valid instruction encoding; instead it
|
||||
* returns an encoding of the delay slot suitable for `OR`ing with the result of
|
||||
* an encoding function for an actual instruction. Care should be taken when
|
||||
* combining the results of this function with the results of \ref
|
||||
* pio_encode_sideset and \ref pio_encode_sideset_opt as they share the same
|
||||
* bits within the instruction encoding.
|
||||
* \note This function does not return a valid instruction encoding; instead it returns an encoding of the delay
|
||||
* slot suitable for `OR`ing with the result of an encoding function for an actual instruction. Care should be taken when
|
||||
* combining the results of this function with the results of \ref pio_encode_sideset and \ref pio_encode_sideset_opt
|
||||
* as they share the same bits within the instruction encoding.
|
||||
*
|
||||
* \param cycles the number of cycles 0-31 (or less if side set is being used)
|
||||
* \return the delay slot bits to be ORed with an instruction encoding
|
||||
|
|
@ -124,42 +113,38 @@ static inline uint pio_encode_delay(uint cycles) {
|
|||
return cycles << 8u;
|
||||
}
|
||||
|
||||
/*! \brief Encode just the side set bits of an instruction (in non optional side
|
||||
* set mode) \ingroup pio_instructions
|
||||
/*! \brief Encode just the side set bits of an instruction (in non optional side set mode)
|
||||
* \ingroup pio_instructions
|
||||
*
|
||||
* \note This function does not return a valid instruction encoding; instead it
|
||||
* returns an encoding of the side set bits suitable for `OR`ing with the result
|
||||
* of an encoding function for an actual instruction. Care should be taken when
|
||||
* combining the results of this function with the results of \ref
|
||||
* pio_encode_delay as they share the same bits within the instruction encoding.
|
||||
* \note This function does not return a valid instruction encoding; instead it returns an encoding of the side set bits
|
||||
* suitable for `OR`ing with the result of an encoding function for an actual instruction. Care should be taken when
|
||||
* combining the results of this function with the results of \ref pio_encode_delay as they share the same bits
|
||||
* within the instruction encoding.
|
||||
*
|
||||
* \param sideset_bit_count number of side set bits as would be specified via
|
||||
* `.sideset` in pioasm \param value the value to sideset on the pins \return
|
||||
* the side set bits to be ORed with an instruction encoding
|
||||
* \param sideset_bit_count number of side set bits as would be specified via `.sideset` in pioasm
|
||||
* \param value the value to sideset on the pins
|
||||
* \return the side set bits to be ORed with an instruction encoding
|
||||
*/
|
||||
static inline uint pio_encode_sideset(uint sideset_bit_count, uint value) {
|
||||
valid_params_if(PIO_INSTRUCTIONS,
|
||||
sideset_bit_count >= 1 && sideset_bit_count <= 5);
|
||||
valid_params_if(PIO_INSTRUCTIONS, sideset_bit_count >= 1 && sideset_bit_count <= 5);
|
||||
valid_params_if(PIO_INSTRUCTIONS, value <= ((1u << sideset_bit_count) - 1));
|
||||
return value << (13u - sideset_bit_count);
|
||||
}
|
||||
|
||||
/*! \brief Encode just the side set bits of an instruction (in optional -`opt`
|
||||
* side set mode) \ingroup pio_instructions
|
||||
/*! \brief Encode just the side set bits of an instruction (in optional -`opt` side set mode)
|
||||
* \ingroup pio_instructions
|
||||
*
|
||||
* \note This function does not return a valid instruction encoding; instead it
|
||||
* returns an encoding of the side set bits suitable for `OR`ing with the result
|
||||
* of an encoding function for an actual instruction. Care should be taken when
|
||||
* combining the results of this function with the results of \ref
|
||||
* pio_encode_delay as they share the same bits within the instruction encoding.
|
||||
* \note This function does not return a valid instruction encoding; instead it returns an encoding of the side set bits
|
||||
* suitable for `OR`ing with the result of an encoding function for an actual instruction. Care should be taken when
|
||||
* combining the results of this function with the results of \ref pio_encode_delay as they share the same bits
|
||||
* within the instruction encoding.
|
||||
*
|
||||
* \param sideset_bit_count number of side set bits as would be specified via
|
||||
* `.sideset <n> opt` in pioasm \param value the value to sideset on the pins
|
||||
* \param sideset_bit_count number of side set bits as would be specified via `.sideset <n> opt` in pioasm
|
||||
* \param value the value to sideset on the pins
|
||||
* \return the side set bits to be ORed with an instruction encoding
|
||||
*/
|
||||
static inline uint pio_encode_sideset_opt(uint sideset_bit_count, uint value) {
|
||||
valid_params_if(PIO_INSTRUCTIONS,
|
||||
sideset_bit_count >= 1 && sideset_bit_count <= 4);
|
||||
valid_params_if(PIO_INSTRUCTIONS, sideset_bit_count >= 1 && sideset_bit_count <= 4);
|
||||
valid_params_if(PIO_INSTRUCTIONS, value <= ((1u << sideset_bit_count) - 1));
|
||||
return 0x1000u | value << (12u - sideset_bit_count);
|
||||
}
|
||||
|
|
@ -169,9 +154,9 @@ static inline uint pio_encode_sideset_opt(uint sideset_bit_count, uint value) {
|
|||
*
|
||||
* This is the equivalent of `JMP <addr>`
|
||||
*
|
||||
* \param addr The target address 0-31 (an absolute address within the PIO
|
||||
* instruction memory) \return The instruction encoding with 0 delay and no side
|
||||
* set value \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt
|
||||
* \param addr The target address 0-31 (an absolute address within the PIO instruction memory)
|
||||
* \return The instruction encoding with 0 delay and no side set value
|
||||
* \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt
|
||||
*/
|
||||
static inline uint pio_encode_jmp(uint addr) {
|
||||
return _pio_encode_instr_and_args(pio_instr_bits_jmp, 0, addr);
|
||||
|
|
@ -182,22 +167,22 @@ static inline uint pio_encode_jmp(uint addr) {
|
|||
*
|
||||
* This is the equivalent of `JMP !X <addr>`
|
||||
*
|
||||
* \param addr The target address 0-31 (an absolute address within the PIO
|
||||
* instruction memory) \return The instruction encoding with 0 delay and no side
|
||||
* set value \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt
|
||||
* \param addr The target address 0-31 (an absolute address within the PIO instruction memory)
|
||||
* \return The instruction encoding with 0 delay and no side set value
|
||||
* \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt
|
||||
*/
|
||||
static inline uint pio_encode_jmp_not_x(uint addr) {
|
||||
return _pio_encode_instr_and_args(pio_instr_bits_jmp, 1, addr);
|
||||
}
|
||||
|
||||
/*! \brief Encode a conditional JMP if scratch X non-zero (and post-decrement X)
|
||||
* instruction \ingroup pio_instructions
|
||||
/*! \brief Encode a conditional JMP if scratch X non-zero (and post-decrement X) instruction
|
||||
* \ingroup pio_instructions
|
||||
*
|
||||
* This is the equivalent of `JMP X-- <addr>`
|
||||
*
|
||||
* \param addr The target address 0-31 (an absolute address within the PIO
|
||||
* instruction memory) \return The instruction encoding with 0 delay and no side
|
||||
* set value \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt
|
||||
* \param addr The target address 0-31 (an absolute address within the PIO instruction memory)
|
||||
* \return The instruction encoding with 0 delay and no side set value
|
||||
* \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt
|
||||
*/
|
||||
static inline uint pio_encode_jmp_x_dec(uint addr) {
|
||||
return _pio_encode_instr_and_args(pio_instr_bits_jmp, 2, addr);
|
||||
|
|
@ -208,22 +193,22 @@ static inline uint pio_encode_jmp_x_dec(uint addr) {
|
|||
*
|
||||
* This is the equivalent of `JMP !Y <addr>`
|
||||
*
|
||||
* \param addr The target address 0-31 (an absolute address within the PIO
|
||||
* instruction memory) \return The instruction encoding with 0 delay and no side
|
||||
* set value \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt
|
||||
* \param addr The target address 0-31 (an absolute address within the PIO instruction memory)
|
||||
* \return The instruction encoding with 0 delay and no side set value
|
||||
* \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt
|
||||
*/
|
||||
static inline uint pio_encode_jmp_not_y(uint addr) {
|
||||
return _pio_encode_instr_and_args(pio_instr_bits_jmp, 3, addr);
|
||||
}
|
||||
|
||||
/*! \brief Encode a conditional JMP if scratch Y non-zero (and post-decrement Y)
|
||||
* instruction \ingroup pio_instructions
|
||||
/*! \brief Encode a conditional JMP if scratch Y non-zero (and post-decrement Y) instruction
|
||||
* \ingroup pio_instructions
|
||||
*
|
||||
* This is the equivalent of `JMP Y-- <addr>`
|
||||
*
|
||||
* \param addr The target address 0-31 (an absolute address within the PIO
|
||||
* instruction memory) \return The instruction encoding with 0 delay and no side
|
||||
* set value \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt
|
||||
* \param addr The target address 0-31 (an absolute address within the PIO instruction memory)
|
||||
* \return The instruction encoding with 0 delay and no side set value
|
||||
* \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt
|
||||
*/
|
||||
static inline uint pio_encode_jmp_y_dec(uint addr) {
|
||||
return _pio_encode_instr_and_args(pio_instr_bits_jmp, 4, addr);
|
||||
|
|
@ -234,9 +219,9 @@ static inline uint pio_encode_jmp_y_dec(uint addr) {
|
|||
*
|
||||
* This is the equivalent of `JMP X!=Y <addr>`
|
||||
*
|
||||
* \param addr The target address 0-31 (an absolute address within the PIO
|
||||
* instruction memory) \return The instruction encoding with 0 delay and no side
|
||||
* set value \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt
|
||||
* \param addr The target address 0-31 (an absolute address within the PIO instruction memory)
|
||||
* \return The instruction encoding with 0 delay and no side set value
|
||||
* \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt
|
||||
*/
|
||||
static inline uint pio_encode_jmp_x_ne_y(uint addr) {
|
||||
return _pio_encode_instr_and_args(pio_instr_bits_jmp, 5, addr);
|
||||
|
|
@ -247,22 +232,22 @@ static inline uint pio_encode_jmp_x_ne_y(uint addr) {
|
|||
*
|
||||
* This is the equivalent of `JMP PIN <addr>`
|
||||
*
|
||||
* \param addr The target address 0-31 (an absolute address within the PIO
|
||||
* instruction memory) \return The instruction encoding with 0 delay and no side
|
||||
* set value \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt
|
||||
* \param addr The target address 0-31 (an absolute address within the PIO instruction memory)
|
||||
* \return The instruction encoding with 0 delay and no side set value
|
||||
* \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt
|
||||
*/
|
||||
static inline uint pio_encode_jmp_pin(uint addr) {
|
||||
return _pio_encode_instr_and_args(pio_instr_bits_jmp, 6, addr);
|
||||
}
|
||||
|
||||
/*! \brief Encode a conditional JMP if output shift register not empty
|
||||
* instruction \ingroup pio_instructions
|
||||
/*! \brief Encode a conditional JMP if output shift register not empty instruction
|
||||
* \ingroup pio_instructions
|
||||
*
|
||||
* This is the equivalent of `JMP !OSRE <addr>`
|
||||
*
|
||||
* \param addr The target address 0-31 (an absolute address within the PIO
|
||||
* instruction memory) \return The instruction encoding with 0 delay and no side
|
||||
* set value \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt
|
||||
* \param addr The target address 0-31 (an absolute address within the PIO instruction memory)
|
||||
* \return The instruction encoding with 0 delay and no side set value
|
||||
* \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt
|
||||
*/
|
||||
static inline uint pio_encode_jmp_not_osre(uint addr) {
|
||||
return _pio_encode_instr_and_args(pio_instr_bits_jmp, 7, addr);
|
||||
|
|
@ -284,8 +269,7 @@ static inline uint _pio_encode_irq(bool relative, uint irq) {
|
|||
* \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt
|
||||
*/
|
||||
static inline uint pio_encode_wait_gpio(bool polarity, uint gpio) {
|
||||
return _pio_encode_instr_and_args(pio_instr_bits_wait,
|
||||
0u | (polarity ? 4u : 0u), gpio);
|
||||
return _pio_encode_instr_and_args(pio_instr_bits_wait, 0u | (polarity ? 4u : 0u), gpio);
|
||||
}
|
||||
|
||||
/*! \brief Encode a WAIT for pin instruction
|
||||
|
|
@ -294,13 +278,12 @@ static inline uint pio_encode_wait_gpio(bool polarity, uint gpio) {
|
|||
* This is the equivalent of `WAIT <polarity> PIN <pin>`
|
||||
*
|
||||
* \param polarity true for `WAIT 1`, false for `WAIT 0`
|
||||
* \param pin The pin number 0-31 relative to the executing SM's input pin
|
||||
* mapping \return The instruction encoding with 0 delay and no side set value
|
||||
* \param pin The pin number 0-31 relative to the executing SM's input pin mapping
|
||||
* \return The instruction encoding with 0 delay and no side set value
|
||||
* \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt
|
||||
*/
|
||||
static inline uint pio_encode_wait_pin(bool polarity, uint pin) {
|
||||
return _pio_encode_instr_and_args(pio_instr_bits_wait,
|
||||
1u | (polarity ? 4u : 0u), pin);
|
||||
return _pio_encode_instr_and_args(pio_instr_bits_wait, 1u | (polarity ? 4u : 0u), pin);
|
||||
}
|
||||
|
||||
/*! \brief Encode a WAIT for IRQ instruction
|
||||
|
|
@ -309,16 +292,14 @@ static inline uint pio_encode_wait_pin(bool polarity, uint pin) {
|
|||
* This is the equivalent of `WAIT <polarity> IRQ <irq> <relative>`
|
||||
*
|
||||
* \param polarity true for `WAIT 1`, false for `WAIT 0`
|
||||
* \param relative true for a `WAIT IRQ <irq> REL`, false for regular `WAIT IRQ
|
||||
* <irq>` \param irq the irq number 0-7 \return The instruction encoding with 0
|
||||
* delay and no side set value \see pio_encode_delay, pio_encode_sideset,
|
||||
* pio_encode_sideset_opt
|
||||
* \param relative true for a `WAIT IRQ <irq> REL`, false for regular `WAIT IRQ <irq>`
|
||||
* \param irq the irq number 0-7
|
||||
* \return The instruction encoding with 0 delay and no side set value
|
||||
* \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt
|
||||
*/
|
||||
static inline uint pio_encode_wait_irq(bool polarity, bool relative, uint irq) {
|
||||
valid_params_if(PIO_INSTRUCTIONS, irq <= 7);
|
||||
return _pio_encode_instr_and_args(pio_instr_bits_wait,
|
||||
2u | (polarity ? 4u : 0u),
|
||||
_pio_encode_irq(relative, irq));
|
||||
return _pio_encode_instr_and_args(pio_instr_bits_wait, 2u | (polarity ? 4u : 0u), _pio_encode_irq(relative, irq));
|
||||
}
|
||||
|
||||
/*! \brief Encode an IN instruction
|
||||
|
|
@ -362,8 +343,7 @@ static inline uint pio_encode_out(enum pio_src_dest dest, uint count) {
|
|||
* \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt
|
||||
*/
|
||||
static inline uint pio_encode_push(bool if_full, bool block) {
|
||||
return _pio_encode_instr_and_args(
|
||||
pio_instr_bits_push, (if_full ? 2u : 0u) | (block ? 1u : 0u), 0);
|
||||
return _pio_encode_instr_and_args(pio_instr_bits_push, (if_full ? 2u : 0u) | (block ? 1u : 0u), 0);
|
||||
}
|
||||
|
||||
/*! \brief Encode a PULL instruction
|
||||
|
|
@ -377,8 +357,7 @@ static inline uint pio_encode_push(bool if_full, bool block) {
|
|||
* \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt
|
||||
*/
|
||||
static inline uint pio_encode_pull(bool if_empty, bool block) {
|
||||
return _pio_encode_instr_and_args(
|
||||
pio_instr_bits_pull, (if_empty ? 2u : 0u) | (block ? 1u : 0u), 0);
|
||||
return _pio_encode_instr_and_args(pio_instr_bits_pull, (if_empty ? 2u : 0u) | (block ? 1u : 0u), 0);
|
||||
}
|
||||
|
||||
/*! \brief Encode a MOV instruction
|
||||
|
|
@ -391,8 +370,7 @@ static inline uint pio_encode_pull(bool if_empty, bool block) {
|
|||
* \return The instruction encoding with 0 delay and no side set value
|
||||
* \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt
|
||||
*/
|
||||
static inline uint pio_encode_mov(enum pio_src_dest dest,
|
||||
enum pio_src_dest src) {
|
||||
static inline uint pio_encode_mov(enum pio_src_dest dest, enum pio_src_dest src) {
|
||||
valid_params_if(PIO_INSTRUCTIONS, !(dest & _PIO_INVALID_MOV_DEST));
|
||||
valid_params_if(PIO_INSTRUCTIONS, !(src & _PIO_INVALID_MOV_SRC));
|
||||
return _pio_encode_instr_and_src_dest(pio_instr_bits_mov, dest, src & 7u);
|
||||
|
|
@ -408,12 +386,10 @@ static inline uint pio_encode_mov(enum pio_src_dest dest,
|
|||
* \return The instruction encoding with 0 delay and no side set value
|
||||
* \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt
|
||||
*/
|
||||
static inline uint pio_encode_mov_not(enum pio_src_dest dest,
|
||||
enum pio_src_dest src) {
|
||||
static inline uint pio_encode_mov_not(enum pio_src_dest dest, enum pio_src_dest src) {
|
||||
valid_params_if(PIO_INSTRUCTIONS, !(dest & _PIO_INVALID_MOV_DEST));
|
||||
valid_params_if(PIO_INSTRUCTIONS, !(src & _PIO_INVALID_MOV_SRC));
|
||||
return _pio_encode_instr_and_src_dest(pio_instr_bits_mov, dest,
|
||||
(1u << 3u) | (src & 7u));
|
||||
return _pio_encode_instr_and_src_dest(pio_instr_bits_mov, dest, (1u << 3u) | (src & 7u));
|
||||
}
|
||||
|
||||
/*! \brief Encode a MOV instruction with bit reverse
|
||||
|
|
@ -426,12 +402,10 @@ static inline uint pio_encode_mov_not(enum pio_src_dest dest,
|
|||
* \return The instruction encoding with 0 delay and no side set value
|
||||
* \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt
|
||||
*/
|
||||
static inline uint pio_encode_mov_reverse(enum pio_src_dest dest,
|
||||
enum pio_src_dest src) {
|
||||
static inline uint pio_encode_mov_reverse(enum pio_src_dest dest, enum pio_src_dest src) {
|
||||
valid_params_if(PIO_INSTRUCTIONS, !(dest & _PIO_INVALID_MOV_DEST));
|
||||
valid_params_if(PIO_INSTRUCTIONS, !(src & _PIO_INVALID_MOV_SRC));
|
||||
return _pio_encode_instr_and_src_dest(pio_instr_bits_mov, dest,
|
||||
(2u << 3u) | (src & 7u));
|
||||
return _pio_encode_instr_and_src_dest(pio_instr_bits_mov, dest, (2u << 3u) | (src & 7u));
|
||||
}
|
||||
|
||||
/*! \brief Encode a IRQ SET instruction
|
||||
|
|
@ -439,14 +413,13 @@ static inline uint pio_encode_mov_reverse(enum pio_src_dest dest,
|
|||
*
|
||||
* This is the equivalent of `IRQ SET <irq> <relative>`
|
||||
*
|
||||
* \param relative true for a `IRQ SET <irq> REL`, false for regular `IRQ SET
|
||||
* <irq>` \param irq the irq number 0-7 \return The instruction encoding with 0
|
||||
* delay and no side set value \see pio_encode_delay, pio_encode_sideset,
|
||||
* pio_encode_sideset_opt
|
||||
* \param relative true for a `IRQ SET <irq> REL`, false for regular `IRQ SET <irq>`
|
||||
* \param irq the irq number 0-7
|
||||
* \return The instruction encoding with 0 delay and no side set value
|
||||
* \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt
|
||||
*/
|
||||
static inline uint pio_encode_irq_set(bool relative, uint irq) {
|
||||
return _pio_encode_instr_and_args(pio_instr_bits_irq, 0,
|
||||
_pio_encode_irq(relative, irq));
|
||||
return _pio_encode_instr_and_args(pio_instr_bits_irq, 0, _pio_encode_irq(relative, irq));
|
||||
}
|
||||
|
||||
/*! \brief Encode a IRQ WAIT instruction
|
||||
|
|
@ -454,14 +427,13 @@ static inline uint pio_encode_irq_set(bool relative, uint irq) {
|
|||
*
|
||||
* This is the equivalent of `IRQ WAIT <irq> <relative>`
|
||||
*
|
||||
* \param relative true for a `IRQ WAIT <irq> REL`, false for regular `IRQ WAIT
|
||||
* <irq>` \param irq the irq number 0-7 \return The instruction encoding with 0
|
||||
* delay and no side set value \see pio_encode_delay, pio_encode_sideset,
|
||||
* pio_encode_sideset_opt
|
||||
* \param relative true for a `IRQ WAIT <irq> REL`, false for regular `IRQ WAIT <irq>`
|
||||
* \param irq the irq number 0-7
|
||||
* \return The instruction encoding with 0 delay and no side set value
|
||||
* \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt
|
||||
*/
|
||||
static inline uint pio_encode_irq_wait(bool relative, uint irq) {
|
||||
return _pio_encode_instr_and_args(pio_instr_bits_irq, 1,
|
||||
_pio_encode_irq(relative, irq));
|
||||
return _pio_encode_instr_and_args(pio_instr_bits_irq, 1, _pio_encode_irq(relative, irq));
|
||||
}
|
||||
|
||||
/*! \brief Encode a IRQ CLEAR instruction
|
||||
|
|
@ -469,14 +441,13 @@ static inline uint pio_encode_irq_wait(bool relative, uint irq) {
|
|||
*
|
||||
* This is the equivalent of `IRQ CLEAR <irq> <relative>`
|
||||
*
|
||||
* \param relative true for a `IRQ CLEAR <irq> REL`, false for regular `IRQ
|
||||
* CLEAR <irq>` \param irq the irq number 0-7 \return The instruction encoding
|
||||
* with 0 delay and no side set value \see pio_encode_delay, pio_encode_sideset,
|
||||
* pio_encode_sideset_opt
|
||||
* \param relative true for a `IRQ CLEAR <irq> REL`, false for regular `IRQ CLEAR <irq>`
|
||||
* \param irq the irq number 0-7
|
||||
* \return The instruction encoding with 0 delay and no side set value
|
||||
* \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt
|
||||
*/
|
||||
static inline uint pio_encode_irq_clear(bool relative, uint irq) {
|
||||
return _pio_encode_instr_and_args(pio_instr_bits_irq, 2,
|
||||
_pio_encode_irq(relative, irq));
|
||||
return _pio_encode_instr_and_args(pio_instr_bits_irq, 2, _pio_encode_irq(relative, irq));
|
||||
}
|
||||
|
||||
/*! \brief Encode a SET instruction
|
||||
|
|
@ -502,7 +473,9 @@ static inline uint pio_encode_set(enum pio_src_dest dest, uint value) {
|
|||
* \return The instruction encoding with 0 delay and no side set value
|
||||
* \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt
|
||||
*/
|
||||
static inline uint pio_encode_nop(void) { return pio_encode_mov(pio_y, pio_y); }
|
||||
static inline uint pio_encode_nop(void) {
|
||||
return pio_encode_mov(pio_y, pio_y);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
11
src/piolib/include/hardware/timer.h
Normal file
11
src/piolib/include/hardware/timer.h
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2024 Raspberry Pi Ltd.
|
||||
* All rights reserved.
|
||||
*/
|
||||
#ifndef _HARDWARE_TIMER_H
|
||||
#define _HARDWARE_TIMER_H
|
||||
|
||||
#include "piolib.h"
|
||||
|
||||
#endif
|
||||
|
|
@ -25,27 +25,17 @@
|
|||
#define PARAM_ASSERTIONS_DISABLE_ALL 0
|
||||
#endif
|
||||
|
||||
#define PARAM_ASSERTIONS_ENABLED(x) \
|
||||
((PARAM_ASSERTIONS_ENABLED_##x || PARAM_ASSERTIONS_ENABLE_ALL) && \
|
||||
!PARAM_ASSERTIONS_DISABLE_ALL)
|
||||
#define invalid_params_if(x, test) \
|
||||
({ \
|
||||
if (PARAM_ASSERTIONS_ENABLED(x)) \
|
||||
assert(!(test)); \
|
||||
})
|
||||
#define valid_params_if(x, test) \
|
||||
({ \
|
||||
if (PARAM_ASSERTIONS_ENABLED(x)) \
|
||||
assert(test); \
|
||||
})
|
||||
#define PARAM_ASSERTIONS_ENABLED(x) ((PARAM_ASSERTIONS_ENABLED_ ## x || PARAM_ASSERTIONS_ENABLE_ALL) && !PARAM_ASSERTIONS_DISABLE_ALL)
|
||||
#define invalid_params_if(x, test) ({if (PARAM_ASSERTIONS_ENABLED(x)) assert(!(test));})
|
||||
#define valid_params_if(x, test) ({if (PARAM_ASSERTIONS_ENABLED(x)) assert(test);})
|
||||
|
||||
#define STATIC_ASSERT(cond) static_assert(cond, #cond)
|
||||
|
||||
#define _u(x) ((uint)(x))
|
||||
#define bool_to_bit(x) ((uint) !!(x))
|
||||
#define bool_to_bit(x) ((uint)!!(x))
|
||||
|
||||
#ifndef count_of
|
||||
#define count_of(a) (sizeof(a) / sizeof((a)[0]))
|
||||
#define count_of(a) (sizeof(a)/sizeof((a)[0]))
|
||||
#endif
|
||||
|
||||
typedef unsigned int uint;
|
||||
|
|
|
|||
|
|
@ -11,32 +11,39 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "pio_platform.h"
|
||||
#include "hardware/clocks.h"
|
||||
#include "hardware/gpio.h"
|
||||
#include "pio_platform.h"
|
||||
|
||||
#ifndef PARAM_ASSERTIONS_ENABLED_PIO
|
||||
#define PARAM_ASSERTIONS_ENABLED_PIO 0
|
||||
#endif
|
||||
|
||||
#define PIO_ERR(x) ((PIO)(uintptr_t)(x))
|
||||
#define PIO_IS_ERR(x) (((uintptr_t)(x) >= (uintptr_t)-200))
|
||||
#define PIO_ERR_VAL(x) ((int)(uintptr_t)(x))
|
||||
#define PIO_ERR(x)((PIO)(uintptr_t)(x))
|
||||
#define PIO_IS_ERR(x)(((uintptr_t)(x) >= (uintptr_t)-200))
|
||||
#define PIO_ERR_VAL(x)((int)(uintptr_t)(x))
|
||||
|
||||
#define PIO_ORIGIN_ANY ((uint)(~0))
|
||||
#define PIO_ORIGIN_INVALID PIO_ORIGIN_ANY
|
||||
#define PIO_ORIGIN_ANY ((uint)(~0))
|
||||
#define PIO_ORIGIN_INVALID PIO_ORIGIN_ANY
|
||||
|
||||
#define pio0 pio_open_helper(0)
|
||||
|
||||
enum pio_fifo_join {
|
||||
PIO_FIFO_JOIN_NONE = 0,
|
||||
PIO_FIFO_JOIN_TX = 1,
|
||||
PIO_FIFO_JOIN_RX = 2,
|
||||
PIO_FIFO_JOIN_NONE = 0,
|
||||
PIO_FIFO_JOIN_TX = 1,
|
||||
PIO_FIFO_JOIN_RX = 2,
|
||||
};
|
||||
|
||||
enum pio_mov_status_type { STATUS_TX_LESSTHAN = 0, STATUS_RX_LESSTHAN = 1 };
|
||||
enum pio_mov_status_type {
|
||||
STATUS_TX_LESSTHAN = 0,
|
||||
STATUS_RX_LESSTHAN = 1
|
||||
};
|
||||
|
||||
enum pio_xfer_dir { PIO_DIR_TO_SM, PIO_DIR_FROM_SM, PIO_DIR_COUNT };
|
||||
enum pio_xfer_dir {
|
||||
PIO_DIR_TO_SM,
|
||||
PIO_DIR_FROM_SM,
|
||||
PIO_DIR_COUNT
|
||||
};
|
||||
|
||||
#ifndef PIOLIB_INTERNALS
|
||||
|
||||
|
|
@ -53,16 +60,16 @@ enum pio_instr_bits {
|
|||
};
|
||||
|
||||
#ifndef NDEBUG
|
||||
#define _PIO_INVALID_IN_SRC 0x08u
|
||||
#define _PIO_INVALID_IN_SRC 0x08u
|
||||
#define _PIO_INVALID_OUT_DEST 0x10u
|
||||
#define _PIO_INVALID_SET_DEST 0x20u
|
||||
#define _PIO_INVALID_MOV_SRC 0x40u
|
||||
#define _PIO_INVALID_MOV_SRC 0x40u
|
||||
#define _PIO_INVALID_MOV_DEST 0x80u
|
||||
#else
|
||||
#define _PIO_INVALID_IN_SRC 0u
|
||||
#define _PIO_INVALID_IN_SRC 0u
|
||||
#define _PIO_INVALID_OUT_DEST 0u
|
||||
#define _PIO_INVALID_SET_DEST 0u
|
||||
#define _PIO_INVALID_MOV_SRC 0u
|
||||
#define _PIO_INVALID_MOV_SRC 0u
|
||||
#define _PIO_INVALID_MOV_DEST 0u
|
||||
#endif
|
||||
|
||||
|
|
@ -71,18 +78,13 @@ enum pio_src_dest {
|
|||
pio_x = 1u,
|
||||
pio_y = 2u,
|
||||
pio_null = 3u | _PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_DEST,
|
||||
pio_pindirs =
|
||||
4u | _PIO_INVALID_IN_SRC | _PIO_INVALID_MOV_SRC | _PIO_INVALID_MOV_DEST,
|
||||
pio_exec_mov = 4u | _PIO_INVALID_IN_SRC | _PIO_INVALID_OUT_DEST |
|
||||
_PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_SRC,
|
||||
pio_status = 5u | _PIO_INVALID_IN_SRC | _PIO_INVALID_OUT_DEST |
|
||||
_PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_DEST,
|
||||
pio_pc =
|
||||
5u | _PIO_INVALID_IN_SRC | _PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_SRC,
|
||||
pio_pindirs = 4u | _PIO_INVALID_IN_SRC | _PIO_INVALID_MOV_SRC | _PIO_INVALID_MOV_DEST,
|
||||
pio_exec_mov = 4u | _PIO_INVALID_IN_SRC | _PIO_INVALID_OUT_DEST | _PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_SRC,
|
||||
pio_status = 5u | _PIO_INVALID_IN_SRC | _PIO_INVALID_OUT_DEST | _PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_DEST,
|
||||
pio_pc = 5u | _PIO_INVALID_IN_SRC | _PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_SRC,
|
||||
pio_isr = 6u | _PIO_INVALID_SET_DEST,
|
||||
pio_osr = 7u | _PIO_INVALID_OUT_DEST | _PIO_INVALID_SET_DEST,
|
||||
pio_exec_out = 7u | _PIO_INVALID_IN_SRC | _PIO_INVALID_SET_DEST |
|
||||
_PIO_INVALID_MOV_SRC | _PIO_INVALID_MOV_DEST,
|
||||
pio_exec_out = 7u | _PIO_INVALID_IN_SRC | _PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_SRC | _PIO_INVALID_MOV_DEST,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -113,17 +115,12 @@ struct pio_chip {
|
|||
int (*open_instance)(PIO pio);
|
||||
void (*close_instance)(PIO pio);
|
||||
|
||||
int (*pio_sm_config_xfer)(PIO pio, uint sm, uint dir, uint buf_size,
|
||||
uint buf_count);
|
||||
int (*pio_sm_xfer_data)(PIO pio, uint sm, uint dir, uint data_bytes,
|
||||
void *data);
|
||||
int (*pio_sm_config_xfer)(PIO pio, uint sm, uint dir, uint buf_size, uint buf_count);
|
||||
int (*pio_sm_xfer_data)(PIO pio, uint sm, uint dir, uint data_bytes, void *data);
|
||||
|
||||
bool (*pio_can_add_program_at_offset)(PIO pio, const pio_program_t *program,
|
||||
uint offset);
|
||||
uint (*pio_add_program_at_offset)(PIO pio, const pio_program_t *program,
|
||||
uint offset);
|
||||
bool (*pio_remove_program)(PIO pio, const pio_program_t *program,
|
||||
uint loaded_offset);
|
||||
bool (*pio_can_add_program_at_offset)(PIO pio, const pio_program_t *program, uint offset);
|
||||
uint (*pio_add_program_at_offset)(PIO pio, const pio_program_t *program, uint offset);
|
||||
bool (*pio_remove_program)(PIO pio, const pio_program_t *program, uint loaded_offset);
|
||||
bool (*pio_clear_instruction_memory)(PIO pio);
|
||||
uint (*pio_encode_delay)(PIO pio, uint cycles);
|
||||
uint (*pio_encode_sideset)(PIO pio, uint sideset_bit_count, uint value);
|
||||
|
|
@ -138,18 +135,14 @@ struct pio_chip {
|
|||
uint (*pio_encode_jmp_not_osre)(PIO pio, uint addr);
|
||||
uint (*pio_encode_wait_gpio)(PIO pio, bool polarity, uint gpio);
|
||||
uint (*pio_encode_wait_pin)(PIO pio, bool polarity, uint pin);
|
||||
uint (*pio_encode_wait_irq)(PIO pio, bool polarity, bool relative,
|
||||
uint irq);
|
||||
uint (*pio_encode_wait_irq)(PIO pio, bool polarity, bool relative, uint irq);
|
||||
uint (*pio_encode_in)(PIO pio, enum pio_src_dest src, uint count);
|
||||
uint (*pio_encode_out)(PIO pio, enum pio_src_dest dest, uint count);
|
||||
uint (*pio_encode_push)(PIO pio, bool if_full, bool block);
|
||||
uint (*pio_encode_pull)(PIO pio, bool if_empty, bool block);
|
||||
uint (*pio_encode_mov)(PIO pio, enum pio_src_dest dest,
|
||||
enum pio_src_dest src);
|
||||
uint (*pio_encode_mov_not)(PIO pio, enum pio_src_dest dest,
|
||||
enum pio_src_dest src);
|
||||
uint (*pio_encode_mov_reverse)(PIO pio, enum pio_src_dest dest,
|
||||
enum pio_src_dest src);
|
||||
uint (*pio_encode_mov)(PIO pio, enum pio_src_dest dest, enum pio_src_dest src);
|
||||
uint (*pio_encode_mov_not)(PIO pio, enum pio_src_dest dest, enum pio_src_dest src);
|
||||
uint (*pio_encode_mov_reverse)(PIO pio, enum pio_src_dest dest, enum pio_src_dest src);
|
||||
uint (*pio_encode_irq_set)(PIO pio, bool relative, uint irq);
|
||||
uint (*pio_encode_irq_wait)(PIO pio, bool relative, uint irq);
|
||||
uint (*pio_encode_irq_clear)(PIO pio, bool relative, uint irq);
|
||||
|
|
@ -162,21 +155,16 @@ struct pio_chip {
|
|||
bool (*pio_sm_unclaim)(PIO pio, uint sm);
|
||||
bool (*pio_sm_is_claimed)(PIO pio, uint sm);
|
||||
|
||||
void (*pio_sm_init)(PIO pio, uint sm, uint initial_pc,
|
||||
const pio_sm_config *config);
|
||||
void (*pio_sm_init)(PIO pio, uint sm, uint initial_pc, const pio_sm_config *config);
|
||||
void (*pio_sm_set_config)(PIO pio, uint sm, const pio_sm_config *config);
|
||||
void (*pio_sm_exec)(PIO pio, uint sm, uint instr, bool blocking);
|
||||
void (*pio_sm_clear_fifos)(PIO pio, uint sm);
|
||||
void (*pio_sm_set_clkdiv_int_frac)(PIO pio, uint sm, uint16_t div_int,
|
||||
uint8_t div_frac);
|
||||
void (*pio_sm_set_clkdiv_int_frac)(PIO pio, uint sm, uint16_t div_int, uint8_t div_frac);
|
||||
void (*pio_sm_set_clkdiv)(PIO pio, uint sm, float div);
|
||||
void (*pio_sm_set_pins)(PIO pio, uint sm, uint32_t pin_values);
|
||||
void (*pio_sm_set_pins_with_mask)(PIO pio, uint sm, uint32_t pin_values,
|
||||
uint32_t pin_mask);
|
||||
void (*pio_sm_set_pindirs_with_mask)(PIO pio, uint sm, uint32_t pin_dirs,
|
||||
uint32_t pin_mask);
|
||||
void (*pio_sm_set_consecutive_pindirs)(PIO pio, uint sm, uint pin_base,
|
||||
uint pin_count, bool is_out);
|
||||
void (*pio_sm_set_pins_with_mask)(PIO pio, uint sm, uint32_t pin_values, uint32_t pin_mask);
|
||||
void (*pio_sm_set_pindirs_with_mask)(PIO pio, uint sm, uint32_t pin_dirs, uint32_t pin_mask);
|
||||
void (*pio_sm_set_consecutive_pindirs)(PIO pio, uint sm, uint pin_base, uint pin_count, bool is_out);
|
||||
void (*pio_sm_set_enabled)(PIO pio, uint sm, bool enabled);
|
||||
void (*pio_sm_set_enabled_mask)(PIO pio, uint32_t mask, bool enabled);
|
||||
void (*pio_sm_restart)(PIO pio, uint sm);
|
||||
|
|
@ -196,34 +184,23 @@ struct pio_chip {
|
|||
void (*pio_sm_drain_tx_fifo)(PIO pio, uint sm);
|
||||
|
||||
pio_sm_config (*pio_get_default_sm_config)(PIO pio);
|
||||
void (*smc_set_out_pins)(PIO pio, pio_sm_config *c, uint out_base,
|
||||
uint out_count);
|
||||
void (*smc_set_set_pins)(PIO pio, pio_sm_config *c, uint set_base,
|
||||
uint set_count);
|
||||
void (*smc_set_out_pins)(PIO pio, pio_sm_config *c, uint out_base, uint out_count);
|
||||
void (*smc_set_set_pins)(PIO pio, pio_sm_config *c, uint set_base, uint set_count);
|
||||
void (*smc_set_in_pins)(PIO pio, pio_sm_config *c, uint in_base);
|
||||
void (*smc_set_sideset_pins)(PIO pio, pio_sm_config *c, uint sideset_base);
|
||||
void (*smc_set_sideset)(PIO pio, pio_sm_config *c, uint bit_count,
|
||||
bool optional, bool pindirs);
|
||||
void (*smc_set_clkdiv_int_frac)(PIO pio, pio_sm_config *c, uint16_t div_int,
|
||||
uint8_t div_frac);
|
||||
void (*smc_set_sideset)(PIO pio, pio_sm_config *c, uint bit_count, bool optional, bool pindirs);
|
||||
void (*smc_set_clkdiv_int_frac)(PIO pio, pio_sm_config *c, uint16_t div_int, uint8_t div_frac);
|
||||
void (*smc_set_clkdiv)(PIO pio, pio_sm_config *c, float div);
|
||||
void (*smc_set_wrap)(PIO pio, pio_sm_config *c, uint wrap_target,
|
||||
uint wrap);
|
||||
void (*smc_set_wrap)(PIO pio, pio_sm_config *c, uint wrap_target, uint wrap);
|
||||
void (*smc_set_jmp_pin)(PIO pio, pio_sm_config *c, uint pin);
|
||||
void (*smc_set_in_shift)(PIO pio, pio_sm_config *c, bool shift_right,
|
||||
bool autopush, uint push_threshold);
|
||||
void (*smc_set_out_shift)(PIO pio, pio_sm_config *c, bool shift_right,
|
||||
bool autopull, uint pull_threshold);
|
||||
void (*smc_set_fifo_join)(PIO pio, pio_sm_config *c,
|
||||
enum pio_fifo_join join);
|
||||
void (*smc_set_out_special)(PIO pio, pio_sm_config *c, bool sticky,
|
||||
bool has_enable_pin, uint enable_pin_index);
|
||||
void (*smc_set_mov_status)(PIO pio, pio_sm_config *c,
|
||||
enum pio_mov_status_type status_sel,
|
||||
uint status_n);
|
||||
void (*smc_set_in_shift)(PIO pio, pio_sm_config *c, bool shift_right, bool autopush, uint push_threshold);
|
||||
void (*smc_set_out_shift)(PIO pio, pio_sm_config *c, bool shift_right, bool autopull, uint pull_threshold);
|
||||
void (*smc_set_fifo_join)(PIO pio, pio_sm_config *c, enum pio_fifo_join join);
|
||||
void (*smc_set_out_special)(PIO pio, pio_sm_config *c, bool sticky, bool has_enable_pin, uint enable_pin_index);
|
||||
void (*smc_set_mov_status)(PIO pio, pio_sm_config *c, enum pio_mov_status_type status_sel, uint status_n);
|
||||
|
||||
uint32_t (*clock_get_hz)(PIO pio, enum clock_index clk_index);
|
||||
void (*pio_gpio_init)(PIO pio, uint pin);
|
||||
void (*pio_gpio_init)(PIO, uint pin);
|
||||
void (*gpio_init)(PIO pio, uint gpio);
|
||||
void (*gpio_set_function)(PIO pio, uint gpio, enum gpio_function fn);
|
||||
void (*gpio_set_pulls)(PIO pio, uint gpio, bool up, bool down);
|
||||
|
|
@ -231,8 +208,7 @@ struct pio_chip {
|
|||
void (*gpio_set_inover)(PIO pio, uint gpio, uint value);
|
||||
void (*gpio_set_oeover)(PIO pio, uint gpio, uint value);
|
||||
void (*gpio_set_input_enabled)(PIO pio, uint gpio, bool enabled);
|
||||
void (*gpio_set_drive_strength)(PIO pio, uint gpio,
|
||||
enum gpio_drive_strength drive);
|
||||
void (*gpio_set_drive_strength)(PIO pio, uint gpio, enum gpio_drive_strength drive);
|
||||
};
|
||||
|
||||
struct pio_instance {
|
||||
|
|
@ -252,58 +228,74 @@ int pio_get_index(PIO pio);
|
|||
void pio_select(PIO pio);
|
||||
PIO pio_get_current(void);
|
||||
|
||||
static inline void pio_error(PIO pio, const char *msg) {
|
||||
static inline void pio_error(PIO pio, const char *msg)
|
||||
{
|
||||
pio->error = true;
|
||||
if (pio->errors_are_fatal)
|
||||
pio_panic(msg);
|
||||
}
|
||||
|
||||
static inline bool pio_get_error(PIO pio) { return pio->error; }
|
||||
static inline bool pio_get_error(PIO pio)
|
||||
{
|
||||
return pio->error;
|
||||
}
|
||||
|
||||
static inline void pio_clear_error(PIO pio) { pio->error = false; }
|
||||
static inline void pio_clear_error(PIO pio)
|
||||
{
|
||||
pio->error = false;
|
||||
}
|
||||
|
||||
static inline void pio_enable_fatal_errors(PIO pio, bool enable) {
|
||||
static inline void pio_enable_fatal_errors(PIO pio, bool enable)
|
||||
{
|
||||
pio->errors_are_fatal = enable;
|
||||
}
|
||||
|
||||
static inline uint pio_get_sm_count(PIO pio) { return pio->chip->sm_count; }
|
||||
static inline uint pio_get_sm_count(PIO pio)
|
||||
{
|
||||
return pio->chip->sm_count;
|
||||
}
|
||||
|
||||
static inline uint pio_get_instruction_count(PIO pio) {
|
||||
static inline uint pio_get_instruction_count(PIO pio)
|
||||
{
|
||||
return pio->chip->instr_count;
|
||||
}
|
||||
|
||||
static inline uint pio_get_fifo_depth(PIO pio) { return pio->chip->fifo_depth; }
|
||||
static inline uint pio_get_fifo_depth(PIO pio)
|
||||
{
|
||||
return pio->chip->fifo_depth;
|
||||
}
|
||||
|
||||
static inline void check_pio_param(__unused PIO pio) {
|
||||
static inline void check_pio_param(__unused PIO pio)
|
||||
{
|
||||
valid_params_if(PIO, pio_get_index(pio) >= 0);
|
||||
}
|
||||
|
||||
static inline int pio_sm_config_xfer(PIO pio, uint sm, uint dir, uint buf_size,
|
||||
uint buf_count) {
|
||||
static inline int pio_sm_config_xfer(PIO pio, uint sm, uint dir, uint buf_size, uint buf_count)
|
||||
{
|
||||
check_pio_param(pio);
|
||||
return pio->chip->pio_sm_config_xfer(pio, sm, dir, buf_size, buf_count);
|
||||
}
|
||||
|
||||
static inline int pio_sm_xfer_data(PIO pio, uint sm, uint dir, uint data_bytes,
|
||||
void *data) {
|
||||
static inline int pio_sm_xfer_data(PIO pio, uint sm, uint dir, uint data_bytes, void *data)
|
||||
{
|
||||
check_pio_param(pio);
|
||||
return pio->chip->pio_sm_xfer_data(pio, sm, dir, data_bytes, data);
|
||||
}
|
||||
|
||||
static inline bool pio_can_add_program(PIO pio, const pio_program_t *program) {
|
||||
static inline bool pio_can_add_program(PIO pio, const pio_program_t *program)
|
||||
{
|
||||
check_pio_param(pio);
|
||||
return pio->chip->pio_can_add_program_at_offset(pio, program,
|
||||
PIO_ORIGIN_ANY);
|
||||
return pio->chip->pio_can_add_program_at_offset(pio, program, PIO_ORIGIN_ANY);
|
||||
}
|
||||
|
||||
static inline bool pio_can_add_program_at_offset(PIO pio,
|
||||
const pio_program_t *program,
|
||||
uint offset) {
|
||||
static inline bool pio_can_add_program_at_offset(PIO pio, const pio_program_t *program, uint offset)
|
||||
{
|
||||
check_pio_param(pio);
|
||||
return pio->chip->pio_can_add_program_at_offset(pio, program, offset);
|
||||
}
|
||||
|
||||
static inline uint pio_add_program(PIO pio, const pio_program_t *program) {
|
||||
static inline uint pio_add_program(PIO pio, const pio_program_t *program)
|
||||
{
|
||||
uint offset;
|
||||
check_pio_param(pio);
|
||||
offset = pio->chip->pio_add_program_at_offset(pio, program, PIO_ORIGIN_ANY);
|
||||
|
|
@ -312,492 +304,548 @@ static inline uint pio_add_program(PIO pio, const pio_program_t *program) {
|
|||
return offset;
|
||||
}
|
||||
|
||||
static inline void
|
||||
pio_add_program_at_offset(PIO pio, const pio_program_t *program, uint offset) {
|
||||
static inline void pio_add_program_at_offset(PIO pio, const pio_program_t *program, uint offset)
|
||||
{
|
||||
check_pio_param(pio);
|
||||
if (pio->chip->pio_add_program_at_offset(pio, program, offset) ==
|
||||
PIO_ORIGIN_INVALID)
|
||||
if (pio->chip->pio_add_program_at_offset(pio, program, offset) == PIO_ORIGIN_INVALID)
|
||||
pio_error(pio, "No program space");
|
||||
}
|
||||
|
||||
static inline void pio_remove_program(PIO pio, const pio_program_t *program,
|
||||
uint loaded_offset) {
|
||||
static inline void pio_remove_program(PIO pio, const pio_program_t *program, uint loaded_offset)
|
||||
{
|
||||
check_pio_param(pio);
|
||||
if (!pio->chip->pio_remove_program(pio, program, loaded_offset))
|
||||
pio_error(pio, "Failed to remove program");
|
||||
}
|
||||
|
||||
static inline void pio_clear_instruction_memory(PIO pio) {
|
||||
static inline void pio_clear_instruction_memory(PIO pio)
|
||||
{
|
||||
check_pio_param(pio);
|
||||
if (!pio->chip->pio_clear_instruction_memory(pio))
|
||||
pio_error(pio, "Failed to clear instruction memory");
|
||||
}
|
||||
|
||||
static inline uint pio_encode_delay(uint cycles) {
|
||||
static inline uint pio_encode_delay(uint cycles)
|
||||
{
|
||||
PIO pio = pio_get_current();
|
||||
return pio->chip->pio_encode_delay(pio, cycles);
|
||||
}
|
||||
|
||||
static inline uint pio_encode_sideset(uint sideset_bit_count, uint value) {
|
||||
static inline uint pio_encode_sideset(uint sideset_bit_count, uint value)
|
||||
{
|
||||
PIO pio = pio_get_current();
|
||||
return pio->chip->pio_encode_sideset(pio, sideset_bit_count, value);
|
||||
}
|
||||
|
||||
static inline uint pio_encode_sideset_opt(uint sideset_bit_count, uint value) {
|
||||
static inline uint pio_encode_sideset_opt(uint sideset_bit_count, uint value)
|
||||
{
|
||||
PIO pio = pio_get_current();
|
||||
return pio->chip->pio_encode_sideset_opt(pio, sideset_bit_count, value);
|
||||
}
|
||||
|
||||
static inline uint pio_encode_jmp(uint addr) {
|
||||
static inline uint pio_encode_jmp(uint addr)
|
||||
{
|
||||
PIO pio = pio_get_current();
|
||||
return pio->chip->pio_encode_jmp(pio, addr);
|
||||
}
|
||||
|
||||
static inline uint pio_encode_jmp_not_x(uint addr) {
|
||||
static inline uint pio_encode_jmp_not_x(uint addr)
|
||||
{
|
||||
PIO pio = pio_get_current();
|
||||
return pio->chip->pio_encode_jmp_not_x(pio, addr);
|
||||
}
|
||||
|
||||
static inline uint pio_encode_jmp_x_dec(uint addr) {
|
||||
static inline uint pio_encode_jmp_x_dec(uint addr)
|
||||
{
|
||||
PIO pio = pio_get_current();
|
||||
return pio->chip->pio_encode_jmp_x_dec(pio, addr);
|
||||
}
|
||||
|
||||
static inline uint pio_encode_jmp_not_y(uint addr) {
|
||||
static inline uint pio_encode_jmp_not_y(uint addr)
|
||||
{
|
||||
PIO pio = pio_get_current();
|
||||
return pio->chip->pio_encode_jmp_not_y(pio, addr);
|
||||
}
|
||||
|
||||
static inline uint pio_encode_jmp_y_dec(uint addr) {
|
||||
static inline uint pio_encode_jmp_y_dec(uint addr)
|
||||
{
|
||||
PIO pio = pio_get_current();
|
||||
return pio->chip->pio_encode_jmp_y_dec(pio, addr);
|
||||
}
|
||||
|
||||
static inline uint pio_encode_jmp_x_ne_y(uint addr) {
|
||||
static inline uint pio_encode_jmp_x_ne_y(uint addr)
|
||||
{
|
||||
PIO pio = pio_get_current();
|
||||
return pio->chip->pio_encode_jmp_x_ne_y(pio, addr);
|
||||
}
|
||||
|
||||
static inline uint pio_encode_jmp_pin(uint addr) {
|
||||
static inline uint pio_encode_jmp_pin(uint addr)
|
||||
{
|
||||
PIO pio = pio_get_current();
|
||||
return pio->chip->pio_encode_jmp_pin(pio, addr);
|
||||
}
|
||||
|
||||
static inline uint pio_encode_jmp_not_osre(uint addr) {
|
||||
static inline uint pio_encode_jmp_not_osre(uint addr)
|
||||
{
|
||||
PIO pio = pio_get_current();
|
||||
return pio->chip->pio_encode_jmp_not_osre(pio, addr);
|
||||
}
|
||||
|
||||
static inline uint pio_encode_wait_gpio(bool polarity, uint gpio) {
|
||||
static inline uint pio_encode_wait_gpio(bool polarity, uint gpio)
|
||||
{
|
||||
PIO pio = pio_get_current();
|
||||
return pio->chip->pio_encode_wait_gpio(pio, polarity, gpio);
|
||||
}
|
||||
|
||||
static inline uint pio_encode_wait_pin(bool polarity, uint pin) {
|
||||
static inline uint pio_encode_wait_pin(bool polarity, uint pin)
|
||||
{
|
||||
PIO pio = pio_get_current();
|
||||
return pio->chip->pio_encode_wait_pin(pio, polarity, pin);
|
||||
}
|
||||
|
||||
static inline uint pio_encode_wait_irq(bool polarity, bool relative, uint irq) {
|
||||
static inline uint pio_encode_wait_irq(bool polarity, bool relative, uint irq)
|
||||
{
|
||||
PIO pio = pio_get_current();
|
||||
return pio->chip->pio_encode_wait_irq(pio, polarity, relative, irq);
|
||||
}
|
||||
|
||||
static inline uint pio_encode_in(enum pio_src_dest src, uint count) {
|
||||
static inline uint pio_encode_in(enum pio_src_dest src, uint count)
|
||||
{
|
||||
PIO pio = pio_get_current();
|
||||
return pio->chip->pio_encode_in(pio, src, count);
|
||||
}
|
||||
|
||||
static inline uint pio_encode_out(enum pio_src_dest dest, uint count) {
|
||||
static inline uint pio_encode_out(enum pio_src_dest dest, uint count)
|
||||
{
|
||||
PIO pio = pio_get_current();
|
||||
return pio->chip->pio_encode_out(pio, dest, count);
|
||||
}
|
||||
|
||||
static inline uint pio_encode_push(bool if_full, bool block) {
|
||||
static inline uint pio_encode_push(bool if_full, bool block)
|
||||
{
|
||||
PIO pio = pio_get_current();
|
||||
return pio->chip->pio_encode_push(pio, if_full, block);
|
||||
}
|
||||
|
||||
static inline uint pio_encode_pull(bool if_empty, bool block) {
|
||||
static inline uint pio_encode_pull(bool if_empty, bool block)
|
||||
{
|
||||
PIO pio = pio_get_current();
|
||||
return pio->chip->pio_encode_pull(pio, if_empty, block);
|
||||
}
|
||||
|
||||
static inline uint pio_encode_mov(enum pio_src_dest dest,
|
||||
enum pio_src_dest src) {
|
||||
static inline uint pio_encode_mov(enum pio_src_dest dest, enum pio_src_dest src)
|
||||
{
|
||||
PIO pio = pio_get_current();
|
||||
return pio->chip->pio_encode_mov(pio, dest, src);
|
||||
}
|
||||
|
||||
static inline uint pio_encode_mov_not(enum pio_src_dest dest,
|
||||
enum pio_src_dest src) {
|
||||
static inline uint pio_encode_mov_not(enum pio_src_dest dest, enum pio_src_dest src)
|
||||
{
|
||||
PIO pio = pio_get_current();
|
||||
return pio->chip->pio_encode_mov_not(pio, dest, src);
|
||||
}
|
||||
|
||||
static inline uint pio_encode_mov_reverse(enum pio_src_dest dest,
|
||||
enum pio_src_dest src) {
|
||||
static inline uint pio_encode_mov_reverse(enum pio_src_dest dest, enum pio_src_dest src)
|
||||
{
|
||||
PIO pio = pio_get_current();
|
||||
return pio->chip->pio_encode_mov_reverse(pio, dest, src);
|
||||
}
|
||||
|
||||
static inline uint pio_encode_irq_set(bool relative, uint irq) {
|
||||
static inline uint pio_encode_irq_set(bool relative, uint irq)
|
||||
{
|
||||
PIO pio = pio_get_current();
|
||||
return pio->chip->pio_encode_irq_set(pio, relative, irq);
|
||||
}
|
||||
|
||||
static inline uint pio_encode_irq_wait(bool relative, uint irq) {
|
||||
static inline uint pio_encode_irq_wait(bool relative, uint irq)
|
||||
{
|
||||
PIO pio = pio_get_current();
|
||||
return pio->chip->pio_encode_irq_wait(pio, relative, irq);
|
||||
}
|
||||
|
||||
static inline uint pio_encode_irq_clear(bool relative, uint irq) {
|
||||
static inline uint pio_encode_irq_clear(bool relative, uint irq)
|
||||
{
|
||||
PIO pio = pio_get_current();
|
||||
return pio->chip->pio_encode_irq_clear(pio, relative, irq);
|
||||
}
|
||||
|
||||
static inline uint pio_encode_set(enum pio_src_dest dest, uint value) {
|
||||
static inline uint pio_encode_set(enum pio_src_dest dest, uint value)
|
||||
{
|
||||
PIO pio = pio_get_current();
|
||||
return pio->chip->pio_encode_set(pio, dest, value);
|
||||
}
|
||||
|
||||
static inline uint pio_encode_nop(void) {
|
||||
static inline uint pio_encode_nop(void)
|
||||
{
|
||||
PIO pio = pio_get_current();
|
||||
return pio->chip->pio_encode_nop(pio);
|
||||
}
|
||||
|
||||
static inline void pio_sm_claim(PIO pio, uint sm) {
|
||||
static inline void pio_sm_claim(PIO pio, uint sm)
|
||||
{
|
||||
check_pio_param(pio);
|
||||
if (!pio->chip->pio_sm_claim(pio, sm))
|
||||
pio_error(pio, "Failed to claim SM");
|
||||
}
|
||||
|
||||
static inline void pio_claim_sm_mask(PIO pio, uint mask) {
|
||||
static inline void pio_claim_sm_mask(PIO pio, uint mask)
|
||||
{
|
||||
check_pio_param(pio);
|
||||
if (!pio->chip->pio_sm_claim_mask(pio, mask))
|
||||
pio_error(pio, "Failed to claim masked SMs");
|
||||
}
|
||||
|
||||
static inline void pio_sm_unclaim(PIO pio, uint sm) {
|
||||
static inline void pio_sm_unclaim(PIO pio, uint sm)
|
||||
{
|
||||
check_pio_param(pio);
|
||||
pio->chip->pio_sm_unclaim(pio, sm);
|
||||
}
|
||||
|
||||
static inline int pio_claim_unused_sm(PIO pio, bool required) {
|
||||
static inline int pio_claim_unused_sm(PIO pio, bool required)
|
||||
{
|
||||
check_pio_param(pio);
|
||||
return pio->chip->pio_sm_claim_unused(pio, required);
|
||||
}
|
||||
|
||||
static inline bool pio_sm_is_claimed(PIO pio, uint sm) {
|
||||
static inline bool pio_sm_is_claimed(PIO pio, uint sm)
|
||||
{
|
||||
check_pio_param(pio);
|
||||
return pio->chip->pio_sm_is_claimed(pio, sm);
|
||||
}
|
||||
|
||||
static inline void pio_sm_init(PIO pio, uint sm, uint initial_pc,
|
||||
const pio_sm_config *config) {
|
||||
static inline void pio_sm_init(PIO pio, uint sm, uint initial_pc, const pio_sm_config *config)
|
||||
{
|
||||
check_pio_param(pio);
|
||||
pio->chip->pio_sm_init(pio, sm, initial_pc, config);
|
||||
}
|
||||
|
||||
static inline void pio_sm_set_config(PIO pio, uint sm,
|
||||
const pio_sm_config *config) {
|
||||
static inline void pio_sm_set_config(PIO pio, uint sm, const pio_sm_config *config)
|
||||
{
|
||||
check_pio_param(pio);
|
||||
pio->chip->pio_sm_set_config(pio, sm, config);
|
||||
}
|
||||
|
||||
static inline void pio_sm_exec(PIO pio, uint sm, uint instr) {
|
||||
static inline void pio_sm_exec(PIO pio, uint sm, uint instr)
|
||||
{
|
||||
check_pio_param(pio);
|
||||
pio->chip->pio_sm_exec(pio, sm, instr, false);
|
||||
}
|
||||
|
||||
static inline void pio_sm_exec_wait_blocking(PIO pio, uint sm, uint instr) {
|
||||
static inline void pio_sm_exec_wait_blocking(PIO pio, uint sm, uint instr)
|
||||
{
|
||||
check_pio_param(pio);
|
||||
pio->chip->pio_sm_exec(pio, sm, instr, true);
|
||||
}
|
||||
|
||||
static inline void pio_sm_clear_fifos(PIO pio, uint sm) {
|
||||
static inline void pio_sm_clear_fifos(PIO pio, uint sm)
|
||||
{
|
||||
check_pio_param(pio);
|
||||
pio->chip->pio_sm_clear_fifos(pio, sm);
|
||||
}
|
||||
|
||||
static inline void pio_sm_set_clkdiv_int_frac(PIO pio, uint sm,
|
||||
uint16_t div_int,
|
||||
uint8_t div_frac) {
|
||||
static inline void pio_sm_set_clkdiv_int_frac(PIO pio, uint sm, uint16_t div_int, uint8_t div_frac)
|
||||
{
|
||||
check_pio_param(pio);
|
||||
pio->chip->pio_sm_set_clkdiv_int_frac(pio, sm, div_int, div_frac);
|
||||
}
|
||||
|
||||
static inline void pio_sm_set_clkdiv(PIO pio, uint sm, float div) {
|
||||
static inline void pio_sm_set_clkdiv(PIO pio, uint sm, float div)
|
||||
{
|
||||
check_pio_param(pio);
|
||||
pio->chip->pio_sm_set_clkdiv(pio, sm, div);
|
||||
}
|
||||
|
||||
static inline void pio_sm_set_pins(PIO pio, uint sm, uint32_t pin_values) {
|
||||
static inline void pio_sm_set_pins(PIO pio, uint sm, uint32_t pin_values)
|
||||
{
|
||||
check_pio_param(pio);
|
||||
pio->chip->pio_sm_set_pins(pio, sm, pin_values);
|
||||
}
|
||||
|
||||
static inline void pio_sm_set_pins_with_mask(PIO pio, uint sm,
|
||||
uint32_t pin_values,
|
||||
uint32_t pin_mask) {
|
||||
static inline void pio_sm_set_pins_with_mask(PIO pio, uint sm, uint32_t pin_values, uint32_t pin_mask)
|
||||
{
|
||||
check_pio_param(pio);
|
||||
pio->chip->pio_sm_set_pins_with_mask(pio, sm, pin_values, pin_mask);
|
||||
}
|
||||
|
||||
static inline void pio_sm_set_pindirs_with_mask(PIO pio, uint sm,
|
||||
uint32_t pin_dirs,
|
||||
uint32_t pin_mask) {
|
||||
static inline void pio_sm_set_pindirs_with_mask(PIO pio, uint sm, uint32_t pin_dirs, uint32_t pin_mask)
|
||||
{
|
||||
check_pio_param(pio);
|
||||
pio->chip->pio_sm_set_pindirs_with_mask(pio, sm, pin_dirs, pin_mask);
|
||||
}
|
||||
|
||||
static inline void pio_sm_set_consecutive_pindirs(PIO pio, uint sm,
|
||||
uint pin_base, uint pin_count,
|
||||
bool is_out) {
|
||||
static inline void pio_sm_set_consecutive_pindirs(PIO pio, uint sm, uint pin_base, uint pin_count, bool is_out)
|
||||
{
|
||||
check_pio_param(pio);
|
||||
pio->chip->pio_sm_set_consecutive_pindirs(pio, sm, pin_base, pin_count,
|
||||
is_out);
|
||||
pio->chip->pio_sm_set_consecutive_pindirs(pio, sm, pin_base, pin_count, is_out);
|
||||
}
|
||||
|
||||
static inline void pio_sm_set_enabled(PIO pio, uint sm, bool enabled) {
|
||||
static inline void pio_sm_set_enabled(PIO pio, uint sm, bool enabled)
|
||||
{
|
||||
check_pio_param(pio);
|
||||
pio->chip->pio_sm_set_enabled(pio, sm, enabled);
|
||||
}
|
||||
|
||||
static inline void pio_set_sm_mask_enabled(PIO pio, uint32_t mask,
|
||||
bool enabled) {
|
||||
static inline void pio_set_sm_mask_enabled(PIO pio, uint32_t mask, bool enabled)
|
||||
{
|
||||
check_pio_param(pio);
|
||||
pio->chip->pio_sm_set_enabled_mask(pio, mask, enabled);
|
||||
}
|
||||
|
||||
static inline void pio_sm_restart(PIO pio, uint sm) {
|
||||
static inline void pio_sm_restart(PIO pio, uint sm)
|
||||
{
|
||||
check_pio_param(pio);
|
||||
pio->chip->pio_sm_restart(pio, sm);
|
||||
}
|
||||
|
||||
static inline void pio_restart_sm_mask(PIO pio, uint32_t mask) {
|
||||
static inline void pio_restart_sm_mask(PIO pio, uint32_t mask)
|
||||
{
|
||||
check_pio_param(pio);
|
||||
pio->chip->pio_sm_restart_mask(pio, mask);
|
||||
}
|
||||
|
||||
static inline void pio_sm_clkdiv_restart(PIO pio, uint sm) {
|
||||
static inline void pio_sm_clkdiv_restart(PIO pio, uint sm)
|
||||
{
|
||||
check_pio_param(pio);
|
||||
pio->chip->pio_sm_clkdiv_restart(pio, sm);
|
||||
}
|
||||
|
||||
static inline void pio_clkdiv_restart_sm_mask(PIO pio, uint32_t mask) {
|
||||
static inline void pio_clkdiv_restart_sm_mask(PIO pio, uint32_t mask)
|
||||
{
|
||||
check_pio_param(pio);
|
||||
pio->chip->pio_sm_clkdiv_restart_mask(pio, mask);
|
||||
}
|
||||
|
||||
static inline void pio_enable_sm_in_sync_mask(PIO pio, uint32_t mask) {
|
||||
static inline void pio_enable_sm_in_sync_mask(PIO pio, uint32_t mask)
|
||||
{
|
||||
check_pio_param(pio);
|
||||
pio->chip->pio_sm_enable_sync(pio, mask);
|
||||
};
|
||||
|
||||
static inline void pio_sm_set_dmactrl(PIO pio, uint sm, bool is_tx,
|
||||
uint32_t ctrl) {
|
||||
static inline void pio_sm_set_dmactrl(PIO pio, uint sm, bool is_tx, uint32_t ctrl)
|
||||
{
|
||||
check_pio_param(pio);
|
||||
pio->chip->pio_sm_set_dmactrl(pio, sm, is_tx, ctrl);
|
||||
};
|
||||
|
||||
static inline bool pio_sm_is_rx_fifo_empty(PIO pio, uint sm) {
|
||||
static inline bool pio_sm_is_rx_fifo_empty(PIO pio, uint sm)
|
||||
{
|
||||
check_pio_param(pio);
|
||||
return pio->chip->pio_sm_is_rx_fifo_empty(pio, sm);
|
||||
}
|
||||
|
||||
static inline bool pio_sm_is_rx_fifo_full(PIO pio, uint sm) {
|
||||
static inline bool pio_sm_is_rx_fifo_full(PIO pio, uint sm)
|
||||
{
|
||||
check_pio_param(pio);
|
||||
return pio->chip->pio_sm_is_rx_fifo_full(pio, sm);
|
||||
}
|
||||
|
||||
static inline uint pio_sm_get_rx_fifo_level(PIO pio, uint sm) {
|
||||
static inline uint pio_sm_get_rx_fifo_level(PIO pio, uint sm)
|
||||
{
|
||||
check_pio_param(pio);
|
||||
return pio->chip->pio_sm_get_rx_fifo_level(pio, sm);
|
||||
}
|
||||
|
||||
static inline bool pio_sm_is_tx_fifo_empty(PIO pio, uint sm) {
|
||||
static inline bool pio_sm_is_tx_fifo_empty(PIO pio, uint sm)
|
||||
{
|
||||
check_pio_param(pio);
|
||||
return pio->chip->pio_sm_is_tx_fifo_empty(pio, sm);
|
||||
}
|
||||
|
||||
static inline bool pio_sm_is_tx_fifo_full(PIO pio, uint sm) {
|
||||
static inline bool pio_sm_is_tx_fifo_full(PIO pio, uint sm)
|
||||
{
|
||||
check_pio_param(pio);
|
||||
return pio->chip->pio_sm_is_tx_fifo_full(pio, sm);
|
||||
}
|
||||
|
||||
static inline uint pio_sm_get_tx_fifo_level(PIO pio, uint sm) {
|
||||
static inline uint pio_sm_get_tx_fifo_level(PIO pio, uint sm)
|
||||
{
|
||||
check_pio_param(pio);
|
||||
return pio->chip->pio_sm_get_tx_fifo_level(pio, sm);
|
||||
}
|
||||
|
||||
static inline void pio_sm_drain_tx_fifo(PIO pio, uint sm) {
|
||||
static inline void pio_sm_drain_tx_fifo(PIO pio, uint sm)
|
||||
{
|
||||
check_pio_param(pio);
|
||||
return pio->chip->pio_sm_drain_tx_fifo(pio, sm);
|
||||
}
|
||||
|
||||
static inline void pio_sm_put(PIO pio, uint sm, uint32_t data) {
|
||||
static inline void pio_sm_put(PIO pio, uint sm, uint32_t data)
|
||||
{
|
||||
check_pio_param(pio);
|
||||
pio->chip->pio_sm_put(pio, sm, data, false);
|
||||
}
|
||||
|
||||
static inline void pio_sm_put_blocking(PIO pio, uint sm, uint32_t data) {
|
||||
static inline void pio_sm_put_blocking(PIO pio, uint sm, uint32_t data)
|
||||
{
|
||||
check_pio_param(pio);
|
||||
pio->chip->pio_sm_put(pio, sm, data, true);
|
||||
}
|
||||
|
||||
static inline uint32_t pio_sm_get(PIO pio, uint sm) {
|
||||
static inline uint32_t pio_sm_get(PIO pio, uint sm)
|
||||
{
|
||||
check_pio_param(pio);
|
||||
return pio->chip->pio_sm_get(pio, sm, false);
|
||||
}
|
||||
|
||||
static inline uint32_t pio_sm_get_blocking(PIO pio, uint sm) {
|
||||
static inline uint32_t pio_sm_get_blocking(PIO pio, uint sm)
|
||||
{
|
||||
check_pio_param(pio);
|
||||
return pio->chip->pio_sm_get(pio, sm, true);
|
||||
}
|
||||
|
||||
static inline pio_sm_config pio_get_default_sm_config_for_pio(PIO pio) {
|
||||
static inline pio_sm_config pio_get_default_sm_config_for_pio(PIO pio)
|
||||
{
|
||||
check_pio_param(pio);
|
||||
return pio->chip->pio_get_default_sm_config(pio);
|
||||
}
|
||||
|
||||
static inline pio_sm_config pio_get_default_sm_config(void) {
|
||||
static inline pio_sm_config pio_get_default_sm_config(void)
|
||||
{
|
||||
PIO pio = pio_get_current();
|
||||
return pio->chip->pio_get_default_sm_config(pio);
|
||||
}
|
||||
|
||||
static inline void sm_config_set_out_pins(pio_sm_config *c, uint out_base,
|
||||
uint out_count) {
|
||||
static inline void sm_config_set_out_pins(pio_sm_config *c, uint out_base, uint out_count)
|
||||
{
|
||||
PIO pio = pio_get_current();
|
||||
pio->chip->smc_set_out_pins(pio, c, out_base, out_count);
|
||||
}
|
||||
|
||||
static inline void sm_config_set_set_pins(pio_sm_config *c, uint set_base,
|
||||
uint set_count) {
|
||||
static inline void sm_config_set_set_pins(pio_sm_config *c, uint set_base, uint set_count)
|
||||
{
|
||||
PIO pio = pio_get_current();
|
||||
pio->chip->smc_set_set_pins(pio, c, set_base, set_count);
|
||||
}
|
||||
|
||||
static inline void sm_config_set_in_pins(pio_sm_config *c, uint in_base) {
|
||||
static inline void sm_config_set_in_pins(pio_sm_config *c, uint in_base)
|
||||
{
|
||||
PIO pio = pio_get_current();
|
||||
pio->chip->smc_set_in_pins(pio, c, in_base);
|
||||
}
|
||||
|
||||
static inline void sm_config_set_sideset_pins(pio_sm_config *c,
|
||||
uint sideset_base) {
|
||||
static inline void sm_config_set_sideset_pins(pio_sm_config *c, uint sideset_base)
|
||||
{
|
||||
PIO pio = pio_get_current();
|
||||
pio->chip->smc_set_sideset_pins(pio, c, sideset_base);
|
||||
}
|
||||
|
||||
static inline void sm_config_set_sideset(pio_sm_config *c, uint bit_count,
|
||||
bool optional, bool pindirs) {
|
||||
static inline void sm_config_set_sideset(pio_sm_config *c, uint bit_count, bool optional, bool pindirs)
|
||||
{
|
||||
PIO pio = pio_get_current();
|
||||
pio->chip->smc_set_sideset(pio, c, bit_count, optional, pindirs);
|
||||
}
|
||||
|
||||
static inline void sm_config_set_clkdiv_int_frac(pio_sm_config *c,
|
||||
uint16_t div_int,
|
||||
uint8_t div_frac) {
|
||||
static inline void sm_config_set_clkdiv_int_frac(pio_sm_config *c, uint16_t div_int, uint8_t div_frac)
|
||||
{
|
||||
PIO pio = pio_get_current();
|
||||
pio->chip->smc_set_clkdiv_int_frac(pio, c, div_int, div_frac);
|
||||
}
|
||||
|
||||
static inline void sm_config_set_clkdiv(pio_sm_config *c, float div) {
|
||||
static inline void sm_config_set_clkdiv(pio_sm_config *c, float div)
|
||||
{
|
||||
PIO pio = pio_get_current();
|
||||
pio->chip->smc_set_clkdiv(pio, c, div);
|
||||
}
|
||||
|
||||
static inline void sm_config_set_wrap(pio_sm_config *c, uint wrap_target,
|
||||
uint wrap) {
|
||||
static inline void sm_config_set_wrap(pio_sm_config *c, uint wrap_target, uint wrap)
|
||||
{
|
||||
PIO pio = pio_get_current();
|
||||
pio->chip->smc_set_wrap(pio, c, wrap_target, wrap);
|
||||
}
|
||||
|
||||
static inline void sm_config_set_jmp_pin(pio_sm_config *c, uint pin) {
|
||||
static inline void sm_config_set_jmp_pin(pio_sm_config *c, uint pin)
|
||||
{
|
||||
PIO pio = pio_get_current();
|
||||
pio->chip->smc_set_jmp_pin(pio, c, pin);
|
||||
}
|
||||
|
||||
static inline void sm_config_set_in_shift(pio_sm_config *c, bool shift_right,
|
||||
bool autopush, uint push_threshold) {
|
||||
static inline void sm_config_set_in_shift(pio_sm_config *c, bool shift_right, bool autopush, uint push_threshold)
|
||||
{
|
||||
PIO pio = pio_get_current();
|
||||
pio->chip->smc_set_in_shift(pio, c, shift_right, autopush, push_threshold);
|
||||
}
|
||||
|
||||
static inline void sm_config_set_out_shift(pio_sm_config *c, bool shift_right,
|
||||
bool autopull, uint pull_threshold) {
|
||||
static inline void sm_config_set_out_shift(pio_sm_config *c, bool shift_right, bool autopull, uint pull_threshold)
|
||||
{
|
||||
PIO pio = pio_get_current();
|
||||
pio->chip->smc_set_out_shift(pio, c, shift_right, autopull, pull_threshold);
|
||||
}
|
||||
|
||||
static inline void sm_config_set_fifo_join(pio_sm_config *c,
|
||||
enum pio_fifo_join join) {
|
||||
static inline void sm_config_set_fifo_join(pio_sm_config *c, enum pio_fifo_join join)
|
||||
{
|
||||
PIO pio = pio_get_current();
|
||||
pio->chip->smc_set_fifo_join(pio, c, join);
|
||||
}
|
||||
|
||||
static inline void sm_config_set_out_special(pio_sm_config *c, bool sticky,
|
||||
bool has_enable_pin,
|
||||
uint enable_pin_index) {
|
||||
static inline void sm_config_set_out_special(pio_sm_config *c, bool sticky, bool has_enable_pin, uint enable_pin_index)
|
||||
{
|
||||
PIO pio = pio_get_current();
|
||||
pio->chip->smc_set_out_special(pio, c, sticky, has_enable_pin,
|
||||
enable_pin_index);
|
||||
pio->chip->smc_set_out_special(pio, c, sticky, has_enable_pin, enable_pin_index);
|
||||
}
|
||||
|
||||
static inline void sm_config_set_mov_status(pio_sm_config *c,
|
||||
enum pio_mov_status_type status_sel,
|
||||
uint status_n) {
|
||||
static inline void sm_config_set_mov_status(pio_sm_config *c, enum pio_mov_status_type status_sel, uint status_n)
|
||||
{
|
||||
PIO pio = pio_get_current();
|
||||
pio->chip->smc_set_mov_status(pio, c, status_sel, status_n);
|
||||
}
|
||||
|
||||
static inline void pio_gpio_init(PIO pio, uint pin) {
|
||||
static inline void pio_gpio_init(PIO pio, uint pin)
|
||||
{
|
||||
check_pio_param(pio);
|
||||
pio->chip->pio_gpio_init(pio, pin);
|
||||
}
|
||||
|
||||
static inline uint32_t clock_get_hz(enum clock_index clk_index) {
|
||||
static inline uint32_t clock_get_hz(enum clock_index clk_index)
|
||||
{
|
||||
PIO pio = pio_get_current();
|
||||
return pio->chip->clock_get_hz(pio, clk_index);
|
||||
}
|
||||
|
||||
static inline void gpio_init(uint gpio) {
|
||||
static inline void gpio_init(uint gpio)
|
||||
{
|
||||
PIO pio = pio_get_current();
|
||||
pio->chip->gpio_init(pio, gpio);
|
||||
}
|
||||
|
||||
static inline void gpio_set_function(uint gpio, enum gpio_function fn) {
|
||||
static inline void gpio_set_function(uint gpio, enum gpio_function fn)
|
||||
{
|
||||
PIO pio = pio_get_current();
|
||||
pio->chip->gpio_set_function(pio, gpio, fn);
|
||||
}
|
||||
|
||||
static inline void gpio_set_pulls(uint gpio, bool up, bool down) {
|
||||
|
||||
static inline void gpio_set_pulls(uint gpio, bool up, bool down)
|
||||
{
|
||||
PIO pio = pio_get_current();
|
||||
pio->chip->gpio_set_pulls(pio, gpio, up, down);
|
||||
}
|
||||
|
||||
static inline void gpio_set_outover(uint gpio, uint value) {
|
||||
static inline void gpio_set_outover(uint gpio, uint value)
|
||||
{
|
||||
PIO pio = pio_get_current();
|
||||
pio->chip->gpio_set_outover(pio, gpio, value);
|
||||
}
|
||||
|
||||
static inline void gpio_set_inover(uint gpio, uint value) {
|
||||
static inline void gpio_set_inover(uint gpio, uint value)
|
||||
{
|
||||
PIO pio = pio_get_current();
|
||||
pio->chip->gpio_set_inover(pio, gpio, value);
|
||||
}
|
||||
|
||||
static inline void gpio_set_oeover(uint gpio, uint value) {
|
||||
static inline void gpio_set_oeover(uint gpio, uint value)
|
||||
{
|
||||
PIO pio = pio_get_current();
|
||||
pio->chip->gpio_set_oeover(pio, gpio, value);
|
||||
}
|
||||
|
||||
static inline void gpio_set_input_enabled(uint gpio, bool enabled) {
|
||||
static inline void gpio_set_input_enabled(uint gpio, bool enabled)
|
||||
{
|
||||
PIO pio = pio_get_current();
|
||||
pio->chip->gpio_set_input_enabled(pio, gpio, enabled);
|
||||
}
|
||||
|
||||
static inline void gpio_set_drive_strength(uint gpio,
|
||||
enum gpio_drive_strength drive) {
|
||||
static inline void gpio_set_drive_strength(uint gpio, enum gpio_drive_strength drive)
|
||||
{
|
||||
PIO pio = pio_get_current();
|
||||
pio->chip->gpio_set_drive_strength(pio, gpio, drive);
|
||||
}
|
||||
|
|
@ -814,7 +862,9 @@ static inline void gpio_disable_pulls(uint gpio) {
|
|||
gpio_set_pulls(gpio, false, false);
|
||||
}
|
||||
|
||||
static inline void stdio_init_all(void) {}
|
||||
static inline void stdio_init_all(void)
|
||||
{
|
||||
}
|
||||
|
||||
void sleep_us(uint64_t us);
|
||||
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@
|
|||
|
||||
#include "pio_platform.h"
|
||||
|
||||
#define DECLARE_PIO_CHIP(chip) \
|
||||
const PIO_CHIP_T *__ptr_##chip __attribute__((section("piochips"))) \
|
||||
__attribute__((used)) = &chip
|
||||
#define DECLARE_PIO_CHIP(chip) \
|
||||
const PIO_CHIP_T *__ptr_ ## chip __attribute__ ((section ("piochips"))) __attribute__ ((used)) = \
|
||||
&chip
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -8,16 +8,16 @@
|
|||
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#define RP1_PIO_INSTRUCTION_COUNT 32
|
||||
#define RP1_PIO_SM_COUNT 4
|
||||
#define RP1_PIO_GPIO_COUNT 28
|
||||
#define RP1_GPIO_FUNC_PIO 7
|
||||
#define RP1_PIO_INSTRUCTION_COUNT 32
|
||||
#define RP1_PIO_SM_COUNT 4
|
||||
#define RP1_PIO_GPIO_COUNT 28
|
||||
#define RP1_GPIO_FUNC_PIO 7
|
||||
|
||||
#define RP1_PIO_ORIGIN_ANY ((uint16_t)(~0))
|
||||
#define RP1_PIO_ORIGIN_ANY ((uint16_t)(~0))
|
||||
|
||||
#define RP1_PIO_DIR_TO_SM 0
|
||||
#define RP1_PIO_DIR_FROM_SM 1
|
||||
#define RP1_PIO_DIR_COUNT 2
|
||||
#define RP1_PIO_DIR_TO_SM 0
|
||||
#define RP1_PIO_DIR_FROM_SM 1
|
||||
#define RP1_PIO_DIR_COUNT 2
|
||||
|
||||
typedef struct {
|
||||
uint32_t clkdiv;
|
||||
|
|
@ -125,12 +125,12 @@ struct rp1_pio_sm_set_dmactrl_args {
|
|||
};
|
||||
|
||||
struct rp1_pio_sm_fifo_state_args {
|
||||
uint16_t sm;
|
||||
uint8_t tx;
|
||||
uint8_t rsvd;
|
||||
uint16_t level; /* OUT */
|
||||
uint8_t empty; /* OUT */
|
||||
uint8_t full; /* OUT */
|
||||
uint16_t sm;
|
||||
uint8_t tx;
|
||||
uint8_t rsvd;
|
||||
uint16_t level; /* OUT */
|
||||
uint8_t empty; /* OUT */
|
||||
uint8_t full; /* OUT */
|
||||
};
|
||||
|
||||
struct rp1_gpio_init_args {
|
||||
|
|
@ -160,10 +160,25 @@ struct rp1_pio_sm_config_xfer_args {
|
|||
uint16_t buf_count;
|
||||
};
|
||||
|
||||
struct rp1_pio_sm_config_xfer32_args {
|
||||
uint16_t sm;
|
||||
uint16_t dir;
|
||||
uint32_t buf_size;
|
||||
uint32_t buf_count;
|
||||
};
|
||||
|
||||
struct rp1_pio_sm_xfer_data_args {
|
||||
uint16_t sm;
|
||||
uint16_t dir;
|
||||
uint16_t data_bytes;
|
||||
uint16_t rsvd;
|
||||
void *data;
|
||||
};
|
||||
|
||||
struct rp1_pio_sm_xfer_data32_args {
|
||||
uint16_t sm;
|
||||
uint16_t dir;
|
||||
uint32_t data_bytes;
|
||||
void *data;
|
||||
};
|
||||
|
||||
|
|
@ -175,75 +190,47 @@ struct rp1_access_hw_args {
|
|||
|
||||
#define PIO_IOC_MAGIC 102
|
||||
|
||||
#define PIO_IOC_SM_CONFIG_XFER \
|
||||
_IOW(PIO_IOC_MAGIC, 0, struct rp1_pio_sm_config_xfer_args)
|
||||
#define PIO_IOC_SM_XFER_DATA \
|
||||
_IOW(PIO_IOC_MAGIC, 1, struct rp1_pio_sm_xfer_data_args)
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
// XXX #define PIO_IOC_SM_XFER_DATA32 _IOW(PIO_IOC_MAGIC, 2, struct
|
||||
// pio_sm_xfer_data_args)
|
||||
#endif
|
||||
#define PIO_IOC_SM_CONFIG_XFER _IOW(PIO_IOC_MAGIC, 0, struct rp1_pio_sm_config_xfer_args)
|
||||
#define PIO_IOC_SM_XFER_DATA _IOW(PIO_IOC_MAGIC, 1, struct rp1_pio_sm_xfer_data_args)
|
||||
#define PIO_IOC_SM_XFER_DATA32 _IOW(PIO_IOC_MAGIC, 2, struct rp1_pio_sm_xfer_data32_args)
|
||||
#define PIO_IOC_SM_CONFIG_XFER32 _IOW(PIO_IOC_MAGIC, 3, struct rp1_pio_sm_config_xfer32_args)
|
||||
|
||||
#define PIO_IOC_READ_HW _IOW(PIO_IOC_MAGIC, 8, struct rp1_access_hw_args)
|
||||
#define PIO_IOC_WRITE_HW _IOW(PIO_IOC_MAGIC, 9, struct rp1_access_hw_args)
|
||||
|
||||
#define PIO_IOC_CAN_ADD_PROGRAM \
|
||||
_IOW(PIO_IOC_MAGIC, 10, struct rp1_pio_add_program_args)
|
||||
#define PIO_IOC_ADD_PROGRAM \
|
||||
_IOW(PIO_IOC_MAGIC, 11, struct rp1_pio_add_program_args)
|
||||
#define PIO_IOC_REMOVE_PROGRAM \
|
||||
_IOW(PIO_IOC_MAGIC, 12, struct rp1_pio_remove_program_args)
|
||||
#define PIO_IOC_CAN_ADD_PROGRAM _IOW(PIO_IOC_MAGIC, 10, struct rp1_pio_add_program_args)
|
||||
#define PIO_IOC_ADD_PROGRAM _IOW(PIO_IOC_MAGIC, 11, struct rp1_pio_add_program_args)
|
||||
#define PIO_IOC_REMOVE_PROGRAM _IOW(PIO_IOC_MAGIC, 12, struct rp1_pio_remove_program_args)
|
||||
#define PIO_IOC_CLEAR_INSTR_MEM _IO(PIO_IOC_MAGIC, 13)
|
||||
|
||||
#define PIO_IOC_SM_CLAIM _IOW(PIO_IOC_MAGIC, 20, struct rp1_pio_sm_claim_args)
|
||||
#define PIO_IOC_SM_UNCLAIM _IOW(PIO_IOC_MAGIC, 21, struct rp1_pio_sm_claim_args)
|
||||
#define PIO_IOC_SM_IS_CLAIMED \
|
||||
_IOW(PIO_IOC_MAGIC, 22, struct rp1_pio_sm_claim_args)
|
||||
#define PIO_IOC_SM_IS_CLAIMED _IOW(PIO_IOC_MAGIC, 22, struct rp1_pio_sm_claim_args)
|
||||
|
||||
#define PIO_IOC_SM_INIT _IOW(PIO_IOC_MAGIC, 30, struct rp1_pio_sm_init_args)
|
||||
#define PIO_IOC_SM_SET_CONFIG \
|
||||
_IOW(PIO_IOC_MAGIC, 31, struct rp1_pio_sm_set_config_args)
|
||||
#define PIO_IOC_SM_SET_CONFIG _IOW(PIO_IOC_MAGIC, 31, struct rp1_pio_sm_set_config_args)
|
||||
#define PIO_IOC_SM_EXEC _IOW(PIO_IOC_MAGIC, 32, struct rp1_pio_sm_exec_args)
|
||||
#define PIO_IOC_SM_CLEAR_FIFOS \
|
||||
_IOW(PIO_IOC_MAGIC, 33, struct rp1_pio_sm_clear_fifos_args)
|
||||
#define PIO_IOC_SM_SET_CLKDIV \
|
||||
_IOW(PIO_IOC_MAGIC, 34, struct rp1_pio_sm_set_clkdiv_args)
|
||||
#define PIO_IOC_SM_SET_PINS \
|
||||
_IOW(PIO_IOC_MAGIC, 35, struct rp1_pio_sm_set_pins_args)
|
||||
#define PIO_IOC_SM_SET_PINDIRS \
|
||||
_IOW(PIO_IOC_MAGIC, 36, struct rp1_pio_sm_set_pindirs_args)
|
||||
#define PIO_IOC_SM_SET_ENABLED \
|
||||
_IOW(PIO_IOC_MAGIC, 37, struct rp1_pio_sm_set_enabled_args)
|
||||
#define PIO_IOC_SM_RESTART \
|
||||
_IOW(PIO_IOC_MAGIC, 38, struct rp1_pio_sm_restart_args)
|
||||
#define PIO_IOC_SM_CLKDIV_RESTART \
|
||||
_IOW(PIO_IOC_MAGIC, 39, struct rp1_pio_sm_restart_args)
|
||||
#define PIO_IOC_SM_ENABLE_SYNC \
|
||||
_IOW(PIO_IOC_MAGIC, 40, struct rp1_pio_sm_enable_sync_args)
|
||||
#define PIO_IOC_SM_CLEAR_FIFOS _IOW(PIO_IOC_MAGIC, 33, struct rp1_pio_sm_clear_fifos_args)
|
||||
#define PIO_IOC_SM_SET_CLKDIV _IOW(PIO_IOC_MAGIC, 34, struct rp1_pio_sm_set_clkdiv_args)
|
||||
#define PIO_IOC_SM_SET_PINS _IOW(PIO_IOC_MAGIC, 35, struct rp1_pio_sm_set_pins_args)
|
||||
#define PIO_IOC_SM_SET_PINDIRS _IOW(PIO_IOC_MAGIC, 36, struct rp1_pio_sm_set_pindirs_args)
|
||||
#define PIO_IOC_SM_SET_ENABLED _IOW(PIO_IOC_MAGIC, 37, struct rp1_pio_sm_set_enabled_args)
|
||||
#define PIO_IOC_SM_RESTART _IOW(PIO_IOC_MAGIC, 38, struct rp1_pio_sm_restart_args)
|
||||
#define PIO_IOC_SM_CLKDIV_RESTART _IOW(PIO_IOC_MAGIC, 39, struct rp1_pio_sm_restart_args)
|
||||
#define PIO_IOC_SM_ENABLE_SYNC _IOW(PIO_IOC_MAGIC, 40, struct rp1_pio_sm_enable_sync_args)
|
||||
#define PIO_IOC_SM_PUT _IOW(PIO_IOC_MAGIC, 41, struct rp1_pio_sm_put_args)
|
||||
#define PIO_IOC_SM_GET _IOWR(PIO_IOC_MAGIC, 42, struct rp1_pio_sm_get_args)
|
||||
#define PIO_IOC_SM_SET_DMACTRL \
|
||||
_IOW(PIO_IOC_MAGIC, 43, struct rp1_pio_sm_set_dmactrl_args)
|
||||
#define PIO_IOC_SM_FIFO_STATE \
|
||||
_IOW(PIO_IOC_MAGIC, 44, struct rp1_pio_sm_fifo_state_args)
|
||||
#define PIO_IOC_SM_DRAIN_TX \
|
||||
_IOW(PIO_IOC_MAGIC, 45, struct rp1_pio_sm_clear_fifos_args)
|
||||
#define PIO_IOC_SM_SET_DMACTRL _IOW(PIO_IOC_MAGIC, 43, struct rp1_pio_sm_set_dmactrl_args)
|
||||
#define PIO_IOC_SM_FIFO_STATE _IOW(PIO_IOC_MAGIC, 44, struct rp1_pio_sm_fifo_state_args)
|
||||
#define PIO_IOC_SM_DRAIN_TX _IOW(PIO_IOC_MAGIC, 45, struct rp1_pio_sm_clear_fifos_args)
|
||||
|
||||
#define PIO_IOC_GPIO_INIT _IOW(PIO_IOC_MAGIC, 50, struct rp1_gpio_init_args)
|
||||
#define PIO_IOC_GPIO_SET_FUNCTION \
|
||||
_IOW(PIO_IOC_MAGIC, 51, struct rp1_gpio_set_function_args)
|
||||
#define PIO_IOC_GPIO_SET_PULLS \
|
||||
_IOW(PIO_IOC_MAGIC, 52, struct rp1_gpio_set_pulls_args)
|
||||
#define PIO_IOC_GPIO_SET_OUTOVER \
|
||||
_IOW(PIO_IOC_MAGIC, 53, struct rp1_gpio_set_args)
|
||||
#define PIO_IOC_GPIO_SET_INOVER \
|
||||
_IOW(PIO_IOC_MAGIC, 54, struct rp1_gpio_set_args)
|
||||
#define PIO_IOC_GPIO_SET_OEOVER \
|
||||
_IOW(PIO_IOC_MAGIC, 55, struct rp1_gpio_set_args)
|
||||
#define PIO_IOC_GPIO_SET_INPUT_ENABLED \
|
||||
_IOW(PIO_IOC_MAGIC, 56, struct rp1_gpio_set_args)
|
||||
#define PIO_IOC_GPIO_SET_DRIVE_STRENGTH \
|
||||
_IOW(PIO_IOC_MAGIC, 57, struct rp1_gpio_set_args)
|
||||
#define PIO_IOC_GPIO_SET_FUNCTION _IOW(PIO_IOC_MAGIC, 51, struct rp1_gpio_set_function_args)
|
||||
#define PIO_IOC_GPIO_SET_PULLS _IOW(PIO_IOC_MAGIC, 52, struct rp1_gpio_set_pulls_args)
|
||||
#define PIO_IOC_GPIO_SET_OUTOVER _IOW(PIO_IOC_MAGIC, 53, struct rp1_gpio_set_args)
|
||||
#define PIO_IOC_GPIO_SET_INOVER _IOW(PIO_IOC_MAGIC, 54, struct rp1_gpio_set_args)
|
||||
#define PIO_IOC_GPIO_SET_OEOVER _IOW(PIO_IOC_MAGIC, 55, struct rp1_gpio_set_args)
|
||||
#define PIO_IOC_GPIO_SET_INPUT_ENABLED _IOW(PIO_IOC_MAGIC, 56, struct rp1_gpio_set_args)
|
||||
#define PIO_IOC_GPIO_SET_DRIVE_STRENGTH _IOW(PIO_IOC_MAGIC, 57, struct rp1_gpio_set_args)
|
||||
|
||||
#endif
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -28,26 +28,33 @@ static PIO pio_instances[PIO_MAX_INSTANCES];
|
|||
static uint num_instances;
|
||||
static pthread_mutex_t pio_handle_lock;
|
||||
|
||||
void pio_select(PIO pio) { __pio = pio; }
|
||||
void pio_select(PIO pio)
|
||||
{
|
||||
__pio = pio;
|
||||
}
|
||||
|
||||
PIO pio_get_current(void) {
|
||||
PIO pio_get_current(void)
|
||||
{
|
||||
PIO pio = __pio;
|
||||
check_pio_param(pio);
|
||||
return pio;
|
||||
}
|
||||
|
||||
int pio_get_index(PIO pio) {
|
||||
int pio_get_index(PIO pio)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < PIO_MAX_INSTANCES; i++) {
|
||||
for (i = 0; i < PIO_MAX_INSTANCES; i++)
|
||||
{
|
||||
if (pio == pio_instances[i])
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int pio_init(void) {
|
||||
int pio_init(void)
|
||||
{
|
||||
static bool initialised;
|
||||
const PIO_CHIP_T *const *p;
|
||||
const PIO_CHIP_T * const *p;
|
||||
uint i = 0;
|
||||
int err;
|
||||
|
||||
|
|
@ -55,7 +62,8 @@ int pio_init(void) {
|
|||
return 0;
|
||||
num_instances = 0;
|
||||
p = &__start_piochips;
|
||||
while (p < &__stop_piochips && num_instances < PIO_MAX_INSTANCES) {
|
||||
while (p < &__stop_piochips && num_instances < PIO_MAX_INSTANCES)
|
||||
{
|
||||
PIO_CHIP_T *chip = *p;
|
||||
PIO pio = chip->create_instance(chip, i);
|
||||
if (pio && !PIO_IS_ERR(pio)) {
|
||||
|
|
@ -75,7 +83,8 @@ int pio_init(void) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
PIO pio_open(uint idx) {
|
||||
PIO pio_open(uint idx)
|
||||
{
|
||||
PIO pio = NULL;
|
||||
int err;
|
||||
|
||||
|
|
@ -112,7 +121,8 @@ PIO pio_open(uint idx) {
|
|||
return pio;
|
||||
}
|
||||
|
||||
PIO pio_open_by_name(const char *name) {
|
||||
PIO pio_open_by_name(const char *name)
|
||||
{
|
||||
int err = -ENOENT;
|
||||
uint i;
|
||||
|
||||
|
|
@ -132,33 +142,38 @@ PIO pio_open_by_name(const char *name) {
|
|||
return pio_open(i);
|
||||
}
|
||||
|
||||
PIO pio_open_helper(uint idx) {
|
||||
PIO pio_open_helper(uint idx)
|
||||
{
|
||||
PIO pio = pio_instances[idx];
|
||||
if (!pio || !pio->in_use) {
|
||||
pio = pio_open(idx);
|
||||
if (PIO_IS_ERR(pio)) {
|
||||
printf("* Failed to open PIO device %d (error %d)\n", idx,
|
||||
PIO_ERR_VAL(pio));
|
||||
printf("* Failed to open PIO device %d (error %d)\n",
|
||||
idx, PIO_ERR_VAL(pio));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
return pio;
|
||||
}
|
||||
|
||||
void pio_close(PIO pio) {
|
||||
void pio_close(PIO pio)
|
||||
{
|
||||
pio->chip->close_instance(pio);
|
||||
pthread_mutex_lock(&pio_handle_lock);
|
||||
pio->in_use = 0;
|
||||
pthread_mutex_unlock(&pio_handle_lock);
|
||||
}
|
||||
|
||||
void pio_panic(const char *msg) {
|
||||
void pio_panic(const char *msg)
|
||||
{
|
||||
fprintf(stderr, "PANIC: %s\n", msg);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void sleep_us(uint64_t us) {
|
||||
const struct timespec tv = {.tv_sec = (us / 1000000),
|
||||
.tv_nsec = 1000ull * (us % 1000000)};
|
||||
const struct timespec tv = {
|
||||
.tv_sec = (us / 1000000),
|
||||
.tv_nsec = 1000ull * (us % 1000000)
|
||||
};
|
||||
nanosleep(&tv, NULL);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "piomatter/piomatter.h"
|
||||
|
|
@ -61,7 +62,7 @@ uint32_t pixels[height][width] = {
|
|||
#undef w
|
||||
#undef _
|
||||
|
||||
#define rgb(r, g, b) ((r << 16) | (g << 8) | b)
|
||||
#define rgb(r, g, b) (((r) << 16) | ((g) << 8) | (b))
|
||||
|
||||
uint32_t colorwheel(int i) {
|
||||
i = i & 0xff;
|
||||
|
|
@ -95,10 +96,73 @@ static uint64_t monotonicns64() {
|
|||
return tp.tv_sec * UINT64_C(1000000000) + tp.tv_nsec;
|
||||
}
|
||||
|
||||
static void print_dither_schedule(const piomatter::schedule_sequence &ss) {
|
||||
for (auto s : ss) {
|
||||
for (auto i : s) {
|
||||
printf("{%d %d} ", i.shift, i.active_time);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
printf(" -> ");
|
||||
std::map<int, int> sums;
|
||||
for (auto s : ss) {
|
||||
for (auto i : s) {
|
||||
sums[-i.shift] += i.active_time;
|
||||
}
|
||||
}
|
||||
for (auto const &i : sums) {
|
||||
printf("{%d %d} ", -i.first, i.second);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static void test_simple_dither_schedule(int n_planes, int pixels_across) {
|
||||
auto ss = piomatter::make_simple_schedule(n_planes, pixels_across);
|
||||
print_dither_schedule(ss);
|
||||
printf("\n");
|
||||
}
|
||||
static void test_temporal_dither_schedule(int n_planes, int pixels_across,
|
||||
int n_temporal_frames) {
|
||||
auto ss = piomatter::make_temporal_dither_schedule(n_planes, pixels_across,
|
||||
n_temporal_frames);
|
||||
print_dither_schedule(ss);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int n = argc > 1 ? atoi(argv[1]) : 0;
|
||||
|
||||
piomatter::matrix_geometry geometry(128, 4, 10, 64, 64, true,
|
||||
test_simple_dither_schedule(7, 1);
|
||||
test_temporal_dither_schedule(7, 1, 2);
|
||||
test_temporal_dither_schedule(7, 1, 3);
|
||||
test_temporal_dither_schedule(7, 1, 4);
|
||||
test_temporal_dither_schedule(7, 1, 5);
|
||||
|
||||
return 0;
|
||||
test_simple_dither_schedule(6, 1);
|
||||
test_temporal_dither_schedule(6, 1, 0);
|
||||
test_temporal_dither_schedule(6, 1, 2);
|
||||
test_temporal_dither_schedule(6, 1, 4);
|
||||
|
||||
test_simple_dither_schedule(5, 16);
|
||||
test_temporal_dither_schedule(5, 16, 2);
|
||||
test_temporal_dither_schedule(5, 16, 3);
|
||||
test_temporal_dither_schedule(5, 16, 4);
|
||||
|
||||
test_simple_dither_schedule(5, 24);
|
||||
test_temporal_dither_schedule(5, 24, 2);
|
||||
test_temporal_dither_schedule(5, 24, 4);
|
||||
|
||||
test_simple_dither_schedule(10, 24);
|
||||
test_temporal_dither_schedule(10, 24, 8);
|
||||
|
||||
test_temporal_dither_schedule(5, 128, 3);
|
||||
test_temporal_dither_schedule(5, 192, 3);
|
||||
test_temporal_dither_schedule(5, 128, 4);
|
||||
test_temporal_dither_schedule(5, 192, 4);
|
||||
return 0;
|
||||
|
||||
piomatter::matrix_geometry geometry(128, 4, 10, 0, 64, 64, true,
|
||||
piomatter::orientation_normal);
|
||||
piomatter::piomatter p(std::span(&pixels[0][0], 64 * 64), geometry);
|
||||
|
||||
179
src/pymain.cpp
179
src/pymain.cpp
|
|
@ -1,5 +1,6 @@
|
|||
#include <iostream>
|
||||
#include <pybind11/pybind11.h>
|
||||
#include <pybind11/stl.h>
|
||||
#include <string>
|
||||
|
||||
#include "piomatter/piomatter.h"
|
||||
|
|
@ -17,7 +18,14 @@ struct PyPiomatter {
|
|||
py::buffer buffer;
|
||||
std::unique_ptr<piomatter::piomatter_base> matter;
|
||||
|
||||
void show() { matter->show(); }
|
||||
void show() {
|
||||
int err = matter->show();
|
||||
if (err != 0) {
|
||||
errno = err;
|
||||
PyErr_SetFromErrno(PyExc_OSError);
|
||||
throw py::error_already_set();
|
||||
}
|
||||
}
|
||||
double fps() const { return matter->fps; }
|
||||
};
|
||||
|
||||
|
|
@ -33,6 +41,14 @@ make_piomatter_pc(py::buffer buffer,
|
|||
const py::buffer_info info = buffer.request();
|
||||
const size_t buffer_size_in_bytes = info.size * info.itemsize;
|
||||
|
||||
if (geometry.n_lanes * 3 > std::size(pinout::PIN_RGB)) {
|
||||
throw std::runtime_error(
|
||||
py::str("Geometry lane count {} exceeds the pinout with {} rgb "
|
||||
"pins ({} lanes)")
|
||||
.attr("format")(geometry.n_lanes, std::size(pinout::PIN_RGB),
|
||||
std::size(pinout::PIN_RGB) / 3)
|
||||
.template cast<std::string>());
|
||||
}
|
||||
if (buffer_size_in_bytes != data_size_in_bytes) {
|
||||
throw std::runtime_error(
|
||||
py::str("Framebuffer size must be {} bytes ({} elements of {} "
|
||||
|
|
@ -54,6 +70,8 @@ enum Colorspace { RGB565, RGB888, RGB888Packed };
|
|||
enum Pinout {
|
||||
AdafruitMatrixBonnet,
|
||||
AdafruitMatrixBonnetBGR,
|
||||
Active3,
|
||||
Active3BGR,
|
||||
};
|
||||
|
||||
template <class pinout>
|
||||
|
|
@ -70,12 +88,10 @@ make_piomatter_p(Colorspace c, py::buffer buffer,
|
|||
case RGB888Packed:
|
||||
return make_piomatter_pc<pinout, piomatter::colorspace_rgb888_packed>(
|
||||
buffer, geometry);
|
||||
|
||||
default:
|
||||
throw std::runtime_error(py::str("Invalid colorspace {!r}")
|
||||
.attr("format")(c)
|
||||
.template cast<std::string>());
|
||||
}
|
||||
throw std::runtime_error(py::str("Invalid colorspace {!r}")
|
||||
.attr("format")(c)
|
||||
.template cast<std::string>());
|
||||
}
|
||||
|
||||
std::unique_ptr<PyPiomatter>
|
||||
|
|
@ -88,15 +104,19 @@ make_piomatter(Colorspace c, Pinout p, py::buffer buffer,
|
|||
case AdafruitMatrixBonnetBGR:
|
||||
return make_piomatter_p<piomatter::adafruit_matrix_bonnet_pinout_bgr>(
|
||||
c, buffer, geometry);
|
||||
default:
|
||||
throw std::runtime_error(py::str("Invalid pinout {!r}")
|
||||
.attr("format")(p)
|
||||
.template cast<std::string>());
|
||||
case Active3:
|
||||
return make_piomatter_p<piomatter::active3_pinout>(c, buffer, geometry);
|
||||
case Active3BGR:
|
||||
return make_piomatter_p<piomatter::active3_pinout_bgr>(c, buffer,
|
||||
geometry);
|
||||
}
|
||||
throw std::runtime_error(py::str("Invalid pinout {!r}")
|
||||
.attr("format")(p)
|
||||
.template cast<std::string>());
|
||||
}
|
||||
} // namespace
|
||||
|
||||
PYBIND11_MODULE(adafruit_blinka_raspberry_pi5_piomatter, m) {
|
||||
PYBIND11_MODULE(_piomatter, m) {
|
||||
py::options options;
|
||||
options.enable_enum_members_docstring();
|
||||
options.enable_function_signatures();
|
||||
|
|
@ -106,18 +126,7 @@ PYBIND11_MODULE(adafruit_blinka_raspberry_pi5_piomatter, m) {
|
|||
HUB75 matrix driver for Raspberry Pi 5 using PIO
|
||||
------------------------------------------------
|
||||
|
||||
.. currentmodule:: adafruit_blinka_raspberry_pi5_piomatter
|
||||
|
||||
.. autosummary::
|
||||
:toctree: _generate
|
||||
|
||||
Orientation
|
||||
Pinout
|
||||
Colorspace
|
||||
Geometry
|
||||
PioMatter
|
||||
AdafruitMatrixBonnetRGB888
|
||||
AdafruitMatrixBonnetRGB888Packed
|
||||
.. currentmodule:: adafruit_blinka_raspberry_pi5_piomatter._piomatter
|
||||
)pbdoc";
|
||||
|
||||
py::enum_<piomatter::orientation>(
|
||||
|
|
@ -138,7 +147,10 @@ PYBIND11_MODULE(adafruit_blinka_raspberry_pi5_piomatter, m) {
|
|||
.value("AdafruitMatrixHat", Pinout::AdafruitMatrixBonnet,
|
||||
"Adafruit Matrix Bonnet or Matrix Hat")
|
||||
.value("AdafruitMatrixHatBGR", Pinout::AdafruitMatrixBonnetBGR,
|
||||
"Adafruit Matrix Bonnet or Matrix Hat with BGR color order");
|
||||
"Adafruit Matrix Bonnet or Matrix Hat with BGR color order")
|
||||
.value("Active3", Pinout::Active3, "Active-3 or compatible board")
|
||||
.value("Active3BGR", Pinout::Active3BGR,
|
||||
"Active-3 or compatible board with BGR color order");
|
||||
|
||||
py::enum_<Colorspace>(
|
||||
m, "Colorspace",
|
||||
|
|
@ -157,19 +169,35 @@ Describe the geometry of a set of panels
|
|||
|
||||
The number of pixels in the shift register is automatically computed from these values.
|
||||
|
||||
``n_planes`` controls the color depth of the panel. This is separate from the framebuffer
|
||||
layout. Decreasing ``n_planes`` can increase FPS at the cost of reduced color fidelity.
|
||||
The default, 10, is the maximum value.
|
||||
|
||||
``n_temporal_planes`` controls temporal dithering of the panel.
|
||||
0 or 1 behave the same: All `n_planes` are transmitted every time.
|
||||
A higher value sets the number of planes that are only transmitted every `1/n_temporal_frames` times.
|
||||
A higher setting can increase FPS at the cost of slightly increasing the variation of brightness across subsequent frames.
|
||||
Settings above 4 are allowed, but generally do not give good results.
|
||||
|
||||
For simple panels with just 1 connector (2 color lanes), the following constructor arguments are available:
|
||||
|
||||
``serpentine`` controls the arrangement of multiple panels when they are stacked in rows.
|
||||
If it is `True`, then each row goes in the opposite direction of the previous row.
|
||||
If this is specified, ``n_lanes`` cannot be, and 2 lanes are always used.
|
||||
|
||||
``rotation`` controls the orientation of the panel(s). Must be one of the ``Orientation``
|
||||
constants. Default is ``Orientation.Normal``.
|
||||
|
||||
``n_planes`` controls the color depth of the panel. This is separate from the framebuffer
|
||||
layout. Decreasing ``n_planes`` can increase FPS at the cost of reduced color fidelity.
|
||||
The default, 10, is the maximum value.
|
||||
For panels with more than 2 lanes, or using a custom pixel mapping, the following constructor arguments are available:
|
||||
|
||||
``n_lanes`` controls how many color lanes are used. A single 16-pin HUB75 connector has 2 color lanes.
|
||||
If 2 or 3 connectors are used, then there are 4 or 6 lanes.
|
||||
|
||||
``map`` is a Python list of integers giving the framebuffer pixel indices for each matrix pixel.
|
||||
)pbdoc")
|
||||
.def(py::init([](size_t width, size_t height, size_t n_addr_lines,
|
||||
bool serpentine, piomatter::orientation rotation,
|
||||
size_t n_planes) {
|
||||
size_t n_planes, size_t n_temporal_planes) {
|
||||
size_t n_lines = 2 << n_addr_lines;
|
||||
size_t pixels_across = width * height / n_lines;
|
||||
size_t odd = (width * height) % n_lines;
|
||||
|
|
@ -185,30 +213,51 @@ The default, 10, is the maximum value.
|
|||
switch (rotation) {
|
||||
case piomatter::orientation::normal:
|
||||
return piomatter::matrix_geometry(
|
||||
pixels_across, n_addr_lines, n_planes, width, height,
|
||||
serpentine, piomatter::orientation_normal);
|
||||
pixels_across, n_addr_lines, n_planes,
|
||||
n_temporal_planes, width, height, serpentine,
|
||||
piomatter::orientation_normal);
|
||||
|
||||
case piomatter::orientation::r180:
|
||||
return piomatter::matrix_geometry(
|
||||
pixels_across, n_addr_lines, n_planes, width, height,
|
||||
serpentine, piomatter::orientation_r180);
|
||||
pixels_across, n_addr_lines, n_planes,
|
||||
n_temporal_planes, width, height, serpentine,
|
||||
piomatter::orientation_r180);
|
||||
|
||||
case piomatter::orientation::ccw:
|
||||
return piomatter::matrix_geometry(
|
||||
pixels_across, n_addr_lines, n_planes, width, height,
|
||||
serpentine, piomatter::orientation_ccw);
|
||||
pixels_across, n_addr_lines, n_planes,
|
||||
n_temporal_planes, width, height, serpentine,
|
||||
piomatter::orientation_ccw);
|
||||
|
||||
case piomatter::orientation::cw:
|
||||
return piomatter::matrix_geometry(
|
||||
pixels_across, n_addr_lines, n_planes, width, height,
|
||||
serpentine, piomatter::orientation_cw);
|
||||
pixels_across, n_addr_lines, n_planes,
|
||||
n_temporal_planes, width, height, serpentine,
|
||||
piomatter::orientation_cw);
|
||||
}
|
||||
throw std::runtime_error("invalid rotation");
|
||||
}),
|
||||
py::arg("width"), py::arg("height"), py::arg("n_addr_lines"),
|
||||
py::arg("serpentine") = true,
|
||||
py::arg("rotation") = piomatter::orientation::normal,
|
||||
py::arg("n_planes") = 10u)
|
||||
py::arg("n_planes") = 10u, py::arg("n_temporal_planes") = 2)
|
||||
.def(py::init([](size_t width, size_t height, size_t n_addr_lines,
|
||||
piomatter::matrix_map map, size_t n_planes,
|
||||
size_t n_temporal_planes, size_t n_lanes) {
|
||||
size_t n_lines = n_lanes << n_addr_lines;
|
||||
size_t pixels_across = width * height / n_lines;
|
||||
for (auto el : map) {
|
||||
if ((size_t)el >= width * height) {
|
||||
throw std::out_of_range("Map element out of range");
|
||||
}
|
||||
}
|
||||
return piomatter::matrix_geometry(pixels_across, n_addr_lines,
|
||||
n_planes, n_temporal_planes,
|
||||
width, height, map, n_lanes);
|
||||
}),
|
||||
py::arg("width"), py::arg("height"), py::arg("n_addr_lines"),
|
||||
py::arg("map"), py::arg("n_planes") = 10u,
|
||||
py::arg("n_temporal_planes") = 0u, py::arg("n_lanes") = 2)
|
||||
.def_readonly("width", &piomatter::matrix_geometry::width)
|
||||
.def_readonly("height", &piomatter::matrix_geometry::height);
|
||||
|
||||
|
|
@ -216,16 +265,16 @@ The default, 10, is the maximum value.
|
|||
HUB75 matrix driver for Raspberry Pi 5 using PIO
|
||||
|
||||
``colorspace`` controls the colorspace that will be used for data to be displayed.
|
||||
It must be one of the ``Colorspace`` constants. Which to use depends on what data
|
||||
It must be one of the `Colorspace` constants. Which to use depends on what data
|
||||
your displaying and how it is processed before copying into the framebuffer.
|
||||
|
||||
``pinout`` defines which pins the panels are wired to. Different pinouts can
|
||||
support different hardware breakouts and panels with different color order. The
|
||||
value must be one of the ``Pinout`` constants.
|
||||
value must be one of the `Pinout` constants.
|
||||
|
||||
``framebuffer`` a numpy array that holds pixel data in the appropriate colorspace.
|
||||
|
||||
``geometry`` controls the size and shape of the panel. The value must be a ``Geometry``
|
||||
``geometry`` controls the size and shape of the panel. The value must be a `Geometry`
|
||||
instance.
|
||||
)pbdoc")
|
||||
.def(py::init(&make_piomatter), py::arg("colorspace"),
|
||||
|
|
@ -239,55 +288,5 @@ 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");
|
||||
|
||||
m.def(
|
||||
"AdafruitMatrixBonnetRGB565",
|
||||
[](py::buffer buffer, const piomatter::matrix_geometry &geometry) {
|
||||
return make_piomatter(Colorspace::RGB565,
|
||||
Pinout::AdafruitMatrixBonnet, buffer,
|
||||
geometry);
|
||||
},
|
||||
py::arg("buffer"), py::arg("geometry"),
|
||||
R"pbdoc(
|
||||
Construct a PioMatter object to drive panels connected to an
|
||||
Adafruit Matrix Bonnet using the RGB565 memory layout (2 bytes per
|
||||
pixel)
|
||||
|
||||
This is deprecated shorthand for `PioMatter(Colorspace.RGB565, Pinout.AdafruitMatrixBonnet, ...)`.
|
||||
)pbdoc");
|
||||
|
||||
m.def(
|
||||
"AdafruitMatrixBonnetRGB888",
|
||||
[](py::buffer buffer, const piomatter::matrix_geometry &geometry) {
|
||||
return make_piomatter(Colorspace::RGB888,
|
||||
Pinout::AdafruitMatrixBonnet, buffer,
|
||||
geometry);
|
||||
},
|
||||
py::arg("framebuffer"), py::arg("geometry"),
|
||||
R"pbdoc(
|
||||
Construct a PioMatter object to drive panels connected to an
|
||||
Adafruit Matrix Bonnet using the RGB888 memory layout (4 bytes per
|
||||
pixel)
|
||||
|
||||
This is deprecated shorthand for `PioMatter(Colorspace.RGB888, Pinout.AdafruitMatrixBonnet, ...)`.
|
||||
)pbdoc")
|
||||
//.doc() =
|
||||
;
|
||||
|
||||
m.def(
|
||||
"AdafruitMatrixBonnetRGB888Packed",
|
||||
[](py::buffer buffer, const piomatter::matrix_geometry &geometry) {
|
||||
return make_piomatter(Colorspace::RGB888Packed,
|
||||
Pinout::AdafruitMatrixBonnet, buffer,
|
||||
geometry);
|
||||
},
|
||||
py::arg("framebuffer"), py::arg("geometry"),
|
||||
R"pbdoc(
|
||||
Construct a PioMatter object to drive panels connected to an
|
||||
Adafruit Matrix Bonnet using the RGB888 packed memory layout (3
|
||||
bytes per pixel)
|
||||
|
||||
This is deprecated shorthand for `PioMatter(Colorspace.RGB888Packed, Pinout.AdafruitMatrixBonnet, ...)`.
|
||||
)pbdoc");
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue