Close

Basic Firmware Testing

A project log for Synthesizer LFO / Function Generator

Building an arbitrary waveform generator to serve as an LFO in a Eurorack synthesizer

steven-gannSteven Gann 07/05/2022 at 20:490 Comments

Setting aside the analog concerns for now, I need to see if the MCU I'm using is even able to generate signals fast enough for my needs.

To keep things fast, I decided to construct lookup tables (LUTs) for each function I need to generate, starting with the Sine wave since it is the most complex. I put together a spreadsheet in LibreOffice Calc that calculated a table of 256 8-bit values that map from 0 to 2pi radians, and a graph as a sanity check to make sure the values looked like a proper sine wave. I then copied the values into a header file in MPLAB and formatted them into an array of uint8_t. While I was at it, I repeated the process with sawtooth and triangle waves.

With the Sine LUT prepared, I put together a very simple program to iterate over the LUT and push the values to the DAC. In the program's main loop I increment a uint8_t and use it as the index to grab a value from the LUT and push it to the DAC's value register, which should be as fast as physically possible. Without compiler optimizations enabled, I was able to get a fairly clean 5.24kHz. To get this sort of speed I'm using the PIC's internal oscillator with a PLL to push the clock even higher, which in theory might introduce a little jitter, but I'm not detecting any on my scope. 5kHz is pretty far outside of the frequency range I need so I should have a bit of time budget to do other things. There is a little aliasing in the DAC output, but a passive low-pass filter on the output will more than compensate for it.

I proceeded to add the remaining function LUTs and a bit of logic to switch between them with the button on the Curiosity Nano I'm using. For reverse sawtooth I use the same LUT as sawtooth but subtract the index from 255, and for square wave I have no LUT and instead check the most-significant bit of the index variable and write either 255 or 0 to the DAC.

Once this logic was implemented I ran into the first potential obstacle for this project. With just a couple of If statements and checking an I/O pin every iteration, the maximum frequency has dropped down to 3.46kHz, which is still fast enough but a significant enough decrease that adding more functionality may require careful planning and optimization.

Discussions