sd_card_audio

我们在前文中讲过一个关于audio的例程,它主要完成了录制播放功能(直接从SDRAM中写/读数据),而本文相较之前的例程有两处不同,第一处:本例程是从SD卡中读取数据;第二处:本例程添加了文件检索功能(但是并没有添加文件系统)。关于第一处我们没有太多问题,因为在之前关于SD卡的读写已经详细讲解过了,那么第二处的文件检索就成为了重中之重,在没有文件系统的情况下如何完成文件检索?在已知文件格式为WAV的条件下(大部分的文件都有文件头),我们需要做的是分析SD卡一个扇区的前几个字节,判断这个文件是否为WAV文件。WAV文件作为多媒体中使用的声波文件格式之一,它是以RIFF格式为标准的。RIFF是英文Resource Interchange File Format的缩写,每个WAV文件的头四个字节便是”RIFF”,所以本实验就简单的以每个扇区的前4个字节是否为”RIFF”判断文件是否为WAV文件,紧接着4个字节表示文件的大小,这样我们就可以确定要读取的数据量。WAV文件头是88字节,在播放时要把前88个字节的文件头去掉。

 

sd_card_audio.v RTL视图 

从上面的RTL视图中我们可以发现,有五个模块构成,但是在功能上有三部分组成

①:sd_card_audio,完成SD卡与audio之间的数据交互,并将检索出来的wave文件通过发送给WM8731音频芯片完成DA转换。

②:i2c_config/lut_wm8731,完成WM8731的配置。

③:seg_decoder/seg_scan,完成数码管的显示。

 

由上图可以看出sd_card_audio_m0由哪几个模块组成,模块之间是怎样连接的,一目了然。

由于其他子模块我们之前有讲过,我们下面详细讲述wav_read这个子模块,该模块主要完成了对WAVE文件的检索,以及将SD卡中读到的数据传给FIFO。

wav_read.v

可以看出该模块总共由五个状态,我之前一再强调,分析任何逻辑代码,不要一上来就从头看到尾,去分析这个信号干什么的,那个信号又是干什么的。我们要先去分析状态机。

 

 

通过观察这段状态机,发现了与以往我们设计状态机不一样的地方,之前在进行状态机设计的时候我们会默认state==IDLE,而在这里我们通过判断sd_init_done的值来决定state是否进入IDLE状态,那么有人会问sd_init_done是个什么信号,怎么既熟悉又陌生?我只能说你是具备了一个渣男的属性(见一个爱一个),在SD卡的设计文档中我们有讲过sd_init_done是SD卡是否完成初始化的标记信号。如果完成初始化,进入S_IDLE,映入眼帘的是find信号,通过RTL视图,我们有了解,find信号与negedge信号相连,也就是说当有检测到键按下,系统进入S_FIND状态。sd_sec_read_addr <= {sd_sec_read_addr[31:3],3'd0};在IDLE状态里的这条逻辑语句我一直没有看懂,我不清楚为什么要在这里对地址减8?

我们先抛开这个问题,往下看,当系统进入S_FIND状态后,我们会看到几个信号,如:sd_sec_read_end;sd_sec_read;渣男本性的你估计又该问这几个是什么信号呢?这两个信号在RTL视图我们可以看出它与sdcard_top相连,关于这部分我们之前有讲过,sd_sec_read_end是读完一个扇区(即512bytes)的标志信号;而sd_sec_read是一个控制信号,它决定了sd_card_sec_read_write这个模块在S_WAIT_READ_WRITE这个状态里是进入S_CMD24还是S_CMD17,也就是在SD卡初始化后,是进行读操作还是写操作。接下来我们继续看这段逻辑,进入到S_FIND中后,sd_sec_read就被置1,开始读对应地址(sd_sec_read_addr <= {sd_sec_read_addr[31:3],3'd0})中的数据,以达到搜索的目的。如果读完该扇区所有的数据,sd_sec_read_end置1,这里开始判断found信号,我们开始搜索前面的程序,来找寻found信号是个什么东东?我们就要看以下两端逻辑来进行分析。

 

 

rd_cnt是用来对该扇区读到的数记录顺序,rd_cnt==0;即读到的第一个数。rd_cnt==1;即读到的第二个数。。。在前面我们有讲过wav的文件头,匹配的过程,就是看你对应位置读到的数据与文件头是否一致。匹配一致,found置1。file_len指的是在头文件中表示的文件长度。

回到上面那段状态机,如果匹配一致,系统进入S_PLAY_WAIT,并将sd_sec_read置0,否则sd_sec_read_addr<=sd_sec_read_addr+32’d8;最开始我也好奇为什么每次+8,也就是每隔8*512bytes=4K的空间进行匹配。这里我给大家分享一个有意思的事情:

我们发现分配单元也是4K,我估计应该是按照分配单元进行检索。

当系统检测到WAV的文件头,即匹配成功,进入S_PLAY_WAIT状态,这又是一个什么状态呢?通过判断语句我们可以看出这个与我们之前check fifo功能上是大体一致的啊,他们都是检查fifo里的空间够不够缓存一个扇区的数据。fifo_wr_cnt连接的正是fifo的wrusedw。

如果fifo中的空间足够缓存扇区的数据,系统进入S_PLAY状态,也就是播放该WAV格式的文件,在这个状态中,我们看到了一个play_cnt,翻找前文,我们找到了它的轨迹:

 

每读完一个数据,注意这里不是读完一个扇区,而是读完一个字节,play_cnt+1。也就是说play_cnt起到了一个记录数据长度的作用。

回到S_PLAY状态,当play_cnt>=file_len也就是读完该文件的所有数据后,进入S_END状态,否则回到S_PLAY_WAIT,继续检测fifo的空间够不够缓存一个扇区的数据(因为sd_sec_read_addr+1,开始读下一个扇区的数据)。

接下来肯定会有同学产生疑问,我们播放的应该是音频的数据,那么整个文件既包括音频又包括头文件,我们如何剔除这些无效信息呢?下面出现的这段代码就帮我们做了这样的事情

 

既然要控制音频芯片播放有效的数据,那么我们缓存在fifo中的数据应该是剔除头文件的有效音频数据。在这里我们通过控制wav_data_wr_en(连接在fifo的wr_req)来完成这一功能。

已知头文件的长度为88bytes,所以要在S_PLAY状态选择性的读取SD卡中的数据,并将数据送至FIFO(if(sd_sec_read_data_valid==1’b1&&play_cnt>32’d87&&play_cnt<file_len)

wav_data_wr_en<=1’b1; wav_data<=sd_sec_read_data;)。

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
FPGA读写SD卡测试实验 Verilog逻辑源码Quartus工程文件+文档说明,FPGA型号Cyclone4E系列中的EP4CE6F17C8,Quartus版本17.1。 // 2017/6/19 meisq 1.0 Original //*******************************************************************************/ module sd_card_test( input clk, input rst_n, input key1, output SD_nCS, output SD_DCLK, output SD_MOSI, input SD_MISO, output [5:0] seg_sel, output [7:0] seg_data ); parameter S_IDLE = 0; parameter S_READ = 1; parameter S_WRITE = 2; parameter S_END = 3; reg[3:0] state; wire sd_init_done; reg sd_sec_read; wire[31:0] sd_sec_read_addr; wire[7:0] sd_sec_read_data; wire sd_sec_read_data_valid; wire sd_sec_read_end; reg sd_sec_write; wire[31:0] sd_sec_write_addr; reg [7:0] sd_sec_write_data; wire sd_sec_write_data_req; wire sd_sec_write_end; reg[9:0] wr_cnt; reg[9:0] rd_cnt; wire button_negedge; reg[7:0] read_data; ax_debounce ax_debounce_m0 ( .clk (clk), .rst (~rst_n), .button_in (key1), .button_posedge (), .button_negedge (button_negedge), .button_out () ); wire[6:0] seg_data_0; seg_decoder seg_decoder_m0( .bin_data (read_data[3:0]), .seg_data (seg_data_0) ); wire[6:0] seg_data_1; seg_decoder seg_decoder_m1( .bin_data (read_data[7:4]), .seg_data (seg_data_1) ); seg_scan seg_scan_m0( .clk (clk), .rst_n (rst_n), .seg_sel (seg_sel), .seg_data (seg_data), .seg_data_0 ({1'b1,7'b1111_111}), .seg_data_1 ({1'b1,7'b1111_111}), .seg_data_2 ({1'b1,7'b1111_111}), .seg_data_3 ({1'b1,7'b1111_111}), .seg_data_4 ({1'b1,seg_data_1}), .seg_data_5 ({sd_init_done,seg_data_0}) ); always@(posedge clk or negedge rst_n) begin if(rst_n == 1'b0) wr_cnt <= 10'd0; else if(state == S_WR

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值