7 模拟SPI进行TF卡操作+Fatfs文件系统移植

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卡的磁盘信息。


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


open

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

5 问题

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容