STM32F4单片机ADC采样及ARM-DSP库的FFT

模拟信号经过ADC采样后变成数字信号,数字信号可以进行FFT运算,在频域中更容易分析信号的特征。本文将介绍如何用STM32F4的进行ADC采样,并利用ARMDSP库里的FFT算法对ADC采样值进行快速傅里叶变换。

我使用的是STM32F407VG单片机,由于需要的ADC采样值较多(采集了4096个点),所以配置了STM32的ADC和DMA,使用DMA将ADC采样值传输到内存效率更高。配置外设时,使用APB2时钟的2分频作为ADCCLK,ADC采样时间为84个ADCCLK,Tconv(总转换时间) = 采样时间+12个ADCCLK,理论上ADC的采样频率约4,375,00Hz。为了保证信号的完整性,由奈奎斯特定理知采样频率需要大于信号中最高频率的2倍,因此该外设配置方法测量的信号频率不应大于218,750Hz。外设配置代码如下:

GPIO_InitTypeDef GPIO_InitStructure;
ADC_InitTypeDef ADC_InitStructure;
ADC_CommonInitTypeDef ADC_CommonInitStructure; 
DMA_InitTypeDef DMA_InitStructure;
NVIC_InitTypeDef  NVIC_InitStructure;	

RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2|RCC_AHB1Periph_GPIOC,ENABLE);	
while(DMA_GetCmdStatus(DMA2_Stream0)!=DISABLE);				
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); 	
RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC1,ENABLE);  	
RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC1,DISABLE); 	
//init GPIO ADC1, channel 14
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;	
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;					
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;			
GPIO_Init(GPIOC, &GPIO_InitStructure);
	
DMA_InitStructure.DMA_Channel = DMA_Channel_0;
DMA_InitStructure.DMA_PeripheralBaseAddr = (unsigned int)&(ADC1->DR);
DMA_InitStructure.DMA_Memory0BaseAddr = (unsigned int)&(fft.ADC_ConvertedValue[0]);
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
DMA_InitStructure.DMA_BufferSize = 4096;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
DMA_Init(DMA2_Stream0,&DMA_InitStructure);

NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);

DMA_ClearFlag(DMA2_Stream0,DMA_IT_TC);
DMA_ITConfig(DMA2_Stream0,DMA_IT_TC,ENABLE);//使能数据流传输完成中断
DMA_Cmd(DMA2_Stream0,ENABLE);

ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div2;//84M/2 = 42M
ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_20Cycles;
ADC_CommonInit(&ADC_CommonInitStructure);
	
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfConversion = 1;
ADC_Init(ADC1,&ADC_InitStructure);
ADC_Cmd(ADC1,ENABLE);
	
ADC_RegularChannelConfig(ADC1,ADC_Channel_14,1,ADC_SampleTime_84Cycles);//转换时间84个ADC周期
 
ADC_SoftwareStartConv(ADC1);
ADC_DMARequestAfterLastTransferCmd(ADC1,ENABLE);
ADC_DMACmd(ADC1,ENABLE);

接下来,将ADC的采样值转换为对应的电压值,利用DSP库的FFT算法进行FFT运算,计算幅频特性。这里,我使用的是基4浮点FFT算法,基4的算法比基2的算法运算速度更快。代码如下:

#define FFT_LENGTH 4096
void FFTTestTask(void *arg)
{
    OS_ERR err;
    CPU_TS ts;
    arm_cfft_radix4_instance_f32 scfft;
    int i = 0;
    unsigned char str[10];
	
    arm_cfft_radix4_init_f32(&scfft,FFT_LENGTH,0,1);//FFT初始化
    while(1)
    {
        OSTaskSemPend(0,OS_OPT_PEND_BLOCKING,&ts,&err);//等待传输完成信号量
	for(i=0;i<FFT_LENGTH;i++)
	{
    	    fft.fft_input[2*i] = (float)fft.ADC_ConvertedValue[i]*3.3f/4096.0f;//实部为ADC采样值
	    fft.fft_input[2*i+1] = 0;//虚部为0
	}		
	arm_cfft_radix4_f32(&scfft,fft.fft_input);//FFT运算
	arm_cmplx_mag_f32(fft.fft_input,fft.fft_output,FFT_LENGTH);//计算每个点的模值
	for(i=0;i<FFT_LENGTH;i++)
	{
            sprintf((char*)str,"%.2f\r\n",fft.fft_output[i]);
	    board.UART4Send(str,strlen((char*)str));//将数据打印至串口助手,便于观察
	    OSTimeDly(1,OS_OPT_TIME_DLY,&err);
	}
	OSTimeDly(500,OS_OPT_TIME_DLY,&err);
	board.ADC1_DMA2Enable();//重新启动ADC转换和DMA传输
    }
}

实验中,我用FPGA和DAC做了DDS信号发生器,生成波形函数为f= 1.65+1.65\cdot cos(2\cdot \pi \cdot 1937\cdot t)。用上述方法采集电压信号进行FFT运算,最后使用MATLAB绘制了部分幅频特性曲线。直流分量的理论幅值为6758.4,实际值7168.2;基波分量的理论幅值为3379.2,实际值3454.3。

不足之处请大佬指正。

发布了8 篇原创文章 · 获赞 18 · 访问量 5231

猜你喜欢

转载自blog.csdn.net/QDchenxr/article/details/97624652