【紫光同创PGL22G学习】五、以SD卡为例,上手SPI接口使用

导读:本篇文章旨在通过SD卡的读写实例,上手SPI接口!

一、实验简介

SD 卡(Secure Digital Memory Card)是一种基于半导体闪存工艺的存储卡, 1999年由日本松下主导概念,参与者东芝和美国 SanDisk 公司进行实质研发而完成。 2000年这几家公司发起成立了 SD 协会(Secure Digital Association 简称 SDA),阵容强大,吸引了大量厂商参加。其中包括 IBM, Microsoft, Motorola, NEC、 Samsung 等。在这些领导厂商的推动下, SD 卡已成为目前消费数码设备中应用最广泛的一种存储卡。

PGL22G板载 SD 卡,支持 SPI 模式,使用的 SD 卡为 MicroSD 卡,与PGL22G的信号连接图如下图所示:
在这里插入图片描述

SD 卡是内部集成nand flash 控制器,方便了主机的的管理。本实验主要是练习对 sd 卡的扇区进行读写,通常 sd 卡都有文件系统,可以按照文件名和目录路径来读写文件,但文件系统非常复杂,本实验不做讲解,在后续的实验中我们通过搜索特定的文件头来读特殊的文件,完成音频播放、图片读取显示等。

二、实验原理

2.1、硬件描述

开发板上装有一个 Micro SD 卡座,FPGA 通过 SPI 数据总线访问 Micro SD 卡, SD 卡座和 FPGA 的硬件电路连接如下:

在这里插入图片描述
在 SD 卡数据读写速度要求不高的情况下,选用 SPI 通信模式可以说是一种最佳的解决方案。因为在 SPI 模式下,通过四条线就可以完成所有的数据交换。本实验将为大家介绍 FPGA 通过 SPI 总线读写 SD 卡。要完成 SD 卡的 FPGA 读写,用户需要理解 SD 卡的命令协议

2.2、SD卡协议简介

SD 卡的协议是一种简单的 命令/响应 的协议。全部命令由主机发起,SD 卡接收到命令后并返回响应数据。根据命令的不同,返回的数据内容和长度也不同

SD 卡命令是一个 6 字节组成的命令包,其中:

  • 1 个字节为命令号, 命令号高位 bit7bit6 为固定的“01“,其它 6 个 bit 为具体的命令号
  • 2 ~ 5 个字节为命令参数
  • 6 个字节为: 7 个 bit 的 CRC 校验 + 1 个 bit 的结束位。如果在 SPI 模式的时候,CRC 校验位为可选。

命令格式如下图所示,Command 表示命令,通常使用十进制表示名称,例如 CMD17,这个时候 Command 就是十进制的 17。SD 卡具体的协议本实验不讲解,可自行找相关资料学习。

在这里插入图片描述
SD 卡对每个命令会返回一个响应,每个命令有一定的响应格式。响应的格式跟给它的命令号有关。在 SPI 模式中,有三种响应格式:R1,R2,R3

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.2.1、SD卡2.0版的初始化步骤

  • ①、上电后延时至少 74 clock,等待 SD 卡内部操作完成
  • ②、片选 CS 低电平选中 SD 卡
  • ③、发送 CMD0,需要返回 0x01,进入 Idle 状态
  • ④、为了区别 SD 卡是 2.0 还是 1.0,或是 MMC 卡,这里根据协议向上兼容的判断方法:
    • 首先发送只有 SD2.0 才有的命令 CMD8,如果 CMD8 返回无错误,则初步判断为 2.0 卡
    • 进一步循环发送命令 CMD55+ACMD41,直到返回 0x00确定 SD2.0 卡
  • ⑤、如果 CMD8 返回错误则判断为 1.0 卡还是 MMC 卡,循环发送 CMD55+ACMD41,返回无错误,则为 SD1.0 卡,到此 SD1.0 卡初始成功
    • 如果在一定的循环次数下,返回为错误,则进一步发送 CMD1 进行初始化,如果返回无错误,则确定为 MMC 卡
    • 如果在一定的循环次数下返回为错误,则不能识别该卡,初始化结束。(通过 CMD16 可以改变 SD 卡一次性读写的长度
  • ⑥、CS 拉高

2.2.2、SD卡的读步骤

  • ①、发送 CMD17单块)或 CMD18多块命令,返回 0x00
  • ②、接收数据:开始令牌 fe(或 fc)+ 正式数据 512Bytes + CRC 校验 2Bytes默认正式传输的数据长度512Bytes

在这里插入图片描述

2.2.3、SD卡的写步骤

  • ①、发送 CMD24(单块)或 CMD25(多块)命令,返回 0x00
  • ②、发送数据:开始令牌 fe(或 fc)+ 正式数据 512Bytes + CRC 校验 2Bytes

在这里插入图片描述

三、程序设计

下面主要对 sd_card_top 及其3个子程序进行介绍和说明。

首先,介绍顶层模块sd_card_top,它输入输出端口如下图所示:

在这里插入图片描述

接着介绍sd_card_top 包含的 3 个子程序,分别为sd_card_sec_read_write.vsd_card_cmd.vspi_master.v 文件。它们的逻辑关系如下图所示:

在这里插入图片描述

在这里插入图片描述

3.1、sd_card_sec_read_write模块

以下为 sd_card_sec_read_write 模块端口说明:

信号名称方向说明
rstin异步复位输入,高复位
clkin时钟输入
---
spi_clk_divinSPI 时钟分频,SPI 时钟频率=系统时钟/(( spi_clk_div + 2)*2)
---
sd_init_doneoutsd 卡初始化完成
---
sd_sec_readinsd 卡扇区读请求
sd_sec_read_addrinsd 卡扇区读地址
sd_sec_read_dataoutsd 卡扇区读出的数据
sd_sec_read_data_validoutsd 卡扇区读出的数据有效
sd_sec_read_endoutsd 卡扇区读完成
---
sd_sec_writeinsd 卡扇区写请求
sd_sec_write_addrsd_sec_write_addrsd 卡扇区写请求地址
sd_sec_write_datainsd 卡扇区写请求数据
sd_sec_write_data_reqoutsd 卡扇区写请求数据读取,提前 sd_sec_write_data 一个时钟周期
sd_sec_write_endoutsd 卡扇区写请求完成
---
cmd_reqinsd 卡命令请求
cmd_req_ackoutsd 卡命令请求应答
cmd_req_erroroutsd 卡命令请求错误
---
cmdinsd 卡命令,命令+参数+CRC,一共 48bit
cmd_r1insd 卡命令期待的 R1 响应
cmd_data_leninsd 卡命令后读取的数据长度,大部分命令没有读取数据
---
block_read_reqin块数据读取请求
block_read_validout块数据读取数据有效
block_read_dataout块数据读取数据
block_read_req_ackout块数据读取请求应答
---
block_write_reqin块数据写请求
block_write_datain块数据写数据
block_write_data_rdout块数据写数据请求,提前 block_write_data 一个时钟周期
block_write_req_ackout块数据写请求应答

sd_card_sec_read_write 模块有一个状态机,首先完成 SD 卡初始化,下图为模块的初始化状态机转换图,首先发送 CMD0 命令,然后发送 CMD8 命令,再发送 CMD55,接着发送 CMD41CMD16。如果应答正常,sd 卡初始化完成,等待 SD 卡扇区的读写命令。

在这里插入图片描述

然后等待扇区读写指令,并完成扇区的读写操作,下图为模块的读写状态机转换图。

在这里插入图片描述
在此模块中定义了两个参数,SD 卡的初始化过程是需要先用慢时钟来发送命令和配置,等待初始化成功后再用快时钟来进行数据读写

parameter SPI_LOW_SPEED_DIV = 248
parameter SPI_HIGH_SPEED_DIV = 0

3.2、sd_card_cmd.v模块

sd_card_cmd 模块端口的说明如下:

信号名称方向说明
rstin异步复位输入,高复位
sys_clkin时钟输入
---
spi_clk_divinSPI 时钟分频,SPI 时钟频率=系统时钟/(( spi_clk_div + 2)*2)
---
cmd_reqinsd 卡命令请求
cmd_req_ackoutsd 卡命令请求应答
cmd_req_erroroutsd 卡命令请求错误
---
cmdinsd 卡命令,命令+参数+CRC,一共 48bit
cmd_r1insd 卡命令期待的 R1 响应
cmd_data_leninsd 卡命令后读取的数据长度,大部分命令没有读取数据
---
block_read_reqin块数据读取请求
block_read_validout块数据读取数据有效
block_read_dataout块数据读取数据
block_read_req_ackout块数据读取请求应答
---
block_write_reqin块数据写请求
block_write_datain块数据写数据
block_write_data_rdout块数据写数据请求,提前 block_write_data 一个时钟周期
block_write_req_ackout块数据写请求应答
---
nCS_ctrlout到 SPI master 控制器,cs 片选控制
clk_divout到 SPI Master 控制器,时钟分频参数
---
spi_wr_reqout到 SPI Master 控制器,写一个字节请求
spi_wr_ackin来自 SPI Master 控制器,写请求应答
spi_data_inout到 SPI Master 控制器,写数据
spi_data_outin来自 SPI Master 控制器,读数据

sd_card_cmd 模块主要实现 sd 卡基本命令操作,还有上电初始化的 80(≥74) 个周期的时钟,数据块的命令和读写的状态机如下。

在这里插入图片描述

SD 2.0 的标准里我们可以看到,从主控设备写命令到 SD 卡, 最高两位 47~46 位必须为“01”,代表命令发送开始。

在这里插入图片描述

所以代码中都是将 48 位命令的高八位与十六进制 0x40或操作得到的结果再写入,所以才有了如下一段代码:

在这里插入图片描述

3.3、spi_master模块

spi_master 模块主要完成 SPI 一个字节的读写,当 SPI 状态机在 idle 的时候,检测到 wr_req 的信号为高,会产生 8 个 DCLK,并把 data in 的数据从高位依次输出到 MOSI 信号线上。MOSI 在 8 个 DCLK 的输出数据为 data in 的值 0x58。

同时 spi_master 程序也会读取 MISO 输入的数据,转换成 8 位的 data_out 数据输出实现 SPI的一个字节的数据读取。

四、实验现象

下载实验程序后,可以看到 LED 显示一个二进制数字,这个数字是存储在 sd 卡中第一扇区的第一个数据数据是随机的,这个时候按键 KEY2 按下,数字加一,并写入了 sd 卡,再次下载程序,可以看到直接显示更新后的数据。

在这里插入图片描述

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ReCclay

如果觉得不错,不妨请我喝杯咖啡

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值