Работа с SD картой. MMC. FAT fs файловая система

Введение

В этой части мы рассмотрим библиотеку для работы с дисками, имеющими файловую систему FAT. Это всем известная библиотека Elm Chan`a - Petit FatFs. Она представляет собой облегченную версия библиотеки FatFs и предназначена для микроконтроллеров с небольшим объемом оперативной памяти. Конечно, функционал Petit FatFs сильно ограничен, но имеет смысл начать знакомство с нее, потому что в ней проще разобраться. 

В этой части изложена информация справочного характера, которую я почерпнул из документации на библиотеку, а в следующей части будет уже практический материал.
 

Состав библиотеки

Библиотека Petit FatFs состоит из 5 файлов. Изначально она не заточена под какой-т о конкретный носитель памяти с файловой системой FAT и может использоваться с любым из них. Для этого к библиотеке нужно добавить реализацию трех низкоуровневых функций для работы с диском - функцию инициализации, записи и чтения.

integer.h - здесь объявлены целочисленные типы данных, используемые в библиотеке

diskio.h - описаны прототипы низкоуровневых функций для работы с диском и статусные коды, возвращаемые функциями.

diskio.c - файл реализации низкоуровневых функций для работы с диском. Изначально содержит "пустышки".

pff.h - здесь задается конфигурация библиотеки, объявлены типы данных и прототипы функций для взаимодействия с файловой системой диска

pff.c - файл реализации функций для взаимодействия с файловой системой диска

Низкоуровневая работа с SD картой

Как вы понимаете из описания выше, для работы с SD картой библиотеку Petit FatFs нужно "допиливать". А именно, писать реализацию низкоуровневых функций в файле diskio.c. На сайте автора есть архив с примерами использования библиотеки Petit FatFs с различными дисками и микроконтроллерами. Есть там и пример использования SD карт с AVR. В принципе, можно не разбираться с низкоуровневой работой с SD картой, а взять уже готовый код, что я и сделаю в следующий части.

Подключение библиотеки и настройка

1. Копируем файлы библиотеки Petit FatFs в папку проекта

2. Подключаем сишные файлы (diskio.c, pff.c)к проекту внутри среды разработки

3. Задаем конфигурацию библиотеки Petit FatFs в файле pff.h

4. Включаем (инклюдим) заголовочные файлы библиотеки (integer.h, pff.h и diskio.h) в сишный файл, где будут использоваться ее функции.

5. Реализуем низкоуровневые функции для работы с диском или подставляем вместо diskio.c какой-нибудь "готовый" файл, например из примеров Elm Chan`a. 

Ну а дальше используем функции библиотеки.

Функции библиотеки Petit FatFs

FRESULT pf_mount (FATFS* fs) - смонтировать/демонтировать диск. Эта функция должна вызываться перед началом работы с диском. Она получает данные о структуре файловой системы диска и позволяет продолжить работу с его содержимым. Также функция демонтирует или виртуально отключает диск, если ее вызывать с нулевым указателем. Когда диск демонтирован, все остальные функции библиотеки возвращают FR_NOT_ENABLED (диск не смонтирован). Функция pf_mount() доступна всегда.

Параметры

FATFS *fs - указатель на объект типа FATFS. Это объект библиотеки, в котором описана структура файловой системы. На него можно взглянуть в файле pff.h. Ну а по сути, это просто переменная определенного типа, которая должна быть предварительно объявлена.

Возвращаемые значения

FR_OK - успешное завершение функции
FR_NOT_READY - не удалось инициализировать диск
FR_DISK_ERR - ошибка диска
FR_NO_FILESYSTEM - на диске нет правильного FAT раздела

Пример использования
 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
FATFS fs; //объявление объекта FATFS
FRESULT res; //переменная для возвращаемых значений
 
...
//смонтировать диск
res = pf_mount(&fs);
if (res == FR_OK){
   //диск смонтирован, продолжаем работу
   ...
 
   //демонтируем диск, передав функции нулевой указатель
   pf_mount(NULL);
}
else{
   //не удалось смонтировать диск
   ...
}

FRESULT pf_open (const char* path) - открывает существующий файл. Функция должна вызываться перед тем, как выполняется любая работа с файлом - чтение, запись, изменение указателя. С открытым файлом можно работать до тех пор, пока не будет открыт другой файл. Функция pf_open() доступна всегда.

Параметры

const char *path - указатель на строку, показывающую путь к файлу. Строка должна заканчиваться нулевым символом. Путь нужно указывать полностью, разделяя подкаталоги символом слэша.

"test.txt" - путь к файлу test.txt, лежащему в корне диска
"Folder/code.txt" - путь к файлу сode.txt, лежащему в папке Folder

Возвращаемые значения

FR_OK - успешное завершение функции
FR_NO_FILE - не удалось найти файл
FR_NO_PATH - не удалось найти путь
FR_DISK_ERR - ошибка диска
FR_NOT_ENABLED - не смонтирован диск

Пример использования

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
FATFS fs; //объявление объекта FATFS
FRESULT res; //переменная для возвращаемых значений
...
//монтируем диск
res = pf_mount(&fs);
 
if (res == FR_OK){
   //открываем файл test.txt, лежащий в корне диска
   if(pf_open("test.txt") == FR_OK){
      ...
 
   }
 ....
 
}

FRESULT pf_read(void* buff, WORD btr, WORD* br) - прочитать данные из файла. Функция читает заданное количество байт из открытого файла и записывает их в буфер пользователя. Также она подсчитывает количество прочитанных байт. Если заданное количество байт и прочитанное не совпадают, значит при чтении был достигнут конец файла. Функция доступна когда параметр _USE_READ равен 1.

Параметры

void* buff - указатель на буфер, в котором будут сохраняться прочитанные данные. Если передать нулевой указатель, то прочитанные данные будут перенаправлены в другой поток, некую функцию, которую нужно описывать самостоятельно.

WORD btr - количество байт, которые нужно прочитать.

WORD* br - указатель на переменную, в которой функция pf_open сохранит количество прочитанных байтов.

Возвращаемые значения

FR_OK - успешное завершение функции
FR_DISK_ERR - ошибка диска
FR_NOT_OPENED - файл не был открыт
FR_NOT_ENABLED - не смонтирован диск

Пример использования

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
FATFS fs; //объявление объекта FATFS
FRESULT res; //переменная для возвращаемых значений
BYTE buff[32]; //буфер для чтения файла
WORD br; //счетчик прочитанных байт
 
...
//монтируем диск и открываем файл без проверки
pf_mount(&fs);
pf_open("test.txt");
 
//читаем 32 байта из файла
res = pf_read(buff, 32, &br);
if(res == FR_OK){
 
   //если прочитали меньше 32,
   // значит достигли конца файла
   if (br != 32){
      ...
   }
 
}

FRESULT pf_write(const void* buff, WORD btw, WORD* bw) - эта функция записывает данные в предварительно открытый файл. Она доступна только тогда, когда параметр _USE_WRITE равен 1. Из-за того, что библиотека Petit FatFs рассчитана на микроконтроллеры с маленьким объемом памяти, функцию записи имеет ряд ограничений:

- нельзя создать новый файл,
- нельзя увеличить размер файла,
- нельзя обновить временную метку файла,
- файловый атрибут "только для чтения" не может запретить запись,
- операцию записи можно запустить/остановить только на границе сектора диска. 

Последний пункт, пожалуй, требует пояснения. SD карта разбита на сектора по 512 байт. Функция pf_write может выполнять запись только с начала какого-либо сектора. При этом, если записывается меньше 512 байт, остаток заполняется нулями. Например, мы порциями записываем 612 байтов полезной информации - первый сектор будет заполнен полностью, а во втором будет 100 байтов полезной информации и 412 нулевых байтов.

Функция pf_lseek(..), о которой рассказано ниже, позволяет перемещать указатель чтения/записи данных. При использовании функции записи указатель нужно устанавливать на начало сектора. Если установить указатель в середину, то он будет округлен к нижней границе сектора и запись все равно будет выполняться с его начала. 

Таким образом, операция записи выполняется в следующей последовательности:

1. Устанавливаем указатель записи на какой-нибудь сектор.
2. Вызываем функцию записи.
3. Если записаны не все данные и конец файла не достигнут повторяем 2-й шаг. 
4. Финализируем запись, вызвав функцию записи с нулевым указателем.

Параметры

const void* buff - указатель на буфер содержащий данные для записи. Нулевое значение завершает текущую операцию записи. 

WORD btw - количество байт, которые нужно записать в файл. 

WORD* bw - указатель на 16-и разрядную переменную, в которой сохранится количество байт, которые удалось записать. По значению этой переменной можно определять, когда достигнут конец файла. 

Возвращаемые значения

FR_OK - успешное завершение функции
FR_DISK_ERR - ошибка диска
FR_NOT_OPENED - файл не был открыт
FR_NOT_ENABLED - не смонтирован диск

Пример использования

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
FATFS fs; //объявление объекта FATFS
FRESULT res; //переменная для возвращаемых значений
BYTE buff[32]; //буфер для чтения файла
WORD br; //счетчик прочитанных байт
 
.....
//монтируем диск и открываем файл без проверки
pf_mount(&fs);
pf_open("test.txt");
 
//устанавливаем указатель на первый сектор
pf_lseek(0);
do{
   //здесь, допустим, заполняем буфер
   ....
 
   //а здесь записываем его на карту
   pf_write(buff, 32, &br);
}while(br == 32);
 
//финализируем запись
pf_write(0, 0, &br);

FRESULT pf_lseek(DWORD offset) - смещает указатель чтения/записи открытого файла. Эта функцию используется чтобы указать с какого байта выполнять чтение, или с какого сектора диска выполнять запись. Можно выполнять абсолютное смещение указателя, передавая функции число, а можно выполнять смещение относительно текущей позиции, передавая значение указателя fs.fptr и величину смещения (смотри пример). Функция доступна, когда параметр _USE_LSEEK равен 1.

Параметры

DWORD offset - количество байт, на которые нужно сместить указатель.

Возвращаемые значения

FR_OK - успешное завершение функции
FR_DISK_ERR - ошибка диска
FR_NOT_OPENED - файл не был открыт

Пример использования

1
2
3
4
5
6
//смещаем указатель на 1000 байт
pf_lseek(1000);
 
//смещаем указатель на 2000 байт
// относительно текущей позиции
pf_lseek(fs.fptr + 2000);

FRESULT pf_opendir(DIR* dp, const char * path) - эта функцию открывает существующую директорию и инициализирует переменную типа DIR. В дальнейшем эта переменная может использоваться для получения списка файлов открытой директории. Функцию доступна, когда параметр _USE_DIR равен 1.

Параметры

DIR *dp - указатель на переменную типа DIR. Она должна быть предварительно объявлена.

const char* path - указатель на строку-путь к директории. Строка должна заканчиваться нулевым символом. Путь нужно указывать полностью, разделяя подкаталоги символом слэша. Написание пути к директории подчиняется тем же правилам, что и написание пути к файлу (смотри функцию pf_open).

Возвращаемые значения

FR_OK - успешное завершение функции
FR_NO_PATH - не удалось найти путь
FR_NOT_READY - не удалось инициализировать диск
FR_DISK_ERR - ошибка диска
FR_NOT_ENABLED - не смонтирован диск

Пример использования

1
2
3
4
5
6
7
8
9
10
11
//объявление переменных
FATFS fs;
DIR dir;
 
..
//монтируем диск без проверки
pf_mount(&fs);
 
//открываем директорию
pf_opendir(&dir, "FOLDER1");
...

FRESULT pf_readdir(DIR* dp, FILINFO* fno) - эта функцию позволяет прочитать содержимое директории. Для этого ее нужно вызывать несколько раз, пока функция не возвратит нулевую строку в одном из членов переменной fno - fno.fname[]. Функция доступна, когда параметр _USE_DIR равен 1. Читаемая директория должна быть предварительно открыта с помощью функции pf_opendir(..).

Параметры

DIR *dp - указатель на переменную типа DIR. Переменная должна быть предварительно объявлена.

FILINFO *fno - указатель на переменную типа FILINFO. Переменная должна быть предварительно объявлена.

Возвращаемые значения

FR_OK - успешное завершение функции
FR_DISK_ERR - ошибка диска
FR_NOT_OPENED - не открыта директория

Пример использования

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
FATFS fs;
FRESULT res;
FILINFO fno;
DIR dir;
 
....
//монтируем диск без проверки
pf_mount(&fs);
 
//открываем директорию
pf_opendir(&dir, "FOLDER1");
 
//чи таем содержимое директории
for(;;){
 res = pf_readdir(&dir, &fno);
 if ((res != FR_OK) || (fno.fname[0] == 0)){
 break;
 }
 USART_SendStr(fno.fname);
 USART_SendStr(" \r");
}
 
....

Ссылки 

Библиотека Petit FatFs

Продолжение следует...

http://chipenable.ru/index.php/programming-avr/item/211

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

Обратная связь

Интересуют вопросы реализации алгоритмов, программирования, выбора электроники и прочая информация, постараюсь осветить в отдельных статьях

пишите мне на netdm@mail.ru