基于STM32F407的SDCard读写操作
目录
在之前没有做过SD卡相关,做这个之前参考了正点原子、野火等等资料,花了1天时间,其实在有参考的情况下,难倒是不难,就是资料挺乱的,然后代码上面有的还有不兼容(可能因为我有的资料是F103的),对于做好的东西做一个备份和总结——后续篇幅是关于SD通过USB端口挂载U盘以及通过HAL库的SD卡操作。
-
硬件电路
-
SD卡结构示意图
管脚连接(SDIO方式)
STM32-GPIO | STM32-GPIO 复用功能 | SDCard管脚 | 其他 |
PC8 | SDIO-D0 | DATA0 | |
PC9 | SDIO-D1 | DATA1 | |
PC10 | SDIO-D2 | DATA2 | |
PC11 | SDIO-D3 | CD/DATA3 | |
PC12 | SDIO-CLK | CLK | |
PD2 | SDIO-CMD | CMD |
-
SDCard初始化
-
SDCacr寄存器介绍
SD卡共有8个寄存器,用于设定或表示SD卡信息,参考下表,这些寄存器只能通过对应的命令访问,对SD卡进行读写控制操作并不是像操作控制寄存器GPIO相关寄存器那样一次读写一个寄存器的,它是通过命令来控制的,SDIO定义了64个命令,每个命令都有意义,可以实实现某一特定功能,SD卡接收到命令之后,根据命令的要求对SD卡内部寄存器进行修改,程序控制中只需要发送组合命令就可以实现SD卡的控制以及读写操作。
名称 | Bit宽度 | 描述 |
CID | 128 | 卡识别号:用来识别SD卡的个体号码 |
RCA | 16 | 相对地址:卡的本地系统地址 |
DSR | 16 | 驱动级寄存器:配置卡的输出驱动 |
CSD | 128 | 卡的特定数据:卡的操作条件信息 |
SCR | 64 | SD配置寄存器:SD卡特性信息 |
OCR | 32 | 操作条件寄存器 |
SSR | 512 | SD状态:SD卡专有特征的信息 |
CSR | 32 | 卡的状态:卡的当前状态信息 |
SDCacr初始化代码
首先对SDIO及中断进行设置;
使能GPIOC、GPIOD、DMA2及SDIO时钟
复位SDIO时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC|RCC_AHB1Periph_GPIOD|RCC_AHB1Periph_DMA2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SDIO, ENABLE);
RCC_APB2PeriphResetCmd(RCC_APB2Periph_SDIO, ENABLE);
初始化PC8~PC12及PD2(SDIO接口)
PC8~PC12设置复用功能、推挽输出、上拉
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
PC8~PC12复用功能映射
GPIO_PinAFConfig(GPIOC,GPIO_PinSource8,GPIO_AF_SDIO);
GPIO_PinAFConfig(GPIOC,GPIO_PinSource9,GPIO_AF_SDIO);
GPIO_PinAFConfig(GPIOC,GPIO_PinSource10,GPIO_AF_SDIO);
GPIO_PinAFConfig(GPIOC,GPIO_PinSource11,GPIO_AF_SDIO);
GPIO_PinAFConfig(GPIOC,GPIO_PinSource12,GPIO_AF_SDIO);
GPIO_PinAFConfig(GPIOD,GPIO_PinSource2,GPIO_AF_SDIO);
解除SDIO时钟复位
RCC_APB2PeriphResetCmd(RCC_APB2Periph_SDIO, DISABLE);
设置SD卡的外设寄存器
SDIO->POWER=0x00000000;
SDIO->CLKCR=0x00000000;
SDIO->ARG=0x00000000;
SDIO->CMD=0x00000000;
SDIO->DTIMER=0x00000000;
SDIO->DLEN=0x00000000;
SDIO->DCTRL=0x00000000;
SDIO->ICR=0x00C007FF;
SDIO->MASK=0x00000000;
设置SDIO中断并打开中断
NVIC_InitStructure.NVIC_IRQChannel = SDIO_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority =0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
GPIO相关设置就结束了
接着根据流程图对SD卡进行配置
1.配置SDIO传输模式
SDIO_InitStructure.SDIO_ClockDiv = SDIO_INIT_CLK_DIV;
SDIOCLK = 72MHz, SDIO_CK = HCLK/(178 + 2) = 400 KHz */
SDIO_InitStructure.SDIO_ClockEdge = SDIO_ClockEdge_Rising;
SDIO_InitStructure.SDIO_ClockBypass = SDIO_ClockBypass_Disable;
SDIO_InitStructure.SDIO_ClockPowerSave = SDIO_ClockPowerSave_Disable;
SDIO_InitStructure.SDIO_BusWide = SDIO_BusWide_1b;
SDIO_InitStructure.SDIO_HardwareFlowControl = SDIO_HardwareFlowControl_Disable;
2.进行SD卡上电、初始化及设备参数读取
首先进行SD卡上电:
发送CMD0,等待回应后
发送CMD8,等待R7响应,判断是2.0还是高容量卡。
然后发送CMD41,等待R3响应,判断上电是否完成。
接着进行SD卡初始化:
发送CMD2,等待R2响应,取得CID,
发送CMD3,等待R6响应,
再次发送CMD3,等待R2响应。
最后读取SD卡信息,选中SD卡,
设置数据宽度,设置SD时钟,设置传输模式(查询或DMA)。
3.SDCard初始化
(1)SDCacr基本读写
偷懒的话可以直接去下载已经写好的sdcard读写函数,这种网上有好多
(附sdcard.c及sdcard.h(正点原子STM32F407的)容我研究下怎么上传文件)
对一个块(Block)的读写操作
所用函数:
SD_WriteBlock(*buf,addr,blksize)
SD_ReadBlock(*buf,addr,blksize)
其中,buf是读、写数据缓存区,addr是起始地址,blksize是块大小。
对多个块(MultiBlock)的读写操作
所用函数:
SD_WriteMultiBlocks(*buf,addr,blksize,nblks)
SD_ReadMultiBlocks(*buf,addr,blksize,nblks)
其中,buf是读、写数据缓存区,addr是起始地址,blksize是块大小,nblks是读、写块的数量。
对扇区(Disk)的读写操作
所用函数:
SD_ WriteDisk(*buf,sector,cnt)
SD_ReadDisk(*buf,sector,cnt)
其中,buf是读、写数据缓存区,sector是扇区地址,cnt是扇区个数。
(2)SDCacr文件系统(FATFS)读写
只有在SD卡基本读写块操作成功之后,才能进行FATFS文件的操作。
移植FATFS文件系统之前,先下载FAT文件源码,解压后里面文件挺多的,我们需要的文件主要是diskio.c、diskio.h、ff.c、ff.h、ffconf.h、integer.h。
PS:Fatfs版本还挺多的,好像还有点差别,所以以下仅供参考
(附上述文件,容我研究下怎么上传文件)
Integer.h:是一些数值类型的定义
diskio.c:底层磁盘的操作函数
ff.c:独立于底层介质操作文件的函数
将文件移植到项目文件中,进行编译,如果出现报错一般都是重复定义啊之类的小毛病,KO掉它们。等到编译无错之后,文件系统移植成功。
FATFS是独立于底层介质的应用函数库,对底层介质的操作都需要自己实现,文件仅仅提供一个函数接口,自己添加代码。偷懒的话可以直接去下载已经写好的sdcard读写函数,这种网上有好多,我上传的也是已经加完的。
完成代码的添加后,我们开始进行文件系统操作。
首先我们对SD卡硬件进行初始化
disk_initialize( 0 );
初始化成功才能进行接下来的步骤。
在文件系统中挂载一个工作区,工作区的设备号为0(相当于盘符),fs为指向工作区的指针。
f_mount(&fs,"0",0);
剩下的操作就比较简单了,和一般的读写文件差不多,通过,f_open、f_write、f_read等函数进行操作,详细函数操作可以查询FatFs - Generic FAT Filesystem Module。
不过fatfs版本不同,函数形参会略有不通,我用过几个版本,一开始用的也忘了哪个版本了,总出错,也不知道是我下的有问题,还是一开始代码有问题,后来换了个版本重写倒是好了,
最后,举个栗子:
写入
res = f_open( &fsrc , "0:/Demo.csv" , FA_OPEN_APPEND | FA_WRITE);
res = f_write(&fsrc, &WD, sizeof(WD), &br);
res = f_write(&fsrc, "\n", sizeof("\n"), &br);
f_close(&fsrc);
读取
res = f_open(&fsrc, "0:/Demo.csv", FA_OPEN_EXISTING | FA_READ);
res = f_read( &fsrc, *&textFileBuffer, sizeof(textFileBuffer), &br );
f_close(&fsrc);
删除
res = f_unlink ("0:/Demo.csv");
操作完记得卸载
f_mount(0, "0",0);