打印
[STM32F1]

STM32 基础篇——SD 卡实验

[复制链接]
楼主: aizaixiyuanqian
手机看帖
扫描二维码
随时随地手机跟帖
41
aizaixiyuanqian|  楼主 | 2018-3-23 14:37 | 只看该作者 回帖奖励 |倒序浏览
写入一个扇区的操作步骤为:
1) 发送 CMD24(命令参数是写入扇区地址)
2) 检测返回值,查看 CMD24 是否发送成功。
3) 然后发送若干个时钟,同时读取返回值,知道返回值不是 0xFF
4) 发送起始令牌 0xFE。
5) 发送一个扇区的数据。
6) 发送 2 个字节的 CRC 效验。
7) 读取返回值,判断是否写入成功。

使用特权

评论回复
42
aizaixiyuanqian|  楼主 | 2018-3-23 14:39 | 只看该作者
发送 CMD25(命令参数是写入扇区地址)。如果是 MMC 卡,就先
发送 CMD55 和 CMD23 擦除扇区。

使用特权

评论回复
43
aizaixiyuanqian|  楼主 | 2018-3-23 14:40 | 只看该作者
检测返回值,查看 CMD25 是否发送成功。
然后一个扇区一个扇区的写入数据,这个部分的步骤为:
a) 然后发送若干个时钟,同时读取返回值,知道返回值不是 0xFF
b) 发送起始令牌 0xFE。
c) 发送一个扇区的数据。
d) 发送 2 个字节的 CRC 效验。
e) 读取返回值,判断是否写入成功。
f)  如果没有写入完成,那么返回 a)继续写入;如果写入完成,那么
跳到下一步。

使用特权

评论回复
44
aizaixiyuanqian|  楼主 | 2018-3-23 14:41 | 只看该作者
然后发送若干个时钟,同时读取返回值,知道返回值不是 0xFF
发送结束令牌 0xFD。

使用特权

评论回复
45
aizaixiyuanqian|  楼主 | 2018-3-23 14:42 | 只看该作者
写多个扇区
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;
}
}

使用特权

评论回复
46
aizaixiyuanqian|  楼主 | 2018-3-23 14: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; //命令无反应,表明发送命令失败
}
}

使用特权

评论回复
47
aizaixiyuanqian|  楼主 | 2018-3-23 14:49 | 只看该作者
本帖最后由 aizaixiyuanqian 于 2018-3-23 14:51 编辑

/* 开始写数据 */
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;
}

使用特权

评论回复
48
aizaixiyuanqian|  楼主 | 2018-3-23 14:54 | 只看该作者
写 512 个数据数据
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;
}

使用特权

评论回复
49
aizaixiyuanqian|  楼主 | 2018-3-23 14:55 | 只看该作者
在主函数中,我们还调用了一个函数,用来读取 SD 的卡的内存容量。
而 SD 卡的内存容量的相关数据都保存在 CSD 寄存器里面。要注意的是,SD
卡 V1.0 协议的 CSD 寄存器和 SD 卡 V2.0 协议的 CSD 寄存器是不相同的,
所以当我们想要读取 SD 卡的内存容量的时候,要注意区分 SD 卡是 V1.0 协
议还是 V2.0 协议。具体的 CSD 寄存器结构,这里就不贴出来了,大家可以
查看《SD 卡 2.0 协议.PDF》,里面有详细列表。

使用特权

评论回复
50
aizaixiyuanqian|  楼主 | 2018-3-23 15:18 | 只看该作者
我们在上面学习过如何读取 SD 卡数据之后,我们在学习读取 CSD 寄存
器就很简单了,它们的操作其实差不多的,具体步骤如下:
1) 发送读取命令 CMD9。
2) 检测返回值,查看是否成功发送 CMD9。
3) 等待起始令牌 0xFE。
4) 读取 16 个字节长度的 CSD 寄存器。
5) 读取 2 个字节的 CRC 效验。
6) 取消片选。

使用特权

评论回复
51
aizaixiyuanqian|  楼主 | 2018-3-23 15:19 | 只看该作者
读取了 CSD 寄存器,我们怎么计算 SD 卡的容量呢,首先我们来看 V2.0
协议的内存容量计算方式(在《SD 卡 2.0 协议.PDF》的 87 页)。
内存容量 = (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]。

使用特权

评论回复
52
aizaixiyuanqian|  楼主 | 2018-3-23 15:20 | 只看该作者
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;
}
}

使用特权

评论回复
53
aizaixiyuanqian|  楼主 | 2018-3-23 15:20 | 只看该作者
/* 读取数据 */
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;
}

使用特权

评论回复
54
aizaixiyuanqian|  楼主 | 2018-3-23 17:32 | 只看该作者
主程序
int main(void)
{
uint8_t ledState, num[6] = {0, 0, 0, 0, 0, 0};
uint32_t i, sdCapacity;
/* 彩屏显示 */
TFT_Init();
FLASH_Init();
GUI_DisplayInit();
/* 初始化 */
USART1_Config(9600);
LED_Config();
while(SD_Init())
{
GUI_Show12Char(0, 84, "SD 卡初始化错误!", RED, BLACK);
}
GUI_Show12Char(0, 84, "SD 卡初始化成功! ", RED, BLACK);
SD_ReadCapacity(&sdCapacity);
sdCapacity >>= 11; //将 KB 转为 MB
printf("\nSD 卡容量为:%d\n", sdCapacity);

使用特权

评论回复
55
aizaixiyuanqian|  楼主 | 2018-3-23 17:33 | 只看该作者
/* 显示 SD 卡类型 */
if(SD_TYPE == 0x06)
{
GUI_Show12Char(96, 105, "SDV2HC OK!", RED, BLACK);
}
else if(SD_TYPE == 0x04)
{
GUI_Show12Char(96, 105, "SDV2 OK!", RED, BLACK);
}
else if(SD_TYPE == 0x02)
{
GUI_Show12Char(96, 105, "SDV1 OK!", RED, BLACK);
}
else if(SD_TYPE == 0x02)
{
GUI_Show12Char(96, 105, "MMC OK!", RED, BLACK);
}

使用特权

评论回复
56
aizaixiyuanqian|  楼主 | 2018-3-23 17:33 | 只看该作者
/* 显示 SD 卡容量 */
num[0] = (sdCapacity /10000 % 10) + '0';
num[1] = (sdCapacity /1000 % 10) + '0';
num[2] = (sdCapacity /100 % 10) + '0';
num[3] = (sdCapacity /10 % 10) + '0';
num[4] = (sdCapacity % 10) + '0';
GUI_Show12Char(96, 126, num, RED, BLACK);

使用特权

评论回复
57
aizaixiyuanqian|  楼主 | 2018-3-23 17:52 | 只看该作者
由于 SD 卡读写测试会破坏 SD 卡的 FAT 数据,所以这里就不测试读写了,
大家可以自己添加,不过对 SD 卡读写之后,重新使用 SD 卡就要重新格式化了,
大家注意保存 SD 卡的文件。

使用特权

评论回复
58
aizaixiyuanqian|  楼主 | 2018-3-23 17:53 | 只看该作者
本次的SD实验是基于SPI通讯,下次再分享下关于SDIO的,

使用特权

评论回复
59
824| | 2022-3-3 11:14 | 只看该作者
nice,学习的好资料

使用特权

评论回复
60
kiwis66| | 2022-3-4 13:39 | 只看该作者
老板,能分享下pdf么

使用特权

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

本版积分规则