
簡介面向 STM32F407 學習與開發者以定時器觸發 ADC 采樣用 DMA 搬運數據后執行 FFT并通過串口輸出頻譜結果幫助讀者走通從采樣率配置到頻域分析的完整流程。資源包共 187 個文件約 4.85MB核心由 58 個 H 頭文件與 47 個 C 源碼文件組成另含工程構建配置、鏈接腳本、編譯輸出和調試文件方便直接了解 ADC、DMA、定時器及 DSP 庫的協作關系。項目支持 512KHZ、256KHZ、128KHZ 三檔采樣頻率也可修改采樣點數與頻率適合開展采樣定理驗證和頻譜分辨率觀察。已有 5782 人學習特別適合嵌入式與信號處理入門者、備賽學生以及需要搭建采集-FFT-串口輸出實驗平臺的技術人員。拿到后可打開工程查看初始化、中斷配置與數據處理鏈路參考結構與關鍵代碼快速移植到其他 STM32 項目中并擴展顯示、存儲或上位機分析功能。 做STM32F407上的ADC采集DMA搬運FFT頻譜分析這個組合聽起來像是教科書里平平無奇的一章但真正在項目里落地時我第一版代碼是拿定時器觸發ADC、在中斷里讀數據、攢夠1024個點再調用FFT結果頻譜圖出來差點讓我把板子扔了一個干干凈凈的1kHz正弦波譜線旁邊全是毛刺和底噪主峰旁邊的旁瓣拖得很長而且每次采出來的結果都在輕微漂移。折騰了好幾天才明白問題基本不在FFT算法本身而是在ADC采樣的“時間質量”和“數據搬運方式”上。這篇就專門聊透STM32F407的ADC、DMA、FFT三者如何正確配合以及每一個環節里那些不寫進參考手冊的坑。1. 中斷采樣喂給FFT為什么頻譜會不忍直視1.1 采樣點的時間間隔被中斷延遲污染了先說一個容易被忽略的事實FFT的數學前提是輸入序列滿足“等時間間隔采樣”。這就像你拿節拍器給樂手打拍子節拍器必須每0.5秒敲一下不能有時候隔0.45秒、有時候隔0.62秒。ADC中斷采樣方式下的真實情況是每次轉換完成ADC硬件置EOC標志觸發中斷CPU響應中斷現場壓棧讀取DR寄存器存進數組退出中斷。這一整套流程里只要有一個更高優先級的中斷插進來或者Flash讀指令時遇到總線等待下一次采樣的觸發就會被延后。在STM32F407跑到168MHz主頻時一次中斷響應加數據搬遷本身只需要幾百納秒但系統里往往不止ADC一個中斷源。我當時的工程里還掛著串口接收、定時器更新、按鍵掃描等中斷FFT采樣周期部分被這些中斷搶占后相鄰采樣點的時間間隔實際上是一個不均勻序列。FFT對這種非均勻采樣非常敏感直觀表現就是頻譜底噪整體抬高、主峰旁邊出現不規則的雜散分量而且看起來像是信號本身不干凈實際上是時間軸“臟”了。測量一下就能驗證把輸入短接到GND理論上FFT結果應該是全頻段接近0但中斷采樣方式下這段“0信號”的頻譜也會有一層噪聲底座原因就是采樣點在時間上的抖動被轉化成了幅度上的隨機誤差。1.2 CPU被搬運任務拖死算力全浪費在搬數據上還有一個繞不開的問題中斷方式下ADC每采樣一個點CPU就要被中斷打斷一次。假設采樣率是100kHz那么1秒鐘就有10萬次中斷。每次中斷就算只占用幾百個周期累計起來也是一筆不小的開銷。最難受的是這些開銷全花在“把數據從外設寄存器搬到內存數組”這種毫無技術含量的重復勞動上。而FFT本身尤其是1024點、4096點這種規模CMSIS-DSP庫在F407上雖然只要幾百微秒但如果CPU一邊忙著搬數據、一邊響應其他中斷FFT運算可能被反復打斷不僅總耗時變長實時性也難以保證。更不要說產品里往往還要同時跑顯示刷新、通信協議、按鍵邏輯中斷采樣方案在這種場景下基本撐不住。用DMA替代中斷本質上是把“搬運工”的活交給專門的硬件通道去做。CPU只需要在緩沖區拿到一批數據后批量處理。這就是DMA方案在ADCFFT場景里成為默認選擇的核心原因。1.3 數據形態也對不上ADC寄存器里是整數FFT要吃浮點序列這一點很少被新手注意到但排查起來非常隱蔽。ADC的DR寄存器是12位右對齊的uint16_t類型而CMSIS-DSP的浮點FFT函數輸入是float32_t數組。有人圖省事直接把uint16_t數組強轉成float指針傳給FFT函數結果數據解釋完全錯亂。因為float在內存里的IEEE754格式和uint16_t的整數布局完全不是一回事。正確的做法是在DMA搬完數據后把每個uint16_t的ADC原始值先轉換為float并按需要線性映射到0.0~3.3V按電壓算或0.0~1.0按滿量程歸一化。這一步看起來簡單但它決定了后續所有幅值計算的物理含義我見過好幾個項目卡在“FFT結果數量級完全不對”上最后發現是這一步的類型映射寫錯了。所以在整個數據通路里明確區分“ADC原始值”和“FFT輸入序列”是很重要的一件事。2. 把DMA加進來數據流架構與Buffer設計里最容易犯的錯2.1 ADC、DMA、內存三者的分工先梳理一下整體數據流HAL庫的HAL_ADC_Start_DMA函數啟動ADC轉換同時啟動DMA搬運。ADC每完成一次轉換產生一個DMA請求DMA控制器將這個轉換結果從ADC的數據寄存器搬到內存Buffer中全程不經過CPU。當搬運次數達到設定的Buffer長度時DMA觸發傳輸完成中斷這時CPU才介入把Buffer里的數據取走做FFT處理。如果配置為單次模式DMA搬完設定長度后會自動停止如果配置為循環模式DMA會“原地轉圈”Buffer被不斷刷新。FFT應用里通常使用循環模式這樣采集是無限連續的CPU在任何時刻去讀Buffer拿到的都是最近一段時間的數據。這就像一條自動傳送帶不斷把工件送到倉庫倉庫管理員只需要在傳送帶堆滿一批時過去取走不需要每送一個工件就跑一趟。值得注意的是ADC連續轉換模式加上DMA循環模式會讓Buffer里的數據一直在被DMA刷新。CPU讀取Buffer做FFT時如果正趕上DMA寫入同一塊緩沖區就會讀到一半是舊數據、一半是新數據FFT結果自然不對。這就引出了雙緩沖和乒乓操作的必要性稍后細說。2.2 Buffer長度到底是“點數”還是“點數×通道數”這里最容易想歪很多人初次配置時以為“我要做1024點FFT那DMA搬運次數就設成1024”。這是在單通道場景下的正確思路。但如果開了ADC掃描模式比如同時采樣兩路信號DMA搬運回來的數據是按通道順序交替排列的ch0的第一個點、ch1的第一個點、ch0的第二個點、ch1的第二個點……這種情況下DMA傳輸次數1024只代表了“兩個通道各采了512個點”拿這1024個數據硬塞給FFT兩個通道的波形混在一起頻譜完全是亂的。正確做法是先把需求想清楚如果你需要每個通道都做1024點FFT并且使用掃描模式DMA的傳輸長度應該設置成FFT點數乘以通道數也就是1024×22048。然后在DMA中斷回調里按通道號進行取模抽取偏移量為偶數的數據屬于ch0偏移量為奇數的數據屬于ch1。抽取出來的數據再分別裝入兩個float數組做FFT。我之前寫過一段提取邏輯大致套路如下#define FFT_POINTS 1024 #define ADC_CH_NUM 2 uint16_t adc_dma_buf[FFT_POINTS * ADC_CH_NUM]; float fft_input_ch0[FFT_POINTS]; float fft_input_ch1[FFT_POINTS]; for (uint16_t i 0; i FFT_POINTS; i) { fft_input_ch0[i] (float)adc_dma_buf[i * ADC_CH_NUM 0]; fft_input_ch1[i] (float)adc_dma_buf[i * ADC_CH_NUM 1]; }這段代碼雖然簡單但它把“物理通道”和“FFT譜線”正確對應起來了比一頭扎進FFT函數里調參數更重要。2.3 雙緩沖與半傳輸中斷采集和運算的相位錯開我剛才提到循環DMA模式會讓數據不斷刷新CPU在任意時刻讀取Buffer都可能讀到剛好被改寫一半的數據。解決這個問題最經典的方案不是“加鎖”而是利用DMA的半傳輸中斷Half Transfer和傳輸完成中斷Transfer Complete實現乒乓操作。具體機制是DMA緩沖區被分成前后兩半前半段寫滿時觸發半傳輸中斷此時CPU處理前半段數據在這段時間里DMA繼續往后半段搬運數據等后半段也寫滿了DMA從頭重新開始填充前半段同時觸發傳輸完成中斷CPU再去處理后半段數據。整個過程里CPU處理的永遠是“上一次已經填滿、且DMA暫時不會再寫”的那一半不會出現讀寫競爭。在STM32 HAL庫中對應的回調函數是HAL_ADC_ConvHalfCpltCallback和HAL_ADC_ConvCpltCallback。我在實際項目里用標志位通知主循環處理不在中斷里直接跑FFT避免長時間占用中斷上下文。volatile uint8_t fft_ready 0; volatile uint8_t half_flag 0; void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef *hadc) { (void)hadc; half_flag 1; } void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc) { (void)hadc; half_flag 2; }主循環里判斷half_flag為1處理前半段為2處理后半段處理完清標志。這個結構保證了FFT輸入序列的完整性是長時間穩定運行的關鍵否則跑半小時后FFT結果就會莫名跳變。3. CubeMX配置里的三個“埋雷點”搬一次就停、數值亂跳、數組沒對齊3.1 DMA模式選Normal還是Circular決定數據是“搬一次”還是“持續搬”CubeMX里配置ADC的DMA時大多數人會隨手選一個Normal模式。Normal模式的含義是DMA傳輸完設定長度后傳輸自動停止通道關閉。這時ADC雖然還在繼續轉換但轉換結果沒人搬了Buffer里的數據永遠停留在第一次傳輸完的狀態。現象就是串口打印第一次數據看起來正常之后打印出來的永遠是同一批數據。解決方法是把DMA模式改成Circular。Circular模式下DMA傳輸計數遞減到0后自動重裝初始值繼續下一次傳輸形成無限循環。這樣只要ADC不停轉換DMA就不停搬運CPU隨時能拿到一份“剛剛更新過”的緩沖區數據。判斷是不是這個問題的快速方法在調試器里看DMA的NDTR寄存器。如果它停在一個固定值不動而ADC狀態寄存器還在繼續轉換基本就是Normal模式沒跑了。改完模式后重啟工程NDTR應該來回滾動數據也會持續刷新。3.2 采樣時間別用最小檔源阻抗會讓ADC讀數“飄”STM32F407的ADC是逐次逼近型SAR結構內部有一個采樣保持電容。在采樣階段ADC引腳通過內部模擬開關給這個電容充電。如果開關閉合時間太短而信號源的輸出阻抗又比較高比如前級是幾十kΩ的電阻分壓網絡電容還沒來得及充滿開關就斷開了轉換結果自然偏低而且會隨著溫度、電源電壓輕微變化而隨機跳動。F407的ADC采樣時間可以從3個周期一路調到480個周期。CubeMX里默認配置經常是3個周期這在信號源內阻很低比如運放直接驅動時沒問題但如果前端是幾十kΩ的分壓電阻3個周期的采樣時間遠遠不夠。我個人的經驗是ADC時鐘21MHz時采樣時間至少取28個周期以上比較穩妥信號源阻抗較高時甚至要拉到144個周期。當然采樣時間變長會直接降低等效采樣率具體數值可以按下面這個關系估算采樣時間設置ADC時鐘21MHz下單個轉換周期理論上限采樣率約3周期15.5周期1.35Msps15周期27.5周期764ksps28周期40.5周期518ksps144周期156.5周期134ksps480周期492.5周期42.6ksps如果只是做音頻頻段的FFT分析幾十kHz到一兩百kHz的等效采樣率完全夠用與其冒險用短采樣時間去追采樣率不如把穩定性放在第一位。3.3 數據寬度和內存對齊CMSIS-DSP函數有硬性要求ADC的DR寄存器有效位數是16位右對齊所以DMA搬運的數據寬度必須配置為Half Word半字16位。如果配置成Word32位或者Byte8位搬回來的數據就會錯位Word會把相鄰寄存器的內容一起讀進來Byte會丟失高8位。這還只是第一步。真正隱藏的坑在FFT輸入數組的定義上。CMSIS-DSP的arm_rfft_fast_f32等函數內部會使用SIMD指令和雙字加載要求傳入的float數組必須是32位對齊的內存地址。如果你在棧上定義一個普通局部數組編譯器默認對齊可能只有4字節甚至更差調用FFT函數時輕則性能下降重則直接進入HardFault。解決辦法是定義數組時加上對齊屬性比如在GCC/Keil環境下__ALIGNED(4) float fft_input[FFT_POINTS]; __ALIGNED(4) float fft_output[FFT_POINTS];CubeMX生成的代碼里如果使用DMA通常會自動把DMA緩沖區設置為32位對齊但自己另外定義的FFT輸入輸出數組很容易漏掉這一點。還有另一個細節CMSIS庫的實數FFT函數要求輸入輸出數組必須互不重疊否則會破壞內部狀態。4. 從ADC原始值到頻點幅值CMSIS-DSP的正確姿勢與幅度還原4.1 arm_rfft_fast_f32的輸入輸出布局別拿Matlab習慣來套ADC采樣得到的是一串實數序列。實數序列的FFT結果具有共軛對稱性所以CMSIS-DSP專門提供了arm_rfft_fast_f32來處理這種情況它比通用的復數FFT函數省了將近一半的內存和計算量。但它的輸出布局非常容易讓人看錯函數并不會直接輸出你想象的N個“頻率點幅值”而是輸出N個float在第0個和第N/2個位置放DC分量和奈奎斯特頻率分量的實數中間位置交替存放正頻率分量的實部和虛部。參考代碼結構如下arm_rfft_fast_instance_f32 fft_inst; arm_rfft_fast_init_f32(fft_inst, FFT_POINTS); float fft_input[FFT_POINTS]; // 時域輸入由ADC原始值轉換而來 float fft_output[FFT_POINTS]; // 頻域輸出布局為實虛交替 arm_rfft_fast_f32(fft_inst, fft_input, fft_output, 0); // 手動計算幅度0點直流除外 float mag_db; for (uint16_t k 0; k FFT_POINTS / 2; k) { float re, im; if (k 0 || k FFT_POINTS / 2) { re fft_output[k]; im 0.0f; } else { re fft_output[2 * k]; im fft_output[2 * k 1]; } mag_half[k] sqrtf(re * re im * im); }手寫遍歷取模其實也不復雜而且能順便把不同bin的實部/虛部對應關系理清楚。等代碼跑通后再決定要不要換成arm_cmplx_mag_f32等批量函數。4.2 減均值、加窗與頻譜泄露FFT本質上是對“N點序列在一個周期內”做傅里葉分析它的隱含假設是這N點序列是周期信號的一個整周期。但實際采集的N個點往往首尾不連續相當于在時域乘了一個矩形窗頻譜上就會有能量從真正的主瓣“漏”到旁瓣去表現為主峰旁邊拖著一串衰減的波紋。加窗的作用就是讓N點序列的首尾都平滑過渡到接近0減弱這種非線性截斷。最常用的是Hann窗它能把旁瓣壓得很低代價是主瓣寬度增加一倍頻率分辨能力略微下降。在實際代碼中減均值這一步往往比加窗還要優先如果不減去DC分量FFT結果的0Hz處會有一個很大的直流譜線而它在Hann窗作用下還會向鄰近頻點泄漏把低頻段的小信號都淹沒了。完整預處理邏輯一般是這樣的float mean 0.0f; for (uint16_t i 0; i FFT_POINTS; i) { mean fft_input[i]; } mean / FFT_POINTS; for (uint16_t i 0; i FFT_POINTS; i) { float w 0.5f - 0.5f * cosf(2.0f * PI * i / (FFT_POINTS - 1)); fft_input[i] (fft_input[i] - mean) * w; }注意Hann窗會讓信號的幅度乘上一個約等于0.5的系數相干增益所以在后面做幅值還原時要把這個系數補回來。4.3 幅值換算單邊譜、Hann窗恢復因子與頻率分辨率做完FFT取模之后得到的|X[k]|并不直接等于信號的幅度。因為CMSIS的FFT變換沒有做歸一化N點FFT結果的量級大約是原始信號幅度的N/2倍正頻率部分。還原真實幅值的完整公式要分兩步第一步幅度歸一化|X[k]| / N這樣回退到原始信號幅度第二步單邊譜合并k0的DC分量不乘2其余k0的頻點要乘以2因為負頻率部分的能量被折回正頻率第三步如果使用了Hann窗再乘上窗恢復系數2因為Hann相干增益約為0.5。三部分合起來對k0的有效頻點恢復系數是4/N。代碼中可以寫float real_amp 2.0f * 2.0f / (float)FFT_POINTS * sqrtf(re * re im * im);頻率分辨率則由采樣率和FFT點數共同決定Δf fs / N。比如采樣率20kHz做1024點FFT每個bin對應的頻率寬度大約是19.5Hz。這意味著兩個頻率差小于19.5Hz的信號會在同一個bin里疊加無法分辨。想做更精細的頻率分析要么降低采樣率要么增加點數二者要按具體場景權衡。舉個例子如果要分辨10Hz間隔的邊帶而信號最高頻率是5kHz那N至少要滿足fs/Δf 10000/10 1000向上取2的冪就選1024。如果把同樣的點數用在40kHz采樣率上分辨率就只有39Hz邊帶信息全糊掉了。這個取舍是FFT應用中最需要對系統級需求有清晰認知的地方。5. 實測翻車記錄五個真實故障的完整排查鏈路5.1 DMA只搬了一次就停之后數據全是重復的第一段現象串口打印ADC數據第一次打印看起來正常再打印發現數值完全沒變化像死機一樣。用調試器查看DMA的NDTR寄存器發現它固定在一個值上不動。排查過程我首先檢查HAL_ADC_Start_DMA是否被反復調用結果發現只調用了一次這個沒問題。再查DMA初始化的模式參數發現CubeMX生成代碼里DMA_HandleTypeDef的Init.Mode字段是DMA_NORMAL問題就出在這里。原因Normal模式下DMA傳輸次數到達設定值后通道自動關閉。ADC還在繼續轉換、繼續產生DMA請求但DMA通道已經不再響應數據自然不被搬運。改為DMA_CIRCULAR后NDTR寄存器會開始循環變化數據恢復連續更新。這個案例看起來簡單但它很典型很多“程序跑著跑著數據不動了”的問題不是程序邏輯死了而是外設配置模式用錯了。排查時先檢查硬件外設狀態寄存器比反復看邏輯代碼高效得多。5.2 干凈信號源FFT卻一片尖峰底噪高出預期現象用信號發生器輸入一個非常干凈的1kHz正弦波FFT結果里除了1kHz主峰還在幾十Hz到幾百Hz區間出現一堆小尖峰底噪比預期高出一個數量級。排查過程我做了兩個實驗先直接把ADC引腳短接到GND跑FFT底噪依然很高說明干擾在ADC通路內部或參考電源上再把ADC引腳接到一個低噪基準電壓源比如干凈的1.65V底噪降下來說明問題在輸入通路的前端或電源質量上。最后在Vref引腳旁加強濾波電容并把該引腳的走線避開數字信號線頻譜干凈了不少。這里要特別說一下STM32F407的參考電壓。很多最小系統板上Vref直接接3.3V電源而這個3.3V同時給一堆數字芯片供電開關噪聲全都會通過Vref耦合進ADC轉換結果。FFT對噪聲極其敏感這種電源噪聲在時域看起來不明顯在頻域卻會表現為寬頻底噪或尖峰。在追求頻譜精度的場合至少要給Vref加一個高性能LDO和低ESR電容模擬地和數字地單點連接這個投資非常值得。5.3 頻率整體偏移了幾個bin信號源頻率明明很準現象輸入1kHz正弦波FFT峰值出現在約1050Hz的位置不是1000Hz而且偏差比例基本固定。排查過程先確認信號發生器輸出頻率足夠準然后懷疑采樣率的實際值和理論值不一致。F407的ADC時鐘來自APB2總線經過預分頻后才能喂給ADC。如果APB2總線和ADC預分頻配置錯了比如我以為ADC時鐘是21MHz實際卻是其他值那么真正的采樣率和代碼里計算用的采樣率就對不上頻率自然整體偏差。我用FFT主峰的已知頻率反推實際采樣率如果程序設定的fs是20kHzFFT顯示1kHz信號在1050Hz那么實際采樣率約等于20kHz×(1050/1000)21kHz。按這個反推值去核對時鐘樹發現確實是ADC預分頻配置問題。修正后主峰立刻回到1000Hz位置。這也引出一個很實用的標定方法做頻率測量類產品時可以用一個已知精度的信號源輸入利用FFT峰值偏移反算實際采樣率再用軟件校準系數修正比逐個寄存器排查快得多。5.4 單通道正常多通道數據串位現象兩個通道掃描模式采集ch0輸入的信號卻在ch1的頻譜里看到了或者兩個通道的頻譜混在一起。排查過程先確認ADC掃描順序DMA搬運的數據是按rank順序排列的ch0在ch1前。問題出在提取數據時直接用了前1024個點沒有按通道數取模。由于DMA緩沖區是兩路交替排列前1024個點里其實混合了兩路數據直接當單序列FFT結果當然不對。按前面2.2小節的代碼重新抽取后兩路頻譜恢復正常。這類問題在代碼里往往很難一眼看出來但只要在調試器里查看DMA緩沖區的排列規律很快就能定位。5.5 長時間運行后FFT結果突然亂掉甚至HardFault現象系統連續運行幾分鐘后FFT結果莫名其妙變成一片噪聲偶爾直接進入HardFault。排查過程先檢查內存越界。FFT的輸入輸出數組長度都是1024但如果手動計算幅值的數組長度只分配了512訪問k1024的一半邊界時越界會覆蓋其他變量。排查之后發現真正的問題在于CPU處理Buffer時DMA正在往同一個Buffer里寫新數據導致FFT讀取的數據被撕裂。解決方案就是用前面說的雙緩沖乒乓結構讓DMA和CPU錯開時段操作不同的半區。改成乒乓結構之后連續跑了一整夜頻譜一直穩定。另外一個小經驗HAL_ADC_ConvHalfCpltCallback和HAL_ADC_ConvCpltCallback里不要直接做FFT運算和打印它們會拖累中斷響應可能影響ADC采樣時序。最好只做標志位或半區索引記錄把重活丟給主循環或RTOS任務。6. 讓FFT結果更“能打”的后續手段如果只是做Demo前面這些已經夠了。但要在實際產品里把頻譜當數據源使用還有幾件事值得做。第一件事是抗混疊濾波。FFT的輸入信號里如果混入了超過采樣率一半的頻率成分這些高頻成分會發生混疊折疊回低頻區形成“幽靈譜線”。軟件層面沒法有效消除混疊必須在ADC輸入前端加RC低通濾波器或者用運放搭一個有源低通把超出fs/2的成分提前濾掉。我做音頻采樣時習慣在ADC引腳前放一個一階RC截止頻率設置在fs/2附近對高頻噪聲的壓制立竿見影。第二件事是考慮FFT運算的實時調度。如果采樣率是20kHz一個1024點FFT的窗口是51.2msCMSIS的arm_rfft_fast_f32在168MHz主頻下跑1024點大約只要幾十到一百多微秒算力綽綽有余。但如果還要同時做顯示刷新、SD卡存儲、通信協議建議把FFT任務放到低優先級循環里采樣和搬運則完全依賴DMA不要用阻塞方式等FFT結果。如果需要更高幀率可以適當減少點數或者把兩個DMA緩沖區擴展成4段環形。第三件事是校準幅值。ADC的增益誤差和偏置誤差、前級放大電路的倍率、Vref的不精確都會讓FFT算出來的幅度與實際物理量對不上。在產品化時通常用標準信號源輸入幾個已知幅度和頻率的點擬合出一條幅值校準曲線在軟件里做補償。這個過程屬于長期調校但一旦做完了你的FFT結果就不只是“看起來有譜”而是可以當作測量數據用了。我現在的習慣是拿到任何STM32F407的FFT需求先把“時鐘樹到底怎么了”“DMA是循環還是單次”“FFT輸入是否對齊、是否加窗、是否減均值”這三件事查清楚再開始寫應用邏輯。這三步看著基礎卻決定了后面所有結果的可靠性。如果你也正在被ADC采出來的數據喂給FFT后頻譜亂糟糟的問題折磨希望這篇記錄能讓你少走一點彎路。本文還有配套的精品資源點擊獲取