Микроконтроллер — работаем на SD карте c FAT16 на низком уровне

 

Данный пост я написал для тех, кто изучает FAT16 с практической точки зрения. Вы не увидите здесь полного набора функционала по работе с файловой системной. Да и зачем, сейчас существует уже много написанных библиотеки, с помощью которых вы можете создавать, удалять, редактировать файлы, например — FatFS. Эти библиотеки помогут вам работать с файловой системой, но не помогут её понять.

 

microcontroller_sd_fat16

Если вы хотите разобраться как устроена FAT, то вам сюда.
Если вас интересует как подключить SD карту к микроконтроллеру то вам сюда.
Если общий принцип как работать с SD картой то вам сюда.

Написанное здесь, должно немного приблизить вас к пониманию, как работать с файловой системой на низком уровне. Можно сказать, это преодоление «психологического барьера», что файловая система это не монстр и написать самому ПО для работы с ней на SD карте  можно и не сложно.

Возможно, это вам поможет реализовать свой драйвер FAT16 или файловый менеджер, в каком ни будь из проектов…

Еще раз, перед «практической» частью, т.е. этой, рекомендую прочесть «теоретическую часть» — описание файловой системы FAT16.

 

 

Немного технической конкретики:

 

Файловая система будет располагаться на SD карте, работать с ней мы будем по интерфейсу SPI. В качестве контроллера будет использоваться Atmega328P, выводить результат работы мы будем на последовательный порт (USART).

 

Я реализую следующий простой функционал:

 

 

Вывести список файлов и директорий на последовательный порт

 

Для простоты примера, условимся что выводить список файлов и директорий мы будем корня файловой системы. Усвоим как это сделать с «корнем»,  нетрудно адаптировать данный пример для вывода содержимое любой другой директории.

Для этой задачи нам нужно знать с какого адреса начинается «корневой каталог» и его размер,

 

Напомню структуру FAT16.

 

structure_fat16_3
 

Перед «корневым каталогом» располагаются загрузочный сектор и таблицы FAT.

 

Размер загрузочного сектора определяются в загрузочном секторе с помощью параметра:

ReservedSectors (0Eh) – число секторов (сколько 512 байт) в загрузочном сектора.

 

Размер таблиц FAT и их количество определяются в загрузочном секторе следующими параметрами:

SectorPerFat (16h) — число секторов (сколько 512 байт) в FAT

NumberOfFATs (10h) — количество FAT таблиц

 

Следовательно, для того, что бы нам определить адрес «корневого каталога», мы должны прочесть «загрузочный сектор», определить значение трех вышеуказанных параметров и посчитать адрес по следующей формуле:

 

 

Эту формула выводиться просто, адрес корневого каталога, это размер загрузочного сектора  (512* ReservedSectors) плюс размер таблицы FAT (SectorPerFat*512) умноженное на их количество (…* NumberOfFATs).

 

Также нам нужно знать, где кончается «корневой каталог» что бы «не наползти на область данных», т.е. нужно узнать размер корневого каталога.

В этом нам поможет параметр загрузочной области :

RootEntries (11h) – количество 32-ух байтных элементов «корневого каталога».

 

Подробнее о параметрах и структуре FAT16 читайте здесь

 

Зная адрес «корневого каталога» и его объем мы можем его прочесть. Для вывода списка файлов и директорий на последовательный порт надо выводить первые 11 байт каждого 32-ех байтного элемента корневого каталога, 8 байт это имя и 3 байта это тип. После каждого имени с типом на последовательный порт желательно вывести знак 0xA, что означает переход на новую строку.

 

Исходный код выполнен в среде IDE Arduino, в качестве библиотек использовалась только SPI.h.

 

Где я могу проверить данный код, если у меня нет ATmega328P? 
 

 

Вывести содержимое файла на последовательный порт

 

В качестве входных данных мы будем использовать имя файла, содержимое которого мы будем выводить на последовательны порт. Для простоты примера, условимся что файл лежит в корневом каталоге (в корне).

 

Последовательность действий… после инициализации мы считываем первый 512 байт корневого каталога (все параметры укладываются в первые 512 байт, даже если загрузочный сектор больше). Помещаем считанный блок в массив-переменную. Дальше, мы узнаем из массива-переменной размер загрузочного сектора с помощью параметра:

ReservedSectros (0Eh) – число секторов (сколько 512 байт) в загрузочном секторе или сколько секторов зарезервировано под загрузочный сектор.

 

Это позволит нам рассчитать адрес размещения таблицы FAT.

Т.к. FAT всегда сразу идет за загрузочным сектором, стык в стык, то начальный адрес FAT мы можем рассчитать следующим образом:

 

 

Теперь надо прочесть таблицу FAT и поместить её в переменную – массив. Для этого, нам нужно узнать размер таблицы FAT с помощью параметра загрузочного сектора:

SectorPerFat (16h) – число секторов (сколько 512 байт) в таблице FAT

 

И через цикл наполнить массив-переменную таблицей FAT.

 

P.S. В данном моменте, есть «слабое место». Файловая система не должна иметь слишком большую таблицу FAT, иначе она может «не уместиться» в переменной, из-за ограниченных размеров ОЗУ микроконтроллеров.

 

Далее как и в предыдущем примере «список файлов и директорий», нам нужно узнать адрес и размер корневого каталога, я не буду повторять ход рассуждений, а приведу только формулу расчета:

 

 

Где

SectorPerFat (16h) — число секторов (сколько 512 байт) в FAT

NumberOfFATs (10h) — количество FAT таблиц

RootEntries (11h) – количество 32-ух байтных элементов «корневого каталога».

 

Также нам нужно знать, где кончается «корневой каталог» что бы «не наползти на область данных». В этом нам поможет параметр загрузочной области :

RootEntries (11h) – количество 32-ух байтных элементов «корневого каталога».

 

Подробнее о параметрах и структуре FAT16 читайте здесь

 

Зная адрес «корневого каталога» и его объем мы можем прочесть его и найти в нем файл по заданному имени. Далее в найденном файле (точнее, в 32-ух битном элементе описывающий данных файл), мы можем узнать его атрибуты и параметры, нас интересует – номер кластера с которого начинается содержимое файла.

 

P.S. В исходном коде ниже, поиск файла по корневому каталогу может вас спугнуть своей массивностью. Дам пару комментариев… поиск файла осуществляется с помощью двух вложенных циклов FOR. Внешний цикл FOR нужен для перемещение с сектора на сектор, внутренний для перемещения с одного 32-ух байтного элемента на другой. В каждом 32-ух байтном элементе считывается имя файла и помещается в буферный массив в формате «имя_файла.тип». Далее, данный массив сравниваются с заданным массивом – имя файла. Если сравнение — истина, то запоминаем номер кластера данного файла.

 

Теперь необходимо определиться, каким размером обладает кластер, т.е. сколько в нем секторов (или 512 байт). В этом нам поможет параметр загрузочного сектора:

SectroPerCluster (0Dh) – размер кластера (сколько секторов в нем)

 

Далее, мы рассчитываем адрес кластера по его номеру:

 

P.S.  Почему формуле расчета имеет такой вид можно в посте «описание файловой системы FAT16»

P.S. Номера кластеров нумеруются с 2-ки, поэтому мы отнимаем 2.

 

Зная размер кластера, мы можем прочесть файл и вывести его содержимое на последовательный порт. Когда мы выведем весь кластер, мы должны узнать в массиве содержащем таблицу FAT номер следующего кластера, если он есть, то продолжаем цикл: рассчитываем адрес по номеру кластера и опять выводим весь кластер по последовательному порту. Если номера нет, а есть одно из значений:

  • 0xFFF8
  • 0xFFF9
  • 0xFFFA
  • 0xFFFB
  • 0xFFFC
  • 0xFFFD
  • 0xFFFE
  • 0xFFFF

то данный кластер последний и чтения файла можно прекратить.

 

Исходный код выполнен в среде IDE Arduino, в качестве библиотек использовалась только SPI.h

 

Где я могу проверить данный код, если у меня нет ATmega328P?

 

 

 

У меня нет железа, на чем я могу попробовать данные исходные коды?

 

Проверить приведенные здесь исходные коды вы можете в программе Proteus. Для этого вам необходимо скомпилировать нужный вам исходный код (в среде разработки Arduino вам нужно нажать на кнопку «Проверить», в результате компилируется исходный код и в папке «C:\Users\Admin\Local Settings\Temp\buildXXXXXXXX.tmp» создается бинарный файл «имя_скетча.cpp.elf»).

Далее указать контроллеру бинарный файл прошивки (два раза кликните на контроллер ATmega328P и в «Program File» укажите бинарный файл прошивки). Для SD карты необходимо указать файл образ, вы можете использовать «fat16_sd.mmc» (лежит в корне проекта). Для запуска моделирования нажмите на значок «Play» в левом нижнем углу.

 

Proteus_2

 

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

 

Proteus_list_result Результат работы программы «вывод списка файлов и директорий»,  SD карта содержит директорию «TEST_DIR» и два файла «1.txt» и «2.txt». Все содержимое должно выводиться в столбец, но по неизвестным мне причинам виртуальный терминал Proteus не воспринимает знак «переход на новую строку».

 

Или так:

 

Proteus_write_result

 Результат работы программы «вывести содержимое файла на последовательный порт».
P.S. С кириллицей в виртуальном терминале Proteus проблема, так что, в нем используете латиницу.

 

Ссылка на проект
В архиве Arduino_STAND.DSN – файл проекта, fat16_sd.mmc – образ SD карты, read.elf и write.elf – тестовые бинарники по исх. кодам «вывод списка файлов и директорий» и «вывести содержимое файла на последовательный порт». Создать и редактировать образ SD карты — fat16_sd.mmc, вы можете с помощью программы «winimage» (искать самому).

 
 

Мой виртуальный терминал в Proteus ничего не отображает, что не так?

 

По непонятным мне причинам, виртуальный терминал будет работать только при определенной настройки ATmega328P, контроллер нужно настроить следующим образом.

 

virtual_terminal_proteus




Буду признателен если вы поделитесь данным постом

Комментарии
  1. 123ksn пишет:

    Большое спасибо. Очень понравился Ваш стиль подачи материала. Успехов Вам.

  2. admin пишет:

    Спасибо, всегда рад вас видеть =)

  3. Дмитрий пишет:

    Очень хорошее и понятное описание. Наверное лучшее, что мне попадались в интернете.
    Как раз искал работу с картой без библиотек для arduino.
    Спасибо.

  4. admin пишет:

    Спасибо Дмитрий!
    Значит я старался не зря, приятно слышать такие слова.

  5. indeez пишет:

    С таким ХОРОШИМ описанием Я точно разберусь с FAT16!!!
    СПАСИБО!!!

  6. admin пишет:

    Спасибо indeez)
    Я рад, что кому-то пригодились мои старания =)



Ваш комментарий


Ответ в цифрах

 
© s-engineer.ru, 2012-2017 | Все права защищены