1)实验平台:正点原子开拓者
FPGA 开发板
2)摘自《开拓者FPGA开发指南》关注官方微信号公众号,获取更多资料:正点原子
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-13912-1-1.html
第三十四章 录音机实验
在“音频环回”实验中,我们成功地使用WM8978实现了音频环回的功能。本章我们将使用
开拓者FPGA开发板上的WM8978实现录音的功能。
本章包括以下几个部分:
34.1 WM8978简介
34.2 实验任务
34.3 硬件设计
34.4 程序设计
34.5 下载验证
WM8978简介
我们在“音频环回实验”中对WM8978的寄存器配置以及接口时序等内容作了详细的介绍。
如果大家对这部分内容不是很熟悉的话,请参考“音频环回实验”中的WM8978简介部分。
我们这里主要讲一下寄存器的配置,和“音频环回实验”相比,只需要修改部分配置就可
以实现录音的功能,录音功能配置的完整寄存器如下:
1.寄存器R0(00h),该寄存器用于控制WM8978的软复位,写任意值到该寄存器地址,即
可实现WM8978的软复位。
2.寄存器R1(01h),该寄存器需要设置VMIDSEL(bit[1:0])为2’b11,开启最快启动;
BUFIOEN(bit2)为1,避免输入输出直接在WM8978内部环回;BIASEN(bit3)为1,模拟部分
的放大器才会工作,才可以听到声音;MICBEN(bit4)设置为1,使能MIC功能以实现录音;PLLEN
(bit5)为1使能WM8978内部PLL功能,使WM8978内部的主时钟为12.288MHz。
3.寄存器R2(02h),该寄存器需要设置ROUT1EN(bit8)、LOUT1EN(bit7)为1,使能耳机输
出;INPPGAENL(bit2)、INPPGAENR(bit3)为1,使能左右声道进入PGA;ADCENR(bit1)、
ADCENL(bit0)为1,使能左右声道的ADC功能。
4.寄存器R3(03h),该寄存器要设置LOUT2EN(bit6),ROUT2EN(bit5),RMIXER(bit3),
LMIXER(bit2),DACENR(bit1)和DACENL(bit0)等6个位为1。LOUT2EN和ROUT2EN,设置为1,使
能喇叭输出;LMIXER和RMIXER设置为1,使能左右声道混合器;DACENL和DACENR则是使能左右
声道的DAC,使数字音频信号转换为模拟音频信号。
5.寄存器R4(04h),该寄存器要设置WL(bit[6:5])和FMT(bit [4:3]) 4个位。WL(bit[6:5])
用于设置字长(即设置音频数据有效位数),00表示16位音频,10表示24位音频;FMT(bit[4:3])
用于设置音频接口数据传输格式,我们设置为10,使用I2S音频数据格式传输音频数据。
6.寄存器R6(06h),该寄存器的MS(bit0)设置为1,使WM8978工作在主模式下,输出BCLK
和LRC给FPGA。
7.寄存器R7(07h),该寄存器我们要设置采样率SR(bit[3:1])为000,使用48kHz的采
样率;设置SLOWCLKEN(bit0)为1,使能零交叉功能。
8.寄存器R10(0Ah),该寄存器我们要设置DACOSR128(bit3)为1,DAC得到最好的SNR。
9.寄存器R14(0Eh),该寄存器我们要设置ADCOSR128(bit3)为1, ADC得到最好的SNR。
10.寄存器R43(2Bh),该寄存器我们只需要设置INVROUT2(bit4)为1即可,反转ROUT2
输出,更好的驱动喇叭。
11.寄存器R44(2Ch),该寄存器按照默认设置即可。
12.寄存器R45(2Dh)和寄存器R46(2Eh),这两个寄存器设置类似,一个用于设置左声
道输入PGA的音量(bit[5:0]),一个用于设置右声道输入PGA的音量(bit[5:0]);并都使能
零交叉(bit7)。
13.寄存器R47(2Fh)和寄存器R48(30h),这两个寄存器设置类似,都设置PGABOOSTL(bit8),
使输入PGA增益达到最大。
14.寄存器R49(31h),该寄存器我们要设置SPKBOOST(bit2)和TSDEN(bit1)这两个位。
SPKBOOST用于设置喇叭的增益,我们设置为1(gain=+1.5)以获得更大的声音;TSDEN用于设
置过热保护,设置为1(开启)即可。
15.寄存器R50(32h)和 R51(33h),这两个寄存器一个用于设置左声道(R50),另外
一个用于设置右声道(R51)。我们只需要设置这两个寄存器的最低位为1即可,将左右声道的
DAC输出接入左右声道混合器里面,才能在耳机/喇叭听到音乐。
16.寄存器R52(34h)和R53(35h),这两个寄存器用于设置耳机音量,同样一个用于设
置左声道(R52),另外一个用于设置右声道(R53)。这两个寄存器的最高位(HPVU)用于设
置是否更新左右声道的音量,最低6位用于设置左右声道的音量,我们可以先设置好两个寄存
器的音量值,最后设置其中一个寄存器最高位为1,即可更新音量设置。
17.寄存器R54(36h)和R55(37h),这两个寄存器用于设置喇叭音量,同R52,R53设置
一模一样,这里就不细说了。
以上,就是我们用WM8978录音时的设置,按照以上所述,对各个寄存器进行相应的配置,
即可使用WM8978录音了。
实验任务
本节实验任务是使用开拓者FPGA开发板上的WM8978实现录音机的功能:按下KEY2按键时开
始录音,松开时结束录音;按下KEY1按键时开始播放录音(录音文件用SDRAM存储)。
硬件设计
音频WM8978接口部分的硬件设计与“音频环回实验”完全相同,请参考“音频环回实验”
中的硬件设计部分。
本实验中,部分端口信号的管脚分配如下表所示,SDRAM的端口参见“SDRAM读写实验”:
表 34.3.1 录音机实验管脚分配
程序设计
根据实验任务,我们可以大致规划出系统的控制流程:FPGA首先通过控制接口配置WM8978
相关的寄存器,当按下录音键时FPGA接收WM8978传输过来的录音音频数据,并将接收到的音频
数据送入SDRAM中临时存储,松开录音按键时结束录音;当按下回放按键时,FPGA从SDRAM读取
录音的音频数据,并将其传递给WM8978发送出去。由此画出系统的功能框图如下所示:
图 34.4.1 WM8978录音实验系统框图
由系统框图可知,FPGA程序设计部分包括六个模块,顶层模块(audio_record)、PLL时
钟模块(pll_clk)、SDRAM控制器模块(sdram_top)、录音控制模块(record_ctrl)、消抖
模块(key_debounce)、WM8978控制模块(wm8978_ctrl)。各模块功能如下:
顶层模块(audio_record):在顶层模块中完成对另外五个模块的例化,并实现各模块控
制及数据信号的交互。PLL时钟模块分别为WM8978音频芯片提供主时钟,为SDRAM控制器模块提
供驱动时钟;按键消抖模块(key_debounce)消除按键的抖动;录音控制模块(record_ctrl)
将WM8978控制模块接收到的录音数据存入到SDRAM,并从SDRAM读取缓存的录音数据进行回放。
顶层模块的原理图如下图所示:
图 34.4.2 顶层模块原理图
PLL时钟模块(pll_clk):PLL时钟模块通过调用锁相环(PLL)IP核来实现,总共输出3
个时钟,频率分别为100Mhz、100Mhz(相位偏移-75度)和12Mhz时钟。100Mhz时钟和100Mhz(相
位偏移-75度)时钟作为SDRAM读写控制模块的驱动时钟,12Mhz时钟作为WM8978的主时钟。
SDRAM读写控制模块(sdram_top):SDRAM读写控制器模块负责驱动SDRAM片外存储器,缓
存WM8978接收到的录音音频数据;该模块将SDRAM复杂的读写操作封装成类似FIFO的用户接口,
非常方便用户的使用。有关该模块的详细介绍请大家参考“SDRAM 读写测试实验”章节。
录音控制模块(record_ctrl):录音控制模块用于控制录音的开始与结束以及录音的回
放。
消抖模块(key_debounce):按键消抖模块对输入的按键进行消抖,有关该模块的详细介
绍可参考“按键控制蜂鸣器实验”。
WM8978控制模块(wm8978_ctrl):WM8978控制模块主要完成WM8978的配置和WM8978接收
的录音音频数据的接收处理,以及FPGA发送的音频数据的发送处理。
顶层模块的代码如下:
1
module audio_record
(
2 //system clock
3
input sys_clk
, // 时钟信号
4
input sys_rst_n
, // 复位信号
5
6 //wm8978 interface
7 //audio interface(master mode)
8
input aud_bclk
, // WM8978位时钟
9
input aud_lrc
, // 对齐信号
10
input aud_adcdat
, // 音频输入
11
output aud_mclk
, // WM8978的主时钟(最大为12.288MHz)
12
output aud_dacdat
, // 音频输出
13 //control interface
14
output aud_scl
, // WM8978的SCL信号
15
inout aud_sda
, // WM8978的SDA信号
16 //SDROM interface
17
output sdram_clk
, // SDRAM 时钟
18
output sdram_cke
, // SDRAM 时钟有效
19
output sdram_cs_n
, // SDRAM 片选
20
output sdram_ras_n
, // SDRAM 行有效
21
output sdram_cas_n
, // SDRAM 列有效
22
output sdram_we_n
, // SDRAM 写有效
23
output [1
:0
] sdram_ba
, // SDRAM Bank地址
24
output [1
:0
] sdram_dqm
, // SDRAM 数据掩码
25
output [12
:0
] sdram_addr
, // SDRAM 地址
26
inout [15
:0
] sdram_data
, // SDRAM 数据
27
28 //user interface
29
input [3
:0
] key // key2按键控制录音、key1按键控制回放
30
);
31
32 //parameter define
33 //录音时长控制
tiME_RECORD = 48000*2*录音时间(秒)
34
parameter TIME_RECORD
= 24'd5760000
; // 60秒
35
36 //reg define
37
wire [31
:0
] dac_data
; // FPGA发送的音频数据
38
wire wr_en
; // SDRAM fifo接口写入数据使能
39
wire [15
:0
] wr_data
; // SDRAM fifo接口写入数据
40
wire rd_en
; // SDRAM fifo接口读出数据使能
41
42 //wire define
43
wire [15
:0
] rd_data
; // SDRAM fifo读出的数据
44
wire sdram_init_done
; // SDRAM 初始化完成信号
45
wire neg_play_key
; // 录音键的下降沿
46
wire wr_load
; // 写地址寄存 & fifo清空
47
wire rx_done
; // 音频数据接收完成
48
wire tx_done
; // 音频数据发送完成
49
wire record_key
; // 消抖后的key0
50
wire play_key
; // 消抖后的key1
51
wire clk_100m
; // sdram 控制时钟
52
wire clk_100m_shift
; // 用于提供给sdram芯片的同步时钟
53
wire clk_50m
; // 复位信号,低电平有效
54
wire locked
; // 锁相环稳定信号
55
wire rst_n
; // 复位信号
56
wire [31
:0
] adc_data
; // FPGA接收的音频数据
57
wire [ 3
:0
] key_value
; // 按键消抖后的输出
58
59 //*****************************************************
60 //** main code
61 //*****************************************************
62
63
assign rst_n
= sys_rst_n
& locked
;
64
65 //例化pll_clk
66 pll_clk u_pll_clk
(
67
.areset
(~sys_rst_n
), // pll_clk异步复位信号
68
.inclk0
(sys_clk
), // 输入sys_clk = 50 MHZ
69
.c0
(clk_100m
), // sdram 控制时钟
70
.c1
(clk_100m_shift
), // 用于提供给sdram芯片的
71
.c2
(aud_mclk
), // WM8978的MCLK信号(12MHz)
72
.locked
(locked
)
73
);
74
75 //例化消抖模块
76 key_debounce u_key_debounce
(
77
.sys_clk
(sys_clk
), // 外部50M时钟
78
.sys_rst_n
(rst_n
), // 复位信号,低有效
79
.key
(key
), // 外部按键输入
80
.key_flag
(), // 按键数据有效信号
81
.key_value
(key_value
) // 按键消抖后的数据
82
);
83
84 //例化WM89878控制模块
85 wm8978_ctrl u_wm8978_ctrl
(
86 //system clock
87
.clk
(sys_clk
), // 时钟信号
88
.rst_n
(rst_n
), // 复位信号
89 //wm8978 interface
90 //audio interface(master mode)
91
.aud_bclk
(aud_bclk
), // WM8978位时钟
92
.aud_lrc
(aud_lrc
), // 对齐信号
93
.aud_adcdat
(aud_adcdat
), // 音频输入
94
.aud_dacdat
(aud_dacdat
), // 音频输出
95 //control interface
96
.aud_scl
(aud_scl
), // WM8978的SCL信号
97
.aud_sda
(aud_sda
), // WM8978的SDA信号
98 //user interface
99
.dac_data
(dac_data
), // 输出的音频数据
100
.adc_data
(adc_data
), // 录音的数据
101
.rx_done
(rx_done
), // 一次接收完成
102
.tx_done
(tx_done
) // 一次发送完成
103
);
104
105 //SDRAM 控制器顶层模块,封装成FIFO接口
106 //SDRAM 控制器地址组成: {bank_addr[1:0],row_addr[12:0],col_addr[8:0]}
107 sdram_top u_sdram_top
(
108
.ref_clk
(clk_100m
), // sdram 控制器参考时钟
109
.out_clk
(clk_100m_shift
), // 用于输出的相位偏移时钟
110
.rst_n
(rst_n
), // 系统复位,低电平有效
111
112 //用户写端口
113
.wr_clk
(aud_bclk
), // 写端口FIFO: 写时钟
114
.wr_en
(wr_en
), // 写端口FIFO: 写使能
115
.wr_data
(wr_data
), // 写端口FIFO: 写数据
116
.wr_min_addr
(24'd0
), // 写SDRAM的起始地址
117
.wr_max_addr
(TIME_RECORD
), // 写SDRAM的结束地址
118
.wr_len
(10'd512
), // 写SDRAM时的数据突发长度
119
.wr_load
(wr_load
), // 写端口复位: 复位写地址,清空写FIFO
120
121 //用户读端口
122
.rd_clk
(aud_bclk
), // 读端口FIFO: 读时钟
123
.rd_en
(rd_en
), // 读端口FIFO: 读使能
124
.rd_data
(rd_data
), // 读端口FIFO: 读数据
125
.rd_min_addr
(24'd0
), // 读SDRAM的起始地址
126
.rd_max_addr
(TIME_RECORD
), // 读SDRAM的结束地址
127
.rd_len
(10'd512
), // 从SDRAM中读数据时的突发长度
128
.rd_load
(neg_play_key
), // 读端口复位: 复位读地址,清空读FIFO
129
130 //用户控制端口
131
.sdram_read_valid
(1'b1
), // SDRAM 读使能
132
.sdram_init_done
(sdram_init_done
), // SDRAM 初始化完成标志
133
134 //SDRAM 芯片接口
135
.sdram_clk
(sdram_clk
), // SDRAM 芯片时钟
136
.sdram_cke
(sdram_cke
), // SDRAM 时钟有效
137
.sdram_cs_n
(sdram_cs_n
), // SDRAM 片选
138
.sdram_ras_n
(sdram_ras_n
), // SDRAM 行有效
139
.sdram_cas_n
(sdram_cas_n
), // SDRAM 列有效
140
.sdram_we_n
(sdram_we_n
), // SDRAM 写有效
141
.sdram_ba
(sdram_ba
), // SDRAM Bank地址
142
.sdram_addr
(sdram_addr
), // SDRAM 行/列地址
143
.sdram_data
(sdram_data
), // SDRAM 数据
144
.sdram_dqm
(sdram_dqm
) // SDRAM 数据掩码
145
);
146
147 //例化录音控制模块
148 record_ctrl
#(.TIME_RECORD
(TIME_RECORD
)
149
) u_record_ctrl
(
150 //system clock
151
.clk
(aud_bclk
), // 时钟信号(12MHz)
152
.rst_n
(rst_n
), // 复位信号
153 //SDRAM interface
154
.wr_data
(wr_data
), // SDRAM fifo接口写入数据
155
.rd_data
(rd_data
), // SDRAM fifo读出的数据
156
.wr_en
(wr_en
), // SDRAM fifo接口写入数据使能
157
.wr_load
(wr_load
), // 写地址寄存 & fifo清空
158
.rd_en
(rd_en
), // SDRAM fifo接口读出数据使能
159
.sdram_init_done
(sdram_init_done
), // SDRAM 初始化完成信号
160 //user interface
161
.adc_data
(adc_data
), // FPGA接收的音频数据
162
.dac_data
(dac_data
), // FPGA发送的音频数据
163
.record_key
(key_value
[2
]), // 消抖后的key0做为录音按键
164
.play_key
(key_value
[1
]), // 消抖后的key1做为播放按键
165
.rx_done
(rx_done
), // 音频数据接收完成
166
.tx_done
(tx_done
), // 音频数据发送完成
167
.neg_play_key
(neg_play_key
) // 录音键的下降沿
168
);
169
170
endmodule
顶层模块中主要完成对其余模块的例化。在代码的第34行定义了参数TIME_RECORD,用于
设置最大的录音时长,由于WM8978的音频采样率为48KHz,分左右声道,所以当设置最大录音
时间为60秒时,TIME_RECORD参数值为48000 × 2 × 60 = 576000。在代码的第163行和第164行,
按键经过消抖模块后得到的key_valut[2]和 key_valut[1]分别为录音按键和回放按键。代码
的第113行和第122行,SDRAM控制器的读写时钟都由WM8978的位时钟aud_bclk驱动。
WM8978控制模块在“音频环回实验”已详细介绍,本实验主要是更改其内部I2C配置模块
寄存器的配置,寄存器配置部分的代码如下:
92
case(init_reg_cnt
)
93 // R0,软复位
94 5'd0
: i2c_data
<= {7'd0
,9'b1
};
95 // R1,设置BUFIOEN = 1,VMIDSEL[1:0]设置为:11(5K)
96 5'd1
: i2c_data
<= {7'd1
,9'b0_0000_0111
};
97 // R1,MICEN设置为1(MIC使能),BIASEN设置为1(模拟器工作),使能PLL(bit5)
98 5'd2
: i2c_data
<= {7'd1
,9'b0_0011_1111
};
99 // R2,使能IP PGA放大器,使能左右通道ADC;使能ROUT1,LOUT1
100 5'd3
: i2c_data
<= {7'd2
,9'b1_1000_1111
};
101 // R4,配置wm8978音频接口数据为I2S格式,字长度(wl)
102 5'd4
: i2c_data
<= {7'd4
,{2'd0
,wl
,5'b10000
}};
103 // R6,设置为MASTER MODE(BCLK和LRC输出)
104 5'd5
: i2c_data
<= {7'd6
,9'b0_0000_0001
};
105 // R7,使能slow clock使用零交叉
106 5'd6
: i2c_data
<= {7'd7
,9'b0_0000_0001
};
107 // R10,SOFTMUTE关闭,128x采样,最佳SNR
108 5'd7
: i2c_data
<= {7'd10
,9'b0_0000_1000
};
109 // R14,设置ADC过采样率为128x,以达到最好的性能
110 5'd8
: i2c_data
<= {7'd14
,9'b0_0000_1000
};
111 // R43,INVROUT2反向,驱动喇叭
112 5'd9
: i2c_data
<= {7'd43
,9'b0_0001_0000
};
113 // R44,设置LRIP2INPPGA、LRIN2INPPGA位,将左右通道差分输入接入INPGA
114 5'd10
: i2c_data
<= {7'd44
,9'b0_0011_0011
};
115 // R45,左声道输入PGA音量(调节麦克风增益)控制并使能零交叉bit7
116 5'd11
: i2c_data
<= {7'd45
,9'b0_1011_1111
};
117 // R46,右声道输入PGA音量(调节麦克风增益)控制并使能零交叉bit7
118 5'd12
: i2c_data
<= {7'd46
,9'b1_1011_1111
};
119 // R47,左通道输入增益控制
120 5'd13
: i2c_data
<= {7'd47
,9'b1_0000_0000
};
121 // R48,右通道输入增益控制
122 5'd14
: i2c_data
<= {7'd48
,9'b1_0000_0000
};
123 // R49,TSDEN(bit0),开启过热保护;SPKBOOST(bit2)1.5倍增益
124 5'd15
: i2c_data
<= {7'd49
,9'b0_0000_0110
};
125 // R50,选择左DAC输出至左输出混合器
126 5'd16
: i2c_data
<= {7'd50
,9'b1
};
127 // R51,选择右DAC输出至右输出混合器
128 5'd17
: i2c_data
<= {7'd51
,9'b1
};
129 // R52,耳机左声道音量设置(bit5:0),使能零交叉(bit7)
130 5'd18
: i2c_data
<= {7'd52
,{3'b010
,PHONE_VOLUME
}};
131 // R53,耳机右声道音量设置(bit5:0),使能零交叉(bit7),同步更新(HPVU=1)
132 5'd19
: i2c_data
<= {7'd53
,{3'b110
,PHONE_VOLUME
}};
133 // R54,喇叭左声道音量设置(bit5:0),使能零交叉(bit7)
134 5'd20
: i2c_data
<= {7'd54
,{3'b010
,SPEAK_VOLUME
}};
135 // R55,喇叭右声道音量设置(bit5:0),使能零交叉(bit7),同步更新(SPKVU=1)
136 5'd21
: i2c_data
<= {7'd55
,{3'b110
,SPEAK_VOLUME
}};
137 // R3,LOUT2,ROUT2输出使能(喇叭工作),RMIX,LMIX,DACENR、DACENL使能
138 5'd22
: i2c_data
<= {7'd3
,9'b0_0110_1111
};
139
default : ;
140
endcase
录音控制模块代码如下:
1
module record_ctrl
#(
2
parameter TIME_RECORD
= 24'd2880000 // 录音时长参数(48K*2*录音时间s)
3
)(
4 //system clock
5
input clk
, // 时钟信号(50MHz)
6
input rst_n
, // 复位信号
7
8 //SDRAM interface
9
output reg [15
:0
] wr_data
, // SDRAM fifo接口写入数据
10
input [15
:0
] rd_data
, // SDRAM fifo读出的数据
11
output reg wr_en
, // SDRAM fifo接口写入数据使能
12
output wr_load
, // 写地址寄存 & fifo清空
13
output reg rd_en
, // SDRAM fifo接口读出数据使能
14
input sdram_init_done
, // SDRAM 初始化完成信号
15
16 //user interface
17
input [31
:0
] adc_data
, // FPGA接收的音频数据
18
output reg [31
:0
] dac_data
, // FPGA发送的音频数据
19
input record_key
, // 录音按键
20
input play_key
, // 播放按键
21
input rx_done
, // 音频数据接收完成
22
input tx_done
, // 音频数据发送完成
23
output neg_play_key // 录音键的下降沿
24
);
25
26 //reg define
27
reg [23
:0
] wr_cnt
; // 控制写使能与数据计数器
28
reg [23
:0
] rd_cnt
; // 控制读使能与数据计数器
29
reg play_key_dy0
; // 采play_key的下降沿打拍0
30
reg play_key_dy1
; // 采play_key的下降沿打拍1
31
reg rx_dy0
; // 采rx_done的下降沿打拍0
32
reg rx_dy1
; // 采rx_done的下降沿打拍1
33
reg tx_dy0
; // 采tx_done的下降沿打拍0
34
reg tx_dy1
; // 采tx_done的下降沿打拍1
35
reg record_key_dy0
; // 采record_key的下降沿打拍0
36
reg record_key_dy1
; // 采record_key的下降沿打拍1
37
reg sdram_init_done_d0
; // SDRAM初始化完成信号打拍采样
38
reg sdram_init_done_d1
; // SDRAM初始化完成信号打拍采样
39
reg play
; // 回放录音控制
40
reg [23
:0
] time_cnt
; // 录音长度计时
41
42 //wire define
43
wire pos_rx
; // rx_done的上升沿
44
wire pos_tx
; // rx_done的上升沿
45
46 //*****************************************************
47 //** main code
48 //*****************************************************
49
50
assign neg_play_key
= (~play_key_dy0
) & play_key_dy1
; // 采下降沿
51
assign pos_rx
= rx_dy0
& (~rx_dy1
); // 采上升沿
52
assign pos_tx
= tx_dy0
& (~tx_dy1
); // 采上升沿
53
assign wr_load
= record_key_dy0
& (~record_key_dy1
); // 采上升沿
54
55 //寄存初始化完成信号采样
56
always @(posedge clk
or negedge rst_n
) begin
57
if(!rst_n
) begin
58 sdram_init_done_d0
<= 1'b0
;
59 sdram_init_done_d1
<= 1'b0
;
60
end
61
else begin
62 sdram_init_done_d0
<= sdram_init_done
;
63 sdram_init_done_d1
<= sdram_init_done_d0
;
64
end
65
end
66
67 //采一个时钟周期的rx_done和tx_done信号
68
always @(posedge clk
or negedge rst_n
) begin
69
if(!rst_n
) begin
70 rx_dy0
<= 1'b0
;
71 rx_dy1
<= 1'b0
;
72 tx_dy0
<= 1'b0
;
73 tx_dy1
<= 1'b0
;
74
end
75
else begin
76 rx_dy0
<= rx_done
;
77 rx_dy1
<= rx_dy0
;
78 tx_dy0
<= tx_done
;
79 tx_dy1
<= tx_dy0
;
80
end
81
end
82
83 //打拍采play_key的下降沿
84
always @(posedge clk
or negedge rst_n
) begin
85
if(!rst_n
) begin
86 record_key_dy0
<= 1'b0
;
87 record_key_dy1
<= 1'b0
;
88 play_key_dy0
<= 1'd0
;
89 play_key_dy1
<= 1'd0
;
90
end
91
else begin
92 record_key_dy0
<= record_key
;
93 record_key_dy1
<= record_key_dy0
;
94 play_key_dy0
<= play_key
;
95 play_key_dy1
<= play_key_dy0
;
96
end
97
end
98
99 //回放录音控制
100
always @(posedge clk
or negedge rst_n
) begin
101
if(!rst_n
) begin
102 play
<= 1'b0
;
103
end
104
else if(neg_play_key
== 1'b1
)
105 play
<= 1'b1
;
106
else if(record_key
== 1'b0
)
107 play
<= 1'b0
;
108
else if(rd_cnt
== time_cnt
)
109 play
<= 1'b0
;
110
end
111
112 //SDRAM fifo接口写入音频数据
113
always @(posedge clk
or negedge rst_n
) begin
114
if(rst_n
== 1'b0
) begin
115 wr_en
<= 1'b0
;
116 wr_cnt
<= 24'd0
;
117 wr_data
<= 16'd0
;
118 time_cnt
<= 24'd0
;
119
end
120
else if(sdram_init_done_d1
&& record_key
== 1'b0
) begin
121
if(pos_rx
&& wr_cnt
< TIME_RECORD
) begin
122 wr_en
<= 1'b1
;
123 wr_cnt
<= wr_cnt
+ 1'b1
;
124 wr_data
<= adc_data
[15
:0
];
125 time_cnt
<= wr_cnt
;
126
end
127
else
128 wr_en
<= 1'b0
;
129
end
130
else begin
131 wr_en
<= 1'b0
;
132 wr_cnt
<= 24'd0
;
133
end
134
end
135
136 //SDRAM fifo接口使能读取音频数据
137
always @(posedge clk
or negedge rst_n
) begin
138
if(rst_n
== 1'b0
) begin
139 rd_en
<= 1'b0
;
140 rd_cnt
<= 24'd0
;
141
end
142
else if(play
== 1'b1
) begin
143
if(pos_tx
&& rd_cnt
<= time_cnt
) begin
144 rd_en
<= 1'b1
;
145 rd_cnt
<= rd_cnt
+ 1'b1
;
146
end
147
else
148 rd_en
<= 1'b0
;
149
end
150
else begin
151 rd_en
<= 1'b0
;
152 rd_cnt
<= 24'd0
;
153
end
154
end
155
156 //SDRAM fifo接口读取音频数据
157
always @(posedge clk
or negedge rst_n
) begin
158
if(!rst_n
) begin
159 dac_data
<= 32'd0
;
160
end
161
else if(rd_en
== 1'b1
)
162 dac_data
[15
:0
] <= rd_data
;
163
else if(play
== 1'b0
)
164 dac_data
<= 32'd0
;
165
end
166
167
endmodule
代码的第100行的always语句块用于回放录音的控制,当采样到回放按键play_key的下降
沿时,开始回放录音,在回放录音过程中当检测到录音按键按下时或回放到录音的时长时结束
播放。代码第113行的always语句块用于控制SDRAM fifo接口写入音频数据,录音按键按下时,
若检测到音频接收完成信号rx_done的上升沿、SDRAM初始化完成并且录音时长在设定的参数范
围内,就使能录音的音频数据写入SDRAM fifo中,否则不使能;录音按键释放时,停止接收音
频数据。代码第137行和第157行的always语句块用于控制SDRAM fifo接口读取音频数据,当按
下回放按键时,使能读取操作,否则不使能,回放按键释放时停止读取。
图 34.4.3是录音控制模块按下录音按键record_key时SignalTap抓取的波形图,当按下录
音按键record_key时wr_cnt开始计数,接收到的音频数据adc_data写入SDRAM fifo的数据接口。
图 34.4.3 按下录音按键record_key时SignalTap抓取的波形图
下载验证
首先我们打开WM8978录音实验工程,在工程所在的路径下打开audio_record/par文件夹,
在里面找到“audio_record.qpf”并双击打开。注意工程所在的路径名只能由字母、数字以及
下划线组成,不能出现中文、空格以及特殊字符等。工程打开后如图 34.5.1所示。
图 34.5.1 录音机实验工程
然后将下载器一端连电脑,另一端与开发板上对应端口连接,并将耳机连接至WM8978的
PHONE接口,最后连接
电源线并打开电源开关。
开拓者开发板实物图如下所示:
图 34.5.2 开发板实物图
接下来我们下载程序,验证WM8978的录音功能。
工程打开后通过点击工具栏中的“Programmer”图标打开下载界面,通过“Add File”按
钮选择audio_record/par/output_files目录下的“audio_record.sof”文件。开发板电源打
开后,在程序下载界面点击“Hardware Setup”,在弹出的对话框中选择当前的硬件连接为
“USB-Blaster[USB-0]”。然后点击“Start”将工程编译完成后得到的sof文件下载到开发板
中,如图 34.5.3所示。
图 34.5.3 程序下载界面
下载完成后按下开拓者开发板上的KEY2按键不放,这时开始录音;当录音结束时,松开KEY0
按键,按下KEY1按键时听到喇叭播放录音,戴上耳机,也能听到播放录音,说明录音机实验下
载验证成功。