1. 程式人生 > >MSP430 SD卡SPI讀寫操作(4) —— FatFs檔案系統實現(以MSP430F5438A為例)

MSP430 SD卡SPI讀寫操作(4) —— FatFs檔案系統實現(以MSP430F5438A為例)

本節介紹MSP430F5438A FatFs檔案系統的移植。

FatFs是一個通用的檔案系統模組,用於在小型嵌入式系統中實現FAT檔案系統。FatFs模組與IO是分開的,因此移植時需要實現下面幾個底層函式:

DSTATUS disk_initialize (BYTE drv); //初始化儲存器
DSTATUS disk_status (BYTE drv); //獲取儲存器狀態
DRESULT disk_read (BYTE drv, BYTE* buff, DWORD sector, UINT count); //讀儲存器
DRESULT disk_write (BYTE drv, const BYTE* buff, DWORD sector, UINT count); //寫儲存器
DRESULT disk_ioctl (BYTE drv, BYTE ctrl, void* buff); //額外功能
DWORD get_fattime (void); //獲取時間(此函式可以沒有,和FatFs模組配置有關)

根據不同的處理器平臺,需要修改 integer.h 相應的資料型別定義。
修改 ffconf.h 可以配置FatFs的功能。

下面是本人實現的MSP430F5438A平臺的移植,使用官方函式庫msp430_driverlib_2_60_00_02,使用IAR for msp430 6.3通過編譯。

本節程式碼對SD卡進行了區分,程式在金士頓 8GB SDHC microSD卡經過驗證可以正常執行。


diskio.h

/*-----------------------------------------------------------------------/
/  Low level disk interface modlue include file   (C)ChaN, 2014          /
/-----------------------------------------------------------------------*/
 
#ifndef _DISKIO_DEFINED
#define _DISKIO_DEFINED
 
#ifdef __cplusplus
extern "C" {
#endif
 
#include "integer.h"
 
 
/* Status of Disk Functions */
typedef BYTE    DSTATUS;
 
/* Results of Disk Functions */
typedef enum {
    RES_OK = 0,     /* 0: Successful */
    RES_ERROR,      /* 1: R/W Error */
    RES_WRPRT,      /* 2: Write Protected */
    RES_NOTRDY,     /* 3: Not Ready */
    RES_PARERR      /* 4: Invalid Parameter */
} DRESULT;
 
#define SD_INIT_CLK 125000
#define SD_HIGH_CLK 3125000
#define SD_CS_PORT GPIO_PORT_P9
#define SD_CS_PIN  GPIO_PIN0
 
/* MMC/SD command (SPI mode) */
#define CMD0 (0)           /* GO_IDLE_STATE */
#define CMD1 (1)           /* SEND_OP_COND */
#define ACMD41 (0x80+41)   /* SEND_OP_COND (SDC) */
#define CMD8 (8)           /* SEND_IF_COND */
#define CMD9 (9)           /* SEND_CSD */
#define CMD10 (10)         /* SEND_CID */
#define CMD12 (12)         /* STOP_TRANSMISSION */
#define CMD13 (13)         /* SEND_STATUS */
#define ACMD13 (0x80+13)   /* SD_STATUS (SDC) */
#define CMD16 (16)         /* SET_BLOCKLEN */
#define CMD17 (17)         /* READ_SINGLE_BLOCK */
#define CMD18 (18)         /* READ_MULTIPLE_BLOCK */
#define CMD23 (23)         /* SET_BLOCK_COUNT */
#define ACMD23 (0x80+23)   /* SET_WR_BLK_ERASE_COUNT (SDC) */
#define CMD24 (24)         /* WRITE_BLOCK */
#define CMD25 (25)         /* WRITE_MULTIPLE_BLOCK */
#define CMD32 (32)         /* ERASE_ER_BLK_START */
#define CMD33 (33)         /* ERASE_ER_BLK_END */
#define CMD38 (38)         /* ERASE */
#define CMD55 (55)         /* APP_CMD */
#define CMD58 (58)         /* READ_OCR */
/*---------------------------------------*/
/* Prototypes for disk control functions */
 
 
DSTATUS disk_initialize (BYTE drv);
DSTATUS disk_status (BYTE drv);
DRESULT disk_read (BYTE drv, BYTE* buff, DWORD sector, UINT count);
DRESULT disk_write (BYTE drv, const BYTE* buff, DWORD sector, UINT count);
DRESULT disk_ioctl (BYTE drv, BYTE ctrl, void* buff);
 
 
/* Disk Status Bits (DSTATUS) */
 
#define STA_NOINIT      0x01    /* Drive not initialized */
#define STA_NODISK      0x02    /* No medium in the drive */
#define STA_PROTECT     0x04    /* Write protected */
 
 
/* Command code for disk_ioctrl fucntion */
 
/* Generic command (Used by FatFs) */
#define CTRL_SYNC           0   /* Complete pending write process (needed at _FS_READONLY == 0) */
#define GET_SECTOR_COUNT    1   /* Get media size (needed at _USE_MKFS == 1) */
#define GET_SECTOR_SIZE     2   /* Get sector size (needed at _MAX_SS != _MIN_SS) */
#define GET_BLOCK_SIZE      3   /* Get erase block size (needed at _USE_MKFS == 1) */
#define CTRL_TRIM           4   /* Inform device that the data on the block of sectors is no longer used (needed at _USE_TRIM == 1) */
 
/* Generic command (Not used by FatFs) */
#define CTRL_POWER          5   /* Get/Set power status */
#define CTRL_LOCK           6   /* Lock/Unlock media removal */
#define CTRL_EJECT          7   /* Eject media */
#define CTRL_FORMAT         8   /* Create physical format on the media */
 
/* MMC/SDC specific ioctl command */
#define MMC_GET_TYPE        10  /* Get card type */
#define MMC_GET_CSD         11  /* Get CSD */
#define MMC_GET_CID         12  /* Get CID */
#define MMC_GET_OCR         13  /* Get OCR */
#define MMC_GET_SDSTAT      14  /* Get SD status */
#define ISDIO_READ          55  /* Read data form SD iSDIO register */
#define ISDIO_WRITE         56  /* Write data to SD iSDIO register */
#define ISDIO_MRITE         57  /* Masked write data to SD iSDIO register */
 
/* ATA/CF specific ioctl command */
#define ATA_GET_REV         20  /* Get F/W revision */
#define ATA_GET_MODEL       21  /* Get model name */
#define ATA_GET_SN          22  /* Get serial number */
 
/* MMC card type flags (MMC_GET_TYPE) */
#define CT_MMC      0x01        /* MMC ver 3 */
#define CT_SD1      0x02        /* SD ver 1 */
#define CT_SD2      0x04        /* SD ver 2 */
#define CT_SDC      (CT_SD1|CT_SD2) /* SD */
#define CT_BLOCK    0x08        /* Block addressing */
 
#ifdef __cplusplus
}
#endif
 
#endif

diskio.c

/*-----------------------------------------------------------------------*/
/* Low level disk I/O module skeleton for FatFs     (C)ChaN, 2016        */
/*-----------------------------------------------------------------------*/
#include "driverlib.h"
#include "../gateway_clk.h"
#include "diskio.h"
 
DSTATUS Stat = STA_NOINIT;
BYTE CardType;
 
static
void SD_csInit(void)
{
  GPIO_setAsOutputPin(SD_CS_PORT,SD_CS_PIN);
}
 
static
uint8_t SD_writeByte(BYTE data)
{
  USCI_B_SPI_transmitData(USCI_B2_BASE,data);
  while(USCI_B_SPI_isBusy(USCI_B2_BASE));
  data = USCI_B_SPI_receiveData(USCI_B2_BASE);
  return data;
}
 
static
BYTE SD_waitReady(void)
{
  WORD tmr;
  for(tmr = 5000; tmr; tmr--)
  {
    if(SD_writeByte(0xFF) == 0xFF) break;
    delay_us(100);
  }
  return tmr ? 1 : 0;
}
 
static
void SD_csDisable(void)
{
  GPIO_setOutputHighOnPin(SD_CS_PORT,SD_CS_PIN);
  SD_writeByte(0xFF);
}
 
static
int SD_csEnable(void)
{
  GPIO_setOutputLowOnPin(SD_CS_PORT,SD_CS_PIN);
  SD_writeByte(0xFF);
  if(SD_waitReady()) return 1;
  SD_csDisable();
  return 0;
}
 
static
void SD_spiInit(void)
{
  GPIO_setAsPeripheralModuleFunctionInputPin(
     GPIO_PORT_P9,
     GPIO_PIN1 + GPIO_PIN2 + GPIO_PIN3
  );
   
  //Initialize Master
  USCI_B_SPI_initMasterParam param = {0};
  param.selectClockSource = USCI_B_SPI_CLOCKSOURCE_SMCLK;
  param.clockSourceFrequency = UCS_getSMCLK();
  param.desiredSpiClock = SD_INIT_CLK;
  param.msbFirst = USCI_B_SPI_MSB_FIRST;
  param.clockPhase = USCI_B_SPI_PHASE_DATA_CHANGED_ONFIRST_CAPTURED_ON_NEXT;
  param.clockPolarity = USCI_B_SPI_CLOCKPOLARITY_INACTIVITY_HIGH;
  USCI_B_SPI_initMaster(USCI_B2_BASE, ¶m);
}
 
static
void SD_spiEnable(void)
{
  USCI_B_SPI_enable(USCI_B2_BASE);
}
 
static
void SD_spiDisable(void)
{
  USCI_B_SPI_disable(USCI_B2_BASE);
}
 
static
void SD_spiSetSpeed(DWORD speed)
{
  USCI_B_SPI_changeMasterClockParam clockparam = {0};
  clockparam.clockSourceFrequency = UCS_getSMCLK();
  clockparam.desiredSpiClock = speed;
  USCI_B_SPI_changeMasterClock(USCI_B2_BASE, &clockparam);
}
 
static
BYTE SD_getResponse(void)
{
  BYTE retrytime = 0;
  BYTE response;
   
  while(retrytime <= 240)
  {
    response = SD_writeByte(0xFF);
    if(response == 0x00) break;
    if(response == 0x01) break;
    if(response == 0xFE) break;
    retrytime++;
  }
  return response;
}
 
static
BYTE SD_sendCmd(BYTE cmd,DWORD arg,const BYTE crc)
{
  BYTE rec;
  if(cmd & 0x80)
  {
    cmd &= 0x7F;
    rec = SD_sendCmd(CMD55,0,0xFF);
    if(rec > 1) return rec;
  }
   
  if(cmd != CMD12)
  {
    SD_csDisable();
    if(!SD_csEnable()) return 0xFF;
  }
   
  SD_writeByte((cmd & 0x3F) | 0x40);
  SD_writeByte(arg >> 24);
  SD_writeByte(arg >> 16);
  SD_writeByte(arg >> 8);
  SD_writeByte(arg);
  SD_writeByte(crc);
  rec = SD_getResponse(); 
  return rec;
}
 
/*-----------------------------------------------------------------------*/
/* Receive a data packet from the card                                   */
/*-----------------------------------------------------------------------*/
static
int SD_readBlock (BYTE *buff, UINT btr)
{
  BYTE d[2];
  UINT tmr;
  for (tmr = 1000; tmr; tmr--) 
  {
    if ((d[0] = SD_writeByte(0xFF)) != 0xFF) break;
    delay_us(100);
  }
  if (d[0] != 0xFE) return 0;
  do
  {
    *buff++ = SD_writeByte(0xFF);
  } while(-- btr);
  SD_writeByte(0xFF);
  SD_writeByte(0xFF);
  return 1;
}
 
/*-----------------------------------------------------------------------*/
/* Send a data packet to the card                                        */
/*-----------------------------------------------------------------------*/
static
int SD_writeBlock (const BYTE *buff, BYTE token)
{
  BYTE d;
  UINT tmr;
  if (!SD_waitReady()) return 0;
  SD_writeByte(token);
  if (token != 0xFD)
  {
    tmr = 512;
    do
    {
      SD_writeByte(*buff ++);
    } while(-- tmr);
    SD_writeByte(0xFF);
    SD_writeByte(0xFF);
    d = SD_writeByte(0xFF);
    if ((d & 0x1F) != 0x05)
    return 0;
  }
  return 1;
}
 
/*-----------------------------------------------------------------------*/
/* Get Drive Status                                                      */
/*-----------------------------------------------------------------------*/
DSTATUS disk_status (BYTE drv)
{
  if(drv) return STA_NOINIT;
  return Stat;
}
 
/*-----------------------------------------------------------------------*/
/* Inidialize a Drive                                                    */
/*-----------------------------------------------------------------------*/
DSTATUS disk_initialize (BYTE drv)
{
  BYTE n,ty,cmd,buf[4];
  UINT tmr;
  DSTATUS s;
  if(drv) return RES_NOTRDY;
   
  SD_spiInit();
  SD_spiEnable();
  SD_csInit();
  SD_csDisable();
   
  for(n = 0;n < 16;n++)
  {
    SD_writeByte(0xFF);
  } //send 128 clocks for normal voltage and sync
   
  ty = 0;
  if(SD_sendCmd(CMD0,0,0x95) == 1) //enter idle state
  {
    if(SD_sendCmd(CMD8,0x1AA,0x87) == 1) //SDV2
    {
      buf[0] = SD_writeByte(0xFF);
      buf[1] = SD_writeByte(0xFF);
      buf[2] = SD_writeByte(0xFF);
      buf[3] = SD_writeByte(0xFF);
      if(buf[2] == 0x01 && buf[3] == 0xAA)
      {
        for(tmr = 1000;tmr;tmr--)
        {
          if(SD_sendCmd(ACMD41,0x40000000,0xFF) == 0) break;
          delay_ms(1);
        }
        if(tmr && SD_sendCmd(CMD58,0,0xFF) == 0)
        {
          buf[0] = SD_writeByte(0xFF);
          buf[1] = SD_writeByte(0xFF);
          buf[2] = SD_writeByte(0xFF);
          buf[3] = SD_writeByte(0xFF);
          ty = (buf[0] & 0x40) ? CT_SD2 | CT_BLOCK : CT_SD2;    /* SDv2 */
    }
      }
    }
    else
    {
      if(SD_sendCmd(ACMD41,0,0xFF) <= 1) //SDV1
      {
        ty = CT_SD1;
        cmd = ACMD41;
      }
      else //MMCv3
      {
        ty = CT_MMC;
        cmd = CMD1;
      }
      for(tmr = 1000; tmr; tmr--)
      {
        if(SD_sendCmd(cmd,0,0xFF) == 0) break;
    delay_ms(1);
      }
      if(!tmr || SD_sendCmd(CMD16,512,0xFF) != 0)
        ty = 0;
    }
  }
   
  CardType = ty;
  s = ty ? 0 : STA_NOINIT;
  Stat = s;
   
  SD_csDisable();
  //SPI HIGH SPEED
  SD_spiSetSpeed(SD_HIGH_CLK);
  return s;
}
 
/*-----------------------------------------------------------------------*/
/* Read Sector(s)                                                        */
/*-----------------------------------------------------------------------*/
DRESULT disk_read (BYTE drv,BYTE *buff,DWORD sector,UINT count)
{
  BYTE cmd;
  if (disk_status(drv) & STA_NOINIT) return RES_NOTRDY;
  if (!(CardType & CT_BLOCK)) sector *= 512;
  cmd = count > 1 ? CMD18 : CMD17;
  if (SD_sendCmd(cmd, sector, 0xFF) == 0)
  {
    do
    {
      if (!SD_readBlock(buff, 512)) break;
      buff += 512;
    } while (--count);
    if (cmd == CMD18) SD_sendCmd(CMD12, 0, 0xFF);
  }
  SD_csDisable();
  return count ? RES_ERROR : RES_OK;
}
 
/*-----------------------------------------------------------------------*/
/* Write Sector(s)                                                       */
/*-----------------------------------------------------------------------*/
DRESULT disk_write (BYTE drv,const BYTE *buff,DWORD sector,UINT count)
{
  if (disk_status(drv) & STA_NOINIT) return RES_NOTRDY;
  if (!(CardType & CT_BLOCK)) sector *= 512;
  if (count == 1) 
  {
    if ((SD_sendCmd(CMD24, sector, 0xFF) == 0) && SD_writeBlock(buff, 0xFE))
    count = 0;
  }
  else
  {
    if (CardType & CT_SDC) SD_sendCmd(ACMD23, count, 0xFF);
    if (SD_sendCmd(CMD25, sector, 0xFF) == 0) 
    {
      do
      {
        if (!SD_writeBlock(buff, 0xFC)) break;
        buff += 512;
      } while (--count);
      if (!SD_writeBlock(0, 0xFD))
      count = 1;
    }
  }
  SD_csDisable();
  return count ? RES_ERROR : RES_OK;
}
 
/*-----------------------------------------------------------------------*/
/* Miscellaneous Functions                                               */
/*-----------------------------------------------------------------------*/
DRESULT disk_ioctl (BYTE drv,BYTE ctrl,void *buff)
{
  DRESULT res;
  BYTE n, csd[16];
  DWORD cs;
   
  if (disk_status(drv) & STA_NOINIT) return RES_NOTRDY;
   
  res = RES_ERROR;
  switch (ctrl)
  {
    case CTRL_SYNC :
      if (SD_csEnable()) res = RES_OK;
      break;
    case GET_SECTOR_COUNT :
      if ((SD_sendCmd(CMD9, 0, 0xFF) == 0) && SD_readBlock(csd, 16))
      {
        if ((csd[0] >> 6) == 1) //SDV2
        {
          cs = csd[9] + ((WORD)csd[8] << 8) + ((DWORD)(csd[7] & 63) << 16) + 1;
          *(DWORD*)buff = cs << 10;
        }
        else //SDV1 or MMC
        {
          n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;
          cs = (csd[8] >> 6) + ((WORD)csd[7] << 2) + ((WORD)(csd[6] & 3) << 10) + 1;
          *(DWORD*)buff = cs << (n - 9);
        }
        res = RES_OK;
      }
      break;
    case GET_BLOCK_SIZE :
      *(DWORD*)buff = 128;
      res = RES_OK;
      break;
    default:
      res = RES_PARERR;
      break;
  }
  SD_csDisable();
  return res;
}