数字系统设计音乐播放器文档格式.docx
《数字系统设计音乐播放器文档格式.docx》由会员分享,可在线阅读,更多相关《数字系统设计音乐播放器文档格式.docx(20页珍藏版)》请在冰豆网上搜索。
![数字系统设计音乐播放器文档格式.docx](https://file1.bdocx.com/fileroot1/2022-12/1/bc854be6-9ecb-4648-92d3-902606b5d116/bc854be6-9ecb-4648-92d3-902606b5d1161.gif)
Input
100MHz时钟信号
reset
复位信号,高电平有效
play_button
“播放”按键,低电平有效
next_button
“下一曲”按键,一个时钟周期宽度的高电平脉冲
play
Output
高电平表示播放
song_done
当播放下一曲时,输出一个时钟周期宽度的高电平复位脉冲reset_play,并复位note_player模块
note_player模块的应答信号一个时钟周期宽度的高电平脉冲表示一曲播放结束
song[1:
0]
当前乐曲的序号
结合以上算法流程图和端口定义,mcu模块代码如下:
modulemcu(clk,reset,play_button,next,play,reset_play,song,song_done);
parameterRESET=0,WAIT=1,END=2,NEXT=3,PLAY=4;
inputreset,play_button,song_done,clk,next;
outputreg[1:
0]song;
outputregplay,reset_play;
regstate;
always@(posedgeclk)
begin
if(reset)
beginplay<
=0;
song<
=2'
b00;
reset_play<
=1;
state<
=RESET;
end
else
case(state)
RESET:
beginreset_play<
=WAIT;
WAIT:
if(song_done)
beginstate<
=END;
play<
reset_play<
elseif(next)
beginstate<
=NEXT;
=song+1;
elseif(~play_button)
=PLAY;
END,NEXT,PLAY:
endcase
end
endmodule
2、乐曲读取模块song_reader的设计
乐曲读取模块song_reader的功能有:
(1)根据mcu模块的要求,选择播放乐曲;
(2)相应note_player模块请求,从song_rom中逐个取出音符{note,duration}送给note_player模块播放;
(3)判断乐曲是否播放完毕,若播放完毕,则回复mcu模块应答信号。
根据设计要求,以下是song_reader模块的结构框图
根据设计要求,以下是song_reader模块的端口含义
100MH时钟信号
来自mcu的控制信号,高电平播放
来自mcu的控制信号,当前播放乐曲的序号
note_done
来自模块note_player应答信号,一个时钟周期宽度的高电平脉冲表示一个音符播放结束
0utput
给mcu的应答信号,当乐曲播放结束,输出一个时钟周期宽度的高电平脉冲
note[5:
音符标记
duration[5:
音符的持续时间
new_note
给note_player的控制信号,一个时钟周期宽度的高电平脉冲表示新的音符需播放
以下是song_reader的算法流程图
地址计数器为5位二进制计数器,其中note_done为计数允许输入,状态q为song_rom的低5位地址,song[1:
0]为song_rom高两位地址。
当地址计数器出现进位或duration为0时,表示乐曲结束,应输出一个时钟周期宽度的高电平脉冲信号song_done。
结束判断模块应调用提供的模块one_pulse,使输出为一个时钟周期宽度的高电平脉冲。
结合上图以及模块要求,编写song_reader代码如下:
modulesong_reader(clk,reset,play,song,note_done,song_done,note,duration,new_note);
inputclk,reset,play,note_done;
input[1:
outputsong_done,new_note;
output[5:
0]note,duration;
parameterRESET=0,NEW_NOTE=1,WAIT=2,NEXT_NOTE=4;
reg[1:
0]STATE;
regnew_note;
always@(posedgeclk)
begin
if(reset==1)beginnew_note<
STATE<
case(STATE)
RESET:
if(play==1)
beginnew_note<
=NEW_NOTE;
else
NEW_NOTE:
if(play==1)
if(note_done==1)
beginnew_note<
=NEXT_NOTE;
else
beginnew_note<
elsebeginnew_note<
NEXT_NOTE:
endcase
wire[4:
0]q;
wireco;
song_romsong_rom1(.clk(clk),.dout({note,duration}),.addr({song,q}));
addr_counteraddr_counter1(.clk(clk),.reset(reset),.note_done(note_done),.q(q),.co(co));
end_judgingend_judging1(.co(co),.duration(duration),.clk(clk),.song_done(song_done));
(返回给主控制器一个应答信号,即框图中的结束判断模块)
其中模块end_judging的代码如下:
moduleend_judging(co,duration,clk,song_done);
parameterN=6;
inputco;
input[N-1:
0]duration;
inputclk;
outputsong_done;
wire[N-1:
0]dd;
wireqq;
assignsong_done=~qq&
dd;
assigndd=co|(duration==6'
b00000);
D_FFdff1(.d(dd),.clk(clk),.q(qq));
其中模块counter的代码如下:
moduleaddr_counter(clk,q,co,reset,note_done);
inputclk,reset,note_done;
outputreg[4:
outputco;
assignco=note_done&
&
(&
q);
always@(posedgeclk)
begin
if(reset)beginq<
else
beginif(note_done)q<
=q+1;
q<
=q;
end
endmodule
3、音符播放模块note_player的设计
音符播放模块note_player是本实验的核心模块,它的主要功能有:
(1)从送song_reader模块接收所需播放的音符信息{note,duration};
(2)根据note值找出DDS的相位增量step_size;
(3)以48kHz速率从SineROM取出正弦样品送给AC97接口模块;
(4)当一个音符播放完毕,向song_reader模块索取新的音符。
根据note_player模块的任务,进一步划分功能单元,为简化设计,可将产生正弦样品的DDS模块设计一个独立子模块sine_reader。
note_player控制器负责与song_reader模块接口,读取音符信息,并根据音符信息从FrequencyROM中读取相位增量step_size送给DDS子模块sine_reader。
另外,note_player控制器还需要控制音符播放时间。
note_player控制器的算法流程如下。
在复位或未播放时,控制器处于RESET状态,PLAY为音符播放状态,当一个音符播放结束时,控制器进入DONE状态,PLAY为音符播放状态,当一个音符播放结束时,控制器进入DONE状态,置位done_with_note,向song_reader模块索取新的音符,然后进入LOAD状态,读取新的音符后进入PLAY状态播放下一个音符。
note_player模块的结构框图如下:
note_player控制器的算法流程图如下:
音符定时器为6位二进制计数器,beat、time_clear分别为使能、清0信号,均为高电平有效。
定时时间为音符的长短(duration_to_load个beat周期),timer_done为定时结束标志。
根据实验要求以下是note_player模块代码:
modulenote_player(clk,reset,play_enable,note_to_load,duration_to_load,
done_with_note,load_new_note,beat,generate_next_sample,sample_out,new_sample_ready);
inputclk;
inputreset;
//Whenhighweplay,whenlowwedon'
t.
inputplay_enable;
//Thenotetoplay
input[5:
0]note_to_load;
//Thedurationofthenotetoplay
0]duration_to_load;
//Tellsuswhenwehaveanewnotetoload
inputload_new_note;
//Whenwearedonewiththenotethisstayshigh.
outputdone_with_note;
//regdone_with_note;
//Thisisour1/48thsecondbeat
inputbeat;
//Tellsuswhenthecodecwantsanewsample
inputgenerate_next_sample;
//Oursampleoutput
output[15:
0]sample_out;
//Tellsthecodecwhenwe'
vegotasample
outputnew_sample_ready;
wire[19:
0]step_size;
sine_readersine_reader_inst(.clk(clk),.reset(reset),.step_size({10'
d18,10'
d791}),
.step_size(step_size),.generate_next_sample(generate_next_sample),
.new_sample_ready(new_sample_ready),.sample_out(sample_out));
(note_player控制器一段式描述代码)
parameterRESET=0,PLAY=1,LOAD=2,DONE=3;
reg[1:
0]state;
regdone_with_note;
regtimer_clear;
wiretimer_done;
reg[5:
0]note;
always@(posedgeclk)
if(reset)beginstate<
note<
=6'
b0;
done_with_note<
timer_clear<
case(state)
RESET,LOAD,DONE:
if(~play_enable)beginstate<
end
elseif(~load_new_note)
beginstate<
end
else
beginstate<
=LOAD;
=note_to_load;
end
PLAY:
if(timer_done)
beginstate<
=DONE;
end
elseif(~play_enable)
elseif(~load_new_note)
state<
elsebeginstate<
timer_clear<
default:
beginstate<
end
endcase
frequency_romfrequency_rom_inst(.clk(clk),.dout(step_size),.addr(note));
note_timernote_timer(.cin(duration_to_load),.en(beat),
.clk(clk),.r(timer_clear),.cout(timer_done));
其中模块note_timer音符定时器代码如下:
modulenote_timer(cout,cin,r,clk,en);
parametern=6;
reg[n-1:
outputcout;
input[n-1:
0]cin;
inputr,clk,en;
assigncout=en&
(q==(cin-1));
always@(posedgeclk)
if(r)q=0;
elseif(en)q=q+1;
elseq=q;
子模块sine_reader的功能就是利用DDS技术产生正弦样品。
在本实验中,系统时钟clk与取样脉冲generate_next_sample为两个不同信号;
实验中相位增量为22位,其中小数部分为10位。
对于step_size本身为20位二进制数的问题,可通过对其进行{2’b00,step_size}处理使其扩展至22位。
sine_reader原理框图:
sine_rom的地址和数据处理方法:
区域(raw_addr[21:
20])
SineRom地址(rom_addr)
Sample
00
raw_addr[19:
10]
raw_data[15:
01
当raw_addr[20:
10]=1024时,rom_addr取1023;
其余取~raw_addr[19:
10]+1
10
~raw_data[15:
0]+1
11
由以上sine_reader原理框图以及sine_rom的地址和数据处理方法可得sine_reader模块代码。
modulesine_reader(step_size,clk,generate_next_sample,reset,sample_out,new_sample_ready);
input[19:
inputclk,generate_next_sample,reset;
wire[21:
0]raw_addr;
0]sum;
wire[9:
0]rom_addr;
wire[15:
0]sample;
0]raw_data;
full_adder#(22)
adder(.a(raw_addr),.b({2'
b00,step_size}),.s(sum),.ci(1'
b0),.co());
D_FFRE#(22)
dffr1(.d(sum),.en(generate_next_sample),.r(reset),.clk(clk),.q(raw_addr));
addr_processor
addr_pro(.in_addr(raw_addr[20:
10]),.out_addr(rom_addr));
sine_rom
sine_rom1(.clk(clk),.dout(raw_data),.addr(rom_addr));
data_processor#(16)
data_pro(.flag(raw_addr[21]),.in_data(raw_data),.out_data(sample));
D_FFRE#(16)dffr2(.d(sample),.en(generate_next_sample),.r(reset),.clk(clk),.q(sample_out));
D_FF
dffr3(.d(generate_next_sample),.clk(clk),.q(new_sample_ready));
其中地址处理(addr_processor)模块的代码如下:
moduleaddr_processor(in_addr