使用FPGA播放音频文件

让我们看一下i2s规范,并尝试用fpga播放音频文件。
开篇第一步
inter-ic sound interface(简称i2s)是由飞利浦公司开发,用于通过不同ic之间的串行接口(例如从处理器到dac)传输数字音频数据。该接口使用以下信号进行数据传输:
sck (串行时钟)——用于数据传输的时钟。
sd (串行数据)- 每个数据字的各个位通过该线传输。
ws (字选择)- 定义传输数据字的长度。它用于标记右或左音频通道。
仅音频数据通过 i2s 传输。附加数据(例如各个总线用户的配置)通过其他接口传输。数据传输总是在两个总线之间沿一个方向进行,其中一路总线必须充当主机并负责生成时钟信号。在由多个发送器和接收器组成的复杂系统中,时钟信号由外部总线主控器生成,并且相应的发送器生成数据。
所有数据均以二进制补码和 msb 优先的方式传输。如果接收方和发送方的字宽存在正差(即一方的字宽小于另一方的字宽),则剩余位填充0。根据规范,数据可以同步于正时钟沿或负时钟沿,从而数据总是在负时钟沿读入。
ws信号选择活动通道,并将低或高相位内的所有数据分配给相应的通道:
ws = 0 – 通道 1(左)
ws = 1 – 通道 2(右)
‌ws信号必须在下一个数据字的 msb 之前的一个时钟周期发生变化,以便接收器可以将数据读入正确的通道。ws信号的时钟频率通常对应于音频信号的采样频率。
在这篇文章中,展示如何设计一个简单的 i2s 发射器,并使用 cs4344 立体声 d/a 转换器通过扬声器输出恒定的声音。
要输出的声音将存储在 fpga 的block memory中,并由发送器读出,并将数据发送到 d/a 转换器。整个项目分为三个部分,将逐步讨论:
集成系统时钟和i2s模块的top设计
集成rom和i2s发送器的i2s模块
i2s发送器
i2s发送器
设计的最底层应该是 i2s 发送器,其任务是通过 i2s 接口发送各个数据字。
该框图产生了以下发送器:
entity 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 i2s_transmitter;  
数据字的大小通过width参数定义。
三级状态机控制发送器,描述如下:
architecture i2s_transmitter_arch of i2s_transmitter is    type state_t is (state_reset, state_loadword, state_transmitword);    signal currentstate     : state_t      := state_reset;    signal tx_int  : std_logic_vector(((2 * width) - 1) downto 0)  := (others => '0');    signal ready_int        : std_logic    := '0';    signal lrclk_int        : std_logic    := '1';    signal sd_int           : std_logic    := '0';    signal enable           : std_logic    := '0';begin    process        variable bitcounter : integer := 0;    begin        wait until falling_edge(clock);        case currentstate is            when state_reset =>                ready_int <= '0';                lrclk_int <= '1';                enable <= '1';                sd_int <= '0';                tx_int  '0');                currentstate                 bitcounter := 0;                tx_int <= tx;                lrclk_int  (width - 1)) then                    lrclk_int <= '1';                end if;                if(bitcounter < ((2 * width) - 1)) then                    ready_int <= '0';                    currentstate <= state_transmitword;                else                    ready_int <= '1';                    currentstate <= state_loadword;                end if;                tx_int <= tx_int(((2 * width) - 2) downto 0) & 0;                sd_int <= tx_int((2 * width) - 1);        end case;        if(nreset = '0') then            currentstate <= state_reset;                end if;    end process;    ready <= ready_int;    sclk <= clock and enable;    lrclk <= lrclk_int;    sd  '0');    signal rom_data : std_logic_vector((width - 1) downto 0)       := (others => '0');    signal rom_address  : std_logic_vector(6 downto 0)             := (others => '0');    signal ready        : 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;    component sinerom is        port (  address : in std_logic_vector(6 downto 0);                clock   : in std_logic;                dataout : out std_logic_vector(15 downto 0)                );    end component sinerom;begin    transmitter : i2s_transmitter generic map(  width => width                                                )                                  port map(     clock => sclk_int,                                                nreset => nreset,                                                ready => ready,                                                tx => tx,                                                lrclk => lrclk,                                                sclk => sclk,                                                sd => sd                                                );    rom : sinerom port map (clock => mclk,                            address => rom_address,                            dataout => rom_data                            );    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                 wordcounter := 0;                currentstate                 if(ready = '1') then                    currentstate <= state_waitforstart;                else                    currentstate                 rom_address <= std_logic_vector(to_unsigned(wordcounter, rom_address'length));                tx <= x0000 & rom_data;                if(ready = '0') then                    currentstate <= state_increaseaddress;                else                    currentstate                 if(wordcounter < 99) then                    wordcounter := wordcounter + 1;                else                    wordcounter := 0;                end if;                currentstate <= state_waitforready;        end case;        if(nreset = '0') then            currentstate <= state_reset;        end if;    end process;end i2s_arch;  
第一个过程用于从mclk生成发送器所需的时钟信号sclk 。
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  clock,                                        nreset => nreset,                                        mclk => mclk_dcm,                                        locked => locked                                        );    i2s_module : i2s generic map (  ratio => ratio,                                    width => width                                    )                          port map ( mclk => mclk_dcm,                                     nreset => nsystemreset,                                     lrclk => lrclk,                                     sclk => sclk,                                     sd => sd                                     );    nsystemreset <= nreset and locked;    led(0) <= nreset;    led(1) <= locked;    led(2) <= nsystemreset;    mclk <= mclk_dcm;end top_arch;  
最后就可以进行测试。理想情况下,d/a 转换器输出 480 hz 正弦信号。因为来自 rom 的信号模式的长度为 100 个样本,采样频率为 48 khz。可以用示波器检查总线和信号:
此外,还可以检查音频信号(示波器的 fft 功能是实现此目的的最佳工具)。
附件-coe
memory_initialization_radix=16;memory_initialization_vector=0000,0809,100a,17fb,1fd4,278d,2f1e,367f,3da9,4495,4b3b,5196,579e,5d4e,629f,678d,6c12,7029,73d0,7701,79bb,7bf9,7dba,7efc,7fbe,7fff,7fbe,7efc,7dba,7bf9,79bb,7701,73d0,7029,6c12,678d,629f,5d4e,579e,5196,4b3b,4495,3da9,367f,2f1e,278d,1fd4,17fb,100a,0809,0000,f7f7,eff6,e805,e02c,d873,d0e2,c981,c257,bb6b,b4c5,ae6a,a862,a2b2,9d61,9873,93ee,8fd7,8c30,88ff,8645,8407,8246,8104,8042,8001,8042,8104,8246,8407,8645,88ff,8c30,8fd7,93ee,9873,9d61,a2b2,a862,ae6a,b4c5,bb6b,c257,c981,d0e2,d873,e02c,e805,eff6,f7f7,  
下一篇文章,将向 i2s 发送器添加 axi-stream 接口,并将其与 zynq 的处理系统连接,播放 sd 卡中的音频文件。


PLC的软件设计步骤及设计举例
这款无人机凭什么被称之为新的VTOL呢?
关于世界上首座运营波农场的分析和介绍
常用二级管的特性及标识
真没有了!iPhone7内存16G版本不会发布
使用FPGA播放音频文件
德索讲新能源汽车高压连接器的技术概述
弘信电子与AI算力服务器合资,助力国产算力芯片落地
三星上线了一款亲民版无线快速充电底座,型号为EP-P1100
系统设计工程师不可不知的DRAM控制器核心结论
高温隧道炉是什么,它的优势都有哪些
法国数字部:已收到苹果iPhone 12的软件更新
航空货运已成为中国航空业中蓬勃发展的重大领域
怎样达到预防犯罪的目的
一文详解OpenWrt系统架构和其他系统架构的对比
总结10种复杂电路分析方法
华米科技宣布将于2020年1月7日举办新品发布会
大联大世平推出多款平板电脑解决方案
浅谈示波器X-Y模式 示波器触发模式及使用
rubik混部引擎的愿景、目标、设计原则