Fat32 на stm32

Библиотека FatFS: модуль файловой системы FAT

Добавил(а) microsin   
FatFs является модулем простой файловой системы FAT (generic FAT file system), предназначенным для маленьких встраиваемых систем на микроконтроллерах (embedded systems).FatFs написан на ANSI C и полностью разделен по уровням дискового ввода/вывода диска (disk I/O). Таким образом, модуль полностью не зависит от аппаратной архитектуры подключения носителя данных (карта памяти, жесткий диск, FLASH, SDRAM и т. п.) к микроконтроллеру, и может быть легко портирован на любой микроконтроллер и систему. Модуль хорошо подходит для микроконтроллеров нижней ценовой категории типа AVR, 8051, PIC, ARM, Z80, 68k и т. д., и портируется без всяких изменений. Для того, чтобы модуль заработал, нужно только предоставить низкоуровневый интерфейс ввода вывода (см. далее, Disk I/O Interface). Модуль Petit FatFs(см. [2]) также доступен как пример реализации такого ввода/вывода для 8-битных микроконтроллеров.Возможности FatFS

  • Поддержка файловой системы, совместимой с FAT Windows.
  • Не зависит от платформы. Легко портируется на любую архитектуру.
  • Очень малые затраты на код и рабочую область.
  • Многочисленные опции конфигурации:
    • Поддержка нескольких томов (Multiple volumes) – физических дисков и их разделов.
    • Поддержка нескольких кодовых страниц ANSI/OEM, включая DBCS.
    • Поддержка длинных имен (Long file name, LFN) в кодировке ANSI/OEM или Unicode.
    • Поддержка со стороны операционных систем реального времени (RTOS, например FreeRTOS) и популярных библиотек (LUFA).
    • Поддержка нескольких размеров сектора.
    • Поддержка Read-only (только чтение), минимизация API, настройка буфера I/O и т. д. …
  • Низкоуровневый код доступа к карте поддерживает как карты стандарта SD, так и SDHC (причем одновременно). Правда, это не совсем относится к библиотеке FatFS, так как уровни доступа к носителю памяти разделены, но все равно упомянуть о поддержке SDHC необходимо, так как это очень полезная и востребованная возможность.

Интерфейс приложения (Application Interface)Модуль FatFs предоставляет набор функций для приложения (API). Другими словами, в этом списке показано, как через модуль FatFs можно получить доступ к томам FAT.

  • f_mount – регистрация/дерегистрация рабочей области
  • f_open – открытие или создание нового файла (Open/Create)
  • f_close – закрытие файла (Close)
  • f_read – чтение файла (Read)
  • f_write – запись в файл (Write)
  • f_lseek – перемещение по файлу позиции чтения/записи, увеличение размера файла (Expand file size)
  • f_truncate – обрезка файла (Truncate file size)
  • f_sync – сброс кэшированных данных на физический носитель (Flush cached data)
  • f_opendir – открыть директорию/папку (Open a directory)
  • f_readdir – прочитать информацию об объекте директории
  • f_getfree – получить количество свободных кластеров
  • f_stat – получить информацию о состоянии файла (Get file status)
  • f_mkdir – создать директорию/папку (Create a directory)
  • f_unlink – удалить файл или директорию
  • f_chmod – поменять атрибуты
  • f_utime – поменять время/дату (Change timestamp)
  • f_rename – переименовать/переместить файл или директорию (Rename/Move a file or directory)
  • f_chdir – поменять текущую директорию (Change current directory)
  • f_chdrive – поменять текущий диск (Change current drive)
  • f_getcwd – запросить текущую директорию
  • f_forward – перенаправить данные файла напрямую в поток
  • f_mkfs – создать файловую систему на диске
  • f_fdisk – поделить физический диск на разделы (логические диски)
  • f_gets – прочитать строку
  • f_putc – записать символ
  • f_puts – записать строку
  • f_printf – записать форматированную строку
  • f_tell – получить текущее значение позиции чтения/записи файла
  • f_eof – проверить на окончание данных файла (т. е. дошла ли позиция чтения до конца файла EOF, end-of-file)
  • f_size – получение размера файла
  • f_error – проверка – были ли ошибки при работе с файлом

Низкоуровневый интерфейс ввода/вывода диска (Disk I/O Interface)Поскольку модуль FatFs спроектирован так, что полностью отделен от ввода/вывода на физический носитель, то для работы он нуждается в нескольких функциях для доступа к физической памяти, где хранятся данные файловой системы FAT (physical media). Если разрешена опция, связанная с операционной системой, то дополнительно нужны функции для работы с процессами/памятью (process/memory functions). Модуль низкоуровневого ввода/вывода не является частью модуля FatFs, и он должен быть предоставлен пользователем (в этом собственно и заключается основной процесс портирования на целевую систему). Примеры таких низкоуровневых модулей/драйверов можно найти в разделе Ссылки. Список функций низкоуровневого ввода/вывода:СсылкиМодуль FatFs является свободным программным обеспечением (free software), открытым для целей образования, исследования и разработки. Вы можете его использовать, модифицировать и/или распространять для персональных проектов или коммерческих продуктов под Вашей личной ответственностью и без каких-либо ограничений. Для подробностей обращайтесь к указаниям по применению (application note).

  • FatFs User Forum “FatFs User Forum site:elm-chan.org”
  • Прочитайте в первую очередь: FatFs module application note 6 сентября 2011
  • Загрузка (Download): FatFs R0.09 | Обновления | Исправления 14 ноября 2011
  • Загрузка (Download): Примеры проектов, использующих FatFs (AVR, PIC24, LPC2300, LPC1700, V850ES, H8/300H, SH-2A, RX62N, Win32 and Generic uC) 18 декабря 2011
  • Загрузка (Download): FatFs R0.08b | R0.08a | R0.08 | R0.07e | R0.07c | R0.07a | R0.07 | R0.06 | < R0.06
  • ARM-проекты от Martin THOMAS “Interfacing ARM controllers with Memory-Cards site:siwawi.arubi.uni-kl.de” (примеры для LPC2000, AT91SAM и STM32)
  • Спецификация FAT32 от Microsoft: “Microsoft Extensible Firmware Initiative FAT32 File System Specification site:msdn.microsoft.com” (исходная документация на файловую систему FAT)
  • Как использовать карты памяти MMC/SDC
  • Benchmark 1 (тестирование скорости) (ATmega64/9.2MHz с MMC через SPI, HDD/CFC через GPIO)
  • Benchmark 2 (LPC2368/72MHz с MMC через MCI)
  • Видеоролик демо-приложения FatFS-demo-lpc23xx.mp4 (этот проект находится в файле ffsample.zip/lpc23xx)

[Ссылки]1. FatFs Generic FAT File System Module – оригинал статьи на английском языке.2. Библиотека Petit FatFS: модуль файловой системы FAT.

Источник: http://microsin.net/programming/file-systems/fatfs-file-system.html

Записки программиста

В рамках статьи Учимся работать с SDHC/SDXC-картами по протоколу SPI мы освоили использование SD-карт подобно блочным устройствам в *nix системах, то есть, читая и записывая блоки размером 512 байт.

Навык этот, бесспорно, полезен, но настоящая крутизна заключается в умении работать с файловыми системами на эти картах. Написать соответствующую библиотеку с нуля — это проект далеко не на один и не на два вечера, как в случае с какими-нибудь OLED-экранчиками.

Поэтому даже такой изобретатель колес, как я, в данном случае был вынужден использовать что-то готовое. А именно, библиотеку FatFs.<\p>

Коротко о главном

FatFs реализует поддержку файловых систем FAT и exFAT. Библиотека написана на ANSI C и имеет BSD-подобную лицензию. Характерно, что FatFs все равно, где вы его используете — на микроконтроллерах AVR, STM32, или вообще на десктопе. Также ему все равно, что вы используете для хранения информации.

Это может быть и SD-карта, и SPI flash, и файл на диске, и даже какое-то сетевое устройство. В рамках этой заметки будет продемонстрировано использование данной библиотеки на примере микроконтроллеров STM32 и SD-карт. Однако важно помнить, что область применения FatFs этим не ограничивается. Кстати, выше я неспроста отметил, что написать аналогичную библиотеку с нуля крайне непросто.

FatFs развивается с 2006 года, и в ней до сих пор временами исправляют небольшие ошибки.

Если говорить конкретно про STM32, то есть по крайней мере два способа подключения библиотеки FatFs к проекту. Это можно сделать через свойства проекта в STM32CubeMX, или же вручную. Проблема с первым способом заключается в том, что он добавляет в проект далеко не самую свежую версию FatFs.

А как было отмечено выше, в старых версиях есть неисправленные ошибки. Поэтому в рамках данной статьи речь пойдет исключительно о ручном способе. Интересующиеся тем, как это делается в STM32CubeMX, могут изучить данный вопрос самостоятельно, это не сложно.

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

Подключение FatFs

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

fatfs/ ??? 00history.txt ??? 00readme.txt ??? diskio.c ??? diskio.h ??? ff.c ??? ffconf.h ??? ff.h ??? ffsystem.c ??? ffunicode.c

??? integer.h

Makefile правим следующим образом. В C_SOURCES добавляем:

fatfs/ff.c fatfs/diskio.c

fatfs/ffunicode.c<\p>

Также дописываем в C_INCLUDES:

Настройка библиотеки осуществляется через fatfs/ffconf.h. Настроек довольно много, но они хорошо документированы. Основные настройки, которые имеет смысл изменить, следующие.

Включаем поддержку длинных имен файлов (LFN, Long File Name). Эта опция необходима для exFAT, который в свою очередь нужен для работы с картами SDXC. Другими словами, если тип карты заранее неизвестен, или планируется использовать карты размером больше 32 Гб, включаем:

Чтобы не греть мозг с кодировками и локалями, включаем поддержку UTF-8:

Поддержка exFAT:

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

#define FF_FS_NORTC    1
#define FF_NORTC_MON   1
#define FF_NORTC_MDAY  1
#define FF_NORTC_YEAR  2018

Далее нужно отредактировать fatfs/diskio.c. Здесь объявляются процедуры, определяющие, где физически библиотека будет искать данные, и прочее в таком духе.

Процедура disk_status возвращает текущий статус диска. В нашем случае она всегда будет возвращать успех:

DSTATUS disk_status (
  BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{
    return 0;
}

Процедура disk_initialize вызывается при инициализации диска. Как ни странно, в нашем случае она тоже будет просто возвращать успех. Связано это вот с чем. Как было объяснено в прошлой статье, SD-карта должна быть либо первым инициализируемым устройством на SPI-шине, либо использовать отдельную шину.

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

Поэтому, чтобы гарантировать, что SD-карта инициализируется сразу после запуска прошиви, disk_initialize ничего не делает:

DSTATUS disk_initialize (
  BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{
    // already initialized in init() procedure
    return 0;
}

… а сама инициализация выполняется вручную в процедуре init из Src/main.c:

int init() {
    // unselect all SPI devices first
    SDCARD_Unselect();
    ST7735_Unselect();

    // initialize SD-card as fast as possible, it glitches otherwise

    // (this is important if SPI bus is shared by multiple devices)
    int code = SDCARD_Init();
    if(code

Источник: https://eax.me/stm32-fatfs/

FAT32 на STM32

В данной статье хочу поделиться своим опытом ковыряния файловой системы FAT32, на которые я потратил почти месяц свободного от работы времени. 

В один прекрасный день, начитавшись в сети статей с громкими заявлениями вроде “STM32 – это просто”, я решил попробовать освоить данный камень. Небольшой опыт программирования AVR у меня был, поэтому я приобрёл десяток stm32F103c8t6 и начал с ними разбираться. Поморгал светодиодом, потом подключил дисплей HD44780, написал на него драйвер.

В общем, стало скучно, и я решил сделать что-то более сложное. Мой выбор пал на mp3 плеер, тем более, я давно хотел поработать с кодеками от VLSI. И пока я ждал заказанные vs1053, закипела работа над программой. Начал я с файловой системы. Выбор пал на FAT32, конечно, намного проще было взять готовый драйвер FatFs или, хотя бы разобраться с FAT16.

Но! первый вариант отпал, потому что: 1 мне было очень сложно разбираться с чужими функциями, 2 для моей задачи мне не было понятно, как находить файлы с произвольным названием и расширением “mp3”, 3 необходимость при чтении указывать количество байт чтения файла, а откуда мне знать сколько? И т.д.

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

Итак, приступим. Сначала нам надо разобраться со структурой файловой системы. Не будем углубляться в подробности. Первое, что надо знать – весь массив данных на носителе разбит на сектора (стандарт – 512 байт), т.е.

минимальное количество информации, которое мы можем прочитать/записать. А сектора в своё время объединены в кластеры (кластерами хранится массив данных файлов). Сама FAT32 состоит из двух основных частей – системной области и области данных.

Системная часть состоит из загрузочного сектора, зарезервированной области и таблиц FAT, далее начинается область данных. Область данных начинается с корневого каталога (т.е. то что у нас храниться в корне носителя),  а за ним идут массивы данных.

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

Теперь по порядку. Загрузочный сектор (512 байт) хранит очень много нужной для нас информации. Для примера приведу скриншот дампа SDHC карты 8 Гб (сделан бесплатной версией программы WINHEX).

Я перечислю только то, что нам пригодится (подробнее можно найти в сети, я рассчитываю, что моя статья пригодится тем, кто уже владеет азами). Не забываем, что байты расположены в непривычном для понимания обратном порядке (от младшего к старшему, т.е., например,  ячейки F2 03 00 00 означают число 0x000003F2).

  • 1 байт – количество секторов в кластере (смещение 0x0D байт)
  • 1 байт – количество таблиц FAT (обычно 2, смещение 0x0C)
  • 4 байта – размер таблицы FAT (смещение 0x24-0x27)
  • 2 байта – адрес начала первой таблицы FAT (0x0E-0x0F)
  • 4 байта – номер кластера корневого каталога (смещение 0x2C-0x2F)

Чем нам поможет данная информация? Самое главное – мы сможем найти адреса начала таблиц FAT, их объём, а так же адрес начала корневого каталога. И размер кластера – тоже очень важный параметр (на картинке в ячейке 0x0D – значение 40h – что составляет 64 в десятичной системе). Давайте разбираться по порядку.

Для начала нам необходимо знать, где начинается корневой каталог – по сути, это основная наша задача.

Из загрузочного сектора (ячейки 0x2C-0x2F) мы узнали, что корень нашего носителя хранится в кластере №2 – число 00000002h (кстати, область данных начинается со второго кластера, кластеры 0 и 1 не существуют) – это уже что-то. Значит, наш корневой каталог будет находится впритык за последней копией фат.

Из ячеек 0x0E-0x0F мы узнаём адрес начала таблиц фат, из ячеек 0x24-0x27 их размер, а из ячейки 0x0C – их количество. Дело за малым – рассчитать объём в секторах фат таблиц и добавить его к начальному адресу первой таблицы:

Объём FAT = количество FAT*объём одной таблицы 

Адрес кластера №2 = адрес начала FAT+объём FAT

Вроде бы всё просто. Рассчитываем все адреса и начинаем работу – но нет. Здесь возникает первый подводный камень. Загрузочный сектор находится в нулевом логическом секторе. А, как оказалось, логический сектор – это далеко не физический сектор. И по нулевому адресу лежит совсем не то. Так вот, в нулевом физическом секторе находится главная загрузочная запись, она же MBR.

В ней,  кроме всего, хранятся записи о разделах диска (всего четыре записи). Так как у наc SD карта – то скорее всего там будет один раздел, адрес его начала (загрузочного сектора) находится в ячейках по адресу 0x1C6-0x1C7 (я это понял опытным путём).

Теперь нам осталось к нашему адресу начала таблицы фат добавить адрес начала нашего загрузочного сектора (физический его адрес):

Физический адрес начала FAT = адрес начала FAT + физический адрес загрузочного сектора

Напишем программу инициализации файловой системы:

Объявим глобальные переменные

uint32_t root_begin; //Физический адрес начала корневого каталога
uint32_t cluster_2_add; //Физический адрес кластера №2 uint16_t fat_start_add; //номер блока, с которого начинается таблица FAT uint8_t fat_block[512]; //Массив, в который будем читать блок FAT таблицы
uint32_t root_cluster; //Первый кластер корневого каталога uint8_t sector_per_clust; //количество секторов в кластере
uint16_t root_volume; //Размер корневого каталога в блоках
uint16_t boot_sector; //Загрузочный сектор void fat_init(void){ // Сама функция инициализации FAT //Локальные переменные, которые используются только внутри функции, чтобы сэкономить ОЗУ ​ uint32_t volume_of_fat; //количество секторов в каждой FAT таблице
uint8_t number_of_fats; //количество копий FAT таблиц read_block(block_data,0x00); // Читаем MBR
boot_sector=(block_data[0x1C6]|(block_data[0x1C7]

Источник: http://cxem.net/mc/mc435.php

Эмуляция носителя FAT32 на stm32f4

Некоторое время назад возникла данная задача — эмуляция носителя FAT32 на stm32f4. Её необычность заключается в том, что среди обвязки микроконтроллера вовсе может не быть накопителя, вроде FLASH-контроллера или SD-карты. В моём случае накопитель был, но правила работы с ним не позволяли разместить файловую систему.

В ТЗ, тем не менее, присутствовало требование организовать Mass Storage интерфейс для доступа к данным. Результатом работы явился модуль, который я озаглавил «emfat», состоящий из одноимённого .h и .c файла. Модуль независим от платформы. В прилагаемом примере он работает на плате stm32f4discovery.

Функция модуля — отдавать куски файловой системы, которые запросит usb-host, подставляя пользовательские данные, если тот пытается считать некоторый файл. Итак, кому это может быть полезно и как это работает — читайте далее.

Кому может быть полезным

В первую очередь — полезно в любом техническом решении, где устройство предлагает Mass Storage интерфейс в режиме «только чтение». Эмуляция FAT32 «на лету» в этом случае позволит хранить данные как Вам угодно, без необходимости поддерживать ФС.

Во вторую очередь — полезно эстетам. Тому кто не имеет физический накопитель, но хочет видеть своё устройство в виде диска в заветном «Мой компьютер». В корне диска при этом могут находиться инструкция, драйверы, файл с описанием версии устройства, и пр.

В этом случае, нужно заметить, вместо эмуляции носителя, можно отдавать хосту части «вкомпиленного» слепка преподготовленной ФС. Однако в этом случае, вероятнее всего, расход памяти МК будет существенно выше, а гибкость решения — нулевая.

Итак, как это работает.

При попытке пользователя прочитать или записать файл, соответствующий вызов транслируется в usb-запросы, которые передаются нашему устройству. Суть запросов проста — записать или считать сектор на конечном носителе. При этом, надо отметить, винда (или другая ОС) ведёт себя как хозяйка в плане организации хранения на носителе.

Только она знает какой сектор хочет считать или записать. А захочет — и вовсе дефрагментирует нас, устроив хаотичное «жанглирование» секторами… Таким образом, функция типового USB MSC контроллера — безропотно вылить на носитель порцию в 512 байт со сдвигом, или считать порцию. Теперь вернёмся к функции эмуляции. Сразу предупрежу, мы не эмулируем запись на носитель.

Наш «носитель» только для чтения. Это связано с повышенной сложностью контроля за формированием файловой таблицы. Тем не менее, в API модуля присутствует функция-пустышка emfat_write. Возможно, в будущем будет найдено решение для корректной эмуляции записи. Задача модуля при запросе на чтение — «отдать» валидные данные. В этом и состоит его основная работа.

В зависимости от запрашиваемого сектора, этими данными могут являться:

  • Запись MBR;
  • Загрузочный сектор;
  • Один из секторов файловой таблицы FAT1 или FAT2;
  • Сектор описания директории;
  • Сектор данных, относящийся к файлу.

Надо отметить, что на ускорение принятия решения «какие данные отдать» был сделан акцент.

Поэтому накладные расходы были минимизированы.

Из-за того что мы отказались от обслуживания записи на накопитель, мы вольны организовать структуру хранения, как нам захочется:Всё совершенно стандартно, кроме нескольких деталей:

  • Данные не фрагментированы;
  • Отсутствуют некоторые ненужные области FAT;
  • Свободных кластеров нет (размер носителя «подогнан» под размер данных);
  • Размер FAT-таблиц также «подогнан» под размер данных.

API модуля

API составлен всего из трёх функций:bool emfat_init(emfat_t *emfat, const char *label, emfat_entry_t *entries); void emfat_read(emfat_t *emfat, uint8_t *data, uint32_t sector, int num_sectors); void emfat_write(emfat_t *emfat, const uint8_t *data, uint32_t sector, int num_sectors);Из них главная функция — emfat_init. Её пользователь вызывает один раз — при подключении нашего usb-устройства или на старте контроллера. Параметры функции — экземпляр файловой системы (emfat), метка раздела (label) и таблица элементов ФС (entries). Таблица задаётся как массив структур emfat_entry_t следующим образом:static emfat_entry_t entries[] = { // name dir lvl offset size max_size user read write { “”, true, 0, 0, 0, 0, 0, NULL, NULL }, // root { “autorun.inf”, false, 1, 0, AUTORUN_SIZE, AUTORUN_SIZE, 0, autorun_read_proc, NULL }, // autorun.inf { “icon.ico”, false, 1, 0, ICON_SIZE, ICON_SIZE, 0, icon_read_proc, NULL }, // icon.ico { “drivers”, true, 1, 0, 0, 0, 0, NULL, NULL }, // drivers/ { “readme.txt”, false, 2, 0, README_SIZE, README_SIZE, 0, readme_read_proc, NULL }, // drivers/readme.txt { NULL } };Следующие поля присутствуют в таблице: name: отображаемое имя элемента; dir: является ли элемент каталогом (иначе — файл); lvl: уровень вложенности элемента (нужен функции emfat_init чтобы понять, отнести элемент к текущему каталогу, или к каталогам выше); offset: добавочное смещение при вызове пользовательской callback-функции чтения файла; size: размер файла; user: данное значение передаётся «как есть» пользовательской callback-функции чтения файла; read: указатель на пользовательскую callback-функцию чтения файла. Callback функция имеет следующий прототип:void readcb(uint8_t *dest, int size, uint32_t offset, size_t userdata);В неё передаётся адрес «куда» читать файл (параметр dest), размер читаемых данных (size), смещение (offset) и userdata. Также в таблице присутствует поле max_size и write. Значение max_size всегда должно быть равным значению size, а значение write должно быть NULL. Остальные две функции — emfat_write и emfat_read. Первая, как говорилось раньше, пустышка, которую, однако, мы вызываем, если от ОС приходит запрос на запись сектора. Вторая — функция, которую мы должны вызывать при чтении сектора. Она заполняет данные по передаваемому ей адресу (data) в зависимости от запрашиваемого сектора (sector). При чтении сектора данных, относящегося к файлу, модуль emfat транслирует номер сектора в индекс читаемого файла и смещение, после чего вызывает пользовательскую callback-функцию чтения. Пользователь, соответственно, отдаёт «кусок» конкретного файла. Откуда он берётся библиотеке не интересно. Так, например, в проекте заказчика, файлы настроек я отдавал из внутренней flash памяти, другие файлы — из ОЗУ и spi-flash.

Код примера

#include “usbd_msc_core.h” #include “usbd_usr.h” #include “usbd_desc.h” #include “usb_conf.h” #include “emfat.h” #define AUTORUN_SIZE 50 #define README_SIZE 21 #define ICON_SIZE 1758 const char *autorun_file = “[autorun]
” “label=emfat test drive
” “ICON=icon.ico
“; const char *readme_file = “This is readme file
“; const char icon_file[ICON_SIZE] = { 0x00,0x00,0x01,0x00,0x01,0x00,0x18, … }; USB_OTG_CORE_HANDLE USB_OTG_dev; // Экземпляр виртуальной ФС emfat_t emfat; // callback функции чтения файлов void autorun_read_proc(uint8_t *dest, int size, uint32_t offset, size_t userdata); void icon_read_proc(uint8_t *dest, int size, uint32_t offset, size_t userdata); void readme_read_proc(uint8_t *dest, int size, uint32_t offset, size_t userdata); // Элементы ФС static emfat_entry_t entries[] = { // name dir lvl offset size max_size user read write { “”, true, 0, 0, 0, 0, 0, NULL, NULL }, // root { “autorun.inf”, false, 1, 0, AUTORUN_SIZE, AUTORUN_SIZE, 0, autorun_read_proc, NULL }, // autorun.inf { “icon.ico”, false, 1, 0, ICON_SIZE, ICON_SIZE, 0, icon_read_proc, NULL }, // icon.ico { “drivers”, true, 1, 0, 0, 0, 0, NULL, NULL }, // drivers/ { “readme.txt”, false, 2, 0, README_SIZE, README_SIZE, 0, readme_read_proc, NULL }, // drivers/readme.txt { NULL } }; // callback функция чтения файла “autorun.inf” void autorun_read_proc(uint8_t *dest, int size, uint32_t offset, size_t userdata) { int len = 0; if (offset > AUTORUN_SIZE) return; if (offset + size > AUTORUN_SIZE) len = AUTORUN_SIZE – offset; else len = size; memcpy(dest, &autorun_file[offset], len); } // callback функция чтения файла “icon.ico” void icon_read_proc(uint8_t *dest, int size, uint32_t offset, size_t userdata) { int len = 0; if (offset > ICON_SIZE) return; if (offset + size > ICON_SIZE) len = ICON_SIZE – offset; else len = size; memcpy(dest, &icon_file[offset], len); } // callback функция чтения файла “readme.txt” void readme_read_proc(uint8_t *dest, int size, uint32_t offset, size_t userdata) { int len = 0; if (offset > README_SIZE) return; if (offset + size > README_SIZE) len = README_SIZE – offset; else len = size; memcpy(dest, &readme_file[offset], len); } // Три предыдущие функции можно объединить в одну, но оставлено именно так – для наглядности // Точка входа int main(void) { emfat_init(&emfat, “emfat”, entries); #ifdef USE_USB_OTG_HS USBD_Init(&USB_OTG_dev, USB_OTG_HS_CORE_ID, &USR_desc, &USBD_MSC_cb, &USR_cb); #else USBD_Init(&USB_OTG_dev, USB_OTG_FS_CORE_ID, &USR_desc, &USBD_MSC_cb, &USR_cb); #endif while (true) { } }Также ключевая часть модуля StorageMode.c (обработка событий USB MSC):int8_t STORAGE_Read( uint8_t lun, // logical unit number uint8_t *buf, // Pointer to the buffer to save data uint32_t blk_addr, // address of 1st block to be read uint16_t blk_len) // nmber of blocks to be read { emfat_read(&emfat, buf, blk_addr, blk_len); return 0; } int8_t STORAGE_Write(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len) { emfat_write(&emfat, buf, blk_addr, blk_len); return 0; }

Заключение

Для использования в своём проекте Mass Storage не обязательно иметь накопитель с организованной на нём ФС. Можно воспользоваться эмулятором ФС. Библиотека реализовывает только базовые функции и имеет ряд ограничений:

  • Нет поддержки длинных имён (только 8.3);
  • Имя должно быть на латинице строчного регистра.

Несмотря на ограничения, лично мне в проектах имеющегося функционала хватает, но, в зависимости от востребованности, в будущем допускаю выпуск обновлённой версии.
Исходный код хранится в репозитории проекта и доступен для всех заинтересовавшихся на правах MIT-лицензии.

Источник: http://we.easyelectronics.ru/STM32/emulyaciya-nositelya-fat32-na-stm32f4.html

Первое, из-за чего возникло желание более подробно разобраться с flash-памятью, явилось то, что в своих проектах хотелось иметь энергонезависимую память, но к сожалению серия микроконтроллеров STM32f1xx не имеет встроенной eeprom. Во вторых, без знания работы flash было бы проблематично написать собственный bootloader.

Кроме, собственно, встроенной flash-памяти, я также немного расскажу и о оперативной памяти. Как обычно, основным источником для статьи является родное руководство от компании STMicroelectronics (Reference Manual), а также обобщенная информация из различных форумов и статей в интернете, ну и на основе своего опыта.

Предисловие

Изначально планировалось объединить в одной статье информацию о структуре, регистрах и методах работы с Flash. Но в итоге, статья получилась бы слишком громоздкой, поэтому было принято решение написать две статьи: одна для начинающих, используя готовые библиотеки от STM, а вторая статья будет о том, как, собственно, эти библиотеки работают с памятью.

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

Почему FLASH?

Во многих небольших проектах возникает необходимость сохранять какие-либо параметры работы устройства в независимой от электричества памяти (EEPROM). Но в STMicroelectronics решили, что использование этой памяти в большинстве микроконтроллеров не целесообразно, исключение составляет только серия LP (Low Power), в которой EEPROM присутствует.

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

В интернете полно различных примеров, есть даже изыски о том, как хранить небольшой объем данных в регистрах backup'a, но все же, если не прибегать к внешней памяти, основным способом хранения данных с защитой при отключении питания является хранение во внутренней flash-памяти.

В первую очередь flash-память предназначена для хранения инструкций для микроконтроллера, другими словами – программного кода. Но если Ваш программный код занимает не весь объем памяти, то почему бы не выделить ее часть под хранение наших настроек? 

Плюсы очевидны:

  1. Не требуется использование внешней памяти, соответственно сокращается время на разработку платы и происходит удешевление продукта;
  2. Меньше программного кода, следовательно меньшее время затрачивается на разработку.

Есть конечно и свои ограничения: 

  1. Запись во Flash требует некоторого времени, что влияет на производительность МК в момент записи или очистки памяти;
  2. Чтобы внести изменения в уже существующие данные, нужно стереть всю страницу или записывать в “чистый” блок памяти;
  3. Количество циклов перезаписи гарантировано в районе 100 тысяч операций – вроде бы много, но перезаписывая данные в страницу раз в секунду, МК выработает ресурс flash чуть более, чем за сутки. Поэтому очень не рекомендую постоянно писать во flash, рекомендуется производить операции очистки / записи лишь для сохранения данных в “энергонезависимой” памяти. В остальных случаях работаем с оперативной памятью;
  4. Минимум Вы можете использовать 1 страницу памяти (даже для одного байта), а размер одной страницы составляет от одного до двух килобайт, в зависимости от модели микроконтроллера. Такова селяви устройства flash-памяти. 

Но не стоит забывать что в первую очередь flash-память предназначена для хранения программного кода и, соответственно, сама структура интерфейса Flash в микроконтроллере устроена таким образом, чтобы оптимизировать загрузку инструкций для МК и организовать их (инструкций) кэширование, пока идет выполнение уже загруженных.

Структура

Как нам сообщает Reference Manual, модуль flash-памяти является высокопроизводительным и имеет следующие ключевые особенности:

  • Для устройств серии XL-density: объем памяти составляет до 1Mb, разбитый на два банка памяти:

– 1-й банк памяти: 512KB;
– 2-й банк памяти: до 512KB, в зависимости от модели микроконтроллера.

  • Для других устройств: объем памяти составляет до 512KB, состоящий из одного банка памяти размером до 512KB.
  • Сама по себе Flash-память состоит из трех блоков: основной блок памяти, информационный блок и блока регистров управления Flash. Структура блоков памяти и их характеристики приведены на рисунке №1.

Рис. 1. Организация структуры flash-памяти в зависимости от модели

Интерфейс flash-памяти (FLITF (Flash Memory Interface)) позволяет выполнять следующие операции:

  • Чтение из памяти с возможностью буферизации (2 слова по 64 бита);
  • Операции записи и очистки памяти;
  • Организация защиты flash от чтения и/или записи.

Основная flash-память разбита на страницы размером в 1 или 2 килобайта, это зависит от линейки микроконтроллеров STM. Использовать под свои нужды Вы можете только целое количество страниц, соответственно, если Ваша прошивка занимает все страницы памяти МК, то использование flash-памяти под свои нужды будет проблематично.

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

Операции с flash-памятью

Операций работы с flash-памятью не так уж и много: можем прочитать, записать и очистить память. Причем запись и очистка памяти, как правило, осуществляется вместе. Также еще можно заблокировать память для чтения и/или для записи. Но перед началом работы необходимо произвести инициализацию модуля Flash.

Инициализация Flash.

Операции чтения flash-памяти и доступ к данным выполняются через шину AHB. Буферизация чтения, для выполнения команд, выполняется через шину ICode. Арбитраж выполнения осуществляется самой flash-памятью, а приоритет отдается доступу к данным на шине DCode. Другими словами, пока микроконтроллер выполняет текущую команду, интерфейс Flash-памяти передает МК данные по следующей.

Чтение данных из flash-памяти может быть сконфигурировано следующим образом:

• Задержка (Latency): количество состояний ожидания для операции чтения, устанавливается «на лету», не требуется инициализация МК;

• Буферизация (2 слова по 64 бита) (Prefetch buffer): активируется во время инициализации МК.

Весь блок памяти может быть заменен за один цикл чтения из памяти, так как размер блока соответствует размеру полосы пропускания шины flash-памяти.

Благодаря буферизации, возможна более быстрая работа МК, поскольку МК извлекает из памяти по одному “слову” за раз одновременно со следующим “словом”, которое помещается в предварительный буфер.

• Половинный цикл (Half cycle): этот режим требуется для оптимизации питания, чаще всего используется при работе от батарейки с программами, которые не требуют особого быстродействия.

Эти параметры следует использовать в соответствии с временем доступа к flash-памяти. Значение периода ожидания представляет собой отношение периода SYSCLK (системных часов) к времени доступа к flash-памяти и устанавливается в зависимости от следующих параметров:

  • Уровень “0”, если 0 16); WRP3_Data = (uint16_t)((FLASH_Pages & WRP3_Mask) >> 24); /* Wait for last operation to be completed */ status = FLASH_WaitForLastOperation(ProgramTimeout); if(status == FLASH_COMPLETE) { // Скопируем текущие настройки защиты, потому что мы потом их сотрем oldData0 = OB->WRP0 & 0xFF; oldData1 = OB->WRP1 & 0xFF; oldData2 = OB->WRP2 & 0xFF; oldData3 = OB->WRP3 & 0xFF; // Очистим Option Byte (Это необходимо сделать до установки записи, иначе ничего не сможем изменить) FLASH_EraseOptionBytes(); /* Wait for last operation to be completed */ status = FLASH_WaitForLastOperation(ProgramTimeout); /* Authorizes the small information block programming */ FLASH->OPTKEYR = FLASH_KEY1; FLASH->OPTKEYR = FLASH_KEY2; FLASH->CR |= CR_OPTPG_Set; // Рассчитаем новые значения if (NewState==ENABLE) { newData0 = oldData0 & WRP0_Data; newData1 = oldData1 & WRP1_Data; newData2 = oldData2 & WRP2_Data; newData3 = oldData3 & WRP3_Data; } else { newData0 = ~(~oldData0 & WRP0_Data); newData1 = ~(~oldData1 & WRP1_Data); newData2 = ~(~oldData2 & WRP2_Data); newData3 = ~(~oldData3 & WRP3_Data); } if(status == FLASH_COMPLETE) { OB->WRP0 = newData0; /* Wait for last operation to be completed */ status = FLASH_WaitForLastOperation(ProgramTimeout); } if(status == FLASH_COMPLETE) { OB->WRP1 = newData1; /* Wait for last operation to be completed */ status = FLASH_WaitForLastOperation(ProgramTimeout); } if(status == FLASH_COMPLETE) { OB->WRP2 = newData2; /* Wait for last operation to be completed */ status = FLASH_WaitForLastOperation(ProgramTimeout); } if(status == FLASH_COMPLETE) { OB->WRP3 = newData3; /* Wait for last operation to be completed */ status = FLASH_WaitForLastOperation(ProgramTimeout); } if(status != FLASH_TIMEOUT) { /* if the program operation is completed, disable the OPTPG Bit */ FLASH->CR &= CR_OPTPG_Reset; } } /* Return the write protection operation Status */ return status; }И необходимо добавить определение функции в модуле “stm32f10x_flash.h” также после функции “FLASH_EnableWriteProtection()”, иначе Вы не сможете вызвать ее из свей программы:Листинг №10-b. Заголовок функции установки/снятия защиты от записи FLASH_Status FLASH_SetWriteProtection(uint32_t FLASH_Pages, FunctionalState NewState);Сама функция является переработанной функцией от STM “FLASH_EnableWriteProtection()”: 
    • добавлен параметр “NewState”, который отвечает за установку или снятия флага защиты;
    • в теле функции добавлена проверка на значение этого параметра и, в зависимости от него, устанавливаются или сбрасываются соответствующие биты.
    • сброс OperationByte производится не перед вызов функции, а внутри нее. Это связано с особенностью записи во Flash, поэтому сначала запоминаются предыдущие значения OperationByte, производится очистка OperationByte, а затем уже записываются новые значения с учетом предыдущих.

    Таким образом можно заменить по своему коду вызовы “FLASH_EnableWriteProtection(ProtectedPages)” на “FLASH_SetWriteProtection(ProtectedPages, ENABLE)” для включения защиты, или “FLASH_SetWriteProtection(ProtectedPages, DISABLE)” для ее отключения. Не забудьте в этом случае убрать очистку OperationByte с помощью функции “FLASH_EraseOptionBytes()”, так как она сбросит все раннее установленные флаги защиты от записи.

    Использование утилиты STM32 ST-LINK Utility

    Вы можете защитить свою прошивку от чтения и записи с помощью стандартной утилиты от STM. Скачать ее можно на официальном сайте, пройдя несложную регистрацию.

    Из всех возможностей утилиты, мы обратимся только к работе с областью данных микроконтроллера, известной как “Option Byte”.

    Попасть в этот функционал программы можно либо комбинацией кнопок “Ctrl+B”, либо через меню “Target” / “Option Byte”. Перед Вами откроется похожее окно:

    Рис. 4. Настройка Option Byte (STM32 ST-LINK Utility).

    Нас сейчас интересуют всего два раздела: чтение и запись.  Здесь Вы можете включить или выключить защиту от чтения и галочками установить страницы, которые хотите защитить от записи. Действия аналогичны описанным выше, поэтому описывать подробно не буду.

    Не знаю почему, но если Вы установили защиту от записи, то после снятие этой защиты через утилиту, автоматически выставляется флаг защиты от чтения. а снятие флага защиты от чтения полностью стирает Flash.

    Заключение

    В этой статье я постарался кратко рассказать как можно использовать Flash  в своих целях, как сделать свой автозагрузчик (Bootloader) и как защитить своий код. Все примеры в статье протестированы и готовы к использованию. На этот раз вложение добавлять не буду – весь материал содержит небольшой объем кода и легко воспринимается в тексте.

    Если найдете ошибки или будут вопросы – welcome в комментарии ))).

    Источник: https://www.smartmode.info/stm32/13-stm32-flash

    STM32 для начинающих. Урок 6. DMA STM32

    DMA(Direct Memory Access) — Контроллер прямого доступа к памяти. Данный контроллер позволяет оперировать данными без участия процессора. То есть, используя DMA, мы можем разгрузить наш процессор от операций с памятью, заняв его чем-то более полезным.

    Например, предположим, что мы считываем напряжение на какой либо из ножек контроллера при помощи АЦП. Разумеется, сохранять полученное значение АЦП можно и при помощи процессора, то есть, вручную сохраняя значения регистров в памяти устройства.

    Однако, при этом мы должны каждый раз по окончанию преобразования АЦП, отвлекать процессор от текущих действий прерыванием, сохранять регистры в память, и вновь возвращаться к незавершенной задаче.

    Этого можно избежать при применении контроллера прямого доступа к памяти (DMA).

    Микроконтроллеры семейства STM32F3 имеют в своем составе до 2 контроллеров DMA, которые имеют 12 линий передачи данных. То есть существует возможность организовать 12 независимых пересылок данных. Однако, следует учитывать что каждый канал соединен с соответствующей периферией согласно данной таблице:

    И для DMA2:

    Давайте попробуем реализовать аппаратный memcpy на DMA.То есть просто перешлем один массив в другой, настроив контроллер прямого доступа к памяти.

    Рассмотрим следующий код:

    #include “stm32f30x_gpio.h”#include “stm32f30x_rcc.h”#include “stm32f30x_syscfg.h”#include “stm32f30x_exti.h”RCC_HSEConfig(RCC_HSE_ON); //Enable HSEwhile(RCC_GetFlagStatus(RCC_FLAG_HSERDY) == RESET) ; //Waiting for HSEFLASH->ACR |= FLASH_ACR_PRFTBE;FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);FLASH->ACR |= (uint32_t)((uint8_t)0x02);RCC_PREDIV1Config(RCC_PREDIV1_Div1);//PREDIV 1 Divider = 1RCC_PLLConfig(RCC_PLLSource_PREDIV1,RCC_PLLMul_9);//Set PREDIV1 as source for PLL,And set PLLMUL=9RCC_PLLCmd(ENABLE);//Enable PLLwhile(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET) ;//Waiting for PLLRCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);//Set PLL as SYSCLK SoucreRCC_HSICmd(DISABLE);//Disable HSIRCC_USBCLKConfig(RCC_USBCLKSource_PLLCLK_1Div5);RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);dma1.DMA_PeripheralBaseAddr =(uint32_t) &source;dma1.DMA_MemoryBaseAddr =(uint32_t) &dest;dma1.DMA_MemoryInc = DMA_MemoryInc_Enable ;dma1.DMA_PeripheralInc = DMA_PeripheralInc_Enable ;dma1.DMA_BufferSize = 255;dma1.DMA_M2M = DMA_M2M_Enable;dma1.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;dma1.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;dma1.DMA_Mode = DMA_Mode_Normal ;dma1.DMA_Priority = DMA_Priority_High ;dma1.DMA_DIR = DMA_DIR_PeripheralSRC ;DMA_Init(DMA1_Channel1,&dma1);DMA_Cmd(DMA1_Channel1,ENABLE);DMA_ITConfig(DMA1_Channel1,DMA_IT_TC,ENABLE);NVIC_EnableIRQ(DMA1_Channel1_IRQn);void DMA1_Channel1_IRQHandler(void)DMA_ClearITPendingBit(DMA1_IT_TC1);

    Разберем данный код подробнее. В функции main, сначала вызывается функция InitRCC, о которой вы можете почитать здесь.Затем мы заполняем исходный массив в цикле.

    После заполнения массива, вызывается функция Init_DMA, в которой мы непосредственно настраиваем контроллер прямого доступа к памяти.

    Разумеется, сначала мы включаем тактирование контроллера DMA1 командой RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE). Затем необходимо настроить структуру DMA_InitTypeDef,рассмотрим её поля:

    • DMA_PeripheralBaseAddr — Адрес периферии (Так же можно указать адрес памяти),из которой или в которую будет происходить чтение/запись.
    • DMA_MemoryBaseAddr — Адрес памяти ,из которой или в которую будет происходить чтение/запись.
    • DMA_MemoryInc — Увеличивать ли адрес в памяти при работе. То есть, при каждой операции записи/чтения производить следующую операцию записи/чтения в последующий участок памяти. Удобно при записи данных в массив.
    • DMA_PeripheralInc — Увеличивать ли адрес в периферии при работе.
    • DMA_BufferSize — Размер буфера. То есть, с массивом какой длины мы будем производить операции записи/чтения.
    • DMA_M2M— Ведется ли передача из памяти в память.
    • DMA_MemoryDataSize — Размер данных в памяти.
    • DMA_PeripheralDataSize — Размер данных периферии
    • DMA_Mode — Режим работы канала. Здесь можем выбрать, включать ли циклическую запись данных. То есть, будет ли запись начата вновь в начальный элемент массива, при его заполнении.
    • DMA_Priority — Приоритет текущего канала.
    • DMA_DIR — Направление передачи. То есть периферия-память или наоборот.

    Читайте также  STM32.Встроенный датчик температуры.

    Заполнив данную структуру, инициализируем контроллер DMA командой DMA_Init.После этого включаем контроллер используя команду DMA_Cmd, и настраиваем прерывание окончания передачи данных DMA.

    В обработчике прерывания DMA1_Channel1_IRQHandler,вызывается команда __NOP, которая поможет нам в дальнейшей отладке. После этого очищается бит окончания передачи данных командой DMA_ClearITPendingBit(DMA1_IT_TC1).

    После сборки прошивки, запускаем отладку, и устанавливаем точку останова перед командой DMA_Init. В окне watch, убеждаемся в том, что исходный массив заполнен.

    После этого устанавливаем точку останова в прерывании, на той самой команде __NOP. Снова заглянув в окно Watch, видим, что массив source перемещен в массив dest, чего мы собственно и добивались!

    Если у Вас ещё остались какие либо вопросы по работе DMA микроконтроллера STM32, вы можете оставить их ниже, в комментариях. Лишь благодаря Вашей поддержке, наши уроки станут лучше!

    На сегодня всё,спасибо за внимание!

    Другие уроки цикла.

    Любое копирование, воспроизведение, цитирование материала, или его частей разрешено только с письменного согласия администрации MKPROG.RU. Незаконное копирование, цитирование, воспроизведение преследуется по закону!

    Источник: http://mkprog.ru/mikrokontrollery-stm32/stm32-dlya-nachinayushhih-urok-6-dma-stm32.html

    Использование SD-карт в малых встраиваемых системах. Часть 2

    Для того чтобы записанные на SD-карту данные можно было прочесть на компьютере, они должны быть упорядочены в соответствии со структурой файловой системы FAT (File Allocation Table или таблица размещения файлов).

    Для облегчения труда разработчиков малых встраиваемых систем при реализации таблицы FAT программистом под псевдонимом ChaN была написана библиотека FatFs [1], которая быстро приобрела популярность благодаря тому, что ее функциональность позволяет выполнять действия, характерные для серьезных операционных систем при работе с файлами и папками. На данный момент доступна версия R0.10, которая вышла в середине октября 2013 года.

    Эта библиотека написана на ANSI C и состоит из нескольких файлов. Она представляет собой связующее программное обеспечение (middleware) между приложением и аппаратным интерфейсным модулем (Рисунок 3).

    Это значит, что библиотека не зависит от аппаратной части и может быть использована с любым микроконтроллером.

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

    Рисунок 3. Место библиотеки FatFs в программной структуре малой встраиваемой системы.

    В библиотеку входят следующие файлы:

    • ff.c и ff.h – являются основой FatFs, поскольку определяют функции, необходимые для работы с файловой системой.
    • diskio.c и diskio.h – отвечают за низкоуровневый интерфейс, эти файлы должны быть изменены разработчиком в соответствии с используемым способом связи с носителем.
    • integer.h – в этом файле можно задать используемые в библиотеке ключевые слова, определяющие тип данных, например, typedef int INT. Следует учитывать возможности этого файла при переходе с 8 разрядной системы на 32 разрядную и наоборот.
    • ffconf.h – здесь определяются настройки библиотеки, например, #define _FS_READONLY 0 разрешает чтение и запись на носитель, при выборе 1 запись была бы запрещена.

    На Рисунке 4 показана взаимосвязь файлов библиотеки FatFs с приложением и отвечающими за работу модуля SDIO файлами, описанными в предыдущей части.

    Рисунок 4. Взаимосвязь файлов.

    Для того чтобы модуль SDIO понимал команды FatFs, нужно определить содержимое функций файла diskio.c. Первым делом организуется функция disk_initialize(drv). Она инициализирует физический диск и подготавливает его к чтению и записи данных. Аргумент drv определяет номер диска.

    В этой функции обязательно должна вызываться функция SD_Init(), которая непосредственно принимает на себя обязанности по инициализации SD-карты.

    Также следует учитывать, что функция disk_initialize не должна вызываться из приложения, поскольку она является служебной функцией в составе файла ff.c. Разработчик FatFs предупреждает, что в противном случае файловая структура носителя может быть испорчена.

    Вместо этого в приложении для монтирования диска нужно вызывать функцию f_mount, содержащую в себе disk_initialize. Функция f_mount будет рассмотрена ниже.

    Следующей необходимой для правильной работы библиотеки функцией является disk_status(drv). Она возвращает текущее состояние диска. В ней обязателен вызов функции SD_GetCardInfo, передающей информацию о SD-карте.

    При получении от SD_GetCardInfo флага SD_OK функция disk_status должна возвращать 0, в противном случае она должна вернуть флаг STA_NOINIT, это будет являться свидетельством того, что диск не был проинициализирован.

    Для чтения секторов имеется функция disk_read, которая в качестве аргументов принимает четыре параметра: drv (номер диска), *buff (указатель на буфер чтения данных), sector (номер начального сектора), count (количество читаемых секторов).

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

    Чтобы прочитать данные, нужно в функции disk_write, принимающей в качестве аргументов те же четыре параметра, что и disk_read, организовать подобный цикл, но уже с функцией SD_WriteBlock, позволяющей считывать информацию с SD-карты.

    Обе функции (disk_read и disk_write) обязательно должны предусматривать возвращение флага RES_OK в случае успешной операции, либо флага RES_ERROR при возникновении аппаратной ошибки чтения/записи. Также библиотека FatFs предусматривает возврат от этих функций флага RES_PARERR в случае приема неверного параметра и флага RES_NOTRDY, если диск не был инициализирован.

    Также для организации дополнительной функциональности, не включающей в себя операции чтения и записи, существует функция disk_ioctl. В качестве аргументов она принимает drv (номер привода), ctrl (команду управления) и *buff (указатель на буфер данных).

    Функция возвращает те же флаги, что и disk_read или disk_write.

    Аргумент ctrl может принимать следующие значения: CTRL_SYNC (позволяет завершить операции, ожидающие окончания процесса записи), GET_SECTOR_SIZE (возвращает размер сектора привода в переменную, на которую указывает buff), GET_SECTOR_COUNT (возвращает количество доступных секторов в переменную, на которую указывает buff), GET_BLOCK_SIZE (возвращает размер блока для очистки в переменную, на которую указывает buff), CTRL_ERASE_SECTOR (очищает область памяти, определяемую массивом, первым элементом которого является начальный адрес этой области памяти, а последним элементом – конечный; на сам массив должен указывать buff).

    Наконец, в файле diskio.c нужно организовать работу еще двух зависящих от времени функций – disk_timerproc и get_fattime. Первая обеспечивает таймауты для надежного функционирования библиотеки. Ее нужно вызывать каждые 10 мс. Вторая возвращает текущее время выполнения определенной операции.

    Для ее правильной работы нужно включить и настроить часы реального времени. Если это не нужно, то можно организовать возврат какого-либо определенного значения или нуля. Подробный пример правильной инициализации всех вышеприведенных функций можно найти в [2].

    Данная программа предназначена для микроконтроллеров STM32F407xxx/42xxxx/43xxxx и основана на работе модуля SDIO и драйвера stm324xg_eval_sdio_sd.

    Теперь перейдем непосредственно к рассмотрению работы библиотеки FatFs в рамках приложения. Как уже было сказано выше, для монтирования и регистрации диска используется функция f_mount с тремя аргументами fatfs, path и opt.

    Аргумент fatfs является указателем на объект файловой системы, который должен быть зарегистрирован, path указывает на строку, которая в случае единственного привода должна быть пустой, opt определяет опцию инициализации и может принимать два значения: 0 (не монтировать привод, он будет смонтирован позже) и 1 (смонтировать привод сейчас, чтобы проверить его доступность). Функции библиотеки FatFs могут возвращать флаги, перечисленные в Таблице 2. f_mount возвращает FR_OK, FR_INVALID_DRIVE, FR_DISK_ERR, FR_NOT_READY и FR_NO_FILESYSTEM.

    Таблица 2. Флаги, возвращаемые функциями библиотеки FatFs

    FR_OK

    Функция завершена успешно

    FR_DISK_ERR

    Ошибка, возникшая на нижнем уровне
    (в функциях disk_read, disk_write или disk_ioctl)

    FR_INT_ERR

    Ошибка во внутреннем процессе, возникающая, например,
    при поврежденной структуре FAT

    FR_NOT_READY

    Привод не может функционировать из-за ошибки предыдущего
    размонтирования или из-за ошибки в функции disk_initialize

    FR_NO_FILE

    Файл не найден

    FR_NO_PATH

    Путь не найден

    FR_INVALID_NAME

    Заданная строка определяет неверный путь

    FR_DENIED

    Запрашиваемый доступ запрещен

    FR_EXIST

    Файл или каталог, имеющий то же имя, уже существует

    FR_INVALID_OBJECT

    Объект (файл или каталог) является недействительным, или
    был задан нулевой указатель

    FR_WRITE_PROTECTED

    Неудачная попытка записи при активном режиме «Только чтение»

    FR_INVALID_DRIVE

    Неверный номер диска в указанном пути

    FR_NOT_ENABLED

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

    FR_NO_FILESYSTEM

    На приводе нет рабочей области FAT

    FR_MKFS_ABORTED

    Работа функции f_mkfs была прервана

    FR_TIMEOUT

    Выполнение функции отменено по истечении времени,
    определяемого блоком управления потоками

    FR_LOCKED

    Доступ к файлу отклонен из-за одновременной работы
    с другим файлом

    FR_NOT_ENOUGH_CORE

    Недостаточно памяти для выполнения операции

    FR_TOO_MANY_OPEN_FILES

    Количество открытых файлов достигло максимального значения,
    следующий файл не будет открыт

    FR_INVALID_PARAMETER

    Заданный параметр неверен

    Для того чтобы создавать файлы и работать с ними используется функция f_open. Она создает файловый объект, необходимый для доступа к конкретному файлу. Функция принимает три аргумента: *fp (указатель на структуру данных объекта файла), *path (указатель на строку с именем файла) и mode (определяет тип доступа и метод открытия файла).

    Аргумент mode может быть представлен одним или несколькими флагами: FA_READ (позволяет считывать из файла), FA_WRITE (позволяет записывать в файл), FA_OPEN_EXISTING (открывает уже имеющийся на диске файл), FA_OPEN_ALWAYS (либо открывает существующий файл, либо создает новый), FA_CREATE_NEW (создает новый файл) и FA_CREATE_ALWAYS (создает новый файл, если файл с таким именем существует, то он будет перезаписан). Функция f_open может возвращать все перечисленные в Таблице 2 флаги, кроме FR_INVALID_OBJECT, FR_MKFS_ABORTED и FR_INVALID_PARAMETER. После работы с файлом требуется его закрыть, для этого существует функция f_close. Она принимает только один аргумент *fp и может вернуть FR_OK, FR_DISK_ERR, FR_INT_ERR, FR_NOT_READY, FR_INVALID_OBJECT и FR_TIMEOUT.

    Для чтения и записи данных служат функции f_read и f_write, соответственно.

    Они обе принимают четыре аргумента: *fp, *buff (указатель на буфер, куда будут записаны прочитанные данные, или где содержатся записываемые данные), btr или btw (количество байт для чтения или для записи, соответственно), *br или *bw (указатели на переменную для возврата количества прочитанных или записанных байтов).

    Эти функции могут возвращать FR_OK, FR_DISK_ERR, FR_INT_ERR, FR_NOT_READY, FR_INVALID_OBJECT и FR_TIMEOUT. Следует отметить, что функция f_write недоступна при включенном режиме «только чтение», то есть при _FS_READONLY = 1.

    Для полноценной работы с файловой системой нужно уметь выполнять операции не только над файлами, но и над каталогами. Создать новый каталог можно с помощью функции f_mkdir, которая принимает строку (аргумент *path), указывающую имя каталога и полный путь.

    Если задать f_mkdir(“sub1”), то каталог sub1 будет создан в корневом каталоге, если записать f_mkdir(“sub1/sub2”), то подкаталог sub2 будет создан в sub1. Здесь также следует учитывать, что строка не должна заканчиваться символом «/».

    Для того, чтобы получить доступ к каталогу, то есть открыть его, используют функцию f_opendir. Ее аргументами являются указатель на незаполненную структуру объекта директории *dp и указатель *path на определяющую путь строку.

    Чтобы поменять текущую директорию диска применяют f_chdir с единственным аргументом *path, указывающим на директорию, которая станет текущей.

    Для удаления файла или каталога используется функция f_unlink. Она принимает только *path – указатель на строку, которая задает имя удаляемого объекта. При этом удаляемый объект не должен иметь атрибута «Только чтение», удаляемый файл не должен быть открыт, удаляемая директория не должна быть текущей и обязательно должна быть пустой.

    При работе с файлами и папками иногда возникает необходимость в их переименовании. Для этого имеется функция f_rename. Ее аргументами являются указатели *old_name и *new_name.

    Первый указывает на строку, которая содержит текущее имя объекта, а второй на строку с новым именем объекта. Помимо обычного переименования функция позволяет переносить объект в другую директорию. Например, запись f_rename(“oldname.txt”, “dir1/newname.

    txt”) переименует файл oldname.txt в newname.txt и переместит его в каталог dir1.

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

    Первое можно осуществить с помощью функции f_fdisk, принимающей аргументы pdrv (определяет номер физического диска, который будет разбит на разделы), part[] (таблица карты разделов, имеющая 4 элемента) и work (определяет размер рабочей области для функции; минимальный размер указывается в переменной _MAX_SS).

    Разбивка на разделы выполняется в соответствии с форматом FDISK, допускающим создание максимум 4 разделов. Соотношение объемов разделов определяется вторым аргументом функции, который можно задать, например, как DWORD plist[] = {50, 50, 0, 0}, что будет означать разбиение диска на два логических раздела с одинаковым объемом.

    Для дальнейшего форматирования диска в соответствии с FAT используют функцию f_mkfs, принимающую аргументы *path (указатель на логический номер диска, который должен быть отформатирован), sfd (правило разбиения на разделы) и au (задает размер единицы данных в байтах). Аргумент sfd может принимать два значения: 0 или 1.

    В первом случае предусматривается правило разбиения FDISK, во втором – SFD. При работе с твердотельными накопителями, в том числе и с SD-картами, следует выбирать по умолчанию 0. Правило SFD подходит для работы с гибкими дисками, поскольку не предусматривает разделы диска, при этом FAT начинается с первого сектора физического диска.

    С помощью аргумента au можно принудительно задать размер кластера, это число должно быть 2n. Именно оно определяет тип используемой файловой системы: FAT12, FAT16 или FAT32.

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

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

    Список источников

    1.  elm-chan.org/fsw/ff/ff10.zip
    2.  skydrive.live.com

    Источник: https://www.rlocman.ru/review/article.html?di=150671

Ссылка на основную публикацию
Adblock
detector
",css:{backgroundColor:"#000",opacity:.6}},container:{block:void 0,tpl:"
"},wrap:void 0,body:void 0,errors:{tpl:"
",autoclose_delay:2e3,ajax_unsuccessful_load:"Error"},openEffect:{type:"fade",speed:400},closeEffect:{type:"fade",speed:400},beforeOpen:n.noop,afterOpen:n.noop,beforeClose:n.noop,afterClose:n.noop,afterLoading:n.noop,afterLoadingOnShow:n.noop,errorLoading:n.noop},o=0,p=n([]),h={isEventOut:function(a,b){var c=!0;return n(a).each(function(){n(b.target).get(0)==n(this).get(0)&&(c=!1),0==n(b.target).closest("HTML",n(this).get(0)).length&&(c=!1)}),c}},q={getParentEl:function(a){var b=n(a);return b.data("arcticmodal")?b:(b=n(a).closest(".arcticmodal-container").data("arcticmodalParentEl"),!!b&&b)},transition:function(a,b,c,d){switch(d=null==d?n.noop:d,c.type){case"fade":"show"==b?a.fadeIn(c.speed,d):a.fadeOut(c.speed,d);break;case"none":"show"==b?a.show():a.hide(),d();}},prepare_body:function(a,b){n(".arcticmodal-close",a.body).unbind("click.arcticmodal").bind("click.arcticmodal",function(){return b.arcticmodal("close"),!1})},init_el:function(d,a){var b=d.data("arcticmodal");if(!b){if(b=a,o++,b.modalID=o,b.overlay.block=n(b.overlay.tpl),b.overlay.block.css(b.overlay.css),b.container.block=n(b.container.tpl),b.body=n(".arcticmodal-container_i2",b.container.block),a.clone?b.body.html(d.clone(!0)):(d.before("
"),b.body.html(d)),q.prepare_body(b,d),b.closeOnOverlayClick&&b.overlay.block.add(b.container.block).click(function(a){h.isEventOut(n(">*",b.body),a)&&d.arcticmodal("close")}),b.container.block.data("arcticmodalParentEl",d),d.data("arcticmodal",b),p=n.merge(p,d),n.proxy(e.show,d)(),"html"==b.type)return d;if(null!=b.ajax.beforeSend){var c=b.ajax.beforeSend;delete b.ajax.beforeSend}if(null!=b.ajax.success){var f=b.ajax.success;delete b.ajax.success}if(null!=b.ajax.error){var g=b.ajax.error;delete b.ajax.error}var j=n.extend(!0,{url:b.url,beforeSend:function(){null==c?b.body.html("
"):c(b,d)},success:function(c){d.trigger("afterLoading"),b.afterLoading(b,d,c),null==f?b.body.html(c):f(b,d,c),q.prepare_body(b,d),d.trigger("afterLoadingOnShow"),b.afterLoadingOnShow(b,d,c)},error:function(){d.trigger("errorLoading"),b.errorLoading(b,d),null==g?(b.body.html(b.errors.tpl),n(".arcticmodal-error",b.body).html(b.errors.ajax_unsuccessful_load),n(".arcticmodal-close",b.body).click(function(){return d.arcticmodal("close"),!1}),b.errors.autoclose_delay&&setTimeout(function(){d.arcticmodal("close")},b.errors.autoclose_delay)):g(b,d)}},b.ajax);b.ajax_request=n.ajax(j),d.data("arcticmodal",b)}},init:function(b){if(b=n.extend(!0,{},a,b),!n.isFunction(this))return this.each(function(){q.init_el(n(this),n.extend(!0,{},b))});if(null==b)return void n.error("jquery.arcticmodal: Uncorrect parameters");if(""==b.type)return void n.error("jquery.arcticmodal: Don't set parameter \"type\"");switch(b.type){case"html":if(""==b.content)return void n.error("jquery.arcticmodal: Don't set parameter \"content\"");var e=b.content;return b.content="",q.init_el(n(e),b);case"ajax":return""==b.url?void n.error("jquery.arcticmodal: Don't set parameter \"url\""):q.init_el(n("
"),b);}}},e={show:function(){var a=q.getParentEl(this);if(!1===a)return void n.error("jquery.arcticmodal: Uncorrect call");var b=a.data("arcticmodal");if(b.overlay.block.hide(),b.container.block.hide(),n("BODY").append(b.overlay.block),n("BODY").append(b.container.block),b.beforeOpen(b,a),a.trigger("beforeOpen"),"hidden"!=b.wrap.css("overflow")){b.wrap.data("arcticmodalOverflow",b.wrap.css("overflow"));var c=b.wrap.outerWidth(!0);b.wrap.css("overflow","hidden");var d=b.wrap.outerWidth(!0);d!=c&&b.wrap.css("marginRight",d-c+"px")}return p.not(a).each(function(){var a=n(this).data("arcticmodal");a.overlay.block.hide()}),q.transition(b.overlay.block,"show",1*")),b.overlay.block.remove(),b.container.block.remove(),a.data("arcticmodal",null),n(".arcticmodal-container").length||(b.wrap.data("arcticmodalOverflow")&&b.wrap.css("overflow",b.wrap.data("arcticmodalOverflow")),b.wrap.css("marginRight",0))}),"ajax"==b.type&&b.ajax_request.abort(),p=p.not(a))})},setDefault:function(b){n.extend(!0,a,b)}};n(function(){a.wrap=n(document.all&&!document.querySelector?"html":"body")}),n(document).bind("keyup.arcticmodal",function(d){var a=p.last();if(a.length){var b=a.data("arcticmodal");b.closeOnEsc&&27===d.keyCode&&a.arcticmodal("close")}}),n.arcticmodal=n.fn.arcticmodal=function(a){return e[a]?e[a].apply(this,Array.prototype.slice.call(arguments,1)):"object"!=typeof a&&a?void n.error("jquery.arcticmodal: Method "+a+" does not exist"):q.init.apply(this,arguments)}}(jQuery)}var debugMode="undefined"!=typeof debugFlatPM&&debugFlatPM,duplicateMode="undefined"!=typeof duplicateFlatPM&&duplicateFlatPM,countMode="undefined"!=typeof countFlatPM&&countFlatPM;document["wri"+"te"]=function(a){let b=document.createElement("div");jQuery(document.currentScript).after(b),flatPM_setHTML(b,a),jQuery(b).contents().unwrap()};function flatPM_sticky(c,d,e=0){function f(){if(null==a){let b=getComputedStyle(g,""),c="";for(let a=0;a=b.top-h?b.top-h{const d=c.split("=");return d[0]===a?decodeURIComponent(d[1]):b},""),c=""==b?void 0:b;return c}function flatPM_testCookie(){let a="test_56445";try{return localStorage.setItem(a,a),localStorage.removeItem(a),!0}catch(a){return!1}}function flatPM_grep(a,b,c){return jQuery.grep(a,(a,d)=>c?d==b:0==(d+1)%b)}function flatPM_random(a,b){return Math.floor(Math.random()*(b-a+1))+a}