Speaker
The MAX98357A amplifier and speaker on the TFT carriers, the Speaker library's tone synthesis and PCM streaming, clocking and diagnostics.
The Neuro Vision TFT and TFT-W carriers include a speaker driven by a MAX98357A I2S class D amplifier, connected to the STM32N6 SAI1 peripheral through the carrier connector. Other modules have no speaker, and begin() on them configures the peripheral with nothing connected. The Speaker library is the transmit counterpart of the Microphone library and follows the same structure.
#include <Speaker.h>
Speaker speaker;
void setup() {
speaker.begin(); // SAI, clock and DMA; the stream starts silent
speaker.setVolume(0.5f);
speaker.tone(440, 250); // 440 Hz for 250 ms
}
void loop() { }Output methods
| Method | Function |
|---|---|
tone(freq), tone(freq, ms), noTone() | On-device sine synthesis at the actual sample rate. A duration of 0 plays continuously |
write(pcm, n), availableForWrite() | Stream raw 16-bit mono PCM into the DMA ring. Returns the number of samples accepted |
setVolume(0.0 to 1.0) | Digital scale. Defaults to 1.0, the loudest undistorted level |
sampleRate(), playing(), end() | Actual rate, activity, shutdown |
debugDump() | Prints a [SPKDBG] block to the console |
tone() takes priority over the write() ring. When neither is active the stream emits silence.
Loudness above unity is set only by the amplifier's hardware GAIN strap, 3 to 15 dB. Output is mono; each sample is duplicated into both I2S slots and the amplifier's SD_MODE strap selects the channel.
Streaming PCM
const uint32_t fs = speaker.sampleRate();
int16_t chunk[128];
// fill chunk
while (speaker.availableForWrite() < 128) { }
speaker.write(chunk, 128);The SpeakerTone example plays an octave with tone() and then streams a sine sweep with write().
Audio clips
The board has no filesystem for audio and the application is RAM resident, so a full-length track cannot be stored. The SpeakerClip example includes clip_to_header.py, which converts a section of an audio file to a C header through ffmpeg. Clips under about 800 KB fit: roughly 8 seconds at 22050 Hz or 16 seconds at 16000 Hz. Playback of longer material requires a run-time data path such as USB streaming, which the firmware does not provide.
Hardware
| Signal | MCU pin | Alternate function | Amplifier |
|---|---|---|---|
| SAI1_SCK_B | PG1 | AF6 | BCLK |
| SAI1_FS_B | PG2 | AF6 | LRCLK |
| SAI1_SD_B | PE3 | AF6 | DIN |
| SD_MODE | PB6 | GPIO | Shutdown |
begin() drives PB6 high after clock bring-up to avoid a turn-on pop; end() drives it low. The amplifier recovers its clock from BCLK and requires no master clock.
Clocking
The SAI kernel clock is taken from a free divider off PLL4, giving 25 MHz. PLL4 is not relocked because the PSRAM depends on it, and the microphone's divider is untouched. With a 16-bit stereo frame, the sample rate is 25 MHz divided by 32 times the SAI divider, approximately 48828 Hz for the default. PLL4 has no factor of 3, so exact 44.1 kHz and 48 kHz are not reachable. This is irrelevant for synthesised tones and only matters for fixed-rate file playback, which is resampled.
Implementation
The ST SAI HAL drivers are vendored in the library and compile only into sketches that use the speaker. GPDMA1 channel 1 feeds a circular linked list from a non-cacheable descriptor; the transmit buffer is cache-cleaned after each refill. SAI1 is marked secure in the resource isolation framework so that the secure DMA channel can write it.
Diagnostics
debugDump() prints the HAL bring-up results (clkcfg, saiinit, tx should be 0), the SAI control register (SAIEN and DMAEN set), the DMA source address (which should sweep the buffer), the status register (no WCKCFG error), the GPIO alternate function nibbles (0x6), and the state of PB6 (which must be 1 for sound). The first incorrect value identifies the failed link.