Files
CelloEffects/AGENTS.md
T
2026-10-18 23:39:44 +02:00

12 KiB

AGENTS.md

Hardware

  • Board: STM32H7S78-DK evaluation board
  • MCU: STM32H7S78 (Cortex-M7 @ 480MHz)
  • Flash: 64 KB internal flash at 0x08000000 (Boot) + 128 MB external OctoSPI NOR flash at 0x70000000 (Appli code + TouchGFX assets)
  • RAM: 1 MB internal + external PSRAM (for Audio/Framebuffers)

Project Architecture

Dual-Boot Structure

This project uses a dual-project architecture:

Project Directory Purpose
Boot /Boot/ Bootloader - initializes hardware, loads application from external flash
Appli /Appli/ Main application - TouchGFX UI + audio processing

Flashing order: Boot must be flashed first (without loader), then Appli (with external loader). After flashing Appli, press NRST button to start.

Note: The Appli uses external memory (0x70000000) for TouchGFX assets and requires the external FlashLoader MX66UW1G45G_STM32H7S78-DK.stldr to program the external flash.

Project Setup

  • IDE: STM32CubeIDE with STM32CubeMX plugin
  • RTOS: FreeRTOS V10.5.1 (CMSIS-RTOS V2)
  • GUI: TouchGFX 4.26.1 with NemaGFX GPU acceleration
  • Language: C/C++
  • Build System: GCC Makefile (not CMake)

Key Constraints

  1. Dual-boot flashing sequence: Flash Boot first, then Appli (gcc/flash.sh all does both and hard-resets the board).
  2. External FlashLoader required: Use gcc/MX66UW1G45G_STM32H7S78-DK.stldr for programming external flash (0x70000000)
  3. Never edit Core/Src/ and Core/Inc/ directly - regenerate via STM32CubeMX instead
  4. High-speed audio acquisition (48kHz) - uses DMA for ADF1 (microphone) and audio codec
  5. External memory usage: Audio buffers (>900KB) stored in external PSRAM (section AudioLooperBuffer)
  6. TouchGFX architecture: Two-stage debug - debug Boot first, then Appli
  7. FreeRTOS heap size: Configured to 50KB in FreeRTOSConfig.h (heap_4)
  8. Interrupt priorities: Audio interrupts at priority 2 (high priority), touch/SD at 14-15
  9. Debug probe connection: always ap=1 mode=UR (STM32_Programmer_CLI) / -m 1 -k (ST-LINK_gdbserver). Without them the connection fails (DEV_TARGET_NOT_HALTED, Cannot connect to access port 1)
  10. No OpenOCD: the installed OpenOCD 0.12 has no STM32H7RS target; debug with ST-LINK_gdbserver (Cortex-Debug in VS Code)

Build & Flash

Toolchain (macOS, verified)

Tool Path
arm-none-eabi-gcc / -gdb 10.3 /usr/local/bin (in PATH)
STM32_Programmer_CLI /Applications/STMicroelectronics/programmer/STM32CubeProgrammer.app/Contents/MacOs/bin/STM32_Programmer_CLI (not in PATH; gcc/flash.sh finds it, override with $STM32_PROGRAMMER_CLI)
ST-LINK_gdbserver /Applications/STMicroelectronics/STM32CubeIDE.app/Contents/Eclipse/plugins/com.st.stm32cube.ide.mcu.externaltools.stlink-gdb-server.macos64_*/tools/bin/ST-LINK_gdbserver
SVD /Applications/STMicroelectronics/programmer/STM32CubeProgrammer.app/Contents/MacOs/SVD/STM32H7S.svd
Probe on-board STLINK-V3 (USB)

TouchGFX 4.26.1 + STM32CubeMX are only needed to regenerate code.

Build Commands

Run from the repository root (the makefiles cd to the root themselves):

make -C gcc -j8                          # Build Boot + Appli
make -C gcc -j8 -f makefile_boot all     # Boot only
make -C gcc -j8 -f makefile_appli all    # Appli only
make -C gcc clean                        # Clean
python3 gcc/gen_compile_commands.py      # Regenerate compile_commands.json (IntelliSense/clangd) after adding sources

Outputs:

  • Boot: Boot/TouchGFX/build/bin/target.elf|.hex (internal flash 0x08000000)
  • Appli: Appli/TouchGFX/build/bin/target.elf|.hex (external flash 0x70000000)

The "old Ruby version (2.6.10)" TouchGFX warning during the build is harmless.

Flashing Commands

Use gcc/flash.sh (wraps STM32_Programmer_CLI with ap=1 mode=UR and the external loader):

gcc/flash.sh boot     # Boot  -> internal flash (with verify)
gcc/flash.sh appli    # Appli -> external flash via gcc/MX66UW1G45G_STM32H7S78-DK.stldr (~20 s)
gcc/flash.sh all      # Boot then Appli (required order), then hard reset
gcc/flash.sh probe    # Check the ST-Link/board connection (no write)
gcc/flash.sh reset    # Hardware reset
gcc/flash.sh verify   # Dump 4 KB of 0x08000000 / 0x70000000 to /tmp/read_int.bin, /tmp/read_ext.bin
make -C gcc flash     # Build + flash both (calls gcc/flash.sh on macOS/Linux)

Equivalent raw commands:

STM32_Programmer_CLI -c port=SWD ap=1 mode=UR -d Boot/TouchGFX/build/bin/target.hex -v -hardRst
STM32_Programmer_CLI -c port=SWD ap=1 mode=UR -el gcc/MX66UW1G45G_STM32H7S78-DK.stldr \
  -d Appli/TouchGFX/build/bin/target.hex -hardRst

Only one tool can hold the ST-Link at a time: stop any debug session before flashing.

VS Code (.vscode/)

  • tasks.json: Build All (default, Cmd+Shift+B), Build Boot/Appli, Clean, Flash Boot/Appli/All, Build + Flash Boot/Appli/All, Board: Probe/Reset/Verify, Generate compile_commands.json. All tasks call the commands above.
  • launch.json (Cortex-Debug, servertype: stlink): Debug Boot, Debug Appli (builds, flashes via GDB with the external loader, stops at Appli main()), Debug Appli (no flash), Attach Appli.
  • settings.json: Cortex-Debug tool paths, IntelliSense from compile_commands.json.
  • c_cpp_properties.json: uses compile_commands.json.

Keep AGENTS.md, .vscode/tasks.json, .vscode/launch.json and gcc/flash.sh consistent when changing any build/flash/debug command.

Debugging

Boot must already be in internal flash (gcc/flash.sh boot): at reset the core runs Boot, which maps the XSPI memories and jumps to Appli main() at 0x70000000.

VS Code: run "Debug Appli" (F5). No NRST press needed.

Command line:

GDBSRV=$(ls -d /Applications/STMicroelectronics/STM32CubeIDE.app/Contents/Eclipse/plugins/com.st.stm32cube.ide.mcu.externaltools.stlink-gdb-server.macos64_*/tools/bin | tail -1)/ST-LINK_gdbserver
"$GDBSRV" -p 61234 --swd --halt -m 1 -k \
  -cp /Applications/STMicroelectronics/programmer/STM32CubeProgrammer.app/Contents/MacOs/bin \
  -el gcc/MX66UW1G45G_STM32H7S78-DK.stldr

# In another terminal
arm-none-eabi-gdb Appli/TouchGFX/build/bin/target.elf
(gdb) target extended-remote :61234
(gdb) load            # optional: programs 0x70000000 through the external loader
(gdb) monitor reset
(gdb) break main      # hardware breakpoint chosen automatically (0x70000000 is flash in the memory map)
(gdb) continue

STM32CubeIDE alternative: debug Boot, terminate (Ctrl+F2), debug Appli, Resume (F8), press NRST.

Sensors/Peripherals

Audio (Primary Function)

  • ADF1 - Digital microphone (PDM interface, 48kHz sample rate)
  • I2C1 - Audio codec (STM32 Audio BSP)
  • DMA2D/GPDMA1/HPDMA1 - Audio DMA channels

Display

  • LTDC - LCD-TFT controller
  • GPU2D (NemaGFX) - 2D acceleration
  • Display: 800x480 RGB565 (LTDC)

Storage & Communication

  • XSPI1 - External PSRAM (for audio buffers)
  • XSPI2 - External Flash (for TouchGFX assets)
  • I2C1 - Touch controller (FT5336), sensors

Touch Control

  • FT5336 - Capacitive touch controller
  • SPI - Touch interface
  • GPIO - Touch interrupt (TP_IRQ)

Audio Processing

Effects Chain (in order)

  1. Octaver - Dual-grain pitch shifting (-2, -1, +1, +2 octaves)
  2. Equalizer - 10-band graphic EQ (31 Hz - 16 kHz, -24..+24 dB)
  3. Overdrive - Distortion effect
  4. Saturation - Audio saturation
  5. Echo - Delay-based echo
  6. Reverb - Room reverb

Analysis

  • FFT: 2048-point real FFT (CMSIS-DSP)
  • Sample rate: 48kHz
  • Bins: 1024 (0-24kHz spectrum)
  • Peak detection: Frequency with maximum magnitude

Looping

  • Duration: Up to 5 seconds at 48kHz
  • Playback: 2x speed (raises pitch by octave)
  • Storage: External PSRAM (937.5KB buffer)

TouchGFX Architecture

Project Structure

Appli/
├── TouchGFX/
│   ├── App/                    # Application code (user created)
│   ├── assets/                 # Images, fonts, videos
│   ├── build/                  # Generated build files
│   ├── config/                 # Build configuration
│   ├── generated/              # Auto-generated code (DO NOT EDIT)
│   ├── gui/                    # Custom GUI code (views, presenters)
│   ├── simulator/              # Simulator files
│   └── target/                 # Target-specific code

GUI Screens

  • InitScreen - Splash screen (title + central button) -> Overview
  • Overview - Home screen: power toggle + master volume, 3x2 grid of effect cards (tap = open the effect screen, switch = enable/bypass), looper (hold to record, plays back x2), Spectrum button
  • Octaver, Equalizer, Overdrive, Saturation, Echo, Reverb - Effect tuning screens (common header: back, title, on/off; sliders with live value)
  • Oscillo - Spectrum analyzer (title "SPECTRE")

UI Conventions (keep when editing the GUI)

  • Never write files in Appli/TouchGFX/generated/: it is regenerated by TouchGFX Designer. Screens, widgets and interactions come from CelloEffects.touchgfx.
  • Custom look is done in user code only (Appli/TouchGFX/gui/): each XxxView::setupScreen() re-lays out the generated widgets and adds extra ones.
    • gui/include/gui/common/UiTheme.hpp: palette, per-effect accent color (ui::accent()), ui::Header, ui::ValueText (formatted value: show("%d %%", v), showDb(v)), ui::layoutEffectHeader(), ui::layoutParamRow()
    • gui/include/gui/common/EffectCard.hpp: home-screen effect card
    • The home grid lives in OverviewView; the generated buttonWithLabel1..6 of Overview are hidden on purpose.
  • A new screen must be created in TouchGFX Designer (so FrontendHeapBase and FrontendApplicationBase include it). A screen hand-written outside Designer is not in the screen heap and corrupts memory.
  • Texts: add them to Appli/TouchGFX/assets/texts/texts.xml (group UiRework); the build regenerates texts and fonts (T_<ID>). Typographies: Default 20 px, Medium 28 px, Large 40 px. Wildcard texts: VALUE_M_LEFT (Medium), VALUE_S_CENTER (Default); a character shown in a wildcard must be listed in the typography's WidgetWildcardCharacters.
  • Canvas widgets (Circle, Graph...) need a per-screen CanvasWidgetRenderer::setupBuffer() (see OverviewView, OscilloViewBase).

Memory Map

Key Sections

  • AudioLooperBuffer - External PSRAM (audio loop recording)
  • TouchGFX framebuffers - External PSRAM (LCD display)
  • Heap - Internal RAM (50KB FreeRTOS heap)
  • Stack - Internal RAM (task stacks)

External Memory

  • PSRAM: 8MB (XSPI1) - Audio buffers + framebuffers
  • Flash: 128MB (XSPI2) - TouchGFX assets (compressed images/fonts)
  • External FlashLoader: gcc/MX66UW1G45G_STM32H7S78-DK.stldr (required for programming external flash via STM32CubeProgrammer)

Data Logging

  • Format: CSV (to be implemented)
  • Storage: SD card via SPI (not currently implemented in main code)
  • FATFS: File system (available via Middlewares/ST)

Last updated: October 2026 Project: STM32H7S78-DK Audio Effects Processor with TouchGFX

Last fixed: October 18, 2026 (VS Code/CLI build-flash-debug verified on board: gcc/flash.sh, Cortex-Debug + ST-LINK_gdbserver)

Flashing Troubleshooting

Common Issues

  1. DEV_TARGET_NOT_HALTED / Cannot connect to access port 1 / gdbserver Target unknown error 32

    • Cause: missing access port or connect-under-reset option
    • Solution: ap=1 mode=UR (CLI) or -m 1 -k (ST-LINK_gdbserver), as used by gcc/flash.sh and .vscode/launch.json
  2. "Upload size is bigger than flash memory size"

    • Cause: reading beyond the 64 KB internal flash (0x08000000-0x08010000)
    • Solution: use a smaller read size
  3. External memory (0x70000000) not accessible

    • Cause: External FlashLoader not loaded
    • Solution: add -el gcc/MX66UW1G45G_STM32H7S78-DK.stldr (CLI and gdbserver)
  4. Application runs but display is blank

    • Cause: Appli not flashed in external memory correctly, or Boot missing
    • Solution: gcc/flash.sh all, then gcc/flash.sh verify
  5. ST-Link busy

    • Cause: a debug session or STM32CubeProgrammer GUI holds the probe
    • Solution: stop it, then retry
  6. nul files appear in the repo

    • Cause: Windows 2> nul redirections in gcc/include/cube_programmer.mk (now guarded with ifeq ($(OS),Windows_NT))