WM7831产生正弦波.docx
- 文档编号:4628041
- 上传时间:2022-12-07
- 格式:DOCX
- 页数:17
- 大小:35.42KB
WM7831产生正弦波.docx
《WM7831产生正弦波.docx》由会员分享,可在线阅读,更多相关《WM7831产生正弦波.docx(17页珍藏版)》请在冰豆网上搜索。
WM7831产生正弦波
DE2实践之WM8731产生正弦波
经历近三周的时间,终于搞定了wm8731产生1khz正弦波的那个DEMO。
现在就将这三周来的收获做一个记录,希望大家与我共同分享,共同进步。
一.本DEMO的目的
通过fpga控制音频编解码芯片wm8731产生一个1khz的正弦波,接上de2板上的耳机接口,试听此正弦波。
二.原理
FPGA与wm8731共有5个接口,分别为SCLK,SDIN,DACLRC,DACDAT,BCLK.其中SCLK,SDIN为控制接口,DACLRC,DACDAT,BCLK为数字音频接口。
由SCLK,SDIN配置wm8731的寄存器,由DACLRC,DACDAT,BCLK产生所需的正弦波并输出到耳机接口。
详细原理见代码中注释。
代码如下
1.sclk,sdin数据传输时序代码(i2c写控制代码)
modulei2c_com(clock_i2c,//wm8731控制接口传输所需时钟,0-400khz,此处为20khz
reset_n,
ack,//应答信号
i2c_data,//sdin接口传输的24位数据
start,//开始传输标志
tr_end,//传输结束标志
cyc_count,
i2c_sclk,//FPGA与wm8731时钟接口
i2c_sdat);//FPGA与wm8731数据接口
input[23:
0]i2c_data;
inputreset_n;
inputclock_i2c;
output[5:
0]cyc_count;
outputack;
inputstart;
outputtr_end;
outputi2c_sclk;
inouti2c_sdat;
reg[5:
0]cyc_count;
regreg_sdat;
regsclk;
regack1,ack2,ack3;
regtr_end;
wirei2c_sclk;
wirei2c_sdat;
wireack;
assignack=ack1|ack2|ack3;
assigni2c_sclk=sclk|(((cyc_count>=4)&(cyc_count<=30))?
~clock_i2c:
0);
assigni2c_sdat=reg_sdat?
1'bz:
0;
always@(posedgeclock_i2cornegedgereset_n)
begin
if(!
reset_n)
cyc_count<=6'b111111;
elsebegin
if(start==0)
cyc_count<=0;
elseif(cyc_count<6'b111111)
cyc_count<=cyc_count+1;
end
end
always@(posedgeclock_i2cornegedgereset_n)
begin
if(!
reset_n)
begin
tr_end<=0;
ack1<=1;
ack2<=1;
ack3<=1;
sclk<=1;
reg_sdat<=1;
end
else
case(cyc_count)
0:
beginack1<=1;ack2<=1;ack3<=1;tr_end<=0;sclk<=1;reg_sdat<=1;end
1:
reg_sdat<=0;//开始传输
2:
sclk<=0;
3:
reg_sdat<=i2c_data[23];
4:
reg_sdat<=i2c_data[22];
5:
reg_sdat<=i2c_data[21];
6:
reg_sdat<=i2c_data[20];
7:
reg_sdat<=i2c_data[19];
8:
reg_sdat<=i2c_data[18];
9:
reg_sdat<=i2c_data[17];
10:
reg_sdat<=i2c_data[16];
11:
reg_sdat<=1;//应答信号
12:
beginreg_sdat<=i2c_data[15];ack1<=i2c_sdat;end
13:
reg_sdat<=i2c_data[14];
14:
reg_sdat<=i2c_data[13];
15:
reg_sdat<=i2c_data[12];
16:
reg_sdat<=i2c_data[11];
17:
reg_sdat<=i2c_data[10];
18:
reg_sdat<=i2c_data[9];
19:
reg_sdat<=i2c_data[8];
20:
reg_sdat<=1;//应答信号
21:
beginreg_sdat<=i2c_data[7];ack2<=i2c_sdat;end
22:
reg_sdat<=i2c_data[6];
23:
reg_sdat<=i2c_data[5];
24:
reg_sdat<=i2c_data[4];
25:
reg_sdat<=i2c_data[3];
26:
reg_sdat<=i2c_data[2];
27:
reg_sdat<=i2c_data[1];
28:
reg_sdat<=i2c_data[0];
29:
reg_sdat<=1;//应答信号
30:
beginack3<=i2c_sdat;sclk<=0;reg_sdat<=0;end
31:
sclk<=1;
32:
beginreg_sdat<=1;tr_end<=1;end
endcase
end
endmodule
这里需要关键注意的是i2c的写控制时序要把握准确:
个人总结如下图:
说明:
A:
这里的i2c控制器采用33个i2c时钟周期进行,其中3-10,12-19,21-28传送数据,11,20,29为应答位。
B:
空闲状态时,sdat为高阻态,sclk为高电平状态。
C:
每发送1个字节的数据,就应返回一个应答信号将sdat状态拉低。
D:
实际传输时,24位的数据各部分为下:
a.第1个字节的前7位为wm8731器件地址,第8位为读或写选择位,写为0,读为1.
b.第2个字节的前7位为wm8731中寄存器的地址位,第8位为寄存器中9位数据的最高位。
c.第3个字节的8位数据为wm8731中寄存器中9位数据的低8位。
2.wm8731中寄存器的配置程序
modulereg_config(clock_50m,
i2c_sclk,
i2c_sdat,
reset_n);
inputclock_50m;
inputreset_n;
outputi2c_sclk;
inouti2c_sdat;
regclock_20k;
reg[15:
0]clock_20k_cnt;
reg[1:
0]config_step;
reg[3:
0]reg_index;
reg[23:
0]i2c_data;
reg[15:
0]reg_data;
regstart;
i2c_comu1(.clock_i2c(clock_20k),
.reset_n(reset_n),
.ack(ack),
.i2c_data(i2c_data),
.start(start),
.tr_end(tr_end),
.i2c_sclk(i2c_sclk),
.i2c_sdat(i2c_sdat));
always@(posedgeclock_50mornegedgereset_n)//产生i2c控制时钟-20khz
begin
if(!
reset_n)
begin
clock_20k<=0;
clock_20k_cnt<=0;
end
elseif(clock_20k_cnt<2499)
clock_20k_cnt<=clock_20k_cnt+1;
else
begin
clock_20k<=!
clock_20k;
clock_20k_cnt<=0;
end
end
always@(posedgeclock_20kornegedgereset_n)//配置过程控制
begin
if(!
reset_n)
begin
config_step<=0;
start<=0;
reg_index<=0;
end
else
begin
if(reg_index<10)
begin
case(config_step)
0:
begin
i2c_data<={8'h34,reg_data};
start<=1;
config_step<=1;
end
1:
begin
if(tr_end)
begin
if(!
ack)
config_step<=2;
else
config_step<=0;
start<=0;
end
end
2:
begin
reg_index<=reg_index+1;
config_step<=0;
end
endcase
end
end
end
always@(reg_index)
begin
case(reg_index)
0:
reg_data<=16'h001f;
1:
reg_data<=16'h021f;
2:
reg_data<=16'h047f;
3:
reg_data<=16'h067f;
4:
reg_data<=16'h08f8;
5:
reg_data<=16'h0a06;
6:
reg_data<=16'h0c00;
7:
reg_data<=16'h0e01;
8:
reg_data<=16'h1002;
9:
reg_data<=16'h1201;
default:
reg_data<=16'h001a;
endcase
end
endmodule
关于此段代码的解释:
A:
此处实例化了前面提到的i2c控制的程序。
B:
共对wm8731中的10个寄存器进行配置,详细的寄存器信息与每位数据的表示意义参考wm8731的datasheet。
C:
wm8731允许的i2c控制时钟在0-400khz有效,这里应用20khz。
D:
配置过程控制这段代码很关键,尤其要注意开始和结束信号的控制
3.正弦波产生程序
modulesinwave_gen(clock_ref,dacclk,bclk,dacdat,reset_n);
inputclock_ref;//wm8731输入时钟,18.432Mhz;
inputreset_n;
outputdacclk;
outputdacdat;
outputbclk;
regdacclk;
reg[8:
0]dacclk_cnt;
regbclk;
reg[3:
0]bclk_cnt;
reg[3:
0]data_num;
reg[15:
0]sin_out;
reg[5:
0]sin_index;
parameterCLOCK_REF=18432000;
parameterCLOCK_SAMPLE=48000;
always@(posedgeclock_refornegedgereset_n)//生产正弦波采样时钟48khz
begin
if(!
reset_n)
begin
dacclk<=0;
dacclk_cnt<=0;
end
elseif(dacclk_cnt>=(CLOCK_REF/(CLOCK_SAMPLE*2)-1))
begin
dacclk<=~dacclk;
dacclk_cnt<=0;
end
else
dacclk_cnt<=dacclk_cnt+1;
end
always@(posedgeclock_refornegedgereset_n)//生产16位数据传输时钟,正弦波采样时钟的32倍
begin
if(!
reset_n)
begin
bclk<=0;
bclk_cnt<=0;
end
elseif(bclk_cnt>=(CLOCK_REF/(CLOCK_SAMPLE*2*16*2)-1))
begin
bclk<=~bclk;
bclk_cnt<=0;
end
else
bclk_cnt<=bclk_cnt+1;
end
always@(negedgebclkornegedgereset_n)
begin
if(!
reset_n)
data_num<=0;
else
data_num<=data_num+1;
end
always@(negedgedacclkornegedgereset_n)
begin
if(!
reset_n)
sin_index<=0;
elseif(sin_index<47)
sin_index<=sin_index+1;
else
sin_index<=0;
end
assigndacdat=sin_out[~data_num];//产生DA转换器数字音频数据
always@(sin_index)
begin
case(sin_index)
0:
sin_out<=0;//32767*sin0
1:
sin_out<=4276;//32767*sin7.5(角度)
2:
sin_out<=8480;//32767*sin15(角度)
3:
sin_out<=12539;
4:
sin_out<=16383;
5:
sin_out<=19947;
6:
sin_out<=23169;
7:
sin_out<=25995;
8:
sin_out<=28377;
9:
sin_out<=30272;
10:
sin_out<=31650;
11:
sin_out<=32486;
12:
sin_out<=32767;
13:
sin_out<=32486;
14:
sin_out<=31650;
15:
sin_out<=30272;
16:
sin_out<=28377;
17:
sin_out<=25995;
18:
sin_out<=23169;
19:
sin_out<=19947;
20:
sin_out<=16383;
21:
sin_out<=12539;
22:
sin_out<=8480;
23:
sin_out<=4276;
24:
sin_out<=0;
25:
sin_out<=61259;
26:
sin_out<=57056;
27:
sin_out<=52997;
28:
sin_out<=49153;
29:
sin_out<=45589;
30:
sin_out<=42366;
31:
sin_out<=39540;
32:
sin_out<=37159;
33:
sin_out<=35263;
34:
sin_out<=33885;
35:
sin_out<=33049;
36:
sin_out<=32768;
37:
sin_out<=33049;
38:
sin_out<=33885;
39:
sin_out<=35263;
40:
sin_out<=37159;
41:
sin_out<=39540;
42:
sin_out<=42366;
43:
sin_out<=45589;
44:
sin_out<=49152;
45:
sin_out<=52997;
46:
sin_out<=57056;
47:
sin_out<=61259;
endcase
end
endmodule
关于此段代码的解释:
由于采样速率是48khz,在所要产生的正弦波的一个周期内进行了48此采样,所以得到的正弦波的频率是1khz。
4.wm8731的输入时钟要求为18.432Mhz,此时钟由DE2板上27Mhz晶振由altera的PLL分频得到。
由于27Mhz不能精确地分频到18.432Mhz,所以采用了一个能分到一个最接近的频率,即18.409091Mhz。
分频程序如下:
moduleaudio_pll(
inclk0,
c0);
inputinclk0;
outputc0;
wire[5:
0]sub_wire0;
wire[0:
0]sub_wire4=1'h0;
wire[0:
0]sub_wire1=sub_wire0[0:
0];
wirec0=sub_wire1;
wiresub_wire2=inclk0;
wire[1:
0]sub_wire3={sub_wire4,sub_wire2};
altpllaltpll_component(
.inclk(sub_wire3),
.clk(sub_wire0),
.activeclock(),
.areset(1'b0),
.clkbad(),
.clkena({6{1'b1}}),
.clkloss(),
.clkswitch(1'b0),
.configupdate(1'b0),
.enable0(),
.enable1(),
.extclk(),
.extclkena({4{1'b1}}),
.fbin(1'b1),
.fbmimicbidir(),
.fbout(),
.locked(),
.pfdena(1'b1),
.phasecounterselect({4{1'b1}}),
.phasedone(),
.phasestep(1'b1),
.phaseupdown(1'b1),
.pllena(1'b1),
.scanaclr(1'b0),
.scanclk(1'b0),
.scanclkena(1'b1),
.scandata(1'b0),
.scandataout(),
.scandone(),
.scanread(1'b0),
.scanwrite(1'b0),
.sclkout0(),
.sclkout1(),
.vcooverrange(),
.vcounderrange());
defparam
altpll_component.clk0_divide_by=27000000,
altpll_component.clk0_duty_cycle=50,
altpll_component.clk0_multiply_by=18409091,
altpll_component.clk0_phase_shift="0",
altpll_pensate_clock="CLK0",
altpll_component.inclk0_input_frequency=37037,
altpll_component.intended_device_family="CycloneII",
altpll_component.lpm_hint="CBX_MODULE_PREFIX=Audio_PLL",
altpll_component.lpm_type="altpll",
altpll_component.operation_mode="NORMAL",
altpll_component.port_activeclock="PORT_UNUSED",
altpll_component.port_areset="PORT_UNUSED",
altpll_component.port_clkbad0="PORT_UNUSED",
altpll_component.port_clkbad1="PORT_UNUSED",
altpll_component.port_clkloss="PORT_UNUSED",
altpll_component.port_clkswitch="PORT_UNUSED",
altpll_component.port_configupdate="PORT_UNUSED",
altpll_component.port_fbin="PORT_UNUSED",
altpll_component.port_inclk0="PORT_USED",
altpll_component.port_inclk1="PORT_UNUSED",
altpll_component.port_locked="PORT_UNUSED",
altpll_component.port_pfdena="PORT_UNUSED",
altpll_component.port_phasecounterselect="PORT_UNUSED",
altpll_component.port_phasedone="PORT_UNUSED",
altpll_component.port_phasestep="PORT_UNUSED",
altpll_component.port_phaseupdown="PORT_UNUSED",
altpll_component.port_pllena="PORT_UNUSED",
altpll_component.port_scanaclr="PORT_UNUSED",
altpll_component.port_scanclk="PORT_UNUSED",
altpll_component.port_scanclkena="PORT_UNUSED",
altpll_component.port_scandata="PORT_UNUSED",
altpll_component.port_scandataout=
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- WM7831 产生 正弦波
![提示](https://static.bdocx.com/images/bang_tan.gif)