Programming:C:FatFS

From Retro-CPU.run
Jump to: navigation, search

Um os recursos que cobiço a muito tempo para o Z80SoC é a possibilidade de acessar dados em cartão de memória SD. Mas para implementar esse recurso, há muitos obstáculos que precisam ser superados:

  • Criar uma aplicação para gerir o sistema de arquivos, certamente do tipo FAT
  • Criar rotinas de baixo nível (drivers) para o sistema de FAT conseguir acessar o cartão
  • Implementar no hardware o protocolo SPI para acessor ao cartão, e disponibilizar comandos para os drivers

Para o sistema de gerenciamento de arquivos, há o excelente FatFS (http://elm-chan.org/fsw/ff/00index_e.html). O problema é entender o funcionamento, como utilizar, e mais difícil, como implementar as rotinas de baixo nível em diskio.c para acesso ao hardware.

Este é exatamente o objectivo deste artigo: implementar o FatFS de modo a compreender sua utilização e adquitir alguma prática, antes de partir para implementar os drivers para o cartão SD de verdae.

A implementação mostrada aqui simula um disco através de um diretório. E cada setor do disco é um arquivo dentro dest diretório, sendo que os setores identificados pelo seu número.

Todos o gerenciamento de arquivos neste "disco" é feito pelo FatFS, e o usuáro só ver o disco (diretório) e os arquivos correspondentes ao setores do disco. Funciona que é uma maravilha, e o mais importante é que, analisando um código simplificado em C, fica mais fácil converter para outros tipos de mídia físicas.


Para fazer download do projeto completo, consulte a área de arquivos do grupo Retro-CPU.run no Facebook:

https://www.facebook.com/groups/retrocpu/files/

O código pode ser facilmente compilado em outras plataformas.

Aqui está o diskio.c:

BYTE DIR_disk_status(
   BYTE vol
)
{
   struct stat info;
   char diskName[18];
   sprintf(diskName, "Disk%i", vol);
   if( !stat( diskName, &info ))
       return 0;
   else
       return 1;
}

DSTATUS disk_status (
   BYTE pdrv 
)
{
      DSTATUS stat;
       stat = DIR_disk_status(pdrv);
       return stat;
   	return STA_NOINIT;
}
 
/*-----------------------------------------------------------------------*/
/* Initialize a Drive                                                    
/*-----------------------------------------------------------------------*/
 DSTATUS disk_initialize (
   BYTE pdrv		/* Physical drive nmuber to identify the drive */
)
{
       DSTATUS stat;
       if (DIR_disk_status(pdrv)==0)
           return 0;
       else {
           char diskName[18];
           sprintf(diskName, "Disk%i", pdrv);
           mkdir(diskName, 0700);
           return DIR_disk_status(pdrv);
       }
       return STA_NOINIT;
}
 
/*-----------------------------------------------------------------------*/
/* Read Sector(s)                                                        
/*-----------------------------------------------------------------------*/ 

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 res;
       int result;
       BYTE fdata = *buff;
       char diskName[18];
       FILE *fp;
   
       for(int i=0 ; i < count ; i++) {
           sprintf(diskName, "Disk%i/sector.%i", pdrv, i+sector);
           printf("disk_read sector %s\n",diskName);    
           fp = fopen(diskName, "rb"); // open a sector
           if (!fp) {
               printf("disk_read: create sector\n");
               fp = fopen(diskName, "w+");
           }
       // Rewind to start of file
       rewind(fp);
       // read a sector of 512 bytes
       fread(*&buff, 512, 1, fp);
       fclose(fp);  // close this sector
       // Move buffer address to receive next sector
       buff += 512;
   }
   
       *buff = &fdata;
       return RES_OK;
}

/*-----------------------------------------------------------------------*/
/* Write Sector(s)                                                       
/*-----------------------------------------------------------------------*/
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 res;
       int result;

       char diskName[18];
       FILE *fp;

       for(int i=0 ; i < count ; i++) {
           sprintf(diskName, "Disk%i/sector.%i", pdrv, i+sector);   
           printf("disk_write sector %s\n",diskName);     
           fp = fopen(diskName, "w+"); // open a sector
           // read a sector of 512 bytes
           fwrite(*&buff, 512, 1, fp);
           fclose(fp);  // close this sector
           // Move buffer address to receive next sector
           buff += 512;    
       }
       return RES_OK;   
}

*-----------------------------------------------------------------------*/
/* Miscellaneous Functions                                               
/*-----------------------------------------------------------------------*/
DRESULT disk_ioctl (
       BYTE pdrv,		/* Physical drive nmuber (0..) */
       BYTE cmd,		/* Control code */
       void *buff		/* Buffer to send/receive control data */
)
{
       DRESULT res;
       int result;

       switch (cmd) {
       case CTRL_SYNC :
           return RES_OK;
       case GET_SECTOR_COUNT :
           *(DWORD*)buff  = 65536;
           //printf("GET_SECTOR_COUNT = %lu\n",*(unsigned long*)buff);
           return RES_OK;
       case GET_SECTOR_SIZE :
           *(DWORD*)buff  = 512;
           //printf("GET_SECTOR_SIZE = %lu\n",*(unsigned long*)buff);
           return RES_OK;
       case GET_BLOCK_SIZE :
           *(DWORD*)buff  = 1;
           return RES_OK;
       case CTRL_TRIM :
           return RES_PARERR;
       }
   
       return RES_PARERR;
}

*-----------------------------------------------------------------------*/
/* Time funciton                                               
/*-----------------------------------------------------------------------*/
DWORD get_fattime (void) {
       return 0;
}


Aqui está o programa de demonstração que permite formatar o disco, criar, ler e apagar arquivos, e listar conteúdo de diretórios:

//  main.c
//  diskff
//
//  Created by Ronivon Costa on 7/15/16.

#include <sys/types.h> 
#include <sys/stat.h> 
#include <stdio.h>
#include <stdlib.h>
#include "diskio.h"
#include "ff.h"

FRESULT scan_files (
                   char* path        /* Start node to be scanned (***also used as work area***) */
)
{
   FRESULT res;
   DIR dir;
   UINT i;
   static FILINFO fno; 
   
   res = f_opendir(&dir, path);                       /* Open the directory */
   if (res == FR_OK) {
       for (;;) {
           res = f_readdir(&dir, &fno);                   /* Read a directory item */
           if (res != FR_OK || fno.fname[0] == 0) break;  /* Break on error or end of dir */
           if (fno.fattrib & AM_DIR) {                    /* It is a directory */
               i = strlen(path);
               sprintf(&path[i], "/%s", fno.fname);
               res = scan_files(path);                    /* Enter the directory */
               if (res != FR_OK) break;
               path[i] = 0;
           } else {                                       /* It is a file. */
               printf("%s/%s\n", path, fno.fname);
           }
       }
       f_closedir(&dir);
   }    
   return res;
}

int main(int argc, const char * argv[]) {

   BYTE *buff;
   FATFS fs;           /* File system object */
   FIL fil;            /* File object */
   FRESULT res;        /* API result code */
   UINT bw;            /* Bytes written */
   BYTE work[_MAX_SS]; /* Work area (larger is better for process time) */
   char pdrv[5];
   char fn[19];
   char txt[255];
   char buffdir[256];

   if (argc < 2) {
           
       printf("\n\nFatFS demonstration and sample code.\n\n");
       printf("This code helps to understand how to use FatFS,\n");
       printf("through the use of a simple diskio.c low level interface.\n");
       printf("The drivers demostrated here implements a single physical disk\n");
       printf("on top of a directory 'Disk0' on the local filesystem.\n");
       printf("This directory represents the physical disk, and needs to be formated before it can be used.\n");
       printf("Each sector inside this Disk is represented as a file with 512 bytes.\n");
       printf("After formating the disk, all fat operations can be performed using FatFS library.\n");
       printf("\n\n");
   
       printf("Options implemented in this example:\n");
       printf("    format \n");
       printf("    write  <file name> <string to write to file>\n");
       printf("    read   file name>\n");
       printf("    delete file/directory name>\n");
       printf("    dir    \n");
       exit(0);
   }
   
   if (strcmp(argv[1],"dir") == 0 && argc > 1) {
       res = f_mount(&fs, "0", 1);
       if (res == FR_OK) {
           strcpy(buffdir, "/");
           res = scan_files(buffdir);
           printf("dir stat = %i\n",res);
       }
   }
   
   if (strcmp(argv[1],"format") == 0) {
       res = f_mkfs("0", FM_ANY, 0, work, sizeof work);
       printf("f_mkfs stat = %i\n",res);
   }
  
   if (strcmp(argv[1],"write") == 0 &&  argc > 2) {
       strcpy(fn, argv[2]);
       strcpy(txt, argv[3]);
       res = f_mount(&fs, "0", 0);
       printf("f_mount stat = %i\n",res);
       printf("Calling f_open\n");
       res = res = f_open(&fil, &fn, FA_CREATE_NEW | FA_WRITE);
       printf("f_open stat = %i\n",res);
       res = f_write(&fil, &txt, sizeof(txt) + 1, &bw);
       printf("f_write file stat = %i\n",res);
       f_close(&fil);
       printf("f_close file stat = %i\n",res);
   }
   
   if (strcmp(argv[1],"read") == 0 && argc > 2) {
       strcpy(fn, argv[2]);
       res = f_mount(&fs, "0", 0);
       printf("f_mount stat = %i\n",res);
       printf("Calling f_open %s\n",fn);
       res = f_open(&fil, &fn, FA_READ);
       printf("f_open read stat = %i\n",res);
       buff = malloc(sizeof(fil)+1);
       res = f_read(&fil, buff, sizeof(fil)+1, &bw);
       printf("f_read file stat = %i\n",res);
       res = f_close(&fil);
       printf("f_close file stat = %i\n",res);
       printf("%s\n",buff);
   }
   
   if (strcmp(argv[1],"delete") == 0 && argc > 2) {
       strcpy(fn, argv[2]);
       res = f_mount(&fs, "0", 0);
       printf("f_mount stat = %i\n",res);
       printf("Calling f_unlink %s\n",fn);
       res = f_unlink(&fn);
       printf("f_unlink read stat = %i\n",res);
   }
   exit(0);
}