幾年前本身就很想DIY一個音頻播放設備,那時候也申請了一些音頻DAC以及運放,前先後後作了一些耳放,也用PCM1793和51作過一個CD-ROM播放器(遙控器控制)。但逐漸感受沒興趣了,本身也沒什麼儀器能夠測試,因此也就是簡單地按照一些典型電路本身焊板子調試,以後很長一段時間都沒有作過相關的東西。不過以前剩下的一些元件放着也惋惜,因而決定再次動手!數組
此次的計劃是作一個USB異步音頻聲卡,用STM32作USB接口,fpga(用的是EP2C5T144)緩存數據併合成I2S信號給PCM1796。9月開學前花了幾天畫了PCB,而後國慶這幾天了實現了初步的USB聲卡功能。先上圖:緩存
音質感受不錯,具體就不評論了,如今對調試經驗進行一點總結。異步
1. USB異步音頻傳輸,主要須要2個同步端點:函數
1) 同步IN端點,該端點負責實時反饋設備的速度(能夠理解爲傳快傳慢)。這個反饋值以USB上的SOF爲參考,表明每一幀須要消耗的數據量,在USB全速設備中(full speed),該值以10.10的格式放在3byte中(對齊MSB),這先後各10位分別表明實際消耗速度的整數和分數部分;若是是高速設備則狀況稍有不一樣,是以微幀爲單位,反饋的格式也不同。 在此我主要參考了USB Audio Class Spec V1.0以及USB Device Class Definition for Audio Data Formats的內容,其中反饋(feedback)的一些狀況在USB Specification Rev.2.0裏也有描述。測試
2) 同步OUT端點,音頻數據流就在這個端點上傳輸,每一幀(Frame)都會有數據傳過來。優化
個人系統是WIN7,在實際測試後發現,反饋值並不影響接收的音頻採樣率,即:PC端根據反饋值會少發或多發一些採樣值,但不會由於的設備反饋值變化而去SRC原始音頻數據,換句話說,原始的音頻數據會始終保持自身的採樣值,不會被SRC而去匹配設備的實際回放採樣率。所以,我認爲這個反饋值不能理解爲設備採樣率,能夠簡單的理解成傳快和傳慢。不過反饋值的只在必定的範圍內起做用,例如:設備被設定在44.1KHZ採樣率以及16BIT的模式下工做,則反饋值是以44.1*2*2 = 176.4爲基準,在必定的範圍內纔有效,假設一種極端狀況,若是我反饋值爲44.1+10 = 54.1(代表我每一幀須要消耗超過200 byte的數據),但實際接收的一幀數據包大小甚至不會超過200。所以,這個反饋值起到一個微調做用,設備能夠根據緩存的狀況實時反饋傳輸速度。ui
2. 關於I2S:設計
由於目前手上只有16.36767MHZ的晶振,能夠經過該晶振產生42.626KHZ的I2S信號(由於暫時只有這個晶振,只能勉強代替44.1KHZ採樣率,這樣的確會使得播放速度變慢,音調變低)。指針
爲了得到I2S首先將16.36767MHZ進行8分頻獲得2.046MHZ的BCK,每24個BCK組成一個聲道的數據。我用的是16bit Right Jusitified的格式,所以16bit的音頻數據就在每24個BCK的後16個上發送,48個BCK傳輸的就是一個雙聲道音頻的一個雙聲道採樣值,同時,對BCK進行48分頻就能獲得42.626KHZ的LRCK。注意LRCK和BCK以及DATA須要按時序對齊。調試
主要參考PCM1796手冊裏的這張時序圖:
3. 在STM32有關USB的設計中必定要當心控制中斷函數裏的執行時間,否則會出現麻煩。爲了分析MCU裏開闢的緩存使用狀況,我在SOF的回調函數(在SOF中斷中調用)裏用串口打印調試信息,結果這段代碼致使了音樂頓卡,最後花了很多時間才終於找到致使問題的緣由。
具體狀況是這樣的:我在SOF回調函數中加入一段每幾百次SOF就printf一次的代碼,因爲串口配置的波特率是9600,且是忙等的方式輸出字符,可想而知輸出一次信息就會佔用好幾個SOF的時間,致使MCU卡在了中斷函數中,同步端點的輸出不能按時接收,結果就致使音樂大概每一秒都會嚴重斷流一次。
4. 經測試發現,在系統沒有聲音的時候(例如沒有音樂播放),在用於傳輸音頻數據的OUT ENDPOINT上是不會有數據包的。
5. 整個製做過程當中最棘手的一個問題是STM32會隨機」死掉」,在DEBUG下才發現產生了HARD FAULT,那問題就來了,這個HARD FAULT是怎麼來的?由於是隨機產生 的,好比說可能設備剛連上PC過幾秒就HARD FAULT,也多是聽了幾分鐘後再HARD FAULT,因此我在定位問題代碼時花了很多時間,具體過程我在另外寫一篇隨筆裏介紹。直接說結果吧,最終找到問題來源於usb_ini.c文件中的HP中斷處理過程當中,在這個端點IO處理的函數中,獲取可能會現端點號爲0的狀況(HP處理的是同步端點和塊傳輸端點,所以不可能會有0號端點,由於0號端點是默認的控制端點),而HP處理0號端點會致使數組越界,跳轉到一個錯誤的入口,從而致使hard fault。出現這樣狀況的緣由暫不明,目前認爲多是傳輸錯誤致使的。
6. 對FSMC總線上的某個地址直接讀寫時,必定要對指針加volatile關鍵字,例如: *(volatile uint16_t*)(FIFO_ADDR) = streamBuff[i]。這就如同庫文件在定義寄存器時會加 __IO,其實這就是關鍵字volatile。若是不加volatile,編譯器可能會進行優化,例如對某個地址連寫4次,編譯後的程序卻只執行最後一次。
7. quartus中調用的異步FIFO的IP,其rdusedw、rdempty和wrusedw、wrfull等信號是分別在rdclk和wrclk下改變的,而且有必定延時,具體狀況能夠查看FIFO說明文檔。這意味着,若是長時間沒有輸入wrclk(或rdclk),則相應的讀寫狀態信號是不肯定的。
暫時就寫到這裏,之後再整理。最後再上兩張圖。