FATFS版本FATFS R0.13b
SD卡容量16G

概述

本文的重点是进行Fatfs文件系统的移植和初步的使用。TF卡的操作实际上是指令操作,即你想它发送固定的CMD指令,它接收到指令给你返回,因此只要是它支持的通讯方式,其实操作起来原理都是差不多的。后面会稍微展开介绍一些。
这里用的是SPI通讯,模拟SPI通讯,就是用普通的IO口模拟SPI的时序,当作SPI跟SD卡通讯。就是在这个部分,抄了一个别人开源的程序,导致我在找bug的过程中,搞清楚了整个文件系统,甚至是扇区的一些东西。
所以总结成这篇文章作为纪念。

1 模拟SPI

具体时序是什么,网上已经有很多了。因为是某个大神已经公开的代码,所以我就直接贴在这里。网上其他代码大多都是转的他的,不过不知道是历史原因还是什么,他代码里有一个错误。
发送指令时,对应的地址他做了移位操作(addr<<9),这里是他的文章链接:nios ii之Micro SD卡(TF卡)spi。这个操作的意思是,一个扇区512个字节,读写某个扇区,起始地址就是扇区*512,也就是传入的addr是扇区号,指令需要的是实际的起始地址。但是经过测试发现,现在指令传入的地址,SD卡死直接作为扇区号去操作的,因此不需要移位。
另外发送命令前,需要先取消片选,等待8个时钟,再打开片选。否则有些SD卡会通讯不成功。
SD_spi_Solution.h

#ifndef SD_SPI_SOLUTION_H_
#define SD_SPI_SOLUTION_H_
#include "mico.h"       //头文件,根据芯片更改#define CMD0    0   /* GO_IDLE_STATE */
#define CMD55   55  /* APP_CMD */
#define ACMD41  41  /* SEND_OP_COND (ACMD) */
#define CMD1    1   /* SEND_OP_COND */
#define CMD17   17  /* READ_SINGLE_BLOCK */
#define CMD8    8   /* SEND_IF_COND */
#define CMD18   18  /* READ_MULTIPLE_BLOCK */
#define CMD12   12  /* STOP_TRANSMISSION */
#define CMD24   24  /* WRITE_BLOCK */
#define CMD25   25  /* WRITE_MULTIPLE_BLOCK */
#define CMD13   13  /* SEND_STATUS */
#define CMD9    9   /* SEND_CSD */
#define CMD10   10  /* SEND_CID */#define CSD     9
#define CID     10//这部分应根据具体的连线来修改!
//SD_MISO(38脚),SD_CLK(37脚),SD_MOSI(36脚),SD_CS(35脚)
#define SD_CS        (MICO_GPIO_35)
#define SD_MISO      (MICO_GPIO_38)
#define SD_CLK       (MICO_GPIO_37)
#define SD_MOSI      (MICO_GPIO_36)
//MISO
#define  SPI_MI (MicoGpioInputGet((mico_gpio_t)SD_MISO))
//CS
#define  SPI_CS_L   (MicoGpioOutputLow( (mico_gpio_t)SD_CS ))
#define  SPI_CS_H   (MicoGpioOutputHigh( (mico_gpio_t)SD_CS ))
//CLK
#define  SPI_CLK_L  (MicoGpioOutputLow( (mico_gpio_t)SD_CLK ))
#define  SPI_CLK_H  (MicoGpioOutputHigh( (mico_gpio_t)SD_CLK ))
//MOSI
#define  SPI_MO_L   (MicoGpioOutputLow( (mico_gpio_t)SD_MOSI ))
#define  SPI_MO_H   (MicoGpioOutputHigh( (mico_gpio_t)SD_MOSI ))
//delay 1us(actually not,it maybe is several us,I don't test it)
void usleep(u16 i);//set CS low
void CS_Enable();//set CS high and send 8 clocks
void CS_Disable();//write a byte
void SDWriteByte(u8 data);//read a byte
u8 SDReadByte();//send a command and send back the response
u8  SDSendCmd(u8 cmd,u32 arg,u8 crc);//reset SD card
u8 SDReset();//initial SD card
u8 SDInit();
//when init set speed low
u8 SDSetLowSpeed();
//after init set speed high
u8 SDSetHighSpeed();//read a single sector
u8 SDReadSector(u32 addr,u8 * buffer);//read multiple sectors
u8 SDReadMultiSector(u32 addr,u8 sector_num,u8 * buffer);//write a single sector
u8 SDWriteSector(u32 addr,u8 * buffer);//write multiple sectors
u8 SDWriteMultiSector(u32 addr,u8 sector_num,u8 * buffer);//get CID or CSD
u8 SDGetCIDCSD(u8 cid_csd,u8 * buffer);void SD_DisSelect(void);
u8 SD_WaitReady(void);
u8 SD_Select(void);//spi speed(0-255),0 is fastest
//u8 spi_speed;
//SD IO init
void SDIOinit(void);#endif /* SD_SPI_SOLUTION_H_ */

SD_spi_Solution.c

#include "SD_spi_Solution.h"
u8  SD_Type=0;//SD卡的类型
u16 spi_speed = 100; //the spi speed(0-255),0 is fastest
//这个读写速度回间接影响到芯片能查找多少文件,这个值过大会导致查询时间过长,引起看门狗复位,这里改为通过函数确定该值的大小
u8 sd_state = 0;
//delay 1us?¨actually not??it maybe is several us??I don't test it??
void usleep(u16 i)
{while (i > 0){i--;};
}//set CS low
void CS_Enable(void)
{//set CS lowSPI_CS_L;
}//set CS high and send 8 clocks
void CS_Disable(void)
{//set CS highSPI_CS_H;//send 8 clocksSDWriteByte(0xff);
}//write a byte
void SDWriteByte(u8 data)
{u8 i;//write 8 bits(MSB)for (i = 0; i < 8; i++){SPI_CLK_L;usleep(spi_speed);if (data & 0x80)SPI_MO_H;elseSPI_MO_L;data <<= 1;SPI_CLK_H;usleep(spi_speed);}//when DI is free,it should be set highSPI_MO_H;
}//read a byte
u8 SDReadByte(void)
{u8 data = 0x00, i;//read 8 bit(MSB)for (i = 0; i < 8; i++){SPI_CLK_L;usleep(spi_speed);SPI_CLK_H;data <<= 1;if (SPI_MI)data |= 0x01;usleep(spi_speed);}return data;
}
//取消选择,释放SPI总线
void SD_DisSelect(void)
{SPI_CS_H;SDWriteByte(0xff);//提供额外的8个时钟
}//等待卡准备好
//返回值:0,准备好了;其他,错误代码
u8 SD_WaitReady(void)
{u32 t=0;do{if(SDReadByte()==0XFF)return 0;//OKt++;            }while(t<0XFFFFFF);//等待 return 1;
}//选择sd卡,并且等待卡准备OK
//返回值:0,成功;1,失败;
u8 SD_Select(void)
{SPI_CS_L;if(SD_WaitReady()==0)return 0;//等待成功SD_DisSelect();return 1;//等待失败
}
//send a command and send back the response
u8 SDSendCmd(u8 cmd, u32 arg, u8 crc)
{u8 r1;  u8 Retry=0; SD_DisSelect();//取消上次片选if(SD_Select())return 0XFF;//片选失效 //发送SDWriteByte(cmd | 0x40);//分别写入命令SDWriteByte(arg >> 24);SDWriteByte(arg >> 16);SDWriteByte(arg >> 8);SDWriteByte(arg);     SDWriteByte(crc); if(cmd==CMD12)SDWriteByte(0xff);//Skip a stuff byte when stop reading//等待响应,或超时退出Retry=0X1F;do{r1=SDReadByte();}while((r1&0X80) && Retry--);    //返回状态值return r1;
}//reset SD card
u8 SDReset(void)
{u8 i, r1, time = 0;//set CS highCS_Disable();//send 128 clocksfor (i = 0; i < 80; i++){SDWriteByte(0xff);}//set CS lowCS_Enable();//send CMD0 till the response is 0x01do{r1 = SDSendCmd(CMD0, 0, 0x95);time++;//if time out,set CS high and return r1if (time > 254){//set CS high and send 8 clocksCS_Disable();return r1;}} while (r1 != 0x01);//set CS high and send 8 clocksCS_Disable();return 0;
}//initial SD card(send CMD55+ACMD41 or CMD1)
void SDIOinit(void)
{GPIO_InitTypeDef GPIO_Initure;__HAL_RCC_GPIOF_CLK_ENABLE();                   //使能GPIOF时钟//PF7 8 9GPIO_Initure.Pin=GPIO_PIN_7|GPIO_PIN_9;GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP;              //复用推挽输出GPIO_Initure.Pull=GPIO_PULLUP;                  //上拉GPIO_Initure.Speed=GPIO_SPEED_FAST;             //快速    HAL_GPIO_Init(GPIOF,&GPIO_Initure);             //初始化GPIO_Initure.Pin=GPIO_PIN_8;GPIO_Initure.Mode=GPIO_MODE_INPUT;              //复用推挽输出GPIO_Initure.Pull=GPIO_PULLUP;                  //上拉GPIO_Initure.Speed=GPIO_SPEED_FAST;             //快速    HAL_GPIO_Init(GPIOF,&GPIO_Initure);             //初始化//片选 PF10GPIO_Initure.Pin=GPIO_PIN_10;                                       //片选 PF10GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP;          //开输出GPIO_Initure.Pull=GPIO_PULLUP;                  //上拉GPIO_Initure.Speed=GPIO_SPEED_FAST;             //快速
//    GPIO_Initure.Alternate=GPIO_AF5_SPI5;           //复用为SPI5SHAL_GPIO_Init(GPIOF,&GPIO_Initure);             //初始化HAL_GPIO_WritePin(GPIOF,GPIO_PIN_10,GPIO_PIN_SET);   //  拉高
}u8 SDInit(void)
{u8 r1,a;      // 存放SD卡的返回值u16 retry;  // 用来进行超时计数u8 buf[4];  u16 i;u8 time = 0;SDIOinit(); SDSetLowSpeed(); //速度设为低速for(i=0;i<10;i++)SDWriteByte(0XFF);//发送最少74个脉冲retry=20;CS_Enable();do{r1=SDSendCmd(CMD0,0,0x95);//进入IDLE状态}while((r1!=0X01) && retry--);if(r1==0X01){a = SDSendCmd(CMD8,0x1AA,0x87);if(a==1)//SD V2.0{for(i=0;i<4;i++)buf[i]=SDReadByte();    //Get trailing return value of R7 respif(buf[2]==0X01&&buf[3]==0XAA)//卡是否支持2.7~3.6V{retry=0XFFFE;do{SDSendCmd(CMD55,0,0X01);    //发送CMD55r1=SDSendCmd(CMD41,0x40000000,0X01);//发送CMD41}while(r1&&retry--);if(retry&&SDSendCmd(CMD58,0,0X01)==0)//鉴别SD2.0卡版本开始{for(i=0;i<4;i++)buf[i]=SDReadByte();//得到OCR值if(buf[0]&0x40)SD_Type=SD_TYPE_V2HC;    //检查CCSelse SD_Type=SD_TYPE_V2;   }}else//SD V1.x/ MMC V3{SDSendCmd(CMD55,0,0X01);        //发送CMD55r1=SDSendCmd(CMD41,0,0X01); //发送CMD41if(r1<=1){       SD_Type=SD_TYPE_V1;retry=0XFFFE;do //等待退出IDLE模式{SDSendCmd(CMD55,0,0X01);    //发送CMD55r1=SDSendCmd(CMD41,0,0X01);//发送CMD41}while(r1&&retry--);}else//MMC卡不支持CMD55+CMD41识别{SD_Type=SD_TYPE_MMC;//MMC V3retry=0XFFFE;do //等待退出IDLE模式{                                               r1=SDSendCmd(CMD1,0,0X01);//发送CMD1}while(r1&&retry--);  }if(retry==0||SDSendCmd(CMD16,512,0X01)!=0)SD_Type=SD_TYPE_ERR;//错误的卡}}}//set CS high and send 8 clocksCS_Disable();SDSetHighSpeed(); //速度设为高速if(SD_Type)return 0;else if(r1)return r1;      return 0xaa;//其他错误
}void SDSetLowSpeed(void)
{spi_speed = 100;
}void SDSetHighSpeed(void)
{spi_speed = 50;
}
//read a single sector
u8 SDReadSector(u32 addr, u8 *buffer)
{u8 r1;u16 i, time = 0;//set CS lowCS_Enable();//send CMD17 for single block read// addr=addr*512;r1 = SDSendCmd(CMD17, addr, 0x55);//if CMD17 fail,returnif (r1 != 0x00){//set CS high and send 8 clocksCS_Disable();return r1;}//continually read till get the start byte 0xfedo{r1 = SDReadByte();time++;//if time out,set CS high and return r1if (time > 30000){//set CS high and send 8 clocksCS_Disable();return r1;}} while (r1 != 0xfe);//read 512 Bits of datafor (i = 0; i < 512; i++){buffer[i] = SDReadByte();}//read two bits of CRCSDReadByte();SDReadByte();//set CS high and send 8 clocksCS_Disable();return 0;
}//read multiple sectors
u8 SDReadMultiSector(u32 addr, u8 sector_num, u8 *buffer)
{u16 i, time = 0;u8 r1;//set CS lowCS_Enable();//send CMD18 for multiple blocks readr1 = SDSendCmd(CMD18, addr, 0xff);//if CMD18 fail,returnif (r1 != 0x00){//set CS high and send 8 clocksCS_Disable();return r1;}//read sector_num sectordo{//continually read till get start bytedo{r1 = SDReadByte();time++;//if time out,set CS high and return r1if (time > 30000 || ((r1 & 0xf0) == 0x00 && (r1 & 0x0f))){//set CS high and send 8 clocksCS_Disable();return r1;}} while (r1 != 0xfe);time = 0;//read 512 Bits of datafor (i = 0; i < 512; i++){*buffer++ = SDReadByte();}//read two bits of CRCSDReadByte();SDReadByte();} while (--sector_num);time = 0;//stop multiple readingr1 = SDSendCmd(CMD12, 0, 0xff);//set CS high and send 8 clocksCS_Disable();return 0;
}//write a single sector
u8 SDWriteSector(u32 addr, u8 *buffer)
{u16 i, time = 0;u8 r1;//set CS lowCS_Enable();do{do{//send CMD24 for single block writer1 = SDSendCmd(CMD24, addr, 0xff);time++;//if time out,set CS high and return r1if (time > 60000){//set CS high and send 8 clocksCS_Disable();return r1;}} while (r1 != 0x00);time = 0;//send some dummy clocksfor (i = 0; i < 5; i++){SDWriteByte(0xff);}//write start byteSDWriteByte(0xfe);//write 512 bytes of datafor (i = 0; i < 512; i++){SDWriteByte(buffer[i]);}//write 2 bytes of CRCSDWriteByte(0xff);SDWriteByte(0xff);//read responser1 = SDReadByte();time++;//if time out,set CS high and return r1if (time > 60000){//set CS high and send 8 clocksCS_Disable();return r1;}} while ((r1 & 0x1f) != 0x05);time = 0;//check busydo{r1 = SDReadByte();time++;//if time out,set CS high and return r1if (time > 60000){//set CS high and send 8 clocksCS_Disable();return r1;}} while (r1 != 0xff);//set CS high and send 8 clocksCS_Disable();return 0;
}//write several blocks
u8 SDWriteMultiSector(u32 addr, u8 sector_num, u8 *buffer)
{u16 i, time = 0;u8 r1;//set CS lowCS_Enable();//send CMD25 for multiple block readr1 = SDSendCmd(CMD25, addr, 0xff);//if CMD25 fail,returnif (r1 != 0x00){//set CS high and send 8 clocksCS_Disable();return r1;}do{do{//send several dummy clocksfor (i = 0; i < 5; i++){SDWriteByte(0xff);}//write start byteSDWriteByte(0xfc);//write 512 byte of datafor (i = 0; i < 512; i++){SDWriteByte(*buffer++);}//write 2 byte of CRCSDWriteByte(0xff);SDWriteByte(0xff);//read responser1 = SDReadByte();time++;//if time out,set CS high and return r1if (time > 254){//set CS high and send 8 clocksCS_Disable();return r1;}} while ((r1 & 0x1f) != 0x05);time = 0;//check busydo{r1 = SDReadByte();printf("n%d", r1);time++;//if time out,set CS high and return r1if (time > 30000){//set CS high and send 8 clocksCS_Disable();return r1;}} while (r1 != 0xff);time = 0;} while (--sector_num);//send stop byteSDWriteByte(0xfd);//check busydo{r1 = SDReadByte();time++;//if time out,set CS high and return r1if (time > 30000){//set CS high and send 8 clocksCS_Disable();return r1;}} while (r1 != 0xff);//set CS high and send 8 clocksCS_Disable();return 0;
}//get CID or CSD
u8 SDGetCIDCSD(u8 cid_csd, u8 *buffer)
{u8 r1;u16 i, time = 0;//set CS lowCS_Enable();//send CMD10 for CID read or CMD9 for CSDdo{if (cid_csd == CID)r1 = SDSendCmd(CMD10, 0, 0xff);elser1 = SDSendCmd(CMD9, 0, 0xff);time++;//if time out,set CS high and return r1if (time > 254){//set CS high and send 8 clocksCS_Disable();return r1;}} while (r1 != 0x00);time = 0;//continually read till get 0xfedo{r1 = SDReadByte();time++;//if time out,set CS high and return r1if (time > 30000){//set CS high and send 8 clocksCS_Disable();return r1;}} while (r1 != 0xfe);//read 512 Bits of datafor (i = 0; i < 16; i++){*buffer++ = SDReadByte();}//read two bits of CRCSDReadByte();SDReadByte();//set CS high and send 8 clocksCS_Disable();return 0;
}

使用的时候需要修改的是void SDIOinit(void);进行相应端口的初始化,头文件上方宏定义中关于IO口电平的操作也需要做自己的修改。
这里附获取SD卡容量的程序供调试用:

         u8 csddata[16] = {0};SDGetCIDCSD(CSD, csddata);u32 csize = csddata[9] + ((uint32_t)csddata[8] << 8) + ((uint32_t)(csddata[7] & 0x3f) << 16) + 1;u32 Capacity = csize << 9;

Capacity单位是Kbyte。

2 SD卡扇区简介

知道一些扇区的知识,有利于程序调试。下载一个DiskGenius,SD卡插在电脑上,就可以看到SD卡的磁盘信息。

RD1

这里要特别特别注意的是,每一步操作都确认一下是SD卡还是你自己的电脑硬盘,不要重演我的悲剧。

2.1 格式化

此格式化非彼格式化,这里有更多的选项,顺便熟悉一些概念。
选中SD卡(点上方的蓝条),然后点工具栏里的delete,删除当前分区(有几个删除几个)。

delete

然后点工具栏里最左边的Save All,再点击New Partition,会弹出来一个框,点下面的Advanced>>,展开高级页面:

New Partition

按图中圈的部分改好,第二个圈让它对其8个扇区,因为Fatfs可以设置读取块的大小,最大是4096,我估计这个选项会有影响,没有具体测试。
比较重要的是三号圈,这里是begining sector,就是FAT32的起始扇区,就是这里存储着SD卡相关的配置信息。
改好之后点OK,然后再点Save All。

2.2 FAT32信息

sector

点dector edit,再点offset,出现下面这个框:

offset

按图上选,点OK就会跳转到2048页扇区了。

0

扇区以0xEB开头,表示活跃,Fatfs会检查这一位,以0x55,0xAA结尾,软件中两个分割线之间就是一个扇区,512个字节。在中间可以占到FAT32字样,FATFS就是通过这个来初步判断是什么文件系统类型的。
最新版本的FATFS可以自己来找这一页,所以不用太担心这个offset,R0.1版本之前的好像只会在0页查找。写在其他页会找不到文件系统。

3 Fatfs移植

3.1 获取源码

在FatFs官网下载源码。

源码

移植要用到的是source里面的文件,把它们添加到工程里。

3.2 移植

这里只写简单移植使用的部分,更多的功能可以结合文档去探索。

3.2.1 头文件修改

include 包含的头文件,有一些是windows的,酌情清除掉,添加自己芯片的头文件,一般不用添加。

  • integer.h
    这里定义的是数据类型,我直接把多余的宏定义去掉了,然后添加自己芯片定义的数据类型,如包含u8 u32等定义所在的头文件。
#ifndef FF_INTEGER
#define FF_INTEGER
#include "mico.h"/* Embedded platform *//* These types MUST be 16-bit or 32-bit */
typedef int             INT;
typedef unsigned int    UINT;/* This type MUST be 8-bit */
typedef unsigned char   BYTE;/* These types MUST be 16-bit */
typedef short           SHORT;
typedef unsigned short  WORD;
typedef unsigned short  WCHAR;/* These types MUST be 32-bit */
typedef long            LONG;
typedef unsigned long   DWORD;/* This type MUST be 64-bit (Remove this for ANSI C (C89) compatibility) */
typedef unsigned long long QWORD;#endif
  • ffconf.h
    这个是配置文件,主要是需要修改FF_VOLUMESFF_VOLUME_STRS,修改成
    #define FF_VOLUMES 2#define FF_VOLUME_STRS "RAM","SD","SD2","USB","USB2","USB3"。之前SD卡死可以挂载0磁盘的,现在不行了,我看了底层,挂载到0的时候会进入其他判断的分支。所以这里做这样的修改,后面会把SD卡挂载到磁盘1。

3.2.2 实现diskio.c

这个文件就是主要的功能上实现了,在这里对接上SPI操作和FatFs,主要是实现以下函数。

  • DSTATUS disk_status
    直接返回RES_OK就可以了。
DSTATUS disk_status(BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{DSTATUS stat;stat = RES_OK;return stat;
}
  • DSTATUS disk_initialize
    初始化函数,可以把初始化IO口,初始化SD卡的函数放到这里,也可以放在自己喜欢的地方。无论如何,在使用文件系统前把SD卡初始化做好,这里返回RES_OK就可以了。
DSTATUS disk_initialize(BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{DSTATUS stat;int result;result = 0;//SDInit();if (result == 0){stat = RES_OK;}else{stat = RES_ERROR;}return stat;
}

参数BYTE pdrv是设备号,如果只有一个设备就不用管它,如果挂载了其他设备,就自己做一下判断。

  • DRESULT disk_read
    读函数。跟SPI那部分程序就是在这里对接的。
DRESULT disk_read(BYTE pdrv,  /* Physical drive nmuber to identify the drive */BYTE *buff,   /* Data buffer to store read data */DWORD sector, /* Start sector in LBA */UINT count  /* Number of sectors to read */
)
{DRESULT stat;int result;//BYTE buffd[512]={0};if(count==1){result=SDReadSector(sector,buff);}else{result = SDReadMultiSector(sector, count, buff);}if (result == 0){stat = RES_OK;}else{stat = RES_ERROR;}
}
  • DRESULT disk_write
    写函数。
DRESULT disk_write(BYTE pdrv,        /* Physical drive nmuber to identify the drive */const BYTE *buff, /* Data to be written */DWORD sector,    /* Start sector in LBA */UINT count        /* Number of sectors to write */
)
{DRESULT stat;int result;if(count==1){result=SDWriteSector(sector,(uint8_t *)buff);}else{result = SDWriteMultiSector(sector, count, (uint8_t *)buff);}if (result == 0){stat = RES_OK;}else{stat = RES_ERROR;}
}
  • DRESULT disk_ioctl
    信息获取的函数,这个函数在格式化的时候有用,这里可以先不管它,直接返回RES_OK。
    我进行了一些指令的实现:
DRESULT disk_ioctl(BYTE pdrv, /* Physical drive nmuber (0..) */BYTE cmd,  /* Control code */void *buff /* Buffer to send/receive control data */
)
{DRESULT stat;switch (cmd){case CTRL_SYNC:stat = RES_OK;break;case GET_SECTOR_COUNT:u8 csddata[16] = {0};SDGetCIDCSD(CSD, csddata);u32 csize = csddata[9] + ((uint32_t)csddata[8] << 8) + ((uint32_t)(csddata[7] & 0x3f) << 16) + 1;u32 Capacity = csize << 9;*((DWORD *)buff) = Capacity;stat = RES_OK;break;case GET_SECTOR_SIZE:*(WORD *)buff = 512; //spi flash的扇区大小是 512 Bytesreturn RES_OK;case GET_BLOCK_SIZE:*((DWORD *)buff) = 4096;stat = RES_OK;break;default:stat = RES_PARERR;break;}return stat;
}
  • DWORD get_fattime
    最后还要添加一个函数,这个函数就是给文件添加时间戳的,也可以不用,直接返回0就可以了。
DWORD get_fattime(void)
{return 0;
}

4 简单实使用

经过上面的修改就差不多可以用了。不能用就找其他移植笔记看看缺了什么没有,主要是要注意现在SD卡不能挂载在0上面了。

4.1 挂载

 FATFS ffs;   /* Work area (filesystem object) for logical drive */FRESULT fr;fr=f_mount(&ffs, "1:/", 1);

f_mount的最后一个参数是1,就会立即挂载,是0就等需要的时候才挂载。

4.2 读写文件

    FATFS ffs;   /* Work area (filesystem object) for logical drive */FRESULT fr;fr=f_mount(&ffs, "1:/", 1);//准备要写入的数据u8 buf[512]={0};for(int j=0;j<512;j++){buf[j]='a';}//接收数据的数组u8 bufb[512]={0};fr = f_open( &fil, "1:/test2.txt",  FA_OPEN_APPEND|FA_WRITE);// int fsizei=0;//fsizei=f_size(&fil);//读取文件大小// fr = f_lseek(&fil, f_size(&fil));//移动光标fr = f_write(&fil, buf, 5, &bw);//写入5个字符f_close(&fil);fr=f_open(&fil, "1:/test2.txt", FA_OPEN_EXISTING | FA_READ);fr=f_read(&fil, bufb, 5, &br);f_close(&fil);

FA_OPEN_APPEND:文件存在就打开,并将光标移动到文件末尾,便于添加新内容。文件不存在就新建文件。
关于后面的参数选项官网有详细的解释:

open

这里与时俱进,不需要自己去移动光标了,可以直接通过参数打开文件并追加内容。

5 问题

因为我这是事后做的记录,没有重新去再移植一遍,所以记录上可能有疏忽。如果遇到什么问题可以给我反馈一下。

作者:Savior2016
链接:https://www.jianshu.com/p/48bf324a17d3
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

https://www.jianshu.com/p/48bf324a17d3

模拟SPI进行TF卡操作+Fatfs文件系统移植相关推荐

  1. STM32+雷龙SD NAND(贴片SD卡)完成FATFS文件系统移植与测试

    一.前言 在STM32项目开发中,经常会用到存储芯片存储数据. 比如:关机时保存机器运行过程中的状态数据,上电再从存储芯片里读取数据恢复:在存储芯片里也会存放很多资源文件.比如,开机音乐,界面上的菜单 ...

  2. 基于STM32采用CS创世 SD NAND(贴片SD卡)完成FATFS文件系统移植与测试

    一.前言 在STM32项目开发中,经常会用到存储芯片存储数据. 比如:关机时保存机器运行过程中的状态数据,上电再从存储芯片里读取数据恢复:在存储芯片里也会存放很多资源文件.比如,开机音乐,界面上的菜单 ...

  3. 基于STM32采用CS创世 SD NAND(贴片SD卡)完成FATFS文件系统移植与测试(中篇)

    3.2 SPI硬件时序方式 上面的3.1小节是采用SPI模拟时序驱动SD NAND,STM32本身集成有SPI硬件模块,可以直接利用STM32硬件SPI接口读写. 下面贴出底层的适配代码. 上面贴出的 ...

  4. STM32CubeMX配置SD卡+DMA+Fatfs文件系统

    STM32CubeMX配置SD卡+DMA+Fatfs文件系统 一.设备及软件 1.keil 2.STM32CubeMX 3.正点原子STM32F407探索者开发板 二.配置步骤 1.配置RCC外部晶振 ...

  5. STM32挂载SD卡基于Fatfs文件系统读取文件信息

    STM32挂载SD卡基于Fatfs文件系统读取文件信息

  6. 2021-08-11 TM32F103 Buffer FatFs 文件系统移植

    FatFs 本文展示了STM32 FatFs文件系统移植 内容涉及 : FatFs 文件系统移植 SPI函数移植过程 SPI字节数据模拟输出独写 缓存读写 USART串口的识别 IO口输入输出 按键的 ...

  7. elm FatFs文件系统移植总结

    1 前言 本文将根据我的一些理解,针对elm FatFs文件系统做一个初步总结. 2 elm FatFs文件系统介绍 顾名思义FatFs文件系统就是针对FAT文件系统来的,主要是应用于MCU中,STM ...

  8. 基于SD卡的FatFs文件系统(FatFs移植到STM32)

    平台:STM32ZET6(核心板)+ST-LINK/V2+SD卡+USB串口线 工程介绍:主要文件在USER组中,bsp_sdio_sdcard.c,bsp_sdio_sdcard.h和main.c, ...

  9. 【中移芯昇】5. spi接口测试tf卡

    文章目录 1 前言 2 源码 3 添加头文件 4 硬件 5 代码 6 结果 1 前言 本章测试spi tf卡示例,实现txt文件的读写. 2 源码 源码路径: https://gitee.com/CM ...

最新文章

  1. 发送意图到浏览器以打开特定的URL [重复]
  2. SAP ABAP bcset激活时,关联的数据库表条目是如何插入的
  3. oracle rac alter日志,ORACLE 11G RAC 增加日志组及增大日志文件
  4. OpenCV可移植图形工具HighGUI实现图像和视频操作
  5. Web Hacking 101 中文版 二十、漏洞报告
  6. oracle十进制函数语法,Oracle 十进制、二进制互相转换自定义函数
  7. 容器编排技术 -- Kubernetes kubectl label 命令详解
  8. 阿里腾讯百度们已经占据了全球互联网半壁江山!
  9. WPD 从便携设备拷贝文件到PC文件不完整的解决办法
  10. python只保留大写字母_python - 匹配某一行并保留大写字母?
  11. (人脸)目标检测指标-FDDB
  12. java边缘检测_Sobel边缘检测实现
  13. 中华酷联小米:未来谁将干掉三星?
  14. Go 面向接口编程实战
  15. Hbase的数据切分
  16. js禁止鼠标滑轮_js 禁止鼠标滑轮滚动的事件
  17. 基于RBAC 的SAAS系统权限设计
  18. druid.io集群与tranquility对zookeeper的使用(2)
  19. java 等额本息计算方式
  20. Foxmail 去掉签名横线 Foxmail 去掉签名默认横线

热门文章

  1. JavaWeb——数据库(Mysql)
  2. python3 mysql5.7_/如何连接python3.4 和MySQL5.7,请大牛们指教这个python入门新手
  3. 信息量,熵,互信息,信道容量等相关概念
  4. 蜜罐天堂Honeydrive的部署和Dionaea的试运行
  5. jquery显示隐藏元素
  6. UEFI安装Win10/Ubuntu16.04双系统记录
  7. (转)资管的四个未来?——寻找中国非银金融的Alpha
  8. 树形结构的处理——组合模式(四)
  9. 1005.E. Maximize Sum Of Array After K Negations
  10. Linux ps aux什么含义,Linux下psaux解释