STM32F4 FFT example

As you maybe know, STM32F4 is Cortex M4 with DSP instructions. This allows you to make a FFT with a few simple steps. For that purpose, I have made an example, on how to create FFT with STM32F4.

I recommend use my FFT library for future use. It is built on ARM DSP library with everything included for beginner.

When the ARM company issued Cortex-M4 core, it also published DSP libraries for mathematics and other stuff. And there are also FFT functions. When you’ve downloaded ST’s standard peripheral drivers, you also downloaded CMSIS (Cortex Microcontroller Software Interface Standard), which are designed for all Cortex-M4 processors from every company.

Note: Tutorial below for Keil DSP does not work anymore with my project. For that purpose I’ve update my project and include all DSP libraries inside. All other libraries are also included in project.

CMSIS libraries are also included in Keil uVision (5 and newest), you just need to enable them. Under “Manager Run-Time Environment” -> CMSIS select DSP. DSP or Digital Signal Processing is a library for “high mathematics” instructions, which are supported by Cortex-M4 with floating point unit.

Enable DSP library in Keil uVision

Enable DSP library in Keil uVision

Fast Fourier Transform – FFT

Very fast about FFT. FFT or Fast Fourier Transform is an algorithm to convert time based signal into frequency domain. In other words, you are able to know from which sinus components is some signal created.

Everything about FFT is described on Wikipedia.


Let’s explain things that we will need here. Example on the bottom is a simple FFT audio equlizer. It will show frequencies in your audio that you will connect to pin. Sound is sampled with 44.1kHz. We will also sampled our signal with about (~) 44.1kHz. To get proper frequency from signal, we need at least 2 samples from one period of highest frequency we want to detect. For our purpose, if we sample with 44.1kHz, then largest frequency you can sample correct is 22050Hz.

One parameter in FFT result is resolution, how good you can detect different frequencies. This depends on how many samples you take before you calculate FFT. In example below, I will take 256 samples for FFT calculating, but only 128 samples will be valid to display them. In our case, we have ~45450Hz sampling frequency and if we take 256 samples, we get resolution of 45450 / 256 = 177.5 Hz. What does it say to us?

We will get back an array, basically 256 length, but results from 0 – 127 are valid, results from 128 – 255 are the same results as first one, but in reverse order.

  • You always have to take number of samples which are power of 2!
  • I took 28 = 256 samples
  • Maximal value of 1 FFT result depends on number of samples
    • If you have only DC value on the input, then everything you will get is Output[0]
    • If you create 256 samples, then it’s value will be 256
    • If you create 1024 samples, then it’s value will be 1024

I will make a table on how to interpret results from FFT output. We have resolution of ~177.5Hz, so:

FFT sample Frequency Description
Output[0] 0Hz First parameter is always DC voltage in signal
Output[1] 177.5Hz Amplitude of 177.5Hz frequency in signal
Output[n] n * 177.5Hz n-th value of your frequency result
Output[N – 1] = Output[127] 127 * Resolution = 127 * ~177.5 = ~22542Hz Maximal frequency you can analyze is always for one resolution less than half of sampling frequency -> 45450 / 2 – 177.5 = ~22547

Example

Example below works on STM32F429-Discovery board. This board has LCD on it, so it can be also a little bit graphical.

  • Provides you a FFT functionality for Cortex-M4
  • Displayed on LCD as graphical equalizer
  • Samples with 45450Hz (every 22us) one sample with ADC
    • Pin for ADC is PA0
  • On pin PA5 is an output sinus signal of 10kHz.
    • You can connect it to PA0 pin and you will get response on display
    • If you do that, you will see that main bar is almost on the middle of LCD, because 10000Hz / 22750 = almost 0.5
  • If you want connect sound to the board, then you have to connect it in a way like image below

    Connect sound to STM32F4

    Connect sound to STM32F4

  • If nothing is connected to PA0 pin, then you will get Output(0) with maximum value and one bar will be displayed
  • ADC is not driven with DMA, it’s with “delay” mode. It’s good to show you principle on FFT.
    • In production, you should use at least DMA, maybe double buffered DMA

If you have any problems with compiling project, I’ve added compiled “FFT.hex” file to project.

Download entire project with libraries and hex file below.

Icon
Project 01- STM32F429-Discovery FFT

FFT sample project for STM32F429-Discovery board using LCD to display bars.

tilz0R

Owner of this site. Also electronic enthusiasts, web developer, 3D printer fan, handball player and more. Big fan of STM32F4 devices. In anticipation of the new Discovery board for STM32F7 lines.

You may also like...

Read before commenting!

Before you make a new comment, make sure you agree with things listed below:

  • - Read post to make sure if it is already posted what you are asking for,
  • - Make sure you have the latest version of libraries used in your project,
  • - Make a clean and grammatically correct written message,
  • - Report as many details as possible, including what have you done so far,
  • - Do NOT post any code here. Use Pastebin,
  • - Do NOT post any error codes here. Use Pastebin,
  • - Specify STM32Fxxx family and used Discovery/EVAL/Nucleo or custom made board,
  • - Make sure your clock is set correct for PLL,
  • - If you are using my HAL drivers, please check this post how to start.
Comment will be deleted on breaking these rules without notification!
  • sapher

    nice it’s really helpful

  • fofo

    It’s very helpful. However with the Keil updates, we have to use the HAL drivers because the standard ones do not work anymore. Do you have any idea how we could counteract this while still using your code ? I believe the error I have come from the libraries. Thank you.

    • Yes, you are right.
      Download keil project from link above and open it. There are files connected externally. Should work on newest Keil versions, including 5.14.

      • fofo

        Thank you for the answer. I’ve had quite a few problems while trying to make it work. How would you say is the best way to make it work after downloading the files. I am really new to Keil and the stm32f4 🙁

  • Seer73

    How would I go about watching the value in the Output array as well as maxIndex and maxValue?
    Thanks

    • Hi,

      One option is that you go to debug mode and put variable to “Watch” and look there,
      another option is to send data via USART at really high speed (1Mbit or more) and make graph on computer.
      Or better, you can use USB VCP for that which allows you even more speed.

      • Seer73

        Hey, thanks for quick reply.
        I’m trying to do this using the watch window and in the ‘value’ field I get for each and in the ‘Type’ heading it is saying that it’s uchar?

        • Make all variabless global.

          • Seer73

            I don’t think I’ve missed any; maxValue, maxIndex & i are all global. Still no change 🙁

          • If you are running directly this example, then they are not by default.
            Btw..why you need that? You can look two variables with usart easily

          • Seer73

            Have taken this example and simply cut/pasted them under global variables.
            Using an external signal source, I’m wanting to manipulate its frequency before reproducing it on the DAC. Haven’t the time to wire & code USART up…
            Just assumed it’d be possible to view the values in the variables under the watch window

          • Seer73

            Have found out what was causing the problem. I was trying to insert some breakpoints for another part in the now adapted program example & after encountering problems with that, checked under the settings. For some reason after opening up the project file that was downloaded, the ‘Output’ tab on the options for target had the ‘Debug Information’ box unchecked. All sorted!!

          • A ok,

            well this is not “for some reason”. I did that with a reason.

  • Lee Liang De

    Hi,

    I am getting this error:

    .TargetsSTM32F429_Discoveryproject.axf: error: L6047U: The size of this image (62716 bytes) exceeds the maximum allowed for this version of the linker

    Is there any ways to solve it>

    Thanks

  • Pingback: Library 62- Fast Fourier Transform (FFT) for STM32F4xx - STM32F4 Discovery()

  • fwaz

    Hi, I currently doing the FFT for audio data from MP45DT02, But when I run the program it suddenly jump to hard fault handler. Do you have experience doing the FFT for audio data?

    • Sounds like problens with too small buffers. Remember that input buffer twice of fft size!

      Also, make buffees global variables.

  • hemant

    HI,
    i m new at this ,can we increase the sampling frequency more than 44.4KHz?
    i have to sample a signal of frequency about 63KHz, so more than 126 KHz sampling Frequency would be required . If it is possible ,plz explain.
    thanks

    • Yes you can, and you have several options:

      1. Init ADC and its DMA, and init TIM which will trigger sampling for your ADC every x us so you will have 126kHz sampling.
      2. Use my example above, just change delay at line 94 to some value to get proper sampling frequency. For 126kHz, you should set it to 7us.

      PS: Functions used in this example from ARM math are deprecated in ARM library. Check my FFT library and use it if you want to.

      • hemant

        yeah ,its working ,
        thank alot

  • yudrassil

    really helpful, but why do you set the imaginary part to 0?

    thanks

    • You have sampled some signal with adc. What is imaginary part in this signal?

      • yudrassil

        Oh Damn!
        You are right, thanks a lot.

      • Mia

        hello,Majerle Tilen, i have a simular question about that, in my program include both real part and imaginary part in the input signal. It is a radar signal.So can i use the program and what should i change.

        thanks

        • You can use. You have real and imag part of data in input signal if you take a look at example above.

          • Mia

            hi TM,
            sorry to trouble you again, now i want to check the result of ADC, but in my board there are no LCD, so can i after the sentence “Input[(uint16_t)i] =…….” Line 99 of the main, add the sentence of printf, and to see the ADC in PC? i am not sure it is correct.

          • You can.

          • Mia

            hi TM,
            I am here again, i have calculated for some times but i can’t get the sample frequency of 44khz, i am only want to make sure, the sample frequency is still at the value of 44khz at my stm32f446 board, because some thing has changed…
            thank you very much

          • What sample frequency you get?

          • Mia

            In fact, i don’t know the sample frequency, i don’t use the LCD with my board and i can only read the FFT result on PC through the series port…When i nearly get the result i suddenly find the problem so i want to make sure…

        • Mia

          hi TM,
          i have used your example above, and also i want to make sure, can i add a FIR filter after the FFT and then get the max value ?
          thx

          • You can add FIR filter, but I’m not sure how you will detect max value with it?

          • Mia

            hi TM,
            at first i use the function “arm_max_f32(Output, FFT_SIZE, &maxValue, &maxIndex)” to get the max FFT result, but now with the FIR i think i can not use it anymore. How about to add a “while” to compair or can you give me some other advise.
            thx

          • I’m not sure I understand what is your point of doing with FIR after FFT and what type of MAX value you wanna detect.

          • Mia

            sorrry about that, I need to do the FFT at first, and then i need to filter the high frequency part like f+Δf , after that i want to get the highest frequency in lower frequency part and use that to calculate. So i must do the process FFT+ FIR.

          • I assume you need to make FFT twice then.

          • Mia

            sorry , what do you mean make FFT twice, after the FIR one more FFT?

          • So you wanna make a fft. Ok, do it. Then you wanna filter signal. Ok do it. And next, if you want to get max frequency in this signal, use fft again on filtered signal and get max amplitude.

            What is not clear here?

          • Mia

            so clear now, i will try it and thanks a lot

  • seprac

    Hey TM..Thanks a lot for all the help you are doing….I am trying to implement the FFT to sample a 3Khz sin wave (generated from an actual source). I am using a sampling frequency of 12Khz, with the help of external trigger from a timer.

    How did you measure the voltage during negative half cycle?
    Since the boards don’t support negative voltage…I am using a DC offset…
    My understanding is for every (1/12000) seconds, I will be sampling. This way I am sampling 256 times.
    When I watch the output array in debug….I see that I get the peak at output[21]…even when I increase the sampling frequency by reducing my prescalar I again find a peak at 21….However, I noticed that on increasing my frequency…I am getting proportionate maxIndex…but even that remains same irrespective of sampling frequency…Am i doing anything wrong?

    • On image you can see how to connect input. And then you must substract virtual zero in program.

      You use DMA or how? I dont know what is going on.

      • seprac

        Hi. Thanks….I never thought of it…I can subtract the DC offset value in the program…I will do that and verify the algorithm….I am not using DMA….Just using trigger from TIM2 to start the conversion of ADC and sampling in the ADC Interrupt handler….Also…do you have any idea how much is the VDD when we connect it through our laptop…or does it vary with respect to laptop manufacturer…?

        • seprac

          Hey TM…. Now it is working perfectly fine. Thanks a lot for your help. Instead of sampling in ADC handler, i did them in TIM period elapsed handler…..

          Just have a small doubt regarding the VDD on connecting via mini USB from a laptop…. Do you have an idea of around what value can it be…. I am getting around 1.5 volt for the register value of 4095…

          • 1.5V is on VDD pin?

          • seprac

            No… It is the value for which my ADC reads 4095…. So i am assuming it is Vref and therefore Vdd

          • If you have offset of 1.5v and you apply 1.5ac, that is 3V

          • seprac

            Hi…. For the example mentioned above…. i am using 0.5 v offset and 0.5 v sine….

            When i apply a dc voltage of 1.5 v i get a full scale reading from adc….

            I don’t know if it was a mistake but i am almost sure i was getting a full scale reading for 3V last week when i was testing…. This week i am getting 1.5 v…. Unable to understand

          • Are you sure you measure correct channel?
            Is pin really initialized as analog?

            Make sure about everything.

          • seprac

            I verified them…and they appear alright to me….anyways I’ll update you in case I come across the solution to the problem..for now, more importantly my FFT seems to be working fine….thanks once again…

  • Pingback: HAL Library 14- Fast Fourier Transform for STM32Fxxx - STM32F4 Discovery()

  • James

    If I want to use the code for STM32F103, what should I change?

    • First, don’t use float variables but integer. Check FFT functions for integers, documentation available http://www.keil.com/pack/doc/CMSIS/DSP/html/group___complex_f_f_t.html

      That would be basically everything regarding Cortex-M3 processor.

      • Maria

        Hi TM,
        if i want to use for stm32f446, and to sample the 1khz signal ,what should i do ?
        ths

        • What you should do?
          Sample at 1kHz and make FFT of samples of desired length.

      • Ysf

        Why it is not possible with float for STM32F103 ?

        • Nobody sam d it is not possible, just dont use float if not required. In most cases, it is not. Float on cm3 is way slower than integer.

  • Immer

    Thank you for posting this, it’s exactly what I was hoping to find.

    A quick question that you might be able to help me with:

    Do you think that the STM32F4 might be powerful enough to sample at 125khz and do FFT analysis on 8 sinewaves (with predefined, unchangeable frequencies per input) to discover their amplitude?

    Thank you again.

    • Answer is yes.
      Performance depends on block size of data to process at a time.

  • Venugopal Saminathan

    hi Majerle Tilen,
    the STM32F4 FFT code works fine for first time (immediate after reset).
    But from next fft magnitudes are getting added. even it continuing after initilizing the input to &input[0], and output to &output[0].

    please help me to correct the error.

    • Very strange. Try my fft lib if there is the samr behaviour.

  • Alina

    Hi TM!
    I used the above project in keil for my stm32f4 discovery board, removed all the lcd part, successfully compiled and loaded the code to the board, connected an actual electret microphone on PA0 just like you mentioned, played a variety of sounds, but there was no response by the the leds. Could you please guide me what step I am probably missing?

    • Hi,

      well this “led” addition to program is not really accurate 🙂
      It may not work. But anyway, did you add circuit like I draw on example?

      • Alina

        Yes, the capacitor however is of 100uF.

        • Maybe is a problem you board is not properly configured for example. Example was made on F429-Disco, you have F4-Disco. Check if leds are properly setup.

          • Alina

            Could you please list down all the necessary changes that were to be made for making this code compatible for the STM32F4 discovery board? Because im surely missing out something. My board isn’t showing any response 🙁

          • Remove LCD and SDRAM parts.

          • Alina

            Thank you for the quick response. No need to change the target controller?

          • Did you check for LED configuration?
            If you put sound with proper intensity and leds are properly set, then I think you must see something.

          • Alina

            No i didnt. Ill check that. Thank you!

  • Hokus Pokus

    Hi why: “Real part, must be between -1 and 1”
    I don’t understand clearly. In explample from CMSIS DSP values are above that range ?

    • Hello,

      it was my first thought about that fact, but totally, totally wrong from me.
      It does not need to be 🙂

      • Hokus Pokus

        OK 😉 . Do you know any why to fast copy result from adc
        buffer which we get from dma to input buffer in function arm_cfft_f32. From adc we get only real part of complex number, but this function needs input buffer with sequence order real,imaginary,.. etc. So we slow down proccess by a fact coping adc input buffer to input buffer with complex numbers one by one in loop ?

        • Actually I dont know. But you can take a look at arm cmsis documentation on keil.com for real fft function.

  • Pingback: All STM32F4 tutorials - STM32F4 Discovery()

  • MinHuWon

    Hi TM. Thank you for sharing your librarys.
    Your code works very well on my 429disco board.
    but dose not work on my own board which i made and got same mcu, stm32f429zi.
    every other code works same on both board but dsp code dosen’t
    Do you have any idea about this problem?

  • Yosa

    Hi TM,
    I use DSP_Lib under the CubeF4 package for FFT bin example, getting some error while the controller executes the function arm_sqrt_f32(float32_t, float32_t*). would you please help?

    • Yosa

      it goes to infinite loop when the controller executes arm_sqrt_f32()

      • Stack size?

        • Yosa

          /* Entry Point */
          ENTRY(Reset_Handler)

          /* Highest address of the user mode stack */
          _estack = 0x20020000; /* end of RAM */

          _Min_Heap_Size = 0; /* required amount of heap */
          _Min_Stack_Size = 0x400; /* required amount of stack */

          /* Memories definition */
          MEMORY
          {
          RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
          ROM (rx) : ORIGIN = 0x8000000, LENGTH = 1024K
          }

          • yosa

            1024 bytes

          • yosa

            Stack size is 1024 bytes..i tried by changing it to 6144 bytes .still facing error. Please help.

  • Qfnnn

    Hi TM,

    I have a question about ADC in your code: the output value of ADC1 minus 2048. Is 2048 a fixed average value of DC offset? Or it will change according to different settings of ADC (e.g. bit resolution) ?

    Thanks.

    • At 12 bits, 2048 is center of signal. If you have for example sinus signal, you have to add DC value to signal before you can convert it on ADC because ADC can’t measure negative voltage. Because of that DC offset, you have to substract that value in program. I choose DC value of VCC / 2 which gives me 11bit value and is 2048.

      • Qfnnn

        I think I get your point.

        I add VCC/2 offset and use ADC to record sounds in 12 bit resolution and store them in .wav file (16 bit per sample). There will be 4 bit 0 each sample so I plan to store data in the following way:

        First sample : 12 bit of first data + 4 bit of second data
        Second sample: last 8 bit of second data + 8 bit of third data
        Third sample:…

        Now, the DC offset could not be 2048 (2^11) and should it be 32768 (2^15)?

        • I’m not sure I did understand you.
          Try to use 16-bit aligned access for each sample will make this your life easier.

          • Qfnnn

            Thanks, you are right… My way is quite awkward.

            So now with each 16-bit sound sample, the DC offset required to be removed would be 2^15, right?

          • No, why?
            ADC returns you 12 bits max, so if you have greater memory align, just add zeros at beginning and that’s all. You still have to subtract actual DC value.