Document Si4684 driver, TunerService, and tuner HTTP API (0.4.0).
Adds ch-si4684 and updates API/class reference chapters for slice 4. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -6,7 +6,7 @@ implemented in \texttt{SetupWebServer}. Request bodies are parsed into
|
|||||||
domain types in the pure core (\texttt{components/core}) before any
|
domain types in the pure core (\texttt{components/core}) before any
|
||||||
persistence or driver call. Exact C++ signatures live in the generated
|
persistence or driver call. Exact C++ signatures live in the generated
|
||||||
Doxygen output under \texttt{docs/api/}; this chapter documents the
|
Doxygen output under \texttt{docs/api/}; this chapter documents the
|
||||||
wire protocol and behaviour as shipped in firmware~0.2.0 (Slices~1--2).
|
wire protocol and behaviour as shipped in firmware~0.4.0 (Slices~1--4).
|
||||||
|
|
||||||
\section{Transport and reachability}
|
\section{Transport and reachability}
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ Returns a health-check DTO serialised by
|
|||||||
|
|
||||||
\begin{drnote}[Response schema]
|
\begin{drnote}[Response schema]
|
||||||
\begin{drcode}[JSON]
|
\begin{drcode}[JSON]
|
||||||
{"status":"ok","fw":"0.2.0"}
|
{"status":"ok","fw":"0.4.0"}
|
||||||
\end{drcode}
|
\end{drcode}
|
||||||
\begin{itemize}
|
\begin{itemize}
|
||||||
\item \texttt{status} --- coarse indicator; \texttt{ok} when the
|
\item \texttt{status} --- coarse indicator; \texttt{ok} when the
|
||||||
@@ -93,10 +93,89 @@ HTTP status: \textbf{200 OK} on save success; \textbf{400 Bad Request} for
|
|||||||
parse/validation failures; \textbf{500 Internal Server Error} when NVS
|
parse/validation failures; \textbf{500 Internal Server Error} when NVS
|
||||||
persistence fails.
|
persistence fails.
|
||||||
|
|
||||||
|
\subsection{\texttt{GET /api/tuner/status}}
|
||||||
|
\label{sec:api-tuner-status}
|
||||||
|
|
||||||
|
Returns a tuner snapshot serialised by
|
||||||
|
\texttt{core::serializeTunerStatusJson()} from \texttt{core::TunerStatus}.
|
||||||
|
The handler calls \texttt{tuner::TunerService::refreshStatus()}.
|
||||||
|
|
||||||
|
\begin{drnote}[Response schema (DAB example)]
|
||||||
|
\begin{drcode}[JSON]
|
||||||
|
{"booted":true,"band":"dab","locked":true,"volume":63,
|
||||||
|
"dab":{"freq_index":12,"fic_quality":80,"cnr_db":25},"fm":null}
|
||||||
|
\end{drcode}
|
||||||
|
For FM, \texttt{fm} carries \texttt{frequency\_khz}, \texttt{rssi\_dbuv},
|
||||||
|
\texttt{snr\_db}, and \texttt{stereo}; \texttt{dab} is \texttt{null}.
|
||||||
|
\end{drnote}
|
||||||
|
|
||||||
|
HTTP status: \textbf{200 OK}; \textbf{500} with
|
||||||
|
\texttt{\{"status":"error","reason":...\}} on driver failure;
|
||||||
|
\textbf{503} when the tuner service is unavailable.
|
||||||
|
|
||||||
|
\subsection{\texttt{GET /api/tuner/services}}
|
||||||
|
\label{sec:api-tuner-services}
|
||||||
|
|
||||||
|
Lists DAB programmes for the current ensemble via
|
||||||
|
\texttt{TunerService::listDabServices()}.
|
||||||
|
|
||||||
|
\begin{drnote}[Response schema]
|
||||||
|
\begin{drcode}[JSON]
|
||||||
|
{"services":[{"service_id":12345,"component_id":1,"label":"BBC R1"}]}
|
||||||
|
\end{drcode}
|
||||||
|
\end{drnote}
|
||||||
|
|
||||||
|
HTTP status: \textbf{200 OK}; \textbf{409 Conflict} when the list cannot be
|
||||||
|
fetched (wrong band, empty ensemble, hardware error).
|
||||||
|
|
||||||
|
\subsection{\texttt{POST /api/tuner/tune}}
|
||||||
|
\label{sec:api-tuner-tune}
|
||||||
|
|
||||||
|
Tunes to a DAB ensemble or FM frequency. Body parsed by
|
||||||
|
\texttt{core::parseTunerTuneJson()}.
|
||||||
|
|
||||||
|
\begin{drnote}[Request schema]
|
||||||
|
\begin{drcode}[JSON]
|
||||||
|
{"band":"dab","freq_index":12}
|
||||||
|
\end{drcode}
|
||||||
|
or
|
||||||
|
\begin{drcode}[JSON]
|
||||||
|
{"band":"fm","frequency_khz":101500}
|
||||||
|
\end{drcode}
|
||||||
|
DAB \texttt{freq\_index} is 0--37; FM \texttt{frequency\_khz} is
|
||||||
|
64\,000--108\,000.
|
||||||
|
\end{drnote}
|
||||||
|
|
||||||
|
On success returns the updated status JSON (same shape as
|
||||||
|
\texttt{GET /api/tuner/status}). HTTP status: \textbf{200 OK};
|
||||||
|
\textbf{400} for invalid JSON; \textbf{409} for tune failures.
|
||||||
|
|
||||||
|
\subsection{\texttt{POST /api/tuner/play}}
|
||||||
|
\label{sec:api-tuner-play}
|
||||||
|
|
||||||
|
Starts a DAB audio service. Body parsed by
|
||||||
|
\texttt{core::parseTunerPlayJson()}.
|
||||||
|
|
||||||
|
\begin{drnote}[Request schema]
|
||||||
|
\begin{drcode}[JSON]
|
||||||
|
{"service_id":12345,"component_id":1}
|
||||||
|
\end{drcode}
|
||||||
|
\end{drnote}
|
||||||
|
|
||||||
|
Success response: \texttt{\{"status":"playing"\}}. HTTP status:
|
||||||
|
\textbf{200 OK}; \textbf{409} when playback cannot start.
|
||||||
|
|
||||||
|
\subsection{\texttt{POST /api/tuner/seek}}
|
||||||
|
\label{sec:api-tuner-seek}
|
||||||
|
|
||||||
|
Seeks FM upward (no request body). Returns
|
||||||
|
\texttt{\{"frequency\_khz":...\}} on success. HTTP status: \textbf{200 OK};
|
||||||
|
\textbf{409} on seek failure.
|
||||||
|
|
||||||
\section{Boot and network state machine}
|
\section{Boot and network state machine}
|
||||||
\label{sec:api-boot-flow}
|
\label{sec:api-boot-flow}
|
||||||
|
|
||||||
At boot, \texttt{net::NetBootstrap::start()} consults
|
At boot, \texttt{net::NetBootstrap::start(store, tuner)} consults
|
||||||
\texttt{ISecureStore::hasWifiCredentials()}:
|
\texttt{ISecureStore::hasWifiCredentials()}:
|
||||||
|
|
||||||
\begin{enumerate}
|
\begin{enumerate}
|
||||||
|
|||||||
@@ -50,15 +50,17 @@ RAII wrapper around the ESP-IDF Wi-Fi stack that starts and stops the
|
|||||||
configured SoftAP. Imperative shell; no business logic.
|
configured SoftAP. Imperative shell; no business logic.
|
||||||
|
|
||||||
\section{SetupWebServer}\label{cls:SetupWebServer}
|
\section{SetupWebServer}\label{cls:SetupWebServer}
|
||||||
Minimal HTTP server: gzipped setup UI, \texttt{GET /api/health}, and
|
Minimal HTTP server: gzipped setup UI, \texttt{GET /api/health},
|
||||||
\texttt{POST /api/wifi} for provisioning. JSON parsing and serialisation
|
\texttt{POST /api/wifi}, and tuner routes (\texttt{/api/tuner/*}).
|
||||||
delegate to the pure core; credentials persist via \texttt{ISecureStore}.
|
JSON parsing and serialisation delegate to the pure core; credentials
|
||||||
|
persist via \texttt{ISecureStore}; tuner operations via
|
||||||
|
\texttt{tuner::TunerService}.
|
||||||
|
|
||||||
\section{NetBootstrap}\label{cls:NetBootstrap}
|
\section{NetBootstrap}\label{cls:NetBootstrap}
|
||||||
Owns network resources for setup or STA mode. \texttt{start(store)}
|
Owns network resources for setup or STA mode.
|
||||||
initialises the platform, joins stored Wi-Fi when credentials exist, or
|
\texttt{start(store, tuner)} initialises the platform, joins stored Wi-Fi
|
||||||
falls back to the \texttt{DigiRadio-setup} SoftAP. Must outlive
|
when credentials exist, or falls back to the \texttt{DigiRadio-setup}
|
||||||
\texttt{app\_main} for the process lifetime.
|
SoftAP. Must outlive \texttt{app\_main} for the process lifetime.
|
||||||
|
|
||||||
% ------------------------------------------------------------------
|
% ------------------------------------------------------------------
|
||||||
% Domain core + secure store (Slice 2)
|
% Domain core + secure store (Slice 2)
|
||||||
@@ -92,8 +94,60 @@ are stored as NVS strings and never logged. Production should enable NVS
|
|||||||
encryption using the reserved \texttt{nvs\_keys} partition.
|
encryption using the reserved \texttt{nvs\_keys} partition.
|
||||||
|
|
||||||
% ------------------------------------------------------------------
|
% ------------------------------------------------------------------
|
||||||
% Future slices (drivers, services)
|
% Hardware drivers (Slice 3)
|
||||||
% ------------------------------------------------------------------
|
% ------------------------------------------------------------------
|
||||||
% \section{Si4684Driver}\label{cls:Si4684Driver}
|
|
||||||
% \section{TunerService}\label{cls:TunerService}
|
\section{IFirmwareBlobReader}\label{cls:IFirmwareBlobReader}
|
||||||
% ...
|
Abstract streaming reader for firmware blobs embedded in flash. The Si4684
|
||||||
|
driver uses it to HOST\_LOAD patch and application images in bounded SPI
|
||||||
|
chunks without allocating the full image on the heap. Host tests use the
|
||||||
|
same interface over in-memory buffers.
|
||||||
|
|
||||||
|
\section{EmbeddedBlobReader}\label{cls:EmbeddedBlobReader}
|
||||||
|
Non-owning \texttt{IFirmwareBlobReader} over a contiguous byte range (linker
|
||||||
|
symbols from \texttt{EMBED\_FILES} or test data). Implements offset-based
|
||||||
|
\texttt{read()} with truncation at end-of-blob.
|
||||||
|
|
||||||
|
\section{Si4684EmbeddedImages}\label{cls:Si4684EmbeddedImages}
|
||||||
|
Owns \texttt{EmbeddedBlobReader} views over \texttt{rom\_patch\_016.bin},
|
||||||
|
\texttt{dab\_firmware.bin}, and \texttt{fm\_firmware.bin}. Constructed once;
|
||||||
|
passed to \texttt{Si4684Driver}. \texttt{applicationImage(Si4684Band)} selects
|
||||||
|
DAB or FM.
|
||||||
|
|
||||||
|
\section{Si4684Driver}\label{cls:Si4684Driver}
|
||||||
|
Full SPI driver for the Si4684 DAB+/FM tuner (Chapter~\ref{ch:si4684}).
|
||||||
|
|
||||||
|
\texttt{boot(Si4684Band)} runs AN649 HOST\_LOAD, configures I\textsuperscript{2}S
|
||||||
|
and band-specific properties, and records \texttt{loadedBand()}. After boot the
|
||||||
|
driver exposes FM tuning/seek/RSQ/RDS, DAB ensemble tuning, digital service list
|
||||||
|
fetch, and \texttt{startDabService}. Property access (\texttt{setProperty},
|
||||||
|
\texttt{setVolume}) and diagnostics (\texttt{getPartInfo}, \texttt{getSysState})
|
||||||
|
are included. Wrong-band calls return \texttt{Si4684Error::WrongBand}.
|
||||||
|
Register-level opcodes remain private; see \texttt{Si4684Types.hpp} for status
|
||||||
|
DTOs.
|
||||||
|
|
||||||
|
\section{Adau1701Driver}\label{cls:Adau1701Driver}
|
||||||
|
RAII I2C driver for the ADAU1701 SigmaDSP. \texttt{boot()} asserts RESET\#,
|
||||||
|
initialises the shared I2C bus, and replays the SigmaStudio export from
|
||||||
|
\texttt{Firmware/ADAU1701-Firmware/} on every power-up (no EEPROM self-boot
|
||||||
|
on DigiRadio).
|
||||||
|
|
||||||
|
% ------------------------------------------------------------------
|
||||||
|
% Application services (Slice 4)
|
||||||
|
% ------------------------------------------------------------------
|
||||||
|
|
||||||
|
\section{ITuner}\label{cls:ITuner}
|
||||||
|
Abstract tuner boundary in the pure core. Defines boot, band selection,
|
||||||
|
status readout, FM/DAB tuning, service list, playback, and volume without
|
||||||
|
ESP-IDF types. \texttt{si4684::Si4684Tuner} implements it on device; host
|
||||||
|
tests can use fakes.
|
||||||
|
|
||||||
|
\section{TunerService}\label{cls:TunerService}
|
||||||
|
Application service exposing intent-level tuner operations to HTTP and
|
||||||
|
future UI. Holds a reference to \texttt{core::ITuner}, tracks last tune
|
||||||
|
target and volume, and maps driver failures to \texttt{core::TunerError}.
|
||||||
|
|
||||||
|
\section{Si4684Tuner}\label{cls:Si4684Tuner}
|
||||||
|
\texttt{ITuner} adapter over \texttt{Si4684Driver}. Translates domain calls
|
||||||
|
into SPI commands and maps \texttt{Si4684Error} to \texttt{TunerError}.
|
||||||
|
Constructed once in \texttt{HardwareBootstrap} alongside the driver.
|
||||||
|
|||||||
@@ -125,16 +125,9 @@ verified against reference values in host tests.
|
|||||||
At power-up the driver follows the documented boot sequence: power on,
|
At power-up the driver follows the documented boot sequence: power on,
|
||||||
load the patch/bootloader, load the firmware image (FM or DAB), then
|
load the patch/bootloader, load the firmware image (FM or DAB), then
|
||||||
boot. Firmware images are large and are streamed to the chip in bounded
|
boot. Firmware images are large and are streamed to the chip in bounded
|
||||||
chunks from flash rather than buffered whole in RAM.
|
chunks from flash rather than buffered whole in RAM. See
|
||||||
|
Chapter~\ref{ch:si4684} for blob sources, uGreen extraction, and the full
|
||||||
\begin{drref}[Datasheet]
|
\texttt{Si4684Driver} API (tuning, RSQ, DAB service list).
|
||||||
The exact power-up, patch, image-load, and boot command sequence follows
|
|
||||||
the Si468x programming guide (AN649). Each step in the driver cites the
|
|
||||||
relevant section; no command byte is issued without a documented source.
|
|
||||||
\end{drref} The public
|
|
||||||
interface is intent-level (\texttt{powerUp}, \texttt{loadImage},
|
|
||||||
\texttt{tuneTo}, \texttt{readRsq}); register-level detail is private to
|
|
||||||
the driver.
|
|
||||||
|
|
||||||
\paragraph{ADAU1701 (DSP).}
|
\paragraph{ADAU1701 (DSP).}
|
||||||
The board carries no self-boot EEPROM. Instead, the ESP32 writes the
|
The board carries no self-boot EEPROM. Instead, the ESP32 writes the
|
||||||
@@ -184,6 +177,28 @@ values cannot be logged or implicitly converted to a string; buffers are
|
|||||||
cleared on destruction. Production builds should enable NVS encryption
|
cleared on destruction. Production builds should enable NVS encryption
|
||||||
at rest (see Chapter~\ref{sec:api-storage}).
|
at rest (see Chapter~\ref{sec:api-storage}).
|
||||||
|
|
||||||
|
\section{Companion-chip boot at power-up}
|
||||||
|
\label{sec:fw-chip-boot}
|
||||||
|
|
||||||
|
The ESP32-S3 loads both audio companion chips from assets under
|
||||||
|
\texttt{Software/Firmware/} during \texttt{app\_main}, before network
|
||||||
|
bring-up:
|
||||||
|
|
||||||
|
\begin{enumerate}
|
||||||
|
\item \textbf{Si4684} (SPI): ROM patch \texttt{rom\_patch\_016.bin}, then either
|
||||||
|
DAB image \texttt{dab\_firmware.bin} or FM image \texttt{fm\_firmware.bin},
|
||||||
|
following AN649 (Chapter~\ref{ch:si4684}). DAB blob default from PE5PVB; FM
|
||||||
|
from eval pack or uGreen \texttt{radio\_cli}. Blobs are local-only
|
||||||
|
(\texttt{.gitignore}); embedded via ESP-IDF \texttt{EMBED\_FILES}.
|
||||||
|
\item \textbf{ADAU1701} (I\textsuperscript{2}C): SigmaStudio export
|
||||||
|
(\texttt{DigiRadio\_IC\_1.h}) replayed through
|
||||||
|
\texttt{SIGMA\_WRITE\_REGISTER\_BLOCK} after hardware reset. The DSP
|
||||||
|
program lives in RAM only; download runs on every boot.
|
||||||
|
\end{enumerate}
|
||||||
|
|
||||||
|
If either driver returns an error, the firmware logs the failure and stops
|
||||||
|
before starting Wi-Fi (fail-closed bring-up).
|
||||||
|
|
||||||
\section{Error-handling model}
|
\section{Error-handling model}
|
||||||
\label{sec:fw-errors}
|
\label{sec:fw-errors}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,258 @@
|
|||||||
|
% ============================================================
|
||||||
|
% DigiRadio — Manual chapter: Si4684 tuner
|
||||||
|
% ============================================================
|
||||||
|
|
||||||
|
\chapter{Si4684 DAB+/FM Tuner}
|
||||||
|
\label{ch:si4684}
|
||||||
|
|
||||||
|
The Skyworks Si4684 is the primary RF front-end of DigiRadio: it demodulates
|
||||||
|
DAB/DAB+ and FM, decodes RDS, and delivers PCM over I\textsuperscript{2}S to
|
||||||
|
the ADAU1701. This chapter documents how firmware images are obtained and kept
|
||||||
|
local, how the chip is booted on the ESP32-S3, and how \texttt{Si4684Driver}
|
||||||
|
exposes every day-to-day operation without leaking register-level details to
|
||||||
|
application code.
|
||||||
|
|
||||||
|
\section{Role in the audio chain}
|
||||||
|
\label{sec:si4684-role}
|
||||||
|
|
||||||
|
After boot the Si4684 runs as an I\textsuperscript{2}S master at
|
||||||
|
44.1\,kHz stereo. The ESP32 loads proprietary application firmware into chip
|
||||||
|
RAM at every cold start (there is no self-boot EEPROM on DigiRadio). Tuning,
|
||||||
|
service selection, and signal-quality reads all go through
|
||||||
|
\texttt{si4684::Si4684Driver}; higher layers (\texttt{TunerService}, web UI)
|
||||||
|
will depend on that class rather than issuing SPI transactions directly.
|
||||||
|
|
||||||
|
\begin{drref}[Programming guide]
|
||||||
|
Command opcodes, property IDs, and reply layouts follow Skyworks application
|
||||||
|
note \textbf{AN649} (Si468x programming guide). The DigiRadio driver aligns
|
||||||
|
with the PE5PVB/SI4684-DAB-Receiver reference for DAB and with community FM
|
||||||
|
images for the FM application blob.
|
||||||
|
\end{drref}
|
||||||
|
|
||||||
|
\section{Firmware images}
|
||||||
|
\label{sec:si4684-firmware}
|
||||||
|
|
||||||
|
Three binary blobs live under \texttt{Firmware/Si4684-Firmware/}. They are
|
||||||
|
\textbf{not} committed to the public Git repository (proprietary Skyworks /
|
||||||
|
uGreen licence); \texttt{.gitignore} keeps them on the developer machine only.
|
||||||
|
|
||||||
|
\begin{table}[htbp]
|
||||||
|
\centering
|
||||||
|
\begin{tabular}{@{}llp{6.2cm}@{}}
|
||||||
|
\toprule
|
||||||
|
\textbf{Local file} & \textbf{Typ.\ size} & \textbf{Source} \\
|
||||||
|
\midrule
|
||||||
|
\texttt{rom\_patch\_016.bin} & 5796 B &
|
||||||
|
ROM patch (\texttt{rom00\_patch.016.bin}); refreshed from
|
||||||
|
PE5PVB or uGreen \texttt{radio\_cli}. \\
|
||||||
|
\texttt{dab\_firmware.bin} & $\sim$517--497 KB &
|
||||||
|
DAB application image; default from PE5PVB header extract; optional
|
||||||
|
uGreen \texttt{dab\_radio\_6\_0\_6.bin}. \\
|
||||||
|
\texttt{fm\_firmware.bin} & $\sim$530 KB &
|
||||||
|
FM/HD application; eval CD, dabpi \texttt{si46xx\_firmware/}, or uGreen
|
||||||
|
\texttt{fmhd\_radio\_5\_1\_3.bin}. \\
|
||||||
|
\bottomrule
|
||||||
|
\end{tabular}
|
||||||
|
\caption{Si4684 firmware blobs (local only).}
|
||||||
|
\label{tab:si4684-blobs}
|
||||||
|
\end{table}
|
||||||
|
|
||||||
|
\subsection{Where images come from}
|
||||||
|
\label{sec:si4684-sources}
|
||||||
|
|
||||||
|
Community projects name the same Skyworks files differently; none of them
|
||||||
|
redistribute the binaries in git:
|
||||||
|
|
||||||
|
\begin{itemize}
|
||||||
|
\item \textbf{PE5PVB/SI4684-DAB-Receiver} --- DAB image and ROM patch as
|
||||||
|
C arrays (\texttt{firmware.h}, \texttt{Si468xROM.h}). Safe to automate.
|
||||||
|
\item \textbf{uGreen DABBoard} (\url{https://ugreen.eu/downloads/}) ---
|
||||||
|
\texttt{Files\_v16.zip} embeds FM, DAB, and patch inside
|
||||||
|
\texttt{radio\_cli}; use \texttt{tools/extract\_ugreen\_radio\_cli.py}.
|
||||||
|
\item \textbf{hitech95/si468x\_dab\_receiver} --- Linux driver only; device
|
||||||
|
tree references \texttt{si468x/fmhd\_radio\_5\_1\_0.bin} but does not
|
||||||
|
ship the files (closed source).
|
||||||
|
\item \textbf{PhilBladen/Radio} --- STM32 reference using \emph{external}
|
||||||
|
SPI flash (\texttt{FLASH\_LOAD}); different hardware path from DigiRadio's
|
||||||
|
\texttt{HOST\_LOAD} flow.
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
\begin{drcaution}[Licence]
|
||||||
|
Do not push \texttt{*.bin} firmware to GitHub. Obtain images from your own
|
||||||
|
eval board, uGreen customer download, or dabpi folder; keep them in
|
||||||
|
\texttt{Firmware/Si4684-Firmware/} locally.
|
||||||
|
\end{drcaution}
|
||||||
|
|
||||||
|
\subsection{Refreshing blobs}
|
||||||
|
\label{sec:si4684-refresh}
|
||||||
|
|
||||||
|
\begin{verbatim}
|
||||||
|
# DAB + patch from PE5PVB (always safe to re-run):
|
||||||
|
python3 tools/fetch_si4684_firmware.py --dab-only
|
||||||
|
|
||||||
|
# FM from uGreen radio_cli (Files_v16.zip):
|
||||||
|
python3 tools/fetch_si4684_firmware.py \
|
||||||
|
--from-ugreen-radio-cli ~/Downloads/Files_v16.zip
|
||||||
|
|
||||||
|
# All three blobs from uGreen (overrides PE5PVB DAB):
|
||||||
|
python3 tools/fetch_si4684_firmware.py \
|
||||||
|
--from-ugreen-radio-cli ~/Downloads/Files_v16.zip --ugreen-all
|
||||||
|
|
||||||
|
# FM from dabpi / Skyworks eval folder:
|
||||||
|
python3 tools/fetch_si4684_firmware.py --si46xx-dir /path/to/si46xx_firmware
|
||||||
|
\end{verbatim}
|
||||||
|
|
||||||
|
Low-level helpers: \texttt{tools/extract\_si4684\_blob.py} (C header or copy),
|
||||||
|
\texttt{tools/extract\_ugreen\_radio\_cli.py} (ELF symbol scrape).
|
||||||
|
|
||||||
|
\section{Boot sequence (HOST\_LOAD)}
|
||||||
|
\label{sec:si4684-boot}
|
||||||
|
|
||||||
|
DigiRadio follows AN649 ``cold start with HOST\_LOAD'' (same as PE5PVB), not
|
||||||
|
the \texttt{FLASH\_LOAD} path used by boards with a pre-programmed SST25V.
|
||||||
|
|
||||||
|
\begin{figure}[htbp]
|
||||||
|
\centering
|
||||||
|
\begin{tikzpicture}[
|
||||||
|
step/.style={draw, rounded corners, minimum width=9cm,
|
||||||
|
minimum height=0.9cm, align=center, font=\small},
|
||||||
|
>={Latex}, node distance=3mm]
|
||||||
|
\node[step, fill=black!6] (rst) {Hardware reset (RST\#)};
|
||||||
|
\node[step, fill=black!6, below=of rst] (pu)
|
||||||
|
{POWER\_UP (crystal / clock args)};
|
||||||
|
\node[step, fill=black!6, below=of pu] (li1) {LOAD\_INIT};
|
||||||
|
\node[step, fill=black!8, below=of li1] (patch)
|
||||||
|
{HOST\_LOAD \texttt{rom\_patch\_016.bin} (124 B chunks)};
|
||||||
|
\node[step, fill=black!6, below=of patch] (li2) {LOAD\_INIT};
|
||||||
|
\node[step, fill=black!8, below=of li2] (img)
|
||||||
|
{HOST\_LOAD \texttt{dab\_} or \texttt{fm\_firmware.bin}
|
||||||
|
(2044 B chunks)};
|
||||||
|
\node[step, fill=black!6, below=of img] (boot) {BOOT};
|
||||||
|
\node[step, fill=black!10, below=of boot] (cfg)
|
||||||
|
{Properties: I\textsuperscript{2}S 44.1 kHz, RDS/DAB FE, volume};
|
||||||
|
\foreach \a/\b in {rst/pu, pu/li1, li1/patch, patch/li2, li2/img,
|
||||||
|
img/boot, boot/cfg} {
|
||||||
|
\draw[->] (\a) -- (\b);
|
||||||
|
}
|
||||||
|
\end{tikzpicture}
|
||||||
|
\caption{Si4684 boot flow on DigiRadio (AN649 / PE5PVB).}
|
||||||
|
\label{fig:si4684-boot}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
Images are embedded in the ESP32 flash partition via ESP-IDF
|
||||||
|
\texttt{EMBED\_FILES} and streamed through \texttt{IFirmwareBlobReader} so the
|
||||||
|
full $\sim$1\,MB of patch + DAB + FM never has to fit in RAM at once. Switching
|
||||||
|
band (\texttt{Si4684Band::Fm} $\leftrightarrow$ \texttt{Dab}) performs reset and
|
||||||
|
a complete reload of patch + the other application image.
|
||||||
|
|
||||||
|
\section{Si4684Driver API}
|
||||||
|
\label{sec:si4684-driver}
|
||||||
|
|
||||||
|
\texttt{Si4684Driver} owns SPI, implements boot, and exposes intent-level
|
||||||
|
methods grouped below. All return \texttt{std::expected<T, Si4684Error>}; FM
|
||||||
|
calls fail with \texttt{WrongBand} if the FM image is not loaded, and vice
|
||||||
|
versa for DAB.
|
||||||
|
|
||||||
|
\subsection{Bring-up and diagnostics}
|
||||||
|
|
||||||
|
\begin{table}[htbp]
|
||||||
|
\centering
|
||||||
|
\begin{tabular}{@{}ll@{}}
|
||||||
|
\toprule
|
||||||
|
\textbf{Method} & \textbf{Purpose} \\
|
||||||
|
\midrule
|
||||||
|
\texttt{boot(Si4684Band)} & Cold start + default properties \\
|
||||||
|
\texttt{isBooted()}, \texttt{loadedBand()} & State query \\
|
||||||
|
\texttt{getPartInfo()} & Chip ID + firmware version \\
|
||||||
|
\texttt{getSysState()} & Running application type \\
|
||||||
|
\texttt{setProperty(id, value)} & Generic SET\_PROPERTY \\
|
||||||
|
\texttt{setVolume(0--63)} & Audio attenuator \\
|
||||||
|
\bottomrule
|
||||||
|
\end{tabular}
|
||||||
|
\caption{Si4684 bring-up and property access.}
|
||||||
|
\end{table}
|
||||||
|
|
||||||
|
\subsection{FM operations}
|
||||||
|
|
||||||
|
Requires \texttt{boot(Si4684Band::Fm)}.
|
||||||
|
|
||||||
|
\begin{itemize}
|
||||||
|
\item \texttt{tuneFm(frequencyKhz)} --- FM\_TUNE\_FREQ, waits for STC.
|
||||||
|
\item \texttt{seekFm(up, wrap)} --- FM\_SEEK\_START; returns tuned kHz.
|
||||||
|
\item \texttt{readFmRsq()} --- RSSI, SNR, stereo flag, validity.
|
||||||
|
\item \texttt{readFmRds()} --- last RDS group blocks A--D.
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
\subsection{DAB operations}
|
||||||
|
|
||||||
|
Requires \texttt{boot(Si4684Band::Dab)}. The default Band~III frequency plan
|
||||||
|
(38 channels, Table~\ref{tab:si4684-dab-plan}) is installed automatically
|
||||||
|
after boot.
|
||||||
|
|
||||||
|
\begin{itemize}
|
||||||
|
\item \texttt{installDefaultDabFrequencyPlan()} --- DAB\_SET\_FREQ\_LIST
|
||||||
|
(also called from boot).
|
||||||
|
\item \texttt{tuneDab(freqIndex)} --- tune ensemble by index 0--37.
|
||||||
|
\item \texttt{readDabDigRadStatus()} --- FIC quality, CNR, lock.
|
||||||
|
\item \texttt{readDabEventStatus()} --- service-list-ready flag.
|
||||||
|
\item \texttt{fetchDabServiceList()} --- parsed services + labels.
|
||||||
|
\item \texttt{startDabService(serviceId, componentId)} --- begin audio
|
||||||
|
decode (START\_DIGITAL\_SERVICE).
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
\begin{table}[htbp]
|
||||||
|
\centering
|
||||||
|
\small
|
||||||
|
\begin{tabular}{@{}cl@{}}
|
||||||
|
\toprule
|
||||||
|
\textbf{Index} & \textbf{Centre frequency (kHz)} \\
|
||||||
|
\midrule
|
||||||
|
0--3 & 174928 -- 180064 (5A--5D) \\
|
||||||
|
4--7 & 181936 -- 187072 (6A--6D) \\
|
||||||
|
8--11 & 188928 -- 194064 (7A--7D) \\
|
||||||
|
12--15 & 195936 -- 201072 (8A--8D) \\
|
||||||
|
16--19 & 202928 -- 208064 (9A--9D) \\
|
||||||
|
20--23 & 209936 -- 215072 (10A--10D) \\
|
||||||
|
24--27 & 216928 -- 222064 (11A--11D) \\
|
||||||
|
28--31 & 223936 -- 229072 (12A--12D) \\
|
||||||
|
32--35 & 230784 -- 235776 (13A--13D) \\
|
||||||
|
36--37 & 237488 -- 239200 (13E--13F) \\
|
||||||
|
\bottomrule
|
||||||
|
\end{tabular}
|
||||||
|
\caption{DAB Band III plan (\texttt{kDefaultDabFrequencyKhz}).}
|
||||||
|
\label{tab:si4684-dab-plan}
|
||||||
|
\end{table}
|
||||||
|
|
||||||
|
\subsection{Typical DAB session}
|
||||||
|
|
||||||
|
\begin{enumerate}
|
||||||
|
\item \texttt{boot(Dab)} at power-up (already done in
|
||||||
|
\texttt{HardwareBootstrap}).
|
||||||
|
\item \texttt{tuneDab(index)} for the desired ensemble.
|
||||||
|
\item Poll \texttt{readDabDigRadStatus()} until FIC quality $> 0$.
|
||||||
|
\item \texttt{fetchDabServiceList()} when
|
||||||
|
\texttt{readDabEventStatus().serviceListReady}.
|
||||||
|
\item \texttt{startDabService(serviceId, componentId)} for the chosen
|
||||||
|
programme; audio appears on I\textsuperscript{2}S.
|
||||||
|
\end{enumerate}
|
||||||
|
|
||||||
|
\section{Integration at power-up}
|
||||||
|
\label{sec:si4684-integration}
|
||||||
|
|
||||||
|
\texttt{main/hardware\_bootstrap.cpp} constructs a static
|
||||||
|
\texttt{Si4684EmbeddedImages}, \texttt{Si4684Driver}, and
|
||||||
|
\texttt{Si4684Tuner}, calls \texttt{boot(Si4684Band::Dab)} before Wi-Fi,
|
||||||
|
then boots the ADAU1701. Failure is fail-closed (logged, no network start).
|
||||||
|
\texttt{main/main.cpp} wraps the tuner adapter in
|
||||||
|
\texttt{tuner::TunerService} and passes it to \texttt{NetBootstrap::start()}
|
||||||
|
so HTTP routes under \texttt{/api/tuner/*} can tune and play at runtime.
|
||||||
|
|
||||||
|
\section{Further reading}
|
||||||
|
\label{sec:si4684-reading}
|
||||||
|
|
||||||
|
\begin{itemize}
|
||||||
|
\item Skyworks AN649 --- command and property reference.
|
||||||
|
\item \texttt{Firmware/Si4684-Firmware/README.md} --- blob refresh cheatsheet.
|
||||||
|
\item PE5PVB \texttt{si4684.cpp} --- DAB service list and MOT (not yet ported).
|
||||||
|
\item uGreen \texttt{radio\_cli\_RELEASE\_NOTES.md} --- embedded firmware versions.
|
||||||
|
\end{itemize}
|
||||||
@@ -45,6 +45,7 @@
|
|||||||
\include{ch-intro}
|
\include{ch-intro}
|
||||||
\include{ch-hardware}
|
\include{ch-hardware}
|
||||||
\include{ch-firmware}
|
\include{ch-firmware}
|
||||||
|
\include{ch-si4684}
|
||||||
\include{ch-api}
|
\include{ch-api}
|
||||||
\include{ch-classes}
|
\include{ch-classes}
|
||||||
\include{ch-build}
|
\include{ch-build}
|
||||||
|
|||||||
Reference in New Issue
Block a user