資料介紹
描述
在上一個教程中,我們創(chuàng)建了一個 I2S 發(fā)送器來從內(nèi)部 ROM 輸出一些音頻數(shù)據(jù)。在下一步中,我們?yōu)檫@個 I2S 發(fā)送器添加一個 AXI-Stream 接口,這樣我們就可以將發(fā)送器與 ZYNQ 的處理系統(tǒng)連接起來,從 SD 卡中輸出一些音頻數(shù)據(jù)。
為此,AXIS_I2S
創(chuàng)建了一個名為的新頂部設計。這個設計應該有如下界面:
此塊設計產(chǎn)生以下實體:
entity AXIS_I2S is
Generic ( RATIO : INTEGER := 8;
WIDTH : INTEGER := 16
);
Port ( MCLK : in STD_LOGIC;
nReset : in STD_LOGIC;
LRCLK : out STD_LOGIC;
SCLK : out STD_LOGIC;
SD : out STD_LOGIC;
ACLK : in STD_LOGIC;
ARESETn : in STD_LOGIC;
TDATA_RXD : in STD_LOGIC_VECTOR(31 downto 0);
TREADY_RXD : out STD_LOGIC;
TVALID_RXD : in STD_LOGIC
);
end AXIS_I2S;
SCLK與MCKL的比率通過參數(shù)定義,RATIO
每個通道的數(shù)據(jù)字寬度通過參數(shù)定義WIDTH
。
此實現(xiàn)僅支持每個通道 16 位數(shù)據(jù)字(即立體聲 32 位)。以下代碼必須適用于更大的總線寬度。
?在設計中必須實現(xiàn)以下組件:
- 用于為 I2S 發(fā)送器創(chuàng)建輸入時鐘的時鐘預分頻器
- AXI-Stream 從接口
- I2S 發(fā)送器的控制邏輯?
為分頻器創(chuàng)建了一個過程,它在MCLK的上升時鐘沿對計數(shù)器進行計數(shù),并在半個周期后切換信號。SCLK_Int
process
variable Counter : INTEGER := 0;
begin
wait until rising_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;
下一步是實現(xiàn) AXI-Stream 接口。為此使用狀態(tài)機:
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;
復位后,機器從State_Reset
狀態(tài)變?yōu)?/font>State_WaitForTransmitterReady
等待來自 I2S 發(fā)送器的就緒信號的狀態(tài)。一旦發(fā)送器準備就緒,TREADY_RXD
AXI-Stream 接口的信號就會被設置,從而通知主機,從機已準備好接收數(shù)據(jù)。然后從站更改為State_WaitForValid
狀態(tài)。
?在這種狀態(tài)下,從機等待主機設置TVALID_RXD
信號以標記有效數(shù)據(jù)。一旦信號被設置,數(shù)據(jù)就會被寫入內(nèi)部 FIFO。然后機器更改為State_WaitForTransmitterBusy
狀態(tài)。
?現(xiàn)在狀態(tài)機等待 I2S 發(fā)送器開始發(fā)送數(shù)據(jù)并刪除就緒信號。一旦完成,機器就會切換回該State_WaitForTransmitterReady
狀態(tài)并再次等待,直到 I2S 發(fā)送器準備好。
? 有了這個,AXI-Stream 接口理論上就完成了。不幸的是,最后它變得有點棘手,因為當前的電路設計使用兩個不同的時鐘域:
- ACLK的時鐘域
- MCLK的時鐘域
一般來說,這兩個時鐘信號不能從時鐘源(例如通過時鐘分頻器)生成,因為 AXI 接口通常以 100 MHz 運行,而音頻接口需要可以巧妙地分頻到采樣頻率的時鐘速率,例如例如 12.288 MHz。結(jié)果,由于過多的最差負松弛 (WNS) 和總負松弛 (TNS) 在實施過程中發(fā)生時序錯誤:
此外,由于觸發(fā)器的亞穩(wěn)態(tài)發(fā)生在不同的時鐘域中,導致數(shù)據(jù)不正確的風險非常高。發(fā)生亞穩(wěn)態(tài) a. 然后當觸發(fā)器切換并且在那一刻數(shù)據(jù)發(fā)生變化時。
因此,各個時鐘域使用的信號必須分別通過相應的電路傳輸?shù)搅硪粋€時鐘域。Xilinx 在文檔UG953中描述了可用于此目的的相應宏。
- xpm_cdc_gray - 此功能塊使用格雷碼將數(shù)據(jù)總線從一個時鐘域 (src) 傳輸?shù)搅硪粋€時鐘域 (dest)。
- xpm_cdc_single - 將單個信號從一個時鐘域 (src) 轉(zhuǎn)換到另一個時鐘域 (dest)。?
宏的示例可以直接用于 VHDL 代碼:
xpm_cdc_Data : xpm_cdc_handshake generic map ( DEST_EXT_HSK => 0,
DEST_SYNC_FF => 4,
INIT_SYNC_FF => 0,
SIM_ASSERT_CHK => 0,
SRC_SYNC_FF => 4,
WIDTH => (2 * WIDTH)
)
port map ( 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_single generic map ( DEST_SYNC_FF => 4,
SRC_INPUT_REG => 1
)
port map ( src_clk => MCLK,
src_in => Ready_Transmitter,
dest_clk => ACLK,
dest_out => Ready_AXI
);
最后,必須插入 I2S 發(fā)送器并傳遞生成的信號。
Transmitter : I2S_Transmitter generic map ( WIDTH => WIDTH
)
port map( Clock => SCLK_Int,
nReset => nReset,
Ready => Ready_Transmitter,
Tx => Tx_Transmitter,
LRCLK => LRCLK,
SCLK => SCLK,
SD => SD
);
I2S 發(fā)送器的 AXI-Stream 接口現(xiàn)已準備就緒,可供使用。完整的代碼現(xiàn)在應該如下所示:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
library xpm;
use xpm.vcomponents.all;
entity AXIS_I2S is
Generic ( RATIO : INTEGER := 8;
WIDTH : INTEGER := 16
);
Port ( MCLK : in STD_LOGIC;
nReset : in STD_LOGIC;
LRCLK : out STD_LOGIC;
SCLK : out STD_LOGIC;
SD : out STD_LOGIC;
ACLK : in STD_LOGIC;
ARESETn : in STD_LOGIC;
TDATA_RXD : in STD_LOGIC_VECTOR(31 downto 0);
TREADY_RXD : out STD_LOGIC;
TVALID_RXD : in STD_LOGIC
);
end AXIS_I2S;
architecture AXIS_I2S_Arch of AXIS_I2S is
type AXIS_State_t is (State_Reset, State_WaitForTransmitterReady, State_WaitForValid, State_WaitForTransmitterBusy);
signal CurrentState : AXIS_State_t := State_Reset;
signal Tx_AXI : STD_LOGIC_VECTOR(((2 * WIDTH) - 1) downto 0) := (others => '0');
signal Ready_AXI : STD_LOGIC;
signal Tx_Transmitter : STD_LOGIC_VECTOR(((2 * WIDTH) - 1) downto 0) := (others => '0');
signal Ready_Transmitter : STD_LOGIC;
signal SCLK_Int : STD_LOGIC := '0';
component I2S_Transmitter is
Generic ( WIDTH : INTEGER := 16
);
Port ( Clock : in STD_LOGIC;
nReset : in STD_LOGIC;
Ready : out STD_LOGIC;
Tx : in STD_LOGIC_VECTOR(((2 * WIDTH) - 1) downto 0);
LRCLK : out STD_LOGIC;
SCLK : out STD_LOGIC;
SD : out STD_LOGIC
);
end component;
begin
Transmitter : I2S_Transmitter generic map ( WIDTH => WIDTH
)
port map( Clock => SCLK_Int,
nReset => nReset,
Ready => Ready_Transmitter,
Tx => Tx_Transmitter,
LRCLK => LRCLK,
SCLK => SCLK,
SD => SD
);
xpm_cdc_Data : xpm_cdc_gray generic map ( DEST_SYNC_FF => 4,
SIM_ASSERT_CHK => 0,
SIM_LOSSLESS_GRAY_CHK => 0,
WIDTH => (2 * WIDTH)
)
port map ( src_clk => ACLK,
src_in_bin => Tx_AXI,
dest_clk => MCLK,
dest_out_bin => Tx_Transmitter
);
xpm_cdc_Ready : xpm_cdc_single generic map ( DEST_SYNC_FF => 4,
SRC_INPUT_REG => 1
)
port map ( src_clk => MCLK,
src_in => Ready_Transmitter,
dest_clk => ACLK,
dest_out => Ready_AXI
);
process
variable Counter : INTEGER := 0;
begin
wait until rising_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;
接下來,我們要使用此接口通過處理系統(tǒng)從 SD 卡中讀取波形文件,并通過連接的揚聲器使用 CS4344 D/A 轉(zhuǎn)換器輸出音樂。
該項目需要以下 IP 內(nèi)核:
- 帶有 AXI-Stream 接口的 I2S 發(fā)送器
- 從 SD 卡讀取數(shù)據(jù)并將其寫入 FIFO 的處理系統(tǒng)
- AXI-Stream FIFO
- 用于生成音頻時鐘的時鐘向?qū)?/font>
時鐘向?qū)蓵r鐘,然后用作 CS4344 的主時鐘。輸出時鐘可以通過 AXI-Lite 接口適應音頻文件的采樣率。時鐘向?qū)褂?12.288 MHz 時鐘初始化,用于 48 kHz 音頻信號。
AXI-Stream FIFO 用作處理系統(tǒng)和 I2S 發(fā)送器之間的鏈接。處理系統(tǒng)通過 AXI-Lite(或 AXI)接口將數(shù)據(jù)寫入 FIFO,然后將數(shù)據(jù)流式傳輸?shù)?I2S 發(fā)送器。
從設計中創(chuàng)建比特流,然后可以開發(fā)軟件。
讀取 SD 卡需要 Xilinx 的 xilffs FAT 庫,它必須集成到 Vitis 項目的 Board Support Package 中(不要忘記啟用LFN
支持大文件名的選項):
第一步,軟件使用該AudioPlayer_Init
函數(shù)初始化音頻播放器,從而初始化 FIFO、GIC 和中斷處理程序,以及時鐘向?qū)Ш?SD 卡。
u32 AudioPlayer_Init(void)
{
xil_printf("[INFO] Looking for FIFO configuration...\r\n");
_Fifo_ConfigPtr = XLlFfio_LookupConfig(XPAR_FIFO_DEVICE_ID);
if(_Fifo_ConfigPtr == NULL)
{
xil_printf("[ERROR] Invalid FIFO configuration!\r\n");
return XST_FAILURE;
}
xil_printf("[INFO] Initialize FIFO...\r\n");
if(XLlFifo_CfgInitialize(&_Fifo, _Fifo_ConfigPtr, _Fifo_ConfigPtr->BaseAddress) != XST_SUCCESS)
{
xil_printf("[ERROR] FIFO initialization failed!\n\r");
return XST_FAILURE;
}
xil_printf("[INFO] Looking for GIC configuration...\r\n");
_GIC_ConfigPtr = XScuGic_LookupConfig(XPAR_PS7_SCUGIC_0_DEVICE_ID);
if(_GIC_ConfigPtr == NULL)
{
xil_printf("[ERROR] Invalid GIC configuration!\n\r");
return XST_FAILURE;
}
xil_printf("[INFO] Initialize GIC...\r\n");
if(XScuGic_CfgInitialize(&_GIC, _GIC_ConfigPtr, _GIC_ConfigPtr->CpuBaseAddress) != XST_SUCCESS)
{
xil_printf("[ERROR] GIC initialization failed!\n\r");
return XST_FAILURE;
}
xil_printf("[INFO] Setup interrupt handler...\r\n");
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] Can not connect interrupt handler!\n\r");
return XST_FAILURE;
}
XScuGic_Enable(&_GIC, XPAR_FABRIC_FIFO_INTERRUPT_INTR);
xil_printf("[INFO] Enable exceptions...\r\n");
Xil_ExceptionInit();
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT, (Xil_ExceptionHandler)XScuGic_InterruptHandler, &_GIC);
Xil_ExceptionEnable();
xil_printf("[INFO] Enable FIFO interrupts...\r\n");
XLlFifo_IntClear(&_Fifo, XLLF_INT_ALL_MASK);
xil_printf("[INFO] Initialize Clocking Wizard...\r\n");
if((ClockingWizard_Init(&_ClkWiz, XPAR_CLOCKINGWIZARD_BASEADDR) || ClockingWizard_GetOutput(&_ClkWiz, &_AudioClock))!= XST_SUCCESS)
{
xil_printf("[ERROR] Clocking Wizard initialization failed!\n\r");
return XST_FAILURE;
}
xil_printf("[INFO] Mount SD card...\r\n");
if(SD_Init())
{
xil_printf("[ERROR] Can not initialize SD card!\n\r");
return XST_FAILURE;
}
return XST_SUCCESS;
}
初始化完成后,立即調(diào)用該函數(shù)從 SD 卡加AudioPlayer_LoadFile
載文件Audio.wav 。
if(AudioPlayer_LoadFile("Audio.wav"))
{
xil_printf("[ERROR] Can not open Audio file!\n\r");
return XST_FAILURE;
}
u32 AudioPlayer_LoadFile(char* File)
{
if(SD_LoadFileFromCard(File, &_File))
{
xil_printf("[ERROR] Can not open Audio file!\n\r");
return XST_FAILURE;
}
xil_printf(" File size: %lu bytes\n\r", _File.Header.ChunkSize + 8);
xil_printf(" File format: %lu\n\r", _File.Format.AudioFormat);
xil_printf(" Channels: %lu\n\r", _File.Format.NumChannels);
xil_printf(" Sample rate: %lu Hz\n\r", _File.Format.SampleRate);
xil_printf(" Bits per sample: %lu bits\n\r", _File.Format.BitsPerSample);
xil_printf(" Block align: %lu bytes\n\r", _File.Format.BlockAlign);
xil_printf(" Data bytes: %lu bytes\n\r", _File.Header.ChunkSize / _File.Format.NumChannels);
xil_printf(" Samples: %lu\n\r", 8 * _File.Header.ChunkSize / _File.Format.NumChannels / _File.Format.BitsPerSample);
if(( _File.Format.BitsPerSample != 16) || (_File.Format.NumChannels > 2))
{
xil_printf("[ERROR] Invalid file format!\n\r");
return XST_FAILURE;
}
AudioPlayer_ChangeFreq(_File.Format.SampleRate);
XLlFifo_TxReset(&_Fifo);
XLlFifo_IntEnable(&_Fifo, XLLF_INT_ALL_MASK);
SD_CopyDataIntoBuffer(_FifoBuffer, 256);
AudioPlayer_CopyBuffer();
return XST_SUCCESS;
}
該函數(shù)AudioPlayer_LoadFile
調(diào)用該函數(shù)SD_LoadFileFromCard
從 SD 卡加載波形文件。
u32 SD_LoadFileFromCard(const char* FileName, Wave_t* File)
{
xil_printf("[INFO] Opening file: %s...\n\r", FileName);
if(f_open(&_FileHandle, FileName, FA_READ))
{
xil_printf("[ERROR] Can not open audio file!\n\r");
return XST_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] Can not read SD card!\n\r");
return XST_FAILURE;
}
Wave_Header_t Header;
uint32_t Offset = 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] Can not read SD card!\n\r");
return XST_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] Can not place SD card pointer!\n\r");
return XST_FAILURE;
}
}
if(f_read(&_FileHandle, &File->DataHeader, sizeof(Wave_Header_t), &_BytesRead))
{
xil_printf("[ERROR] Can not read SD card!\n\r");
return XST_FAILURE;
}
if(File->Format.AudioFormat != WAVE_FORMAT_PCM)
{
xil_printf("[ERROR] Audio format not supported! Keep sure that the file use the PCM format!\n\r");
return XST_FAILURE;
}
_RemainingBytes = File->DataHeader.ChunkSize;
_IsBusy = true;
return XST_SUCCESS;
}
在下一步中,根據(jù)使用的采樣頻率從波形文件中設置時鐘向?qū)У妮敵鲱l率:
static void AudioPlayer_ChangeFreq(const u32 SampleRate)
{
if(SampleRate == 44100)
{
xil_printf(" Use clock setting 1...\n\r");
_ClkWiz.DIVCLK_DIVIDE = 5;
_ClkWiz.CLKFBOUT_MULT = 42;
_ClkWiz.CLKFBOUT_Frac_Multiply = 0;
_AudioClock.DIVIDE = 93;
_AudioClock.FRAC_Divide = 0;
}
else if(SampleRate == 48000)
{
xil_printf(" Use clock setting 2...\n\r");
_ClkWiz.DIVCLK_DIVIDE = 3;
_ClkWiz.CLKFBOUT_MULT = 23;
_ClkWiz.CLKFBOUT_Frac_Multiply = 0;
_AudioClock.DIVIDE = 78;
_AudioClock.FRAC_Divide = 0;
}
else if(SampleRate == 96000)
{
xil_printf(" Use clock setting 3...\n\r");
_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)整時鐘向?qū)У妮敵鲱l率后,將從波形文件中讀取第一個數(shù)據(jù)塊并將其復制到 FIFO:
u32 SD_CopyDataIntoBuffer(u8* Buffer, const u32 Length)
{
if(_RemainingBytes >= Length)
{
if(f_read(&_FileHandle, Buffer, Length, &_BytesRead))
{
return XST_FAILURE;
}
_RemainingBytes -= _BytesRead;
}
else
{
if(f_read(&_FileHandle, Buffer, _RemainingBytes, &_BytesRead))
{
return XST_FAILURE;
}
if(f_close(&_FileHandle))
{
xil_printf("[ERROR] Can not close audio file!\n\r");
return XST_FAILURE;
}
_IsBusy = false;
}
return XST_SUCCESS;
}
然后程序流程的其余部分發(fā)生在 FIFO 的回調(diào)中:
static void AudioPlayer_FifoHandler(void* CallbackRef)
{
XLlFifo* InstancePtr = (XLlFifo*)CallbackRef;
u32 Pending = 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);
}
else if(Pending & XLLF_INT_TFPE_MASK)
{
AudioPlayer_CopyBuffer();
if(!SD_IsBusy())
{
XLlFifo_IntDisable(&_Fifo, XLLF_INT_ALL_MASK);
}
XLlFifo_IntClear(InstancePtr, XLLF_INT_TFPE_MASK);
}
else if(Pending & XLLF_INT_ERROR_MASK)
{
xil_printf(" Error: %lu!\n\r", 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 就會被來自內(nèi)部緩沖區(qū)的新數(shù)據(jù)填充。當從處理系統(tǒng)到 FIFO 的傳輸完成時,會觸發(fā)TC中斷(傳輸完成)并從 SD 卡中讀取下一個數(shù)據(jù)塊。這將重復,直到文件完全播放。
static void AudioPlayer_CopyBuffer(void)
{
u32 Bytes = 0x00;
for(u32 i = 0x00; i < AUDIOPLAYER_FIFO_BUFFER_SIZE; i += _File.Format.BlockAlign)
{
u32 Word = 0x00;
for(u8 Byte = 0x00; Byte < _File.Format.BlockAlign; Byte++)
{
Word |= _FifoBuffer[i + Byte];
Word <<= 0x08;
}
if(XLlFifo_iTxVacancy(&_Fifo))
{
XLlFifo_TxPutWord(&_Fifo, Word);
Bytes += sizeof(u32);
}
}
XLlFifo_iTxSetLen(&_Fifo, Bytes);
}
現(xiàn)在需要一個波形文件。存儲庫中提供了簡單的測試信號,或者可以在例如wavtones.com上生成。
然后只需將相應的文件復制到名為Audio.wav的 SD 卡中,您就可以開始使用了。
-----------I2S Audio player-----------
[INFO] Looking for FIFO configuration...
[INFO] Initialize FIFO...
[INFO] Looking for GIC configuration...
[INFO] Initialize GIC...
[INFO] Setup interrupt handler...
[INFO] Enable exceptions...
[INFO] Enable FIFO interrupts...
[INFO] Initialize Clocking Wizard...
[INFO] Mount SD card...
[INFO] Opening file: Single.wav...
File size: 264610 bytes
File format: 1
Channels: 1
Sample rate: 48000 Hz
Bits per sample: 16 bits
Data bytes: 264602 bytes
Samples: 132301
Use clock setting 2...
[INFO] Finished!
或使用立體聲音頻:
-----------I2S Audio player-----------
[INFO] Looking for FIFO configuration...
[INFO] Initialize FIFO...
[INFO] Looking for GIC configuration...
[INFO] Initialize GIC...
[INFO] Setup interrupt handler...
[INFO] Enable exceptions...
[INFO] Enable FIFO interrupts...
[INFO] Initialize Clocking Wizard...
[INFO] Mount SD card...
[INFO] Opening file: Dual.wav...
File size: 529208 bytes
File format: 1
Channels: 2
Sample rate: 44100 Hz
Bits per sample: 16 bits
Block align: 4 bytes
Data bytes: 264600 bytes
Samples: 132300
Use clock setting 1...
[INFO] Finished!
?
- 基于STM32單片機SD卡使用庫文件設計源代碼 2次下載
- 使用Arduino UNO播放SD卡中的Midi文件
- Arduino WAV播放器開源
- 基于SPI協(xié)議的SD卡讀寫說明 49次下載
- 使用單片機制作WAV播放器的資料說明
- FPGA實現(xiàn)從SD卡讀出MP3文件并播放 25次下載
- 基于WINCE的多路數(shù)據(jù)采集處理播放及SD卡存儲的實驗系統(tǒng) 14次下載
- 基于Cortex和_COS的SD卡文件系統(tǒng)研究 2次下載
- 28335實用版SD卡文件系統(tǒng)實驗 0次下載
- 基于SD卡的FATFS文件系統(tǒng)的研究與應用_崔鵬偉 40次下載
- SD卡的讀寫控制研究_張淼 9次下載
- 51單片機與SD卡接口設計
- CF卡MP3/WAV方案錄/放板OTG15F
- WAV波形文件的結(jié)構(gòu)及其應用實踐
- CSF文件播放器
- 貼片式SD卡功能介紹【MK SD NAND】 387次閱讀
- 如何將SD卡眾多文件打包成一個.img文件方便一鍵燒寫呢? 917次閱讀
- sd卡是什么有什么用途 TF卡和SD卡有什么區(qū)別 3861次閱讀
- sd卡是什么有什么用途 TF卡和SD卡有什么區(qū)別 2015次閱讀
- WAV文件格式詳解 4103次閱讀
- 如何移植FatFs文件系統(tǒng)到SD卡內(nèi) 1375次閱讀
- 如何使用WAV文件與LTSpice交互 1968次閱讀
- 如何利用Arduino UNO和SD卡制作音樂播放器 7322次閱讀
- 鏡像文件下載到SD卡中的方法 1w次閱讀
- 沁恒股份U盤和SD卡高速文件管理控制芯片CH378概述 3826次閱讀
- 沁恒股份U盤和SD卡文件管理控制芯片CH376簡介 2897次閱讀
- ZYNQ-7000如何生成從Flash和SD卡啟動的鏡像文件 7505次閱讀
- 基于μC/OS-II的SD卡文件系統(tǒng)的設計與實現(xiàn) 1378次閱讀
- spi讀取sd卡數(shù)據(jù)例程 8823次閱讀
- mmc卡和sd卡的區(qū)別是什么 4.7w次閱讀
下載排行
本周
- 1山景DSP芯片AP8248A2數(shù)據(jù)手冊
- 1.06 MB | 532次下載 | 免費
- 2RK3399完整板原理圖(支持平板,盒子VR)
- 3.28 MB | 339次下載 | 免費
- 3TC358743XBG評估板參考手冊
- 1.36 MB | 330次下載 | 免費
- 4DFM軟件使用教程
- 0.84 MB | 295次下載 | 免費
- 5元宇宙深度解析—未來的未來-風口還是泡沫
- 6.40 MB | 227次下載 | 免費
- 6迪文DGUS開發(fā)指南
- 31.67 MB | 194次下載 | 免費
- 7元宇宙底層硬件系列報告
- 13.42 MB | 182次下載 | 免費
- 8FP5207XR-G1中文應用手冊
- 1.09 MB | 178次下載 | 免費
本月
- 1OrCAD10.5下載OrCAD10.5中文版軟件
- 0.00 MB | 234315次下載 | 免費
- 2555集成電路應用800例(新編版)
- 0.00 MB | 33566次下載 | 免費
- 3接口電路圖大全
- 未知 | 30323次下載 | 免費
- 4開關(guān)電源設計實例指南
- 未知 | 21549次下載 | 免費
- 5電氣工程師手冊免費下載(新編第二版pdf電子書)
- 0.00 MB | 15349次下載 | 免費
- 6數(shù)字電路基礎(chǔ)pdf(下載)
- 未知 | 13750次下載 | 免費
- 7電子制作實例集錦 下載
- 未知 | 8113次下載 | 免費
- 8《LED驅(qū)動電路設計》 溫德爾著
- 0.00 MB | 6656次下載 | 免費
總榜
- 1matlab軟件下載入口
- 未知 | 935054次下載 | 免費
- 2protel99se軟件下載(可英文版轉(zhuǎn)中文版)
- 78.1 MB | 537798次下載 | 免費
- 3MATLAB 7.1 下載 (含軟件介紹)
- 未知 | 420027次下載 | 免費
- 4OrCAD10.5下載OrCAD10.5中文版軟件
- 0.00 MB | 234315次下載 | 免費
- 5Altium DXP2002下載入口
- 未知 | 233046次下載 | 免費
- 6電路仿真軟件multisim 10.0免費下載
- 340992 | 191187次下載 | 免費
- 7十天學會AVR單片機與C語言視頻教程 下載
- 158M | 183279次下載 | 免費
- 8proe5.0野火版下載(中文版免費下載)
- 未知 | 138040次下載 | 免費
評論
查看更多