DAC、ADC、FFT使用总结
目錄
- 計(jì)算公式
- 波形生成
- DAC波形頻率
- ADC采樣時(shí)間
- 離散傅里葉變換DFT
- FFT
計(jì)算公式
DAC、ADC、FFT之間有些參數(shù)環(huán)環(huán)相扣,所以先整合一下公式。
1.系統(tǒng)時(shí)鐘周期72MHZ。
2.定時(shí)器的單個(gè)時(shí)鐘周期。
3.定時(shí)器的觸發(fā)周期。
4.正弦波一個(gè)周期的時(shí)間,其中N為正弦波一個(gè)周期的點(diǎn)數(shù)。
5.正弦波的頻率。
6.adc的采樣頻率,其中n為一個(gè)波形周期的采樣點(diǎn),fsin為被采樣的波形的頻率。
7.為設(shè)置adc的采樣頻率,要根據(jù)這個(gè)公式去配置觸發(fā)adc的定時(shí)器。
8.進(jìn)行傅里葉變換后,fft輸出數(shù)組下標(biāo)對(duì)應(yīng)的頻率。其中i為數(shù)組下標(biāo),fadc為adc的采樣頻率,fftnum為fft計(jì)算的點(diǎn)數(shù)。
波形生成
x取值范圍[0,2π]
y=sin(x)取值范圍[-1,1]
y=six(x)+1取值范圍[0,2]
DAC輸出電壓范圍[0,Vmax]
將y取值范圍擴(kuò)大到DAC輸出電壓范圍,只需y=((six(x)+1)/2)*Vmax
周期2π,波形一個(gè)周期的點(diǎn)數(shù)為N,兩點(diǎn)間距2π/N
下面這個(gè)代碼,增加了一個(gè)DAClength為的是與DAC的DMA=normal配合,使DAC輸出幾個(gè)周期波形后,停止輸出,滿足一些特定電路的需求(有的電路起始階段需要幾個(gè)波形之后就停止在一個(gè)高電位,并不需持續(xù)輸出波形)。
如果僅生成一個(gè)波形周期,那么可以無(wú)視DAClength參數(shù)和下面的復(fù)制周期。
/*** 生成正弦波數(shù)據(jù)點(diǎn)函數(shù)* @param NPoints 一個(gè)周期內(nèi)的點(diǎn)數(shù)* @param DAClength 目的輸出點(diǎn)數(shù)總數(shù),DAClength為NPoints整數(shù)倍* @param VMaxRange 輸出的電壓最大值,取值范圍0~3.3V* @param SineWaveTable 存放生成的數(shù)據(jù)點(diǎn)*/ void SineWaveGen(uint32_t NPoints, uint32_t DAClength, float VMaxRange, uint16_t* SineWaveTable) { #ifndef PI #define PI 3.14159265358979323846 #endifint i = 0;int j = 0; int k = DAClength/NPoints; //增加波形后的周期數(shù)double radian = 0; // 弧度double setup = 0; // 弧度和弧度之間的大小double voltage = 0; // 輸出電壓setup = (2 * PI) / NPoints; // 兩點(diǎn)之間的間距while (i < NPoints){voltage = VMaxRange / 2.0 * (sin(radian) + 1.0); // 計(jì)算電壓//printf("%d %lf\r\n",i,voltage);SineWaveTable[i] = (uint16_t)(voltage * 4095 / 3.3); // 電壓轉(zhuǎn)為DAC數(shù)值//printf("%d %d\r\n",i,SineWaveTable[i]);radian += setup; // 下一個(gè)點(diǎn)的弧度i++;}for(j=1; j<k;++j)//復(fù)制k-1個(gè)周期{for(i=0;i<NPoints;++i){SineWaveTable[NPoints*j+i]=SineWaveTable[i];//printf("%d %d\r\n",NPoints*j+i,SineWaveTable[NPoints*j+i]);}}}DAC波形頻率
設(shè)置波形一個(gè)周期的點(diǎn)數(shù),會(huì)影響DAC輸出波形頻率。
如果波形一個(gè)周期128個(gè)點(diǎn),10k頻率。不改變定時(shí)器設(shè)置的話,波形一個(gè)周期256個(gè)點(diǎn),輸出波形頻率就變成了5k。
如果按下面配置定時(shí)器觸發(fā)DAC,觸發(fā)頻率為72M/141=510638HZ。
波形一個(gè)周期點(diǎn)數(shù)為128,那么波形頻率為510638HZ/128=3,989HZ,約為4k。
如果編寫如下代碼,波形一個(gè)周期點(diǎn)數(shù)為128,但是復(fù)制了九個(gè)周期的波形到dac數(shù)組里。
并且設(shè)置DMA的數(shù)據(jù)傳輸數(shù)量(0至65535)為128*9,最終得到的波形仍然是4k。
如果配置DMA Mode為Normal的話,那么可以發(fā)現(xiàn)觸發(fā)一次DAC,輸出了九個(gè)周期的頻率為4k的波形。
#define POINTS 128 #define DAC_length 1152 uint16_t SineWaveTable[DAC_length]; SineWaveGen(POINTS,DAC_length, 2, SineWaveTable);//points擴(kuò)展到daclength HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1, (uint32_t *)SineWaveTable, DAC_length, DAC_ALIGN_12B_R);ADC采樣時(shí)間
ADC使用若干個(gè)ADC_CLK周期對(duì)輸入電壓采樣,采樣周期數(shù)目可以通過(guò)ADC_SMPR1和ADC_SMPR2寄存器中的SMP[2:0]位更改。每個(gè)通道可以分別用不同的時(shí)間采樣 。
總轉(zhuǎn)換時(shí)間 :TCONV = 采樣時(shí)間+ 12.5個(gè)周期
設(shè)置ADC輸入時(shí)鐘為12Mhz,那么1個(gè)ADC周期占用的時(shí)間=1 / 12MHZ = 0.0833334 uS
如果設(shè)置采樣時(shí)間為1.5個(gè)周期,那么一次采樣總的時(shí)間 = 采樣時(shí)間 + 12.5個(gè)周期 = 1.5周期 + 12.5周期 = 14周期 = 14 * 0.0833334 = 1.166667 uS
兩次采樣間隔時(shí)間 = 1.166667 uS (ADCCLK為12MHZ時(shí)的最小采樣間隔時(shí)間)
下面是ADC轉(zhuǎn)換的時(shí)序圖,可知,定時(shí)器觸發(fā)adc轉(zhuǎn)換的時(shí)間間隔需要大于adc兩次采樣間隔時(shí)間。
如果按照下面這樣配置觸發(fā)adc的定時(shí)器,72M/141=510638HZ
轉(zhuǎn)換成時(shí)間就是1.95us,大于adc兩次采樣間隔時(shí)間,所以ok。
離散傅里葉變換DFT
一開(kāi)始其實(shí)是打算手寫一個(gè)DFT,但是實(shí)際用的時(shí)候無(wú)法滿足單片機(jī)性能需求。單片機(jī)里面的算法,最好只有加減乘,不能有除。
離散傅里葉變換:
N為時(shí)域離散信號(hào)的點(diǎn)數(shù),n為時(shí)域離散信號(hào)的編號(hào)(取值范圍為0 ~ N-1),m為頻域信號(hào)的編號(hào)(取值范圍為0 ~ N-1),頻域信號(hào)的點(diǎn)數(shù)也為N。
離散傅里葉變換的輸入為N個(gè)離散的點(diǎn)(時(shí)域信號(hào)),輸出為N個(gè)離散的點(diǎn)(頻域信號(hào),頻域信號(hào)的每個(gè)點(diǎn)都用一個(gè)復(fù)數(shù)表示)。
那么可以根據(jù)以上公式寫一個(gè)dft:
#include<iostream> #include<cmath> using namespace std; double a[128] = {2.028, 2.038, 2.041, 2.032, 2.020, 2.026, 2.070, 2.070, 2.060, 2.061, 2.042, 2.037, 2.072, 2.080, 2.064, 2.063, 2.037, 2.032, 2.060, 2.051, 2.038, 2.034, 2.003, 1.990, 2.007, 2.005, 1.988, 1.978, 1.942, 1.936, 1.951, 1.940, 1.914, 1.902, 1.862, 1.850, 1.858, 1.852, 1.822, 1.810, 1.760, 1.755, 1.767, 1.751, 1.725, 1.716, 1.659, 1.648, 1.659, 1.652, 1.625, 1.607, 1.560, 1.544, 1.566, 1.550, 1.525, 1.510, 1.469, 1.454, 1.481, 1.469, 1.440, 1.434, 1.389, 1.380, 1.406, 1.401, 1.383, 1.377, 1.337, 1.333, 1.365, 1.360, 1.345, 1.339, 1.309, 1.310, 1.345, 1.348, 1.342, 1.347, 1.316, 1.322, 1.359, 1.358, 1.352, 1.355, 1.338, 1.347, 1.393, 1.397, 1.395, 1.406, 1.388, 1.403, 1.452, 1.459, 1.464, 1.474, 1.460, 1.479, 1.522, 1.535, 1.547, 1.561, 1.545, 1.562, 1.611, 1.625, 1.633, 1.651, 1.640, 1.659, 1.707, 1.718, 1.728, 1.743, 1.727, 1.738, 1.792, 1.803, 1.798, 1.818, 1.807, 1.816, 1.865, 1.873,}; double b[128]; const double PI = acos(-1.0); //定義一個(gè)結(jié)構(gòu)體來(lái)描述一個(gè)復(fù)數(shù) typedef struct {float re;// reallyfloat im;// imaginary } complex,*pcomplex;//構(gòu)建并初始化一個(gè)復(fù)數(shù)結(jié)構(gòu)體 complex complexBuild(float re,float im) {complex cx;cx.re=re;cx.im=im;return cx; }//復(fù)數(shù)加法 complex complexAdd(complex a,complex b) {complex ret;ret.re=a.re+b.re;ret.im=a.im+b.im;return ret; } //復(fù)數(shù)乘法 complex complexMult(complex a,complex b) {complex ret;ret.im=a.im*b.re+a.re*b.im; ret.re=a.re*b.re-a.im*b.im;return ret; }void DFT(complex x[],complex X[],int N) {int k,n;complex Wnk;for (k=0; k<N; k++) {X[k].re=0;X[k].im=0;for (n=0; n<N; n++) {//帶公式 Wnk.re=(float)cos(2*PI*k*n/N);Wnk.im=(float)-sin(2*PI*k*n/N);X[k]=complexAdd(X[k],complexMult(x[n],Wnk));}} }int main() {complex samples[128],_out[128];double _out2[128];int i;for(int i=0; i<128; ++i) {b[i]=a[i];samples[i].re=b[i];samples[i].im=0;printf("%.3f\n",b[i]);//printf("%.3f\n",b[i]);} // for (i=0; i<120; i++) { // samples[i].re=i; // samples[i].im=0; // }DFT(samples,_out,128);//求幅值 for(i=0;i<128;++i){_out2[i]=sqrt(_out[i].re*_out[i].re+_out[i].im*_out[i].im);} // for (i=0; i<120; i++) { // if(i==0) // printf("(%f,%f)\n",_out[i].re/128,_out[i].im); // else // printf("(%f,%f)\n",_out[i].re/64,_out[i].im); // }//數(shù)據(jù)處理 for (i=0; i<128; i++) {if(i==0)printf("%f\n",_out2[i]/128);elseprintf("%f\n",_out2[i]/64);} } /* int main() {//memset(b,0,sizeof(b));for(int i=0; i<120; ++i) {b[i]=a[i]/4096.0*3.3;printf("%.3f\n",b[i]);}} */由dft變換后的幅度可以看出波形的一些特征。比如直流偏置為1.674602。
fi是,進(jìn)行傅里葉變換后,fft輸出數(shù)組下標(biāo)對(duì)應(yīng)的頻率。其中i為數(shù)組下標(biāo),fadc為adc的采樣頻率,fftnum為fft計(jì)算的點(diǎn)數(shù)。
按照之前的設(shè)置,adc的采樣頻率為510638HZ,fft計(jì)算的點(diǎn)數(shù)為128,那么
i=1時(shí),f1=510638HZ/128=3989HZ。約等于4k,剛好對(duì)應(yīng)上dac輸出的正弦波的頻率。
FFT
最后選擇使用dsp庫(kù)里面的fft進(jìn)行傅里葉變換。
下面是一部分核心代碼。直接做1024個(gè)點(diǎn)的fft。
#include "arm_math.h" #include "arm_const_structs.h" #define FFT_LENGTH 1024 float fft_inputbuf[FFT_LENGTH * 2]; float fft_outputbuf[FFT_LENGTH]; //部分代碼:for (int i = 0; i < FFT_LENGTH; i++){fft_inputbuf[i * 2] = adc1_buff[i] * 3.0 / 4095;//實(shí)部賦值,* 3 / 4096是為了將ADC采集到的值轉(zhuǎn)換成實(shí)際電壓//printf("%.4f\r\n",fft_inputbuf[i * 2]);fft_inputbuf[i * 2 + 1] = 0;//虛部賦值,固定為0.}arm_cfft_f32(&arm_cfft_sR_f32_len1024, fft_inputbuf, 0, 1);arm_cmplx_mag_f32(fft_inputbuf, fft_outputbuf, FFT_LENGTH); /*處理變換結(jié)果*/fft_outputbuf[0] /= FFT_LENGTH;for (int i = 1; i < FFT_LENGTH; i++)//輸出各次諧波幅值{fft_outputbuf[i] /= FFT_LENGTH/2;}/*打印結(jié)果*/printf("FFT Result:\r\n");for (int i = 0; i < FFT_LENGTH; i++)//輸出各次諧波幅值{printf("%d:\t%.4f\r\n", i, fft_outputbuf[i]);}但是實(shí)際操作過(guò)程中,模擬電路設(shè)計(jì)的有問(wèn)題,導(dǎo)致adc讀取數(shù)據(jù)在一定范圍向上偏斜。
所以改變思路,做128個(gè)點(diǎn)的fft,去掉最大最小取平均。
adc讀取數(shù)據(jù)一直向上偏,是電路本身存在問(wèn)題,如果用算法去抵消這個(gè)影響,其實(shí)并沒(méi)有解決本質(zhì)問(wèn)題。
int bnum = FFT_LENGTH / POINTS;//倍數(shù)int inum = 1;//4k對(duì)應(yīng)fft128下標(biāo)float resultzhi[bnum];//直流float resultfen[bnum];//分流for(int j=0;j<bnum;++j){for(int i=0;i<POINTS;++i){fft_inputpoint[i*2] = adc1_buff[j*POINTS+i] * 3.0 / 4095;fft_inputpoint[i*2+1] = 0;}arm_cfft_f32(&arm_cfft_sR_f32_len128, fft_inputpoint, 0, 1);arm_cmplx_mag_f32(fft_inputpoint, fft_outputpoint, POINTS); resultzhi[j]=fft_outputpoint[0]/POINTS;resultfen[j]=fft_outputpoint[inum]/(POINTS/2);//fft_outputpoint[inum]/=POINTS/2;//resultfen[j]=fft_outputpoint[inum];}float zhi=0,fen=0;for(int i=1;i<bnum-1;++i)//一定是首位最值{zhi+=resultzhi[i];fen+=resultfen[i];}zhi=zhi/(bnum-2);fen=fen/(bnum-2);printf("%.4f\r\n",zhi);printf("%.4f\r\n",fen);后面經(jīng)過(guò)一系列測(cè)試,得到一系列非線性的公式,無(wú)論選用何種擬合手段,都無(wú)法滿足精度需求,所以,只能繼續(xù)修改模擬電路。
由此,感悟就是,測(cè)量得到兩個(gè)值之間不是線性關(guān)系。此時(shí)很多人會(huì)從算法層面切入。
但是還有一種方式就是從硬件層面切入,使用合適的電路,讓兩個(gè)值之間是線性關(guān)系。
總結(jié)
以上是生活随笔為你收集整理的DAC、ADC、FFT使用总结的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: [css] 请举例说明伪元素 (pse
- 下一篇: [html] 页面导入样式时,使用li