原创 一个比较高效的SPI方式SD卡的驱动

2009-3-7 19:23 6587 6 6 分类: 通信
  流传的用SPI驱动SD卡的程序基本都是针对没有FIFO的SPI接口的,这样做程序比较通用;但是如果处理器的SPI接口有FIFO的话,那么FIFO的性能就浪费了。其实把程序稍加改动,就可以大幅度提高SD卡的读取速度,具体程序如下:

 


/******************************************************/
/*          SD Card driver using SSP for LPC2148      */
/*                   by 314forever                    */
/*                 spi_mmc.c  2009.1                  */
/******************************************************/



#ifndef __SPI_MMC_H__
#define __SPI_MMC_H__



#define SPI_SEL        16


#define SET_MMC_CS FIO0SET = (1 << SPI_SEL);
#define CLR_MMC_CS FIO0CLR = (1 << SPI_SEL);



#define MMC_CMD_SIZE   6        // the SPI data is 8 bit long, the MMC use 48 bits, 6 bytes
#define MMC_DATA_SIZE  512      // data of one sector in byte
#define MAX_TIMEOUT    0xFF     // retry times


#define GO_IDLE_STATE           0x00
#define SEND_CSD                0x09
#define SEND_CID                0x0A
#define READ_SINGLE_BLOCK       0x11
#define WRITE_SINGLE_BLOCK      0x18
#define SEND_OP_COND            0x29
#define APP_CMD                 0x37
#define CRC_ON_OFF              0x3B



void spiSend(BYTE *buffer, DWORD length);
void spiReceive(BYTE *buffer, DWORD length);
BYTE spiReceiveByte(void);
BYTE mmcInit(void);
BOOL mmcReadSector(DWORD sector, BYTE *buffer, DWORD *SectorInCache);
BOOL mmcWriteSector(DWORD sector, BYTE *buffer);


#endif


 


/******************************************************/
/*          SD Card driver using SSP for LPC2148      */
/*                   by 314forever                    */
/*                 spi_mmc.c  2009.1                  */
/******************************************************/



#include <LPC214X.H>
#include "..\type.h"
#include "spi_mmc.h"



#define sspLowSpeed()   SSPCPSR = 0x80;
#define sspHighSpeed()  SSPCPSR = 0x08;



void sspSend(BYTE *buffer, DWORD length){
 BYTE dummy;
 
 while(SSPSR & 0x04)dummy = SSPDR;    // clean FIFO
 SSPDR = *buffer;                     // first byte to send
 buffer ++;
 length --;
 
 while(length){
  SSPDR = *buffer;                 // keep FIFO not empty nor not full
  buffer ++;
  length --; 
  while(!(SSPSR & 0x04));
  dummy = SSPDR;
 }
 while(!(SSPSR & 0x04));
 dummy = SSPDR;
}



void sspReceive(BYTE *buffer, DWORD length){
 BYTE dummy;
  
 while(SSPSR & 0x04)dummy = SSPDR;    // clean FIFO
 SSPDR = 0xFF;
 length --;
 
 while(length){
  SSPDR = 0xFF;                    // keep FIFO not empty nor not full
  while(!(SSPSR & 0x04));
  *buffer = SSPDR;
  buffer ++;
  length --;
 }
 while(!(SSPSR & 0x04));              // last byte to receive
 *buffer = SSPDR;
}



BYTE sspReceiveByte(void){
 SSPDR = 0xFF;
 while(SSPSR & 0x10);
 return SSPDR;
}



void sspSendByte(BYTE data){
 BYTE dummy;
 
 SSPDR = data;
 while(SSPSR & 0x10);
 dummy = SSPDR;
}



BYTE mmcSendCommand(BYTE command, DWORD argument){
 BYTE R1;
 BYTE retry ;
 BYTE MMCCmd[MMC_CMD_SIZE + 1];
 
 MMCCmd[0]= command | 0x40;
 MMCCmd[1]= argument >> 24;
 MMCCmd[2]= argument >> 16;
 MMCCmd[3]= argument >> 8;
 MMCCmd[4]= argument;
 if(!command)MMCCmd[5]= 0x95;
 else MMCCmd[5]= 0xFF;
 MMCCmd[6]= 0xFF;
 sspSend(MMCCmd, MMC_CMD_SIZE + 1);
 
 retry = MAX_TIMEOUT;
 do{
  R1 = sspReceiveByte();
  if(!(retry --))break;
 }while(R1 == 0xFF);


 sspReceiveByte();


 return R1;
}



BYTE mmcInit(void){
 BYTE i;
 BYTE R1;
 BYTE retry;
 WORD dummy;


 PINSEL1 |= 0x000000A8;      // enable SCK(P0.17), MISO(P0.18), MOSI(P0.19)
 FIO0DIR |= 1 << SPI_SEL;    // use P0.16 as SSEL of SSP port
 FIO0SET = 1 << SPI_SEL;
 
 SSPCR0 = 0x0007;            // 8bit, SPI format, CPOL = 0, CPHA = 0, and SCR is 0
 sspLowSpeed();
 SSPCR1 = 0x02;              // master mode, SSP enabled, normal operational mode


 while(SSPSR & 0x04)dummy = SSPDR; // clear the RxFIFO


 retry = MAX_TIMEOUT;
 do{
  SET_MMC_CS;
  for(i = 0; i < 10; i ++)sspReceiveByte();
  CLR_MMC_CS;
  R1 = mmcSendCommand(GO_IDLE_STATE, 0);  // CMD0
  if(!(retry --)){
   SET_MMC_CS;
   sspReceiveByte();
   return 1;
  }
 }while(R1 != 0x01);
 
 retry = MAX_TIMEOUT;
 do{
  if(!(retry --)){
   SET_MMC_CS;
   sspReceiveByte();
   return 1;
  }
  R1 = mmcSendCommand(APP_CMD, 0);  // CMD55
  if(R1 != 0x01)continue; 
  R1 = mmcSendCommand(SEND_OP_COND, 0);  // CMD41
 }while(R1 != 0x00);


 retry = MAX_TIMEOUT;
 do{
  R1 = mmcSendCommand(1, 0);  // send active command
  if(!(retry --)){
   SET_MMC_CS;
   sspReceiveByte();
   return 1;
  }
 }while(R1);
 
 sspHighSpeed(); 
 mmcSendCommand(CRC_ON_OFF, 0);  // disable CRC
 mmcSendCommand(16, 512);  // set sector size to 512
 
 SET_MMC_CS;
 return 0; 
}



BOOL mmcReadSector(DWORD sector, BYTE *buffer, DWORD *SectorInCache){
 BYTE R1;
 BYTE retry = MAX_TIMEOUT;
 
 if(*SectorInCache == sector)return 0;
 else *SectorInCache = sector;
 
 CLR_MMC_CS;


 R1 = mmcSendCommand(READ_SINGLE_BLOCK, sector << 9);  // read command
 if(R1 != 0x00){
  SET_MMC_CS;
  sspReceiveByte();
  return 1;
 }


 while(sspReceiveByte() != 0xFE){  // wait to start recieve data
  if(!(retry --)){
   SET_MMC_CS;
   sspReceiveByte();
   return 1;
  }
 }


 sspReceive(buffer, 512);
 sspReceiveByte();  // dummy CRC
 sspReceiveByte();
 
 SET_MMC_CS;
 sspReceiveByte();  // extra 8 CLK


 return 0;
}



BOOL mmcWriteSector(DWORD sector, BYTE *buffer){
 BYTE R1;
 WORD retry = 0xFFFF;
 
 CLR_MMC_CS;
  
 R1 = mmcSendCommand(WRITE_SINGLE_BLOCK, sector << 9);  // write command
 if(R1 != 0x00){
  SET_MMC_CS;
  sspReceiveByte();
  return 1;
 }
 
 sspReceiveByte();
 sspReceiveByte();
 sspSendByte(0xFE);               // start transmit
 sspSend(buffer, MMC_DATA_SIZE);  // 512 bytes
 sspReceiveByte();                // dummy CRC
 sspReceiveByte();
 
 R1 = sspReceiveByte();
 if((R1 & 0x1F) != 0x05){
  SET_MMC_CS;
  sspReceiveByte();
  return 1;
 }
 
 while(!sspReceiveByte()){        // wait until write finish
  if(!(retry --)){
   SET_MMC_CS;
   sspReceiveByte();
   return 1;
  }
 }
 
 SET_MMC_CS;
 sspReceiveByte();
 return 0;
}


        与通用的程序相比就是在连续发送和连续接收的地方作了一些简单处理。改动不大,但是提升的效率却很惊人,读取速度提高了能有50%左右。

PARTNER CONTENT

文章评论0条评论)

登录后参与讨论
EE直播间
更多
我要评论
0
6
关闭 站长推荐上一条 /3 下一条