SD-карта и файловая система FAT.

По просьбам читателей нашего сайта сегодняшняя статья будет посвящена реализации работы с файловой системой FAT на подключенной к микроконтроллеру STM32 SD-карте. Статья будет небольшая, но информативная, только голые факты и ничего лишнего 😉

SD-карта и FatFs

Подключение карты памяти к микроконтроллеру уже было описано ранее — ссылка — поэтому сегодня мы сразу перейдем к обсуждению именно файловой системы FAT. Напомню только, что для экспериментов мы используем контроллер STM32 и самую обычную SD-карту. Подключение реализовано при помощи интерфейса SDIO. Осталось только прикрутить FAT 😉

Для работы с файловой системой мы будем использовать отличную библиотеку — FatFs, предназначенную, в первую очередь, именно для встраиваемых систем на микроконтроллерах. Плюсов у этой библиотеки очень много:

  • очень легко портируется на любую архитектуру
  • способна работать с несколькими томами
  • гибкая в настройке итд…

Список всех функций, реализованных в FatFs приводить не буду, рассмотрим в процессе работы некоторые из них.

Работа с файловой системой начинается, собственно, с «регистрации». Для этого API библиотеки FatFs содержит функцию f_mount():

FRESULT f_mount (
  BYTE  Drive,              // Номер диска
  FATFS*  FileSystemObject  // Указатель на объект файловой системы
);

В общем-то на этом подготовка к работе с FAT и заканчивается =) После выполнения операции f_mount() можно работать непосредственно с данными — создавать и удалять директории, файлы, записывать и считывать данные итд.

Таким образом, с FatFs в принципе все ясно, но остался открытым следующий вопрос — а как вообще адаптировать библиотеку под конкретную задачу. В нашем случае необходимо настроить использование интерфейса SDIO. А для этого нам нужно будет внести изменения всего лишь в один файл, а именно discio.c (файл идет в составе библиотеки FatFs). Итак, открываем файл и видим следующие функции:

DSTATUS disk_initialize (
	BYTE pdrv				/* Physical drive nmuber (0..) */
)
{
  return 0;
}
 
DSTATUS disk_status (
	BYTE pdrv		/* Physical drive nmuber (0..) */
)
{
  return 0;
}
 
DRESULT disk_read (
	BYTE pdrv,		/* Physical drive nmuber (0..) */
	BYTE *buff,		/* Data buffer to store read data */
	DWORD sector,	/* Sector address (LBA) */
	UINT count		/* Number of sectors to read (1..128) */
)
{
  return RES_OK;
}
 
#if _USE_WRITE
DRESULT disk_write (
	BYTE pdrv,			/* Physical drive nmuber (0..) */
	const BYTE *buff,	/* Data to be written */
	DWORD sector,		/* Sector address (LBA) */
	UINT count			/* Number of sectors to write (1..128) */
)
{ 
  return RES_OK;
}
#endif
 
#if _USE_IOCTL
DRESULT disk_ioctl (
	BYTE pdrv,		/* Physical drive nmuber (0..) */
	BYTE cmd,		/* Control code */
	void *buff		/* Buffer to send/receive control data */
)
{    
  return res;
}

Тело функций я оставил пока пустым. Именно эти функции использует FatFs и именно их нужно изменить в соответствии с используемым типом памяти/подключением. То есть, грубо говоря, если я использую SD-карту, то должен реализовать функцию disk_read() следующим образом:

DRESULT disk_read (
	BYTE pdrv,		/* Physical drive nmuber (0..) */
	BYTE *buff,		/* Data buffer to store read data */
	DWORD sector,	/* Sector address (LBA) */
	UINT count		/* Number of sectors to read (1..128) */
)
{
  SD_ReadData(buff, sector, count);
 
  return RES_OK;
}

В данном случае функция SD_ReadData() — это абстрактная функция записи данных на карту памяти SD, придуманная только для примера 😉 Если я буду использовать NAND-память, то нужно будет функцию SD_ReadData() заменить на NAND_ReadData(). Как видите, идея очень проста. На деле все сложнее, но не намного.

Итак, привожу полный код файла diskio.c, адfптированного под работу с картой памяти SD, подключенной к микроконтроллеру при помощи интерфейса SDIO:

/*-----------------------------------------------------------------------*/
/* Low level disk I/O module skeleton for FatFs     (C)ChaN, 2013        */
/*-----------------------------------------------------------------------*/
/* If a working storage control module is available, it should be        */
/* attached to the FatFs via a glue function rather than modifying it.   */
/* This is an example of glue functions to attach various exsisting      */
/* storage control module to the FatFs module with a defined API.        */
/*-----------------------------------------------------------------------*/
 
#include "diskio.h"		/* FatFs lower layer API */
#include "sdcard.h"
 
/* Definitions of physical drive number for each media */
#define ATA		0
#define MMC		1
#define USB		2
#define SECTOR_SIZE 512U
 
/*-----------------------------------------------------------------------*/
/* Inidialize a Drive                                                    */
/*-----------------------------------------------------------------------*/
 
DSTATUS disk_initialize (
	BYTE pdrv				/* Physical drive nmuber (0..) */
)
{
  return 0;
}
 
/*-----------------------------------------------------------------------*/
/* Get Disk Status                                                       */
/*-----------------------------------------------------------------------*/
 
DSTATUS disk_status (
	BYTE pdrv		/* Physical drive nmuber (0..) */
)
{
  return 0;
}
 
/*-----------------------------------------------------------------------*/
/* Read Sector(s)                                                        */
/*-----------------------------------------------------------------------*/
 
DRESULT disk_read (
	BYTE pdrv,		/* Physical drive nmuber (0..) */
	BYTE *buff,		/* Data buffer to store read data */
	DWORD sector,	/* Sector address (LBA) */
	UINT count		/* Number of sectors to read (1..128) */
)
{
  if(count==1)
  {
    SD_ReadBlock(&buff[0] ,sector << 9,SECTOR_SIZE);
    while(SD_GetStatus() != SD_TRANSFER_OK);
  }
  else
  {
    SD_ReadMultiBlocks((&buff[0]), sector << 9,SECTOR_SIZE,count);
    while(SD_GetStatus() != SD_TRANSFER_OK);
  }
 
  return RES_OK;
}
 
/*-----------------------------------------------------------------------*/
/* Write Sector(s)                                                       */
/*-----------------------------------------------------------------------*/
 
#if _USE_WRITE
DRESULT disk_write (
	BYTE pdrv,			/* Physical drive nmuber (0..) */
	const BYTE *buff,	/* Data to be written */
	DWORD sector,		/* Sector address (LBA) */
	UINT count			/* Number of sectors to write (1..128) */
)
{
  if(count==1)
  {
    SD_WriteBlock((BYTE*)(&buff[0]),sector << 9, SECTOR_SIZE);
    while(SD_GetStatus() != SD_TRANSFER_OK);
  }
  else
  {
    SD_WriteMultiBlocks((BYTE*)(&buff[0]) , sector << 9, SECTOR_SIZE, count);
    while(SD_GetStatus() != SD_TRANSFER_OK);
  }
 
  return RES_OK;
}
#endif
 
/*-----------------------------------------------------------------------*/
/* Miscellaneous Functions                                               */
/*-----------------------------------------------------------------------*/
 
#if _USE_IOCTL
DRESULT disk_ioctl (
	BYTE pdrv,		/* Physical drive nmuber (0..) */
	BYTE cmd,		/* Control code */
	void *buff		/* Buffer to send/receive control data */
)
{
  DRESULT res = RES_ERROR;
  SD_CardInfo CardInfo;
 
  switch (cmd)
  {
    case CTRL_SYNC:
      res = RES_OK;
      break;
 
    case GET_SECTOR_COUNT:
      SD_GetCardInfo(&CardInfo);
      *(DWORD*)buff = CardInfo.CardCapacity / 512;
      res = RES_OK;
      break;
 
    case GET_SECTOR_SIZE:
      *(DWORD*)buff = 512;
      res= RES_OK;
      break;
 
    case GET_BLOCK_SIZE:
      *(DWORD*)buff = 512;
      res= RES_OK;
      break;
  }
 
  return res;
}
#endif

Теперь наша библиотека готова к работе! Давайте рассмотрим небольшой пример, включающий в себя монтирование диска FAT и чтение данных из файла:

void main()
{
  FATFS fileSystem;
  FIL testFile;
  uint8_t testBuffer[4];
  UINT testBytes;
 
  if(f_mount(&fileSystem, "0", 1) == FR_OK)
  {
    uint8_t path[9] = "test.txt";
    path[8] = '\0';
 
    f_open(&testFile, (char*)path, FA_READ);
 
    f_read(&testFile, testBuffer, 4, &testBytes);
  }
}

Что же тут происходит? Для начала мы регистрируем диск FAT, и если эта операция прошла успешно, то мы считываем 4 байта из файла test.txt. Важной особенностью является то, что путь к файлу в FatFs должен заканчиваться символом конца строки -\0.

Переменная testBytes нужна для хранения количества прочитанных байт. То есть в случае успешного чтения данных в этом примере после выполнения функции f_read() testBytes должна стать равной 4. Вот в принципе и все, что тут нужно обсудить 😉

Итак, завершаем сегодняшнюю статью! Для сборки полного проекта можно скачать проект с SDIO из соответствующей статьи на нашем сайте и добавить в него модуль FatFS с изменненым файлом discio.c.

Понравилась статья? Поделись с друзьями!

SD-карта и файловая система FAT.: 12 комментариев
  1. Я автору блога выслал материалы, как он их оформит, так и будет статья в скором времени.

  2. Спасибо. Но лучше бы выложили проект рабочий, а так фиг знает подчеркивает половину вашего файла и непонятно что ему надо. Жалко было готовый проект выложить что ли…

  3. Какой толк от статьи без проекта. Ругается на отсутствие функций SD_GetStatus() и не знает что такое SD_TRANSFER_OK

  4. Не знаю, у кого-то может и запустился проект с приведенным здесь diskio.c, но у меня виснет на while. Пришлось переписать по-другому и все заработало. Тестил на stm32f103rgt6. Кому нужен исходник проекта — обращайтесь

  5. Приветствую. Подскажите, пытаюсь запустить stm32f103 по USB в режиме флешки (mass storage class). Как настроить файлик user_diskio.c? Проект собирал в CubMX, но файлы и функции для FATFS ровно те же. Заранее спасибо!

  6. День добрый. У меня опять вопрос. В общем реализовал я режим MSC, без использования каких либо физических носителей пока что. В общем с компа форматируется. Суть в чем. Создаю файлик с нужным именем (на носителе), пишу в него текст, вызываю функции, чтобы считать открыть и считать данные, все работает, вижу информация. Другой случай, вызываю функция f_open(&testFile, (char*)file_way, FA_WRITE | FA_CREATE_ALWAYS) и f_write(&testFile, write_buffer, 12, &testBytes) ну и f_close(&testFile). Смотрю в компе, файла нету, пусто. Вызываю функцию открыть и прочитать искомый файл, а он есть на носителе, все работает, только с компа его не видно. Передергиваю USB, файл нашелся.Как это исправить. Я так понимаю нужно, чтобы комп снова прочитал таблицу файловой системы внешнего носителя после того, как STMка записала данные. Но как это делать. Заранее спасибо!

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *