STM32 и FatFs. Файловая система FAT на внешней SD-карте.

SD-карта и FatFs

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

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

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

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

Список всех функций, реализованных в FatFs приводить не буду, в процессе работы рассмотрим некоторые из них. Работа с файловой системой начинается, собственно, с “регистрации”. Для этого API библиотеки FatFs содержит функцию f_mount():

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

В общем-то на этом подготовка к работе и заканчивается 🙂 После выполнения операции 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, адаптированного под работу с картой памяти 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] = '
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);
}
}
'; f_open(&testFile, (char*)path, FA_READ); f_read(&testFile, testBuffer, 4, &testBytes); } }

Что же тут происходит? Для начала мы регистрируем диск FAT, и если эта операция прошла успешно, то мы считываем 4 байта из файла test.txt. Важной особенностью является то, что путь к файлу в FatFs должен заканчиваться символом конца строки – “\0”. Переменная testBytes нужна для хранения количества прочитанных байт. То есть в случае успешного чтения данных в этом примере после выполнения функции f_read() testBytes должна стать равной 4. Вот в принципе и все, что тут нужно обсудить 🙂

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

Поделиться!

Подписаться
Уведомление о
guest
16 Комментарий
старее
новее большинство голосов
Inline Feedbacks
View all comments
Elis
Elis
5 лет назад

Спасибо за статью. Весьма полезно.

GLEP
GLEP
5 лет назад

Отличная статья, спасибо!

GLEP
GLEP
5 лет назад

Статейка про тачскрин MiniSTM32 будет?

mishadesh
mishadesh
5 лет назад

Я автору блога выслал материалы, как он их оформит, так и будет статья в скором времени.

Павел
Павел
4 лет назад

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

Олег
Олег
4 лет назад

Будьте добры, приведите демо проект

Евгений
Евгений
4 лет назад

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

Алексей
Алексей
Reply to  Евгений
1 год назад

У меня такая же проблема. Хотелось бы решение посмотреть… ну попробую сам допилить конечно..

Сергей
Сергей
4 лет назад

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

Дима
Дима
Reply to  Сергей
4 лет назад

Поделитесь, если можно)

Геннадий
Геннадий
Reply to  Сергей
1 год назад

Сергей, если можно поделитесь проектом. Спасибо.

Дмитрий
Дмитрий
4 лет назад

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

Dimaster
3 лет назад

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

Алексей
Алексей
1 год назад

Добрый день!
Продолжаю разбираться с файловой системой и работой с картой через SDMMC.
Вопрос такой: в большинстве описаний низкоуровневой работы с SD с применением diskio. В большинстве источников пишут про некую функцию disk_timerproc которая должны вызываться раз в 10 мс. В HAL я её не нашел. Описаний, как её создавать тоже.
У меня родились подозрения, что её функционал реализовал cube и теперь не нужно заморачиваться по этому поводу.
Так ли это?

Присоединяйтесь!

Profile Profile Profile Profile Profile
Vkontakte
Twitter

Язык сайта

Август 2020
Пн Вт Ср Чт Пт Сб Вс
 12
3456789
10111213141516
17181920212223
24252627282930
31  

© 2013-2020 MicroTechnics.ru