$home

Audio Analysis on Zephyr

Real-time audio analysis on the Nucleo G474RE using Zephyr RTOS, CMSIS-DSP, and hardware-triggered ADC sampling at 44.1 kHz.

Technologies Used

Zephyr RTOSSTM32G474RE (Cortex-M4F)CMSIS-DSPSTM32 LL/HALDMAPython

Motivation

I built this project to learn Zephyr RTOS by doing something more involved than blinking an LED. I've used FreeRTOS before and wanted to understand how Zephyr handles devicetree, Kconfig, and the kernel primitives. This project ended up touching all of those plus direct STM32 LL/HAL register work, which was a good way to see where Zephyr's abstractions end and the hardware begins.

Hardware

BoardST Nucleo G474RE
MCUSTM32G474RE (Cortex-M4F, 170 MHz, FPU)
Audio InputAnalog signal on PA0 (Arduino A0)
ConsoleLPUART1 via onboard ST-Link VCP
ADC TriggerTIM6 TRGO at 44,098.6 Hz

How It Works

TIM6 overflows at ~44.1 kHz and triggers an ADC1 conversion via hardware TRGO. DMA transfers each sample into a ping-pong buffer (2 × 1024 samples). On half-transfer and transfer-complete interrupts, a semaphore wakes the processing thread, which runs a 1024-point real FFT using CMSIS-DSP and sends the results over UART.

TIM6 (44.1 kHz) → ADC1 conversion → DMA → ping-pong buffer
                                              |
                                        DMA half/full IRQ
                                              |
                                        proc_thread wakes
                                              |
                                   FFT + RMS + peak detect
                                              |
                                        UART output

The Cortex-M4F at 170 MHz handles a 1024-point float FFT in well under a millisecond. Thread analyzer shows 98% idle time - the CPU barely breaks a sweat.

What I Learned

Devicetree and Kconfig - Devicetree describes what hardware exists. Kconfig enables software features. They answer different questions and you need both.

Where Zephyr stops and HAL starts - Zephyr's ADC API doesn't expose hardware timer triggering. For the TIM6 → ADC1 trigger routing and DMA setup, I had to use STM32 LL functions directly.

DMA ping-pong buffering - One contiguous buffer, DMA in circular mode, half-transfer and transfer-complete interrupts. While one half fills, the CPU processes the other. No memcpy, just pointer swapping.

What's Next

This project is still in active development. More updates to come.

Source Code

The full source is available on my Gitea instance:

git.pjanik.ca/philip/audio-analysis