我正在尝试使用PIC32MZ2064DAB176可用的FFT库来导出音频信号的频率。
我正在使用MPLAB Harmony进行配置。
为了进行测试,使用了两个频率为1002 Hz和750 Hz的正弦波。这是借助在线音调生成器工具完成的。我在一个浏览器窗口上有1002 Hz,在另一个浏览器窗口上有750 Hz。直流偏置后,音频O / P插孔的输出将馈送到微控制器ADC。
执行1.6 V的DC偏置后,信号被发送到12位ADC。我期望的最大电压是3 V P-P,所以我猜DC偏置为1.6 V就足够了。
由于我需要读取高达20 kHz的频率,因此信号以48 kHz采样。
FFT是1024点FFT。
我能够在频点的第0个索引中获得DC值。
用于从垃圾箱获取频率值的公式为 频率=索引*采样频率/ FFT点数
但是,对于任何输入频率值,我总是在第一和第二频率仓中得到很高的幅值。根据我的理解,对于1002 Hz,振幅应在频率仓的第21个索引附近较高;对于750 Hz信号,该振幅应在第16个索引附近较高。
我附上我的代码,ADC Harmony配置屏幕截图,结果屏幕截图和信号输入屏幕截图。
在代码中,用于频点的数组是“ singleSidedFFT”
对于获得正确频率值的任何帮助,我们深表感谢。
/* FFT */
#define N 1024// Also change the log2N variable below!!
#define SAMPLE_FREQ 48000
#define PI 3.14
// Section: Global Data Definitions
APP_DATA appData;
/* ADC */
long count = 0;
/* FFT */
int16c fftCoefs[N];
int16c *fftc;
int log2N = 10;
extern const int16c twiddleFactors[];
long int freqVector[N];
int16c sampleBuffer[N]; //initialize buffer to collect samples
long int singleSidedFFT[N];
void APP_Tasks ( void )
{
/* Check the application's current state. */
switch ( appData.state )
{
/* Application's initial state. */
case APP_STATE_INIT:
{
bool appInitialized = true;
if (appInitialized)
{
int i;
fftc = &fftCoefs; /* Stores the twiddle factors */
// zero the freqVector and singleSidedFFT
for (i=0; i<N; i++)
{
freqVector = 0;
singleSidedFFT = 0;
sampleBuffer.re = 0;
}
// generate frequency vector this is the x-axis of your single sided fft
for (i=0; i<N; i++)
{
freqVector = i*(SAMPLE_FREQ/2)/((N/2) - 1);
}
/* Calculate the twiddle factors */
DSP_TransformFFT16_setup(fftc, log2N);
appData.state = APP_STATE_SERVICE_TASKS;
}
break;
}
case APP_STATE_SERVICE_TASKS:
{
/* Trigger a conversion */
ADCCON3bits.GSWTRG = 1;
/* Wait the conversions to complete */
while (ADCDSTAT1bits.ARDY2 == 0);
if (count < N)
{
sampleBuffer[count].re = ADCDATA2; /* fetch the result */
sampleBuffer[count].im = 0;
count++;
}
else
{
appData.state = APP_STATE_COMPUTE_FREQ;
count = 0;
}
break;
}
case APP_STATE_COMPUTE_FREQ:
{
APP_ComputeFreq();
appData.state = APP_STATE_SERVICE_TASKS;
break;
}
}
}
void APP_ComputeFreq(void)
{
int i;
int16c dout[N]; //holds computed FFT
int16c scratch[N];
// load complex input data into din
DSP_TransformFFT16(dout, sampleBuffer, fftc, scratch, log2N);
// compute single sided fft
for(i = 0; i < N/2; i++)
{
singleSidedFFT = sqrt((dout.re*dout.re) + (dout.im*dout.im));
}
LATAbits.LATA6 = ~LATAbits.LATA6;
}
我也尝试编写独立的FFT函数。结果是一样的。 在这里。
void APP_ComputeFreq_2(void)
{
int16_t k, t;
for (k = 0; k < N; k++)
{
// For each output element
int16_t sumreal = 0;
int16_t sumimag = 0;
for (t = 0; t < N; t++)
{
// For each input element
double angle = 2 * M_PI * t * k / N;
sumreal += sampleBuffer[t].re * cos(angle) + sampleBuffer[t].im * sin(angle);
sumimag += -sampleBuffer[t].re * sin(angle) + sampleBuffer[t].im * cos(angle);
}
singleSidedFFT[k] = sqrt((sumreal * sumreal) + (sumimag * sumimag));
}
}
非常感谢。
答案 0 :(得分:1)
Ted发现microcontroller PIC32MZ Graphics (DA) Family的数据表与12 bit Successive Approximation Register (SAR) Analog-to-Digital Converter (ADC)
的规范的版本B 之间存在不一致在两种情况下,驱动ADC采样率的时钟源均由寄存器ADCSEL<1:0>
的位ADCCON3
控制。
数据表,第452页,提供了以下时钟源:
11 = FRC
10 = REFCLK3
01 = System Clock (Tcy)
00 = PBCLK3
相反,the specifications of the ADC, in version B, on page 14是:
11 = System Clock (TCY)
10 = REFCLK3
01 = FRC Oscillator output
00 = Peripheral bus clock (PBCLK)
At the same point, version D of the specifications指出:
有关ADC时钟源的选择,请参见具体器件数据手册中的“ 12位高速逐次逼近寄存器(SAR)”一章。
MPLAB Harmony ADC配置器符合此配置。不过,采用版本B的时钟设置解决了采样问题,这表明家庭数据表不正确。
采样率还可能受以下因素影响:
CONCLKDIV<5:0> of
ADCCON3`:控制时钟分频器ADCDIV<6:0>
中的ADCxTIME
:附加分频器,用于定义单个ADC的时钟源。或者,ADCDIV<6:0> of
ADCCON2`用于共享ADC。ADCxTIME<9:0>
或ADCCON2<25:16>
:时钟滴答数。 由于采样比预期的要高得多(625 kHz对48kHz),因此帧的长度(1024个采样= 0.0016s)与输入信号的周期(约1kHz)相当。因此,大部分振幅都存储在DFT的第一个仓中,并且应用窗口并不能解决问题。
一旦校正了采样率,DFT将具有对应于频率或输入信号的最大值。通过应用窗口和estimating the frequency of a peak as its mean frequency wih respect to power density
可以准确识别这些频率