Petit Fat File System Module Porting to the ADUCM3029 (Review)

Petit FatFs is a sub-set of FatFs module for tiny 8-bit microcontrollers. It is written in compliance with ANSI C and completely separated from the disk I/O layer. It can be incorporated into the tiny microcontrollers with limited memory even if the RAM size is less than sector size.

Features

  • Very small RAM consumption (44 bytes work area + certain stack).
  • Very small code size (2K-4K bytes).
  • FAT12, FAT16 and FAT32.
  • Single volume and Single file.
  • Streaming file read.
  • File write function with some restrictions.

I've tried to implement the petit fat to the aducp3029 for SDCards adapters guided by sample projects in the http://elm-chan.org/fsw/ff/00index_p.html.

After compiling, the map file indicated:

Module                  ro code  ro data  rw data

diskio.o                  1 140        9       67

pff.o                     1 536                 4

I'm asking for a code review before passing to tests. If I succeed the implementation I will post the project in this topic to be used by anyone.

/*-----------------------------------------------------------------------*/
/* Low level disk I/O module skeleton for Petit FatFs (C)ChaN, 2014      */
/*-----------------------------------------------------------------------*/

#include "diskio.h"


/*-----------------------------------------------------------------------*/
/* ADI LIBS */
/*-----------------------------------------------------------------------*/
#include <stdint.h>
#include <stdbool.h>
#include <adi_gpio.h>
#include <adi_spi.h>


/*--------------------------------------------------------------------------
   Module Private Functions
---------------------------------------------------------------------------*/
// SDCard Command Types
#define CMD0	0                       // software reset
#define CMD1    1
#define CMD8	8                       // check voltage range
#define CMD9	9                       // read CSD register
#define CMD10   10                      // card id
#define CMD12	12                      // stop to read data
#define ACMD13	13                      
#define CMD16	16
#define CMD17	17
#define CMD18	18
#define ACMD23  23
#define CMD24   24
#define CMD25   25
#define	ACMD41	41
#define CMD55	55
#define CMD58	58

/* Card type flags (CardType) */
#define CT_MMC				0x01	/* MMC version 3 */
#define CT_SD1				0x02	/* SD version 1 */
#define CT_SD2				0x04	/* SD version 2+ */
#define CT_BLOCK			0x08	/* Block addressing */

static BYTE CardType;
static uint8_t dummybyte=0xFF;
static uint8_t rcv;

#define SPI_DEV_NUM     1
#define SPI_CS_PORT     ADI_GPIO_PORT2
#define SPI_CS_PIN      ADI_GPIO_PIN_11


// status for spi config
#define SPI_CONFIG_SUCCESS                  0
#define SPI_CONFIG_FAIL                     1

// spi speed: 0 - 400kHz , 1 - 4MHz
#define SPI_LOW_SPEED                       0
#define SPI_HI_SPEED                        1

#define ADI_DEBUG_SPI
#ifdef ADI_DEBUG_SPI
#include <common.h>
#endif

uint8_t spi_config(void);
void spi_read_command(uint8_t tx_size);
void spi_chip_select(bool assert);
void spi_clockrate(uint8_t speed);
void spi_readwrite(uint8_t *txbuff, uint8_t *rxbuff, uint16_t txsize, uint16_t rxsize);
void spi_terminate();


static uint8_t SPIMem[ADI_SPI_MEMORY_SIZE];
static ADI_SPI_HANDLE spih_Dev;

static uint8_t GPIOCallbackMem[ADI_GPIO_MEMORY_SIZE];

void spi_chip_select(bool assert);
void spi_clockrate(uint8_t speed);
void spi_terminate();
bool spi_cs_init();
static bool spi_init_peripheral();

void spi_readwrite(uint8_t *txbuff, uint8_t *rxbuff, uint16_t txsize, uint16_t rxsize);

/* SD card commands */
uint8_t send_command(uint8_t cmd, uint32_t args);

/* SPI implementations */
void spi_chip_select(bool assert){  
  //DEBUG_MESSAGE("chip_select start \n");
  (!assert) ? adi_gpio_SetHigh(SPI_CS_PORT, SPI_CS_PIN) : adi_gpio_SetLow(SPI_CS_PORT, SPI_CS_PIN);
  if(!assert)
  {
    for(unsigned int k=0;k<1000;k++);
  }
  //DEBUG_MESSAGE("chip_select end \n");
}

void spi_clockrate(uint8_t speed){
  adi_spi_SetBitrate(spih_Dev, (speed) ? 4000000 : 400000);
}

bool spi_cs_init(){
  
  adi_gpio_Init(GPIOCallbackMem, ADI_GPIO_MEMORY_SIZE);
  adi_gpio_PullUpEnable(SPI_CS_PORT , SPI_CS_PIN, false);
  adi_gpio_OutputEnable(SPI_CS_PORT, SPI_CS_PIN, true);
  adi_gpio_SetHigh(SPI_CS_PORT, SPI_CS_PIN);
  return true;
}

void spi_terminate()
 {
   adi_spi_Close(spih_Dev);
 }

static bool spi_init_peripheral(){
   adi_spi_Open(SPI_DEV_NUM, SPIMem, ADI_SPI_MEMORY_SIZE, &spih_Dev);
   adi_spi_SetBitrate(spih_Dev, 400000);    
   adi_spi_SetChipSelect(spih_Dev, ADI_SPI_CS_NONE);
   adi_spi_SetIrqmode(spih_Dev, 0u);
   adi_spi_SetContinuousMode (spih_Dev, false);
   return true;
   
}

uint8_t spi_config(void){ 
  bool is_success = spi_init_peripheral() && spi_cs_init(); 
  return is_success;
}


void spi_readwrite(uint8_t *txbuff, uint8_t *rxbuff, uint16_t txsize, uint16_t rxsize){
  ADI_SPI_TRANSCEIVER spi_xcv_buff;

  if(txsize > 0){
    
    spi_xcv_buff.pTransmitter = txbuff;
    spi_xcv_buff.pReceiver = NULL;
    spi_xcv_buff.TransmitterBytes = txsize;
    spi_xcv_buff.ReceiverBytes = 0;
    spi_xcv_buff.nTxIncrement = 1;
    spi_xcv_buff.nRxIncrement = 0;
        spi_xcv_buff.bDMA = true;
    spi_xcv_buff.bRD_CTL = false;
    
    adi_spi_MasterSubmitBuffer (spih_Dev, &spi_xcv_buff);
    
//    
//    bool complete = false;
//   do{
//    adi_spi_isBufferAvailable(spih_Dev, &complete );
//    }while(!complete);
    
      
   // adi_spi_MasterReadWrite(spih_Dev, &spi_xcv_buff);
        bool complete = false;
   do{
    adi_spi_isBufferAvailable(spih_Dev, &complete );
     }while(!complete);
   
  }
  
  if(rxsize > 0){
    
    spi_xcv_buff.pTransmitter = NULL;
    spi_xcv_buff.pReceiver = rxbuff;
    spi_xcv_buff.TransmitterBytes = 0;
    spi_xcv_buff.ReceiverBytes = rxsize;
    spi_xcv_buff.nTxIncrement = 0;
    spi_xcv_buff.nRxIncrement = 1;
          spi_xcv_buff.bDMA = true;
    spi_xcv_buff.bRD_CTL = false;

    adi_spi_MasterSubmitBuffer (spih_Dev, &spi_xcv_buff);
    
//    
//      bool complete = false;
//   do{
//    adi_spi_isBufferAvailable(spih_Dev, &complete );
//    }while(!complete);
    
          //  adi_spi_MasterReadWrite(spih_Dev, &spi_xcv_buff);
    
        bool complete = false;
   do{
    adi_spi_isBufferAvailable(spih_Dev, &complete );
     }while(!complete);
   
  }  
}

void read_response_args(uint8_t *ret_val, uint16_t size)
 {
     spi_chip_select(true); 
     //  for(unsigned int k=0;k<100;k++);
     spi_readwrite(NULL, ret_val, 0, size);  
     //   for(unsigned int k=0;k<100;k++);
     spi_chip_select(false);
     // for(unsigned int k=0;k<100;k++);
}

/* send command function */
 uint8_t send_command(uint8_t cmd, uint32_t args)
 {   
     uint8_t tx_frame[7] = {0x40, 0, 0, 0, 0, 1, 0xff};
     uint8_t timeout_counter = 0, loop_count;
     uint8_t resp = 255;
     bool busy = true;
     // cmd
     tx_frame[0] = 0x40 + cmd;
     // arguments
     tx_frame[1] = (args >> 24) & 0xff;
     tx_frame[2] = (args >> 16) & 0xff;
     tx_frame[3] = (args >> 8) & 0xff;
     tx_frame[4] = args & 0xff; 
     // crc
     if(cmd == CMD0) tx_frame[5] = 0x95;  
     if(cmd == CMD8) tx_frame[5] = 0x87;
  
     do
     {   
         spi_chip_select(true);
         //  for(unsigned int k=0;k<1000;k++);
         spi_readwrite(tx_frame, &resp, 7, 1);
         //  for(unsigned int k=0;k<1000;k++);
         spi_chip_select(false);
         //  for(unsigned int k=0;k<100;k++);
         loop_count = 0;    
         while(resp == 0xff)
         {
             read_response_args(&resp, 1);
             if(++loop_count == 8) break;
         }    
          busy = ((resp & 0x80) == 0x80)? true : false;    
      } while(busy && timeout_counter++ < 5);    
     return resp;
}

/*-----------------------------------------------------------------------*/
/* Initialize Disk Drive                                                 */
/*-----------------------------------------------------------------------*/

DSTATUS disk_initialize (void)
{
	BYTE ty;
    uint8_t n,cmd,ocr[4],rxbuff[10];
    uint16_t tmr;
    
#if PF_USE_WRITE
	if (CardType != 0 && IS_CS_LOW) disk_writep(0, 0);	/* Finalize write process if it is in progress */
#endif
    spi_config();
    for (n = 10; n; n--) 
        spi_readwrite(&dummybyte,rxbuff,1,1);/* 80 dummy clocks with CS=H */
    ty=0;
    if(send_command(CMD0,0)==1) /* GO_IDLE_STATE */
    {
        if(send_command(CMD8,0x1AA)==1) /* check SD v2 */
        {
            for(n=0;n<4;n++) 
                spi_readwrite(&dummybyte,ocr,1,4); /* Get trailing return value of R7 resp */
            if(ocr[2]==0x01 && ocr[3] == 0xAA) /* The card can work at vdd range of 2.7-3.6V */
            {
                for(tmr=10000;tmr && send_command(ACMD41, 1UL << 30);tmr--)
                    /* delay 100µs*/ ; /* Wait for leaving idle state (ACMD41 with HCS bit) */
                if (tmr && send_command(CMD58, 0) == 0) /* Check CCS bit in the OCR */ 
                {		
					for (n = 0; n < 4; n++) 
                        spi_readwrite(&dummybyte,ocr,1,4);
					ty = (ocr[0] & 0x40) ? CT_SD2 | CT_BLOCK : CT_SD2;	/* SDv2 (HC or SC) */
				}
            }
        }
        else  /* SDv1 or MMCv3 */
        {
            if(send_command(ACMD41,0) <=1) /* SDv1 */
            {
                ty = CT_SD1; 
                cmd = ACMD41;	
            }
            else /* MMCv3 */
            {
                ty = CT_MMC; 
                cmd = CMD1;	
            }
            for (tmr = 10000; tmr && send_command(cmd, 0); tmr--) 
                /*dly_100us()*/;	/* Wait for leaving idle state */
			if (!tmr || send_command(CMD16, 512) != 0) /* Set R/W block length to 512 */
            {	
				ty = 0;
			}
        }
    }
    CardType = ty;
	spi_chip_select(false);
	spi_readwrite(&dummybyte,rxbuff,1,1);
	return ty ? 0 : STA_NOINIT;
}



/*-----------------------------------------------------------------------*/
/* Read Partial Sector                                                   */
/*-----------------------------------------------------------------------*/

DRESULT disk_readp (
	BYTE* buff,		/* Pointer to the destination object */
	DWORD sector,	/* Sector number (LBA) */
	UINT offset,	/* Offset in the sector */
	UINT count		/* Byte count (bit15:destination) */
)
{
    BYTE* buff_i=buff;
    DRESULT res;
	BYTE rc;
	UINT bc;
	if (!(CardType & CT_BLOCK)) sector *= 512;	/* Convert to byte address if needed */

	res = RES_ERROR;
	if (send_command(CMD17, sector) == 0) /* READ_SINGLE_BLOCK */
    {	
		bc = 40000;	/* Time counter */
		do /* Wait for data block */
        {				
			spi_readwrite(&dummybyte,&rc,1,1);
		} while (rc == 0xFF && --bc);
		if (rc == 0xFE) /* A data block arrived */
        {	
			bc = 512 + 2 - offset - count;	/* Number of trailing bytes to skip */
			/* Skip leading bytes in the sector */
			while (offset--) 
                spi_readwrite(&dummybyte,&rcv,1,1);
			/* Receive a part of the sector */
			if (buff) /* Store data to the memory */
            {	
				do 
                {
					spi_readwrite(&dummybyte,buff_i++,1,1);
				} while (--count);
			} 
            else /* Forward data to the outgoing stream */
            {	
				do 
                {
					spi_readwrite(&dummybyte,&rcv,1,1);
				} while (--count);
			}

			/* Skip trailing bytes in the sector and block CRC */
			do spi_readwrite(&dummybyte,&rcv,1,1); while (--bc);

			res = RES_OK;
		}
	}

	spi_chip_select(false);
	spi_readwrite(&dummybyte,&rcv,1,1);
	return res;
}



/*-----------------------------------------------------------------------*/
/* Write Partial Sector                                                  */
/*-----------------------------------------------------------------------*/

DRESULT disk_writep(const BYTE* buff,DWORD sc)
{
    DRESULT res;
	UINT bc;
	static UINT wc;	/* Sector write counter */
    
    res = RES_ERROR;

	if (buff) /* Send data bytes */
    {		
		bc = sc;
		while (bc && wc) /* Send data bytes to the card */
        {		
            spi_readwrite((uint8_t*)buff++,NULL,1,0);
			wc--; bc--;
		}
		res = RES_OK;
	} else {
		if (sc) /* Initiate sector write process */
        {	
			if (!(CardType & CT_BLOCK)) 
                sc *= 512;	/* Convert to byte address if needed */
			if (send_command(CMD24, sc) == 0) /* WRITE_SINGLE_BLOCK */
            {
                uint8_t ss=dummybyte-1;
                spi_readwrite(&dummybyte,NULL,1,0);spi_readwrite(&ss,NULL,1,0); 
				wc = 512;							/* Set byte counter */
				res = RES_OK;
			}
		} else 
        {	/* Finalize sector write process */
			bc = wc + 2;
			while (bc--) /* Fill left bytes and CRC with zeros */
            {
                uint8_t ss=dummybyte+1;
                spi_readwrite(&ss,NULL,1,0); 
            }
            spi_readwrite(&dummybyte,&rcv,1,1);
			if ((rcv & 0x1F) == 0x05) /* Receive data resp and wait for end of write process in timeout of 500ms */
            {	
                spi_readwrite(&dummybyte,&rcv,1,1);
				for (bc = 5000; rcv != 0xFF && bc; bc--) /* Wait for ready */
                {	
					/*dly_100us()*/;	/* Wait for leaving idle state */
				}
				if (bc) res = RES_OK;
			}
			spi_chip_select(false);        
	        spi_readwrite(&dummybyte,&rcv,1,1);
		}
	}
	return res;
}