打印
[应用相关]

STM32采集SD卡数据

[复制链接]
3021|37
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
SD 卡简介
SD 卡是由 MMC 卡的基础上发展而来的,它是一种基于半导体快闪**器
的新一代**设备,被广泛应用于各种便携设备,如手机、数码相机、多媒体播
放器等。

沙发
chenqiang10|  楼主 | 2018-6-17 21:05 | 只看该作者
SD 卡可以分为三类:SD 卡、SDHC 卡、SDXC 卡

使用特权

评论回复
板凳
chenqiang10|  楼主 | 2018-6-17 21:06 | 只看该作者
它的通信协议也分为两种,一种是 V1.0 版本和 V2.0 版本,所以不同的版本
操作过程中会有一定的差异。对于 SD 卡和 SDHC 卡来说,协议基本是兼容的,
不过 SDXC 卡,区别就大了。在这里我们主要讨论的是 SD 卡和 SDHC 卡。而
SD 卡的通信接口也有两种模式,一种是 SDIO 模式,还有一种是 SPI 模式,这
里我们主要讨论的是 SPI 模式下的 SD 卡。

使用特权

评论回复
地板
chenqiang10|  楼主 | 2018-6-17 21:06 | 只看该作者
使用 STM32 的 SPI 驱动 SD 卡,最高
通信速度可达 18Mbps,每秒可传输数据 2M 字节以上,对于一般的应用足够了。

使用特权

评论回复
5
chenqiang10|  楼主 | 2018-6-17 21:48 | 只看该作者
SD 卡的操作
要操作 SD 卡,最重要的就是如何初始化 SD 卡、读 SD 卡、写 SD 卡。其他
的读取 SD 卡的其他型号信息等,都是次要的,只有偶尔才会用到。而学习初始
化 SD 卡,读 SD 卡之前,我们要先学会怎么向 SD 写命令。

使用特权

评论回复
6
chenqiang10|  楼主 | 2018-6-17 21:50 | 只看该作者
SD 卡写命令操作
SD 卡的命令格式如下:

长度为 48 位,一共分为第一个字节的最高 2 位必须是 01。接下来 6 位是命
令号,比如:CMD0 其实就是 0x00;CMD16 其实是 0x10。第 2 个字节到第 5
个字节是命令参数,命令参数是特定的命令才会有的。剩下第 6 个字节是一个 7
位的 CRC 效验和最低位为 1 组成。

使用特权

评论回复
7
chenqiang10|  楼主 | 2018-6-17 21:51 | 只看该作者
SD 卡的一些命令参数:

使用特权

评论回复
8
chenqiang10|  楼主 | 2018-6-17 21:51 | 只看该作者
写命令的操作步骤为:
1) 如果上次片选没有取消,那么先取消片选,取消片选之后提供格外
的 8 个时钟周期,方便 SD 卡准备好。
2) 选择片选。
3) 检测 SD 卡是否准备好,也就是一直发送 0xFF,直到接收到非 0xFF
为止。
4) 发送 6 个字节的命令格式。包括 1 个字节命令序号,4 个字节的命令
参数,1 个字节的 CRC 效验。注意第 1 个字节最高两位为 01。第 6
个字节最低位为 1。
5) 如果是 CMD12 停止数据传输命令的话,多发送 8 个时钟周期。也就
是发送 0xFF 一次,为了避免影响之后检测是否发送成功。
6) 检测是否发送命令成功。因为我们不知道回应有多少个字节,所以
一直读取回应数据,直到最高两位为 00 时,说明发送成功。

使用特权

评论回复
9
chenqiang10|  楼主 | 2018-6-17 21:52 | 只看该作者
程序实现如下:
static uint8_t SD_WriteCmd(uint8_t cmd, uint32_t dat, uint8_t crc)
{
uint8_t r1 = 0;
uint16_t i = 0;
//--复位 SD 卡,取消上次片选--//
SD_CS_SET;
SPI2_WriteReadData(0xFF); //额外提供 8 个时钟
SD_CS_CLR;
while(SPI2_WriteReadData(0xFF) != 0xFF) //等待卡是否准备好
{
i++;
if(i > 100)
{
return 0xFF; //等待失败返回
}
}
//--发送数据--//
SPI2_WriteReadData(cmd | 0x40);
SPI2_WriteReadData(dat >> 24); //发送 Dat 的最高 8 位
SPI2_WriteReadData(dat >> 16);
SPI2_WriteReadData(dat >> 8);
SPI2_WriteReadData(dat & 0x00FF);
SPI2_WriteReadData(crc & 0x01);
if(cmd == SD_CMD12) //如果是停止数据传输命令,额外多发
一个时钟
{
SPI2_WriteReadData(0xFF);
}
i = 0;
do
{
r1 = SPI2_WriteReadData(0xFF);
i++;
if(i > 100)
{
return 0xFF;
}
}
while((r1 & 0x80) != 0); //发送成功的最高位是 0
return r1;
}

使用特权

评论回复
10
chenqiang10|  楼主 | 2018-6-17 21:52 | 只看该作者
SD 卡初始化操作。
在《SD 卡 2.0 协议.pdf》中,有 SPI 模式的初始化流程它这里分为两种类型的卡初始化,一种 SD 卡 V1.0 协议的初始化和 SD
卡 V2.0 协议的初始化。从上图,我们可以看出初始化的时候我们要用到
CMD0、CMD8、ACMD41、CMD58。其中 CMD8、CMD58 都要查看相应
应答,这里我们来讲一下这些命令的应答。

使用特权

评论回复
11
chenqiang10|  楼主 | 2018-6-17 21:53 | 只看该作者
上电之后初始化之后,进行至少 74 个时钟周期的上电延时,这个是
必须的(具体大家可以查看数据手册复位部分)。

使用特权

评论回复
12
chenqiang10|  楼主 | 2018-6-17 21:59 | 只看该作者
然后进入 SPI 模式。也就是在片选信号为低电平时,发送 CMD0。

使用特权

评论回复
13
chenqiang10|  楼主 | 2018-6-17 22:02 | 只看该作者
发送 CMD8。如果是非法命令,那么该卡是 SD 卡 V1.0 协议(MMC
卡也是得到非法回应)。如果得到正确回应,说明该卡是 SD 卡 V2.0
或者更高版本的协议。

使用特权

评论回复
14
chenqiang10|  楼主 | 2018-6-17 22:02 | 只看该作者
CMD8 正确回应,读取返回值中的“提供电压”和“模式检测”,
看看是否匹配,否则卡出错。

使用特权

评论回复
15
chenqiang10|  楼主 | 2018-6-17 22:26 | 只看该作者
发送 ACMD41。要注意,ACMD41 是特定的应用命令,所以发
送 ACMD41 的时候,要先发送 CMD55 告诉 SD 卡,接下来的命
令是特定的应用命令。

使用特权

评论回复
16
chenqiang10|  楼主 | 2018-6-18 10:14 | 只看该作者
发送 CMD58,读取 CCS,查看 SD 卡是高容量卡(2G 及 2G 以
上为高容量卡)还是标准卡。

使用特权

评论回复
17
chenqiang10|  楼主 | 2018-6-18 10:15 | 只看该作者
当 SD 卡是 V1.0 协议的时候:
发送 CMD58,读取电压检测。如果检测电压出错,表示卡出错。

使用特权

评论回复
18
chenqiang10|  楼主 | 2018-6-18 10:21 | 只看该作者
发送 ACMD41。要注意,ACMD41 是特定的应用命令,所以发
送 ACMD41 的时候,要先发送 CMD55 告诉 SD 卡,接下来的命
令是特定的应用命令。

使用特权

评论回复
19
chenqiang10|  楼主 | 2018-6-18 10:22 | 只看该作者
int8_t SD_Init(void)
{
uint8_t r1, buf[4];
uint16_t i = 0;
SD_GPIO_Config();
SPI2_SetSpeed(SPI_BaudRatePrescaler_256);
/* 将 SD 卡通信模式转换为 SPI 模式,上电默认是用 DAT0 作数据线 */
/* 接到 CMD0 时,CS 信号有效,SPI 模式启动 */
for(i=0; i<0x0F; i++)//初始时,先发送至少 74 个时钟,这个是必须的。
{
SPI2_WriteReadData(0xFF); //发送 8*16 个
}
/* 当读取到 0x01 的时候表示初始化成功 */
i = 0;
while(SD_WriteCmd(SD_CMD0, 0, 0x95) != 0x01)
{
i++;
if(i > 100)
{
return 0xFF; //初始化失败返回 0
}
}
/* 发送 CMD8,检测是否 SD V2.0 */
i = 0;
do
{
i++;
if(i > 100)  //若是发送超过次数跳出循环管
{
break;
}
r1 = SD_WriteCmd(SD_CMD8, 0x01AA, 0x87);
}
while(r1 != 0x01); //发送 CMD8
if(r1 == 0x01) //如果 CMD8 有回应说明是 SD V2.0 协议
{
/* 读取 CMD8 的返回值,检测是否支持电压 */
/* 读取 CMD8 的返回值,检测是否支持电压 */
for(i=0; i<4; i++)
{
buf[i] = SPI2_WriteReadData(0xFF);
}
/* 卡电压不支持电压,返回错误 */
if((buf[2] != 0x01) || (buf[3] != 0xAA))
{
return 0xFF;
}
/* 初始化 SD 卡 */
i = 0;
do
{
i++;
if(i > 100)
{
return 0xFF; //返回失败
}
SD_WriteCmd(SD_CMD55, 0, 0x01);
r1 = SD_WriteCmd(SD_CMD41, 0x40000000, 0x01);
}
while(r1 != 0); //写入 CMD41 成功了,这里省略读取 CMD41
是否设置成功。
/* 检测是 SDHC 卡还是 SD 卡 */
i = 0;
while(SD_WriteCmd(SD_CMD58, 0, 0x01) != 0)
{
i++;
if(i > 100)
{
SD_TYPE = SD_TYPE_ERR;
break;
}
}

使用特权

评论回复
20
chenqiang10|  楼主 | 2018-6-18 10:22 | 只看该作者
/* 读取 OCR */
for(i=0; i<4; i++)
{
buf[i] = SPI2_WriteReadData(0xFF);
}
if(buf[0] & 0x40)
{
SD_TYPE = SD_TYPE_V2HC;
}
else
{
SD_TYPE = SD_TYPE_V2;
}
}

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

39

主题

940

帖子

1

粉丝