Well it has been absolutely ages since I last posted! I’m blaming it on exam season. Regardless, I’ve done managed a quick project in between the immense revision and what not.
Here, I’ve coded up a really simple audio range sine wave quadrature generator (sin[x] + cos[x]) with a frequency range from 0Hz to <24kHz, I say <24kHz as up to 23kHz produces strange effects where the amplitude seems to be modulated with what I’m gathering is due to aliasing as the Nyquist frequency is 24kHz. Obviously increasing the sampling frequency would give a much higher reproducible frequency range but the STM32F051R8 I2S PLL only supports 48kHz as the highest frequency without massive error in the output frequency.
The architecture for a quadrature generator is actually really simple. Send a sine wave to the right channel of the I2S DAC and a cosine wave to the leftchannel of the I2S DAC. In the terms of STM32 software, it involves initializing the GPIO, I2S transmitter, DMA and NVIC modules totaling to ~ 130 lines of code.
During operation, the DMA peripheral transfers samples from a memory buffer to the I2S module. One must remember however that the left and right samples are interlaced, therefore all even samples (0, 2, 4, 8…) will be sent to the left channel and all odd samples (1, 3, 5, 7…) will be sent to the right channel. This means when populating, the left and right samples will need to be calculated concurrently and can’t just be copied into the DMA buffer. This is solved in software by checking whether the loop counter is odd of even by testing the LSB to see if it is zero or one.
Configuring the I2S PLL
ST offer a really useful excel document that can be used to generate the system .c file which initializes the system, including clocks and flash wait states. In this excel spreadsheet, the required clock values including the I2S clock can be configured, it also tells you the error associated with each I2S clock frequency, neat!
Generating the clocks required for the STM32F0 using the ST spreadsheet
As can be seen in the image above, with a 48MHz system clock and an I2S sample clock of 48kHz, an error of 2.3438% is incurred. This translates directly to an error in the output frequency of the waveforms of 2.3438% as can be seen in further images.
Wiring up the I2S DAC
I didn’t actually have any new I2S DACs to hand so I had to settle on a really old WM8727GED DAC, manufactured by the now defunct company Wolfson Microelectronics who were taken over by Cirrus Logic. I’m not sure if Cirrus Logic still manufacture this chip though its exceptionally easy to use requiring an I2S bit clock, master clock, data input and word select input. This code however should work with any I2S DAC which can accept 16bit data in the standard I2S Phillips standard. After a quick internet search, the Cirrus Logic CS4334 seems a suitable replacement.
Wiring up the I2S DAC on my breadboard with the STM32F0. All circuitry to the right of the purple wire is not required! Pin locations are in the program source code.
Testing the output under certain conditions shows relatively consistent performance. The datasheet gives a frequency vs amplitude response of the internal delta sigma FIR filters and the response is relatively flat so I wouldn’t expect massive amplitude variations with frequency. I was however running the DAC without an output anti aliasing filter so the output was probably contained much more conversion noise than otherwise – not that I could really tell on my oscilloscope anyway.
Testing at 30Hz, note the error of 2.34% causing the actual frequency to be 29.3Hz. The calculated frequency using this error should be 30*(100-2.34)/100 = 29.298Hz. This error becomes much more apparent at higher frequencies.
Testing the output at 8Khz, once again the clock error produces a ~200Hz disparity.
Testing the upper range at 22kHz, note the minor amplitude decrease and frequency error!
Generating frequencies near to the Nyquist limit produces this strange form of modulation
Clock error correction
As can be seen in the above images, clock error produces a pretty notable frequency error, especially in the upper frequencies. This can be largely eliminated by pre-correcting the sampling frequency used for the DDS calculation. Instead of using 48kHz for the sampling frequency, a pre-corrected sampling frequency of 46875Hz is used instead. This is a result of: 48000*(100-2.34)/100 = 46874.98Hz.
Quadrature waveform output after sampling rate error correction. Target frequency was 8kHz, note how the error is much better!
The code can be found on my Github!