19 KiB
Wiring: 4.0" ST7796S 480×320 SPI TFT + XPT2046 Touch → Teensy 4.1
This document describes how to wire the Elecrow/Hosyond 4.0" 480×320 SPI TFT module with ST7796S driver and XPT2046 resistive touch to the Teensy 4.1.
It is the reference for Phase 1 of plans/teensy41_signer_port.md
(display + touch bring-up). Source documents for the pinout live in
firmware/teensy41/documents/:
ST7796S_Datasheet.pdf— display controller4.0inch_SPI_Module_User_Manual.pdf— module manual4.0 Inch 480_320 SPI TFT LCD Module with ST7796 Driver_With Touch Function - Elecrow Wiki.htmlTeensy® 4.1.html— PJRC pinout reference
Module pinout
The module breaks out 14 pins along one long edge. The labels below are the silk-screen names on the actual Elecrow/Hosyond board (read in order from one end of the header to the other). Alternate names in parentheses are the ones used in some other vendors' docs.
| # | Silkscreen | Alt name | Function |
|---|---|---|---|
| 1 | t_irq | Touch interrupt (active low) | |
| 2 | t_do | T_MISO | Touch SPI MISO (shared bus) |
| 3 | t_din | T_MOSI | Touch SPI MOSI (shared bus) |
| 4 | t_cs | Touch chip select (active low) | |
| 5 | t_clk | T_SCK | Touch SPI clock (shared bus) |
| 6 | sdo (miso) | SDO / MISO | TFT SPI MISO (shared with touch) |
| 7 | led | BL / BKL | Backlight (high = on; PWM-able) |
| 8 | sck | CLK / SCL | TFT SPI clock (shared with touch) |
| 9 | sdi (mosi) | SDI / SDA | TFT SPI MOSI (shared with touch) |
| 10 | dc/rs | DC / A0 | TFT data/command select |
| 11 | reset | RST | TFT reset (active low) |
| 12 | cs | LCD_CS | TFT chip select (active low) |
| 13 | gnd | Ground | |
| 14 | vcc | Power (3.3V–5V) |
The module has a second SD card slot on the PCB (separate from the Teensy's built-in slot). Its pins are not broken out on the 14-pin header — they go to the SD contacts on the back of the module. We do not use the module's SD slot; the 1 TB OTP pad lives in the Teensy's built-in SDMMC slot (4-bit bus, ~20–40 MB/s, exFAT). See
README.md§SD card size support.
Connection chart (wire-by-wire)
This is the simple "connect this Teensy pin to that module pin" list. Power first, then signals.
By display header position (use this when the silkscreen is hidden)
When the display is plugged into a breadboard, you can't read the silkscreen.
This table is indexed by physical position on the display header, counting
from the end with t_irq (pin 1) to the end with vcc (pin 14). Hold the
display with the header at the bottom and the screen facing you; pin 1 is the
leftmost pin (the t_irq end), pin 14 is the rightmost (the vcc end).
| Display header pos | Silkscreen | → | Teensy 4.1 pin | Function |
|---|---|---|---|---|
| 1 | t_irq | → | 2 | Touch interrupt (active low) — see note below on why not pin 10 |
| 2 | t_do | → | 12 | SPI0 MISO (shared) |
| 3 | t_din | → | 11 | SPI0 MOSI (shared) |
| 4 | t_cs | → | 9 | Touch chip select (active low) |
| 5 | t_clk | → | 13 | SPI0 SCK (shared) — opposite edge |
| 6 | sdo (miso) | → | 12 | SPI0 MISO (same wire as pos 2) |
| 7 | led | → | 8 | Backlight (PWM-able) |
| 8 | sck | → | 13 | SPI0 SCK (same wire as pos 5) |
| 9 | sdi (mosi) | → | 11 | SPI0 MOSI (same wire as pos 3) |
| 10 | dc/rs | → | 7 | TFT data/command |
| 11 | reset | → | 6 | TFT reset (active low) |
| 12 | cs | → | 5 | TFT chip select (software CS) |
| 13 | gnd | → | GND | Ground (opposite edge, next to pin 13) |
| 14 | vcc | → | 3V3 | 3.3V power (long edge, past pin 12) — NOT 5V |
Shared pins (pos 2↔6, 3↔9, 5↔8): these pairs are tied together on the display PCB, so you only need to wire one of each pair to the Teensy. The table shows both for completeness, but in practice you can wire pos 2, 3, 5 and leave pos 6, 8, 9 floating (or wire them to the same Teensy pin — harmless). That brings it to 11 wires total (8 unique signals + 3V3 + GND
- the 3 shared-pair duplicates you choose to wire).
By Teensy pin number (for lookup at the Teensy)
Sorted by Teensy pin number for easy lookup at the Teensy end of the wires.
| Teensy 4.1 pin | → | Module silkscreen | What it does |
|---|---|---|---|
| GND | → | gnd | Ground (GND is on the opposite edge, next to pin 13) |
| 3V3 | → | vcc | 3.3V power (not 5V) — the 3V3 pin is on the long edge, just past pin 12 |
| 2 | → | t_irq | Touch interrupt (active low) — not pin 10 (see note below) |
| 5 | → | cs | TFT chip select (software CS — see note below) |
| 6 | → | reset | TFT reset (active low) |
| 7 | → | dc/rs | TFT data/command |
| 8 | → | led | Backlight (HIGH = on; PWM-able) |
| 9 | → | t_cs | Touch chip select (active low) |
| 11 | → | sdi (mosi) | SPI0 MOSI (also wires to t_din) |
| 12 | → | sdo (miso) | SPI0 MISO (also wires to t_do) |
| 13 | → | sck | SPI0 SCK (also wires to t_clk) — pin 13 is on the opposite edge, route via a short jumper |
That's 11 wires total, all in the pins 5-13 range plus 3V3 and GND. The
three shared SPI lines (sdi (mosi), sdo (miso), sck) each fan out to two
module pins — see the next section for why and how.
Why
csis on pin 5 (software CS) andt_irqis on pin 2 (not pin 10): Pin 10 is the hardware CS0 for SPI0. Do not use pin 10 for anything else — callingpinMode(10, ...)(e.g. fort_irqas an input) corrupts the SPI0 engine state and reverts the ST7796S display to landscape mode aftersetRotation(). This is a hard-won lesson documented inplans/teensy41_bringup.md. So:
csis on pin 5 (software CS viaSPI.beginTransaction()) — the speed difference vs hardware CS0 is negligible for a signer UI.t_irqis on pin 2 (long edge, before pin 5) — keeps it off pin 10 and out of the SPI0 CS0 conflict. If you want hardware CS0 for the TFT, you can putcson pin 10, but thent_irqmust stay on pin 2 (or be dropped entirely in favor of polling).
Shared-bus shortcut:
sdi (mosi)andt_dinare the same net on the module PCB,sdo (miso)andt_doare the same net, andsckandt_clkare the same net. So for each shared pair you can either (a) wire the Teensy pin to both module pins (harmless, just a Y-jumper), or (b) wire the Teensy pin to one module pin and leave the other floating (it's tied internally on the PCB). The connection chart above wires each Teensy SPI pin to the TFT-side silkscreen; the touch-side silkscreen of each pair is tied to the same net on the board.
Teensy 4.1 pin assignments
The Teensy 4.1 has 3 hardware SPI ports. We use SPI0 (the primary port with the FIFO) on pins 11/12/13. The TFT and the XPT2046 share this bus; each has its own CS. All pins are 3.3V — the module is 3.3V-logic compatible, so no level shifting is needed.
| Silkscreen | Teensy 4.1 pin | Teensy function | Notes |
|---|---|---|---|
| gnd | GND | Ground | GND is on the opposite edge, next to pin 13. |
| vcc | 3V3 | 3.3V output | Use 3.3V, not 5V/VIN. The 3V3 pin is on the long edge, just past pin 12. The Teensy's 3.3V regulator can supply up to ~250 mA; the display + backlight draw ~80–120 mA, well within budget. |
| cs | 5 | GPIO (software CS) | TFT chip select (active low). Software CS via SPI.beginTransaction() — see the note in the connection chart about why we didn't use hardware CS0 on pin 10. |
| reset | 6 | GPIO | TFT reset. Drive low for >10 µs to reset; pull high (or set as OUTPUT HIGH) for normal operation. Can also be tied to a 10 kΩ pull-up and left floating if you don't need software reset. |
| dc/rs | 7 | GPIO | TFT data/command. High = data (pixel/command param), Low = command. |
| led | 8 | GPIO / PWM | Backlight. Drive HIGH for full brightness, or use analogWrite(8, 0..255) for dimming. Can also be tied to 3.3V through a ~100 Ω resistor for always-on. |
| t_cs | 9 | GPIO | Touch chip select. Active low. Must be HIGH when talking to the TFT, LOW when reading the touch. |
| t_irq | 2 | GPIO (input + interrupt) | Touch interrupt. Pulled high by the XPT2046 internally; goes low when touch pressure is detected. Optional — can be left unconnected and polled, but wiring it lets us sleep until a touch happens. Must not be pin 10 (SPI0 CS0 — pinMode(10, INPUT) corrupts the SPI engine and reverts the display to landscape). |
| sdi (mosi) | 11 | SPI0 MOSI | Shared with touch t_din. |
| sdo (miso) | 12 | SPI0 MISO | Shared with touch t_do. The TFT rarely drives MISO (most ST7796S commands are write-only), but the XPT2046 reads return on this line. |
| sck | 13 | SPI0 SCK | Shared with touch t_clk. Pin 13 is on the opposite edge of the Teensy (next to GND), so route it via a short jumper across the board. Pin 13 also drives the onboard orange LED — that's fine, the LED just blinks during SPI activity. |
| t_clk | 13 | SPI0 SCK (shared) | Same wire as sck. |
| t_do | 12 | SPI0 MISO (shared) | Same wire as sdo (miso). |
| t_din | 11 | SPI0 MOSI (shared) | Same wire as sdi (mosi). |
Pin map summary (Teensy side)
Wires, listed in the same order as the module's silkscreen header (pin 1 → 14):
Teensy 4.1 Silkscreen Function
----------- ----------- ------------------------------------------
pin 2 t_irq Touch interrupt (active low — NOT pin 10, see note)
pin 12 t_do Touch MISO (shared SPI0 MISO)
pin 11 t_din Touch MOSI (shared SPI0 MOSI)
pin 9 t_cs Touch chip select (active low)
pin 13 t_clk Touch SCK (shared SPI0 SCK — opposite edge)
pin 12 sdo (miso) TFT MISO (shared SPI0 MISO — same wire as t_do)
pin 8 led Backlight (HIGH = on; analogWrite for dimming)
pin 13 sck TFT SCK (shared SPI0 SCK — same wire as t_clk)
pin 11 sdi (mosi) TFT MOSI (shared SPI0 MOSI — same wire as t_din)
pin 7 dc/rs TFT data/command
pin 6 reset TFT reset (active low)
pin 5 cs TFT chip select (software CS)
GND gnd Ground (opposite edge, next to pin 13)
3V3 vcc Power (3.3V — NOT 5V; long edge, past pin 12)
Shared-bus note: t_do and sdo (miso) are the same electrical net on the
module (both connect to the SPI MISO line), and t_din/sdi (mosi) and
t_clk/sck are likewise the same nets. You only need to wire one of each
shared pair to the Teensy — but on this module the header brings both out, so
you can either wire both to the same Teensy pin (harmless) or wire one and leave
the other floating (also fine, since they're tied together on the PCB). The
table above wires both to the same Teensy pin for clarity.
SPI bus sharing
The TFT and the XPT2046 share MOSI / SCK / MISO. The rule for sharing:
- Only one CS is low at a time. Before talking to the TFT, set
t_csHIGH andcsLOW. Before reading the touch, setcsHIGH andt_csLOW. - Use
SPI.beginTransaction(SPISettings(spi_speed, MSBFIRST, SPI_MODE0))/SPI.endTransaction()around each device's access. The TFT and the XPT2046 both use SPI mode 0, but they may want different clock speeds:- TFT: up to 40 MHz (the ST7796S supports 1-line SPI up to ~62 MHz; 40 MHz is a safe conservative choice on the Teensy's SPI0).
- XPT2046: 2–2.5 MHz max (the XPT2046 datasheet specifies a max serial clock of ~2.5 MHz). Always drop the clock before reading the touch.
- The XPT2046 control byte is
0x90for X read (channel 5) and0xD0for Y read (channel 1), 8-bit, MSB first, with the start bit set. Seefirmware/cyd_esp32_2432s028/main/touch.cxpt2046_transfer_12b()for the bit-banged reference; on the Teensy we can use the hardware SPI engine instead of bit-banging.
Routing notes (trace-crossing analysis)
This assignment uses hardware SPI0 (MOSI=11, MISO=12, SCK=13) for maximum display throughput (FIFO + DMA, up to 40 MHz), with all display pins in a contiguous block on the Teensy's long edge (pins 5-12) plus SCK=13 on the opposite edge. The display module's silkscreen pin order doesn't match the Teensy's SPI0 pin order, so a small number of trace crossings are unavoidable. This is fine for jumper wires and trivial on a 2-layer PCB.
Teensy 4.1 physical layout (relevant part):
- Long edge: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 3V3, 24, 25, ...
- Opposite edge: 0, 13, 14, ... with GND in the middle
So pins 5-12 are contiguous on the long edge, 3V3 is right past pin 12, and pin 13 (SCK) + GND are on the opposite edge. All display signals fit in the pins 5-13 range plus 3V3 and GND — no need to reach up to pins 24+.
Crossings (2 on the long edge + 1 SCK via):
- MOSI/MISO swap. Display order is MISO(
t_do/sdo, pos 2/6) then MOSI(t_din/sdi, pos 3/9); Teensy SPI0 is MOSI(11) then MISO(12). One crossing — unavoidable given the fixed SPI0 pin assignments. t_csjumps over the SPI pins. Display pos 3-4-5 is MOSI-t_cs-SCK; Teensy pins 11-12-13 are MOSI-MISO-SCK with no free pin between MOSI(11) and SCK(13).t_cs(Teensy pin 9) routes below the SPI block, crossing the MOSI/MISO pair. One crossing.- SCK via to the opposite edge. SCK is on pin 13, which is on the opposite edge of the Teensy from pins 5-12. This is a via (a short jumper across the board), not a trace crossing on the long edge.
The remaining GPIOs (led=8, dc/rs=7, reset=6, cs=5) run monotonically
down the long edge below t_cs=9 — zero crossings among them.
Why not zero crossings? A zero-crossing assignment is possible by bit-banging SPI on Teensy pins in exact display order, but it loses the hardware SPI engine — display refresh drops to ~0.2-0.5 s per full 480×320 frame. We chose hardware SPI0 for a snappy UI and accepted 2 crossings + 1 via, which is trivial for jumper wires or a 2-layer PCB.
Why cs=5 (software CS) and t_irq=2? Pin 10 is the hardware CS0 for
SPI0, but pin 10 cannot be used for t_irq — calling pinMode(10, INPUT)
corrupts the SPI0 engine and reverts the display to landscape after
setRotation() (verified during bring-up, see
plans/teensy41_bringup.md). So t_irq is
on pin 2 (long edge, before pin 5), and cs is on pin 5 (software CS via
SPI.beginTransaction()). The speed difference vs hardware CS0 is negligible
for a signer UI. If you want hardware CS0 for the TFT, put cs on pin 10 and
leave t_irq on pin 2 (or drop t_irq and poll).
Power notes
- 3.3V only. The Teensy 4.1's pins are not 5V tolerant. The display
module is rated 3.3V–5V and works at 3.3V logic — power it from the Teensy's
3V3pin, notVIN/VUSB(5V). Driving VCC with 5V while the signal pins are at 3.3V can back-power the ST7796S level shifters and is unnecessary. - Backlight current. The LED pin on these modules is typically driven
through a transistor on the module; a direct 3.3V GPIO high is enough to turn
it on. If the backlight flickers or the GPIO can't hold high, drive
LEDfrom 3.3V through a ~100 Ω resistor instead and skip PWM dimming. - Total draw. Display + backlight + touch ≈ 80–120 mA at 3.3V. The Teensy's onboard regulator is rated for ~250 mA external use, so this is within budget. If you add other peripherals (Ethernet PHY, etc.), re-check the budget.
Wiring checklist (do this before powering on)
- VCC → 3V3 (not 5V/VIN).
- GND → GND.
- All SPI signal pins (
cs,dc/rs,reset,sdi (mosi),sck,sdo (miso),t_cs,t_irq) go to the Teensy pins listed above — confirm none are swapped. The easy one to get backwards issdi (mosi)↔sdo (miso): on this modulesdi (mosi)= Teensy pin 11 (MOSI) andsdo (miso)= Teensy pin 12 (MISO). - The shared-bus pairs (
t_do/sdo (miso),t_din/sdi (mosi),t_clk/sck) are tied together on the module PCB — wire one of each pair to the Teensy pin and the other can go to the same pin or float. Don't wire them to different Teensy pins. - No 5V signal reaches any Teensy pin.
- The Teensy's built-in SD slot is not wired to the display — the module's SD slot is unused. The 1 TB pad goes in the Teensy's built-in slot only.
Bring-up order (Phase 1)
- TFT only first. Wire
vcc,gnd,cs,reset,dc/rs,sdi (mosi),sck,led,sdo (miso). Leavet_cs,t_irqfloating for now. Run a fill-screen + draw-text sketch. Exit criterion: solid color fill + readable text. - Touch second. Add
t_cs,t_clk(shared withsck),t_din(shared withsdi (mosi)),t_do(shared withsdo (miso)),t_irq. Run a touch-read sketch that prints raw X/Y + mapped pixel coords. Exit criterion: stylus press prints coordinates that track the stylus position across the full 480×320 area. - Calibration. The XPT2046 raw readings need a 2-point calibration per
axis (min/max raw → 0/480 and 0/320). Port the calibration struct from
firmware/cyd_esp32_2432s028/main/touch.cs_caland re-calibrate on the Teensy (the CYD's numbers won't match — different panel, different controller).
References
- Port plan:
plans/teensy41_signer_port.md - Bring-up log:
plans/teensy41_bringup.md - CYD display driver to port:
firmware/cyd_esp32_2432s028/main/ili9341.c - CYD touch driver to port:
firmware/cyd_esp32_2432s028/main/touch.c - ST7796S datasheet:
firmware/teensy41/documents/ST7796S_Datasheet.pdf - Module user manual:
firmware/teensy41/documents/4.0inch_SPI_Module_User_Manual.pdf - Teensy 4.1 pinout:
firmware/teensy41/documents/Teensy® 4.1.html