Compare commits

...

42 commits

Author SHA1 Message Date
Limor "Ladyada" Fried
abd7f18fc5
Merge pull request #55 from FoamyGuy/fix_active3_pinouts
Some checks failed
Pip / build (ubuntu-24.04-arm, 3.11) (push) Has been cancelled
Pip / build (ubuntu-24.04-arm, 3.12) (push) Has been cancelled
Pip / build (ubuntu-24.04-arm, 3.13) (push) Has been cancelled
pre-commit / pre-commit (push) Has been cancelled
Wheels / Build SDist (push) Has been cancelled
Wheels / Wheels on ubuntu-24.04-arm (push) Has been cancelled
Wheels / Upload release (push) Has been cancelled
Fix active3 pinouts swapped pins R and B
2025-08-19 15:04:30 -04:00
foamyguy
b057531ef1 standard pinout for active3 simpletest 2025-08-01 11:04:49 -05:00
foamyguy
d5a979df18 correct active3 pinouts 2025-08-01 11:03:38 -05:00
foamyguy
5d46945596
Merge pull request #49 from FoamyGuy/triple_matrix_stuff
Some checks failed
Pip / build (ubuntu-24.04-arm, 3.11) (push) Has been cancelled
Pip / build (ubuntu-24.04-arm, 3.12) (push) Has been cancelled
Pip / build (ubuntu-24.04-arm, 3.13) (push) Has been cancelled
pre-commit / pre-commit (push) Has been cancelled
Wheels / Build SDist (push) Has been cancelled
Wheels / Wheels on ubuntu-24.04-arm (push) Has been cancelled
Wheels / Upload release (push) Has been cancelled
active3 simpletest
2025-07-15 08:13:00 -05:00
foamyguy
263f530375 more spiral, more rainbow!! 2025-07-14 17:14:34 -05:00
foamyguy
e9a07d4942 active3 simpletest 2025-07-10 08:46:48 -05:00
foamyguy
792955cf1a
Merge pull request #39 from FoamyGuy/cli_examples_serpentine_fix
add serpentine arg to CLI examples
2025-03-18 13:28:12 -05:00
foamyguy
b35b3d35bb add serpentine arg to CLI examples 2025-03-18 12:20:42 -05:00
foamyguy
0225964f24
Merge pull request #36 from adafruit/large-xfer-workaround
Restore large xfer workaround & actually report errors in pio_sm_xfer_data
2025-03-17 13:47:18 -05:00
89dd515ae5 restore large xfer workaround
sadly, it's not possible to gracefully switch from large to blocked
xfers, further xfer ioctls fail after the first large xfer fails.
2025-03-17 11:29:28 -05:00
fc7295eb74 Actually report errors in pio_sm_xfer_data 2025-03-17 09:26:21 -05:00
b19b6c7cb0
Merge pull request #31 from FoamyGuy/resample_methods
add a way to specify resample method for xdisplay_mirror
2025-03-13 19:38:50 -05:00
foamyguy
fe7c44be88 Merge branch 'refs/heads/main' into resample_methods
# Conflicts:
#	examples/xdisplay_mirror.py
2025-03-13 16:22:30 -05:00
foamyguy
bf3409d92b
Merge pull request #32 from FoamyGuy/brightness_arg
add brightness argument to virtualdisplay and xdisplay_mirror
2025-03-13 16:08:05 -05:00
foamyguy
f80ae1e772
Merge pull request #33 from adafruit/reduce-post-addr-delay
Reduce post addr delay
2025-03-13 16:01:57 -05:00
a2827b6e28 remove debug code & a stray semicolon 2025-03-13 15:33:53 -05:00
db9a925103 tweak docs about temporal planes. 2025-03-13 10:43:08 -05:00
8b9f7d7006 Enable 3 & 5 temporal planes
By doing a bit more math we can create a sensible schedule for
these situations.  A 6/3 schedule gives a very nice 150fps with
temporal shimmer at 50Hz
2025-03-13 10:40:31 -05:00
450e3b61d3 change how the C smoke test program builds 2025-03-13 10:14:52 -05:00
a565d17654 Remove "xfer_data_large" workaround
it's not needed with updated piolib
2025-03-13 09:56:47 -05:00
57c66624ce update piolib
This diff looks bigger than it is because we'd previously
reformatted piolib sources via pre-commit.
2025-03-13 09:54:46 -05:00
8e69092434 reduce post_addr_delay
The original post_addr_delay (the delay after changing the ABCD(E)
address lines) was pretty long, for two reasons: First, the initial
timing numbers were chosen conservatively without regard to
performance; and second, because they were originally chosen
assuming a high PIO clock rate but we lowered the PIO clock to
2.7MHz.

Together this made the delay a huge 185us!

The new value of 5 is about 1.8us instead; this gives a nice
increase in FPS from 88 to 120 on my 128x128 (4 lane, 6/2 plane)
setup, without any visible artifacts.
2025-03-13 09:34:19 -05:00
foamyguy
9465e133b8 add brightness argument to virtualdisplay and xdisplay_mirror 2025-03-12 14:43:08 -05:00
foamyguy
270c8b854c use click.Choice 2025-03-12 11:47:21 -05:00
foamyguy
9a4ed78ca6 add a way to specify resample method for xdisplay_mirror 2025-03-12 10:55:21 -05:00
0a9e578944
Merge pull request #30 from adafruit/jepler-patch-1
Update virtualdisplay.py
2025-03-12 09:18:36 -05:00
499d5c56a7
Update virtualdisplay.py
remove a rogue comment
2025-03-12 09:02:43 -05:00
ac6b65f23b sssshhhh! 2025-03-11 13:03:13 -05:00
foamyguy
0e717476c4
Merge pull request #29 from FoamyGuy/xdisplay_mirror_example
add xdisplay_mirror
2025-03-11 12:26:37 -05:00
foamyguy
5961685d45 add xdisplay_mirror, remove virtualdisplay_keyboard. update virtualdisplay for new args. remove unused function from fbmirror 2025-03-11 10:21:11 -05:00
foamyguy
8d3355fca3
Merge pull request #27 from adafruit/make-package
Allow defining geometries (including more than 2 color lanes) via Python code
2025-03-11 09:40:55 -05:00
6d3e236828 This improves the doc situation
There are still some problems & some build warnings but at least the
sphinx docs have useful content now.
2025-03-11 09:36:43 -05:00
b26f8dc48d Another file to rename
docs are wrong and this might be part of why .. but not all
2025-03-11 09:26:09 -05:00
34d8cd1e4d Update copyright year 2025-03-11 09:26:09 -05:00
3fb93d310b Finish removing deprecated aliases 2025-03-11 09:26:09 -05:00
f7a247eeae this is a private variable 2025-03-11 09:19:00 -05:00
908766eeb8 remove deprecated aliases 2025-03-11 09:07:15 -05:00
372a158bd7 Make n_temporal_planes, n_lanes "standard" argmuents
& add the simple multilane mapper to examples that use click
2025-03-11 08:53:07 -05:00
8ae0fea913 move simple_multilane_mapper into package 2025-03-11 08:48:48 -05:00
7c26836a68
Merge pull request #24 from adafruit/dependabot/github_actions/pypa/cibuildwheel-2.23.0
Bump pypa/cibuildwheel from 2.22.0 to 2.23.0
2025-03-10 16:37:49 -05:00
foamyguy
1d9d652cf3 update piomatter_click import 2025-03-10 10:19:03 -05:00
dependabot[bot]
8d967c26e1
Bump pypa/cibuildwheel from 2.22.0 to 2.23.0
Bumps [pypa/cibuildwheel](https://github.com/pypa/cibuildwheel) from 2.22.0 to 2.23.0.
- [Release notes](https://github.com/pypa/cibuildwheel/releases)
- [Changelog](https://github.com/pypa/cibuildwheel/blob/main/docs/changelog.md)
- [Commits](https://github.com/pypa/cibuildwheel/compare/v2.22.0...v2.23.0)

---
updated-dependencies:
- dependency-name: pypa/cibuildwheel
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-03 09:52:57 +00:00
35 changed files with 2663 additions and 2606 deletions

View file

@ -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*"

View file

@ -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"

View file

@ -0,0 +1,8 @@
HUB75 matrix driver for Raspberry Pi 5 using PIO
------------------------------------------------
.. autosummary::
:toctree: _generate
:recursive:
adafruit_blinka_raspberry_pi5_piomatter

View file

@ -1 +0,0 @@
.. automodule:: adafruit_blinka_raspberry_pi5_piomatter

View file

@ -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

View file

@ -3,5 +3,6 @@
# SPDX-License-Identifier: Unlicense
sphinx
sphinx-autoapi
sphinx-rtd-theme
sphinxcontrib-jquery

View file

@ -17,6 +17,7 @@ import numpy as np
import adafruit_blinka_raspberry_pi5_piomatter as piomatter
import adafruit_blinka_raspberry_pi5_piomatter.click as piomatter_click
from adafruit_blinka_raspberry_pi5_piomatter.pixelmappers import simple_multilane_mapper
with open("/sys/class/graphics/fb0/virtual_size") as f:
screenx, screeny = [int(word) for word in f.read().split(",")]
@ -34,31 +35,16 @@ with open("/sys/class/graphics/fb0/stride") as f:
linux_framebuffer = np.memmap('/dev/fb0',mode='r', shape=(screeny, stride // bytes_per_pixel), dtype=dtype)
def make_pixelmap_multilane(width, height, n_addr_lines, n_lanes):
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)
print(m)
return m
@click.command
@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(n_lanes=2, n_temporal_planes=4)
@piomatter_click.standard_options
def main(xoffset, yoffset, width, height, serpentine, rotation, pinout, n_planes, n_temporal_planes, n_addr_lines, n_lanes):
if n_lanes != 2:
pixelmap = make_pixelmap_multilane(width, height, n_addr_lines, n_lanes)
pixelmap = simple_multilane_mapper(width, height, n_addr_lines, n_lanes)
geometry = piomatter.Geometry(width=width, height=height, n_planes=n_planes, n_addr_lines=n_addr_lines, n_temporal_planes=n_temporal_planes, n_lanes=n_lanes, map=pixelmap)
else:
geometry = piomatter.Geometry(width=width, height=height, n_planes=n_planes, n_addr_lines=n_addr_lines, n_temporal_planes=n_temporal_planes, rotation=rotation)
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)

View file

@ -43,6 +43,7 @@ import PIL.Image as Image
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(",")]
@ -66,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)

View file

@ -15,6 +15,7 @@ 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
@ -22,29 +23,14 @@ n_addr_lines = 5
height = n_lanes << n_addr_lines
pen_radius = 1
def make_pixelmap_multilane(width, height, n_addr_lines, n_lanes):
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
canvas = Image.new('RGB', (width, height), (0, 0, 0))
draw = ImageDraw.Draw(canvas)
pixelmap = make_pixelmap_multilane(width, height, n_addr_lines, n_lanes)
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,
pinout=piomatter.Pinout.Active3BGR,
framebuffer=framebuffer,
geometry=geometry)
@ -80,7 +66,7 @@ def darken_color(hex_color, darkness_factor):
return darkened_hex_color
step_count = 4
step_count = 8
darkness_factor = 0.5
clearing = False

View 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")

View file

@ -20,18 +20,17 @@ 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 click
import numpy as np
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
@ -39,25 +38,38 @@ import adafruit_blinka_raspberry_pi5_piomatter.click as piomatter_click
@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()

View file

@ -1,175 +0,0 @@
#!/usr/bin/python3
"""
Display a (possibly scaled) X session to a matrix
The display runs until the graphical program exits.
Raw keyboard inputs are read from stdin and then injected into the running programs session with xdotool.
For help with commandline arguments, run `python virtualdisplay.py --help`
This needs additional software to be installed (besides a graphical program to run). At a minimum you have to
install a virtual display server program (xvfb) and the pyvirtualdisplay importable Python module:
$ sudo apt install -y xvfb xdotool
$ pip install pyvirtualdisplay
Here's an example for running an emulator using a rom stored in "/tmp/snesrom.smc" on a virtual 128x128 panel made from 4 64x64 panels:
$ python virtualdisplay_keyboard.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 os
import selectors
import shlex
import string
import sys
import termios
import tty
from subprocess import Popen, run
import click
import numpy as np
import piomatter_click
from pyvirtualdisplay.smartdisplay import SmartDisplay
import adafruit_blinka_raspberry_pi5_piomatter as piomatter
keyboard_debug = False
keys_down = set()
basic_characters = string.ascii_letters + string.digits
key_map = {
# https://gitlab.com/nokun/gestures/-/wikis/xdotool-list-of-key-codes
b' ': "space",
b'/': "slash",
b'\\': "backslash",
b"'": "apostrophe",
b'\x7f': "BackSpace",
b'.': "period",
b',': "comma",
b'\t': "Tab",
b'\r': "Return",
b'!': "exclam",
b'?': "question",
b'@': "at",
b'<': "less",
b'>': "greater",
b'=': "equal",
b';': "semicolon",
b':': "colon",
b'+': "plus",
b'-': "minus",
b'*': "asterisk",
b'(': "parenleft",
b')': "parenright",
b'&': "ampersand",
b'%': "percent",
b'$': "dollar",
b'#': "numbersign",
b'\x1b[A': "Up",
b'\x1b[B': "Down",
b'\x1b[C': "Right",
b'\x1b[D': "Left",
b'\x1b': "Escape",
b'^': "caret",
b'[': "bracketleft",
b']': "bracketright",
b'{': "braceleft",
b'}': "braceright",
b'_': "underscore",
#b'': "",
}
ctrl_modified_range = (1, 26)
@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("--use-xauth/--no-use-xauth", help="If a Xauthority file should be created", default=False)
@click.option("--ctrl-c-interrupt/--no-ctrl-c-interrupt", help="If Ctrl+C should be handled as an interrupt.", default=True)
@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, ctrl_c_interrupt, command):
def handle_key_event(evt_data):
if evt_data in key_map.keys():
keys_down.add(key_map[evt_data])
run(["xdotool", "keydown", key_map[evt_data]], env=disp.env())
elif evt_data.decode() in basic_characters:
run(["xdotool", "keydown", f"{evt_data.decode()}"], env=disp.env())
keys_down.add(evt_data.decode())
elif ctrl_modified_range[0] <= int.from_bytes(evt_data) <= ctrl_modified_range[1]:
if evt_data == b'\x03' and ctrl_c_interrupt:
raise KeyboardInterrupt
keys_down.add("Control_L")
run(["xdotool", "keydown", "Control_L"], env=disp.env())
modified_key = chr(int.from_bytes(evt_data) + 96)
if keyboard_debug:
print(f"ctrl modified {modified_key}")
keys_down.add(modified_key)
run(["xdotool", "keydown", modified_key], env=disp.env())
elif len(evt_data) > 1:
if keyboard_debug:
print("recvd multiple")
for char_val in evt_data:
if keyboard_debug:
print(f"{char_val} {chr(char_val)}")
char_bytes = char_val.to_bytes(1)
handle_key_event(char_bytes)
else:
print(f"unknown input data: {evt_data}")
old_settings = termios.tcgetattr(sys.stdin)
selector = selectors.DefaultSelector()
selector.register(fileobj=sys.stdin, events=selectors.EVENT_READ)
tty.setraw(sys.stdin.fileno())
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)
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)
try:
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)
# print(disp.env())
if img is None:
continue
img = img.resize((width, height))
framebuffer[:, :] = np.array(img)
matrix.show()
event_count = 0
for key, __ in selector.select(timeout=0):
event_count += 1
# read up 3 bytes, so we full data for arrow keys
kbd_data = os.read(key.fileobj.fileno(), 3)
if keyboard_debug:
print(kbd_data)
handle_key_event(kbd_data)
# no kbd events, so keyup all keys
if event_count == 0:
for key in keys_down:
run(["xdotool", "keyup", key], env=disp.env())
keys_down.clear()
finally:
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_settings)
if __name__ == '__main__':
main()

View 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()

View file

@ -18,5 +18,6 @@ lint.extend-select = [
]
lint.extend-ignore = [
"E501", # Line too long
"RUF002", # Yes I meant to type 'multiplication sign'!
]
target-version = "py311"

20
src/CMakeLists.txt Normal file
View 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)

View file

@ -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 $< $@

View file

@ -1,7 +1,22 @@
"""
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 (
AdafruitMatrixBonnetRGB565,
AdafruitMatrixBonnetRGB888,
AdafruitMatrixBonnetRGB888Packed,
Colorspace,
Geometry,
Orientation,
@ -10,9 +25,6 @@ from ._piomatter import (
)
__all__ = [
'AdafruitMatrixBonnetRGB565',
'AdafruitMatrixBonnetRGB888',
'AdafruitMatrixBonnetRGB888Packed',
'Colorspace',
'Geometry',
'Orientation',

View file

@ -10,7 +10,7 @@ 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)]
@ -26,9 +26,9 @@ class PybindEnumChoice(click.Choice):
r = getattr(self.enum, value)
return r
def validate_temporal_planes(ctx, param, value):
if value not in (0, 2, 4):
raise click.BadParameter("must be 0, 2, or 4")
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(
@ -40,9 +40,9 @@ def standard_options(
rotation=piomatter.Orientation.Normal,
pinout=piomatter.Pinout.AdafruitMatrixBonnet,
n_planes=10,
n_temporal_planes=None,
n_temporal_planes=0,
n_addr_lines=4,
n_lanes=None,
n_lanes=2,
) -> Callable[[], None]:
"""Add standard commandline flags, with the defaults given
@ -69,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:
@ -77,13 +77,13 @@ 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)
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)
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:

View 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

View file

@ -134,47 +134,27 @@ schedule_sequence make_temporal_dither_schedule(int n_planes,
if (n_temporal_planes >= n_planes) {
throw std::range_error("n_temporal_planes can't exceed n_planes");
}
if (n_temporal_planes != 2 && n_temporal_planes != 4) {
// the code can generate a schedule for 8 temporal planes, but it
// flickers intolerably
throw std::range_error("n_temporal_planes must be 0, 1, 2, or 4");
}
int n_real_planes = n_planes - n_temporal_planes;
schedule base_sched;
for (int j = 0; j < n_real_planes; j++) {
base_sched.emplace_back(
9 - j, (1 << (n_temporal_planes + n_real_planes - j - 1)) /
n_temporal_planes);
}
schedule_sequence result;
auto add_sched = [&result, &base_sched](int plane, int count) {
auto sched = base_sched;
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(n_real_planes + i, 1 << (n_temporal_planes - i - 1));
add_sched(i, n_real_planes + i, 1 << (n_temporal_planes - i - 1));
}
#if 0
std::vector<uint32_t> counts(10, 0);
for (auto s : result) {
for(auto t: s) {
counts[t.shift] += t.active_time;
}
}
for (auto s : counts) {
printf("%d ", s);
}
printf("\n");
#endif
return rescale_schedule(result, pixels_across);
;
}
struct matrix_geometry {

View file

@ -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,29 +37,10 @@ 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[] = {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 = 500;
};
struct active3_pinout_bgr {
static constexpr pin_t PIN_RGB[] = {11, 27, 7, 8, 9, 10, 12, 5, 6,
19, 13, 20, 14, 2, 3, 26, 16, 21};
static constexpr pin_t PIN_ADDR[] = {22, 23, 24, 25, 15};
@ -75,7 +56,26 @@ struct active3_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_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

View file

@ -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;
};
@ -62,7 +84,11 @@ struct piomatter : piomatter_base {
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 &bufseq = buffers[buffer_idx];
bufseq.resize(geometry.schedules.size());
@ -75,6 +101,7 @@ struct piomatter : piomatter_base {
old_active_time = geometry.schedules[i].back().active_time;
}
manager.put_filled_buffer(buffer_idx);
return 0;
}
~piomatter() {
@ -106,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");
}
@ -188,8 +215,15 @@ struct piomatter : piomatter_base {
const auto &data = cur_buf[seq_idx];
auto datasize = sizeof(uint32_t) * data.size();
auto dataptr = const_cast<uint32_t *>(&data[0]);
pio_sm_xfer_data_large(pio, sm, PIO_DIR_TO_SM, datasize,
dataptr);
// 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);
@ -209,6 +243,7 @@ struct piomatter : piomatter_base {
matrix_geometry geometry;
colorspace converter;
std::thread blitter_thread;
std::atomic<int> pending_error_errno;
};
} // namespace piomatter

View file

@ -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
};

View file

@ -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 {

View file

@ -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

View 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

View file

@ -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;

View file

@ -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);

View file

@ -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

View file

@ -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

View file

@ -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);
}

View file

@ -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;
@ -102,28 +103,42 @@ static void print_dither_schedule(const piomatter::schedule_sequence &ss) {
}
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;
test_simple_dither_schedule(5, 1);
test_temporal_dither_schedule(5, 1, 0);
test_temporal_dither_schedule(5, 1, 2);
test_temporal_dither_schedule(5, 1, 4);
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);
@ -131,6 +146,7 @@ int main(int argc, char **argv) {
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);
@ -140,6 +156,8 @@ int main(int argc, char **argv) {
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;

View file

@ -18,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; }
};
@ -119,18 +126,7 @@ PYBIND11_MODULE(_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>(
@ -177,9 +173,11 @@ The number of pixels in the shift register is automatically computed from these
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. The acceptable values
are 0 (the default), 2, and 4. A higher setting can increase FPS at the cost of
slightly increasing the variation of brightness across subsequent frames.
``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:
@ -267,16 +265,16 @@ If 2 or 3 connectors are used, then there are 4 or 6 lanes.
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"),
@ -290,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");
}