万利的板子上创建SD卡文件系统 (一)<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
---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上的代码上来最后终于读出0扇区,而且与读卡器加WinHEX软件读出来的一致(有图为证)。我用的是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_InitTypeDef GPIO_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 €<€t€<uuL[1]t€<t<tV ^|[1]W_s3Ou}=Uu|Invalid partition tableError loading operating systemMissing operating system
在串口助手和Winhex软件读出来的分别是下图:
每个人的电脑和SD卡不同,这些数据会有小差异,但基本相同,如果你的试验也是这些数据,那么,恭喜你,你也做成SD卡读写试验了,没成功也不要紧,检查SD卡插紧了没有。跳线接了没有,量量电压对不对,等等,一定能做成。
还有很多人用IAR,可以把我的几个文件拷到你的IAR工程,编译,调试,不难的,我开始也用的IAR。
好了,第一课就上到这里,下一次开始介绍FAT了,谢谢你的关注,如果你参考我的例子做成了,或者认为我的代码和经验有帮助,就顶我一下啊。祝你好运。
用户377235 2014-1-4 00:19
用户312603 2010-12-12 17:22