Skip to content

ili9325: add ILI9325 driver for 8-bit parallel displays - #926

Merged
deadprogram merged 3 commits into
tinygo-org:devfrom
davecheney:ili9325
Oct 27, 2026
Merged

deadprogram merged 3 commits into
tinygo-org:devfrom
davecheney:ili9325

Conversation

@davecheney

@davecheney davecheney commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

This adds a driver for the ILI9325 TFT controller on an 8-bit parallel bus, with an example for the ITead ITDB02-2.8 Arduino shield.

IMG_2788.MOV

@davecheney
davecheney marked this pull request as ready for review October 24, 2026 05:12
This adds a driver for the ILI9325 TFT controller on an 8-bit parallel
bus, with an example for the ITead ITDB02-2.8 Arduino shield.
This adds an example that draws the Lorenz attractor on the ITead
ITDB02-2.8 shield.
davecheney added a commit to davecheney/tinygo that referenced this pull request Oct 25, 2026
The Mega and Zero board files name the analog pins ADC0 and up, and drivers examples such as the ITDB02 shield demo in tinygo-org/drivers#926 use those names. The Due now has the same names as aliases of A0 to A11.
davecheney added a commit to davecheney/drivers that referenced this pull request Oct 25, 2026
This ports the Lorenz example from the ILI9325 PR (tinygo-org#926) to the Adafruit 2.2" TFT on an Arduino Due, to show a moving drawing on a fast SPI bus. Only the display setup changed. It was tested on a Due, where it is upright and rotates.

It uses SPI at 42 MHz, which worked on the test wiring but is above the ILI9341 rating of 10 MHz. The README says to lower the clock if the display shows noise. Like the other Due example, it needs the arduino-due target from davecheney/tinygo, so it is not in smoketest.sh.

@deadprogram deadprogram left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this. The smoketest builds and go vet pass locally, and the rotation mapping in setWindow looks right to me. A few small things inline.

Comment thread ili9325/ili9325.go Outdated
cx, cy = x0, y0
mode = entryModeIDX | entryModeIDY
}
d.writeReg(ENTRY_MODE, entryModeBGR|mode)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ENTRY_MODE only depends on rotation, so this could be written once in SetRotation instead of on every setWindow call. SetPixel currently does 8 register writes per pixel, so this would help the lorenz example a bit.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 74c77c1. SetRotation now writes ENTRY_MODE once, so setWindow does 7 register writes per call, not 8.

Comment thread ili9325/portbus_atsamd21.go Outdated
Comment thread ili9325/ili9325.go Outdated
Comment thread ili9325/ili9325.go Outdated
Comment thread ili9325/ili9325.go
@davecheney

Copy link
Copy Markdown
Contributor Author

Thanks for this. The smoketest builds and go vet pass locally, and the rotation mapping in setWindow looks right to me. A few small things inline.

Thanks for the review. I’ll check them today. Especially rotation as I simply turned the board til it was the right way up for me :)

Review of tinygo-org#926 asked for ENTRY_MODE to be written once per rotation, for clearer errors and doc comments, and for a check of the WR pulse width against the ILI9325 AC characteristics.

The datasheet V0.43 13.4.1 gives PWLW and PWHW 50 ns min. A DSLogic capture at 400 MS/s on an Arduino M0 at -opt=2 showed WR high of 25 ns between the two bytes of a pixel. WR low was in spec. At the default -opt=z each register store is a call, so the pulses were long only by accident. Inline asm padding gives the same minimum at every opt level. After the change the minimums are 58 ns low and 65 ns high. Traces: https://raw.githubusercontent.com/davecheney/tinygo/pr-assets/drivers926-wr-timing.png

@deadprogram deadprogram left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the addition @davecheney now squash/merging.

@deadprogram
deadprogram merged commit 3b24140 into tinygo-org:dev Oct 27, 2026
1 check passed
@davecheney
davecheney deleted the ili9325 branch October 27, 2026 11:13
davecheney added a commit to davecheney/tinygo that referenced this pull request Oct 28, 2026
At -opt=z every function has minsize, and LLVM ignores inlinehint in a minsize caller. A register write through a pointer, such as a Register32 held in a driver struct, then becomes a call to Set. The inlined store is also smaller than the call.

Measured with the ili9325 Lorenz example from tinygo-org/drivers#926 on an Arduino M0 (SAMD21, 48 MHz). The flash sizes include timing code. The speeds are the mean of three readings.

| opt | flash before | flash after | FillScreen before | FillScreen after | Lorenz steps/s before | Lorenz steps/s after |
|---|---|---|---|---|---|---|
| z | 24104 | 24024 | 137.9 ms | 34.2 ms | 3125 | 3667 |
| s | 24196 | 24148 | 29.2 ms | 29.2 ms | 3972 | 3943 |
| 1 | 33620 | 33620 | 29.3 ms | 29.3 ms | 4117 | 4134 |
| 2 | 33092 | 33092 | 29.2 ms | 29.5 ms | 4156 | 4116 |

The z before values are from the same dev commit. The s, 1 and 2 before speeds are from an earlier dev commit.

At -opt=z, FillScreen is still 5 ms slower than at -opt=s, with no calls to runtime/volatile left. That difference comes from minsize in other code and is not part of this change.

Fixes tinygo-org#6063
davecheney added a commit to davecheney/tinygo that referenced this pull request Oct 28, 2026
At -opt=z every function has minsize, and LLVM ignores inlinehint in a minsize caller. A register write through a pointer, such as a Register32 held in a driver struct, then becomes a call to Set. The inlined store is also smaller than the call.

Measured with the ili9325 Lorenz example from tinygo-org/drivers#926 on an Arduino M0 (SAMD21, 48 MHz). The flash sizes include timing code. The speeds are the mean of three readings.

-opt=z: flash 24104 to 24024 bytes, FillScreen 137.9 to 34.2 ms, Lorenz 3125 to 3667 steps/s.

-opt=s: flash 24196 to 24148 bytes, FillScreen 29.2 to 29.2 ms, Lorenz 3972 to 3943 steps/s.

-opt=1: flash 33620 to 33620 bytes, FillScreen 29.3 to 29.3 ms, Lorenz 4117 to 4134 steps/s.

-opt=2: flash 33092 to 33092 bytes, FillScreen 29.2 to 29.5 ms, Lorenz 4156 to 4116 steps/s.

The z before values are from the same dev commit. The s, 1 and 2 before speeds are from an earlier dev commit.

At -opt=z, FillScreen is still 5 ms slower than at -opt=s, with no calls to runtime/volatile left. That difference comes from minsize in other code and is not part of this change.

Fixes tinygo-org#6063
davecheney added a commit to davecheney/tinygo that referenced this pull request Oct 28, 2026
At -opt=z every function has minsize, and LLVM ignores inlinehint in a minsize caller. A register write through a pointer, such as a Register32 held in a driver struct, then becomes a call to Set. The inlined store is also smaller than the call.

Measured with the ili9325 Lorenz example from tinygo-org/drivers#926 on an Arduino M0 (SAMD21, 48 MHz). The flash sizes include timing code. The speeds are the mean of three readings.

| opt | flash before | flash after | FillScreen before | FillScreen after | Lorenz steps/s before | Lorenz steps/s after |
|---|---|---|---|---|---|---|
| z | 24104 | 24024 | 137.9 ms | 34.2 ms | 3125 | 3667 |
| s | 24196 | 24148 | 29.2 ms | 29.2 ms | 3972 | 3943 |
| 1 | 33620 | 33620 | 29.3 ms | 29.3 ms | 4117 | 4134 |
| 2 | 33092 | 33092 | 29.2 ms | 29.5 ms | 4156 | 4116 |

The z before values are from the same dev commit. The s, 1 and 2 before speeds are from an earlier dev commit.

At -opt=z, FillScreen is still 5 ms slower than at -opt=s, with no calls to runtime/volatile left. That difference comes from minsize in other code and is not part of this change.

Fixes tinygo-org#6063
davecheney added a commit to davecheney/tinygo that referenced this pull request Oct 28, 2026
At -opt=z every function has minsize, and LLVM ignores inlinehint in a minsize caller. A register write through a pointer, such as a Register32 held in a driver struct, then becomes a call to Set. The inlined store is also smaller than the call.

Measured with the ili9325 Lorenz example from tinygo-org/drivers#926 on an Arduino M0 (SAMD21, 48 MHz). The flash sizes include timing code. The speeds are the mean of three readings.

| opt | flash before | flash after | FillScreen before | FillScreen after | Lorenz steps/s before | Lorenz steps/s after |
|---|---|---|---|---|---|---|
| z | 24104 | 24024 | 137.9 ms | 34.2 ms | 3125 | 3667 |
| s | 24196 | 24148 | 29.2 ms | 29.2 ms | 3972 | 3943 |
| 1 | 33620 | 33620 | 29.3 ms | 29.3 ms | 4117 | 4134 |
| 2 | 33092 | 33092 | 29.2 ms | 29.5 ms | 4156 | 4116 |

The z before values are from the same dev commit. The s, 1 and 2 before speeds are from an earlier dev commit.

At -opt=z, FillScreen is still 5 ms slower than at -opt=s, with no calls to runtime/volatile left. That difference comes from minsize in other code and is not part of this change.

Fixes tinygo-org#6063
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.

2 participants