chuanhuibh 发表于 2008-7-28 22:09:37

万利EK-stm32创建SD卡文件系统(一)SD卡读写

万利的板子上创建SD卡文件系统 (一)
                     ---SD卡读写
    之前在AVR上做过SD和FAT文件系统,万利的EK-STM32F板子上有SD接口,正好可以做一个基于SD的FAT文件系统,打算要做成支持FAT32和FAT16,通过串口与PC连接,可以显示SD卡中的文件信息,也可以把文件读出来,例如,把指定文件名的txt文件通过串口显示到PC。
   我是去年过年以前决定开始做MP3,由于一直用AVR,所以自然就选用那个流传最广的方案——AVR+SD+VS1003,自己做了PCB,最后终于做成了,主要参考www.ourdev.cn上波仔的MEGA8版本的MP3(感谢波仔),其间花了大量时间学习SD卡读写和FAT文件系统的知识,现在正好用上。
   昨天晚上本来想用KEIL的SD卡工程,但是一直不成功,于是装到以前在AVR上的代码上来。我用的是Keil3.22,这个版本比较新,可以从Keil的中文网站上下载,最大的好处就是使用方便,已经开始支持J-Link2了,万利的板子可以用Keil调试了,当然老版本的Keil可以先生成hex再用串口烧写到STM32。
EK-STM32F板子上使用的是SPI1访问SD卡,还有一个SD卡的Power和CS线,另外一个PA2接到了SD卡插入检测,大家可以不理会,(万利不专业,原理图上没有画出来,板子背面写了)。
软件: Keil3.22工程
硬件:
SD   STM32
SCK-   PA5
MISO —   PA6
MOSI —   PA7
POWER—PD10
CS   -PD9
具体的可以参考原理图,这个光盘里有,万利网站上有。
SPI和GPIO的初始化如下。
void SPI_Config(void)
{
GPIO_InitTypeDefGPIO_InitStructure;
SPI_InitTypeDef   SPI_InitStructure;

/* GPIOA and GPIOD Periph clock enable */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOD, ENABLE);
/* SPI1 Periph clock enable */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);

/* Configure SPI1 pins: SCK, MISO and MOSI */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);

/* Configure PD9 pin: CS pin,PD10 : SD Power*/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9|GPIO_Pin_10;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOD, &GPIO_InitStructure);

/* SPI1 Config */
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(SPI1, &SPI_InitStructure);

/* SPI1 enable */
SPI_Cmd(SPI1, ENABLE);
}

定义两个宏,分别是SD卡选中和不选中
#define        SPI_CS_Assert()   GPIO_ResetBits(GPIOD, GPIO_Pin_9)
/* Deselect MSD Card: ChipSelect pin high */
#define SPI_CS_Deassert()    GPIO_SetBits(GPIOD, GPIO_Pin_9)

对于万利的板子,还要一个上电的过程,该MOS管是的电平导通。
在SPI初始化之后,我加了一句GPIO_ResetBits(GPIOD, GPIO_Pin_10);就是SD上电。注意上电之后,最好有一段延时,这样给SD内部初始化流充足的时间,避免初始化的失败。
初始化SD卡代码如下:
//sd卡复位                //reset sd card (software)
uint8 MMC_SD_Reset(void)
{
        uint8 i;
        uint8 retry;
        uint8 r1=0;
        retry = 0;

        SPI_Config();
    GPIO_ResetBits(GPIOD, GPIO_Pin_10);// SD卡上电,打开MOS管
    delay(1000);                         // 重要的延时
/* MSD chip select high */
    SPI_CS_Deassert();
        do
        {
                for(i=0;i<10;i++) SPI_WriteByte(0xff);
                r1 = MMC_SD_SendCommand(0, 0);//发idle命令        //send idle command
                retry++;
                if(retry>100) return 1;//超时退出                //time out
        } while(r1 != 0x01);       


        retry = 0;
        do
        {
                r1 = MMC_SD_SendCommand(1, 0);//发active命令        //send active command
                retry++;
                if(retry>200) return 1;//超时退出                //time out
        } while(r1);
        //SPI_High();
        r1 = MMC_SD_SendCommand(59, 0);//关crc                //disable CRC

        r1 = MMC_SD_SendCommand(16, 512);//设扇区大小512        //set sector size to 512
        return 0;//正常返回                //normal return
}

还有两个基本函数,即写和读SPI(读写合二为一)
unsigned char SPI_WriteByte(unsigned char data)
{
uint8 Data = 0;

/* Wait until the transmit buffer is empty */
while (SPI_GetFlagStatus(SPI1, SPI_FLAG_TXE) == RESET);
/* Send the byte */
SPI_SendData(SPI1, data);

/* Wait until a data is received */
while (SPI_GetFlagStatus(SPI1, SPI_FLAG_RXNE) == RESET);
/* Get the received data */
Data = SPI_ReceiveData(SPI1);

/* Return the shifted data */
return Data;
}

往SD卡中写一个命令的函数

//sd卡写命令                //sd send command
uint8 MMC_SD_SendCommand(uint8 cmd, uint32 arg)
{
        uint8 r1;
        uint8 retry=0;
       
        SPI_WriteByte(0xff);
        SPI_CS_Assert();
       
        SPI_WriteByte(cmd | 0x40);//分别写入命令        //send command
        SPI_WriteByte(arg>>24);
        SPI_WriteByte(arg>>16);
        SPI_WriteByte(arg>>8);
        SPI_WriteByte(arg);
        SPI_WriteByte(0x95);
       
        while((r1 = SPI_WriteByte(0xff)) == 0xff)//等待响应,        //wait response
                if(retry++ > 200) break;//超时退出                                        //time out error

        SPI_CS_Deassert();

        return r1;//返回状态值                                        //return state
}

有了这些函数之后就可以读写SD卡了,主要是读一个扇区及写一个扇区,注意SD卡只能是读写扇区,即512 个字节。具体代码见我的工程文件的SD_MMC.c, uint8 MMC_SD_ReadSingleBlock(uint32 sector, uint8* buffer)读第sector扇区到Buffer。uint8 MMC_SD_WriteSingleBlock(uint32 sector, uint8* buffer)是写扇区。
{

为了调试方便,我把串口1接上了(最左边哪个口),初始化成功后,把0扇区的512个字节读出来,大部分为0,只有前后的一些字节不为0,调试助手接到的字符为
3|P P&not;      ���uuLt �t <t
V^ |W _s3 Ou}=Uu|Invalid partition tableError loading operating systemMissing operating system 7

每个人的电脑和SD卡不同,这些数据会有小差异,但基本相同,如果你的试验也是这些数据,那么,恭喜你,你也做成SD卡读写试验了,没成功也不要紧,检查SD卡插紧了没有。跳线接了没有,量量电压对不对,等等,一定能做成。
还有很多人用IAR,可以把我的几个文件拷到你的IAR工程,编译,调试,不难的,我开始也用的IAR。

   好了,第一课就上到这里,下一次开始介绍FAT了,谢谢你的关注,如果你参考我的例子做成了,或者认为我的代码和经验有帮助,就顶我一下啊。祝你好运。

bjj9217 发表于 2008-7-28 22:18:24

好!

eddia2000 发表于 2008-7-29 07:54:38

好文章!

erxun 发表于 2008-7-29 08:23:22

顶啊,正好现在也在调试stm32驱动sd卡

dovebird 发表于 2008-7-29 08:50:57

做个记号好好研究。

abcdezh 发表于 2008-7-29 09:18:27

mark
谢谢

yayongzhang 发表于 2008-7-29 09:44:45

记号

mail2li 发表于 2008-7-29 09:53:23

赞扬。。。。mark

like027 发表于 2008-7-29 14:41:54

还是先找个SD memory的参考文档看看好。

SD+CARD完整参考文档ourdev_361752.rar(文件大小:5.69M) (原文件名:SD+CARD参考文档.rar)

dogcome 发表于 2008-7-29 16:24:12

谢谢!

Feeling_MCU 发表于 2008-7-29 20:02:09

顶!嘿嘿

sunsky 发表于 2008-7-29 22:19:40

mark

doom 发表于 2008-7-29 22:54:08

路过

hl1200aa 发表于 2008-7-30 00:27:34

万利提供的STM32F10x_Demo工程里面有SD卡的读写程序,具体在msd.c和msd.h文件中。下面是一张512M SD卡的头512byte的数据:
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 2D 00 06 08 E8 E2 E9 00 00 00 17 4B 0F 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 AA

muyuyuzhong 发表于 2008-9-30 12:56:39

楼主能否解释一下--我读头512字节成功,也就是说地址0,但我读其他512个字节老是不成功,为什么呢?&nbsp;&nbsp;&nbsp;是不是读取其他512字节要激活SD卡才行啊

418425051 发表于 2008-9-30 15:00:20

顶一下老帖

chuanhuibh 发表于 2008-9-30 22:41:42

【14楼】&nbsp;muyuyuzhong&nbsp;:

应该没有这个问题,你用winhex软件和读卡器在电脑读出来对比以下,看下STM32&nbsp;读的对不对

teltium 发表于 2008-10-1 13:55:04

记号

muyuyuzhong 发表于 2008-10-2 16:35:58

哈哈,我知道我为什么读取其他块错误了,原来我以为地址0是从0~511字节,地址1就是512~1023,我在读取512~1023的数据时误以为地址是1,其实要想读取512~1023的数据,地址应该是&nbsp;0&nbsp;+&nbsp;(块号数&nbsp;*&nbsp;块长度)&nbsp;,即是&nbsp;0&nbsp;+&nbsp;1&nbsp;*&nbsp;512&nbsp;,这里一个块是512个字节.

我是参照万利的例程改的,里面也包含了st7920液晶模块的串行和并行的驱动程序,感兴趣的可以看看&nbsp;

万利配套的SD驱动和st7920串并行驱动ourdev_437791.rar(文件大小:1.24M)&nbsp;(原文件名:LUCKY_API.rar)&nbsp;



本贴被 muyuyuzhong 编辑过,最后修改时间:2008-10-02,22:42:11.

essencezj 发表于 2008-11-4 16:34:35

。。。为啥每次读的512字节内容都不一样呢

kgp0213 发表于 2008-11-5 09:08:49

深圳英贝特提供的sd卡读写范例在st2.02库下初始化SD卡有问题,单步可以执行,全速则出错。换用1.0库则无问题。请问有没有其他人碰到次问题?

yayongzhang 发表于 2008-11-21 17:38:34

一定要注意POWER—&nbsp;&nbsp;PD10&nbsp;配制成输出即可

否者的话,初始化都不能成功

020411 发表于 2008-11-26 09:32:12

谢谢

xycqsou 发表于 2009-1-9 10:24:12

请问一下,POWER线要接到SD卡的哪一个引脚啊?

chuanhuibh 发表于 2009-1-12 13:17:05

我也忘了,你在&nbsp;&nbsp;这个论坛搜索SD,可以找到许多份资料,有一个介绍了管脚

zyzoe2003 发表于 2009-1-12 14:19:36

顶啊,正好现在也在调试stm32驱动sd卡

jerico 发表于 2009-1-12 15:56:00

//sd卡写命令&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//sd&nbsp;send&nbsp;command&nbsp;

uint8&nbsp;MMC_SD_SendCommand(uint8&nbsp;cmd,&nbsp;uint32&nbsp;arg)&nbsp;

{&nbsp;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;uint8&nbsp;r1;&nbsp;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;uint8&nbsp;retry=0;&nbsp;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SPI_WriteByte(0xff);&nbsp;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SPI_CS_Assert();&nbsp;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SPI_WriteByte(cmd&nbsp;|&nbsp;0x40);//分别写入命令&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//send&nbsp;command&nbsp;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SPI_WriteByte(arg>>24);&nbsp;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SPI_WriteByte(arg>>16);&nbsp;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SPI_WriteByte(arg>>8);&nbsp;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SPI_WriteByte(arg);&nbsp;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SPI_WriteByte(0x95);&nbsp;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;while((r1&nbsp;=&nbsp;SPI_WriteByte(0xff))&nbsp;==&nbsp;0xff)//等待响应,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//wait&nbsp;response&nbsp;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(retry++>&nbsp;200)&nbsp;break;//超时退出&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//time&nbsp;out&nbsp;error&nbsp;



&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SPI_CS_Deassert();&nbsp;



&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;r1;//返回状态值&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//return&nbsp;state&nbsp;

}&nbsp;



请问为什么片选之前要发个0xff啊?

woshiyujiayang 发表于 2009-2-1 15:00:33

楼主,为什么我的512M卡O扇区读的于WINHEX不同啊?

woshiyujiayang 发表于 2009-2-1 16:24:00

楼主,你的程序把我害的好惨,不过还是要谢谢你,你给了我一个模板

hedianchao 发表于 2009-4-20 21:38:09

楼主你好,不知道为什么。我的SD卡读的时候有时候好使有时候不好使,不好使的时候居多。每次都要拔一下SD卡再插上,才能偶尔好使。但是一旦好使了,就这么复位都好使,断电之后在插上的时候还得重新拔卡,再插。才能偶尔一两次好使。特在此虚心请教,感谢楼主!

zz_apple 发表于 2009-7-23 16:58:17

lz好!我在读sd卡的时候,第一个扇区可以读,并且和winhex下看到的内容一样,但是其他的扇区都是0,不存在上面那位朋友说的地址问题,我把sector号左移9位作为命令参数发送的,请lz帮想下是怎么回事?
谢谢!

tangliangbo 发表于 2009-7-30 17:27:57

jaky80000 发表于 2009-8-27 17:30:00

MARK

dingdangCat 发表于 2009-8-27 18:22:38

mark

xinjie1023 发表于 2009-9-20 09:12:13

mark

cqsgcqsg 发表于 2009-11-19 23:37:12

看看

yu_studio 发表于 2009-11-20 01:03:30

做个记号

xiaoxuanfeng 发表于 2010-3-3 15:56:24

mark

damao0668 发表于 2010-3-19 17:17:47

想问个小问题,关于:对文本写入文字
首先是写入:我先在文本所在起始扇区:开始写入54个字符
然后就是读取这54个字符:但读取的是所以扇区512个字符,所以读出来的数据里面:除了有自己写入的54个外还有512字节中其它的没用乱码出现
请问这应该怎样解决呢?

jinpoiu 发表于 2010-3-19 21:10:30

请问楼主,我也读出了SD卡的第0扇区,也是能读到partition table和operating system什么的,但是用winhex读出来的不是这些东西啊,开头是PwrShot CANON_DC FAT16等一些东西(佳能相机格式化的卡),这是怎么回事呢?

cndongj 发表于 2010-4-6 15:00:55

mark,谢谢

chengyi2009 发表于 2010-4-20 19:41:19

请问第二课在哪里啊?好东西

hang2480 发表于 2010-6-15 18:12:07

ding ding ding

liliang9554 发表于 2010-6-18 00:12:59

发送6条芯片询价信息就能获得十元话费,试试去
http://www.21ic.com/invad/invad.htm

sunchao151 发表于 2010-8-4 14:42:17

mark

zouyf12 发表于 2010-8-10 00:35:45

说真的,SD卡读写其实没有用SPI不太好,感觉支持不好,我是从网上抄的程序,和楼主的一样。但是看了SD卡实际的读写时序,其实要复杂一些。最搞笑的就是发送命令, 要重试200次以上才有效果,当时我只重试50次,结果老是启动不了。。。。

sankunchai 发表于 2010-8-17 09:55:05

楼主很好,我自己参考时序图写的程序一次就读取成功了,就是发现不同的卡初始化速度都有些差别,不知道怎样才能确定出一个标准的初始化速度,让不同的卡都能准确初始化。

ckliushx 发表于 2011-3-10 10:22:23

楼主这个帖子真的很好。可工程文件在哪呢

sunhaiben 发表于 2011-3-10 13:47:24

谢谢,正在研究SD卡,太及时了。

106415718 发表于 2011-3-25 14:07:32

mark~!!

123332 发表于 2011-4-9 19:48:38

最近在看SD卡读写的程序,还是楼主的介绍比较容易理解

liaohui198821 发表于 2011-5-17 16:38:54

liuyuhui2012 发表于 2011-9-13 08:48:04

貌似我的读写扇区有问题

MATT_C 发表于 2012-9-6 13:22:35

MARK,正在调试

songjing1228 发表于 2013-5-31 16:07:51

mark                        

ewing 发表于 2013-8-4 00:34:03

能支持多大的SD?16G行不行?

蓝蓝的恋 发表于 2014-10-28 16:38:38

头晕!{:smile:}

cece_co 发表于 2014-10-28 16:39:34

mark            
页: [1]
查看完整版本: 万利EK-stm32创建SD卡文件系统(一)SD卡读写