# x-cube-n6-camera-capture-nucleo

> Esplorazione dello STM32N6, prima MCU STM32 con NPU integrata: pipeline di acquisizione, encode H.264 hardware e webcam UVC su USB su Nucleo-N657X0-Q.

Pubblicato: 2026-05-20
Aggiornato: 2026-08-25
Ambito: elettronica
Riferimento normativo: Dalla cattura all'encode H.264 e allo streaming UVC <https://en.wikipedia.org/wiki/Advanced_Video_Coding>
Repository: <https://github.com/stefanofante/x-cube-n6-camera-capture-nucleo>

Pagina: <https://www.stline.it/wiki/x-cube-n6-camera-capture-nucleo/>

---

Lo STM32N6 è la prima MCU STM32 con NPU integrata (Neural-ART, 600 GOPS, 3 TOPS/W) per AI on-device, su core Arm Cortex-M55 @ 800 MHz con vettoriale Helium, 4,2 MB di RAM, ISP dedicato e acceleratori media/grafica; non ha flash interna (esecuzione Load & Run da NOR Octo-SPI esterna). Questo è il primo di una serie di progetti con cui iniziamo a testarne le capability: parte dalla pipeline acquisizione→encode H.264→streaming UVC su USB (Nucleo-N657X0-Q); seguiranno progetti che sfruttano la NPU per AI on-device. Nota: questo specifico firmware esercita il percorso imaging + encoder H.264 hardware, non ancora la NPU. Contenuti dal README del repository; il riferimento autoritativo resta GitHub.

## Perché lo STM32N6

Lo STM32N6 è la prima MCU STM32 con NPU integrata: l’acceleratore Neural-ART eroga ~600 GOPS con efficienza 3 TOPS/W, abilitando inferenza AI on-device finora riservata ai microprocessori. Il core è un Arm Cortex-M55 @ 800 MHz (1280 DMIPS / 3360 CoreMark), primo Cortex-M con vettoriale Helium (MVE) — DSP e ML accelerati sulla CPU.

Completano il quadro 4,2 MB di RAM contigua (la più ampia su STM32), ISP dedicato, interfacce camera multiple e acceleratori grafici/multimediali, senza flash interna (esecuzione Load & Run da NOR Octo-SPI). In questo progetto, sul concreto: STM32N657X0HxQ, PLL1 a 800 MHz, encoder H.264 hardware (IP Verisilicon H1), ricevitore MIPI-CSI2 + ISP, USB High-Speed.

È il primo di una serie: qui validiamo end-to-end la catena imaging→encode→USB su una board accessibile (Nucleo), come base concreta su cui innestare i progetti successivi che sfruttano la NPU per AI on-device.

## Cosa fa il firmware

All’avvio configura clock (PLL1 → 800 MHz) e MPU, porta su il ricevitore MIPI-CSI2 e il sensore IMX335 tramite la Camera Middleware ST + libreria ISP, e cattura i frame via DCMIPP in formato YUY2.

Il piano di luma viene dato in pasto all’encoder H.264 hardware dello STM32N6 (IP Verisilicon H1); un piano di crominanza grigio statico rende il flusso un bitstream YUV 4:2:0 valido. Le NAL unit H.264 vengono impacchettate in payload UVC Frame-Based e inviate all’host su USB High-Speed. Tutto gira su FreeRTOS con un task dedicato a cattura, encode e USB.

Risultato: una webcam UVC standard con un singolo stream, default 480×272 H.264 a 30 fps.

## La pipeline di imaging: dal sensore al frame

Il sensore IMX335 non emette pixel pronti all’uso: invia su MIPI-CSI2 un flusso di dati RAW Bayer, dove ogni fotosito porta un solo colore secondo la matrice del filtro. La conversione in un’immagine visualizzabile avviene a bordo dello STM32N6, in due stadi.

Il **ricevitore MIPI-CSI2** deserializza le due corsie differenziali (D-PHY), ricostruisce i pacchetti di riga e li consegna al **DCMIPP** (Digital Camera Memory Interface Pixel Processor), il blocco di acquisizione dello STM32N6. Il DCMIPP, appoggiandosi all’**ISP** (Image Signal Processor) configurato dalla libreria ST, esegue demosaicizzazione, bilanciamento del bianco, correzione del colore e conversione di spazio, scrivendo in RAM frame in formato **YUY2** (YUV 4:2:2 packed: luminanza a piena risoluzione, crominanza sottocampionata in orizzontale).

Separare luminanza (Y) e crominanza (U, V) è esattamente ciò che serve allo stadio successivo, perché l’encoder video lavora su piani Y/UV e perché l’occhio umano è molto più sensibile alla luminanza che al colore — la base di tutta la compressione video moderna.

## L’encoder H.264 hardware

Lo STM32N6 integra un **encoder video H.264 hardware** (IP Verisilicon H1): un blocco dedicato che comprime i frame senza caricare la CPU. Riceve in ingresso un’immagine YUV 4:2:0 e produce un **bitstream H.264** organizzato in NAL unit (Network Abstraction Layer), le stesse unità con cui l’H.264 viaggia in qualunque file o stream.

In questo firmware l’encoder riceve **solo il piano di luma** prodotto dal DCMIPP; il piano di crominanza viene riempito con un grigio statico, così il bitstream resta un YUV 4:2:0 formalmente valido ma l’immagine è monocromatica. È un compromesso voluto: senza PSRAM esterna sul Nucleo non c’è banda/RAM per gestire anche i piani colore lungo tutta la pipeline. La compressione hardware riduce di molto la banda richiesta sull'USB — quanto, e quanto serva davvero a questa risoluzione, è nei conti più sotto.

## Trasporto UVC su USB High-Speed

Le NAL unit H.264 non vengono salvate su file: vengono impacchettate in **payload UVC Frame-Based** e spinte verso l’host su **USB High-Speed** (480 Mbit/s). La USB Video Class (UVC) è la classe standard che rende una telecamera utilizzabile senza driver; il formato **Frame-Based** è quello previsto da UVC per stream compressi o non standard come l’H.264.

All’enumerazione il dispositivo si presenta come la webcam “STM32 N6 Camera”, espone i propri descrittori (formato, risoluzione, frame rate) e negozia lo stream con l’host tramite i controlli Probe/Commit. Da lì in poi è una webcam come un’altra: qualunque applicazione UVC la apre.

## Architettura software su FreeRTOS

Il firmware gira su **FreeRTOS**, con **USBX** (Azure RTOS / ThreadX) per lo stack USB. Un task dedicato orchestra l’intero percorso cattura → encode → invio: attende il frame YUY2 dal DCMIPP, lo passa all’encoder H.264, raccoglie le NAL unit risultanti e le consegna allo stack UVC per la trasmissione. Il disaccoppiamento fra acquisizione, compressione e USB tramite RTOS è ciò che mantiene i 30 fps stabili senza che un blocco resti in attesa a vuoto dell’altro.

## Hardware

Board NUCLEO-N657X0-Q (STM32N657X0HxQ, Cortex-M55, ST-LINK/V3 onboard, USB utente High-Speed su CN13). NOR esterna Macronix MX25UM51245G (512 Mbit Octo-SPI, onboard) per FSBL + applicazione firmata.

Camera Sony IMX335 (5 MP, 2-lane MIPI) su daughterboard MIPI-CSI2 MB1854. La Camera Middleware supporta anche altri sensori (non testati sul Nucleo). I pin di boot del Nucleo vanno impostati su Flash boot per il funzionamento standalone.

## Boot model (Load & Run)

Il Nucleo-N6 non ha flash interna: il codice risiede sulla NOR Octo-SPI esterna. Il BootROM verifica la firma dell’FSBL e lo carica in AXISRAM2; l’FSBL porta su l’Octo-SPI in memory-mapped, copia l’applicazione firmata in AXISRAM e ci salta.

FSBL a `0x70000000`, applicazione a `0x70100000`; entrambi i binari firmati con `STM32_SigningTool_CLI` (chiavi dummy, solo per sviluppo). Il sotto-progetto FSBL è interamente vendorizzato in `Tools/FSBL/`, derivato dal template ST `Template_FSBL_LRUN` e convertito in build CMake standalone.

## Differenze vs l’esempio ST (DK → Nucleo)

È un port/adattamento dell’esempio ufficiale ST `x-cube-n6-camera-capture` (board DK) al Nucleo-N657X0-Q: niente PSRAM esterna (tutti i buffer stanno in AXISRAM/DTCM interna), niente LCD/touch (l’unico output è lo stream UVC), FSBL completamente vendorizzato per poter programmare ed eseguire standalone.

Build con CMake + Ninja + task VS Code per build/sign/flash: STM32CubeIDE non è richiesto, solo STM32CubeProgrammer. Tutto il codice ST/terze parti è congelato (vendored) nel repository: niente pack esterni o progetti CubeMX.

## Build, firma e flash

Toolchain testato: `arm-none-eabi-gcc` 14.3.1 + patch ST, CMake ≥ 3.22, Ninja ≥ 1.10, STM32CubeProgrammer 2.22.0 (richiesto per sign + flash). Wrapper `cube-cmake` (estensione VS Code STM32) per puntare al GCC bundled; qualsiasi `arm-none-eabi-gcc` su `PATH` va bene.

I task VS Code coprono: Configure/Build app e FSBL, Sign FSBL/App, Program FSBL (`0x70000000`) / App (`0x70100000`), il one-shot “Build + Sign + Program App” e il provisioning iniziale “Full Program (FSBL + App)”. External loader: `MX25UM51245G_STM32N6570-NUCLEO.stldr`.

## Uso come webcam

Dopo Program App: reset della board, collegare il cavo USB utente (CN13, USB-C sul Nucleo) al PC. Compare un device UVC “STM32 N6 Camera”; si apre con qualunque app UVC — Windows Camera, OBS, VLC, ffplay (es. `ffplay -f dshow -i video="STM32 N6 Camera"`).

## Risoluzione dei problemi

- **Il dispositivo non compare.** Verifica che i pin di boot del Nucleo siano su Flash boot e che FSBL e applicazione siano stati programmati entrambi (FSBL a `0x70000000`, app a `0x70100000`). Senza un FSBL valido il BootROM non avvia nulla.
- **Compare ma lo stream non parte.** Collega il cavo alla USB *utente* (CN13), non alla porta ST-LINK: sono due connettori distinti. Alcune app aprono solo formati non compressi — usa un player che gestisce UVC Frame-Based/H.264 (OBS, VLC, ffplay).
- **Immagine assente o corrotta.** Controlla che la daughterboard MIPI-CSI2 MB1854 con l’IMX335 sia inserita correttamente; un sensore diverso non è testato e può richiedere una configurazione ISP differente.

## Dalla cattura all’AI on-device

Questo firmware esercita il percorso imaging + encoder H.264, **non ancora la NPU**. È il primo passo deliberato di una serie: validare end-to-end la catena sensore → ISP → encode → USB su una board accessibile, prima di innestare l’inferenza. I progetti successivi sfrutteranno l’acceleratore Neural-ART per AI on-device — rilevamento, classificazione o pre-elaborazione direttamente sullo stream, senza inviare i frame a un host. La pipeline costruita qui resta la base su cui quegli sviluppi si appoggeranno.

## I tre collegamenti della catena, contati

I conti su tutta la catena servono perché il collo di bottiglia non sta dove l'intuizione lo mette — e sapere quale sia è esattamente ciò che dice se una risoluzione superiore è raggiungibile o no.

### Il trasporto USB: il piu' scarico dei tre

Una singola endpoint isocrona su USB High-Speed trasporta al massimo 3 transazioni da 1024 byte per microframe, cioè **196,6 Mbit/s**. Contro quel budget:

| Risoluzione | Pixel | YUY2 a 30 fps | Sta in una endpoint isocrona HS? |
|---|---|---|---|
| **480×272** | 130 560 | 62,7 Mbit/s | sì |
| 640×480 | 307 200 | 147,5 Mbit/s | sì |
| 854×480 | 409 920 | 196,8 Mbit/s | **no** |
| 1280×720 | 921 600 | 442,4 Mbit/s | **no** |
| 1920×1080 | 2 073 600 | 995,3 Mbit/s | **no** |

Il risultato è controintuitivo e va detto con precisione: **a 480×272 il flusso non compresso ci starebbe**, occupando il 32 % del budget. L'H.264 qui non serve a far entrare lo stream nell'USB — serve perché esercitare l'encoder hardware è lo scopo del progetto, e perché è l'unica strada per le risoluzioni che l'USB non regge: il non compresso sfonda il budget appena sopra i **409.600 pixel**, quindi 854×480 è già fuori e il 720p chiederebbe 442 Mbit/s.

### La memoria: il vincolo che morde davvero

Quello che ferma le risoluzioni alte su questa board non è l'USB e non è l'encoder: è la RAM, e il conto è semplice perché un frame YUY2 occupa esattamente due byte per pixel.

| Risoluzione | YUY2 (16 bpp) | Solo luma (8 bpp) | Doppio buffer YUY2 | Rispetto a 480×272 |
|---|---|---|---|---|
| **480×272** | 255 kB | 128 kB | 0,50 MB | × 1,0 |
| 640×480 | 600 kB | 300 kB | 1,17 MB | × 2,4 |
| 854×480 | 801 kB | 400 kB | 1,56 MB | × 3,1 |
| 1280×720 | 1800 kB | 900 kB | 3,52 MB | × 7,1 |
| 1920×1080 | 4050 kB | 2025 kB | 7,91 MB | × 15,9 |

Un 480×272 in doppio buffer sta in **mezzo megabyte**; il 720p ne chiede **3,52**, sette volte tanto — e a quello va aggiunto ciò che serve all'encoder per i suoi frame di riferimento, che l'H.264 richiede per la predizione inter-frame. Senza PSRAM esterna il Nucleo non ha spazio per quella somma: è la ragione tecnica precisa del limite dichiarato più sotto, e la colonna «solo luma» spiega anche il compromesso monocromatico — rinunciare alla crominanza **dimezza** l'occupazione lungo tutto il percorso di codifica, da 255 a 128 kB per frame.

### Il link MIPI: il piu' carico, e per un motivo non ovvio

Il collegamento più sollecitato della catena è il primo, e la ragione è che il DCMIPP **scala a valle**: sul link CSI-2 viaggia il RAW alla risoluzione con cui il sensore è configurato, non i 480×272 dell'uscita.

| Cosa viaggia sul link | Data rate | Capacità (2 lane) | Occupazione |
|---|---|---|---|
| 2592×1944 RAW10 30 fps (piena risoluzione) | 1512 Mbit/s | 2000 Mbit/s | 75,6 % |
| 1296×972 RAW10 30 fps (binning 2×2) | 378 Mbit/s | 2000 Mbit/s | 18,9 % |
| 480×272 RAW10 30 fps (se si potesse croppare così) | 39 Mbit/s | 2000 Mbit/s | 2,0 % |

Alla piena risoluzione dell'IMX335 le due lane sono al **75,6 %** di occupazione: il link è il componente più vicino al proprio limite di tutta la catena, mentre l'USB sta al 32 % e l'encoder non fatica. È anche il motivo per cui il *binning* sul sensore, quando disponibile, conviene sempre: dividere per quattro i pixel sul link libera banda che nessuno stadio a valle potrà restituire. I conti si possono rifare con il [calcolatore di banda MIPI CSI-2](/tools/mipi-csi2-bandwidth/) — ricordando di aggiungere il blanking, che su questi numeri vale un altro 20–30 %.

Riassunto della catena, in una riga: **MIPI 76 %, encoder comodo, USB 32 %, RAM esaurita**. Chi volesse salire di risoluzione su questa board deve partire dall'ultimo termine, non dai primi tre.

## Limiti noti

Output monocromatico: l’encoder H.264 richiede input YUV 4:2:0 ma viene riempito solo il piano di luma (crominanza grigia statica) — è un compromesso RAM/banda dovuto all’assenza di PSRAM sul Nucleo; la build DK dell’esempio originale fornisce il colore.

Stream singolo 480×272 @ 30 fps (risoluzioni superiori richiederebbero PSRAM esterna); le chiavi di firma sono le chiavi dummy di sviluppo ST — da non usare in produzione. Stato: prototipo funzionante, port personale pubblicato as-is.

## Crediti e licenza

Riusa e adatta codice da progetti ST upstream, tutto vendorizzato nel repo con la rispettiva licenza originale: l’esempio `camera-capture` DK, lo STM32CubeN6 (HAL/CMSIS/BSP/FSBL ref.), l’encoder H.264 Verisilicon H1 + EWL, la `stm32-camera-middleware` + ISP, USBX (Azure RTOS / ThreadX), il kernel FreeRTOS e l’ExtMem Manager ST.

I file specifici dell’applicazione seguono i termini dell’esempio ST da cui derivano; ogni componente di terze parti mantiene il proprio file di licenza accanto ai sorgenti.
