From e315eeb3be964079d4a01c5f28c749e902959c46 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Wed, 15 Jan 2025 16:43:24 -0600 Subject: [PATCH 1/4] fix gamma curve, it was all broken! --- src/include/piomatter/render.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/include/piomatter/render.h b/src/include/piomatter/render.h index c2bb58a..75934be 100644 --- a/src/include/piomatter/render.h +++ b/src/include/piomatter/render.h @@ -18,7 +18,7 @@ constexpr uint32_t command_delay = 0; struct gamma_lut { gamma_lut(double exponent = 2.2) { for (int i = 0; i < 256; i++) { - auto v = std::max(i, int(round(1023 * pow(i / 255, exponent)))); + auto v = std::max(i, int(round(1023 * pow(i / 255., exponent)))); lut[i] = v; } } From 862b7fb1d5daa3639072ab58d262de223c6ff12e Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Wed, 15 Jan 2025 20:57:25 -0600 Subject: [PATCH 2/4] Fix brightness of high numbered bitplanes Cycle counting the PIO program, each data word should take 2 PIO clocks of clock_get_hz while each repetition of the delay loop should take 1 PIO clock. However, with a non-gamma-corrected ramp, discontinuities (decreases) in brightness were seen for the bitplanes that needed the additional delay with oe enabled. Empirically, the value of 128 gives a plausible linear ramp and also fixes the big buck bunny rendering artifacts with 10 planes. It also makes the granularity of the on-time twice as fine, by being able to turn off during either the "clock on" or "clock off" phase of the shift register loading process, when the output enable time is short. Thus, with 10 bitplanes and a 64x32 panel, only the most significant bitplane needs any extra delay. --- src/include/piomatter/render.h | 47 +++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/src/include/piomatter/render.h b/src/include/piomatter/render.h index 75934be..fda6264 100644 --- a/src/include/piomatter/render.h +++ b/src/include/piomatter/render.h @@ -8,7 +8,9 @@ namespace piomatter { constexpr unsigned DATA_OVERHEAD = 3; -constexpr unsigned CLOCKS_PER_DATA = 2; +// this is ... flatly wrong!? but it's the number that makes the ramp intensity +// correct to my eye +constexpr unsigned CLOCKS_PER_DATA = 128; constexpr unsigned DELAY_OVERHEAD = 5; constexpr unsigned CLOCKS_PER_DELAY = 1; @@ -159,10 +161,21 @@ void protomatter_render_rgb10(std::vector &result, result.push_back(d); }; - auto do_data_delay = [&](uint32_t d, uint32_t delay) { + int32_t active_time; + + auto do_data_active = [&active_time, &do_data](uint32_t d) { + bool active = active_time > 0; + active_time--; + d |= active ? pinout::oe_active : pinout::oe_inactive; + do_data(d); + }; + + auto do_data_delay = [&prep_data, &do_data, &do_delay](uint32_t d, + int32_t delay) { prep_data(1); do_data(d); - do_delay(delay); + if (delay > 0) + do_delay(delay); }; auto calc_addr_bits = [](int addr) { @@ -184,11 +197,10 @@ void protomatter_render_rgb10(std::vector &result, return data; }; - auto add_pixels = [&do_data, &result](uint32_t addr_bits, bool r0, bool g0, - bool b0, bool r1, bool g1, bool b1, - bool active) { - uint32_t data = - (active ? pinout::oe_active : pinout::oe_inactive) | addr_bits; + auto add_pixels = [&do_data_active, &result](uint32_t addr_bits, bool r0, + bool g0, bool b0, bool r1, + bool g1, bool b1) { + uint32_t data = addr_bits; if (r0) data |= (1 << pinout::PIN_RGB[0]); if (g0) @@ -202,8 +214,8 @@ void protomatter_render_rgb10(std::vector &result, if (b1) data |= (1 << pinout::PIN_RGB[5]); - do_data(data); - do_data(data | pinout::clk_bit); + do_data_active(data); + do_data_active(data | pinout::clk_bit); }; int last_bit = 0; @@ -230,7 +242,7 @@ void protomatter_render_rgb10(std::vector &result, // the shortest /OE we can do is one DATA_OVERHEAD... // TODO: should make sure desired duration of MSB is at least // `pixels_across` - uint32_t desired_duration = 1 << last_bit; + active_time = 1 << last_bit; last_bit = bit; prep_data(2 * pixels_across); @@ -247,17 +259,12 @@ void protomatter_render_rgb10(std::vector &result, auto g1 = pixel1 & g; auto b1 = pixel1 & b; - add_pixels(addr_bits, r0, g0, b0, r1, g1, b1, - x < desired_duration); + add_pixels(addr_bits, r0, g0, b0, r1, g1, b1); } - // hold /OE low until desired time has elapsed to illuminate the - // LAST line - int remain = desired_duration - pixels_across; - if (remain > 0) { - do_data_delay(addr_bits | pinout::oe_active, - remain * CLOCKS_PER_DATA - DELAY_OVERHEAD); - } + do_data_delay(addr_bits | pinout::oe_active, + active_time * CLOCKS_PER_DATA / CLOCKS_PER_DELAY - + DELAY_OVERHEAD); do_data_delay(addr_bits | pinout::oe_inactive, pinout::post_oe_delay); From 4ef2d552ceffefaf269ac979647aa33147338e2c Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Wed, 15 Jan 2025 21:54:51 -0600 Subject: [PATCH 3/4] show fps in playframes --- examples/playframes.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/examples/playframes.py b/examples/playframes.py index 37e2874..7609b2b 100644 --- a/examples/playframes.py +++ b/examples/playframes.py @@ -11,6 +11,7 @@ import glob import sys +import time import adafruit_raspberry_pi5_piomatter import numpy as np @@ -20,10 +21,15 @@ geometry = adafruit_raspberry_pi5_piomatter.Geometry(width=64, height=32, n_addr_lines=4, rotation=adafruit_raspberry_pi5_piomatter.Orientation.Normal) framebuffer = np.asarray(Image.open(images[0])) + 0 # Make a mutable copy +nimages = len(images) matrix = adafruit_raspberry_pi5_piomatter.AdafruitMatrixBonnetRGB888Packed(framebuffer, geometry) while True: + t0 = time.monotonic() for i in images: - print(i) framebuffer[:] = np.asarray(Image.open(i)) matrix.show() + t1 = time.monotonic() + dt = t1 - t0 + fps = nimages/dt + print(f"{nimages} frames in {dt}s, {fps}fps") From eddb20cb42b069930811f6a1cd62ab70a44573b2 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Wed, 15 Jan 2025 21:57:01 -0600 Subject: [PATCH 4/4] ignore generated files --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index d0fffe1..83eb938 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ ./*.pio.h protodemo ./build +*.egg-info