Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

STM32 Timer-Triggered ADC Sampling with DMA for FFT Analysis

Tech 1

Signal Analysis Parameters

To analyze a signal using FFT, several key parameters must be defined:

  • Target signal frequency: f
  • Target signal period: T = 1/f
  • Sampling frequency: fs
  • Sampling period: Ts = 1/fs
  • Total sample points: NPT
  • Total sampling time: t = NPT × Ts
  • Frequency resolution: f0 = fs/NPT
  • Captured signal periods: NT = t/T
  • Samples per signal period: fs/f

Practical Example Configuration

For a 1 kHz signal sampled at 32 kHz with 256 points:

Signal frequency: f = 1000 Hz
Signal period: T = 0.001 s
Sampling frequency: fs = 32000 Hz
Sampling period: Ts = 31.25 μs
Total samples: NPT = 256
Total time: t = 8 ms
Frequency resolution: f0 = 125 Hz
Captured periods: NT = 8
Samples per period: 32

Frequency Resolution Considerations

The frequency resolution f0 = 125 Hz determines the spacing between frequency bins in the FFT spectrum. Signals at multiples of 125 Hz will appear as peaks in the spectrum. The actual amplitude is twice the displayed value due to symmetrical positive and negative frequency components.

Hardware Configuration

Timer Setup for ADC Triggering

Configure a timer to generate periodic triggers matching the desired sampling frequency:

// Timer configuration for 32 kHz sampling
void Timer_Config(void)
{
    TIM_HandleTypeDef timer_handle;
    
    timer_handle.Instance = TIM2;
    timer_handle.Init.Prescaler = 0;
    timer_handle.Init.CounterMode = TIM_COUNTERMODE_UP;
    timer_handle.Init.Period = (SystemCoreClock / 32000) - 1;
    timer_handle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    
    HAL_TIM_Base_Init(&timer_handle);
    HAL_TIM_Base_Start(&timer_handle);
}

ADC Configuration

Set up ADC for external trigger, single conversion mode:

void ADC_Config(void)
{
    ADC_HandleTypeDef adc_handle;
    ADC_ChannelConfTypeDef channel_config;
    
    adc_handle.Instance = ADC1;
    adc_handle.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T2_TRGO;
    adc_handle.Init.ContinuousConvMode = DISABLE;
    adc_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT;
    adc_handle.Init.NbrOfConversion = 1;
    adc_handle.Init.ScanConvMode = DISABLE;
    
    HAL_ADC_Init(&adc_handle);
    
    channel_config.Channel = ADC_CHANNEL_0;
    channel_config.Rank = 1;
    channel_config.SamplingTime = ADC_SAMPLETIME_3CYCLES;
    HAL_ADC_ConfigChannel(&adc_handle, &channel_config);
}

DMA Configuration

Configure DMA for peripheral-to-memory transfer with memory address increment:

void DMA_Config(void)
{
    DMA_HandleTypeDef dma_handle;
    
    dma_handle.Instance = DMA1_Channel1;
    dma_handle.Init.Direction = DMA_PERIPH_TO_MEMORY;
    dma_handle.Init.PeriphInc = DMA_PINC_DISABLE;
    dma_handle.Init.MemInc = DMA_MINC_ENABLE;
    dma_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
    dma_handle.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
    dma_handle.Init.Mode = DMA_NORMAL;
    dma_handle.Init.Priority = DMA_PRIORITY_LOW;
    
    HAL_DMA_Init(&dma_handle);
    __HAL_LINKDMA(&adc_handle, DMA_Handle, dma_handle);
}

Complete Implementation Example

Header File (adc_dma.h)

#ifndef ADC_DMA_H
#define ADC_DMA_H

#include "stm32f4xx.h"

#define SAMPLE_COUNT 256

extern uint16_t sample_buffer[SAMPLE_COUNT];

void ADC_DMA_Init(void);
void Trigger_Sampling(void);

#endif

ADC and DMA Implementation

#include "adc_dma.h"

uint16_t sample_buffer[SAMPLE_COUNT];

void ADC_DMA_Init(void)
{
    GPIO_InitTypeDef gpio_config;
    DMA_InitTypeDef dma_config;
    ADC_InitTypeDef adc_config;
    ADC_CommonInitTypeDef adc_common_config;
    
    // Enable clocks
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC3, ENABLE);
    
    // Configure GPIO for analog input
    gpio_config.GPIO_Pin = GPIO_Pin_3;
    gpio_config.GPIO_Mode = GPIO_Mode_AN;
    gpio_config.GPIO_Speed = GPIO_Speed_100MHz;
    gpio_config.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOF, &gpio_config);
    
    // ADC common configuration
    adc_common_config.ADC_Mode = ADC_Mode_Independent;
    adc_common_config.ADC_Prescaler = ADC_Prescaler_Div4;
    adc_common_config.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
    adc_common_config.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;
    ADC_CommonInit(&adc_common_config);
    
    // ADC configuration
    adc_config.ADC_Resolution = ADC_Resolution_12b;
    adc_config.ADC_ScanConvMode = DISABLE;
    adc_config.ADC_ContinuousConvMode = DISABLE;
    adc_config.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_TRGO;
    adc_config.ADC_DataAlign = ADC_DataAlign_Right;
    adc_config.ADC_NbrOfConversion = 1;
    ADC_Init(ADC3, &adc_config);
    
    // Configure ADC channel
    ADC_RegularChannelConfig(ADC3, ADC_Channel_9, 1, ADC_SampleTime_84Cycles);
    
    // DMA configuration
    dma_config.DMA_Channel = DMA_Channel_2;
    dma_config.DMA_PeripheralBaseAddr = (uint32_t)&(ADC3->DR);
    dma_config.DMA_Memory0BaseAddr = (uint32_t)sample_buffer;
    dma_config.DMA_DIR = DMA_DIR_PeripheralToMemory;
    dma_config.DMA_BufferSize = SAMPLE_COUNT;
    dma_config.DMA_PeripheralInc = DMA_PINC_DISABLE;
    dma_config.DMA_MemoryInc = DMA_MINC_ENABLE;
    dma_config.DMA_PeripheralDataSize = DMA_PDATAALIGN_HALFWORD;
    dma_config.DMA_MemoryDataSize = DMA_MDATAALIGN_HALFWORD;
    dma_config.DMA_Mode = DMA_Mode_Normal;
    dma_config.DMA_Priority = DMA_Priority_Medium;
    DMA_Init(DMA2_Stream0, &dma_config);
    
    // Enable peripherals
    DMA_Cmd(DMA2_Stream0, ENABLE);
    ADC_DMACmd(ADC3, ENABLE);
    ADC_Cmd(ADC3, ENABLE);
}

void Trigger_Sampling(void)
{
    // Restart DMA for new sampling sequence
    DMA_Cmd(DMA2_Stream0, DISABLE);
    DMA_ClearITPendingBit(DMA2_Stream0, DMA_IT_TCIF0);
    ADC_ClearITPendingBit(ADC3, ADC_IT_OVR);
    DMA_Cmd(DMA2_Stream0, ENABLE);
}

FFT Processing

// Buffer declarations
int32_t input_buffer[SAMPLE_COUNT];
int32_t output_buffer[SAMPLE_COUNT];
uint32_t magnitude_buffer[SAMPLE_COUNT/2];

void Process_FFT(void)
{
    // Prepare input data (shift ADC values to high 16 bits)
    for(uint16_t i = 0; i < SAMPLE_COUNT; i++) {
        input_buffer[i] = ((int16_t)sample_buffer[i]) << 16;
    }
    
    // Perform FFT
    cr4_fft_256_stm32(output_buffer, input_buffer, SAMPLE_COUNT);
    
    // Calculate magnitude spectrum
    for(uint16_t i = 0; i < SAMPLE_COUNT/2; i++) {
        int32_t real = output_buffer[i] & 0xFFFF;
        int32_t imag = output_buffer[i] >> 16;
        magnitude_buffer[i] = sqrt(real*real + imag*imag);
    }
}

Key Implementation Notes

  • Timer period calculation: (SystemCoreClock / sampling_frequency) - 1
  • ADC sampling time must be sufficient for signal settling
  • DMA should be configured in normal mode for single acquisitions
  • FFT input data requires proper scaling and formtating
  • Frequency bin indexing corresponds to f0 × bin_number

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.