Skip to content

examples/parallel/tufty: add Displayer, faster bus, animated demo - #1

Closed
davecheney wants to merge 1 commit into
davecheney-dfc/tufty-pio-displayfrom
davecheney-tufty-display-improvements
Closed

davecheney wants to merge 1 commit into
davecheney-dfc/tufty-pio-displayfrom
davecheney-tufty-display-improvements

Conversation

@davecheney

Copy link
Copy Markdown
Owner

Builds on tinygo-org#55. The example now works, but was pinned to 1 MHz and could only fill rectangles. This adds a real Displayer implementation, raises the PIO bus rate to a hardware-verified 15 MHz, and swaps the solid blue fill for something that actually exercises the driver.

Approach

  • ST7789 now implements a small local Displayer interface (Size, SetPixel, Display) backed by a static .bss framebuffer ([320*240*2]byte, ~154 KB, high-byte-first RGB565). A compile-time assertion pins the interface. No dependency on tinygo.org/x/drivers — deliberately keeping go.mod zero-require; the interface is trivial and matching upstream can happen later if we ever pull the driver into the module.
  • Framebuffer is stored as []byte and streamed via Tx8 rather than []uint16/Tx16, because piolib.NewParallel uses BitsPerPull: 8 and shifts LSB-first, so 16-bit words would silently drop the high byte. Storing high-byte-first + Tx8 matches RAMCTRL byte order.
  • busBaud is now a named constant at 15 MHz. The PIO parallel program is 3 instructions, so PIO SM clock = 3 x baud = 45 MHz. On RP2040 that gives an actual WR cycle of ~66.6 ns, just inside the ST7789 8080-II 66 ns minimum, and Dave confirmed it visually. Display() runs at ~40 FPS for a bouncing-rects demo and ~14 FPS for a plasma effect on real hardware.
  • The demo cycles three animations (bouncing rectangles, a slowly-zooming Mandelbrot in Q6.26 fixed-point with no math, and a sin-LUT plasma) under all four rotations, printing per-run FPS over UART.

Notes for review

  • Display() sends the full framebuffer as a single RAMWR stream, followed by a 10us settle before dc.High(). This matches the same Tx8-return / WR-edge race we fixed in command() and FillRectangle in examples/parallel/tufty: fix ST7789 parallel display init tinygo-org/pio#55: helperPushUntilStall returns on TxStall, which fires a couple of PIO cycles before the last WR falling edge propagates through the pads, so flipping DC immediately can corrupt the trailing byte.
  • Pin configuration (CS/DC/RD outputs at idle-high) is moved before piolib.NewParallel in main(), and removed from CommonInit. This closes the intermittent init race where PIO SM startup would clock zeroed OSR contents onto floating pins while CS was still an input; the panel would occasionally latch stray bytes and refuse to init.
  • The rotation-cycling outer loop was added on rebase to sit alongside the geometry/rotation rework in examples/parallel/tufty: fix ST7789 parallel display init tinygo-org/pio#55; it exercises configureDisplayRotation end-to-end. FPS is unchanged vs the pre-rebase tip.

Verification

Hardware verified on a real Tufty 2040:

Demo FPS
bouncing rects ~40
Mandelbrot ~0.9
plasma ~14

gofmt -l . clean. tinygo build -target=tufty2040 succeeds. go.mod remains zero-require.

Implement SetPixel and Display on ST7789 backed by a fixed
[320*240*2]byte framebuffer in .bss (~154KB, ~58% of RP2040 SRAM). The
buffer is stored high-byte-first so it streams straight to the panel
(RAMCTRL=0xC0, big-endian) via Tx8 with no per-frame byte swap. Add a
compile-time assertion that ST7789 satisfies the local Displayer
interface, whose signatures match tinygo.org/x/drivers.Displayer so
downstream code can substitute that interface trivially without adding
the dependency here.

Expose the PIO bus rate as a top-level busBaud constant. The PIO
program is three instructions long, so the state machine clock runs at
3 * busBaud. The ST7789 8080-II parallel interface has a 66ns minimum
write cycle (~15.15 MHz); run at 15 MHz, verified visually clean on
Tufty 2040 hardware.

Replace the solid blue fill in main() with a cycling animated demo:
bouncing rectangles, a Mandelbrot fractal zoom (fixed-point Q6.26), and
a sin-LUT plasma. Each demo reports measured FPS over UART. Measured
on Tufty 2040 at 15 MHz: bouncing rects ~40 FPS (bus-saturated
full-frame transfer), plasma ~14 FPS, mandelbrot ~0.8 FPS.
@davecheney

Copy link
Copy Markdown
Owner Author

Reopening against tinygo-org/pio; stacked on tinygo-org#55 there.

@davecheney davecheney closed this Sep 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant