-
Future Work
10/26/2025 at 10:47 • 0 commentsThis was just an experiment, and I will step back from further work for now. But there is still a lot to do to improve this further.
- Support for other tracker formats (S3M, XM, IT). I found this project, also called MODplay, quite promising, but the memory footprint is significantly larger (~30kb flash)
- Stream the tracker file from an attached memory, e.g. a serial NOR flash, to allow for even larger files to be used.
- Introduce data compression to reduce memory footprint of the MOD files as the MODfiles are quite a redundant data format.
- Using better digital signal processing techniques to improve audio quality (interpolation, filtering, noise shaping). For example one could go for 8bit PWM resolution at 176kHz sample rate to move all the PWM noise far away from the audio band. Delta-sigma modulation with dithering can then be used to recover the loss in resolution, even going beyond the 11 bit we are using now.
A first estimate of achievable SNR vs bit depth with more advanced audio processing is shown below. The two lines indicate 12bit and 14bit effective number of bits (ENOB) after noise shaping.
![]()
MODplay (INT driven) -> Noise shaper -> SRAM (Ring buffer) -> DMA -> Timer PWM -> RC Filter -> Audio Out
Even low end 32bit microcontrollers are in many instances as capable as a homecomputer / PC from the early 90ies. Powerful DMA feature keep the burden of playing samples off the CPU core. A 32 bit single cycle RISC-V CPU core can very well be compared to a 80486 or 68040, with some limitations regarding memory speed (There are waitstates on flash memory access and no cache).
-
Background
10/26/2025 at 10:37 • 0 commentsA "$0.10" 32-Bit Microcontroller today packs significantly more processing power and vastly more powerful peripherals compared to an AVR/PIC from decades ago.
The CH32V00x sports a very powerful timer that can run pulse width modulation (PWM) at 48MHz clock. By changing the duty cycle of the PWM signal, we can use it as a (crude) digital to analog converter. The comparator value that determines the duty cycle can be updated directly from SRAM using DMA. This allows for audio sample playback using zero CPU load.
SRAM (Ring buffer) -> DMA -> Timer PWM -> RC Filter -> Audio Out
In this experiment, I am using a 22.05kHz sample rate. This means that every PWM period is equal to 48MHZ/22050 = ~2172 clock cycles. This allows for 11 bit (2^11 = 2048) sample resolution, leaving some values unused. This is already quite decent for MOD audio playback, as the source samples before mixing are usually only 8 bit anyway. There are also plenty of options to improve audio quality further with clever digital signal processing.
Since the CPU is now idling, we can use it to render audio signals in real-time and update the ring buffer when it runs out of data. This can be implemented fully interrupt-driven, so that the music player can run in the background without blocking the main application. Here, I used ModPlay, which is a very tiny footprint MOD-Player that directly outputs into an audio buffer. I modified the source a little to generate mono and scale the signal directly to the PWM range.
The full pipeline looks like this:
MODplay (INT driven) -> SRAM (Ring buffer) -> DMA -> Timer PWM (PC3) -> RC Filter -> Audio Out
I integrated a simple SysTick-based profiler to measure the interrupt handler execution time in real-time. Here is an example output while playing a MOD file with the inner loop running in SRAM for zero wait state execution:
IRQ: avg=936 us, min=824 us, max=1031 us, rate=172 Hz, CPU=16%
Everything in flash (two waitstates per 32bit access)
IRQ: avg=1434 us, min=1230 us, max=1549 us, rate=172 Hz, CPU=24%
This leaves ample processing time for other tasks, so even on this tiny MCU, we could use a MOD player to run music in the background, while using the remaining processing power for a game engine etc.
I used a two stage RC low-pass filter (1kohm+10nF, 3dB@~15kHz) to smooth the PWM output. You can see the unfiltered PWM on the top and filtered audio signal on the bottom:
![]()
![]()
There is still significant high-frequency noise visible in the filtered audio, but it seems the speakers do a good job of low pass filtering it out further. A better option may be to use a higher PWM frequency and implement noise shaping / delta-sigma modulation to recover SNR.
-
Motivation
10/26/2025 at 10:30 • 0 commentsAfter seeing this article celebrating some simple beeps on the "$0.10" CH32V003, I was wondering whether we should not have moved beyond the beeper-melody stage of PIC microcontrollers? (Of course, I fell prey to HaD incitive language here, but any motivation is good).
We will be using the CH32V002 here, which is actually the little brother of the CH32V003 with a much smaller die (~1mm²). compared to the twice as large CH32V003, meaning the V002 is actually even cheaper. However, it comes with the advantage of a hardware multiplaction instruction, which is helpful for audio generation. The project here should work on a V003 nevertheless, albeit with more CPU consumption.
First, lets have a look at the results:
Tim

