Speeding up a project with SPI audio / visuals

Hello All!

I'm working on a project that should be able to play "retro games". The base of the system is a homemade proto-board with Atmega 1284P, using the MightyCore Arduino core.

Currently I have all the hardware modules ready and now i'm trying to put together all the software.

The project should prduce some music, i'll used the "arduino-music-player" project to make it possible:

Shortly, this project reads a prepared file from the program memory (which is converted from .mod / .it / .xm files on PC) and computes the PCM samples to output. While this computation happens in the regular loop(), outputting the samples must be precisely timed, so it happens in a timer interrupt.

I modified this code to use an SPI DAC (MCP4921) instead of parallel resistor ladder. Saves IO pins, improves quality. I attached the DAC on the hardware SPI pins, and it works without any problems, music plays just fine.

After this I tried to connect the "display" to the system, which is a homemade LED matrix made from 200 pieces of SK9822 (~APA102) RGB LEDs. These leds are programmable chips, also using an SPI like interface, but not featuring a chip-select line.

I'm using the FastLED library to drive the LEDs, and at first I tried to use their bit-banging mode (software SPI), since the hardware SPI is already taken by the DAC, and it is not trivial (at least for me) to connect both peripherials to the hardware controller:

  • SK9822 has no chip select; this may be worked around using some logic gates
  • LEDs must be driven from loop(), while the DAC is driven from an interrupt; what happens if the two transmissions interfere?

Though the software SPI worked fine, the LEDs were running OK, but the audio output became unrecognizable distorted. It looks that updating the LEDs in the loop() takes too much cycles, and the audio player cannot fill the sample buffer quickly enough.

I was quite hopeless at this point, but then i realized that the 1284P has additional USART controllers which can be configured to operate as SPI masters. FastLED even supports this out of the box. I only needed to define the USART pins for it and I could drive the LEDs much faster! The audio is now recognizable, but still jittering annoyingly. As I lower the LED count, the jittering becomes less and less.

At this point I'm thinking of the following things:

  • switch the two peripherals: the LEDs go to the hardware SPI, DAC to the USART. I'm not sure, from what I could learn, their speed should be comparable. And in theory, the DAC produces more traffic (only 2 bytes but outputted at 22050 Hz vs. ~ 200*3 bytes at ~30 Hz), so it should be connected to the faster lane?

  • replacing the current 16 MHz external crystal to a 20 MHz one. 1284P should run fine with it, and it is a ~25% speed gain, could be enough?

  • reducing the audio sample rate: easiest to try, definitely would help, but I do not want to drastically reduce the audio quality

What do you think about this design? Do you have some ideas for me to try?

Thanks in advance!

  • LEDs must be driven from loop(), while the DAC is driven from an interrupt; what happens if the two transmissions interfere?

They can't. When the interrupt happens, loop() is suspended, the interrupt handler does its thing, and then loop() is resumed.

It looks that updating the LEDs in the loop() takes too much cycles, and the audio player cannot fill the sample buffer quickly enough.

That is almost certainly the problem.

Do you have some ideas for me to try?

I'd almost certainly offload some of the processing to another computer. A Teensy would do a better job with the audio. Is there some synchronization required between the audio and the LEDs?

Hi PaulS,

thanks for your reply! Regarding the interrupts I was thinking about what happens if the interrupt comes when FastLED is communicating with the LEDs? Is it legal to deselect the LEDs in the middle of the transmission? Or does the SPI controller handle these kind of situations?

I was also thinking on separating the project to multiple MCUs, but in the end this will be a handheld device, so I would like to achieve the simplest design.

I’d need only some basic communication between the MCUs, play music, stop music, play sound effect X, etc… This could be the last resort if there is no other solution.

What do you think about raising the clock speed? 16 → 20 Mhz would be noticable?

Update: lowering the sampling rate to 20 kHz actually helped, no jittering, and the quality is still acceptable. I ordered though the 20Mhz crystal, I'll test if I could go back to 22050 Hz with that.

Just for the record, upgrading to 20MHz i could crank up the sampling rate up to 24kHz without any jittering.