0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看威廉希尔官方网站 视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

如何从SD卡读取音频文件并将其输出到扬声器上?

OpenFPGA 来源:OpenFPGA 2024-01-22 09:23 次阅读

开篇第一步

在上一篇教程中,创建了一个 I2S 发送器用来发送来从FPGA内部 ROM音频数据。下一步,我们向该 I2S 发送器添加 AXI-Stream 接口,这样我们就可以将发送器与 ZYNQ 的处理系统连接,还可以从 SD 卡读取音频数据。

为此,创建一个新的top设计。本设计应具有以下接口:

501d60ec-b8c4-11ee-8b88-92fbcf53809c.png

该块设计产生以下代码:

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的比率通过RATIO参数定义,每个通道的数据字宽度通过WIDTH参数定义。

PS:此实现仅支持每个通道 16 位数据字(即立体声 32 位)。

‌设计中必须实现以下组件:

用于为 I2S 发送器创建输入时钟的时钟预分频器

AXI-Stream 从接口

I2S发送器的控制逻辑‌

为分频器创建了一个过程,该过程在MCLK时钟上升沿对计数器进行计数,并在半个周期后切换信号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;

下一步是实现 AXI-Stream 接口。为此使用状态机:

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;

复位后,机器从State_Reset状态变为State_WaitForTransmitter等待I2S 发送器发出就绪Ready信号的状态。一旦发送器准备好,TREADY_RXD就会设置 AXI-Stream 接口的信号,通知主机从机已准备好接收数据。然后从机改变为State_WaitForValid状态。

‌在此状态下,从机等待主机置位信号TVALID_RXD标记有效数据。一旦置位了信号,数据就会写入内部 FIFO。然后机器改变到State_WaitForTransmitterBusy状态。

‌现在状态机等待I2S发送器开始发送数据并“删除”就绪信号。一旦完成,状态机就会切换回State_WaitForTransmitterReady状态并再次等待,直到 I2S 发送器准备就绪。

‌这样,理论上 AXI-Stream 接口就完成了。不幸的是,最后变得有点棘手,因为当前的电路设计使用两个不同的时钟域:

ACLK的时钟域

MCLK的时钟域

一般来说,这两个时钟信号不能从时钟源生成(例如通过时钟分频器),因为 AXI 接口通常以 100 MHz 运行,而音频接口需要可以整齐地分频至采样频率的时钟速率,例如 12.288 MHz。因此,由于最差负裕量 (WNS) 和总负裕量 (TNS) 过多,在实现过程中会出现时序错误:

502e470e-b8c4-11ee-8b88-92fbcf53809c.png

此外,由于触发器在不同时钟域中发生亚稳态而导致数据不正确的风险非常高。

因此,各个时钟域所使用的信号必须在每种情况下经由相应的电路传送到另一时钟域。Xilinx 在文档UG953(https://www.xilinx.com/support/documentation/sw_manuals/xilinx2018_3/ug953-vivado-7series-libraries.pdf)中描述了可用于此目的的相应宏。

xpm_cdc_gray - 该功能块使用格雷码将数据总线从一个时钟域 (src) 传输到另一个时钟域 (dest)。

xpm_cdc_single - 将单个信号从一个时钟域 (src) 转换到另一个时钟域 (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 发送器并传递生成的信号。

Transmitter:I2S_Transmittergenericmap(WIDTH=>WIDTH
)
portmap(Clock=>SCLK_Int,
nReset=>nReset,
Ready=>Ready_Transmitter,
Tx=>Tx_Transmitter,
LRCLK=>LRCLK,
SCLK=>SCLK,
SD=>SD
);

I2S 发送器的 AXI-Stream 接口现已准备就绪并可供使用。完整的代码如下所示:

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;

接下来,我们希望使用该接口从 SD 卡读取波形文件,并使用 CS4344 D/A 转换器通过连接的扬声器输出音乐。

该项目需要以下IP核:

具有 AXI-Stream 接口的 I2S 发送器

处理系统从 SD 卡读取数据并将其写入 FIFO

AXI-Stream FIFO

用于生成音频时钟的PLL

5038a082-b8c4-11ee-8b88-92fbcf53809c.png

时钟向导生成时钟,然后将其用作 CS4344 的主时钟。输出时钟可以通过 AXI-Lite 接口适应音频文件的采样率。

5049e2b6-b8c4-11ee-8b88-92fbcf53809c.png

AXI-Stream FIFO 充当处理系统和 I2S 发送器之间的链接。处理系统通过 AXI-Lite(或 AXI)接口将数据写入 FIFO,然后将数据传输至 I2S 发送器。

505b26d4-b8c4-11ee-8b88-92fbcf53809c.png

根据设计创建比特流,然后可以开发软件。

读取 SD 卡需要 Xilinx 的 xilffs FAT 库,该库必须集成到 Vitis 项目的板级支持包中(不要忘记启用LFN支持大文件名的选项):

506eb532-b8c4-11ee-8b88-92fbcf53809c.png

第一步,软件使用该AudioPlayer_Init函数初始化音频播放器,从而初始化 FIFO、GIC 和中断处理程序,以及时钟向导和 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;
}

一旦初始化完成,就会调用AudioPlayer_LoadFile函数从 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;
}
该函数AudioPlayer_LoadFile调用函数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;
}
在下一步中,根据使用的采样频率从波形文件中设置时钟向导的输出频率:

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);
}

加载音频文件并且调整时钟向导的输出频率后,将从波形文件中读取第一个数据块并将其复制到 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 的回调中进行:

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 触发TFPE中断(发送 FIFO 可编程空),FIFO 就会被来自内部缓冲区的新数据填充。当从处理系统到 FIFO 的传输完成时,会触发TC中断(传输完成),并从 SD 卡读取下一个数据块。之后重复进行上面步骤,直到文件完全播放。

staticvoidAudioPlayer_CopyBuffer(void)
{
u32Bytes=0x00;
for(u32i=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);
}

现在需要一个波形文件。简单的测试信号可以wavtones.com上生成(https://www.wavtones.com/functiongenerator.php)。

然后,只需将相应的文件以Audio.wav名称复制到 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!

或者使用立体声音频:

508ecf98-b8c4-11ee-8b88-92fbcf53809c.png

-----------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!
50abb266-b8c4-11ee-8b88-92fbcf53809c.png






审核编辑:刘清

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 音频
    +关注

    关注

    29

    文章

    2877

    浏览量

    81545
  • 扬声器
    +关注

    关注

    29

    文章

    1302

    浏览量

    63027
  • SD卡
    +关注

    关注

    2

    文章

    565

    浏览量

    63899
  • 分频器
    +关注

    关注

    43

    文章

    447

    浏览量

    49910
  • fifo
    +关注

    关注

    3

    文章

    388

    浏览量

    43678
  • 发送器
    +关注

    关注

    1

    文章

    259

    浏览量

    26821
  • 时钟信号
    +关注

    关注

    4

    文章

    448

    浏览量

    28568

原文标题:使用 FPGA 播放 SD 卡中的音频文件

文章出处:【微信号:Open_FPGA,微信公众号:OpenFPGA】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    用stm32f103vet6读取SD中的音频文件遇到的疑问求解

    各位大佬,请求援助啊。。 最近在做一个MP3播放,大概做法就是stm32f103vet6读取SD中的音频文件,然后在传入VS1053b
    发表于 04-23 06:48

    5509A播放音频文件感觉全是杂音

    我将音频文件放到SD中,用DSP5509A将其读取出来,并发送给AIC23,但是播放出来的音乐感觉全是杂音,请问是对AIC23的配置不对吗
    发表于 02-12 11:41

    Spat Revolution沉浸式音频引擎中的自定义扬声器配置

    创建新配置。图1:扬声器配置窗口显示预定义的13.1 Auro 3D扬声器布置管理扬声器配置包括删除配置,重命名配置,将配置导出到文件
    发表于 09-21 13:09

    【项目分享】教你用Arduino、Micro SD制作一个简单的音乐播放

    如何与Arduino结合我们刚才提到过,这个Arduino音乐播放的重要组成成分是SD模块。音频文件会存储在SD
    发表于 09-27 17:20

    怎么设置wavedac参考SD读取

    大家好。我想从SD读取波形声音放了wavevdac8连接扬声器。我的第一个问题是可能的吗?2-secound问题我不知道怎么设置wavedac参考
    发表于 11-04 09:27

    基于MM32F5270开发板SD读取音频文件和界面的设计实现

    1、基于MM32F5270开发板SD读取音频文件和界面的设计目前实现功能如下:实现从SD
    发表于 08-29 14:40

    如何利用更高阻抗的扬声器提高汽车音频质量

    需要设计汽车外部放大器,可以通过增加输出功率、利用更高阻抗的扬声器以及在系统中实施H类控制来升级音频系统架构,从而增强用户体验。本文将详细介绍每种方法,包括这些方法对音频系统重量和性能
    发表于 11-03 08:27

    如何本地WLANSD男取出的主机数据?

    中它的插槽。它仅在读写期间为 SD 模块供电。我希望使用 Arduino SD 模块
    发表于 06-02 08:17

    制作便携式扬声器的方法,DIY制作便携式扬声器的教程

    将两根电线连接到放大器 ic   的 vcc 5V 和接地引脚,将一个扬声器连接到一个通道输出名称 +L -L,另一个连接到 +R -R,这些是输出到我们的扬声器   将输入连接到放大
    发表于 07-31 16:18

    使用M487和NAU88L25在微小SD中解码并播放AMR音频文件

    应用程序:此示例代码使用 M487 微控制和外部音频编码NAU88L25在微小SD中解码并播放AMR
    发表于 08-29 07:28

    消除扬声器音频杂音的技巧

    消除扬声器音频杂音的技巧音频杂音(click and pop)是扬声器或耳机产生的不良噪声。它是由注入扬声器线圈的瞬态电流脉冲引起的,该电流
    发表于 05-11 16:04 107次下载

    JBL扬声器的选择及应用

    JBL扬声器的选择及应用 如何选择扬声器扬声器实际是一种把可范围内的音频电功率信号通过换能器(
    发表于 01-14 16:15 7457次阅读

    读取SDFAT12_16_32文件系统

    读取SDFAT12_16_32文件系统。
    发表于 05-20 16:29 22次下载

    基于红牛开发板的SD读取文件名在LCD显示

    文档内容介绍了基于红牛开发板的SD读取文件名在LCD显示的资料。
    发表于 09-01 17:09 25次下载
    基于红牛开发板的<b class='flag-5'>SD</b><b class='flag-5'>卡</b><b class='flag-5'>读取</b><b class='flag-5'>文件</b>名在LCD<b class='flag-5'>上</b>显示

    stm32程序升级SD读取hex文件写入flash

    stm32程序升级SD读取hex文件写入flash
    发表于 11-20 12:36 59次下载
    stm32程序升级<b class='flag-5'>SD</b><b class='flag-5'>卡</b><b class='flag-5'>读取</b>hex<b class='flag-5'>文件</b>写入flash