← All posts

Build log: a TFT sensor panel, and reading its SPI bus on the logic analyser

· Ohmeris team

This is the second of our bench demo circuits. An ATmega328P reads an analog sensor and draws the reading on a 240×320 ILI9341 colour TFT over SPI. Then we put a logic analyser on the bus to see what the firmware is actually sending. Everything here is simulated. No board was built.

The circuit

Three blocks:

  1. Sensor input. A sensor header feeds a 10 kΩ / 100 nF low-pass filter into PC0 (ADC0). On the bench, a function source drives the header with a 2 Hz triangle wave so the reading keeps moving.
  2. The microcontroller. An ATmega328P at 16 MHz running compiled Arduino firmware.
  3. The display. An ILI9341 TFT connected through a header: SCK, MOSI, chip select on pin 10 (PB2), data/command on pin 9 (PB1) and reset on pin 8 (PB0). MISO is not connected. The firmware only writes to the panel.

The filter's corner is about 160 Hz (1 / 2π·10k·100n), with a 1 ms time constant. A 2 Hz triangle passes through it essentially unchanged, while fast noise on the sensor line would be smoothed out. On real hardware, the capacitor also gives the ADC's sample-and-hold a low-impedance source to charge from. The simulator does not model that second effect. More on that below.

The firmware

The short sketch (docs/qa/marketing-demos-2026-10-24/sketches/tft_panel/tft_panel.ino) uses the Adafruit ILI9341 library with the SPI clock set to 1 MHz. Setup draws a header bar reading "OHMERIS SENSOR" and an outline for a bar graph. The main loop then repeats:

  1. analogRead(A0) gives a 10-bit value, which is scaled to millivolts against a 5 V reference.
  2. It prints the millivolt value.
  3. It fills a green bar proportional to the reading, then fills the rest of the bar in black.

Steps 2 and 3 are where the bus gets busy. Each colour pixel is two bytes. The two bar rectangles together cover 216 × 20 pixels, so every pass through the loop sends about 8,640 bytes of pixel data plus the command bytes that set up each window. At 1 MHz that is roughly 70 ms of bus time per update, which you can see on the analyser as long solid bursts. In one headless run of the compiled firmware, the bench counted 20,926 SPI bytes, the panel turned on, and no commands were ignored.

On the bench

Three instruments share one simulation timeline:

  • The display window mirrors what the panel would show. In the bench still (tft-bench.png), it reads "OHMERIS SENSOR", "1510 mV" and a partial green bar.
  • The logic analyser captures CS, SCK, MOSI and DC. It decodes SPI into bytes in a table beneath the traces. When you scrub through a burst, look for DC going low for a command byte, then high for the data that follows. The ILI9341 window-and-write sequence (column address, page address, memory write) is the pattern to look for at the start of each rectangle.
  • The sensor source drives the triangle wave.

Two details are worth knowing before you try this yourself:

  • Sample rate versus bit rate. The analyser in the still captures 20,000 samples at 8 MS/s, a 2.5 ms window. The SPI decoder needs at least 8 samples per bit, and 8 MS/s on a 1 MHz clock is exactly 8. Raise the SPI clock without raising the sample rate and the decode stops.
  • Word alignment. If the capture starts in the middle of a transfer, the analyser waits for the next chip-select edge before it decodes. That way it does not guess where a byte starts. It says so on screen.

The bench is also open about the circuit itself. With MISO unconnected, it shows a banner: received bits were simulated as 0, so do not use them as slave data. The ERC panel lists the analyser and source channels we left unconnected. We left both visible in the capture.

Schematic and board

As with the synth demo, the schematic was placed by hand on the grid. It is not an example of Benchmate output or of the automatic Tidy layout. Wires were routed by the editor's normal orthogonal router. The SPI lines run as a straight bus from the MCU to the header, with the unused MISO row marked NC.

The PCB is 55 × 40 mm, auto-placed and auto-routed with 23 traces and 5 vias, and passes the design rule check. The TFT sits on the board as a header connector, because the real panel is a module that plugs in. We have not ordered or assembled this board.

Honest limits

  • Simulation only. The firmware is real compiled AVR code on a simulated ATmega328P. The TFT and the sensor are modeled devices, not a specific vendor module's exact behaviour.
  • Ideal ADC input. The bench ADC samples an ideal voltage. It does not model source impedance, sample-and-hold charging, noise or temperature. That is why the filter capacitor's second job does not show up here.
  • Quantized timing. Peripheral edges in the simulator are quantized to solver steps and instruction latency, up to four CPU clocks. Bus timing is close but not cycle-exact silicon.
  • Slower than real time. The bench ran at roughly 1/60 of real time on our machine for these demos. The header, value, bar and decodable SPI appeared by about 1.5 s of simulated time, which took about 90 seconds of recording.

What we did not test

  • We did not build this circuit or connect a real ILI9341 module, so we have no real panel's refresh timing or colours to compare against.
  • We did not measure a real ATmega328P's SPI timing or ADC accuracy.
  • We did not test SPI clocks other than 1 MHz, or the read-back path (MISO).
  • We did not ask Benchmate to design this circuit. It is a demo of the bench and instruments, not of the AI.