SD卡的应用代码

SD 卡在正常读写操作之前,必须先对 SD 卡进行初始化, SD 卡的初始化过程就是向 SD 中写入命令,使其工作在预期的工作模式。在对 SD 卡进行读写操作时同样需要先发送写命令和读命令 。SD 卡的命令格式由 6 个字节组成, 发送数据时高位在前 。

顶层文件

module sd_bmp_hdmi(    
    input                 sys_clk      ,  //系统时钟
    input                 sys_rst_n    ,  //系统复位,低电平有效
    //SD卡接口
    input                 sd_miso      ,  //SD卡SPI串行输入数据信号
    output                sd_clk       ,  //SD卡SPI时钟信号
    output                sd_cs        ,  //SD卡SPI片选信号
    output                sd_mosi      ,  //SD卡SPI串行输出数据信号     
    // DDR3                            
    inout   [15:0]        ddr3_dq      ,  //DDR3 数据
    inout   [1:0]         ddr3_dqs_n   ,  //DDR3 dqs负
    inout   [1:0]         ddr3_dqs_p   ,  //DDR3 dqs正  
    output  [13:0]        ddr3_addr    ,  //DDR3 地址   
    output  [2:0]         ddr3_ba      ,  //DDR3 banck 选择
    output                ddr3_ras_n   ,  //DDR3 行选择
    output                ddr3_cas_n   ,  //DDR3 列选择
    output                ddr3_we_n    ,  //DDR3 读写选择
    output                ddr3_reset_n ,  //DDR3 复位
    output  [0:0]         ddr3_ck_p    ,  //DDR3 时钟正
    output  [0:0]         ddr3_ck_n    ,  //DDR3 时钟负
    output  [0:0]         ddr3_cke     ,  //DDR3 时钟使能
    output  [0:0]         ddr3_cs_n    ,  //DDR3 片选
    output  [1:0]         ddr3_dm      ,  //DDR3_dm
    output  [0:0]         ddr3_odt     ,  //DDR3_odt                                                                
    //hdmi接口                             
    output                  tmds_clk_p   ,  // TMDS 时钟通道
    output                  tmds_clk_n   ,
    output  [2:0]           tmds_data_p  ,  // TMDS 数据通道
    output  [2:0]           tmds_data_n  ,
    output                  tmds_oen        // TMDS 输出使能
    );

时钟模块(clk_wiz_0)

clk_wiz_0 u_clk_wiz_0
   (
    // Clock out ports
    .clk_out1              (clk_200m),     
    .clk_out2              (clk_50m),
    .clk_out3              (pixel_clk_5x),
    .clk_out4              (pixel_clk),
    .clk_out5              (clk_50m_180deg),
    // Status and control signals
    .reset                 (1'b0), 
    .locked                (locked),       
   // Clock in ports
    .clk_in1               (sys_clk)
    );     

200Mhz 时钟作为 DDR 控制模块的参考时钟,由 MIG IP 核产生的 ui_clk(100Mhz)作为 DDR 控制模块的驱动时钟, 50Mhz 时钟和 50Mhz(相位偏移 180 度)作为其余各模块的驱动时钟 .

SD 卡读取图片控制模块(sd_read_photo)

SD卡读取BMP图片

module sd_read_photo(
    input                clk           ,  //时钟信号
    input                rst_n         ,  //复位信号,低电平有效
​
    input        [23:0]  ddr_max_addr  ,  //DDR读写最大地址  
    input        [15:0]  sd_sec_num    ,  //SD卡读扇区个数
    input                rd_busy       ,  //SD卡读忙信号
    input                sd_rd_val_en  ,  //SD卡读数据有效信号
    input        [15:0]  sd_rd_val_data,  //SD卡读出的数据
    output  reg          rd_start_en   ,  //开始写SD卡数据信号
    output  reg  [31:0]  rd_sec_addr   ,  //读数据扇区地址
    output  reg          ddr_wr_en     ,  //DDR写使能信号
    output       [15:0]  ddr_wr_data      //DDR写数据
    );

SD 卡控制器模块(sd_ctrl_top)

sd_init ------> SD卡初始化模块

初始化代码完全一样,只需要移植.v文件即可。

module sd_init(
    input          clk_ref       ,  //时钟信号
    input          rst_n         ,  //复位信号,低电平有效
    
    input          sd_miso       ,  //SD卡SPI串行输入数据信号
    output         sd_clk        ,  //SD卡SPI时钟信号
    output  reg    sd_cs         ,  //SD卡SPI片选信号
    output  reg    sd_mosi       ,  //SD卡SPI串行输出数据信号
    output  reg    sd_init_done     //SD卡初始化完成信号
    );
sd_read -----> SD卡读模块
module sd_read(
    input                clk_ref       ,  //时钟信号
    input                clk_ref_180deg,  //时钟信号,与clk_ref相位相差180度
    input                rst_n         ,  //复位信号,低电平有效
    //SD卡接口
    input                sd_miso       ,  //SD卡SPI串行输入数据信号
    output  reg          sd_cs         ,  //SD卡SPI片选信号
    output  reg          sd_mosi       ,  //SD卡SPI串行输出数据信号
       //用户读接口    
    input                rd_start_en   ,  //开始读SD卡数据信号
    input        [31:0]  rd_sec_addr   ,  //读数据扇区地址                        
    output  reg          rd_busy       ,  //读数据忙信号
    output  reg          rd_val_en     ,  //读数据有效信号
    output  reg  [15:0]  rd_val_data      //读数据
    );

读模块可以完全移植,sd卡读取的数据由sd_miso输入,rd_val_data信号是连续读的16个数据,这里是可以对连续的16个数据进行处理,如果想对其他位数进行处理,可以让rx_data_t变成相对应的位数,然后接受n*512/rx_data_t次。然后通过一个always语句输出相对应的数据,并且加一个.v文件来对数据进行处理。

n*512/rx_data_t次-->我们可以通过命令 CMD16 来配置单次读写操作的数据长度,以使每次读写的数据量为(n512) 个字节(n≥1),本次 SD 卡的读写操作使用 SD 卡默认配置,即单次读写操作的数据量为 512 个字节 。

SD 卡的读操作流程如下:

1、 拉低片选 CS 引脚, 发送命令 CMD17(0x51)读取单个数据块,命令发送完成后等待 SD 卡返回响应

数据;

2、 SD 卡返回正确响应数据 0x00 后, 准备开始解析 SD 卡返回的数据头 0xfe;

3、 解析到数据头 0xfe 后,接下来接收 SD 卡返回的 512 个字节的数据;

4、 数据解析完成后,接下来接收两个字节的 CRC 校验值。 由于 SPI 模式下不对数据进行 CRC 校验, 可

直接忽略这两个字节;

5、 校验数据接收完成后,等待 8 个时钟周期;

6、 拉高片选 CS 引脚,等待 8 个时钟周期后允许进行其它操作。

sd_write ----> SD卡写模块
module sd_write(
    input                clk_ref       ,  //时钟信号
    input                clk_ref_180deg,  //时钟信号,与clk_ref相位相差180度
    input                rst_n         ,  //复位信号,低电平有效
    //SD卡接口
    input                sd_miso       ,  //SD卡SPI串行输入数据信号
    output  reg          sd_cs         ,  //SD卡SPI片选信号  
    output  reg          sd_mosi       ,  //SD卡SPI串行输出数据信号
    //用户写接口    
    input                wr_start_en   ,  //开始写SD卡数据信号
    input        [31:0]  wr_sec_addr   ,  //写数据扇区地址
    input        [15:0]  wr_data       ,  //写数据                          
    output  reg          wr_busy       ,  //写数据忙信号
    output  reg          wr_req           //写数据请求信号
    );

在移植sd卡写模块的时候,就无需改变啥代码了,可以直接移植,他是指把wr_data的数据写入到sd卡中,至于这个数据是怎么来的,是通过其他模块得到的。

SD 卡的写操作流程如下:

1、 拉低片选 CS 引脚, 发送命令 CMD24(0x58)读取单个数据块,命令发送完成后等待 SD 卡返回响应数据;

2、 SD 卡返回正确响应数据 0x00 后, 等待至少 8 个时钟周期, 开始发送数据头 0xfe;

3、 发送完数据头 0xfe 后,接下来开始发送 512 个字节的数据;

4、 数据发送完成后,发送 2 个字节的 CRC 校验数据。 由于 SPI 模式下不对数据进行 CRC 校验, 直接发送两个字节的 0xff 即可;

5、 校验数据发送完成后,等待 SD 卡响应;

6、 SD 卡返回响应数据后会进入写忙状态(MISO 引脚为低电平), 即此时不允许其它操作。当检测到MISO

引脚为高电平时, SD 卡此时退出写忙状态;

7、 拉高 CS 引脚,等待 8 个时钟周期后允许进行其它操作。

详细初始化步骤

1, SD 卡完成上电后, 主机 FPGA 先对从机 SD 卡发送至少 74 个以上的同步时钟, 在上电同步期间,片选 CS 引脚和 MOSI 引脚必须为高电平(MOSI 引脚除发送命令或数据外, 其余时刻都为高电平);

2,拉低片选 CS 引脚, 发送命令 CMD0(0x40) 复位 SD 卡,命令发送完成后等待SD 卡返回响应数据;

3, SD 卡返回响应数据后, 先等待 8个时钟周期再拉高片选 CS 信号, 此时判断返回的响应数据。如果返回的数据为复位完成信号 0x01,在接收返回信息期间片选 CS 为低电平,此时 SD 卡进入 SPI 模式,并开始进行下一步, 如果返回的值为其它值, 则重新执行第2步;

4,拉低片选 CS 引脚, 发送命令 CMD8(0x48)查询 SD 卡的版本号, 只有 SD2.0 版本的卡才支持此命令,命令发送完成后等待 SD 卡返回响应数据;

5, SD 卡返回响应数据后, 先等待 8 个时钟周期再拉高片选 CS 信号, 此时判断返回的响应数据。 如果返回的电压范围为 4’b0001 即 2.7V~3.6V, 说明此 SD 卡为 2.0 版本,进行下一步,否则重新执行第 4步;

6, 拉低片选 CS 引脚, 发送命令 CMD55(0x77)告诉 SD 卡下一次发送的命令是应用相关命令,命令发送完成后等待 SD 卡返回响应数据;

7, SD 卡返回响应数据后, 先等待 8 个时钟周期再拉高片选 CS 信号, 此时判断返回的响应数据。 如

果返回的数据为空闲信号 0x01,开始进行下一步,否则重新执行第 6 步。

8, 拉低片选 CS 引脚, 发送命令 ACMD41(0x69)查询 SD 卡是否初始化完成,命令发送完成后等待SD

卡返回响应数据;

9, SD 卡返回响应数据后, 先等待 8 个时钟周期再拉高片选 CS 信号, 此时判断返回的响应数据。 如果返回的数据为 0x00,此时初始化完成,否则重新执行第 6 步。

DDR3控制器模块(ddr3_top)

ddr3_top文件可以一般都是不变的,是用来把ddr3_rw,MIG ip核和fifo控制模块给串起来。总体来说这个DDR3控制模块对于图像显示基本是可以完全移植的,总体思路就是从什么地方得到数据,然后存到wr_fifo中然后存到fifo一次的写位数后哦写入到DDR3中,然后通过rd_fifo从DDR3中得到数据。

具体介绍详见:DDR3介绍-CSDN博客

module ddr3_top(    
    input              sys_clk_i           ,   //MIG IP核输入时钟  
    input              clk_ref_i           ,   //ddr3参考时钟
    input              rst_n               ,   //复位,低有效     
    input              sys_init_done   ,   //系统初始化完成       
    //DDR3接口信号                           
    input   [27:0]     app_addr_rd_min     ,   //读ddr3的起始地址
    input   [27:0]     app_addr_rd_max     ,   //读ddr3的结束地址
    input   [7:0]      rd_bust_len         ,   //从ddr3中读数据时的突发长度
    input   [27:0]     app_addr_wr_min     ,   //读ddr3的起始地址
    input   [27:0]     app_addr_wr_max     ,   //读ddr3的结束地址
    input   [7:0]      wr_bust_len         ,   //从ddr3中读数据时的突发长度
    // DDR3 IO接口 
    inout   [15:0]     ddr3_dq             ,   //ddr3 数据
    inout   [1:0]      ddr3_dqs_n          ,   //ddr3 dqs负
    inout   [1:0]      ddr3_dqs_p          ,   //ddr3 dqs正  
    output  [13:0]     ddr3_addr           ,   //ddr3 地址   
    output  [2:0]      ddr3_ba             ,   //ddr3 banck 选择
    output             ddr3_ras_n          ,   //ddr3 行选择
    output             ddr3_cas_n          ,   //ddr3 列选择
    output             ddr3_we_n           ,   //ddr3 读写选择
    output             ddr3_reset_n        ,   //ddr3 复位
    output  [0:0]      ddr3_ck_p           ,   //ddr3 时钟正
    output  [0:0]      ddr3_ck_n           ,   //ddr3 时钟负
    output  [0:0]      ddr3_cke            ,   //ddr3 时钟使能
    output  [0:0]      ddr3_cs_n           ,   //ddr3 片选
    output  [1:0]      ddr3_dm             ,   //ddr3_dm
    output  [0:0]      ddr3_odt            ,   //ddr3_odt      
    //用户
    input              ddr3_read_valid     ,   //DDR3 读使能   
    input              ddr3_pingpang_en    ,   //DDR3 乒乓操作使能       
    input              wr_clk              ,   //wfifo时钟   
    input              rd_clk              ,   //rfifo的读时钟      
    input              wr_en               ,   //数据有效使能信号
    input   [15:0]     wrdata              ,   //有效数据 
    input              rdata_req           ,   //请求像素点颜色数据输入  
    input              rd_load             ,   //输出源更新信号
    input              wr_load             ,   //输入源更新信号
    output  [15:0]     rddata              ,   //rfifo输出数据
    output             init_calib_complete     //ddr3初始化完成信号
    );    

ddr3_fifo_ctrl
module ddr3_fifo_ctrl(
    input                rst_n            ,  //复位信号
    input                wr_clk           ,  //wfifo时钟
    input                rd_clk           ,  //rfifo时钟
    input                ui_clk           ,  //用户时钟
    input                wr_en            ,  //数据有效使能信号
    input  [15:0]        wrdata          ,   //有效数据
    input  [127:0]       rfifo_din        ,  //用户读数据
    input                rdata_req        ,  //请求像素点颜色数据输入 
    input                rfifo_wren       ,  //从ddr3读出数据的有效使能
    input                wfifo_rden       ,  //wfifo读使能
    input                rd_load          ,  //输出源场信号
    input                wr_load          ,  //输入源场信号          
​
    output [127:0]       wfifo_dout       ,  //用户写数据
    output [9:0]         rfifo_wcount     ,  //rfifo剩余数据计数
    output [9:0]         wfifo_rcount     ,  //wfifo写进数据计数
    output reg [15:0]    rddata              //读有效数据        
    );
ddr3_rw

这个模块是用来控制读写的

module ddr3_rw(          
    input           ui_clk               ,  //用户时钟
    input           ui_clk_sync_rst      ,  //复位,高有效
    input           init_calib_complete  ,  //DDR3初始化完成
    input           app_rdy              ,  //MIG IP核空闲
    input           app_wdf_rdy          ,  //MIG写FIFO空闲
    input           app_rd_data_valid    ,  //读数据有效
    input   [9:0]   wfifo_rcount         ,  //写端口FIFO中的数据量
    input   [9:0]   rfifo_wcount         ,  //读端口FIFO中的数据量
    input           rd_load              ,  //输出源更新信号
    input           wr_load              ,  //输入源更新信号
    input   [27:0]  app_addr_rd_min      ,  //读DDR3的起始地址
    input   [27:0]  app_addr_rd_max      ,  //读DDR3的结束地址
    input   [7:0]   rd_bust_len          ,  //从DDR3中读数据时的突发长度
    input   [27:0]  app_addr_wr_min      ,  //写DDR3的起始地址
    input   [27:0]  app_addr_wr_max      ,  //写DDR3的结束地址
    input   [7:0]   wr_bust_len          ,  //从DDR3中写数据时的突发长度
​
    input           ddr3_read_valid      ,  //DDR3 读使能   
    input           ddr3_pingpang_en     ,  //DDR3 乒乓操作使能          
    output          rfifo_wren           ,  //从ddr3读出数据的有效使能 
    output  [27:0]  app_addr             ,  //DDR3地址                 
    output          app_en               ,  //MIG IP核操作使能
    output          app_wdf_wren         ,  //用户写使能   
    output          app_wdf_end          ,  //突发写当前时钟最后一个数据 
    output  [2:0]   app_cmd                 //MIG IP核操作命令,读或者写       
    );

HDMI 顶层模块(hdmi_top)

显示模块的代码可以移植hdmi的通用.v文件,RGB2DVI 模块 -----> DVI发送端顶层模块和video_driver------>视频显示驱动模块,在显示模块中,当像素坐标处于有有效区范围内时,video_driver会发送一个data_req(数据请求信号),rd_fifo就会从DDR3中一次读出128个数据,128/16=8,即8个像素点位置的数据,通过一个计数器,当这8个像素点的数据都显示出来后,再次让rd_fifo从DDR3中读取128个数据.这样,图像就能通过hdmi线顺利的显示在屏幕上了。

具体的hdmi模块如何移植详见:https://blog.csdn.net/m0_74221172/article/details/136285600

  • 25
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值