让我们看一下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混部引擎的愿景、目标、设计原则