Close
0%
0%

DSP PAW

Design, study, and analyze DSP algorithms from anywhere.

Similar projects worth following
(previously named "stmdsp")

The DSP PAW (DSP Portable All-in-one Workstation) project provides hardware and software to turn any computer into a complete learning environment for digital signal processing (DSP). The project's aim is to enhance DSP education through hands-on learning, stepping above current methods involving theoretical problems and pen-and-paper calculations.

Users write DSP algorithms in plain C++ within an Arduino-like IDE that includes easy-to-use analysis and debugging tools. The DSP PAW hardware easily interfaces with lab equipment and audio signals, but can also function on its own through its signal generator and signal capture feature.

What if DSP education could be hands-on?

Many courses on digital signal processing (DSP) focus on the theory: formulas and algorithms are written out by hand, and solutions are verified through plotting or charting results. In reality, DSP is only useful in the real world – from audio processors applying sound effects, to modems encoding data into radio waves.

A hands-on approach to learning DSP would allow students to see the effects of their algorithms in real time, greatly enhancing their learning. Some courses use microcontroller development boards to achieve this, but that often creates tangents into programming peripherals and configuring pins and clocks that detracts from the primary topic. This also creates dependency on expensive lab equipment to generate input signals and capture the resulting outputs.

What DSP PAW provides

DSP PAW builds on top of a common development board to give students an affordable and versatile DSP platform. This platform connects over USB to any Windows or Linux-based computer running the project’s IDE, creating a portable and feature-filled algorithm design environment.

DSP PAW in action!
(attenuation controlled by potentiometer)

Algorithms are written in plain C++, with a `process_data` function that takes in the input sample buffer and returns an output sample buffer. This allows even novice programmers to enter the world of DSP: see how the below example uses just a few lines of code to create a 2X amplification algorithm:

Sample* process_data(Samples samples)
{
    for (int i = 0; i < SIZE; i++) {
        samples[i] = samples[i] * 2 - 2048;
    }

    return samples;
}

The firmware

The DSP PAW firmware provides the USB and DSP functionality required for algorithm design through the IDE. Firmware programming is only done once; DSP algorithms are received later over USB and managed by the firmware, abstracting microcontroller details away and allowing users to focus primarily on algorithm design.

The firmware is built on top of the ChibiOS real-time operating system. This allows for real-time processing of input signals by the uploaded algorithm for immediate results. Algorithm execution is also “sandboxed”, so the platform can recover from common errors or faults induced by the algorithm.

The hardware

The project’s hardware functionalities are achieved through a custom “add-on” board that is built to be compatible with STMicroelectronics’ NUCLEO line of development boards. DSP PAW currently only supports the NUCLEO-L476RG variant of this product line, but other variants can be added with some effort. In the past the NUCLEO-H743ZI variant was supported, which allowed for high-performance algorithms given its 480MHz processing speed, math co-processor, and 5x available RAM.

The add-on board packs in many features:

  • 0.1” headers and 3.5mm jacks for interfacing with input and output signals
  • Circuitry to protect analog pins and support a +/- 3.3V signal range
  • Two potentiometers that can be used to adjust algorithm parameters during execution
  • A status LED

The software

The IDE provides an interface for writing, executing, and analyzing DSP algorithms. It is written in C++ using the cross-platform wxWidgets graphics library (tested on Windows and Linux systems).

The following features are made available through the IDE:

  • Write, compile, upload, and execute DSP algorithms
  • Configure sampling rate (8-96 kS/s) and buffer size (up to 4,096)
  • Measure algorithm execution time
  • View algorithm disassembly
  • Signal capture: watch input and output signals in real time, or record output signal to a .csv file
  • Signal generator: can source from a sample list, y=x formula, or WAV audio file
  • Included examples for topics including convolution and filters (FIR averaging, IIR echo)

Entirely open-source

All components of DSP PAW are currently released under the GNU GPL version 3 license. Source code and schematics are available from the GitHub project links.

DSP PAW...

Read more »

DSP PAW Specifications and Requirements.pdf

Supported hardware, add-on board pinout, and specification table

Adobe Portable Document Format - 120.71 kB - 09/11/2023 at 22:55

Preview
Download

Adobe Portable Document Format - 1.34 MB - 08/20/2023 at 01:41

Preview
Download

DSP PAW add-on board fabdrawing.pdf

Board fabrication drawing

Adobe Portable Document Format - 292.58 kB - 08/20/2023 at 01:41

Preview
Download

DSP_PAW_add-on_board_buildfiles.zip

KiCad gerber, drill file, and BOM export package

Zip Archive - 133.61 kB - 08/20/2023 at 01:40

Download

  • 1 × NUCLEO-L476RG The microcontroller development board that DSP PAW builds on top of. Other NUCLEO (or Arduino-compatible) boards could be supported in the future.
  • 1 × DSP PAW add-on board The custom Arduino form-factor board with the circuitry necessary for DSP PAW to function. Not available for sale (yet), but all of the design files are available here.
  • 1 × Micro-B USB cable Connects the add-on board to the computer for using the user interface.
  • 1 × Mini-B USB cable Necessary for programming the NUCLEO development board, which may already come with this.
  • 1 × Female-to-any wires, 0.1"-pitch (at least one to use the signal generator as an input) For connecting input and output signals to the 0.1"-pitch header.

View all 6 components

  • PCB Part 3: Analog output testing

    Clyne09/04/2023 at 16:23 0 comments

    With the input signal circuit figured out, it's time to move on to the output circuits. There are two, one for the algorithm output and another for the signal generator, though both use the same exact circuit. This means we can just test the signal output and copy any necessary changes over to the generator circuit.

    For these tests, I used the Analog Discovery 2 again to provide an input signal and oscillator probe for the signal output pin. The development board will be used with the DSP PAW user interface to execute a "pass-through" algorithm that simply outputs the received signal. If all goes well, the oscilloscope will show matching input and output signals (though the microcontroller's sampling rate will introduce some visible steps in the output voltage).

    Test setup, with waveform generator (yellow) as a signal input and a probe (blue) on the output.

    My first test used an easy 1V, 100 Hz sine wave, shown below. The output signal (blue) is surprisingly clean, though it's offset by nearly one Volt. There is a DC blocking capacitor directly on the microcontroller's DAC output, so the offset should not be there...

    The signal may appear to be inverted, but that's just coincidential. Processing the signal introduces latency that can create this offset; the opamp's filtering may also cause a change in signal phase.

    Orange: Input signal. Blue: Add-on board output.

    We'll take care of the offset soon, but first I was skeptical of the output's clean performance. I changed the input signal to a square wave, creating impulses that had a better chance of causing noise or oscillations.

    Orange: Input signal. Blue: Output signal.

    Those shaded blue areas are spots of noise caught by the oscilloscope. From here, we increase the input signal frequency and zoom in to see some pretty nasty impulses (see below). The output signal is not perfectly matched to the input, but that is just a result of our limited sampling rate.

    Orange: Input signal. Blue: Output signal.

    To fix up this output, we'll go back to the incorrect offset first and then handle the impulse response.

    Output signal offset

    Again, we have a DC-blocking capacitor at the beginning of the output circuit, so any offsets should get absorbed and leave the signal centered at 0V. I took a probe to both sides of this capacitor: the microcontroller-side of course had the DAC output with its 1V offset, but when I switched to the other end of the capacitor, the signal began making its way down to 0V just as we wanted.

    It turns out the capacitor needs some kind of minimum load on it (like an oscilloscope probe) to allow its charge to settle the output at 0V. Further testing found that a 100 kOhm resistor could do the same trick, and the board may be redesigned to accommodate one.

    Touching a 100 kOhm through-hole resistor (grounded via the USB's shield) to the DC-blocking cap (C6).

    The circuit's response to offset is quick now, and fairly accurate:

    Orange: Input signal. Blue: Output signal.

    Fixing impulse oscillation

    Now it's time to clean up the signal. The strange thing about this issue is that the chosen filter design, a Sallen-Key Bessel filter, should not be creating any overshoot at all. This led me back to the filter design application note I worked off of, reviewing the design process with the circuit on the add-on board.

    ...and something came up. Here is a side-by-side picture of the board's circuit (left) and the application note's design (right):

    Left: Add-on board's filter design. Right: Filter design from Texas Instruments (SLOA049D).

    These are not the same circuit, ugh. I attached the feedback capacitor (C5/C2) to the opamp's non-inverting input, instead of between the two input resistors. I have strong confidence that this is our problem.

    To test this, I removed C5 from its pads and soldered one end of the cap to R8 (where R8 connects to R9). I used a small enamel-coated wire to get the other end of the capacitor re-connected to the opamp's output through...

    Read more »

  • PCB Part 2: Analog input testing

    Clyne09/03/2023 at 16:12 0 comments

    This log will give an in-depth report on the testing and ciruit alterations that were made to get the analog input signal circuit working as desired. The alterations were able to be kept to simple component swaps, so future boards can be ordered with just a few changes to the bill of materials.

    To analyze the signals, I hooked up my Analog Discovery 2 to the NUCLEO and add-on board. For the input signal path, the Discovery's waveform generator was connected to the add-on board's signal input and an oscilloscope probe was hooked to the ADC pin that receives the conditioned signal. A third wire gives us a common ground:

    A couple of power supply observations

    Once the add-on board was connected to the NUCLEO, some of the add-on board's power supplies were not behaving as expected. First, I noticed that the add-on board's +5V and -5V test pads were reading voltages around +/- 3.6V. The add-on board gives USB power to the NUCLEO through its VIN pin, and expects the NUCLEO to give that back on its 5V pin; however, revisiting the NULCEO's schematic proves that this is not the case.

    The NUCLEO puts VIN through a 5V linear regulator and a protection diode before the "E5V" net, which is passed to +5V through a jumper. Both of these components create significant voltage drops for the power going through them:

    The LD1117S50TR experiences a "low" dropout voltage of around 1V.
    The STPS2L30A diode drops voltage by 0.3 to 0.4V.

    Take the 5V input voltage and subtract the 1V and 0.4V drops and you get 3.6V -- just what we're measuring. Ideally, the add-on board would be re-routed to give its 5V USB power directly to the 5V pin, but for now we can get by since the operational amplifiers (the only components using +/- 5V) just need +/- 2V to properly handle our signals.

    Analog voltage reference

    I also verified that 2.048V analog voltage reference now that the NUCLEO board was connected, and measured... 3.3V. That would be the NUCLEO's default reference voltage, with the schematic showing that a jumper handles this connection. We'll need to desolder a jumper on the bottom of the NUCLEO board.

    SB57 connects 3.3V to the microcontroller's voltage reference pin.

    This might present an issue going forward, since it means users will need to be comfortable with (and have the means for) desoldering this jumper. The 2.048V reference is far less noisy than the NUCLEO's 3.3V, so this decision will have to take some thought.

    Apart from this connection, the resistor divider on this reference that's used for shifting the input signal above negative voltage is incorrect. The division needs to be by four, not two as the board was accidentally designed for. I should have caught this before ordering the boards by comparing schematics with the previous iteration, but oh well... the fix is an easy resistor swap on R7 (from 10k to 30k):

    Some of these resistor pads are thin and flimsy, so some extra solder is needed to make the connection sturdy.

    Input signal analysis

    With the above out of the way, it's time to turn the oscilloscope on and run some simple tests. We start with a 0V signal to check noise and offset (+/- 2V is converted to 0 to 2V, so a 0V input should read on the ADC pin as 1V):

    Sadly, this is not a clean signal or a 1V signal. A closer capture shows that we're getting a sine wave with a 1.4V swing at 1.4 MHz.

    This is a clear indicator that one (or both) of the opamp stages are unstable and creating oscillations with their feedback networks. The remedy is often found with capacitive adjustments: we can either balance the input and output capacitance, or we can add capacitance to the feedback network to create a delay that stabilizes the feedback. I tried adding output capacitance first, and while that cleared the oscillation it also created a low-pass filter that ruined signal inputs above 1 kHz. Adjusting the capacitance to allow higher frequencies would lead the opamp back into instability.

    The above should...
    Read more »

  • PCB Part 1: Arrival and voltage testing

    Clyne09/01/2023 at 17:53 0 comments

    The assembled PCBs have finally arrived, and after some testing and tweaking have proven to be good successors to the previous design. There is a ton to go over for the bring-up process; I'll share some initial observations and testing here, and then in the following log take a deep dive into validating the input and output signal paths.

    Although the 3D renders were of green PCBs, I chose to stick to the blue color that the previous iteration used. It compliments the NUCLEO deveopment board better.

    The USB breakaway came out better than I expected, and feels sturdy when a cable is attached. There's only one issue with the board that's immediately noticable: the protection diodes are missing.

    I went back to my order confirmation, and oddly enough the BOM used for assembly did not include any diode part numbers. I double-check the original BOM next to ensure my sanity, and...

    ReferencePart Number
    D1,D2,D3
    ESD5Z2.5T1G
    D4,D5,D6DF2B7AFS,L3M

    ...we realize that the BOM exports as a comma-separated file (.csv). The comma in D4-D6's part number probably caused an error and made the ordering page drop that row. I guess that a similar issue exists for the part number with a period in it. Fortunately, these parts are not essential to the design, though it's a shame that this wasn't caught.

    Anyways, we now move on to powering the board up and making sure the power regulation is correct and stable. It's best to test this before risking the connection to the microcontroller board, so I soldered on a little header for the power pins and plugged in the USB cable:

    Nothing put out heat or smoke, which is good. I used a multimeter to check the power supply test points: ground, +5V, -5V, and the 2.048V voltage reference -- everything looks good!

    The USB cable does not supply a perfect +5V, but that doesn't really matter. The opamps only need +/- 2V to properly handle the analog signals, so anything beyond that minimu should work well.

    At this point, it should be safe to fully connect the new add-on board to the NUCLEO and test the inputs and outputs completely. All that is needed is a pair of 14-pin male headers (to break apart and fill the Arduino connector), and a 2-pin female header to connect to the microcontroller's USB data pins.

    By sticking the headers into the NUCLEO board, I could solder them onto the add-on board with everything lined up and fitting:

    Before and after. I'm proud of the solder joints -- it takes practice to make them "clean".

    The input and output circuits are certainly more complex than the power supplies, and that'll show in the amount of testing and adjusting they needed. Stay tuned...

  • PCB layout and Arduino footprint

    Clyne08/19/2023 at 12:26 0 comments

    Layout of the new add-on board is complete. I should note that the schematic and board layout are being done with the open-source electronics design suite KiCad. This is my first "real" design using KiCad, and the process has gone a lot smoother than I anticipated. There is a large community available for support, plenty of component libraries available, and even some one-click gerber generation plugins for PCB prototype manufacturers for easy order quoting. It's my personal recommendation to learn KiCad if you are in need of circuit design software.

    Change to Arduino footprint

    A significant design change was made during the process of layout which is important to cover first: the board is now based on the common Arduino header format rather than the “ST Morpho” format used by STM NUCLEO development boards. This change greatly expands the compatibility of this board with other microcontroller development boards with no practical compromise. The layout process revealed that nearly all connections are either routed through or could be substituted with pins on the Arduino header of the NUCLEO board.

    The exception is the pair of USB data pins. Since these are near a corner of the board, the USB circuitry was moved to be contained in that corner. Since this is a NUCLEO-specific requirement, cut-outs were made to allow removing this portion of the board for development boards that do not require it:

    Layout process and results

    The layout could have been done with either two or four copper layers. The four-layer option would allow for two internal copper layers, where typically one carries a ground plane and the other carries DC power traces. Isolation of power traces and insulation of signal traces through the ground plane would minimize the possibly of induced signal noise, although with the drawback of increased manufacturing cost. Additionally, that level of care for signal quality is not quite necessary for the intended applications of this board. Since board design was achievable with two layers, the two-layer approach was taken.

    All components were kept on the top layer of the board. Available board space allowed for this, but it also simplifies board manufacturing. Prototype PCB assembly services typically only offer component placement on one side of the board; in this case, that means only the Arduino and USB pin headers need to be soldered by hand after delivery.

    Both layers were filled with ground pours (i.e. spreads of grounded copper) to surround the traces and minimize the potential transfer of noise between them. Other common techniques were employed in the design: components like decoupling capacitors were kept near their sources, application notes were followed for the four ICs circuits and layout, and some consistency was made on trace directionality (top layer prefers north-south paths, bottom layer prefers east-west). Power circuits were kept away from the analog circuit paths where possible. Ground vias were spread throughout the board to ensure consistent grounding. And finally, KiCad’s electrical and design rule checks were used to confirm a sound design.

    A nice 3D rendering

    KiCad also features the ability to create 3D models of circuit board designs, and so I’ve done that for the add-on board:

    The board is 63mm at its widest, and 53.5mm in length. The previous NUCLEO-based design was 70x55mm; fortunately, the reduction in board size was manageable.

    Design files are open source

    The new add-on board design is released under the CERN Open Hardware Licence Version 2 - Strongly Reciprocal license, a license made specifically for open hardware. I am also working on a new source repository for DSP PAW which contains all of the project's firmware, software, and hardware files. It is hosted on both my personal server and on GitHub. The KiCad project files can be found here. PDF versions of the schematic and fabrication drawing will be made available by the time the prototype batch of these boards is ordered....

    Read more »

  • Schematic design changes

    Clyne08/18/2023 at 01:02 0 comments

    The add-on board’s revised schematic and board layout are practically complete; the next couple of logs will go over the significant changes in the hardware design for this next iteration.

    The primary changes in the schematic come down to the filtering and conditioning of the input and output signals given the new signal specifications. Design tips and strategies were taken from multiple resources to maximize accuracy and minimize noise. This highlights the best piece of advice I can give to those designing circuits and schematics: use all of the resources at your disposal. Nearly all datasheets for integrated circuits (ICs) include detailed “Application” sections that show circuits, discuss component selection, provide calculations to meet design parameters, etc. Following these instructions will ensure a sound design. Major manufacturers also have vast collections of application notes that cover a wide array of electronics design topics. These can be found on their websites, or simply by searching the internet. There’s no reason not to work off of proven designs and knowledge when you can.

    Input signal

    Previous designs of the add-on board used basic inverting amplifier configurations to achieve the simple goal of scaling signals to the desired voltage ranges. Amplifier ICs were chosen solely on the basis of if they can achieve this goal. Later on, slight changes with passive components were made to reduce high-frequency noise.

    For the next design, I turned to an application note from Texas Instruments (TI) on “Active Low-Pass Filter Design” and the accompanying Filter Design Tool. Given the project’s new specification on maximum sampling rate, a second-order active filter could be used to achieve an optimal signal-to-noise ratio. The max rate of 96 kHz was rounded up to 100 kHz for simplicity.

    A Bessel filter was chosen to avoid gain overshoot below the cut-off frequency, at the cost of reduced attenuation performance. To compensate, the Multiple Feedback (MFB) architecture was used to optimize the high-frequency response; placeholders for an additional low-pass filter stage (R6 and C4 in the above photo) were also added in case increased attenuation is necessary. MFB also has reduced sensitivity to component variation, allowing for some component cost savings.

    The Bessel filter is preceded by a series capacitor that cancels out any DC offset in the incoming signal. The previous design instead canceled the offset by feeding the ground of the input audio jack into the differential amplifier; however, this approach was not proven to be sound or “correct”. A series capacitor is an easier and safer choice. The value of 10uF is fairly arbitrary, and will be adjusted during testing if needed.

    The second stage of the input signal path is a simple inverting amplifier, as the MFB architecture of the first stage is also inverting. The second stage is also used to add a DC offset to the signal, bringing it into the acceptable voltage range for the ADC.

    Output signals

    The output signal (and signal generator output) uses a Bessel filter just like the input signal. A Sallen-Key architecture is chosen this time though, primarily for the fact that it produces a non-inverting configuration. This means there only needs to be one active stage in the output path. The drawback is a potential reduction in high-frequency response; the optional low-pass RC filter is included in case this reduction is worse than desired.

    ESD protection

    This design introduces ESD (electro-static discharge) diodes on the audio jacks and USB port. ESD can occur on any component that interacts with the outside world (e.g. human touch), so it is good practice to include this kind of protection. In a worst-case scenario, unprotected ESD could damage components on either of the add-on or microcontroller boards.

    The choice of diode is flexible as long as the diode does not breakdown within the normal voltage range...

    Read more »

  • Design decisions and specifications

    Clyne08/08/2023 at 23:33 0 comments

    This project has compiled a set of specifications over its lifetime which influence the design of its hardware (i.e. the add-on board) and the capabilities of the device and its software overall. The buildup of these "specs" happened fairly naturally, or arbitrarily; however, these need to be fine-tuned for the upcoming hardware iteration since it aims to be a more-or-less "finished" design. As a result, a few of these specifications have changed. I'll take this chance to list out all of the current design specifications, as well as discuss some of the decisions that led up to them.

    I'm sharing these now since the schematic for the next add-on board is nearly complete. These are the specifications it follows, and the next project log will show how the implemented circuits meet these requirements.

    Sampling rate and buffer size

    These two values determine the speed and quantity of incoming data that needs to be processed. Limits for these values depend on the given application; for DSP PAW, educational and audio applications are the primary target.

    Educational projects will generally lean towards simplicity, so we would like to avoid super-fast sampling rates and huge sample buffer sizes. On the other end, too low of a sampling rate would cause slow algorithm reactions and make testing a nuisance.

    For the lower bound, a fairly arbitrary choice of 8 kHz was made. This allows for working with slower signals as well as some audio since the frequency is a telephony standard. This rate is also easy to create with microcontroller (MCU) clock -- the MCU needs an 8 kHz clock to sample signals at 8 kHz.

    The upper bound sampling rate was inspired by audio applications. The two most common audio recording rates are 44.1 kHz and 48 kHz. Generating a 44.1 kHz clock proved to be difficult, especially since the one clock would also have to support the lower frequencies (e.g. 8 kHz). So, design leaned towards 48 kHz, ultimately choosing its double (96 kHz) as the maximum. This gives a maximum Nyquist frequency of 48 kHz, meaning signals up to 48 kHz can be sampled without aliasing/distortion.

    The STM32L476 microcontroller can actually support sampling at up to 5 MHz, but for audio and education there is little need to go faster than the chosen limit. Getting anywhere close to the MHz range would both reduce the algorithm execution window to an unusable size, and interfere with the microcontroller’s essential USB communications with the computer.

    For buffer size, the configurable range was made to be between 100 and 4,096 samples. The upper limit was partially a result of the microcontroller's constrained memory, though this allows for up to half a second of signal data when sampling at 8 kHz. Smaller buffer sizes are handy for simple algorithms and/or faster algorithm reaction times.

    Signal amplitude

    The other primary factor of a signal apart from its frequency is its amplitude. Supporting larger amplitudes means flexibility with external signals, though it also requires caution regarding electrical safety of the hardware. The MCU can only handle voltages between 0V and the ADC reference voltage (3.3V by default), so the add-on board needs to scale signals to or from that range.

    Previously, an arbitrary decision to support +/- 3.3V was made. This range is wide, and allowed us to rely on the MCU’s default ADC reference. The next design iteration will see a reduction to +/- 2V, for a few reasons: first, this means the addition of an external ADC reference which will eliminate power supply noise from affecting the signals; second, the MCU's use of the 2V reference will lead to better accuracy and precision...

    Third, the project's target applications do not need the additional range that was previously allowed. Educational applications will either use the on-board signal generator, which follows the chosen limit, or external hardware which can most often be configured to an acceptable amplitude. For audio, line levels should practically always be...

    Read more »

  • Real-time frequency visualization

    Clyne07/15/2023 at 15:27 1 comment

    Many DSP algorithms are designed to target frequencies within a given signal. This makes it essential to be able to view the frequency response of an algorithm, and is why I had to bring in an external tool to view the results of the low- and high-pass filters in the previous project log.

    DSP PAW aims to be an all-in-one solution, and so I set aside some time to implement the ability to view the frequencies present in the output signal. The previous log used spectrograms, which show magnitude over a range of frequencies over a range of time. A simpler approach would be to drop the range of time, only showing the instantaneous frequency magnitudes; this is possible by implementing the Fourier transform.

    For computer applications, the Fourier transform can be efficiently calculated through what is known as the "Fast Fourier Transform" (FFT). Instead of "reinventing" the wheel, I chose a C library called kissfft to take care of the FFT calculations.

    Designing the FFT visualization

    The GUI uses the imgui framework to build its interface, making the addition of this feature fairly simple. To start, a checkbox is added to the menu which controls a drawFrequencies flag that will be used to make the visualizer visible:

    bool drawFrequencies = false;
    // ...
    ImGui::Checkbox("Plot over freq.", &drawFrequencies); 

    Later on in the rendering code, we simply add an if-statement that checks the drawFrequencies flag and renders the visualizer if true. This is the exact approach taken for the current signal plotter; in fact, most of the current plotter's code will be copied for this new visualization. The only significant difference will be the application of the FFT to the data before plotting.

    The first FFT change is within a conditional that checks for changes in the sample buffer size. This prepares our buffers for taking in the incoming samples, as well as the kissfft configuration structure which needs to know this buffer size.

    // Adds new samples to bufferCirc unless the block size has changed.
    auto newSize = pullFromDrawQueue(bufferCirc);
    
    if (newSize > 0) {
        // Sample block size has changed, resize buffers.
        buffer.resize(newSize);
        bufferFFTIn.resize(newSize);
        bufferFFTOut.resize(newSize);
        bufferCirc = CircularBuffer(buffer);
        pullFromDrawQueue(bufferCirc);
    
        // FFT: Re-initialize kissfft's configuration.
        kiss_fftr_free(kisscfg);
        kisscfg = kiss_fftr_alloc(buffer.size(), false, nullptr, nullptr);
    }

    kissfft needs separate input and output buffers (bufferFFTIn and bufferFFTOut) due to type differences: our samples come in as 16-bit unsigned integers, the FFT calculation requires floating-point values, and the calculation results are complex numbers.

    So, we use std::copy to move the raw samples into bufferFFTIn, and then call kiss_fftr to perform a "real" FFT since we don't care about the imaginary components of the calculation.

    std::copy(buffer.begin(), buffer.end(), bufferFFTIn.begin());
    kiss_fftr(kisscfg, bufferFFTIn.data(), bufferFFTOut.data());
    
    // Plot real values only: bufferFFTOut[i].r
    

    When building the plot, the x-axis will simply span the range of bufferFFTOut. The y-axis ends up being more difficult to put constraints on: to my knowledge there isn't a real unit (it's simply "magnitude"), and there isn't a clear upper bound either. Dividing the output by the sampling rate isn't enough, neither is taking a fourth of that division. However, this constrains the plot enough that we can observe spikes in present frequencies and make relative comparisons between them.

    Testing the FFT visualizer

    I tested this new feature by using the signal generator as an input signal with the default pass-through algorithm. This is the output given the input equation "0.8*sin(x/7)":

    Given sampling rate Fs, an Fout frequency sine wave can be created with the equation sin(2*pi*x/Fs*Fout). At the default sampling rate of 32kHz, this means sin(x/7) creates a sine wave at about 728Hz. This would be the red spike in the above plot; the mouse cursor is controlling...

    Read more »

  • Algorithm design: Low- and high-pass filters

    Clyne06/11/2023 at 14:20 0 comments

    Realizing the hardware and software for DSP PAW is central to the project's success; however, examples of algorithm implementations will be essential for making DSP PAW an adaptable solution. So, I've begun work on some guides that walk through writing and analyzing some common algorithms. As they're finished, these guides will get uploaded or linked to the project's code repository. Below is a kind of "rough draft" of what I would include in a guide; in the future, I will most likely post about newly available guides rather than writing them in these logs.


    To start, I will show the implementation of low- and high-pass filters. These filters block out high and low frequencies respectively, and so their effect and behavior are easily observed. I chose to keep these implementations relatively simple by using first-order infinite impulse response (IIR) filters. As much as I would like to get into the DSP theory behind IIR filters, it will be best for now if I leave that to the proven educational materials found elsewhere online.

    In short, both the low- and high-pass filters follow this formula:

    y[n] = b0 * x[n] + b1 * x[n - 1] - a1 * y[n - 1]

    ...which is conveniently already valid C++ code. The coefficients a1, b0, and b1 can be calculated into variables given specified cut-off (Fc) and sampling (Fs) frequencies:

    constexpr float Fc = 1000;
    constexpr float Fs = 32000;
    
    
    constexpr float Omega = 2 * 3.14159f * Fc / Fs;
    constexpr float K = tan(Omega / 2.f);
    constexpr float alpha = 1 + K;
    
    constexpr float a1 = -(1 - K) / alpha;
    
    // For low-pass filtering:
    constexpr float b0 = K / alpha;
    constexpr float b1 = b0;
    
    // For high-pass:
    constexpr float b0 = 1 / alpha;
    constexpr float b1 = -b0;

    By using the constexpr keyword, these values are calculated at compile-time, reducing execution time. Unfortunately, the above code will have an issue with the current DSP PAW software: a custom tan() implementation that calls into the core firmware will prevent K's compile-time evaluation. This can be fixed, but for now K will need to be calculated by hand and typed in.

    One more requirement of these filters is that the sample data needs to be centered at zero. So, a short "normalization" function is written to convert a sample's 0 through 4095 integer value into a decimal -1.0 to 1.0 range. It may be possible to integrate this transformation into the filter as a performance optimization.

    inline float N(Sample s) {
        return (s - 2048) / 2048.f;
    }

    Finally, we write the iterative loop that calculates each output sample. A buffer is defined to store the output since the input samples need to be preserved for calculation. If that were not the case, the sample buffer passed into process_data could be reused:

    Sample* process_data(Samples x) {
    	static Samples y;
    
    	for (int n = 1; n < SIZE; ++n) {
    		float y_n = b0 * N(x[n]) + b1 * N(x[n - 1]) - a1 * N(y[n - 1]);
    		y[n] = (y_n + 1.f) * 2048; // return to 0-4095 range
    	}
    
    	return y;
    }

    To test these filters, I used an Analog Discovery 2. The software for this device is advanced, and provided me with a sweeped-frequency signal and a spectrogram to capture the filters' frequency responses. Both of these features could (and should) be built into DSP PAW. For now, testing without external hardware could have been done by loading the on-board signal generator with a sine wave (or perhaps low- and high-frequency sine waves added together) and observing the output's attenuation/form through the signal visualizer.

    Here is the spectrogram with no filter running. All frequencies in the sweep are showing up in green:

    With the implemented low-pass filter, attenuation of frequencies higher than 1kHz can be seen. Due to the nature of first-order IIR filtering, the attenuation is gradual and not "strong" at the specified cut-off.

    The implemented high-pass filter passes through the frequencies beyond 1kHz, with attenuation below 1kHz clearly observable. Again, the cut-off's strength and spread is fairly weak.

    As an extension to this algorithm,...

    Read more »

  • Development board support (or integrate the processor?)

    Clyne04/23/2023 at 19:23 0 comments

    Many lines of development boards, especially the NUCLEO line from STMicroelectronics, have seen supply shortages over the past few years. This is a significant problem for DSP PAW since it currently only supports one (popular) NUCLEO variant.

    Fortunately, this variant is in stock with distributors at the moment; though some are limiting purchase quantities. To avoid this issue, DSP PAW has two options:

    1. Expand support for more NUCLEO boards.
    2. Integrate an STM32 microcontroller into the "add-on" board to create a single, complete device.

    Integration has been on the table ever since this project's inception: no need to worry about multiple PCBs, the device could be contained in a nice case; seems like the "professional/commercial" path forward.

    However, the project's audience probably does not care for a integrated product. Keeping to an add-on boards lets students have their NUCLEO boards for other projects or study. Being able to switch the add-on board between NUCLEO variants lets users choose a microcontroller depending on their needs (memory, performance, other functionality?).

    Since education is our primary target, DSP PAW will most likely remain with an add-on board for hardware. Maybe an integrated option will be offered in the future if there a better need for it comes around.

    NUCLEO board support

    Currently, the firmware requires an STM32 microcontroller with a >=72MHz clock, 96kB of RAM, and USB/ADC/DAC support. Hardware floating-point support would be a big plus too, since many algorithms will want decimal precision. Adding support for a new board to the firmware would take some work, but not be super difficult; ChibiOS does a decent job with hardware abstraction.

    The add-on board fits to 64 and 144 pin NUCLEO boards. Most F7 and H7 NUCLEO options should be compatible, the same goes for F4 and L4. USB support is the primary thing to watch out for.

    Some specific options I'm seeing:

    • NUCLEO-F303RE
    • NUCLEO-F446ZE
    • NUCLEO-L496ZG

  • Finalizing the add-on board's design

    Clyne04/22/2023 at 15:26 0 comments

    The DSP add-on board has gone through three iterations since the project's beginnings. It first started with a proto-board and DIP-socket ICs, then moved to a slightly-buggy PCB with surface-mount components:

    The PCB implementation used a high-precision resistor divided to create a voltage reference; however, poor value/power choices led to the resistors burning out. Resistor adjustments also had to be made to the amplifiers to reduce some major signal noise. Additionally, the three status LEDs are facing south rather than up, a mistake in component selection.

    The following revision (the current blue-PCB board) added a 3.3V reference, lower-noise operational amplifiers, and some signal filtering RC circuits. A couple of minor edits to this board has resulted in a reliable, high-quality add-on board.

    R11, R13, R15, and R17 have 220pF capacitors stacked on top to reduce high-frequency noise in the input and output signals. C5 was removed as the RC filter was not working as expected, instead distorted the signal. 

    A final, fourth iteration is planned to build in these edits and produce a board that is ready for larger production and distributing to other users. Designing this iteration will also see a change in CAD software: the previous designs were done with EasyEDA, an online and proprietary solution linked to JLCPCB. This was chosen to simplify the production process, but is not ideal for more mature, open-source projects.

    So, this iteration will be designed with KiCAD. This also means that the KiCAD project files can be added to the project's repositories, making it easier for others to work with hardware "source".

View all 10 project logs

  • 1
    Gather required hardware

    See the components list for the hardware necessary to use DSP PAW. You will also need a computer running either Windows or a Linux-based operating system.

  • 2
    Download required software

    Windows

    Download and install the programs listed below. You should ensure that all of these programs are added to your PATH.

    Linux

    You will need the same programs listed above, though it would make more sense to go through your distribution's package manager to obtain them. On Debian-based systems, the following command should work:

    sudo apt install git gcc-arm-none-eabi openocd make
  • 3
    Obtain and compile the microcontroller firmware

    Open a terminal or command prompt in the folder or directory where you would like to keep the DSP PAW firmware files.

    Use git to download the DSP PAW source package:

    git clone https://code.bitgloo.com/bitgloo/dsp-paw.git
    cd dsp-paw

    Next, use git to fetch the project’s submodules (i.e. third-party dependencies): 

    git submodule update --init --recursive

    You can now enter the firmware directory and compile the source code:

    cd firmware
    make

    This should produce a directory named “build” which contains the file “ch.hex” (among others). This is the compiled firmware file that will be programmed onto the microcontroller.

View all 9 instructions

Enjoy this project?

Share

Discussions

hamslabs wrote 05/17/2023 at 16:47 point

This looks really great and i'd be up for buying one if you make them available for purchase.

  Are you sure? yes | no

Clyne wrote 06/02/2023 at 17:42 point

Thanks for your interest! I am planning to make the boards available for purchase, but first I'm doing some clean up and "finalization" of the design. I'll look into offering the boards on Tindie once it's ready.

  Are you sure? yes | no

Justin Tienter wrote 04/22/2023 at 17:21 point

I absolutely love the idea of a hardware and software solution for a learning environment. 

  Are you sure? yes | no

Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates