examples/parallel/tufty: fix ST7789 parallel display init - #55
Conversation
The Tufty parallel display example did not drive the panel. Several
independent problems, any one of which was enough to leave the screen
blank:
- rdPin was never configured as an output, so rdPin.High() was a no-op on
a floating input. RD must be held high for the ST7789 to accept writes.
- configureDisplayRotation sent CASET twice, so the row address window was
never set. It also byte swapped the CASET/RASET values and then emitted
them big endian, swapping them twice.
- machine.GP10 and friends do not exist, so the example did not compile.
Use the GPIOnn constants for the same pins.
While here, correct some other problems:
- FillRectangle allocated a 150KB framebuffer regardless of the size of
the rectangle requested, and issued RAMWR twice because setWindow had
already sent it. It now streams a small pre-filled chunk with CS held
low for the duration.
- Deassert CS before the first command, and let the bus settle before
raising CS so the final WR edge lands while the panel is still selected.
- Drop a stray Tx8("Hello World") issued before CS and DC were configured.
- Fill with blue rather than white, as the surrounding code claims.
The init sequence values are updated to match Pimoroni's 320x240 Tufty
profile, and each command is now commented with what it does in place of
the println tracing used to debug this.
Verified on hardware: displays a full screen blue fill.
CS and DC were configured as outputs by CommonInit(), but that ran AFTER piolib.NewParallel had started the PIO state machine. During PIO SM startup the state machine drives its initial (zeroed) OSR contents onto the data lines with WR toggling; with CS floating those stray bytes were being latched by the panel non-deterministically depending on line state, leaving the ST7789 in an unknown mode. The user reported this as "sometimes the display does not come up after reset, but resetting again clears it". Configure csPin, dcPin and rdPin as outputs at safe idle levels (all high) in main() before calling piolib.NewParallel, and drop the now- redundant pin setup from the top of CommonInit. Add a small settle delay in CommonInit so warm resets have time for VDDI/VCI to stabilise before the first SWRESET.
|
Hey Dave! It seems you are set to be the proud owner of a bounty I had setup a while back. The code looks A-OK. Will leave this open in case anyone wants to test it out |
|
Let me have a look at rotation today 👍 |
command() and FillRectangle's inlined RAMWR write both flipped DC to high immediately after Tx8 returned for the command byte. Tx8 can return a couple of PIO cycles before the command's final WR edge has actually landed, so DC could change mid-pulse. This intermittently corrupted the command byte and/or its latching, causing CASET/RASET to not reliably take effect (FillRectangle then painted vertical bands whose width depended on byte count rather than the requested window) and produced visible pixel corruption at colour-band boundaries. Add the same 10us settle already used before raising CS, but before flipping DC high, in both command() and FillRectangle's RAMWR path. Also fix configureDisplayRotation: with SWAP_XY (MV) set, ROW_ORDER and COL_ORDER's effect on the physical axes is swapped, so the two branches had the wrong MADCTL bit selected, producing a top/bottom mirrored image. Swap them to match. Verified on hardware with quadrant and full-width band fills: correct geometry, correct top-to-bottom order, no corruption at boundaries.
configureDisplayRotation previously only handled the landscape
rotations (0/180) correctly; portrait (90/270) inherited landscape's
MADCTL bits and hardcoded 320x239 CASET/RASET regardless of the
requested orientation. Rework it to compute width/height, MADCTL, and
the addressing window from the rotation directly, so all four
rotations produce a correctly oriented, correctly sized window.
Replace the single static blue fill in main() with a continuously
looping demo: for each of the four rotations, run through four
stages (2s pause between each):
1. full screen blue fill
2. quadrant fill (white/red/green/yellow, TL/TR/BL/BR)
3. four colored corner boxes on a black background
4. a fill "stress test" that shrinks the fill window by 2px per
iteration, alternating a palette color and black, producing
concentric 1px rings -- a visual check that FillRectangle's
addressing is pixel-accurate at small sizes and screen edges.
Verified on hardware: all four rotations render each stage correctly
oriented and sized, with no corruption.
|
@soypat @conejoninja PTAL, fill and rotation now work better IMG_2491.MOV |
|
@conejoninja I have a branch which is stacked on this one that implements the displayer interface. I haven't pushed a PR yet, but check it out https://github.com/davecheney/pio/tree/davecheney-tufty-display-improvements it needs to be squashed and rebased when this PR lands. |
|
@conejoninja i tried, and failed to push a stacked PR on top of #55, you'll have to make do with the branch for now. |
|
Or, #58, which is dirty because it contains this PR as well |
|
no rush, I'm happy this is getting done and being taken care of |
I can port the Displayer changes from #58 into this PR if you like, then you don't have to take the silly demo examples along with it. let me know what works for you |
|
don't worry, I don't need to use right now, and prefer to wait for 55+58 to be merged.
tested! @soypat can be merge this? |
With great pleasure @conejoninja |
Add the trivial SetPixel and Display stubs requested in the PR tinygo-org#55 review, plus a compile-time interface assertion. Fix the SetPixel interface comment typo.
Add the trivial SetPixel and Display stubs requested in the PR #55 review, plus a compile-time interface assertion. Fix the SetPixel interface comment typo.


The Tufty parallel display example did not drive the panel. Several independent problems, any one of which was enough to leave the screen blank:
rdPinwas never configured as an output, sordPin.High()was a no-op on a floating input. RD must be held high for the ST7789 to accept writes.configureDisplayRotationsentCASETtwice, so the row address window was never set. It also byte swapped theCASET/RASETvalues and then emitted them big endian, swapping them twice.machine.GP10and friends do not exist, so the example did not compile. Use theGPIOnnconstants for the same pins.While here, correct some other problems:
FillRectangleallocated a 150KB framebuffer regardless of the size of the rectangle requested, and issuedRAMWRtwice becausesetWindowhad already sent it. It now streams a small pre-filled chunk with CS held low for the duration.Tx8("Hello World")issued before CS and DC were configured.The init sequence values are updated to match Pimoroni's 320x240 Tufty profile, and each command is now commented with what it does in place of the
printlntracing used to debug this.Testing
Built with TinyGo 0.42.0 for
-target=tufty2040and flashed to a Tufty 2040. The display shows a full screen blue fill.