发新帖我要提问
12
返回列表
打印
[应用相关]

STM32采集SD卡数据

[复制链接]
楼主: chenqiang10
手机看帖
扫描二维码
随时随地手机跟帖
21
chenqiang10|  楼主 | 2018-6-18 10:22 | 只看该作者 回帖奖励 |倒序浏览
else //否则就是 SD V1.0 或者 MMC V3
{
SD_WriteCmd(SD_CMD55, 0x00, 0x01);
r1 = SD_WriteCmd(SD_CMD41, 0x00, 0x01);
if(r1 <= 1) //对 CMD41 有回应说明是 SD V1.0
{
SD_TYPE = SD_TYPE_V1; //是 V1.0 卡
i = 0;
do
{
if(i > 100)
{
return 0xFF;
}
SD_WriteCmd(SD_CMD55, 0x00, 0x01);
r1 = SD_WriteCmd(SD_CMD41, 0x00, 0x01);
}
while(r1 != 0);
}

使用特权

评论回复
22
chenqiang10|  楼主 | 2018-6-18 10:23 | 只看该作者
else //没有回应说明是 MMC V3
{
SD_TYPE = SD_TYPE_MMC;  //卡类型是 MMC 卡
i = 0;
while(SD_WriteCmd(SD_CMD1, 0, 0x01) != 0)
{
i++;
if(i > 100)
{
return 0xFF;
}
}
}
}
SD_CS_SET;  //取消片选
SPI2_WriteReadData(0xFF);
return 0;
}

使用特权

评论回复
23
chenqiang10|  楼主 | 2018-6-18 10:23 | 只看该作者
SD 卡读取数据操作
SD 卡读取数据有两个命令,一个命令是 CMD17:读取一个扇区(一般
为 512 字节);另一个是 CMD18:读取多个扇区。

使用特权

评论回复
24
chenqiang10|  楼主 | 2018-6-18 10:25 | 只看该作者
单个扇区读取的步骤为:
1) 发送 CMD17。(命令参数是读取扇区地址)
2) 检测卡响应是否发送成功。
3) 等待起始令牌 0xFE。
4) 接收一个扇区的数据。
5) 接收两个字节的 CRC 效验。

使用特权

评论回复
25
chenqiang10|  楼主 | 2018-6-18 10:25 | 只看该作者
多个扇区读取的步骤为:
1) 发送 CMD18。(命令参数是读取扇区地址)
2) 检测卡响应是否发送成功。
3) 然后一个扇区一个扇区的接收数据,这个部分的步骤为:
a) 等待起始令牌 0xFE。
b) 接收一个扇区的数据。
c) 接收两个字节的 CRC 效验。
d) 接收完一个扇区,如果没有结束,返回 a)继续接收;接收结束,
跳到下一步。
4) 发送停止命令 CMD12。

使用特权

评论回复
26
chenqiang10|  楼主 | 2018-6-18 10:40 | 只看该作者
读取扇区的程序实现如下:
int8_t SD_ReadDisk(uint8_t *buf, uint32_t sector, uint8_t num)
{
uint16_t i;
if(SD_TYPE != SD_TYPE_V2HC)
{
sector <<= 9; //转换位字节地址
}
SPI2_SetSpeed(SPI_BaudRatePrescaler_2);
if(num == 1)
{
/* 发送读取命令 */
i = 0;
while(SD_WriteCmd(SD_CMD17, sector, 0x01) != 0);
{
i++;
if(i > 100)
{
return 0xFF; //命令无反应,表明发送命令失败
}
}
/* 接收数据 */
if(SD_ReadData(buf) != 0)
{
return 0xFF;
}
}
else
{
/* 发送连续读取命令 */
i = 0;
while(SD_WriteCmd(SD_CMD18, sector, 0x01) != 0);
{
i++;
if(i > 100)
{
return 0xFF; //命令无反应,表明发送命令失败
}
}
/* 接收数据 */
while(num--)
{
/* 接收数据 */
if(SD_ReadData(buf) != 0)
{
return 0xFF;
}
buf += 512;
}
SD_WriteCmd(SD_CMD12, 0, 0x01); //发送停止信号
}
/* 取消片选 */
SD_CS_SET;
SPI2_WriteReadData(0xFF);
return 0;
}

使用特权

评论回复
27
chenqiang10|  楼主 | 2018-6-18 10:41 | 只看该作者
接收一个扇区数据的函数,具体代码如下:
static int8_t SD_ReadData(uint8_t *buf)
{
uint16_t i;
/* 等待起始令牌 0XFE */
i = 0;
while(SPI2_WriteReadData(0xFF) != 0xFE)
{
i++;
if(i > 0x0FFF)
{
return 0xFF;
}
}
/* 接收数据 */
for(i=0; i<512; i++)
{
*buf = SPI2_WriteReadData(0xFF);
buf++;
}
/* 读完数据再读两位 CRC 效验,但是我们可以不需要它们 */
SPI2_WriteReadData(0xFF);
SPI2_WriteReadData(0xFF);
return 0;
}

使用特权

评论回复
28
chenqiang10|  楼主 | 2018-6-18 10:44 | 只看该作者
SD 卡写入数据操作
SD 卡写入数据也有两个命令,一个命令是 CMD24:写入一个扇区(一
般为 512 字节);另一个是 CMD25:写入多个扇区。(MMC 卡写入多个扇区
之前要先试用特定命令 CMD23 擦除扇区,写入特定命令之前,记得要先发
送命令 CMD55。)

使用特权

评论回复
29
chenqiang10|  楼主 | 2018-6-18 10:46 | 只看该作者
写入一个扇区的操作步骤为:
1) 发送 CMD24(命令参数是写入扇区地址)
2) 检测返回值,查看 CMD24 是否发送成功。
3) 然后发送若干个时钟,同时读取返回值,知道返回值不是 0xFF
4) 发送起始令牌 0xFE。
5) 发送一个扇区的数据。
6) 发送 2 个字节的 CRC 效验。
7) 读取返回值,判断是否写入成功。

使用特权

评论回复
30
chenqiang10|  楼主 | 2018-6-18 10:46 | 只看该作者
写入多个扇区的操作步骤为:
1) 发送 CMD25(命令参数是写入扇区地址)。如果是 MMC 卡,就先
发送 CMD55 和 CMD23 擦除扇区。
2) 检测返回值,查看 CMD25 是否发送成功。
3) 然后一个扇区一个扇区的写入数据,这个部分的步骤为:
a) 然后发送若干个时钟,同时读取返回值,知道返回值不是 0xFF
b) 发送起始令牌 0xFE。
c) 发送一个扇区的数据。
d) 发送 2 个字节的 CRC 效验。
e) 读取返回值,判断是否写入成功。
f)  如果没有写入完成,那么返回 a)继续写入;如果写入完成,那么
跳到下一步。
4) 然后发送若干个时钟,同时读取返回值,知道返回值不是 0xFF
5) 发送结束令牌 0xFD。

使用特权

评论回复
31
chenqiang10|  楼主 | 2018-6-18 10:47 | 只看该作者
写入扇区的程序实现如下:
int8_t SD_WriteDisk(uint8_t *buf, uint32_t sector, uint8_t num)
{
uint8_t i;
if(SD_TYPE != SD_TYPE_V2HC)
{
sector <<= 9; //转换位字节地址
}
SPI2_SetSpeed(SPI_BaudRatePrescaler_2);
/* 只写一个扇区 */
if(num == 1)
{
/* 发送写命令 */
i = 0;
while(SD_WriteCmd(SD_CMD24, sector, 0x01) != 0);
{
i++;
if(i > 100)
{
return 0xFF; //命令无反应,表明发送命令失败
}
}
if(SD_WriteData(buf, 0xFE) != 0)
{
return 0xFF;
}
}

使用特权

评论回复
32
chenqiang10|  楼主 | 2018-6-18 10:48 | 只看该作者
/* 写多个扇区 */
else
{
if(SD_TYPE == SD_TYPE_MMC)  //如果是 MMC 卡
{
SD_WriteCmd(SD_CMD55, 0, 0X01);
SD_WriteCmd(SD_CMD23, num, 0X01); //写入前先擦除 num 个
扇区里面的数据
}
/* 发送连续写命令 */
i = 0;
while(SD_WriteCmd(SD_CMD25, sector, 0x01) != 0);
{
i++;
if(i > 100)
{
return 0xFF; //命令无反应,表明发送命令失败
}
}

使用特权

评论回复
33
chenqiang10|  楼主 | 2018-6-18 10:48 | 只看该作者
/* 开始写数据 */
while(num--)
{
if(SD_WriteData(buf, 0xFC) != 0)
{
return 0xFF;
}
buf += 512;
}
/* 发送若干个时钟,等待 SD 卡准备好 */
i = 0;
while(SPI2_WriteReadData(0xFF) != 0xFF)
{
if(i > 0x0FFF)
{
return 0xFF;
}
}
/* 发送结束命令 */
SPI2_WriteReadData(0xFD);
}
SD_CS_SET;
SPI2_WriteReadData(0xFF);
return 0;
}

使用特权

评论回复
34
chenqiang10|  楼主 | 2018-6-18 10:50 | 只看该作者
往 SD 卡内写入一个扇区数据的函数,代码如下:
static int8_t SD_WriteData(uint8_t *buf, uint8_t cmd)
{
uint16_t i;
/* 发送若干个时钟,等待 SD 卡准备好 */
i = 0;
while(SPI2_WriteReadData(0xFF) != 0xFF)
{
if(i > 0x0FFF)
{
return 0xFF;
}
}
/* 发送起始令牌 */
SPI2_WriteReadData(cmd);
/* 开始写入数据 */
for(i = 0; i<512; i++)
{
SPI2_WriteReadData(*buf);
buf++;
}
/* 发送两位 CRC 效验码,随便发 */
SPI2_WriteReadData(0xFF);
SPI2_WriteReadData(0xFF);
/* 读取返回值 */
i = SPI2_WriteReadData(0xFF);
if((i & 0x1F) != 0x05)  //判断是否写成功
{
SD_CS_SET;
SPI2_WriteReadData(0xFF);
return 0xFF;
}
return 0;
}

使用特权

评论回复
35
chenqiang10|  楼主 | 2018-6-18 10:51 | 只看该作者
读取 CSD 操作。
在主函数中,我们还调用了一个函数,用来读取 SD 的卡的内存容量。
而 SD 卡的内存容量的相关数据都保存在 CSD 寄存器里面。要注意的是,SD
卡 V1.0 协议的 CSD 寄存器和 SD 卡 V2.0 协议的 CSD 寄存器是不相同的,
所以当我们想要读取 SD 卡的内存容量的时候,要注意区分 SD 卡是 V1.0 协
议还是 V2.0 协议。

使用特权

评论回复
36
chenqiang10|  楼主 | 2018-6-18 10:52 | 只看该作者
具体步骤如下:
1) 发送读取命令 CMD9。
2) 检测返回值,查看是否成功发送 CMD9。
3) 等待起始令牌 0xFE。
4) 读取 16 个字节长度的 CSD 寄存器。
5) 读取 2 个字节的 CRC 效验。
6) 取消片选。

使用特权

评论回复
37
chenqiang10|  楼主 | 2018-6-18 10:52 | 只看该作者
读取了 CSD 寄存器,我们怎么计算 SD 卡的容量  V2.0协议的内存容量计算方式
内存容量 = (C_SIZE + 1) * 512。
C_SIZE 在 CSD[73:62]。
V1.0 协议的内容容量计算方式为(在《SD 卡 2.0 协议.PDF》的 81 页):
内存容量 = BLOCKNR * BLOCK_LEN;
BLOCKNR = (C_SIZE + 1) * C_SIZE_MUL;
BLOCK_LEN = (READ_BL_LEN < 12);
C_SIZE_MULT 在 CSD[49:47];READ_BL_LEN 在 CSD[83:80];C_SIZE
在 CSD[73:62]。

使用特权

评论回复
38
chenqiang10|  楼主 | 2018-6-18 10:53 | 只看该作者
读取 SD 卡内存容量的程序实现如下:
int8_t SD_ReadCapacity(uint32_t *capacity)
{
uint8_t csdValue[16];
uint16_t n, i = 0;
/* 发送命令 */
while(SD_WriteCmd(SD_CMD9, 0, 0x01) != 0);
{
i++;
if(i > 100)
{
return 0xFF; //发送命令失败
}
}
/* 等待起始令牌 0XFE */
i = 0;
while(SPI2_WriteReadData(0xFF) != 0xFE)
{
i++;
if(i > 500)
{
return 0xFF;
}
}
/* 读取数据 */
for(i=0; i<16; i++)
{
csdValue[i] = SPI2_WriteReadData(0xFF);
}
/* 读取两位 CRC 效验 */
SPI2_WriteReadData(0xFF); //RCC
SPI2_WriteReadData(0xFF);
/* 取消片选 */
SD_CS_SET;
SPI2_WriteReadData(0xFF);
/* SD V2.0 的卡 CSD 第一个数据是 0x40 */
if((csdValue[0] & 0xC0) == 0x40)
{
/* 计算 C_SIZE,在 CSD[69:48] */
*capacity = csdValue[9] + ((uint16_t)csdValue[8] << 8) + 1;
/* 实际上就是乘以 1024 */
*capacity = (*capacity) << 10;//得到扇区数
}
else
{
/* 内存算法是 capacity = BLOCKNR * BLOCK_LEN */
/* BLOCKNR = (C_SIZE + 1) * MULT; */
/* BLOCK_LEN = (READ_BL_LEN < 12) 或 2^(READ_BL_LEN)
*/
/* 计 算 BLOCK_LEN 在 ,C_SIZE_MULT 在
CSD[49:47];READ_BL_LEN 在 CSD[83:80] */
n = (csdValue[5] & 0x0A) + ((csdValue[10] & 0x80) >> 7)
+ ((csdValue[9] & 0x03) << 1) + 2;
/* 计算 C_SIZE,C_SIZE 在 CSD[73:62] */
*capacity = (csdValue[8] >> 6) + ((uint16_t)csdValue[7] << 2)
+ ((uint16_t)(csdValue[6] & 3) << 10) + 1;
*capacity = (*capacity) << (n - 9);//得到扇区数
}
return 0;
}

使用特权

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

本版积分规则