[DemoCode下载]

FFT算法实现

[复制链接]
10697|30
手机看帖
扫描二维码
随时随地手机跟帖
jiekou001|  楼主 | 2023-6-20 16:29 | 显示全部楼层 |阅读模式
EC_NUC121_FFT_Demo_V1.00.zip (1.36 MB)

使用特权

评论回复
jiekou001|  楼主 | 2023-6-20 16:30 | 显示全部楼层
/**************************************************************************//**
* @file     main.c
* @version  V1.00
* @brief    Perform Fast Fourier Transform with ADC samples.
*
* @copyright (C) 2019 Nuvoton Technology Corp. All rights reserved.
*****************************************************************************/
#include <stdio.h>
#include <math.h>
#include "NuMicro.h"
#include "fft_lut.h"

/* Select the macro FFT_N_POINT_SEL defined in fft_lut.h.
* The look-up table for fft algorithm requires user indicates specific data length.
*/

/* System clock frequency */
#define PLL_CLOCK                   (48000000)

/* The parameter SAMPLE_RATE is configurable. It is based on your application.
* The unit of SAMPLE_RATE is hertz.
*/
#define SAMPLE_RATE                 (1024)

/* The parameter SAMPLE_DATA_RSHIFT is configurable, the purpose is to truncate
* the data size by right shifting the raw ADC data, and it can prevent the buffer
* from overflow when calculating a large amount N point of FFT. The longer data
* length is selected, the higher SAMPLE_DATA_RSHIFT is required. If the spectrum
* buffer is overflow, try to increase the SAMPLE_DATA_RSHIFT parameter.
*/
#define SAMPLE_DATA_RSHIFT          (5)
#define SHRINK(x)                   ((x) >> SAMPLE_DATA_RSHIFT)


/* Macros to simplify the math presentations for FFT algorithm */
#define COS(angle_index)            (g_ai16cos[angle_index])
#define SIN(angle_index)            (g_ai16sin[angle_index])
#define REV(origin_index)           (g_ai16rev[origin_index])

/*----------------------------------------------------------------------------------------------------------*/
/* Define Function Prototypes                                                                               */
/*----------------------------------------------------------------------------------------------------------*/
void SYS_Init(void);
void UART0_Init(void);
void TIMER0_Init(void);
void ADC_Init(void);
void Remove_DC_Component(int *pi32DataBuffer, uint32_t u32Len);
void NuFFT(int *pi32RealBuffer, int *pi32ImageBuffer, uint32_t u32Len);
void Display_Spectrum(int *pi32Spectrum, uint32_t u32Len, uint32_t u32SampleRate);
void Display_AdcData(int *pi32Data, uint32_t u32Len);

/*----------------------------------------------------------------------------------------------------------*/
/* Define global variables and constants                                                                    */
/*----------------------------------------------------------------------------------------------------------*/
const uint8_t g_u8AdcChannel = 2;

/* Look-up table for FFT twiddle factors and the bit-reversal index */
const int16_t g_ai16cos[SEQUENCE_LEN] = COS_LUT;
const int16_t g_ai16sin[SEQUENCE_LEN] = SIN_LUT;
const int16_t g_ai16rev[SEQUENCE_LEN] = REV_LUT;

/* The data buffer for storing the ADC data samples and the output spectrum */
int g_i32RealBuffer [SEQUENCE_LEN];
int g_i32ImageBuffer[SEQUENCE_LEN];
int g_i32Spectrum   [SEQUENCE_LEN];

/* Global variables for handing ADC conversions */
volatile uint32_t g_u32AdcConvNum = 0;


/*----------------------------------------------------------------------------------------------------------*/
/*  Function:    SYS_Init                                                                                   */
/*  Parameters:  None.                                                                                      */
/*  Returns:     None.                                                                                      */
/*  Description: System and periphial module clock setting.                                                 */
/*----------------------------------------------------------------------------------------------------------*/
void SYS_Init(void)
{
    /* Enable HIRC clock (Internal RC 48MHz) */
    CLK_EnableXtalRC(CLK_PWRCTL_HIRCEN_Msk);

    /* Enable HIRC clock (Internal RC 48MHz) */
    CLK_EnableXtalRC(CLK_PWRCTL_HXTEN);

    /* Wait for HIRC clock ready */
    CLK_WaitClockReady(CLK_STATUS_HIRCSTB_Msk);

    CLK_WaitClockReady(CLK_STATUS_HXTSTB_Msk);

    /* Set core clock as PLL_CLOCK from PLL (From HXT if HXT is enabled)*/
    CLK_SetCoreClock(PLL_CLOCK);

    /* Enable UART module clock */
    CLK_EnableModuleClock(UART0_MODULE);

    /* Select UART module clock source as HIRC/2 and UART module clock divider as 1 */
    CLK_SetModuleClock(UART0_MODULE, CLK_CLKSEL1_UARTSEL_HIRC_DIV2, CLK_CLKDIV0_UART(1));

    /* Enable TMR0 module clock */
    CLK_EnableModuleClock(TMR0_MODULE);

    /* Set TMR0 module clock source as HIRC/2 */
    CLK_SetModuleClock(TMR0_MODULE, CLK_CLKSEL1_TMR0SEL_HXT, 0);

    /* Enable ADC module clock */
    CLK_EnableModuleClock(ADC_MODULE);

    /* Set ADC clock source to PCLK0=48MHz, set divider to 3, ADC clock will be 16 MHz */
    CLK_SetModuleClock(ADC_MODULE, CLK_CLKSEL1_ADCSEL_PCLK0, CLK_CLKDIV0_ADC(3));

    /* Update core clock */
    SystemCoreClockUpdate();


    /* Set GPB multi-function pins for UART0 RXD and TXD */
    SYS->GPB_MFPL = SYS_GPB_MFPL_PB0MFP_UART0_RXD | SYS_GPB_MFPL_PB1MFP_UART0_TXD;

    /* Set PD.2 and PD.3 to input mode */
    PD->MODE &= ~(GPIO_MODE_MODE2_Msk | GPIO_MODE_MODE3_Msk);

    /* Set PD2 and PD3 to ADC mode for ADC input channel 2 and 3 */
    SYS->GPD_MFPL &= ~(SYS_GPD_MFPL_PD2MFP_Msk | SYS_GPD_MFPL_PD3MFP_Msk);
    SYS->GPD_MFPL |= (SYS_GPD_MFPL_PD2MFP_ADC_CH2 | SYS_GPD_MFPL_PD3MFP_ADC_CH3);

    /* Disable the digital input paths of ADC analog pins */
    GPIO_DISABLE_DIGITAL_PATH(PD, BIT2 | BIT3);
}

/*----------------------------------------------------------------------------------------------------------*/
/*  Function:    UART0_Init                                                                                 */
/*  Parameters:  None.                                                                                      */
/*  Returns:     None.                                                                                      */
/*  Description: Initialize UART0 module, setting buadrate is 115200 bps                                    */
/*----------------------------------------------------------------------------------------------------------*/
void UART0_Init(void)
{
    /* Reset UART0 */
    SYS_ResetModule(UART0_RST);

    /* Configure UART0 and set UART0 baud rate */
    UART_Open(UART0, 115200);
}

/*----------------------------------------------------------------------------------------------------------*/
/*  Function:    TIMER0_Init                                                                                */
/*  Parameters:  None.                                                                                      */
/*  Returns:     None.                                                                                      */
/*  Description: Initialize TIMER0 module. Setup timeout trigger ADC.                                       */
/*----------------------------------------------------------------------------------------------------------*/
void TIMER0_Init(void)
{
    uint32_t u32Freq;

    /* Set TIMER0 working frequency to SAMPLE_RATE */
    u32Freq = TIMER_Open(TIMER0, TIMER_PERIODIC_MODE, SAMPLE_RATE);

    if (u32Freq != SAMPLE_RATE)
    {
        printf("# Warning. Sample rate does not equal to the pre-defined setting.\n\r");
    }

    /* Enable timer timeout interrupt trigger ADC */
    TIMER0->CTL |= TIMER_TRGSRC_TIMEOUT_EVENT | TIMER_TRG_TO_ADC | TIMER_CTL_INTEN_Msk;

    printf(" CTL:0x%08X\n\r", TIMER0->CTL);
    printf(" CMP:0x%08X\n\r", TIMER0->CMP);

}

/*----------------------------------------------------------------------------------------------------------*/
/*  Function:    ADC_Init                                                                                   */
/*  Parameters:  None.                                                                                      */
/*  Returns:     None.                                                                                      */
/*  Description: Initialize ADC module channel 2. Enable TIMER trigger ADC function.                        */
/*----------------------------------------------------------------------------------------------------------*/
void ADC_Init(void)
{
    /* Set the ADC operation mode as single, input mode as single-end and enable the analog input channel 2 */
    ADC_Open(ADC, ADC_ADCR_DIFFEN_SINGLE_END, ADC_ADCR_ADMD_SINGLE, 0x1 << g_u8AdcChannel);

    /* Enable TIMER Trigger ADC */
    ADC_EnableTimerTrigger(ADC, ADC_ADCR_TRGS_TIMER, 0);

    /* Power on ADC module */
    ADC_POWER_ON(ADC);

    /* Clear the A/D interrupt flag for safe */
    ADC_CLR_INT_FLAG(ADC, ADC_ADF_INT);

    /* Enable the ADC interrupt */
    ADC_EnableInt(ADC, ADC_ADF_INT);
    NVIC_EnableIRQ(ADC_IRQn);
}

/*----------------------------------------------------------------------------------------------------------*/
/*  Function:    Remove_DC_Component                                                                        */
/*  Parameters:  pi32DataBuffer is the raw data buffer pointer.                                             */
/*  Parameters:  u32Len is the length of the raw data buffer.                                               */
/*  Returns:     None.                                                                                      */
/*  Description: None.                                                                                      */
/*----------------------------------------------------------------------------------------------------------*/
void Remove_DC_Component(int *pi32DataBuffer, uint32_t u32Len)
{
    uint32_t i, u32Mean, u32Accumulate = 0;

    for (i = 0; i < u32Len; i++)
    {
        u32Accumulate += pi32DataBuffer[i];
    }

    u32Mean = u32Accumulate / u32Len;

    for (i = 0; i < u32Len; i++)
    {
        pi32DataBuffer[i] -= u32Mean;
    }
}

/*----------------------------------------------------------------------------------------------------------*/
/*  Function:    NuFFT                                                                                      */
/*  Parameters:  pi32RealBuffer  is the pointer to the real  part of the data buffer.                       */
/*  Parameters:  pi32ImageBuffer is the pointer to the image part of the data buffer.                       */
/*  Parameters:  u32Len indicates the length of the data sequence.                                          */
/*  Returns:     None.                                                                                      */
/*  Description: Fast Fourier Transform Algorithm. Transform the time-domain data                           */
/*               sequence into a frequency-domain spectrum.                                                 */
/*----------------------------------------------------------------------------------------------------------*/
void NuFFT(int *pi32RealBuffer, int *pi32ImageBuffer, uint32_t u32Len)
{
    const uint32_t N = u32Len;
    const uint32_t u32TotalStage = log(N) / log(2);

    uint32_t u32Stage, u32Span, u32Node, u32Twiddle, u32Angle;
    int32_t i32X1Real, i32X1Image, i32X2Real, i32X2Image;

    /* Iterations for log2(N) FFT butterfly stages */
    for (u32Stage = 0; u32Stage < u32TotalStage; u32Stage++)
    {
        /* Span indicates the buffer index for constituting a butterfly matrix */
        u32Span = pow(2, u32Stage);

        for (u32Twiddle = 0; u32Twiddle < u32Span; u32Twiddle++)
        {
            u32Angle = (N >> 1) / u32Span * u32Twiddle;

            for (u32Node = u32Twiddle; u32Node < N; u32Node += 2 * u32Span)
            {
                /* Get the real and image part of the input variable X1 */
                i32X1Real  = pi32RealBuffer [u32Node];
                i32X1Image = pi32ImageBuffer[u32Node];

                /* Get the real and image part of the input variable X2 */
                i32X2Real  = pi32RealBuffer [u32Node + u32Span];
                i32X2Image = pi32ImageBuffer[u32Node + u32Span];

                /* Y1 = X1 + X2 * twiddle factor
                 * The real part is   real * cos() + image * sin()
                 * The image part is -real * sin() + image * cos()
                 */
                pi32RealBuffer [u32Node] = i32X1Real  + RESCALE(i32X2Real * COS(u32Angle)) + RESCALE(i32X2Image * SIN(u32Angle));
                pi32ImageBuffer[u32Node] = i32X1Image - RESCALE(i32X2Real * SIN(u32Angle)) + RESCALE(i32X2Image * COS(u32Angle));

                /* Y2 = X1 - X2 * twiddle factor
                 * The real part is  -real * cos() - image * sin()
                 * The image part is  real * sin() - image * cos()
                 */
                pi32RealBuffer [u32Node + u32Span] = i32X1Real  - RESCALE(i32X2Real * COS(u32Angle)) - RESCALE(i32X2Image * SIN(u32Angle));
                pi32ImageBuffer[u32Node + u32Span] = i32X1Image + RESCALE(i32X2Real * SIN(u32Angle)) - RESCALE(i32X2Image * COS(u32Angle));
            }
        }
    }
}

/*----------------------------------------------------------------------------------------------------------*/
/*  Function:    Display_Spectrum                                                                           */
/*  Parameters:  pi32Spectrum is the spectrum buffer pointer.                                               */
/*  Parameters:  u32Len is the length of the spectrum buffer.                                               */
/*  Parameters:  u32SampleRate is the frequency of TIMER trigger EADC.                                      */
/*  Returns:     none.                                                                                      */
/*  Description: Show the detail informations from the spectrum bffer.                                      */
/*----------------------------------------------------------------------------------------------------------*/
void Display_Spectrum(int *pi32Spectrum, uint32_t u32Len, uint32_t u32SampleRate)
{
    const float  fconstResolution = (float)u32SampleRate / (float)u32Len;

    int i, i32Base, i32Accumulate = 0;

    printf("+-----------------------------------------------------------+\n\r");
    printf("Spectrum Parameters         \n\r");
    printf("# Sample rate: %d Hz        \n\r", u32SampleRate);
    printf("# Data length: %d samples   \n\r", u32Len);
    printf("# Resolution : %.2f Hz      \n\r", fconstResolution);
    printf("+-----------------------------------------------------------+\n\r");
    printf("#Index     #Frequency(Hz)     #Amplitude     #Ratio \n\r");

    for (i = 0; i < u32Len; i++)
    {
        i32Accumulate += pi32Spectrum[i];
    }

    for (i = 0; i < u32Len; i++)
    {
        i32Base = (i <= u32Len / 2) ? (i) : (i - u32Len);

        printf(" %-7d    %-15.2f    %-11d    %.2f%%\n\r", i, (float)(fconstResolution * i32Base), pi32Spectrum[i], (float)pi32Spectrum[i] / (float)i32Accumulate * 100);

    }

    printf("+-----------------------------------------------------------+\n\r");
    printf("FFT complete\n\r");
}

/*----------------------------------------------------------------------------------------------------------*/
/*  Function:    Display_AdcData                                                                            */
/*  Parameters:  pi32Spectrum is the Time Domian Data buffer pointer.                                       */
/*  Parameters:  u32Len is the length of the Data.                                                          */
/*  Returns:     none.                                                                                      */
/*  Description: Show the detail informations from the spectrum bffer.                                      */
/*----------------------------------------------------------------------------------------------------------*/
void Display_AdcData(int *pi32Data, uint32_t u32Len)
{
    int i;
    printf("+-----------------------------------------------------------+\n\r");
    printf("| Display Time-Domain Data                                  +\n\r");
    printf("+-----------------------------------------------------------+\n\r");
    printf("#Index before reversal    #Index after reversal    #ADC Data \n\r");

    for (i = 0; i < u32Len; i++)
    {
        printf(" %-22d    %-21d    %d\n\r", i, REV(i), pi32Data[REV(i)]);
    }
}

/*----------------------------------------------------------------------------------------------------------*/
/*  Function:    ADC_IRQHandler                                                                             */
/*  Parameters:  None.                                                                                      */
/*  Returns:     None.                                                                                      */
/*  Description: Interrupt service routine for EADC interrupt.                                              */
/*----------------------------------------------------------------------------------------------------------*/
void ADC_IRQHandler(void)
{
    /* clear the A/D conversion flag */
    ADC_CLR_INT_FLAG(ADC, ADC_ADF_INT);

    /* Toggle PB.0 to indicate an ADC data conversion is complete */
    PB0 ^= 1;

    /* Place the time-domain signal into the real part buffer */
    g_i32RealBuffer [REV(g_u32AdcConvNum)] = SHRINK(ADC_GET_CONVERSION_DATA(ADC, g_u8AdcChannel));

    /* The image part buffer is initialized to zeros */
    g_i32ImageBuffer[REV(g_u32AdcConvNum)] = 0;

    /* Increase the buffer index by 1 */
    g_u32AdcConvNum++;
}

/*----------------------------------------------------------------------------------------------------------*/
/*  Function:    main                                                                                       */
/*  Parameters:  None.                                                                                      */
/*  Returns:     None.                                                                                      */
/*  Description: main routine of the example.                                                               */
/*----------------------------------------------------------------------------------------------------------*/
int32_t main(void)
{
    int i;

    /* Unlock protected registers */
    SYS_UnlockReg();

    /* Init System, peripheral clock and multi-function I/O */
    SYS_Init();

    /* Lock protected registers */
    SYS_LockReg();

    /* Init UART0 for printf */
    UART0_Init();

    /* Init TIMER0 to trigger ADC sampling later. */
    TIMER0_Init();

    /* Init ADC channel 2.  */
    ADC_Init();

    printf("\n");
    printf("+-----------------------------------------------------------+\n\r");
    printf("|            Fast Fourier Transform Example Code            |\n\r");
    printf("+-----------------------------------------------------------+\n\n\r");
    printf("# Demonstrate the decimate-in-time(DIT) radix-2 FFT          \n\r");
    printf("# This example code supports 3 kinds of data length:         \n\r");
    printf("    * 128 samples                                            \n\r");
    printf("    * 256 samples                                            \n\r");
    printf("    * 512 samples                                            \n\r");
    printf("# Please confirm the selected length of fft sequence is [%d].\n\r", SEQUENCE_LEN);
    printf("    Otherwise, change the fft sequence length settings and   \n\r");
    printf("    re-compile again to acquire the correct look-up table.   \n\r");
    printf("# Connect PD.2 to the signal source.                         \n\r");
    printf("# Press any key to start ADC capturing....\n\n\r");
    getchar();


    /* Reset the ADC numbers of conversion and Start A/D conversion */
    g_u32AdcConvNum = 0;

    PB0 = 0;
    GPIO_SetMode(PB, BIT0, GPIO_MODE_OUTPUT);

    /* Start capturing data. */
    TIMER_Start(TIMER0);

    while (g_u32AdcConvNum != SEQUENCE_LEN)
    {
        /* Wait for the number of ADC data samples reach SEQUENCE_LEN samples. */
        __NOP();
    }

    /* Stop TIMER trigger EADC */
    TIMER_Stop(TIMER0);

    /* Disable EDC module */
    ADC_Close(ADC);

    /* Disable ADC IP clock */
    CLK_DisableModuleClock(ADC_MODULE);

    /* Disable TIMER0 IP clock */
    CLK_DisableModuleClock(TMR0_MODULE);

    /* Disable External Interrupt */
    NVIC_DisableIRQ(ADC_IRQn);

    Display_AdcData(g_i32RealBuffer, SEQUENCE_LEN);

    /* Remove the DC component from the spectrum by zero-mean the time-domain signal before
       performing FFT. Otherwise, it will remain a high DC component at the 0 Hz point. */
    Remove_DC_Component(g_i32RealBuffer, SEQUENCE_LEN);

    /* Transform the time domain signal into frequency domain */
    NuFFT(g_i32RealBuffer, g_i32ImageBuffer, SEQUENCE_LEN);

    for (i = 0; i < SEQUENCE_LEN ; i++)
    {
        /* The amplitude of the spectrum could be presented as the sum of square of the real part and the complex part. */
        g_i32Spectrum[i] = (((g_i32RealBuffer[i] * g_i32RealBuffer[i]) + (g_i32ImageBuffer[i] * g_i32ImageBuffer[i])));
    }

    Display_Spectrum(g_i32Spectrum, SEQUENCE_LEN, SAMPLE_RATE);

    while (1);
}


使用特权

评论回复
youtome| | 2023-7-8 17:59 | 显示全部楼层
FFT算法要求输入数据长度为2的幂次方。如果输入数据长度不符合要求,需要进行补零或截断操作。

使用特权

评论回复
vivilyly| | 2023-7-8 18:28 | 显示全部楼层
选择适当的数据类型和精度来存储输入数据和计算结果。

使用特权

评论回复
nomomy| | 2023-7-9 10:07 | 显示全部楼层
在实现 FFT 算法时,需要根据具体的应用需求和硬件条件,选择合适的算法实现方式和开发工具

使用特权

评论回复
nomomy| | 2023-7-9 13:40 | 显示全部楼层
可以利用多线程或并行处理器来加速计算过程。

使用特权

评论回复
benjaminka| | 2023-7-9 14:25 | 显示全部楼层
如果采样点数太少,会导致 FFT 算法的效率和准确性降低;如果采样点数太多,会导致 FFT 算法的计算量增加,从而影响算法的效率。

使用特权

评论回复
gygp| | 2023-7-9 15:30 | 显示全部楼层
FFT算法是一种高效的算法,但在具体实现中,要注意优化计算过程以降低时间复杂度。

使用特权

评论回复
yeates333| | 2023-7-9 16:32 | 显示全部楼层
在实现 FFT 算法时,可以通过调整窗口大小来改善算法的效率和准确性。

使用特权

评论回复
bestwell| | 2023-7-10 14:02 | 显示全部楼层
FFT算法在计算过程中可能会引入一定的数值误差。要注意处理舍入误差、截断误差和级联误差等问题

使用特权

评论回复
plsbackup| | 2023-7-10 14:47 | 显示全部楼层
非常耗时的算法,可以采用并行计算的方式来提高算法的效率

使用特权

评论回复
abotomson| | 2023-7-10 15:41 | 显示全部楼层
在应用FFT算法之前,通常需要对输入数据进行预处理。这可能包括零填充、加窗(如汉宁窗)等操作,以减少频谱泄漏和提高频谱分辨率。

使用特权

评论回复
ccook11| | 2023-7-10 16:44 | 显示全部楼层
FFT算法可以通过递归或迭代的方式进行实现。递归实现通常较为简洁,但可能会占用较多的栈空间。

使用特权

评论回复
mmbs| | 2023-7-10 17:23 | 显示全部楼层
FFT是一种高效的算法,用于将时域信号转换为频域信号。了解算法的基本原理和步骤是非常重要的。

使用特权

评论回复
hudi008| | 2023-7-10 18:11 | 显示全部楼层
FFT有多种实现方式,如Cooley-Tukey算法、Radix-2算法等。

使用特权

评论回复
kkzz| | 2023-7-10 18:45 | 显示全部楼层
FFT算法在计算结果中可能存在舍入误差或截断误差。在应用中需要考虑这些误差对结果的影响,并选择适当的精度和误差控制策略。

使用特权

评论回复
plsbackup| | 2023-7-10 19:18 | 显示全部楼层
FFT算法中使用了复数运算,需要确保编程语言或工具支持复数类型。在实现过程中,要注意正确地进行复数的加法、减法、乘法和除法等运算。

使用特权

评论回复
ulystronglll| | 2023-7-10 20:03 | 显示全部楼层
合理选择算法参数和调整算法细节,可以提高计算效率。

使用特权

评论回复
mmbs| | 2023-7-10 20:43 | 显示全部楼层
在实现FFT算法后,进行验证和测试是必要的。

使用特权

评论回复
sdCAD| | 2023-7-10 21:18 | 显示全部楼层
FFT算法函数的实现需要注意性能和精度的平衡,尤其是涉及到浮点数运算时需要注意精度误差。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

147

主题

1479

帖子

2

粉丝