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 low
SPI_CS_L;
}
//set CS high and send 8 clocks
void CS_Disable(void)
{
//set CS high
SPI_CS_H;
//send 8 clocks
SDWriteByte(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;
else
SPI_MO_L;
data <<= 1;
SPI_CLK_H;
usleep(spi_speed);
}
//when DI is free,it should be set high
SPI_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;//OK
t++;
}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 high
CS_Disable();
//send 128 clocks
for (i = 0; i < 80; i++)
{
SDWriteByte(0xff);
}
//set CS low
CS_Enable();
//send CMD0 till the response is 0x01
do
{
r1 = SDSendCmd(CMD0, 0, 0x95);
time++;
//if time out,set CS high and return r1
if (time > 254)
{
//set CS high and send 8 clocks
CS_Disable();
return r1;
}
} while (r1 != 0x01);
//set CS high and send 8 clocks
CS_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 9
GPIO_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); //初始化
//片选 PF10
GPIO_Initure.Pin=GPIO_PIN_10; //片选 PF10
GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP; //开输出
GPIO_Initure.Pull=GPIO_PULLUP; //上拉
GPIO_Initure.Speed=GPIO_SPEED_FAST; //快速
// GPIO_Initure.Alternate=GPIO_AF5_SPI5; //复用为SPI5S
HAL_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 resp
if(buf[2]==0X01&&buf[3]==0XAA)//卡是否支持2.7~3.6V
{
retry=0XFFFE;
do
{
SDSendCmd(CMD55,0,0X01); //发送CMD55
r1=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; //检查CCS
else SD_Type=SD_TYPE_V2;
}
}else//SD V1.x/ MMC V3
{
SDSendCmd(CMD55,0,0X01); //发送CMD55
r1=SDSendCmd(CMD41,0,0X01); //发送CMD41
if(r1<=1)
{
SD_Type=SD_TYPE_V1;
retry=0XFFFE;
do //等待退出IDLE模式
{
SDSendCmd(CMD55,0,0X01); //发送CMD55
r1=SDSendCmd(CMD41,0,0X01);//发送CMD41
}while(r1&&retry--);
}else//MMC卡不支持CMD55+CMD41识别
{
SD_Type=SD_TYPE_MMC;//MMC V3
retry=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 clocks
CS_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 low
CS_Enable();
//send CMD17 for single block read
// addr=addr*512;
r1 = SDSendCmd(CMD17, addr, 0x55);
//if CMD17 fail,return
if (r1 != 0x00)
{
//set CS high and send 8 clocks
CS_Disable();
return r1;
}
//continually read till get the start byte 0xfe
do
{
r1 = SDReadByte();
time++;
//if time out,set CS high and return r1
if (time > 30000)
{
//set CS high and send 8 clocks
CS_Disable();
return r1;
}
} while (r1 != 0xfe);
//read 512 Bits of data
for (i = 0; i < 512; i++)
{
buffer[i] = SDReadByte();
}
//read two bits of CRC
SDReadByte();
SDReadByte();
//set CS high and send 8 clocks
CS_Disable();
return 0;
}
//read multiple sectors
u8 SDReadMultiSector(u32 addr, u8 sector_num, u8 *buffer)
{
u16 i, time = 0;
u8 r1;
//set CS low
CS_Enable();
//send CMD18 for multiple blocks read
r1 = SDSendCmd(CMD18, addr, 0xff);
//if CMD18 fail,return
if (r1 != 0x00)
{
//set CS high and send 8 clocks
CS_Disable();
return r1;
}
//read sector_num sector
do
{
//continually read till get start byte
do
{
r1 = SDReadByte();
time++;
//if time out,set CS high and return r1
if (time > 30000 || ((r1 & 0xf0) == 0x00 && (r1 & 0x0f)))
{
//set CS high and send 8 clocks
CS_Disable();
return r1;
}
} while (r1 != 0xfe);
time = 0;
//read 512 Bits of data
for (i = 0; i < 512; i++)
{
*buffer++ = SDReadByte();
}
//read two bits of CRC
SDReadByte();
SDReadByte();
} while (--sector_num);
time = 0;
//stop multiple reading
r1 = SDSendCmd(CMD12, 0, 0xff);
//set CS high and send 8 clocks
CS_Disable();
return 0;
}
//write a single sector
u8 SDWriteSector(u32 addr, u8 *buffer)
{
u16 i, time = 0;
u8 r1;
//set CS low
CS_Enable();
do
{
do
{
//send CMD24 for single block write
r1 = SDSendCmd(CMD24, addr, 0xff);
time++;
//if time out,set CS high and return r1
if (time > 60000)
{
//set CS high and send 8 clocks
CS_Disable();
return r1;
}
} while (r1 != 0x00);
time = 0;
//send some dummy clocks
for (i = 0; i < 5; i++)
{
SDWriteByte(0xff);
}
//write start byte
SDWriteByte(0xfe);
//write 512 bytes of data
for (i = 0; i < 512; i++)
{
SDWriteByte(buffer[i]);
}
//write 2 bytes of CRC
SDWriteByte(0xff);
SDWriteByte(0xff);
//read response
r1 = SDReadByte();
time++;
//if time out,set CS high and return r1
if (time > 60000)
{
//set CS high and send 8 clocks
CS_Disable();
return r1;
}
} while ((r1 & 0x1f) != 0x05);
time = 0;
//check busy
do
{
r1 = SDReadByte();
time++;
//if time out,set CS high and return r1
if (time > 60000)
{
//set CS high and send 8 clocks
CS_Disable();
return r1;
}
} while (r1 != 0xff);
//set CS high and send 8 clocks
CS_Disable();
return 0;
}
//write several blocks
u8 SDWriteMultiSector(u32 addr, u8 sector_num, u8 *buffer)
{
u16 i, time = 0;
u8 r1;
//set CS low
CS_Enable();
//send CMD25 for multiple block read
r1 = SDSendCmd(CMD25, addr, 0xff);
//if CMD25 fail,return
if (r1 != 0x00)
{
//set CS high and send 8 clocks
CS_Disable();
return r1;
}
do
{
do
{
//send several dummy clocks
for (i = 0; i < 5; i++)
{
SDWriteByte(0xff);
}
//write start byte
SDWriteByte(0xfc);
//write 512 byte of data
for (i = 0; i < 512; i++)
{
SDWriteByte(*buffer++);
}
//write 2 byte of CRC
SDWriteByte(0xff);
SDWriteByte(0xff);
//read response
r1 = SDReadByte();
time++;
//if time out,set CS high and return r1
if (time > 254)
{
//set CS high and send 8 clocks
CS_Disable();
return r1;
}
} while ((r1 & 0x1f) != 0x05);
time = 0;
//check busy
do
{
r1 = SDReadByte();
printf("n%d", r1);
time++;
//if time out,set CS high and return r1
if (time > 30000)
{
//set CS high and send 8 clocks
CS_Disable();
return r1;
}
} while (r1 != 0xff);
time = 0;
} while (--sector_num);
//send stop byte
SDWriteByte(0xfd);
//check busy
do
{
r1 = SDReadByte();
time++;
//if time out,set CS high and return r1
if (time > 30000)
{
//set CS high and send 8 clocks
CS_Disable();
return r1;
}
} while (r1 != 0xff);
//set CS high and send 8 clocks
CS_Disable();
return 0;
}
//get CID or CSD
u8 SDGetCIDCSD(u8 cid_csd, u8 *buffer)
{
u8 r1;
u16 i, time = 0;
//set CS low
CS_Enable();
//send CMD10 for CID read or CMD9 for CSD
do
{
if (cid_csd == CID)
r1 = SDSendCmd(CMD10, 0, 0xff);
else
r1 = SDSendCmd(CMD9, 0, 0xff);
time++;
//if time out,set CS high and return r1
if (time > 254)
{
//set CS high and send 8 clocks
CS_Disable();
return r1;
}
} while (r1 != 0x00);
time = 0;
//continually read till get 0xfe
do
{
r1 = SDReadByte();
time++;
//if time out,set CS high and return r1
if (time > 30000)
{
//set CS high and send 8 clocks
CS_Disable();
return r1;
}
} while (r1 != 0xfe);
//read 512 Bits of data
for (i = 0; i < 16; i++)
{
*buffer++ = SDReadByte();
}
//read two bits of CRC
SDReadByte();
SDReadByte();
//set CS high and send 8 clocks
CS_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卡的磁盘信息。
这里要特别特别注意的是,每一步操作都确认一下是SD卡还是你自己的电脑硬盘,不要重演我的悲剧。
2.1 格式化
此格式化非彼格式化,这里有更多的选项,顺便熟悉一些概念。
选中SD卡(点上方的蓝条),然后点工具栏里的delete,删除当前分区(有几个删除几个)。
然后点工具栏里最左边的Save All,再点击New Partition,会弹出来一个框,点下面的Advanced>>,展开高级页面:
按图中圈的部分改好,第二个圈让它对其8个扇区,因为Fatfs可以设置读取块的大小,最大是4096,我估计这个选项会有影响,没有具体测试。
比较重要的是三号圈,这里是begining sector,就是FAT32的起始扇区,就是这里存储着SD卡相关的配置信息。
改好之后点OK,然后再点Save All。
2.2 FAT32信息
点dector edit,再点offset,出现下面这个框:
按图上选,点OK就会跳转到2048页扇区了。
扇区以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_VOLUMES
和FF_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 Bytes
return 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:文件存在就打开,并将光标移动到文件末尾,便于添加新内容。文件不存在就新建文件。
关于后面的参数选项官网有详细的解释:
这里与时俱进,不需要自己去移动光标了,可以直接通过参数打开文件并追加内容。
5 问题
因为我这是事后做的记录,没有重新去再移植一遍,所以记录上可能有疏忽。如果遇到什么问题可以给我反馈一下。