Standard embedded audio projects typically rely on external storage, such as SD cards formatted with FAT file systems, to stream pre-recorded audio files. In hardware scenarios where external mass storage is unavailable or strictly prohibited, an alternative approach is required. This project outlines the development of a "fileless" real-time audio player. The system mathematically synthesizes multi-track instrumentals on the fly using an STM32F407ZGT6 microcontroller, a FreeRTOS kernel, and an external ES8388 audio codec.
2. Design and TestingThe approach required decoupling the heavy calculation of audio waveforms from the asynchronous tasks of updating a display and reading user inputs.
Hardware DescriptionThe architecture centers around an ARM Cortex-M4 STM32F407ZGT6 development board operating at 168 MHz. The core audio reproduction is handled by an ES8388 audio codec. Control registers for the codec are configured via the I2C2 bus, while the raw synthesized PCM data is streamed continuously over the I2S3 peripheral configured in Master Transmission mode.
For the user interface, a TFT LCD is driven by the microcontroller's Flexible Static Memory Controller (FSMC) using Bank 1 SRAM memory lines. Analog volume control is achieved via a potentiometer routed to the ADC1 peripheral (Pin PA1). Finally, a four-button parallel interface (Pins PC0, PC1, PC4, PC5) is utilized for track navigation.
(Note: The complete HTML/CSS schematic mapping these precise GPIO connections should be referenced here for hardware replication).
Software DescriptionTo prevent CPU starvation and ensure artifact-free audio playback, the software is structured around three concurrent FreeRTOS threads:
update_lcd_leds_thread: Manages the playback state machine and handles the heavy lifting of updating the FSMC-driven LCD graphics within a safe mutex lock.
update_lcd_leds_thread: Manages the playback state machine and handles the heavy lifting of updating the FSMC-driven LCD graphics within a safe mutex lock.
adjust_volume: Periodically samples the 12-bit ADC, filters the noise floor, and transmits dynamic scaling values directly to the ES8388 codec via I2C.
adjust_volume: Periodically samples the 12-bit ADC, filters the noise floor, and transmits dynamic scaling values directly to the ES8388 codec via I2C.
polling_buttons: Evaluates debounced digital inputs to parse track navigation.
polling_buttons: Evaluates debounced digital inputs to parse track navigation.
Concept and Implementation: The Synthesis EngineBecause no storage media is present, the system generates raw audio streams using dynamic floating-point sine synthesis mapped to a fixed 44.1 kHz sampling rate. A 512-byte circular DMA buffer (debug_audio_buffer) acts as a ping-pong queue. While the DMA controller streams the first half of the memory allocation to the codec, a hardware interrupt (HAL_I2S_TxHalfCpltCallback) triggers the processor to calculate the next sequence of mathematical sine waves for the second half. When the buffer completes (HAL_I2S_TxCpltCallback), the process alternates.
The following commented code snippets highlight the core logic of the audio synthesis and the unique binary state machine used for navigation.
Ping-Pong Audio Synthesis
/* This function is called during the I2S DMA interrupts to fill the inactive buffer half */
void Fill_Audio_Buffer(uint16_t offset, uint16_t length)
{
float phase_inc = 0.0f;
if (current_freq > 0.0f) {
/* Calculate phase step based on the 44.1kHz sampling rate */
phase_inc = (2.0f * 3.1415926535f * current_freq) / 44100.0f;
}
for (uint16_t i = 0; i < length; i += 2)
{
int16_t sample = 0;
if (current_freq > 0.0f) {
/* Generate raw mathematical sound data */
sample = (int16_t)(sinf(current_phase) * 8192.0f);
current_phase += phase_inc;
/* Keep the phase wrapped to prevent overflow */
if (current_phase >= 2.0f * 3.1415926535f) {
current_phase -= 2.0f * 3.1415926535f;
}
} else {
current_phase = 0.0f;
}
/* Populate stereo channels */
debug_audio_buffer[offset + i] = (uint16_t)sample;
debug_audio_buffer[offset + i + 1] = (uint16_t)sample;
}
}Parallel Binary Interface State MachineInstead of linear next/previous buttons, tracks 0 through 7 are selected by holding a combination of three binary-weighted buttons (BTN2, BTN3, BTN4).
/* RTOS Thread for evaluating track selection */
void polling_buttons(void const * argument)
{
bool btn1_prev = false;
for(;;)
{
/* Decode parallel binary track configuration */
bool b2 = (HAL_GPIO_ReadPin(BTN2_GPIO_Port, BTN2_Pin) == GPIO_PIN_RESET);
bool b3 = (HAL_GPIO_ReadPin(BTN3_GPIO_Port, BTN3_Pin) == GPIO_PIN_RESET);
bool b4 = (HAL_GPIO_ReadPin(BTN4_GPIO_Port, BTN4_Pin) == GPIO_PIN_RESET);
int bin_idx = (b2 ? 1 : 0) | (b3 ? 2 : 0) | (b4 ? 4 : 0);
bool b1 = (HAL_GPIO_ReadPin(BTN1_GPIO_Port, BTN1_Pin) == GPIO_PIN_RESET);
/* Require BTN1 to lock in the selection and initiate a timeout window */
if (b1 && !btn1_prev) {
if (!is_waiting_for_confirmation) {
selected_song_idx = bin_idx % NUM_SONGS;
is_waiting_for_confirmation = true;
confirmation_timeout_tick = osKernelSysTick() + 5000;
} else {
/* Verified confirm path inside the 5-second window */
current_song_idx = selected_song_idx;
note_idx = 0;
state = PLAYING;
is_waiting_for_confirmation = false;
}
}
/* Handle window expiration safely without crashing the playback */
if (is_waiting_for_confirmation && (osKernelSysTick() > confirmation_timeout_tick)) {
is_waiting_for_confirmation = false;
}
btn1_prev = b1;
osDelay(20);
}
}4. ResultsThe final output of the project performed exceptionally well under testing. The hardware generated smooth, continuous 44.1 kHz sine wave audio out of the ES8388 codec.
Qualitatively, there were no audible artifacts, clicking, or buffer underflows during playback. A major success of the architecture was the complete isolation of tasks; it was observed that intense graphical screen-clearing operations on the LCD did not interrupt the continuous DMA audio stream.
The parallel binary UI functioned accurately as designed. The implementation of the 5-second confirmation window successfully mitigated erratic track switching and provided a highly responsive user experience compared to traditional polling loops.
5. Conclusions
The successful synthesis of real-time audio demonstrates the efficacy of DMA-driven ping-pong buffers paired with RTOS multitasking. The project effectively bypassed the need for physical storage media by generating PCM data mathematically.
Several issues were faced and debugged during the development phase. Initially, audio stuttering was observed when the LCD updated. This problem was debugged by realizing the CPU was blocking the audio math generation. Shifting the audio transfer to a DMA controller with half-cplt hardware interrupts completely resolved the starvation.
For further improvements, the software could be expanded to include a more complex waveform generator. By implementing square waves, sawtooth waves, or a digital ADSR (Attack, Decay, Sustain, Release) envelope, the system could simulate different physical instruments rather than just basic sine wave tones.








Comments