The assignments

Complex numbers and a ring buffer

Classes for numbers in rectangular and polar form, with conversion and arithmetic, and a fixed-size circular buffer written as a template, with a running sum and average. Both are reused in the later assignments.

Moving averages

A desktop application (wxWidgets) that smooths measured data with either a moving average on the ring buffer or an exponential average, drawn as bars with the average as a line. The same moving average then ran on the embedded board, where an oscilloscope showed it acting as a low-pass filter: a 1 Hz sine passes, a 3 Hz sine is attenuated and a 5 Hz sine is blocked.

FFT on the desktop

An application that computes the spectrum of a signal with the FFTW library (a real-to-complex transform) and plots amplitude and phase, using our Complex and PolarNumber classes for the conversion.

Fixed-point FIR filter design

A tool that designs a band-pass FIR filter: a sinc low-pass shifted to the centre frequency and shaped by a rectangular, triangular or Hamming window, stored as 16-bit fixed-point coefficients with a saturation check. It also plots the frequency response.

FIR filter in real time on the STM32

The designed filter (4 kHz sampling, 200–600 Hz pass band, filter order 64, Hamming window) running on an STM32 in a real-time loop: read a sample from the ADC, filter it and write it to the DAC. The multiply-accumulate step uses the Cortex-M4's single-cycle instructions (SMLABB and SMLAD), with loop unrolling that handles four taps per pass.

Illustration of the signal-processing assignments: the response of the moving average, the response of the FIR band-pass filter and the real-time chain from ADC to DAC
Illustration, calculated from the design parameters: the moving average, the frequency response of the band-pass filter and the real-time chain on the STM32.

How it's built

  • The same code on the desktop and on the board. The ring buffer and the FIR filter are written once and reused: first in desktop applications, later on the STM32, where a fixed-size version of the buffer is used.
  • Fixed-point arithmetic. Samples and coefficients are 16-bit integers. Products are accumulated in 32 bits, scaled back and checked for saturation, so the result cannot overflow.
  • Filter design from first principles. The band-pass coefficients come from a sinc low-pass shifted to the centre frequency and shaped with a window; the tool also computes the frequency response from the stored coefficients.
  • Generic and optimised version. The generic filter stays as a fallback. A compile option switches to the version that uses the processor's multiply-accumulate instructions (one tap with SMLABB, two taps with SMLAD) and unrolls the loop to four taps per pass, with a small loop for any taps left over.
  • A real-time loop. The board sets the ADC sampling rate (4 kHz), waits for each new sample, converts it from the ADC's unsigned 24-bit value to a signed 16-bit sample, filters it and writes the result to the DAC.

Who built what

This was a pair project. The course provided the application frameworks (the wxWidgets GUI, the STM32 board support and the assignment skeletons with stubs); we implemented the signal-processing parts described above.

Tools

  • C++
  • FFT (FFTW)
  • FIR filters
  • Fixed-point
  • STM32 (Cortex-M4)
  • wxWidgets

Want to know more about this project?

I'm happy to walk you through it.