豆皮 - STM32开发板入门教程(十三) - SPI模式读写SD卡 (原创)
豆皮- STM32开发板入门教程(十三)- SPI模式读写SD卡(原创)
豆皮- STM32开发板入门教程(十三)- SPI模式读写SD卡(原创)
版权所有STMFANS 原创,转载请保留出处
https://www.360docs.net/doc/8c11340330.html,/bbs/viewthread.php?tid=1203&extra=page%3D1&frombbs=1
功能介绍:使用SPI模式读写SD卡block数据可通过串口发送到PC机查看
SD卡是Secure Digital Card卡的简称,直译成汉语就是“安全数字卡”,是由日本松下公司、东芝公司和美国SANDISK公司共同开发研制的全新的存储卡产品。SD存储卡是一个完全开放的标准(系统),多用于MP3、数码摄像机、数码相机、电子图书、A V器材等等,尤其是被广泛应用在超薄数码相机上。呵呵现在偶们做东西也喜欢用sd卡了为啥?容量大啊价格便宜啊读写次数100000次以上(也有资料说是300000次呵呵)这个次数够猛了啊读写的速度也很快现在高速的SD卡写速度可以达到20M/S 呵呵如果你买的SD卡达不到这个速度就应该考虑一下是不是买到假货了呵呵现在SD卡都是白菜价了啊貌似4G的也就50块钱一个啦
嗯不扯远了继续正题SD卡读写有2种方式一种是SD模式一种是SPI模式SD模式操作时序复杂使用的IO口也多呵呵常用的一般是SPI模式我们今天介绍的也是SPI模式接口简单移植也方便速度也不慢了啊使用高速的SPI模式1M/S应该是很容易达到的刚刚把STM32下的SD卡读写移植好也没测试读写速度呵呵下回有时间把读写速度测试了发上来呵呵
SD卡初始化过程:
1. 初始化STM32的SPI接口使用低速模式
2. 延时至少74clock
3. 发送CMD0,需要返回0x01,进入Idle状态
4. 循环发送CMD55+ACMD41,直到返回0x00,进入Ready状态
5. 设置读写block大小为512byte
5. 把STM32的SPI设置为高速模式
读一个block块的过程
1. 发送CMD17(单块)或CMD18(多块)读命令,返回0x00
2. 接收数据开始令牌0xfe + 正式数据512Bytes + CRC 校验2Bytes
写一个block块的过程
1. 发送CMD24(单块)或CMD25(多块)写命令,返回0x00
2. 发送数据开始令牌0xfe + 正式数据512Bytes + CRC校验2Bytes
附上一个datasheet上的标准spi mode initialization flow
初始化函数
/****************************************************************************** *
* Function Name : SD_MMC_SPI_Init
* Description : SD_MMC_SPI_Init
* Input : None
* Output : None
* Return : zero init success, non-zero init error
******************************************************************************* /
u8 SD_MMC_SPI_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* Enable SPI1 and GPIO clocks */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1 | RCC_APB2Periph_GPIOA |
RCC_APB2Periph_SD_MMC_SPI_CS, ENABLE);
/* Configure SPI1 pins: SCK, MISO and MOSI */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Configure SD_MMC_SPI_CS */
GPIO_InitStructure.GPIO_Pin = SD_MMC_SPI_CS_Pin_CS;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(SD_MMC_SPI_CS, &GPIO_InitStructure);
////////////////////////////////////////////////////////////////////////////////////////////////
/* initialize SPI with lowest frequency */
SD_MMC_Low_Speed();
/* card needs 74 cycles minimum to start up */
for(u8 i = 0; i < 10; ++i)
{
/* wait 8 clock cycles */
SD_MMC_ReadWrite_Byte(0x00);
}
/* address card */
SD_MMC_SPI_SELECT();
/* reset card */
u8 response;
for(u16 i = 0; ; ++i)
{
response = SD_MMC_Send_Command(CMD_GO_IDLE_STATE , 0 );
if( response == 0x01 )
break;
if(i == 0x1ff)
{
SD_MMC_SPI_DESELECT();
return 1;
}
}
/* wait for card to get ready */
for(u16 i = 0; ; ++i)
{
response = SD_MMC_Send_Command(CMD_SEND_OP_COND, 0);
if(!(response & (1 << R1_IDLE_STATE)))
break;
if(i == 0x7fff)
{
SD_MMC_SPI_DESELECT();
return 1;
}
}
/* set block size to 512 bytes */
if(SD_MMC_Send_Command(CMD_SET_BLOCKLEN, 512))
{
SD_MMC_SPI_DESELECT();
return 1;
}
/* deaddress card */
SD_MMC_SPI_DESELECT();
/* switch to highest SPI frequency possible */
SD_MMC_High_Speed();
return 0;
}
发送一个命令
/****************************************************************************** *
* Function Name : SD_MMC_Send_Command
* Description : SD_MMC_Send_Command
* Input : None
* Output : None
* Return : None
******************************************************************************* /
u8 SD_MMC_Send_Command(u8 cmd, u32 arg)
{
u8 Response;
u8 Retry = 0;
SD_MMC_ReadWrite_Byte(0xff);
SD_MMC_SPI_SELECT();
//分别写入命令
SD_MMC_ReadWrite_Byte(cmd | 0x40);
SD_MMC_ReadWrite_Byte(arg >> 24);
SD_MMC_ReadWrite_Byte(arg >> 16);
SD_MMC_ReadWrite_Byte(arg >> 8);
SD_MMC_ReadWrite_Byte(arg);
SD_MMC_ReadWrite_Byte(0x95);
do{
// 等待响应,响应的开始位为0
Response = SD_MMC_ReadWrite_Byte(0xff);
Retry++;
}while( ((Response&0x80)!=0) && (Retry < 200) );
SD_MMC_SPI_DESELECT();
return Response; //返回状态值
}
读一个block块读取成功返回0 非0 则读取失败
/****************************************************************************** *
* Function Name : SD_MMC_Read_Single_Block
* Description : SD_MMC_Read_Single_Block
* Input : sector number and buffer data point
* Output : None
* Return : zero success, non-zero error
******************************************************************************* /
u8 SD_MMC_Read_Single_Block(u32 sector, u8* buffer)
{
u8 Response;
u16 i;
u16 Retry = 0;
//读命令send read command
Response = SD_MMC_Send_Command(CMD_READ_SINGLE_BLOCK, sector<<9);
if(Response != 0x00)
return Response;
SD_MMC_SPI_SELECT();
// start byte 0xfe
while(SD_MMC_ReadWrite_Byte(0xff) != 0xfe)
{
if(++Retry > 0xfffe)
{
SD_MMC_SPI_DESELECT();
return 1; //timeout
}
}
for(i = 0; i < 512; ++i)
{
//读512个数据
*buffer++ = SD_MMC_ReadWrite_Byte(0xff);
}
SD_MMC_ReadWrite_Byte(0xff); //伪crc
SD_MMC_ReadWrite_Byte(0xff); //伪crc
SD_MMC_SPI_DESELECT();
SD_MMC_ReadWrite_Byte(0xff); // extra 8 CLK
return 0;
}
写一个block块成功返回0 非0 则写入失败
/****************************************************************************** *
* Function Name : SD_MMC_Write_Single_Block
* Description : SD_MMC_Write_Single_Block
* Input : sector number and buffer data point
* Output : None
* Return : zero success, non-zero error.
******************************************************************************* /
u8 SD_MMC_Write_Single_Block(u32 sector, u8* buffer)
{
u8 Response;
u16 i;
u16 retry=0;
//写命令send write command
Response = SD_MMC_Send_Command(CMD_WRITE_SINGLE_BLOCK, sector<<9);
if(Response != 0x00)
return Response;
SD_MMC_SPI_SELECT();
SD_MMC_ReadWrite_Byte(0xff);
SD_MMC_ReadWrite_Byte(0xff);
SD_MMC_ReadWrite_Byte(0xff);
//发开始符start byte 0xfe
SD_MMC_ReadWrite_Byte(0xfe);
//送512字节数据send 512 bytes data
for(i=0; i<512; i++)
{
SD_MMC_ReadWrite_Byte(*buffer++);
}
SD_MMC_ReadWrite_Byte(0xff); //dummy crc
SD_MMC_ReadWrite_Byte(0xff); //dummy crc
Response = SD_MMC_ReadWrite_Byte(0xff);
//等待是否成功judge if it successful
if( (Response&0x1f) != 0x05)
{
SD_MMC_SPI_DESELECT();
return Response;
}
//等待操作完wait no busy
while(SD_MMC_ReadWrite_Byte(0xff) != 0x00)
{
if(retry++ > 0xfffe)
{
SD_MMC_SPI_DESELECT();
return 1;
}
}
SD_MMC_SPI_DESELECT();
SD_MMC_ReadWrite_Byte(0xff);// extra 8 CLK
return 0;
}
做这个SD卡读写的时候有一个意外的收获我的一个1g的sd卡有问题时好时坏的用winhex查看发现0扇区的内容有问题主引导分区的数据不对劲看下面这个截图
然后我找来一个好的1g SD卡把0扇区copy出来然后写到我的有问题的SD卡里面再插到电脑上格式化一遍就能正常使用了呵呵读写了几回大文件数据等都没有出错(以前读写1,2回就报错了)呵呵下面的截图是用程序把0扇区重写了的写成功了后再读一遍0扇区然后数据从UART发出来确认是写对了的
呵呵不知道是不是真的被修好了反正先用着嘿嘿