開篇第一步
在上一篇教程中,創(chuàng)建了一個(gè) I2S 發(fā)送器用來(lái)發(fā)送來(lái)從FPGA內(nèi)部 ROM 的音頻數(shù)據(jù)。下一步,我們向該 I2S 發(fā)送器添加 AXI-Stream 接口,這樣我們就可以將發(fā)送器與 ZYNQ 的處理系統(tǒng)連接,還可以從 SD 卡讀取音頻數(shù)據(jù)。
為此,創(chuàng)建一個(gè)新的top設(shè)計(jì)。本設(shè)計(jì)應(yīng)具有以下接口:
該塊設(shè)計(jì)產(chǎn)生以下代碼:
entityAXIS_I2Sis Generic(RATIO:INTEGER:=8; WIDTH:INTEGER:=16 ); Port(MCLK:inSTD_LOGIC; nReset:inSTD_LOGIC; LRCLK:outSTD_LOGIC; SCLK:outSTD_LOGIC; SD:outSTD_LOGIC; ACLK:inSTD_LOGIC; ARESETn:inSTD_LOGIC; TDATA_RXD:inSTD_LOGIC_VECTOR(31downto0); TREADY_RXD:outSTD_LOGIC; TVALID_RXD:inSTD_LOGIC ); endAXIS_I2S;
SCLK與MCKL的比率通過(guò)RATIO參數(shù)定義,每個(gè)通道的數(shù)據(jù)字寬度通過(guò)WIDTH參數(shù)定義。
PS:此實(shí)現(xiàn)僅支持每個(gè)通道 16 位數(shù)據(jù)字(即立體聲 32 位)。
?設(shè)計(jì)中必須實(shí)現(xiàn)以下組件:
用于為 I2S 發(fā)送器創(chuàng)建輸入時(shí)鐘的時(shí)鐘預(yù)分頻器
AXI-Stream 從接口
I2S發(fā)送器的控制邏輯?
為分頻器創(chuàng)建了一個(gè)過(guò)程,該過(guò)程在MCLK時(shí)鐘上升沿對(duì)計(jì)數(shù)器進(jìn)行計(jì)數(shù),并在半個(gè)周期后切換信號(hào)SCLK_Int。
process variableCounter:INTEGER:=0; begin waituntilrising_edge(MCLK); if(Counter((RATIO?/?2)?-?1))?then ????????Counter?:=?Counter?+?1; ????else ????????Counter?:=?0; ????????SCLK_Int?<=?not?SCLK_Int; ????end?if; ????if(nReset?=?'0')?then ????????Counter?:=?0; ????????SCLK_Int?<=?'0'; ????end?if; end?process;
下一步是實(shí)現(xiàn) AXI-Stream 接口。為此使用狀態(tài)機(jī):
process begin waituntilrising_edge(ACLK); caseCurrentStateis whenState_Reset=> Tx_AXI<=?(others?=>'0'); CurrentState<=?State_WaitForTransmitterReady; ????????when?State_WaitForTransmitterReady?=> if(Ready_AXI='1')then TREADY_RXD<=?'1'; ????????????????CurrentState?<=?State_WaitForValid; ????????????else ????????????????TREADY_RXD?<=?'0'; ????????????????CurrentState?<=?State_WaitForTransmitterReady; ????????????end?if; ????????when?State_WaitForValid?=> if(TVALID_RXD='1')then TREADY_RXD<=?'0'; ????????????????Tx_AXI?<=?TDATA_RXD; ????????????????CurrentState?<=?State_WaitForTransmitterBusy; ????????????else ????????????????TREADY_RXD?<=?'1'; ????????????????CurrentState?<=?State_WaitForValid; ????????????end?if; ????????when?State_WaitForTransmitterBusy?=> if(Ready_AXI='0')then CurrentState<=?State_WaitForTransmitterReady; ????????????else ????????????????CurrentState?<=?State_WaitForTransmitterBusy; ????????????end?if; ????end?case; ????if(ARESETn?=?'0')?then ????????????CurrentState?<=?State_Reset; ????end?if; end?process;
復(fù)位后,機(jī)器從State_Reset狀態(tài)變?yōu)镾tate_WaitForTransmitter等待I2S 發(fā)送器發(fā)出就緒Ready信號(hào)的狀態(tài)。一旦發(fā)送器準(zhǔn)備好,TREADY_RXD就會(huì)設(shè)置 AXI-Stream 接口的信號(hào),通知主機(jī)從機(jī)已準(zhǔn)備好接收數(shù)據(jù)。然后從機(jī)改變?yōu)镾tate_WaitForValid狀態(tài)。
?在此狀態(tài)下,從機(jī)等待主機(jī)置位信號(hào)TVALID_RXD標(biāo)記有效數(shù)據(jù)。一旦置位了信號(hào),數(shù)據(jù)就會(huì)寫入內(nèi)部 FIFO。然后機(jī)器改變到State_WaitForTransmitterBusy狀態(tài)。
?現(xiàn)在狀態(tài)機(jī)等待I2S發(fā)送器開始發(fā)送數(shù)據(jù)并“刪除”就緒信號(hào)。一旦完成,狀態(tài)機(jī)就會(huì)切換回State_WaitForTransmitterReady狀態(tài)并再次等待,直到 I2S 發(fā)送器準(zhǔn)備就緒。
?這樣,理論上 AXI-Stream 接口就完成了。不幸的是,最后變得有點(diǎn)棘手,因?yàn)楫?dāng)前的電路設(shè)計(jì)使用兩個(gè)不同的時(shí)鐘域:
ACLK的時(shí)鐘域
MCLK的時(shí)鐘域
一般來(lái)說(shuō),這兩個(gè)時(shí)鐘信號(hào)不能從時(shí)鐘源生成(例如通過(guò)時(shí)鐘分頻器),因?yàn)?AXI 接口通常以 100 MHz 運(yùn)行,而音頻接口需要可以整齊地分頻至采樣頻率的時(shí)鐘速率,例如 12.288 MHz。因此,由于最差負(fù)裕量 (WNS) 和總負(fù)裕量 (TNS) 過(guò)多,在實(shí)現(xiàn)過(guò)程中會(huì)出現(xiàn)時(shí)序錯(cuò)誤:
此外,由于觸發(fā)器在不同時(shí)鐘域中發(fā)生亞穩(wěn)態(tài)而導(dǎo)致數(shù)據(jù)不正確的風(fēng)險(xiǎn)非常高。
因此,各個(gè)時(shí)鐘域所使用的信號(hào)必須在每種情況下經(jīng)由相應(yīng)的電路傳送到另一時(shí)鐘域。Xilinx 在文檔UG953(https://www.xilinx.com/support/documentation/sw_manuals/xilinx2018_3/ug953-vivado-7series-libraries.pdf)中描述了可用于此目的的相應(yīng)宏。
xpm_cdc_gray - 該功能塊使用格雷碼將數(shù)據(jù)總線從一個(gè)時(shí)鐘域 (src) 傳輸?shù)搅硪粋€(gè)時(shí)鐘域 (dest)。
xpm_cdc_single - 將單個(gè)信號(hào)從一個(gè)時(shí)鐘域 (src) 轉(zhuǎn)換到另一個(gè)時(shí)鐘域 (dest)。
宏的示例可以直接用于 VHDL 代碼:
xpm_cdc_Data:xpm_cdc_handshakegenericmap(DEST_EXT_HSK=>0, DEST_SYNC_FF=>4, INIT_SYNC_FF=>0, SIM_ASSERT_CHK=>0, SRC_SYNC_FF=>4, WIDTH=>(2*WIDTH) ) portmap(src_clk=>ACLK, src_in=>Data_Fast, dest_clk=>MCLK, dest_out=>Data_Slow, dest_ack=>'0', src_send=>src_send, src_rcv=>src_rcv, dest_req=>dest_req ); xpm_cdc_Ready:xpm_cdc_singlegenericmap(DEST_SYNC_FF=>4, SRC_INPUT_REG=>1 ) portmap(src_clk=>MCLK, src_in=>Ready_Transmitter, dest_clk=>ACLK, dest_out=>Ready_AXI );
最后,必須插入 I2S 發(fā)送器并傳遞生成的信號(hào)。
Transmitter:I2S_Transmittergenericmap(WIDTH=>WIDTH ) portmap(Clock=>SCLK_Int, nReset=>nReset, Ready=>Ready_Transmitter, Tx=>Tx_Transmitter, LRCLK=>LRCLK, SCLK=>SCLK, SD=>SD );
I2S 發(fā)送器的 AXI-Stream 接口現(xiàn)已準(zhǔn)備就緒并可供使用。完整的代碼如下所示:
libraryIEEE; useIEEE.STD_LOGIC_1164.ALL; libraryxpm; usexpm.vcomponents.all; entityAXIS_I2Sis Generic(RATIO:INTEGER:=8; WIDTH:INTEGER:=16 ); Port(MCLK:inSTD_LOGIC; nReset:inSTD_LOGIC; LRCLK:outSTD_LOGIC; SCLK:outSTD_LOGIC; SD:outSTD_LOGIC; ACLK:inSTD_LOGIC; ARESETn:inSTD_LOGIC; TDATA_RXD:inSTD_LOGIC_VECTOR(31downto0); TREADY_RXD:outSTD_LOGIC; TVALID_RXD:inSTD_LOGIC ); endAXIS_I2S; architectureAXIS_I2S_ArchofAXIS_I2Sis typeAXIS_State_tis(State_Reset,State_WaitForTransmitterReady,State_WaitForValid,State_WaitForTransmitterBusy); signalCurrentState:AXIS_State_t:=State_Reset; signalTx_AXI:STD_LOGIC_VECTOR(((2*WIDTH)-1)downto0):=(others=>'0'); signalReady_AXI:STD_LOGIC; signalTx_Transmitter:STD_LOGIC_VECTOR(((2*WIDTH)-1)downto0):=(others=>'0'); signalReady_Transmitter:STD_LOGIC; signalSCLK_Int:STD_LOGIC:='0'; componentI2S_Transmitteris Generic(WIDTH:INTEGER:=16 ); Port(Clock:inSTD_LOGIC; nReset:inSTD_LOGIC; Ready:outSTD_LOGIC; Tx:inSTD_LOGIC_VECTOR(((2*WIDTH)-1)downto0); LRCLK:outSTD_LOGIC; SCLK:outSTD_LOGIC; SD:outSTD_LOGIC ); endcomponent; begin Transmitter:I2S_Transmittergenericmap(WIDTH=>WIDTH ) portmap(Clock=>SCLK_Int, nReset=>nReset, Ready=>Ready_Transmitter, Tx=>Tx_Transmitter, LRCLK=>LRCLK, SCLK=>SCLK, SD=>SD ); xpm_cdc_Data:xpm_cdc_graygenericmap(DEST_SYNC_FF=>4, SIM_ASSERT_CHK=>0, SIM_LOSSLESS_GRAY_CHK=>0, WIDTH=>(2*WIDTH) ) portmap(src_clk=>ACLK, src_in_bin=>Tx_AXI, dest_clk=>MCLK, dest_out_bin=>Tx_Transmitter ); xpm_cdc_Ready:xpm_cdc_singlegenericmap(DEST_SYNC_FF=>4, SRC_INPUT_REG=>1 ) portmap(src_clk=>MCLK, src_in=>Ready_Transmitter, dest_clk=>ACLK, dest_out=>Ready_AXI ); process variableCounter:INTEGER:=0; begin waituntilrising_edge(MCLK); if(Counter((RATIO?/?2)?-?1))?then ????????????Counter?:=?Counter?+?1; ????????else ????????????Counter?:=?0; ????????????SCLK_Int?<=?not?SCLK_Int; ????????end?if; ????????if(nReset?=?'0')?then ????????????Counter?:=?0; ????????????SCLK_Int?<=?'0'; ????????end?if; ????end?process; ????process ????begin ????????wait?until?rising_edge(ACLK); ????????case?CurrentState?is ????????????when?State_Reset?=> Tx_AXI<=?(others?=>'0'); CurrentState<=?State_WaitForTransmitterReady; ????????????when?State_WaitForTransmitterReady?=> if(Ready_AXI='1')then TREADY_RXD<=?'1'; ????????????????????CurrentState?<=?State_WaitForValid; ????????????????else ????????????????????TREADY_RXD?<=?'0'; ????????????????????CurrentState?<=?State_WaitForTransmitterReady; ????????????????end?if; ????????????when?State_WaitForValid?=> if(TVALID_RXD='1')then TREADY_RXD<=?'0'; ????????????????????Tx_AXI?<=?TDATA_RXD; ????????????????????CurrentState?<=?State_WaitForTransmitterBusy; ????????????????else ????????????????????TREADY_RXD?<=?'1'; ????????????????????CurrentState?<=?State_WaitForValid; ????????????????end?if; ????????????when?State_WaitForTransmitterBusy?=> if(Ready_AXI='0')then CurrentState<=?State_WaitForTransmitterReady; ????????????????else ????????????????????CurrentState?<=?State_WaitForTransmitterBusy; ????????????????end?if; ????????end?case; ????????if(ARESETn?=?'0')?then ????????????CurrentState?<=?State_Reset; ????????end?if; ????end?process; end?AXIS_I2S_Arch;
接下來(lái),我們希望使用該接口從 SD 卡讀取波形文件,并使用 CS4344 D/A 轉(zhuǎn)換器通過(guò)連接的揚(yáng)聲器輸出音樂(lè)。
該項(xiàng)目需要以下IP核:
具有 AXI-Stream 接口的 I2S 發(fā)送器
處理系統(tǒng)從 SD 卡讀取數(shù)據(jù)并將其寫入 FIFO
AXI-Stream FIFO
用于生成音頻時(shí)鐘的PLL
時(shí)鐘向?qū)蓵r(shí)鐘,然后將其用作 CS4344 的主時(shí)鐘。輸出時(shí)鐘可以通過(guò) AXI-Lite 接口適應(yīng)音頻文件的采樣率。
AXI-Stream FIFO 充當(dāng)處理系統(tǒng)和 I2S 發(fā)送器之間的鏈接。處理系統(tǒng)通過(guò) AXI-Lite(或 AXI)接口將數(shù)據(jù)寫入 FIFO,然后將數(shù)據(jù)傳輸至 I2S 發(fā)送器。
根據(jù)設(shè)計(jì)創(chuàng)建比特流,然后可以開發(fā)軟件。
讀取 SD 卡需要 Xilinx 的 xilffs FAT 庫(kù),該庫(kù)必須集成到 Vitis 項(xiàng)目的板級(jí)支持包中(不要忘記啟用LFN支持大文件名的選項(xiàng)):
第一步,軟件使用該AudioPlayer_Init函數(shù)初始化音頻播放器,從而初始化 FIFO、GIC 和中斷處理程序,以及時(shí)鐘向?qū)Ш?SD 卡。
u32AudioPlayer_Init(void) { xil_printf("[INFO]LookingforFIFOconfiguration... "); _Fifo_ConfigPtr=XLlFfio_LookupConfig(XPAR_FIFO_DEVICE_ID); if(_Fifo_ConfigPtr==NULL) { xil_printf("[ERROR]InvalidFIFOconfiguration! "); returnXST_FAILURE; } xil_printf("[INFO]InitializeFIFO... "); if(XLlFifo_CfgInitialize(&_Fifo,_Fifo_ConfigPtr,_Fifo_ConfigPtr->BaseAddress)!=XST_SUCCESS) { xil_printf("[ERROR]FIFOinitializationfailed! "); returnXST_FAILURE; } xil_printf("[INFO]LookingforGICconfiguration... "); _GIC_ConfigPtr=XScuGic_LookupConfig(XPAR_PS7_SCUGIC_0_DEVICE_ID); if(_GIC_ConfigPtr==NULL) { xil_printf("[ERROR]InvalidGICconfiguration! "); returnXST_FAILURE; } xil_printf("[INFO]InitializeGIC... "); if(XScuGic_CfgInitialize(&_GIC,_GIC_ConfigPtr,_GIC_ConfigPtr->CpuBaseAddress)!=XST_SUCCESS) { xil_printf("[ERROR]GICinitializationfailed! "); returnXST_FAILURE; } xil_printf("[INFO]Setupinterrupthandler... "); XScuGic_SetPriorityTriggerType(&_GIC,XPAR_FABRIC_FIFO_INTERRUPT_INTR,0xA0,0x03); if(XScuGic_Connect(&_GIC,XPAR_FABRIC_FIFO_INTERRUPT_INTR,(Xil_ExceptionHandler)AudioPlayer_FifoHandler,&_Fifo)!=XST_SUCCESS) { xil_printf("[ERROR]Cannotconnectinterrupthandler! "); returnXST_FAILURE; } XScuGic_Enable(&_GIC,XPAR_FABRIC_FIFO_INTERRUPT_INTR); xil_printf("[INFO]Enableexceptions... "); Xil_ExceptionInit(); Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,(Xil_ExceptionHandler)XScuGic_InterruptHandler,&_GIC); Xil_ExceptionEnable(); xil_printf("[INFO]EnableFIFOinterrupts... "); XLlFifo_IntClear(&_Fifo,XLLF_INT_ALL_MASK); xil_printf("[INFO]InitializeClockingWizard... "); if((ClockingWizard_Init(&_ClkWiz,XPAR_CLOCKINGWIZARD_BASEADDR)||ClockingWizard_GetOutput(&_ClkWiz,&_AudioClock))!=XST_SUCCESS) { xil_printf("[ERROR]ClockingWizardinitializationfailed! "); returnXST_FAILURE; } xil_printf("[INFO]MountSDcard... "); if(SD_Init()) { xil_printf("[ERROR]CannotinitializeSDcard! "); returnXST_FAILURE; } returnXST_SUCCESS; }
一旦初始化完成,就會(huì)調(diào)用AudioPlayer_LoadFile函數(shù)從 SD 卡加載Audio.wav文件 。
if(AudioPlayer_LoadFile("Audio.wav")) { xil_printf("[ERROR]CannotopenAudiofile! "); returnXST_FAILURE; } u32AudioPlayer_LoadFile(char*File) { if(SD_LoadFileFromCard(File,&_File)) { xil_printf("[ERROR]CannotopenAudiofile! "); returnXST_FAILURE; } xil_printf("Filesize:%lubytes ",_File.Header.ChunkSize+8); xil_printf("Fileformat:%lu ",_File.Format.AudioFormat); xil_printf("Channels:%lu ",_File.Format.NumChannels); xil_printf("Samplerate:%luHz ",_File.Format.SampleRate); xil_printf("Bitspersample:%lubits ",_File.Format.BitsPerSample); xil_printf("Blockalign:%lubytes ",_File.Format.BlockAlign); xil_printf("Databytes:%lubytes ",_File.Header.ChunkSize/_File.Format.NumChannels); xil_printf("Samples:%lu ",8*_File.Header.ChunkSize/_File.Format.NumChannels/_File.Format.BitsPerSample); if((_File.Format.BitsPerSample!=16)||(_File.Format.NumChannels>2)) { xil_printf("[ERROR]Invalidfileformat! "); returnXST_FAILURE; } AudioPlayer_ChangeFreq(_File.Format.SampleRate); XLlFifo_TxReset(&_Fifo); XLlFifo_IntEnable(&_Fifo,XLLF_INT_ALL_MASK); SD_CopyDataIntoBuffer(_FifoBuffer,256); AudioPlayer_CopyBuffer(); returnXST_SUCCESS; } 該函數(shù)AudioPlayer_LoadFile調(diào)用函數(shù)SD_LoadFileFromCard從SD卡加載波形文件。 u32SD_LoadFileFromCard(constchar*FileName,Wave_t*File) { xil_printf("[INFO]Openingfile:%s... ",FileName); if(f_open(&_FileHandle,FileName,FA_READ)) { xil_printf("[ERROR]Cannotopenaudiofile! "); returnXST_FAILURE; } if(f_read(&_FileHandle,&File->RIFF,sizeof(Wave_RIFF_t),&_BytesRead)||f_read(&_FileHandle,&File->Format,sizeof(Wave_Format_t),&_BytesRead)) { xil_printf("[ERROR]CannotreadSDcard! "); returnXST_FAILURE; } Wave_Header_tHeader; uint32_tOffset=sizeof(Wave_RIFF_t)+sizeof(Wave_Format_t); if(f_read(&_FileHandle,Header.ChunkID,sizeof(Wave_Header_t),&_BytesRead)||f_lseek(&_FileHandle,Offset)) { xil_printf("[ERROR]CannotreadSDcard! "); returnXST_FAILURE; } if(strncmp("LIST",Header.ChunkID,4)==0) { Offset+=Header.ChunkSize+sizeof(Wave_Header_t); if(f_read(&_FileHandle,&File->ListHeader,sizeof(Wave_Header_t),&_BytesRead)||f_lseek(&_FileHandle,Offset)) { xil_printf("[ERROR]CannotplaceSDcardpointer! "); returnXST_FAILURE; } } if(f_read(&_FileHandle,&File->DataHeader,sizeof(Wave_Header_t),&_BytesRead)) { xil_printf("[ERROR]CannotreadSDcard! "); returnXST_FAILURE; } if(File->Format.AudioFormat!=WAVE_FORMAT_PCM) { xil_printf("[ERROR]Audioformatnotsupported!KeepsurethatthefileusethePCMformat! "); returnXST_FAILURE; } _RemainingBytes=File->DataHeader.ChunkSize; _IsBusy=true; returnXST_SUCCESS; } 在下一步中,根據(jù)使用的采樣頻率從波形文件中設(shè)置時(shí)鐘向?qū)У妮敵鲱l率: staticvoidAudioPlayer_ChangeFreq(constu32SampleRate) { if(SampleRate==44100) { xil_printf("Useclocksetting1... "); _ClkWiz.DIVCLK_DIVIDE=5; _ClkWiz.CLKFBOUT_MULT=42; _ClkWiz.CLKFBOUT_Frac_Multiply=0; _AudioClock.DIVIDE=93; _AudioClock.FRAC_Divide=0; } elseif(SampleRate==48000) { xil_printf("Useclocksetting2... "); _ClkWiz.DIVCLK_DIVIDE=3; _ClkWiz.CLKFBOUT_MULT=23; _ClkWiz.CLKFBOUT_Frac_Multiply=0; _AudioClock.DIVIDE=78; _AudioClock.FRAC_Divide=0; } elseif(SampleRate==96000) { xil_printf("Useclocksetting3... "); _ClkWiz.DIVCLK_DIVIDE=3; _ClkWiz.CLKFBOUT_MULT=23; _ClkWiz.CLKFBOUT_Frac_Multiply=0; _AudioClock.DIVIDE=39; _AudioClock.FRAC_Divide=0; } ClockingWizard_SetClockBuffer(&_ClkWiz); ClockingWizard_SetOutput(&_ClkWiz,&_AudioClock); }
加載音頻文件并且調(diào)整時(shí)鐘向?qū)У妮敵鲱l率后,將從波形文件中讀取第一個(gè)數(shù)據(jù)塊并將其復(fù)制到 FIFO:
u32SD_CopyDataIntoBuffer(u8*Buffer,constu32Length) { if(_RemainingBytes>=Length) { if(f_read(&_FileHandle,Buffer,Length,&_BytesRead)) { returnXST_FAILURE; } _RemainingBytes-=_BytesRead; } else { if(f_read(&_FileHandle,Buffer,_RemainingBytes,&_BytesRead)) { returnXST_FAILURE; } if(f_close(&_FileHandle)) { xil_printf("[ERROR]Cannotcloseaudiofile! "); returnXST_FAILURE; } _IsBusy=false; } returnXST_SUCCESS; }
程序流程的其余部分在 FIFO 的回調(diào)中進(jìn)行:
staticvoidAudioPlayer_FifoHandler(void*CallbackRef) { XLlFifo*InstancePtr=(XLlFifo*)CallbackRef; u32Pending=XLlFifo_IntPending(InstancePtr); while(Pending) { if(Pending&XLLF_INT_TC_MASK) { SD_CopyDataIntoBuffer(_FifoBuffer,AUDIOPLAYER_FIFO_BUFFER_SIZE); XLlFifo_IntClear(InstancePtr,XLLF_INT_TC_MASK); } elseif(Pending&XLLF_INT_TFPE_MASK) { AudioPlayer_CopyBuffer(); if(!SD_IsBusy()) { XLlFifo_IntDisable(&_Fifo,XLLF_INT_ALL_MASK); } XLlFifo_IntClear(InstancePtr,XLLF_INT_TFPE_MASK); } elseif(Pending&XLLF_INT_ERROR_MASK) { xil_printf("Error:%lu! ",Pending); XLlFifo_IntClear(InstancePtr,XLLF_INT_ERROR_MASK); } else { XLlFifo_IntClear(InstancePtr,Pending); } Pending=XLlFifo_IntPending(InstancePtr); } }
一旦 FIFO 觸發(fā)TFPE中斷(發(fā)送 FIFO 可編程空),F(xiàn)IFO 就會(huì)被來(lái)自內(nèi)部緩沖區(qū)的新數(shù)據(jù)填充。當(dāng)從處理系統(tǒng)到 FIFO 的傳輸完成時(shí),會(huì)觸發(fā)TC中斷(傳輸完成),并從 SD 卡讀取下一個(gè)數(shù)據(jù)塊。之后重復(fù)進(jìn)行上面步驟,直到文件完全播放。
staticvoidAudioPlayer_CopyBuffer(void) { u32Bytes=0x00; for(u32i=0x00;i
現(xiàn)在需要一個(gè)波形文件。簡(jiǎn)單的測(cè)試信號(hào)可以wavtones.com上生成(https://www.wavtones.com/functiongenerator.php)。
然后,只需將相應(yīng)的文件以Audio.wav名稱復(fù)制到 SD 卡上,即可開始使用。
-----------I2SAudioplayer----------- [INFO]LookingforFIFOconfiguration... [INFO]InitializeFIFO... [INFO]LookingforGICconfiguration... [INFO]InitializeGIC... [INFO]Setupinterrupthandler... [INFO]Enableexceptions... [INFO]EnableFIFOinterrupts... [INFO]InitializeClockingWizard... [INFO]MountSDcard... [INFO]Openingfile:Single.wav... Filesize:264610bytes Fileformat:1 Channels:1 Samplerate:48000Hz Bitspersample:16bits Databytes:264602bytes Samples:132301 Useclocksetting2... [INFO]Finished!
或者使用立體聲音頻:
-----------I2SAudioplayer----------- [INFO]LookingforFIFOconfiguration... [INFO]InitializeFIFO... [INFO]LookingforGICconfiguration... [INFO]InitializeGIC... [INFO]Setupinterrupthandler... [INFO]Enableexceptions... [INFO]EnableFIFOinterrupts... [INFO]InitializeClockingWizard... [INFO]MountSDcard... [INFO]Openingfile:Dual.wav... Filesize:529208bytes Fileformat:1 Channels:2 Samplerate:44100Hz Bitspersample:16bits Blockalign:4bytes Databytes:264600bytes Samples:132300 Useclocksetting1... [INFO]Finished!
審核編輯:劉清
-
音頻
+關(guān)注
關(guān)注
29文章
2833瀏覽量
81346 -
揚(yáng)聲器
+關(guān)注
關(guān)注
29文章
1289瀏覽量
62897 -
SD卡
+關(guān)注
關(guān)注
2文章
559瀏覽量
63798 -
分頻器
+關(guān)注
關(guān)注
43文章
447瀏覽量
49810 -
fifo
+關(guān)注
關(guān)注
3文章
387瀏覽量
43547 -
發(fā)送器
+關(guān)注
關(guān)注
1文章
258瀏覽量
26795 -
時(shí)鐘信號(hào)
+關(guān)注
關(guān)注
4文章
445瀏覽量
28506
原文標(biāo)題:使用 FPGA 播放 SD 卡中的音頻文件
文章出處:【微信號(hào):Open_FPGA,微信公眾號(hào):OpenFPGA】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論