F439_CPP_SPI_RA8875_TFT_LCD_04 1.0
STM32F439 SPI RA8875 7-inch TFT display with FRAM-backed touch calibration, built on the STM32_GFX, STM32_RA8875 and STM32_MB85RS64V libraries
Loading...
Searching...
No Matches
F439_CPP_SPI_RA8875_TFT_LCD_04 Documentation

This documentation covers a STM32 NUCLEO-F439ZI tutorial project that drives Adafruit's RA8875 driver board and 7-inch 800x480 TFT display over SPI, reads its resistive touch panel, and keeps the touch calibration in an MB85RS64V SPI FRAM so it survives a power cycle. It builds on three reusable STM32 HAL libraries: STM32_GFX (a port of Adafruit GFX), STM32_RA8875 (a port of Adafruit_RA8875) and STM32_MB85RS64V (an FRAM driver). It is the fourth part of the RA8875 series; part 3 introduced the two display libraries.

Project Overview

The firmware demonstrates:

  • Sharing one SPI bus (SPI1) between two devices, each with its own chip select: the display on PB6 and the FRAM on PB10, see initTest and initFRAM
  • Creating an MB85RS64V object, identifying the chip by its ID and proving that it keeps its data across resets, see initFRAM and testFRAM
  • A three-point touch calibration of the RA8875's resistive panel, see touchCalibrationRun, with the pen held on each target for about 0.3 seconds and the readings averaged
  • Advancing the calibration from the serial terminal: the space bar shows the next target and Esc redoes the current one. The key is received by interrupt, see HAL_UART_RxCpltCallback() in main.c
  • Storing the calibration in the FRAM with a validity header and a CRC-32, and reading it back at every boot, see touchCalibrationSave, touchCalibrationLoad and initTouchCalibration
  • Reaching C++ from the plain-C CubeMX main.c through C-callable functions declared in entryPointCPP.hpp

Project-Level Documentation

Hardware

Tested on a breadboard with a NUCLEO-F439ZI:

Signal NUCLEO-F439ZI Notes
SCK (display and FRAM) PA5 SPI1_SCK, shared
MISO (display and FRAM) PA6 SPI1_MISO, shared
MOSI (display and FRAM) PA7 SPI1_MOSI, shared
RA8875 CS PB6 GPIO output, label RA8875_CS, initial level high, pull-up
RA8875 Rst PC7 GPIO output, label LCD_RESET
MB85RS64V CS PB10 GPIO output, label MB85RS64_CS, initial level high, pull-up
MB85RS64V VCC +5V The chip takes 3.0 to 5.5 V. See the note below the table about its input levels
MB85RS64V GND GND
MB85RS64V HOLD VCC (+5V) Active low: a low level pauses the chip, so it will not answer
MB85RS64V WP VCC (+5V) Active low: only protects the status register; keep high so it stays writable
Debug console PA9 (TX) / PA10 (RX) USART1, 19200 baud

The FRAM is powered from +5V here. Its high input level is 0.8 x VDD, which is 4.0 V at 5 V, while the STM32 drives its outputs to 3.3 V, so SCK, MOSI and CS are below the datasheet's input level. It works on this breadboard, but powering the chip from 3.3 V instead puts those signals inside the specification.

The RA8875 board's Wait, Int, LITE and 3Vo pins are not connected.

Do not connect 3Vo. It is an output: the 3.3 V that the board's own regulator produces. It is not a power input, so never wire it to VCC, to the NUCLEO's 3.3 V pin or to any other supply rail. The board is powered from Vin (+5V, see part 3).

See the MB85RS64V class documentation for why HOLD and WP must be high. The display's touch panel is wired through the display board; it needs no extra connections.

Debug output goes to a USB-to-serial adapter on USART1 at 19200 baud: printf() is redirected there by the pre-build scripts. The same terminal is used to answer the calibration prompts, so it must be able to send keys.

What the firmware does

  • Start-up (main.c). initTest starts the display, then initFRAM starts the FRAM (the display is started first because the FRAM shares its bus), then testFRAM, then initTouchCalibration.
  • FRAM test. testFRAM prints the 16 bytes at address 0x0000 that the previous run left there, writes a new pattern whose first byte is a boot counter, reads it back and compares. The counter going up from one run to the next shows that the FRAM keeps its data.
  • Stored calibration. initTouchCalibration reads the calibration from the FRAM and prints it, or prints why none can be used.
  • Calibration mode. When TOUCH_CALIBRATION_ENABLE is defined in main.h, testTouch runs the interactive calibration regardless of what the FRAM holds, stores the result, runs a 15-second touch-to-draw demo, prints "Calibration Complete" and stops. With the macro commented out, none of that code is built and the program runs the graphics and text demos from part 3.

Calibration in detail

  • Targets. Three red circles are shown one at a time at 10% over and 10% down, 50% over and 90% down, and 90% over and 50% down of the display (80,48, then 400,432, then 720,240 on an 800x480 panel).
  • Capture. A touch counts only when the pen stays on the target for about 0.3 seconds. A green ring then appears and the pen can be lifted. Shorter taps are ignored, and the readings taken during the hold are averaged after a short settling period.
  • Prompt. After the first and the second target the terminal asks for the space bar (show the next target) or Esc (discard the touch and show the same target again). The next target never appears by itself, so a second tap cannot be taken as the next point.
  • Result. touchCalibrationCompute turns the three display points and the three raw readings into a matrix; touchCalibrationApply converts a raw reading into display coordinates with it. If the three touches lie on one line the attempt is thrown away and repeated, up to three attempts.

How the calibration is stored

The RA8875 library writes the matrix itself: seven big-endian 32-bit values plus a "calibrated" flag byte, 29 bytes at FRAM address 0x0100. This project adds a 12-byte header at 0x0120 holding the marker "CAL1", a format version, the length, and a CRC-32 of the matrix block. The matrix is written first and the header last, so a power cut in between leaves a header that does not match and the half-written data is never used. After writing, the data is read back and compared. Loading checks the marker, version, length, checksum, the library's flag and that the divider is not zero.

Things to know

  • Calibration mode overwrites. With TOUCH_CALIBRATION_ENABLE defined, every boot recalibrates and replaces the stored calibration.
  • FRAM test writes address 0 on every boot. The calibration lives at 0x0100 and above, so the two do not overlap. FRAM endurance is 10^12 writes per byte.
  • The key is received by interrupt. The USART1 global interrupt must be enabled in CubeMX, or the space bar and Esc are never seen.
  • The display library has no SPI error checking. It does not check the HAL return codes of its SPI transfers, so a failed transfer shows up as a blank or garbled display. initTest checks the chip ID, which catches most wiring and SPI-setting mistakes at start-up. The FRAM driver, by contrast, retries transient failures and then calls Error_Handler().
  • Not thread-safe. The display and the FRAM share one SPI bus and each operation is several separate transactions; see the notes on each function.
  • Build. The stm32-cubeide-scripts pre-build step edits main.c and main.h on each build (the printf redirect, the error handler and full assert). The libraries are git submodules; clone with git clone --recurse-submodules.