STM32. CubeMX+FatFs+SDIO+DMA 1

STM32. CubeMX+FatFs+SDIO+DMA

Часть 1.

Александр Левчук dslev@yandex.ru

Обзор FatFs (UM1721 User manual)

Архитектура FatFs

Особенности FatFs

FatFs API низкого уровня

FatFs в STM32CubeF4

Интерфейс вашего собственного диска для FatFs

FatFs приложения


Главная

STM32. Работаем в среде VisualStudio+VisualGDB+ CubeMX

Виртуальный USB СОМ порт. Часть 1

Виртуальный USB СОМ порт. Часть 2

Часы RTC, настройка по серверам точного времени

FatFs часть 2

Предисловие

В нескольких статья описывается применение FatFs совместно с CubeMX для режима работы SDIO+DMA. В качестве носителя используется карта памяти микро SD HC 16 гигабайтов. Рассмотрены проблемы использования режима длинных имен для русского языка.

В первой части статьи представлен краткий обзор FatFs по документации сайта STM.

 

 

 

 

 

 

 

Обзор FatFs (UM1721 User manual)

В этом разделе приводятся некоторые сведения о FatFS из документа UM1721 User manual Developing applications on STM32Cube™ with FatFs (сайт STM). Если вы не знакомы с FatFS, тогда следует предварительно ознакомиться с полным описанием FatFS на русском языке, которое есть на сайте http://microsin.net/programming/file-systems/fatfs-appnotes.html.


FatFs - это общий модуль файловой системы FAT для небольших встроенных систем. FatFs написан в соответствии с ANSI C и полностью отделен от слоя ввода-вывода диска.
Поэтому он не зависит от аппаратной архитектуры и имеет следующие особенности:

• Windows совместимая файловая система FAT.
• Очень маленький занимаемый объем кода и рабочей области.
• Различные варианты конфигурации:

- Несколько томов (физические диски и разделы).
- Несколько кодовых страниц ANSI / OEM, включая DBCS.
- Поддержка длинных имен файлов в ANSI / OEM или Unicode.
- Поддержка RTOS.
- Поддержка нескольких размеров сектора.
- Только для чтения, минимизированный API, буфер ввода / вывода.
- Подтипы FAT: FAT12, FAT16 и FAT32.
- Количество открытых файлов: не ограничено, зависит от доступной памяти.
- Количество томов: до 10.
- Размер файла: зависит от спецификации FAT. (до 4G-1 байт)
- Размер тома: зависит от спецификации FAT. (до 2T байт на 512 байт / сектор)
- Размер кластера: зависит от спецификации FAT. (до 64 Кбайт на 512 байт / сектор)
- Размер сектора: зависит от спецификации FAT (до 4 Кбайт).

 Архитектура FatFs

Модуль FatFs - промежуточное ПО, которое предоставляет множество функций для доступа к томам FAT, таких как f_open (), f_close (), f_read (), f_write () и т. д. (см. ff.c).
В этом модуле нет зависимости от платформы, если компилятор совместим с ANSI C.

Модуль чтения/записи диска низкого уровня используется для чтения/записи физического диска, а модуль RTC используется для получения текущего времени.
Дисковый ввод-вывод низкого уровня и модуль RTC полностью отделены от модуля FatFs.

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

Примечание

Портирование выполняет CubeMX, но не полностью.

 Особенности FatFs

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

Модуль FatFs не поддерживает повторный доступ к файлу по умолчанию. Доступ разрешен только в режиме чтения. Повторное открытие для записи в файл всегда запрещено, и открытый файл нельзя переименовывать или удалять, поскольку файловая структура может быть разрушена.
Общий доступ к файлам может быть разрешен, если _FS_LOCK установлен в 1 или более. Значение указывает количество одновременно управляемых файлов. В этом случае, если предпринимается попытка открытия, переименования или удаления, нарушающее правило использования файлов, описанное выше, то функция завершится с ошибкой FR_LOCKED. Если количество открытых файлов становится больше, чем _FS_LOCK, функция f_open() завершится с ошибкой FR_TOO_MANY_OPEN_FILES.

 Независимость файловых операций (Reentrancy )

Файловые операции на разных томах всегда независимы и могут работать одновременно. Файловые операции для одного и того же тома не являются независимыми, но их также можно настроить для поточно-ориентированного режима с параметром _FS_REENTRANT. В этом случае в проект должны быть добавлены, зависящие от ОС, функции управления объектами синхронизации, ff_cre_syncobj(), ff_del_syncobj(), ff_req_grant() и ff_rel_grant().
Когда том используется какой-либо другой задачей и вызывается файловая функция, то она приостанавливается до тех пор, пока эта задача не завершит свой вызов файловой функции. Если время ожидания превысило период, определенный _TIMEOUT, функция будет завершена с возвратом FR_TIMEOUT. Функция тайм-аута может не поддерживаться на некоторых ОСРВ.

 Длинное имя файла

Модуль FatFs начал поддерживать длинное имя файла (LFN) с версии 0.07. Два разных имени файла, SFN и LFN, прозрачны в файловых функциях, за исключением функции f_readdir(). Чтобы включить функцию LFN, установите _USE_LFN в 1, 2 или 3 и добавьте в проект функции преобразования в Юникод ff_convert() и ff_wtoupper(). Функция LFN требует дополнительного рабочего буфера. Размер буфера можно настроить с помощью _MAX_LFN, в соответствии с доступным объемом памяти. Размер длинного имени файла может достигать 255 символов, так что _MAX_LFN должен быть равен 255 для полноценной работы LFN. Если размер рабочего буфера недостаточен для данного имени файла, то файловая функция возвратится с FR_INVALID_NAME. При разрешении длинных имен LFN одновременно с функцией общего доступа (Reentrancy) значение _USE_LFN должно быть установлено 2 или 3. В этом случае файловая функция выделяет рабочий буфер на стеке или куче. Рабочий буфер занимает (_MAX_LFN + 1) * 2 байта.
При разрешении длинных имен LFN размер модуля будет увеличен в зависимости от выбранной кодовой страницы.

Примечание
В общем случае использование длиных имен приводит к увеличению размера кода приблизительно до 3 кбайтов (для русского языка). Особенности использования длинных имен и русского языка в проектах CubeMX и исправление некоторых ошибок CubeMX для этого случая описаны в разделе….

 FatFs API низкого уровня

Поскольку модуль FatFs полностью отделен от дискового ввода-вывода и модуля RTC, ему требуются некоторые функции низкого уровня для управления физическим диском: чтение/запись и получение текущего времени. Поскольку функции низкоуровневого дискового ввода-вывода и модуль RTC не являются частью модуля FatFs, они должны быть предоставлены пользователем.
Решение FatFs Middleware предоставляет низкоуровневые драйверы дискового ввода-вывода для некоторых поддерживаемых дисков (RAMDisk, microSD, USBDisk).
Был добавлен дополнительный интерфейсный уровень diskio.c для динамического добавления/удаления (связывания) физических носителей с модулем FatFs, обеспечивая функции низкоуровневого дискового ввода-вывода, как указано ниже:

• disk_initialize (): инициализирует физический диск
• disk_status (): возвращает состояние выбранного физического диска.
• disk_read (): читает сектор (ы) с диска
• disk_write (): записывает сектор (ы) на диск
• disk_ioctl (): управляет определенными функциями устройства
• get_fattime (): возвращает текущее время

Прикладная программа НЕ ДОЛЖНА вызывать эти функции, они вызываются только функциями файловой системы FatFs, такими как f_mount (), f_read () или f_write ().

 FatFs в STM32CubeF4

В решении STM32CubeF4 (также и для STM32CubeF1) был добавлен дополнительный слой интерфейса для добавления/удаления динамически физических носителей в/из модуля FatFs. Для соединения модуля FatFs с драйвером диска I/O низкого уровня пользователь может использовать FATFS_LinkDriver() и FATFS_UnLinkDriver() для добавления или динамического удаления драйвера диска.

Приложению, возможно, потребуется знать количество текущих подключенных драйверов I/O диска, это делается через FATFS_GetAttachedDriversNbr() API.

Общий драйвер низкого уровня ff_gen_drv.c/h находится в корневом каталоге модулей FatFs. Две структуры в файле ff_gen_drv.h определяют тип драйвера дискового ввода-вывода и используются для динамического управления подключенными дисками, как упомянуто ниже.

 

 

 

Diskio_drv_TypeDef structure

Diskio_drv_TypeDef structure

Чтобы связать модуль FatFs с драйвером дискового ввода-вывода низкого уровня, пользователь может использовать следующие API:

•  FATFS_LinkDriver(): для динамического добавления драйвера дискового ввода-вывода,
•  FATFS_UnLinkDriver(): для динамического удаления драйвера дискового ввода-вывода,
• FATFS_GetAttachedDriversNbr(): определить количество текущих подключенных драйверов дискового ввода-вывода.

 FATFS_LinkDriver()

Эта функция связывает совместимый драйвер дискового ввода-вывода и увеличивает количество активных связанных драйверов. В случае успеха возвращает 0, в противном случае возвращает 1.

Примечание.

Из-за ограничений FatFs максимальное количество подключаемых дисков (_VOLUMES) составляет не более 10.

Реализация:

FATFS_LinkDriver:uint8_t FATFS_LinkDriver(Diskio_drvTypeDef *drv, char *path)

{

  uint8_t ret = 1;

  uint8_t DiskNum = 0;

  if(disk.nbr <= _VOLUMES)

  {

    disk.drv[disk.nbr] = drv; 

    DiskNum = disk.nbr++;

    path[0] = DiskNum + '0';

    path[1] = ':';

    path[2] = '/';

    path[3] = 0;

    ret = 0;

  }

  return ret;

}

 

 FATFS_UnlinkDriver ()

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

Реализация FATFS_UnLinkDriver:

uint8_t FATFS_UnLinkDriver(char *path)

{

  uint8_t DiskNum = 0;

  uint8_t ret = 1;

 

  if(disk.nbr >= 1)

  {   

    DiskNum = path[0] - '0';

    if(DiskNum <= disk.nbr)

    { disk.drv[disk.nbr--] = 0;

      ret = 0;

    }

  }

 

  return ret;

}

Примечание

Обратите внимание на реализацию функций в документе UM1721. В этих функциях параметр path имеет тип *char, что вызывает ошибку при компиляции с длинными именами. Параметр должен иметь тип *TCHAR, подробности далее.

 Интерфейс вашего собственного диска для FatFs

Если имеется работающий модуль управления хранилищем, он должен быть присоединен к FatFs с помощью функции склеивания, а не его модификации. Пользователь может взаимодействовать с любым новым диском, разработав соответствующий драйвер низкого уровня дискового ввода-вывода (mynewdisk_diskio.c/.h), и сохранить эти файлы драйверов в папке: \Middlewares\ Third_Party\FatFs\ src\ drivers.
Стоит отметить, что предоставляемые драйверы низкого уровня дискового ввода-вывода FatFs зависят от драйверов BSP платы. Чтобы удалить эту зависимость BSP, пользователь может просто заменить «BSP _...» вызовы API через собственный код, обеспечивающий соответствующую функциональность. Для разработки низкоуровневого драйвера дискового ввода-вывода пользователь может начать с скелета склеивающих функций ниже, чтобы подключить существующий модуль управления хранилищем к FatF с помощью определенного API.

Скелет низкоуровневого модуля ввода-вывода для FatFs:

/*-------------------------------------------------------------------------*/

/* mynewdisk_diskio.c: Low level disk I/O module skeleton for FatFs        */

/*-------------------------------------------------------------------------*/

 

/* Includes ---------------------------------------------------------------*/

#include <string.h>

#include "ff_gen_drv.h"

/* Private define ---------------------------------------------------------*/

#define BLOCK_SIZE 512 /* Block Size in Bytes */

 

/* Private variables ------------------------------------------------------*/

static volatile DSTATUS Stat = STA_NOINIT; /* Disk status */

 

/* Private function prototypes --------------------------------------------*/

DSTATUS mynewdisk_initialize (void);

DSTATUS mynewdisk_status (void);

DRESULT mynewdisk_read (BYTE*, DWORD, BYTE);

 

#if _USE_WRITE == 1

  DRESULT mynewdisk_write (const BYTE*, DWORD, BYTE);

#endif /* _USE_WRITE == 1 */

 

#if _USE_IOCTL == 1

  DRESULT mynewdisk_ioctl (BYTE, void*);

#endif  /* _USE_IOCTL == 1 */

 

Diskio_drvTypeDef  mynewdisk_Driver =

{

  mynewdisk_initialize,

  mynewdisk_status,

  mynewdisk_read,

#if  _USE_WRITE == 1

  mynewdisk_write,

#endif /* _USE_WRITE == 1 */

 

/*------------------------ Initialize a Drive ---------------------------*/

DSTATUS mynewdisk_initialize (void)

{

  Stat = STA_NOINIT;

 

  // write your own code here to initialize the drive

 

  Stat &= ~STA_NOINIT;

  return Stat;

}

 

/*------------------------- Get Disk Status -----------------------------*/

DSTATUS mynewdisk_status (void)

{

  Stat = STA_NOINIT;

 

  // write your own code here

 

  return Stat;

}

 

/*-------------------------- Read Sector(s) -----------------------------*/

DRESULT mynewdisk_read (BYTE *buff, /* Data buffer to store read data */

               DWORD sector, /* Sector address (LBA) */

               BYTE count) /* Number of sectors to read (1..128) */

{

  DRESULT res = RES_ERROR;

 

  // write your own code here to read sectors from the drive

 

  return res;

}

 

/*--------------------------- Write Sector(s) ---------------------------*/

#if _USE_WRITE == 1

DRESULT mynewdisk_write (const BYTE *buff, /* Data to be written */ DWORD sector, /* Sector address (LBA) */

                BYTE count) /* Number of sectors to write (1..128) */

 

{

  DRESULT res = RES_ERROR;

 

  // write your own code here to write sectors to the drive

 

  return res;

}

#endif /* _USE_WRITE == 1 */

 

/*------------------------ Miscellaneous Functions ----------------------*/

#if _USE_IOCTL == 1

DRESULT mynewdisk_ioctl (BYTE cmd, /* Control code */

void *buff) /* Buffer to send/receive control data */

{

  DRESULT res = RES_ERROR;

 

  // write your own code here to control the drive specified features

  // CTRL_SYNC, GET_SECTOR_SIZE, GET_SECTOR_COUNT, GET_BLOCK_SIZE

 

  return res;

}

#endif /* _USE_IOCTL == 1 */

 

Header Low level disk I/O module:

/*-------------------------------------------------------------------------*/

/* mynewdisk_diskio.h: Header for Low level disk I/O module                */

/*-------------------------------------------------------------------------*/

 

/* Define to prevent recursive inclusion ----------------------------------*/

#ifndef __MYNEWDISK_DISKIO_H

#define __MYNEWDISK_DISKIO_H

 

extern Diskio_drvTypeDef  mynewdisk_Driver;

 

#endif /* __MYNEWDISK_DISKIO_H */

 

 

FatFs приложения

В решении STM32CubeF4 многие приложения базируются на основе промежуточного программного обеспечения (middleware) FatFs . Таблица ниже дает представление о том, как компонент промежуточного программного обеспечения FatFs используется в различных примерах, которые классифицируются по сложности и в зависимости от используемого физического дисковода (uSD, RAMDisk, USBDisk):

Приложения FatFs, перечисленные выше в решении STM32CubeF4, представляют собой набор прошивок, доступных в двух режимах:

• Автономный режим
• Режим RTOS с использованием программного обеспечения FreeRTOS.

Стоит отметить, что пользователь должен обеспечить соответствующие значения стека и кучи, при использовании или разработке FatFs приложений на основе ST драйверов низкого уровня. При использовании приложения USB Disk на основе класса USB Host Mass Storage (MSC) значение стека _MAX_SS в файле ff_conf.h должно быть выравнено на максимальный размер сектора диска.

Значение размера кучи также должно быть скорректировано при разработке любого приложения FatFs в режиме RTOS (FreeRTOS), на основе общего API CMSIS-OS.

 HAL drivers configuration

Приложения FatFs, предоставляемые в рамках решения STM32CubeF4, представляет собой набор прошивок, используемых для интерфейса различных физических дисков (uSD, RAM Disk, USB Disk). Пользователю нужны драйверы HAL, которые необходимы для запуска приложения FatFs. Соответствующие драйверы HAL подключаются в файле конфигурации HAL stm32f4xx_hal_conf.h, правильные модули должны быть раскомментированы.
Правильный драйвер диска указывается соответствующим определением:

• FatFs_uSD: #define HAL_SD_MODULE_ENABLED
• FatFs_RAMDisk: #define HAL_SDRAM_MODULE_ENABLED или #define HAL_SRAM_MODULE_ENABLED
• FatFs_USBDisk: #define HAL_HCD_MODULE_ENABLED

 Конфигурация файловой системы FATFs

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

Совместное использование функций (Reentrancy)

Совместное использование функций является ключевым различием между конфигурациями режимов Standalone и RTOS, которые могут быть установлены на файл конфигурации FatFs ffconf.h:

Отключение совместного доступа (Reentrancy) в автономном режиме:
• #define _FS_REENTRANT 0

Включение в режиме RTOS:
• #define _FS_REENTRANT 1

После включения, пользователь должен предоставить, зависимый от ОС, объект синхронизации (#define _SYNC_t osSemaphoreId).
Проекты приложений режима RTOS должны включать файл syscall.c, предоставляющий функции, зависящие от ОС, который находится в директории \Middlewares\Third_Party\FatFs\src\option.

 Длинное имя файла

Модуль FatFs поддерживает длинное имя файла (LFN) и имя файла формата 8.3 (SFN).
Обратите внимание, что свойство LFN в файловой системе FAT является патентом корпорации Microsoft. Это не относится к FAT32, но большинство драйверов FAT32 включают свойство LFN. FatFs могут переключать свойство LFN с помощью опции конфигурации. При включении свойства LFN в коммерческих продуктах может потребоваться лицензия от Microsoft, в зависимости от завершающего распространения продукта. LFN можно использовать, когда включена функция LFN в файле конфигурации FatFs ffconf.h: (_USE_LFN> 0):

• Функция LFN отключена, когда определено #define _USE_LFN 0

• Функция LFN включена, когда 3 ≥ _USE_LFN> 0

После включения файла конфигурации ffconf.h, проект приложения должен подключать
файл syscall.c/unicode.c, обеспечивающие функции управления памятью, которфй находится в директории \Middlewares\Third_Party\FatFs\src\option.

 Пример приложения FatFs

Если пользователь уже подключил свой собственный диск, разработал низкоуровневый драйвер дискового ввода-вывода (mynewdisk_diskio.c/.h), то связать этот драйвер с модулем FatFs можно следующим образом:

/*-------------------------------------------------------------------------*/

/* main.c: Main program body                                               */

/*-------------------------------------------------------------------------*/

 

/* Includes ---------------------------------------------------------------*/

#include "main.h"

 

/* Private variables ------------------------------------------------------*/

FATFS mynewdiskFatFs;  /* File system object for User logical drive */

FIL MyFile;            /* File object */

char mynewdiskPath[4]; /* User logical drive path */

 

int main(void)

{

  uint32_t wbytes; /* File write counts */

  uint8_t wtext[] = "text to write logical disk"; /* File write buffer */

 

if(FATFS_LinkDriver(&mynewdisk_Driver, mynewdiskPath) == 0)

  {

    if(f_mount(&mynewdiskFatFs, (TCHAR const*)mynewdiskPath, 0) == FR_OK)

    {

      if(f_open(&MyFile, "STM32.TXT", FA_CREATE_ALWAYS | FA_WRITE) == FR_OK)

      {

        if(f_write(&MyFile, wtext, sizeof(wtext), (void *)&wbytes) == FR_OK);

        {

          f_close(&MyFile);

        }

      }

    }

  }

  FATFS_UnLinkDriver(mynewdiskPath);

}

 

Пользователь должен включить файл заголовка ff_gen_drv.h, а также заголовочный файл mynewdisk_diskio.h.

/*-------------------------------------------------------------------------*/

/* main.h: Header for main.c module                                        */

/*-------------------------------------------------------------------------*/

/* Includes ---------------------------------------------------------------*/

#include "ff_gen_drv.h"

#include "mynewdisk_diskio.h"

 

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