×

使用ZYBO从SD卡播放wav文件

消耗积分:0 | 格式:zip | 大小:0.00 MB | 2023-06-15

h1654155273.0656

分享资料个

描述

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

为此,AXIS_I2S创建了一个名为的新顶部设计。这个设计应该有如下界面:

pYYBAGNsXIiADptfAAA6hTKWlAI227.png
 

此块设计产生以下实体:

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;

SCLKMCKL的比率通过参数定义,RATIO每个通道的数据字宽度通过参数定义WIDTH

此实现仅支持每个通道 16 位数据字(即立体声 32 位)。以下代码必须适用于更大的总线宽度。

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

  • 用于为 I2S 发送器创建输入时钟的时钟预分频器
  • AXI-Stream 从接口
  • I2S 发送器的控制逻辑‌

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

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

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状态变为State_WaitForTransmitterReady等待来自 I2S 发送器的就绪信号的状态。一旦发送器准备就绪,TREADY_RXDAXI-Stream 接口的信号就会被设置,从而通知主机,从机已准备好接收数据。然后从站更改为State_WaitForValid状态。

‌在这种状态下,从机等待主机设置TVALID_RXD信号以标记有效数据。一旦信号被设置,数据就会被写入内部 FIFO。然后机器更改为State_WaitForTransmitterBusy状态。

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

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

  • ACLK的时钟域
  • MCLK的时钟域

一般来说,这两个时钟信号不能从时钟源(例如通过时钟分频器)生成,因为 AXI 接口通常以 100 MHz 运行,而音频接口需要可以巧妙地分频到采样频率的时钟速率,例如例如 12.288 MHz。结果,由于过多的最差负松弛 (WNS) 和总负松弛 (TNS) 在实施过程中发生时序错误:

poYBAGNsXIuAUbW3AABcOkpscO4430.png
 

此外,由于触发器的亚稳态发生在不同的时钟域中,导致数据不正确的风险非常高。发生亚稳态 a. 然后当触发器切换并且在那一刻数据发生变化时。

因此,各个时钟域使用的信号必须分别通过相应的电路传输到另一个时钟域。Xilinx 在文档UG953中描述了可用于此目的的相应宏。

  • xpm_cdc_gray - 此功能块使用格雷码将数据总线从一个时钟域 (src) 传输到另一个时钟域 (dest)。
  • xpm_cdc_single - 将单个信号从一个时钟域 (src) 转换到另一个时钟域 (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 发送器并传递生成的信号。

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 发送器的 AXI-Stream 接口现已准备就绪,可供使用。完整的代码现在应该如下所示:

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;

接下来,我们要使用此接口通过处理系统从 SD 卡中读取波形文件,并通过连接的扬声器使用 CS4344 D/A 转换器输出音乐。

该项目需要以下 IP 内核:

  • 带有 AXI-Stream 接口的 I2S 发送器
  • 从 SD 卡读取数据并将其写入 FIFO 的处理系统
  • AXI-Stream FIFO
  • 用于生成音频时钟的时钟向导
poYBAGNsXJWAWQWGAAFzFS9WHfY413.png
 

时钟向导生成时钟,然后用作 CS4344 的主时钟。输出时钟可以通过 AXI-Lite 接口适应音频文件的采样率。时钟向导使用 12.288 MHz 时钟初始化,用于 48 kHz 音频信号。

poYBAGNsXJmAdYItAAFECEdnobo694.png
 

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

pYYBAGNsXJuAWK_YAADIkwm0xJo335.png
 

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

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

poYBAGOIIreADpENAAFhBOxMkqY831.png
 

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

初始化完成后,立即调用该函数从 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;
}

该函数AudioPlayer_LoadFile调用该函数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;
}

在下一步中,根据使用的采样频率从波形文件中设置时钟向导的输出频率:

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

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

然后程序流程的其余部分发生在 FIFO 的回调中:

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

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

现在需要一个波形文件。存储库中提供了简单的测试信号,或者可以在例如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!
pYYBAGOIIrqAHw9kAADkg8R_eCg272.png
 

或使用立体声音频:

-----------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!
pYYBAGOIIryAf51CAAEVrD4XhNw780.png
 

 


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

评论(0)
发评论

下载排行榜

全部0条评论

快来发表一下你的评论吧 !

'+ '

'+ '

'+ ''+ '
'+ ''+ ''+ '
'+ ''+ '' ); $.get('/article/vipdownload/aid/'+webid,function(data){ if(data.code ==5){ $(pop_this).attr('href',"/login/index.html"); return false } if(data.code == 2){ //跳转到VIP升级页面 window.location.href="//m.lene-v.com/vip/index?aid=" + webid return false } //是会员 if (data.code > 0) { $('body').append(htmlSetNormalDownload); var getWidth=$("#poplayer").width(); $("#poplayer").css("margin-left","-"+getWidth/2+"px"); $('#tips').html(data.msg) $('.download_confirm').click(function(){ $('#dialog').remove(); }) } else { var down_url = $('#vipdownload').attr('data-url'); isBindAnalysisForm(pop_this, down_url, 1) } }); }); //是否开通VIP $.get('/article/vipdownload/aid/'+webid,function(data){ if(data.code == 2 || data.code ==5){ //跳转到VIP升级页面 $('#vipdownload>span').text("开通VIP 免费下载") return false }else{ // 待续费 if(data.code == 3) { vipExpiredInfo.ifVipExpired = true vipExpiredInfo.vipExpiredDate = data.data.endoftime } $('#vipdownload .icon-vip-tips').remove() $('#vipdownload>span').text("VIP免积分下载") } }); }).on("click",".download_cancel",function(){ $('#dialog').remove(); }) var setWeixinShare={};//定义默认的微信分享信息,页面如果要自定义分享,直接更改此变量即可 if(window.navigator.userAgent.toLowerCase().match(/MicroMessenger/i) == 'micromessenger'){ var d={ title:'使用ZYBO从SD卡播放wav文件',//标题 desc:$('[name=description]').attr("content"), //描述 imgUrl:'https://'+location.host+'/static/images/ele-logo.png',// 分享图标,默认是logo link:'',//链接 type:'',// 分享类型,music、video或link,不填默认为link dataUrl:'',//如果type是music或video,则要提供数据链接,默认为空 success:'', // 用户确认分享后执行的回调函数 cancel:''// 用户取消分享后执行的回调函数 } setWeixinShare=$.extend(d,setWeixinShare); $.ajax({ url:"//www.lene-v.com/app/wechat/index.php?s=Home/ShareConfig/index", data:"share_url="+encodeURIComponent(location.href)+"&format=jsonp&domain=m", type:'get', dataType:'jsonp', success:function(res){ if(res.status!="successed"){ return false; } $.getScript('https://res.wx.qq.com/open/js/jweixin-1.0.0.js',function(result,status){ if(status!="success"){ return false; } var getWxCfg=res.data; wx.config({ //debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。 appId:getWxCfg.appId, // 必填,公众号的唯一标识 timestamp:getWxCfg.timestamp, // 必填,生成签名的时间戳 nonceStr:getWxCfg.nonceStr, // 必填,生成签名的随机串 signature:getWxCfg.signature,// 必填,签名,见附录1 jsApiList:['onMenuShareTimeline','onMenuShareAppMessage','onMenuShareQQ','onMenuShareWeibo','onMenuShareQZone'] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2 }); wx.ready(function(){ //获取“分享到朋友圈”按钮点击状态及自定义分享内容接口 wx.onMenuShareTimeline({ title: setWeixinShare.title, // 分享标题 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享给朋友”按钮点击状态及自定义分享内容接口 wx.onMenuShareAppMessage({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 type: setWeixinShare.type, // 分享类型,music、video或link,不填默认为link dataUrl: setWeixinShare.dataUrl, // 如果type是music或video,则要提供数据链接,默认为空 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享到QQ”按钮点击状态及自定义分享内容接口 wx.onMenuShareQQ({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享到腾讯微博”按钮点击状态及自定义分享内容接口 wx.onMenuShareWeibo({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享到QQ空间”按钮点击状态及自定义分享内容接口 wx.onMenuShareQZone({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); }); }); } }); } function openX_ad(posterid, htmlid, width, height) { if ($(htmlid).length > 0) { var randomnumber = Math.random(); var now_url = encodeURIComponent(window.location.href); var ga = document.createElement('iframe'); ga.src = 'https://www1.elecfans.com/www/delivery/myafr.php?target=_blank&cb=' + randomnumber + '&zoneid=' + posterid+'&prefer='+now_url; ga.width = width; ga.height = height; ga.frameBorder = 0; ga.scrolling = 'no'; var s = $(htmlid).append(ga); } } openX_ad(828, '#berry-300', 300, 250);