ARM技术论坛
直播中

正点原子运营官

5年用户 1793经验值
擅长:模拟技术 嵌入式技术 控制/MCU
私信 关注
[资料]

正点原子开拓者FPGA开发板资料连载第四十八章基于以太网的板对板音频互传实验

1)实验平台:正点原子开拓者FPGA 开发板
2)摘自《开拓者FPGA开发指南》关注官方微信号公众号,获取更多资料:正点原子
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-13912-1-1.html


第四十八章 基于以太网的板对板音频互传实验
在音频环回实验中,我们成功地在开发板上实现音频的采集与播放功能;在以太网通信
验中,我们通过网口调试助手成功地和开发板完成了以太网通信的功能。本章我们将使用以太
网接口实现两块FPGA开发板之间的音频互传与播放功能。
本章包括以下几个部分:
48.1 板对板音频互传简介
48.2 实验任务
48.3 硬件设计
48.4 程序设计
48.5 下载验证
板对板音频互传简介
音频板对板互传实验是基于以太网来传输音频数据的,我们在“以太网通信实验”章节中
对以太网的协议、MII时序等内容作了详细的介绍,如果大家对这部分内容不是很熟悉的话,
请参考“以太网通信实验”中的以太网简介部分。
从“音频环回实验”章节中可以知道,WM8978音频芯片的采样率最大为48Khz,在数据位
宽 为 32 位 数 据 格 式 下 , 每 秒 钟 传 输 的 数 据 量 为 48000*32*2 ( 左 右 两 个 声 道 ) =
3072000bit≈2.930Mbit。我们FPGA开发板上的PHY芯片类型为百兆以太网,理论上最大传输速
率为 100Mbit/s,即使加上帧头、CRC校验以及帧间隙带来的额外开销,对于实时传输音频数
据来说也是毫无压力的。我们知道,以太网通信是以数据包为单位进行数据传输,单包数据除
以太网帧头与CRC校验之外,以太网数据至少为46个有效字节,对于不足46个字节的数据要在
数据的后面补充任意值。如果我们使用以太网通信单包只传输一个32位的音频数据,那么会极
大的浪费以太网的高速传输能力,因此我们先将收到的音频数据使用fifo缓存下来,待数据量
达到预设值之后,再通过以太网发送出去。
实验任务
本节实验任务是使用以太网接口实现两块FPGA开发板之间的音频互传与播放功能。首先开
发板A对WM8978芯片进行音频数据采集,并将采集的数据通过以太网接口发送给开发板B,开发
板B将收到的数据通过WM8978芯片进行播放;开发板B同样对WM8978芯片进行音频数据采集,并
将采集的数据通过以太网接口发送给开发板A,开发板A将收到的数据通过WM8978芯片进行播放,
从而实现两块FPGA开发板之间的音频互传与播放功能。
硬件设计
WM8978音频芯片及音频接口原理图与“音频环回实验”完全相同,请参考“音频环回实验”
的硬件设计部分。以太网接口部分的硬件设计请参考“以太网通信实验”中的硬件设计部分。
由于以太网接口和WM8978音频引脚数目较多且在前面相应的章节中已经给出它们的管脚
列表,这里不再列出管脚分配。
程序设计
图 48.4.1是根据本章实验任务画出的系统框图。PLL时钟模块为WM8978音频芯片提供主时
钟,而UDP模块的驱动时钟是由开发板上的PHY芯片提供;WM8978配置模块用于初始化WM8978音
频芯片,使其能够在预设的工作模式下工作;音频接收模块用于接收来自WM8978的音频数据,将WM8978串行输入的1位数据转换成32位的并行数据;音频缓存发送控制模块用于缓存32位的
音频数据,当缓存的数据量达到预设值之后,控制以太网发送模块开始发送音频数据。以太网
接收模块负责接收另一块开发板传输的音频数据,并将接收到的数据写入音频缓存接收控制模
块,音频缓存接收控制模块负责缓存以太网接收到的音频数据,将数据存入fifo模块等待被音
频发送模块读取,音频发送模块发送音频数据,将并行输入的32位数据转成1位串行数据发送
出去。
板对板音频互传实验系统框图如下图所示:


图 48.4.1 基于以太网的板对板音频互传系统框图
顶层模块的原理图如下图所示:


图 48.4.2 顶层模块原理图
由上图可知,FPGA顶层模块(eth_audio_transmit)例化了以下五个模块:PLL时钟模块
(pll_clk)、WM8978控制模块(wm8978_ctrl)、音频缓存发送控制模块(audio_cache_tx_ctrl)、
UDP模块(udp)和音频缓存接收控制模块(audio_cache_rx_ctrl)。
PLL时钟模块(pll_clk):PLL时钟模块通过调用锁相环(PLL)IP核来实现,输出1个频
率为12Mhz的时钟,作为WM8978的主时钟MCLK。
WM8978控制模块(wm8978_config):WM8978控制模块完成了WM8978的初始化配置、音频
数据采集和音频数据发送的功能,该模块例化了WM8978配置模块(wm8978_config)、音频接
收模块(audio_receive)、音频发送模块(audio_send),其中WM8978配置模块例化了IIC配
置模块(i2c_reg_cfg)和IIC驱动模块(i2c_dri)。有关该模块的详细介绍请大家参考“音
频环回实验”章节。
音频缓存发送控制(audio_cache_tx_ctrl):音频缓存发送控制模块用于缓存32位的音
频数据,当缓存的数据量达到预设值之后,控制以太网发送模块开始发送音频数据。
UDP模块(udp):UDP模块实现以太网通信的收发功能,该模块内部例化了以太网接收模
块(ip_receive)、以太网发送模块(ip_send)和CRC32校验模块(crc32_d4)。有关该模块
的详细介绍请大家参考“以太网通信实验”章节。
音频缓存接收控制(audio_cache_rx_ctrl):音频缓存接收控制模块负责缓存以太网接
收到的音频数据,将数据存入fifo模块等待被WM8978控制模块读取。
顶层模块代码如下:
1 module eth_audio_transmit(
2 input sys_clk , //系统时钟
3 input sys_rst_n , //系统复位信号,低电平有效
4 //以太网接口
5 input eth_rx_clk , //MII接收数据时钟
6 input eth_rxdv , //MII输入数据有效信号
7 input eth_tx_clk , //MII发送数据时钟
8 input [3:0] eth_rx_data , //MII输入数据
9 output eth_tx_en , //MII输出数据有效信号
10 output [3:0] eth_tx_data , //MII输出数据
11 output eth_rst_n , //以太网芯片复位信号,低电平有效
12 //wm8978 interface
13 //audio interface(master mode)
14 input aud_bclk , // WM8978位时钟
15 input aud_lrc , // 对齐信号
16 input aud_adcdat , // 音频输入
17 output aud_mclk , // WM8978的主时钟(最大为12.288MHz)
18 output aud_dacdat , // 音频输出
19 //control interface
20 output aud_scl , // WM8978的SCL信号
21 inout aud_sda // WM8978的SDA信号
22 );
23
24 //parameter define
25 //这里DES_IP=BOARD_IP,这样两块开发板可以用同一个程序实现互传音频播放
26 //开发板MAC地址 00-11-22-33-44-55
27 parameter BOARD_MAC = 48'h00_11_22_33_44_55;
28 //开发板IP地址 192.168.1.123
29 parameter BOARD_IP = {8'd192,8'd168,8'd1,8'd123};
30 //目的MAC地址 ff_ff_ff_ff_ff_ff
31 parameter DES_MAC = 48'hff_ff_ff_ff_ff_ff;
32 //目的IP地址 192.168.1.102
33 parameter DES_IP = {8'd192,8'd168,8'd1,8'd123};
34 //wire define
35 wire rst_n ;
36 wire locked ;
37
38 wire aud_rx_done ; //音频数据接收完成信号
39 wire [31:0] adc_data ; //接收到的音频数据
40 wire udp_tx_start_en ; //以太网开始发送信号
41 wire [15:0] udp_tx_byte_num ; //以太网发送的有效字节数
42 wire [31:0] udp_tx_data ; //以太网发送的数据
43 wire udp_rec_pkt_done; //以太网单包数据接收完成信号
44 wire udp_rec_en ; //以太网接收使能信号
45 wire [31:0] udp_rec_data ; //以太网接收到的数据
46 wire udp_tx_req ; //以太网发送请求数据信号
47 wire udp_tx_done ; //以太网发送完成信号
48 wire aud_tx_done ; //音频发送完成信号
49 wire [31:0] dac_data ; //音频dac数据
50
51 //*****************************************************
52 //** main code
53 //*****************************************************
54
55 assign rst_n = sys_rst_n & locked;
56
57 //锁相环
58 pll_clk u_pll_clk(
59 .inclk0 (sys_clk),
60 .areset (~sys_rst_n),
61 .c0 (aud_mclk),
62 .locked (locked)
63 );
64
65 //WM89878模块
66 wm8978_ctrl
67 #(
68 .WL (6'd32 ) //word length音频字长定义
69 )
70 u_wm8978_ctrl(
71 //system clock
72 .clk (sys_clk ),
73 .rst_n (rst_n ),
74 //wm8978 interface
75 //audio interface(master mode)
76 .aud_bclk (aud_bclk ),
77 .aud_lrc (aud_lrc ),
78 .aud_adcdat (aud_adcdat ),
79 .aud_dacdat (aud_dacdat ),
80 //control interface
81 .aud_scl (aud_scl ),
82 .aud_sda (aud_sda ),
83 //user interface
84 .dac_data (dac_data ),
85 .adc_data (adc_data ),
86 .rx_done (aud_rx_done),
87 .tx_done (aud_tx_done)
88 );
89
90 //音频缓存发送控制
91 audio_cache_tx_ctrl u_audio_cache_tx_ctrl(
92 .aud_bclk (aud_bclk),
93 .rst_n (rst_n),
94 .aud_rx_done (aud_rx_done),
95 .aud_adc_data (adc_data),
96 .eth_tx_clk (eth_tx_clk),
97 .udp_tx_req (udp_tx_req),
98 .udp_tx_done (udp_tx_done),
99 .udp_tx_start_en (udp_tx_start_en),
100 .udp_tx_byte_num (udp_tx_byte_num),
101 .udp_tx_data (udp_tx_data)
102 );
103
104 //UDP模块
105 udp
106 #(
107 .BOARD_MAC (BOARD_MAC), //参数例化
108 .BOARD_IP (BOARD_IP ),
109 .DES_MAC (DES_MAC ),
110 .DES_IP (DES_IP )
111 )
112 u_udp(
113 .eth_rx_clk (eth_rx_clk ),
114 .rst_n (rst_n ),
115 .eth_rxdv (eth_rxdv ),
116 .eth_rx_data (eth_rx_data),
117 .eth_tx_clk (eth_tx_clk ),
118 .tx_start_en (udp_tx_start_en),
119 .tx_data (udp_tx_data),
120 .tx_byte_num (udp_tx_byte_num),
121 .tx_done (udp_tx_done),
122 .tx_req (udp_tx_req ),
123 .rec_pkt_done (udp_rec_pkt_done),
124 .rec_en (udp_rec_en),
125 .rec_data (udp_rec_data),
126 .rec_byte_num (),
127 .eth_tx_en (eth_tx_en ),
128 .eth_tx_data (eth_tx_data),
129 .eth_rst_n (eth_rst_n )
130 );
131
132 //音频缓存接收控制
133 audio_cache_rx_ctrl u_audio_cache_rx_ctrl(
134 .eth_rx_clk (eth_rx_clk),
135 .rst_n (rst_n),
136 .udp_rec_pkt_done (udp_rec_pkt_done),
137 .udp_rec_en (udp_rec_en),
138 .udp_rec_data (udp_rec_data),
139 .aud_bclk (aud_bclk),
140 .aud_dac_req (aud_tx_done),
141 .dac_data (dac_data)
142 );
143
144 endmodule
在代码的第25至第33行定义了四个参量:开发板MAC地址BOARD_MAC、开发板IP地址
BOARD_IP、目的MAC地址DES_MAC和目的IP地址DES_IP。需要注意的是,如果目的IP地址和开发
板IP地址不一致或者目的MAC(公共MAC地址除外)地址和开发板MAC地址不一致的话,以太网
接收模块会直接丢掉数据,导致接收音频数据失败。因此目的MAC地址这里写的是公共MAC地址
(48'hff_ff_ff_ff_ff_ff),目的IP地址写的是和开发板IP地址相同的值,目的是为了让同
一程序可以下载在两个开发板中。
在代码的第85至第86行代码中,aud_rx_done(音频数据接收完成信号)和adc_data(接
收到的音频数据)写入音频缓存发送控制模块,该模块输出的udp_tx_start_en(以太网开始
发送信号)用于控制以太网发送模块开始传输音频数据。UDP模块输出的udp_rec_en(以太网
接收数据有效信号)和udp_rec_data(以太网接收到的数据)写入音频缓存接收控制模块。
WM8978控制模块输出的aud_tx_done(音频数据发送完成)信号作为音频缓存接收控制模块的
读请求信号,并将读取后的数据dac_data通过引脚aud_dacdat引脚发送出去。
音频缓存发送控制模块用于缓存32位的音频数据,当缓存的数据量达到预设值之后,控制
以太网发送模块开始发送音频数据。该模块代码如下所示:
1 module audio_cache_tx_ctrl(
2 input aud_bclk , //WM8978位时钟
3 input rst_n , //复位信号,低电平有效
4 input aud_rx_done , //音频数据接收完成信号
5 input [31:0] aud_adc_data , //32位音频数据
6
7 input eth_tx_clk , //以太网发送时钟
8 input udp_tx_req , //以太网发送请求数据信号
9 input udp_tx_done , //以太网发送完成信号
10 output reg udp_tx_start_en, //以太网开始发送信号
11 output [15:0] udp_tx_byte_num, //以太网发送的字节数
12 output [31:0] udp_tx_data //以太网发送的数据
13 );
14
15 //parameter define
16 //fifo缓存的数量大于等于此值时控制udp开始发送数据
17 parameter AUDIO_TX_NUM = 9'd256;
18
19 //reg define
20 reg udp_tx_flag ; //udp正在发送数据的标志
21
22 //wire define
23 wire [8:0] data_cnt; //fifo中缓存的个数
24
25 //*****************************************************
26 //** main code
27 //*****************************************************
28
29 //以太网发送的字节数(1个32位音频数据 = 4个字节),即udp_tx_byte_num = AUDIO_TX_NUM * 4
30 assign udp_tx_byte_num = {AUDIO_TX_NUM,2'd0};
31
32 //判断fifo中缓存的个数,超过预设值控制udp开始发送数据
33 always @(posedge eth_tx_clk or negedge rst_n) begin
34 if(rst_n == 1'b0) begin
35 udp_tx_flag <= 1'b0;
36 udp_tx_start_en <= 1'b0;
37 end
38 else begin
39 udp_tx_start_en <= 1'b0;
40 //只有当udp没有发送数据时才判断fifo大小是否满足发送条件
41 if(udp_tx_flag == 1'b0) begin
42 if(data_cnt >= AUDIO_TX_NUM) begin
43 udp_tx_flag <= 1'b1;
44 udp_tx_start_en <= 1'b1; //udp开始发送信号
45 end
46 end
47 else if(udp_tx_done) //udp发送完成后,将udp发送标志清零
48 udp_tx_flag <= 1'b0;
49 end
50 end
51
52 //异步fifo
53 async_fifo_512x32b u_async_fifo(
54 .aclr (~rst_n),
55 .data (aud_adc_data),
56 .rdclk (eth_tx_clk),
57 .rdreq (udp_tx_req),
58 .wrclk (aud_bclk),
59 .wrreq (aud_rx_done),
60 .q (udp_tx_data),
61 .rdempty (),
62 //注意rdusedw为读时钟下的计数,如果需要在写时钟下读取数据时,在建立fifo时选择wrusedw
63 .rdusedw (data_cnt), //fifo缓存的个数
64 .wrfull ()
65 );
66
67 endmodule
在代码的第17行定义了参数AUDIO_TX_NUM(单包发送音频数据个数),当以太网没有在发
送数据时,判断data_cnt(fifo中缓存的个数)的值是否大于等于AUDIO_TX_NUM值,当大于等
于此值时,开始通知以太网发送数据,发送的字节数为AUDIO_TX_NUM的4倍(32bit=4个字节)。
AUDIO_TX_NUM的值在这里设置为256,设置成其它值也是可以的。需要注意的是不建议单
包发送的音频数据个数太小或者太大,单包发送音频数据量太小传输效率低,太大会造成音频
传输的延时。需要注意的是,如果单包发送的数据量增加,fifo的深度也要根据缓存量相应增
加,否则fifo写满溢出,导致音频数据丢失。
图 48.4.3为音频缓存发送模块SignalTap抓取的波形图,当rdusedw(fifo中缓存的个数,
同data_cnt)计数达到256之后,udp_tx_start_en(以太网开始发送信号)开始输出一个脉冲
信号,发送的有效字节个数为1024(256*4)个字节。


图 48.4.3 音频缓存发送模块SignalTap波形图
音频缓存接收控制模块负责缓存以太网接收到的音频数据,将数据存入fifo模块等待被音
频发送模块读取。该模块代码如下所示:
1 module audio_cache_rx_ctrl(
2 input eth_rx_clk , //以太网接收时钟
3 input rst_n , //复位信号,低电平有效
4 input udp_rec_pkt_done, //以太网单包数据接收完成信号
5 input udp_rec_en , //以太网接收数据使能信号
6 input [31:0] udp_rec_data , //以太网接收到的数据
7
8 input aud_bclk , //WM8978位时钟
9 input aud_dac_req , //dac数据请求信号
10 output [31:0] dac_data //dac值
11 );
12
13 //reg define
14 reg rec_done_flag ; //单包数据接收完成后给出标志
15 reg rec_done_flag_d0; //异步信号打拍处理
16 reg rec_done_flag_d1; //异步信号打拍处理
17
18 wire fifo_rd_req ; //fifo读请求信号
19
20 //*****************************************************
21 //** main code
22 //*****************************************************
23
24 //接收完单包数据后再开始读fifo,防止fifo为空时被读取
25 assign fifo_rd_req = aud_dac_req & rec_done_flag_d1;
26
27 //接收完单包数据后给出标志
28 always @(posedge eth_rx_clk or negedge rst_n) begin
29 if(rst_n == 1'b0)
30 rec_done_flag <= 1'b0;
31 else if(udp_rec_pkt_done)
32 rec_done_flag <= 1'b1;
33 end
34
35 //异步信号打拍处理
36 always @(posedge aud_bclk or negedge rst_n) begin
37 if(rst_n == 1'b0) begin
38 rec_done_flag_d0 <= 1'b0;
39 rec_done_flag_d1 <= 1'b0;
40 end
41 else begin
42 rec_done_flag_d0 <= rec_done_flag;
43 rec_done_flag_d1 <= rec_done_flag_d0;
44 end
45 end
46
47 //异步fifo
48 async_fifo_512x32b u_async_fifo(
49 .aclr (~rst_n),
50 .data (udp_rec_data),
51 .rdclk (aud_bclk),
52 .rdreq (fifo_rd_req),
53 .wrclk (eth_rx_clk),
54 .wrreq (udp_rec_en),
55 .q (dac_data),
56 .rdempty (),
57 .rdusedw (),
58 .wrfull ()
59 );
60
61 endmodule
在代码的第27行开始的always语句块中,当接收完单包数据之后,rec_done_flag(单包
数据接收完成后给出的标志)信号拉高;由于rec_done_flag信号对于aud_bclk时钟来说是异
步信号,因此该信号通过延时打拍的方式同步到aud_bclk时钟下。在代码的第25行,fifo必须
在rec_done_flag_d1为高电平之后,即接收完单包数据之后才开始读取数据,防止在fifo为空
时进行读操作。
下载验证
首先我们打开基于以太网的板对板音频互传实验工程,在工程所在的路径下打开
eth_audio_transmit/par文件夹,在里面找到“eth_audio_transmit.qpf”并双击打开。注意
工程所在的路径名只能由字母、数字以及下划线组成,不能出现中文、空格以及特殊字符等。
工程打开后如图 48.5.1所示:


图 48.5.1 基于以太网的板对板音频互传实验工程
然后将下载器一端连电脑,另一端与开发板上对应端口连接;将音频连接线的一端连接至电脑或手机的音频输出端口,另一端连接至其中一块开发板的WM8978的LINE_IN接口,并将耳
机连接至另一开发板的PHONE接口;网线的两端分别接在两个开发板上的以太网接口,最后连
电源线并打开电源开关。
接下来我们下载程序,验证音频互传与播放的功能。工程打开后通过点击工具栏中的
“Programmer” 图 标 打 开 下 载 界 面 , 通 过 “Add File” 按钮选择
eth_audio_transmit/par/output_files目录下的“eth_audio_transmit.sof”文件。开发板
电源打开后,在程序下载界面点击“Hardware Setup”,在弹出的对话框中选择当前的硬件连
接为“USB-Blaster[USB-0]”。然后点击“Start”将工程编译完成后得到的sof文件下载到开
发板中,如图 48.5.所示:


图 48.5.2 程序下载完成界面
程序下载完成后并且硬件连接无误的话,我们可以看到两块开发板上以太网接口的灯会不
停地闪烁,说明此时两块开发板正在互传音频。如果音频连接线的另一端已经连接至电脑或手
机的音频输出端口,此时打开音乐,就可以听到另一块开发板上喇叭播放的音乐。戴上耳机,
也能听到耳机播放的音乐,说明音频互传实验验证成功。





更多回帖

发帖
×
20
完善资料,
赚取积分