Playing Chiptunes on an STM32L476G Discovery Board
Hours of music from a board with 128k RAM / 1MB Storage
For ECE271 the final lab of the semester was playing a 440Hz sine wave
out the DAC port of the STM32L476G Discovery board. For extra credit
they could play some music too (the textbook has most of the code
for playing Twinkle Twinkle Little Star). Since I already had some
existing sound code from previous projects, I kept escalating my demo code:
first by playing some 3-channel sound files from a custom tracker,
then by playing some ZX Spectrum PT3 chiptunes.
Initially the resulting 16-bit mono samples were fed at roughly 44kHz
into the 12-bit DAC on the STM SoC. This comes out pin PA5 and
a capacitor is used to drop it to line level.
The final version uses i2c/i2s to send 16-bit stereo samples the CS43L22 DAC
which can output through the audio jack.
The Progression (In Reverse Chronological Order)
Playing a 6-Channel PT3 AY-3-8910 Chiptune (stereo) by outputting
to the CS32L22 Stereo DAC via I2S. Using the on-board audio
jack rather than the filter cap hack.
Initial PT3 AY-3-8910 ZX Spectrum chiptune support.
Every 50Hz the emulated AY-3-8910 is updated, which then generates
44kHz mono audio to write out to the on-chip 12-bit DAC via DMA.
Simple video game music, from a custom tracker I have.
3 Channels of sine-waves, mixed in software,
reducing noise by waiting until
zero-crossing before switching frequencies. Some really
minor ASDR envelopes applied to the output.
Playing some FF7/video game music and Korobeinki (Tetris)
Sinewave "Twinkle-twinkle Little Star" (not pictured)
Lab 11 -- 440Hz sinewave (not pictured)
Output via the DAC, using a lookup table.
Each 3-channel chiptune is roughly 8k and plays around 2 minutes.
The player itself is around 16k.
So as a rough estimate, with roughly 126 songs, at 2 minutes each,
would be 252 minutes (about 4 hours).
Control the volume with the joypad?
Better visualization, using the LEDs?
Run off of battery power.
4 June 2019
Finally got everything working! The last hurdle was having a * where I meant
/ and thus was setting the buffer length to too short a value.
Posted video of the final (for now) project.
3 June 2019
After much frustrating search, finally figured out why the SAI/i2s was not
talking to the CS43L22. I had more or less everything configured right, but
was not setting the SAI1 clock input source mux so the SAI was not getting
a good clock.
Finally audio was coming out, but again the board wasn't fast enough to
bitbang the output values. Time for DMA.
30 May 2019
Wasted a lot of time trying to find out why i2c wasn't working.
Scope confirmed some sort of i2c was happening on the pins, but really
need a logic analyzer. Tried the old bus pirate which was useless.
Eventually figured out I was forgetting to set GPIOE3 (reset of the DAC)
to output and so it was never getting reset. That and a wrong pointing
less than sign in the i2c_send() routine and now we are talking to the
29 May 2019
Finally figured out why I couldn't run at 80MHz. I wasn't setting the
FLASH wait state to 4, which is needed if you run the board higher
Then finally managed to convert the code to use DMA/TIM7 to get some
really nice output.
28 May 2019
Turns out the sound was wrong frequency because the board at 16MHz is
not fast enough to play software triggered DAC audio at 44.1kHz.
The AY-3-8910 emulation wasn't finishing quickly enough.
Started work trying to get board to run at 80MHz but not having much luck.
27 May 2019
Trying to figure out why the sound was a bit too high pitched
(doesn't seem like it's an obvious software problem? Maybe a limitation
of the hardware setup?).
Made a Linux/OSS version of the player to verify things worked.
They do (once I figured out padsp).