完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
1)实验平台:正点原子开拓者FPGA 开发板
2)摘自《开拓者FPGA开发指南》关注官方微信号公众号,获取更多资料:正点原子 3)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-13912-1-1.html 在上述代码的always语句块中,我们使用div_clk_180deg(同sd_clk)的上升沿采集SD卡 返回的信号,而其它语句块使用div_clk时钟(sd_clk相位偏差180度的时钟)来操作,这是因 为SD卡的SPI模式下SD卡的上升沿锁存(采集)数据,在下降沿的时候更新(发送)数据,所 以在SD卡的上升沿采集数据是数据保持稳定的时刻,以确保采集的数据不会发送错误。我们知 道,SD卡在初始化过程中共返回三种响应类型,分别为R1、R3和R7,其中返回的R3类型和R7类 型中包含R1类型,R1类型最高位固定为0,而SD_MISO在空闲时是为高电平状态,因此我们可以 通过判断SD_MISO引脚拉低作为开始接收响应信号的条件。 图 39.4.4 和图 39.4.5为SD卡初始化过程中SignalTap抓取的波形图,从图中我们可以 清晰的看到在SD卡初始化过程中,各个状态的跳转。在初始化完成之后,sd_init_done信号由 低电平变为高电平,说明SD卡初始化完成。 图 39.4.4 SD卡初始化SignalTap波形图 图 39.4.5 SD卡初始化完成SignalTap波形图 SD卡写操作模块的代码如下: 1 module sd_write( 2 input clk_ref , //时钟信号 3 input clk_ref_180deg, //时钟信号,与sd_clk相位相差180度 4 input rst_n , //复位信号,低电平有效 5 //SD卡接口 6 input sd_miso , //SD卡SPI串行输入数据信号 7 output reg sd_cs , //SD卡SPI片选信号 8 output reg sd_mosi , //SD卡SPI串行输出数据信号 9 //用户写接口 10 input wr_start_en , //开始写SD卡数据信号 11 input [31:0] wr_sec_addr , //写数据扇区地址 12 input [15:0] wr_data , //写数据 13 output reg wr_busy , //写数据忙信号 14 output reg wr_req //写数据请求信号 15 ); 16 17 //parameter define 18 parameter HEAD_BYTE = 8'hfe ; //数据头 19 20 //reg define 21 reg wr_en_d0 ; //wr_start_en信号延时打拍 22 reg wr_en_d1 ; 23 reg res_en ; //接收SD卡返回数据有效信号 24 reg [7:0] res_data ; //接收SD卡返回数据 25 reg res_flag ; //开始接收返回数据的标志 26 reg [5:0] res_bit_cnt ; //接收位数据计数器 27 28 reg [3:0] wr_ctrl_cnt ; //写控制计数器 29 reg [47:0] cmd_wr ; //写命令 30 reg [5:0] cmd_bit_cnt ; //写命令位计数器 31 reg [3:0] bit_cnt ; //写数据位计数器 32 reg [8:0] data_cnt ; //写入数据数量 33 reg [15:0] wr_data_t ; //寄存写入的数据,防止发生改变 34 reg detect_done_flag ; //检测写空闲信号的标志 35 reg [7:0] detect_data ; //检测到的数据 36 37 //wire define 38 wire pos_wr_en ; //开始写SD卡数据信号的上升沿 39 40 //***************************************************** 41 //** main code 42 //***************************************************** 43 44 assign pos_wr_en = (~wr_en_d1) & wr_en_d0; 45 46 //wr_start_en信号延时打拍 47 always @(posedge clk_ref or negedge rst_n) begin 48 if(!rst_n) begin 49 wr_en_d0 <= 1'b0; 50 wr_en_d1 <= 1'b0; 51 end 52 else begin 53 wr_en_d0 <= wr_start_en; 54 wr_en_d1 <= wr_en_d0; 55 end 56 end 57 58 //接收sd卡返回的响应数据 59 //在clk_ref_180deg(sd_clk)的上升沿锁存数据 60 always @(posedge clk_ref_180deg or negedge rst_n) begin 61 if(!rst_n) begin 62 res_en <= 1'b0; 63 res_data <= 8'd0; 64 res_flag <= 1'b0; 65 res_bit_cnt <= 6'd0; 66 end 67 else begin 68 //sd_miso = 0 开始接收响应数据 69 if(sd_miso == 1'b0 && res_flag == 1'b0) begin 70 res_flag <= 1'b1; 71 res_data <= {res_data[6:0],sd_miso}; 72 res_bit_cnt <= res_bit_cnt + 6'd1; 73 res_en <= 1'b0; 74 end 75 else if(res_flag) begin 76 res_data <= {res_data[6:0],sd_miso}; 77 res_bit_cnt <= res_bit_cnt + 6'd1; 78 if(res_bit_cnt == 6'd7) begin 79 res_flag <= 1'b0; 80 res_bit_cnt <= 6'd0; 81 res_en <= 1'b1; 82 end 83 end 84 else 85 res_en <= 1'b0; 86 end 87 end 88 89 //写完数据后检测SD卡是否空闲 90 always @(posedge clk_ref or negedge rst_n) begin 91 if(!rst_n) 92 detect_data <= 8'd0; 93 else if(detect_done_flag) 94 detect_data <= {detect_data[6:0],sd_miso}; 95 else 96 detect_data <= 8'd0; 97 end 98 99 //SD卡写入数据 100 always @(posedge clk_ref or negedge rst_n) begin 101 if(!rst_n) begin 102 sd_cs <= 1'b1; 103 sd_mosi <= 1'b1; 104 wr_ctrl_cnt <= 4'd0; 105 wr_busy <= 1'b0; 106 cmd_wr <= 48'd0; 107 cmd_bit_cnt <= 6'd0; 108 bit_cnt <= 4'd0; 109 wr_data_t <= 16'd0; 110 data_cnt <= 9'd0; 111 wr_req <= 1'b0; 112 detect_done_flag <= 1'b0; 113 end 114 else begin 115 wr_req <= 1'b0; 116 case(wr_ctrl_cnt) 117 4'd0 : begin 118 wr_busy <= 1'b0; //写空闲 119 sd_cs <= 1'b1; 120 sd_mosi <= 1'b1; 121 if(pos_wr_en) begin 122 cmd_wr <= {8'h58,wr_sec_addr,8'hff}; //写入单个命令块CMD24 123 wr_ctrl_cnt <= wr_ctrl_cnt + 4'd1; //控制计数器加1 124 //开始执行写入数据,拉高写忙信号 125 wr_busy <= 1'b1; 126 end 127 end 128 4'd1 : begin 129 if(cmd_bit_cnt <= 6'd47) begin //开始按位发送写命令 130 cmd_bit_cnt <= cmd_bit_cnt + 6'd1; 131 sd_cs <= 1'b0; 132 sd_mosi <= cmd_wr[6'd47 - cmd_bit_cnt]; //先发送高字节 133 end 134 else begin 135 sd_mosi <= 1'b1; 136 if(res_en) begin //SD卡响应 137 wr_ctrl_cnt <= wr_ctrl_cnt + 4'd1; //控制计数器加1 138 cmd_bit_cnt <= 6'd0; 139 bit_cnt <= 4'd1; 140 end 141 end 142 end 143 4'd2 : begin 144 bit_cnt <= bit_cnt + 4'd1; 145 //bit_cnt = 0~7 等待8个时钟周期 146 //bit_cnt = 8~15,写入命令头8'hfe 147 if(bit_cnt>=4'd8 && bit_cnt <= 4'd15) begin 148 sd_mosi <= HEAD_BYTE[4'd15-bit_cnt]; //先发送高字节 149 if(bit_cnt == 4'd14) 150 wr_req <= 1'b1; //提前拉高写数据请求信号 151 else if(bit_cnt == 4'd15) 152 wr_ctrl_cnt <= wr_ctrl_cnt + 4'd1; //控制计数器加1 153 end 154 end 155 4'd3 : begin //写入数据 156 bit_cnt <= bit_cnt + 4'd1; 157 if(bit_cnt == 4'd0) begin 158 sd_mosi <= wr_data[4'd15-bit_cnt]; //先发送数据高位 159 wr_data_t <= wr_data; //寄存数据 160 end 161 else 162 sd_mosi <= wr_data_t[4'd15-bit_cnt]; //先发送数据高位 163 if((bit_cnt == 4'd14) && (data_cnt <= 9'd255)) 164 wr_req <= 1'b1; 165 if(bit_cnt == 4'd15) begin 166 data_cnt <= data_cnt + 9'd1; 167 //写入单个BLOCK共512个字节 = 256 * 16bit 168 if(data_cnt == 9'd255) begin 169 data_cnt <= 9'd0; 170 //写入数据完成,控制计数器加1 171 wr_ctrl_cnt <= wr_ctrl_cnt + 4'd1; 172 end 173 end 174 end 175 //写入2个字节CRC校验,由于SPI模式下不检测校验值,此处写入两个字节的8'hff 176 4'd4 : begin 177 bit_cnt <= bit_cnt + 4'd1; 178 sd_mosi <= 1'b1; 179 //crc写入完成,控制计数器加1 180 if(bit_cnt == 4'd15) 181 wr_ctrl_cnt <= wr_ctrl_cnt + 4'd1; 182 end 183 4'd5 : begin 184 if(res_en) //SD卡响应 185 wr_ctrl_cnt <= wr_ctrl_cnt + 4'd1; 186 end 187 4'd6 : begin //等待写完成 188 detect_done_flag <= 1'b1; 189 //detect_data = 8'hff时,SD卡写入完成,进入空闲状态 190 if(detect_data == 8'hff) begin 191 wr_ctrl_cnt <= wr_ctrl_cnt + 4'd1; 192 detect_done_flag <= 1'b0; 193 end 194 end 195 default : begin 196 //进入空闲状态后,拉高片选信号,等待8个时钟周期 197 sd_cs <= 1'b1; 198 wr_ctrl_cnt <= wr_ctrl_cnt + 4'd1; 199 end 200 endcase 201 end 202 end 203 204 endmodule SD卡写数据模块主要完成对SD卡的写操作,在程序的第18定义了一个参数HEAD_BYTE(SD 卡写数据头),在开始写入有效数据之前,必须先发送数据头8’hfe。在代码的第60行开始的 always语句块中,同样使用clk_ref_180deg(sd_clk)的上升沿采集数据,其原因同SD初始化 模块一样,clk_ref_180deg的上升沿是数据保持稳定的时刻,采集的数据不会发生错误。 在代码第100行开始的always语句块中,使用写计数控制器(wr_ctrl_cnt)控制写入的流 程。其流程为首先检测开始写入数据信号(wr_start_en)的上升沿,检测到上升沿之后开始 发送写命令(CMD24);写命令发送完成等待SD卡返回响应信号;SD卡返回响应命令后,等待8 个时钟周期,随后写入数据头和数据,注意写数据之前写请求信号要提前拉高,以保证写入数 据时刻数据是有效的;发送完数据之后,再次发送两个字节的CRC校验值,由于SPI模式下不对 数据做校验,这里发送两个字节的8’hff,然后等待SD卡返回响应数据。在接收完SD卡的响应 之后给出标志detect_done_flag,以检测SD卡是否进入空闲状态。当SD卡进入空闲状态后等待8个时钟周期即可重新检测开始写入数据信号(wr_start_en)的上升沿。 图 39.4.6为SD卡写数据过程中SignalTap抓取的波形图。从图中可以看出,在检测到 wr_start_en的上升沿(脉冲信号)后,wr_busy(写忙信号)开始拉高,sd_cs片选信号拉低, 开始对SD卡写命令和数据,wr_req为请求写入的数据,共请求数据256次。在数据及CRC校验写 完后detect_done_flag信号拉高,开始等待SD卡空闲。 图 39.4.6 SD卡写数据SignalTap波形图 图 39.4.7为数据写完后抓取到的SignalTap波形图。从图中可以看出,SD卡返回空闲信号 (sd_miso由低电平变为高电平)后,片选信号开始拉高拉高,detect_done_flag信号拉低, wr_busy(写忙信号)拉低,此时可以进行下一次写操作。 图 39.4.7 SD卡数据写完SignalTap波形图 SD卡读操作模块的代码如下: 1 module sd_read( 2 input clk_ref , //时钟信号 3 input clk_ref_180deg, //时钟信号,与sd_clk相位相差180度 4 input rst_n , //复位信号,低电平有效 5 //SD卡接口 6 input sd_miso , //SD卡SPI串行输入数据信号 7 output reg sd_cs , //SD卡SPI片选信号 8 output reg sd_mosi , //SD卡SPI串行输出数据信号 9 //用户读接口 10 input rd_start_en , //开始读SD卡数据信号 11 input [31:0] rd_sec_addr , //读数据扇区地址 12 output reg rd_busy , //读数据忙信号 13 output reg rd_val_en , //读数据有效信号 14 output reg [15:0] rd_val_data //读数据 15 ); 16 17 //reg define 18 reg rd_en_d0 ; //rd_start_en信号延时打拍 19 reg rd_en_d1 ; 20 reg res_en ; //接收SD卡返回数据有效信号 21 reg [7:0] res_data ; //接收SD卡返回数据 22 reg res_flag ; //开始接收返回数据的标志 23 reg [5:0] res_bit_cnt ; //接收位数据计数器 24 25 reg rx_en_t ; //接收SD卡数据使能信号 26 reg [15:0] rx_data_t ; //接收SD卡数据 27 reg rx_flag ; //开始接收的标志 28 reg [3:0] rx_bit_cnt ; //接收数据位计数器 29 reg [8:0] rx_data_cnt ; //接收的数据个数计数器 30 reg rx_finish_en ; //接收完成使能信号 31 32 reg [3:0] rd_ctrl_cnt ; //读控制计数器 33 reg [47:0] cmd_rd ; //读命令 34 reg [5:0] cmd_bit_cnt ; //读命令位计数器 35 reg rd_data_flag ; //准备读取数据的标志 36 37 //wire define 38 wire pos_rd_en ; //开始读SD卡数据信号的上升沿 39 40 //***************************************************** 41 //** main code 42 //***************************************************** 43 44 assign pos_rd_en = (~rd_en_d1) & rd_en_d0; 45 46 //rd_start_en信号延时打拍 47 always @(posedge clk_ref or negedge rst_n) begin 48 if(!rst_n) begin 49 rd_en_d0 <= 1'b0; 50 rd_en_d1 <= 1'b0; 51 end 52 else begin 53 rd_en_d0 <= rd_start_en; 54 rd_en_d1 <= rd_en_d0; 55 end 56 end 57 58 //接收sd卡返回的响应数据 59 //在clk_ref_180deg(sd_clk)的上升沿锁存数据 60 always @(posedge clk_ref_180deg or negedge rst_n) begin 61 if(!rst_n) begin 62 res_en <= 1'b0; 63 res_data <= 8'd0; 64 res_flag <= 1'b0; 65 res_bit_cnt <= 6'd0; 66 end 67 else begin 68 //sd_miso = 0 开始接收响应数据 69 if(sd_miso == 1'b0 && res_flag == 1'b0) begin 70 res_flag <= 1'b1; 71 res_data <= {res_data[6:0],sd_miso}; 72 res_bit_cnt <= res_bit_cnt + 6'd1; 73 res_en <= 1'b0; 74 end 75 else if(res_flag) begin 76 res_data <= {res_data[6:0],sd_miso}; 77 res_bit_cnt <= res_bit_cnt + 6'd1; 78 if(res_bit_cnt == 6'd7) begin 79 res_flag <= 1'b0; 80 res_bit_cnt <= 6'd0; 81 res_en <= 1'b1; 82 end 83 end 84 else 85 res_en <= 1'b0; 86 end 87 end 88 89 //接收SD卡有效数据 90 //在clk_ref_180deg(sd_clk)的上升沿锁存数据 91 always @(posedge clk_ref_180deg or negedge rst_n) begin 92 if(!rst_n) begin 93 rx_en_t <= 1'b0; 94 rx_data_t <= 16'd0; 95 rx_flag <= 1'b0; 96 rx_bit_cnt <= 4'd0; 97 rx_data_cnt <= 9'd0; 98 rx_finish_en <= 1'b0; 99 end 100 else begin 101 rx_en_t <= 1'b0; 102 rx_finish_en <= 1'b0; 103 //数据头0xfe 8'b1111_1110,所以检测0为起始位 104 if(rd_data_flag && sd_miso == 1'b0 && rx_flag == 1'b0) 105 rx_flag <= 1'b1; 106 else if(rx_flag) begin 107 rx_bit_cnt <= rx_bit_cnt + 4'd1; 108 rx_data_t <= {rx_data_t[14:0],sd_miso}; 109 if(rx_bit_cnt == 4'd15) begin 110 rx_data_cnt <= rx_data_cnt + 9'd1; 111 //接收单个BLOCK共512个字节 = 256 * 16bit 112 if(rx_data_cnt <= 9'd255) 113 rx_en_t <= 1'b1; 114 else if(rx_data_cnt == 9'd257) begin //接收两个字节的CRC校验值 115 rx_flag <= 1'b0; 116 rx_finish_en <= 1'b1; //数据接收完成 117 rx_data_cnt <= 9'd0; 118 rx_bit_cnt <= 4'd0; 119 end 120 end 121 end 122 else 123 rx_data_t <= 16'd0; 124 end 125 end 126 127 //寄存输出数据有效信号和数据 128 always @(posedge clk_ref or negedge rst_n) begin 129 if(!rst_n) begin 130 rd_val_en <= 1'b0; 131 rd_val_data <= 16'd0; 132 end 133 else begin 134 if(rx_en_t) begin 135 rd_val_en <= 1'b1; 136 rd_val_data <= rx_data_t; 137 end 138 else 139 rd_val_en <= 1'b0; 140 end 141 end 142 143 //读命令 144 always @(posedge clk_ref or negedge rst_n) begin 145 if(!rst_n) begin 146 sd_cs <= 1'b1; 147 sd_mosi <= 1'b1; 148 rd_ctrl_cnt <= 4'd0; 149 cmd_rd <= 48'd0; 150 cmd_bit_cnt <= 6'd0; 151 rd_busy <= 1'b0; 152 rd_data_flag <= 1'b0; 153 end 154 else begin 155 case(rd_ctrl_cnt) 156 4'd0 : begin 157 rd_busy <= 1'b0; 158 sd_cs <= 1'b1; 159 sd_mosi <= 1'b1; 160 if(pos_rd_en) begin 161 cmd_rd <= {8'h51,rd_sec_addr,8'hff}; //写入单个命令块CMD17 162 rd_ctrl_cnt <= rd_ctrl_cnt + 4'd1; //控制计数器加1 163 //开始执行读取数据,拉高读忙信号 164 rd_busy <= 1'b1; 165 end 166 end 167 4'd1 : begin 168 if(cmd_bit_cnt <= 6'd47) begin //开始按位发送读命令 169 cmd_bit_cnt <= cmd_bit_cnt + 6'd1; 170 sd_cs <= 1'b0; 171 sd_mosi <= cmd_rd[6'd47 - cmd_bit_cnt]; //先发送高字节 172 end 173 else begin 174 sd_mosi <= 1'b1; 175 if(res_en) begin //SD卡响应 176 rd_ctrl_cnt <= rd_ctrl_cnt + 4'd1; //控制计数器加1 177 cmd_bit_cnt <= 6'd0; 178 end 179 end 180 end 181 4'd2 : begin 182 //拉高rd_data_flag信号,准备接收数据 183 rd_data_flag <= 1'b1; 184 if(rx_finish_en) begin //数据接收完成 185 rd_ctrl_cnt <= rd_ctrl_cnt + 4'd1; 186 rd_data_flag <= 1'b0; 187 sd_cs <= 1'b1; 188 end 189 end 190 default : begin 191 //进入空闲状态后,拉高片选信号,等待8个时钟周期 192 sd_cs <= 1'b1; 193 rd_ctrl_cnt <= rd_ctrl_cnt + 4'd1; 194 end 195 endcase 196 end 197 end 198 199 endmodule SD卡读数据模块主要完成对SD卡的读操作。在代码的第60行开始的always语句块和第91行 开始的always语句块中,同样采用clk_ref_180deg的上升沿采集数据,其原因同SD初始化模块 一样,clk_ref_180deg的上升沿是数据保持稳定的时刻,采集的数据不会发生错误。 在代码第144行开始的always语句块中,使用读计数控制器(rd_ctrl_cnt)控制读取数据 的流程。其流程为检测开始读取数据信号(rd_start_en)的上升沿,检测到上升沿之后开始 发送读命令(CMD17);读命令发送完成之后等待SD卡返回响应信号;SD卡返回响应命令后, 准备接收SD卡的数据头,因为SD卡的数据头为8’hfe = 8’b1111_1110,所以我们只需要检测 SD_MISO输入引脚的第一个低电平即可检测到数据头;检测到数据头之后,紧跟后面的就是256 个16位数据和两个字节的CRC校验值,我们只需接收有效数据,CRC的校验值可不用关心;CRC 校验接收完成后等待8个时钟周期即可检测开始读取数据信号(rd_start_en)的上升沿,再次 对SD卡进行读操作。 图 39.4.8为SD卡读数据过程中SignalTap抓取的波形图。从图中可以看出,在检测到rd_start_en的上升沿(脉冲信号)后,rd_busy(读忙信号)开始拉高,sd_cs片选信号拉低, 开始对SD卡发送读命令;在SD卡返回读命令响应信号后,拉高rd_data_flag信号,开始检测数 据头8’hfe;在检测到数据头8’hfe后,开始接收SD卡返回的有效数据,共返回256个16位数 据;数据接收完成后产生rx_finish_en(接收完成信号)脉冲信号,拉低rd_data_flag和sd_cs (SD片选信号),等待8个时钟周期后拉低rd_busy信号,此时可以进行下一次读操作。 图 39.4.8 SD卡读数据过程中SignalTap波形图 SD卡测试数据产生模块的代码如下: 1 module data_gen( 2 input clk , //时钟信号 3 input rst_n , //复位信号,低电平有效 4 input sd_init_done , //SD卡初始化完成信号 5 //写SD卡接口 6 input wr_busy , //写数据忙信号 7 input wr_req , //写数据请求信号 8 output reg wr_start_en , //开始写SD卡数据信号 9 output reg [31:0] wr_sec_addr , //写数据扇区地址 10 output [15:0] wr_data , //写数据 11 //读SD卡接口 12 input rd_val_en , //读数据有效信号 13 input [15:0] rd_val_data , //读数据 14 output reg rd_start_en , //开始写SD卡数据信号 15 output reg [31:0] rd_sec_addr , //读数据扇区地址 16 17 output error_flag //SD卡读写错误的标志 18 ); 19 20 //reg define 21 reg sd_init_done_d0 ; //sd_init_done信号延时打拍 22 reg sd_init_done_d1 ; 23 reg wr_busy_d0 ; //wr_busy信号延时打拍 24 reg wr_busy_d1 ; 25 reg [15:0] wr_data_t ; 26 reg [15:0] rd_comp_data ; //用于对读出数据作比较的正确数据 27 reg [8:0] rd_right_cnt ; //读出正确数据的个数 28 29 //wire define 30 wire pos_init_done ; //sd_init_done信号的上升沿,用于启动写入信号 31 wire neg_wr_busy ; //wr_busy信号的下降沿,用于判断数据写入完成 32 33 //***************************************************** 34 //** main code 35 //***************************************************** 36 37 assign pos_init_done = (~sd_init_done_d1) & sd_init_done_d0; 38 assign neg_wr_busy = wr_busy_d1 & (~wr_busy_d0); 39 //wr_data_t变化范围0~256;wr_data范围:0~255 40 assign wr_data = (wr_data_t > 16'd0) ? (wr_data_t - 1'b1) : 16'd0; 41 //读256次正确的数据,说明读写测试成功,error_flag = 0 42 assign error_flag = (rd_right_cnt == (9'd256)) ? 1'b0 : 1'b1; 43 44 //sd_init_done信号延时打拍 45 always @(posedge clk or negedge rst_n) begin 46 if(!rst_n) begin 47 sd_init_done_d0 <= 1'b0; 48 sd_init_done_d1 <= 1'b0; 49 end 50 else begin 51 sd_init_done_d0 <= sd_init_done; 52 sd_init_done_d1 <= sd_init_done_d0; 53 end 54 end 55 56 //SD卡写入信号控制 57 always @(posedge clk or negedge rst_n) begin 58 if(!rst_n) begin 59 wr_start_en <= 1'b0; 60 wr_sec_addr <= 32'd0; 61 end 62 else begin 63 if(pos_init_done) begin 64 wr_start_en <= 1'b1; 65 wr_sec_addr <= 32'd2000; //任意指定一块扇区地址 66 end 67 else 68 wr_start_en <= 1'b0; 69 end 70 end 71 72 //SD卡写数据 73 always @(posedge clk or negedge rst_n) begin 74 if(!rst_n) 75 wr_data_t <= 16'b0; 76 else if(wr_req) 77 wr_data_t <= wr_data_t + 16'b1; 78 end 79 80 //wr_busy信号延时打拍 81 always @(posedge clk or negedge rst_n) begin 82 if(!rst_n) begin 83 wr_busy_d0 <= 1'b0; 84 wr_busy_d1 <= 1'b0; 85 end 86 else begin 87 wr_busy_d0 <= wr_busy; 88 wr_busy_d1 <= wr_busy_d0; 89 end 90 end 91 92 //SD卡读出信号控制 93 always @(posedge clk or negedge rst_n) begin 94 if(!rst_n) begin 95 rd_start_en <= 1'b0; 96 rd_sec_addr <= 32'd0; 97 end 98 else begin 99 if(neg_wr_busy) begin 100 rd_start_en <= 1'b1; 101 rd_sec_addr <= 32'd2000; 102 end 103 else 104 rd_start_en <= 1'b0; 105 end 106 end 107 108 //读数据错误时给出标志 109 always @(posedge clk or negedge rst_n) begin 110 if(!rst_n) begin 111 rd_comp_data <= 16'd0; 112 rd_right_cnt <= 9'd0; 113 end 114 else begin 115 if(rd_val_en) begin 116 rd_comp_data <= rd_comp_data + 16'b1; 117 if(rd_val_data == rd_comp_data) 118 rd_right_cnt <= rd_right_cnt + 9'd1; 119 end 120 end 121 end 122 123 endmodule 在代码的第45行开始的always语句块中,对SD卡控制器的初始化完成信号(sd_init_done) 打两拍,以检测SD卡的上升沿,用于发起SD卡的写入操作。在代码的第57行开始的always块中, 当检测到SD卡的上升沿之后,开始发起写入操作,即wr_start_en(开始写SD卡数据信号)和 wr_sec_addr(wr_sec_addr)。这里需要注意的是,写扇区地址并没有从0开始写,0扇区地址 为SD卡的分区引导记录区,如果从0扇区地址开始写入测试数据会破坏SD卡的FAT文件系统,因 此我们随意指定SD卡的中间数据区域开始测试,这里是从扇区地址2000开始写入,读出扇区地 址和写扇区地址保持一致。 测试数据写完后(wr_busy写忙信号的下降沿代表写完数据),就开始从SD卡中读出数据。 需要注意的是代码中的error_flag默认是高电平,即错误状态,正确读取到256次(单个扇区 为512个字节,因为接口封装成16位,因此需要读取256次)时才会变成低电平,目的是在未插 入SD卡时,SD卡无法完成初始化,也就无法对SD卡进行读写测试,如果此时默认低电平的话, 会误以为读写测试正确。 下载验证 首先我们打开SD卡读写测试实验工程,在工程所在的路径下打开top_sd_rw/par文件夹, 在里面找到“top_sd_rw.qpf”并双击打开。注意工程所在的路径名只能由字母、数字以及下 划线组成,不能出现中文、空格以及特殊字符等。工程打开后如图 39.5.1所示: 图 39.5.1 以太网通信实验工程 然后将SD卡适配器(用于插入MicroSD卡)或者SD卡插入开发板的SD卡插槽,注意带有金 属引脚的一面朝上。接下来将下载器一端连接电脑,另一端与开发板上对应端口连接,最后连 接电源线并打开电源开关。 注意:本次实验是基于市面上常用的SD2.0版本协议的SDHC卡或者SD2.0版本协议的 MicroSD卡,存储容量为2GB至32GB。 开拓者开发板实物图如下图所示: 图 39.5.2 开拓者开发板实物图 接下来我们下载程序,验证SD卡读写测试功能。工程打开后通过点击工具栏中的 “Programmer”图标打开下载界面,通过“Add File”按钮选择top_sd_rw/par/output_files 目录下的“top_sd_rw.sof”文件。开发板电源打开后,在程序下载界面点击“Hardware Setup”,在弹出的对话框中选择当前的硬件连接为“USB-Blaster[USB-0]”。然后点击 “Start”将工程编译完成后得到的sof文件下载到开发板中,如图 39.5.3所示: 图 39.5.3 程序下载完成界面 下载完成后开发板上最右侧的LED灯常亮,说明从SD卡读出的512个字节(256个16位数据) 与写入的数据相同,SD卡读写测试程序下载验证成功。 |
|
相关推荐
|
|
1367 浏览 1 评论
助力AIoT应用:在米尔FPGA开发板上实现Tiny YOLO V4
1046 浏览 0 评论
2442 浏览 1 评论
2146 浏览 0 评论
矩阵4x4个按键,如何把识别结果按编号01-16(十进制)显示在两个七段数码管上?
2408 浏览 0 评论
1889 浏览 50 评论
6018 浏览 113 评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-23 16:01 , Processed in 0.535546 second(s), Total 34, Slave 26 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号