Bode Analyzer using STM32F407 Discovery board


Frequency domain analysis is very important to know more about any system/ circuit or transfer function that we deal with. And the first thought comes to our mind about frequency domain analysis is "Bode plot". Bode plot is a combination plot of magnitude and phase difference of output vs. input of a cosine wave of single frequency, when it is applied to a circuit.

Each of these cosine wave (signal) are applied once at a time and the frequency (in Hz or rad/sec) is increased in linear order (called chirp). All the while the amplitude of sine wave is kept constant (at say 1V_peak). While the frequency can be increased in small steps (1Hz/ sec), the time required to complete the entire frequency range can be quite big. Hence, it is preferred to choose 20 frequencies/ decade. For example, I had chosen 05Hz, 1Hz, 1.5Hz, 2Hz, 2.5Hz... 10Hz.

Generation (output) and sampling (input or feedback) of Sine wave

Generation of cosine wave (and sine) waveform is pretty straight forward, using floating point device and DAC. Be aware that using the sin or cos function from standard "math.h" library takes a lot of time (because, it is designed to meet a certain resolution, by iterative process). Instead, I am using a sine table (of 2048 points of amplitude, over 0 to 2*pi rad). I kept the "sampling" frequency (for both DAC and ADC) at 200KHz. Keeping a fixed frequency is vital to any digital signal processing algorithm.

Now the complication, if different output frequencies are needed: -

  • Keeping sampling frequency of 200KHz and output of 1Hz = 200,000 points in 1 sine table.
  • Keeping sampling frequency of 200KHz and output of 10KHz = 20 points in 1 sine table.

Obviously, a straight forward approach of a single sine table will not suffice. Hence, I use a linear interpolation technique. If the angle is such that direct output is available from the 2048 sine table, the the value is directly used. For values of angle in between, the amplitude is arrived using linear interpolation of 2 nearby values. For higher frequencies, the points in sine table are skipped (and interpolated). A function is written, which take care of all these logic and outputs the amplitude for any angle needed.

I could have used external DDS chips such as AD9833, but knowing both the sine and cosine of signal being generated, makes the magnitude and phase detection simpler (explained later). If I had used externally (DDS) generated signal, then I would have known the "frequency" of output signal (by knowing the SPI command of frequency that I send to AD9833), but knowing the phase is impossible, without sampling. And then, we would have needed separate means to get 90-deg phase shifted waveform.

Anti aliasing filter at DAC output

Using DAC is pretty simple. We just need to scale the float value from +1 to -1 (peak value of sine waveform) to 2048 (half of max value of 12-bit DAC, 4096). While using DAC makes life easier, there are some complications. Firstly, the DAC performance is decided by settling time. In case of STM32F407, the settling time is 3uSec, which translates to a max frequency of (1/3uS) = 333.33KHz . Hence, my choice of 200KHz sampling rate is safe (without introducing non-linearity in output cosine waveform.

Following page from the STM32F407 data sheet depicts the settling time.

Another limiting factor is slew rate of internal buffer used at output of DAC (inside the STM32F407 device. This limitation can be bypassed by following the following application note from ST. While, this will definitely help me to increase the sampling rate to 2MHz, but the frequent sampling also reduces the time available between each interrupt to perform my calculation.

Since, my software calculation takes about 4.1uSec (for optimization level of O-0), selecting a sampling rate of 200KHz, which gives me a time of 5uSec between each interrupt to perform by calculations in software (interpolation, finding the magnitude and phase of signal) was a safe option.

As mentioned above, for a sampling frequency of 200KHz and output frequency of 10KHz, there are only (200KHz/ 10KHz =) 20 points every sine wave. Hence, for higher frequencies (say above 1KHz), causes generation of the fundamental 10KHz and extra (odd and even) harmonics. Unless, these harmonics are suppressed, these harmonics will appear as wrong (aliased) frequency at the ADC input. In order to suppress those harmonics, I tried different 1st order RC filter and arrived at a value of about 132KHz.

Following scope captures shows the output of DAC. The requirement of chirp is that the output should remain constant. But, it is quite evident that selecting an anti-aliasing of 18KHz and 45KHz causes attenuation (reduction in amplitude) of DAC output (though it reduces harmonics more effectively). Hence, I finalized on an anti-aliasing filter of 132KHz, which causes almost no attenuation of output signal.

Test result with physical (analog) RC-filter

Below waveforms shows the output of my bode analyzer when tested on a 1st order low pass filter (a real circuit of R-C) of 3 different cutoff frequencies. Results are very promising. The -3db point and -45deg matches well with the design cut off frequency of the low pass filter circuit. Also, the magnitude and phase roll off are as expected. The best part is that, the amplitude is resolved till -80dB, which is 100uV (micro-Volt).

Test result with Digital filter(s)

While I wanted to test my bode plotter with different circuits, realizing them in analog circuit involves lot of work (like sourcing the R and C components and soldering them on prototype boards etc.) Hence, I choose to use a digital realization of filters. I am not going into the explanation of my implementation of digital filter, but just presenting the results below. The pictures are self explanatory.

Though, I wanted to test the implementation on higher order filter (such as 2nd order) with different zeta (damping ratio), to achieve filters such as bessel filter or butterworth filter or chebyshev filter, I am pretty sure that the outcome will be good, as the above implementation of band pass filter is similar to a second order filter. I will test them in coming days.

Picture of test setup

Since, entire realization is digital (in software), the setup is pretty simple. It consists of a STM32F407 Discovery board which works as "bode plotter" and it interfaces to PC using a TTL-USB board. The "bode-plotter" is connected either to a analog circuit, or to a digital filter (another STM32F407 Discovery), which implements the transfer function of digital filter in software.

Plotting on PC

The plotting on PC is done using matplotlib module of python. The screen shot of python script is shown below.

  • The first screen shot shows the python code for setting up the arrays for frequency display (x-axis) and serial comm setup.
  • The second screen shot shows the python code for converting the received magnitude and phase to log scale and degree.

Demo Video

STM32 code and Python Script

As described, the python script collects data from STM32 Board and plots the semilog.

STM32 Code is written, to directly access the registers and does not use any hardware abstraction layer (HAL/ CUBE).

If you have come this far and want to know the knowledge behind the scene, then read through.

Determining Magnitude of output vs. input

We will start discussion based on two trigonometric identities that we learnt from school days: -

In above identities, ‘a’ and ‘b’ are time dependent and are NOT constant. That means, ‘a’ and ‘b’ are like (ω1*t) and (ω2*t). ω1 and ω2 are in rad/sec. One interesting property of any sinusoid (sine or cosine, which varies with time and has both positive half cycle and negative half cycle) is that the area under the curve for an integral cycle (1 complete cycle or 2 complete cycle or 10 complete cycle) is always zero.

This output if integrated over 1 cycle of input signal will be zero, since it has equal positive halves and negative halves. The following wavform shows the plot of explanation

This output if integrated over 1 cycle of input signal will be zero, since it has equal positive halves and negative halves. The following wavform shows the plot of explanation

Overall, what is means is that, if we know the measurement of both waveforms, then we can find the magnitude of waveform.

The implemented formula is: -

Magnitude of input = 1 (since 1Vpeak always)

Magnitude of output = (2/Nos of points in 1 cycle)*[SQRT(sum of all cos*cos + sum of all sin*sin)]

Determining phase of output vs. input

Whatever I explained above, is a layman explanation for the famous "Discrete fourier transform" (DFT).