Full WHOIS ELM-CHAN.ORG — Полная WHOIS информация домен / сайт ELM-CHAN.ORG | Портал WHOIS.UANIC.NAME

Full WHOIS ELM-CHAN.ORG - Полная WHOIS информация домен / сайт ELM-CHAN.ORG | Портал WHOIS.UANIC.NAME ОБД2

Avr: работаем с sdmmc-картами памяти

Разбираемся с библиотекой FatFs, пишем программу в AVR Studio, симулируем все в Proteus

Немного о FatFs

FatFs — это модуль файловой системы для микропроцессоров. Он написан полностью на Си и поддерживается не только AVR, но и многими другими микропроцессорами: PIC, ARM, 8051 и др. Вообще говоря, из той горы вариантов что я нашел, альтернатив этой библиотеке нет. По сравнению с другими, у FatFs сплошные плюсы — и их много! :). Даже перечислять их лень — взгляните на официальную страничку модуля — http://elm-chan.org/fsw/ff/00index_e.html (Features).

Разбираемся, что входит в состав FatFs

На момент написания статьи последняя версия, доступная для загрузки — восьмая R0.08а (August 29, 2021). Дополнительно, нужно загрузить архив с примерами FatFs sample projects — он нам пригодится.

Распаковываем архив модуля, открываем папку scr:

Модуль поддерживает длинные имена файлов (Long file name support in ANSI/OEM or Unicode). По дефолту, эта поддержка отключена, так как она требует дополнительное количество кода (flash-памяти) и не малое. Без данной поддержки длинные имена будут преобразованы в т.н. «8.3 filename»: «TextFile.Mine.txt» ->  «TE021F~1.TXT». Чтобы работать с длинными именами, библиотеке нужны специальные таблицы кодировок — они находятся в папке scr/option. Посмотрите на объемы файлов с китайскими-корейскими-японскими 🙂 кодировками — это уж какой контроллер надо с объемом флеш > 500 КБ!

К счастью, мы воспринимаем русскийанглийский текст — это файл optionccsbcs.c объемом 30 КБ. Однако, реально, в размере он меньше, т.к. бОльшая часть файла откинется компилятором (в зависимости, какую кодировку мы выбрали):

#if _CODE_PAGE == 437
#define _TBLDEF 1
static const WCHAR Tbl[] = {...................};

#elif _CODE_PAGE == 720
#define _TBLDEF 1
static const WCHAR Tbl[] = {...................};

#elif _CODE_PAGE == 737
и т.д.

Первые шаги в создании проекта для работы с SD-картой на основе модуля FatFs

Итак, первое и самое главное, нам нужен файл diskio.c заточеный специально для работы с SD-картой. Таковой ищем в архиве с примерами, скачанный с официального сайта модуля. В папке avr находится большое количество файлов, но все что нужно — это файл mmc.c — тот самый, который умеет общаться с MMCSD-картой. А, да, еще прихватите rtc.c и rtc.h — они нам позже пригодятся.

Давайте переименуем mmc.c на diskio.c, а старый diskio.c удалим.

Итого, в папке с проектом должны находится файлы:

Дополнительно подготовьте ваши любимые библиотеки для работы с UART — чтобы выводить отладочные сообщения по каналу TX (позже мы будем симулировать наш проект в Proteus).

а) Дело касается функции void disk_timerproc (void). По задумке, нам самим нужно реализовывать таймер и вызывать эту функцию каждые 10 ms. Она выполняет роль привычного нам _delay_ms. Только _delay_ms — синхронная, а эта функция получается асинхронной — в этом есть плюс.

Если вы дошли до SD-карт, то я думаю вы легко справитесь с реализацией таймера. Если вас смущает disk_timerproc, и вам больше по душе привычный _delay_ms, то я вас направлю на http://frank.circleofcurrent.com/cache/fat_sd.htm — там модифицируют diskio.c так, чтобы можно было обойтись без таймера.

UPD1: в версии PetitFatFs функции в diskio.c реализованы без таймера и disk_timerproc. Так что можете поглядеть там.

б) Последние штрихи в этом файле нужно внести в функциях void power_on (void) и void power_off (void), а также в #define CS_LOW() и #define CS_HIGH(). В первых двух функциях изменяем инициализацию порта DDRB так, как нам нужно: MISO — должно быть входом, остальные MOSI, SCK и SS должны быть выходами. Кстати, в качестве SS может сгодиться любая другая ножка контроллера (разумеется, относящаяся к порту вводавывода). Саму эту SS мы и указываем в директивах #define CS_LOW() и #define CS_HIGH().

Создаем проект (я работаю в AVR STUDIO), забыв о SD-карте: первым делом настраиваем UART, реализовываем таймер с частотой 10 ms. В качестве прошиваемого контроллера в проекте укажите ATMEGA32. Проверьте все в симуляторе Proteus — нужно точно убедиться что все работает, чтобы потом не искать ошибку там, где ее нет.

После, подключаем следующие файлы:

Остальные файлы пока подключать не будем:

rtc.c и rtc.h, ccsbcs.c — они будут задействованы позже.

Не забудьте void disk_timerproc (void) поместить в прерывание вашего таймера.

Общие настройки для работы с SD-картой на основе модуля FatFs

Все нужные настройки находятся в файле ffconf.h — открываем его. Вот некоторые из опций:

Специально для упрощения работы с текстовыми файлами в файловой системе FATxx в FatFs написаны функции:

int f_putc (int, FIL*); /* Put a character to the file */
int f_puts (const char*, FIL*); /* Put a string to the file */
int f_printf (FIL*, const char*, ...); /* Put a formatted string to the file */
char* f_gets (char*, int, FIL*); /* Get a string from the file */

Их можно активизировать, выставив _USE_STRFUNC в 1 или 2. Чем различается 1 и 2 — написано у них на сайте. Честно, я непомню. Все равно нам пока это не важно:

Все, с первоначальной настройкой ffconf.h закончили.

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

Симуляция SDMMC-карты памяти в Proteus.

Сложность тут не в подключении карты, а в образе диска, который нужно указать в настройках SDMMC-карты. Странно, но в Proteus-е я так и не нашел какого-либо упоминания, чем нужно воспользоваться, чтобы создать образ диска .mmc. Пришлось перерыть немножко гугл с яндексом.

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

Когда скачаете, давайте попробуем создать виртуальный образ диска. Открываем меню File->New…. В окне Format выбираем Select custom image format. В следующем окне я указал файловую систему FAT12/16 и количество секторов 8192 — это соответствует 4 МБ (4096 КБ — отображено строкой ниже). Указать можете что-то свое, но помните, что в Proteus можно выбрать только целые значения 1,2,4,8…..128 МБ. Нажимаем ОК, сохраняем образ в файле sd.ima.

Код ошибки:  Ошибки Киа Сид, диагностика Kia Ceed, расшифровка кодов

Перед нами пустое окно — диск пуст. Перетащите любые файлыпапки из проводника Windows в окно WinImage и сохраните образ. Внизу, в статусной строке указан объем «диска» — 4096 КБ и сколько КБ свободно. Посмотрите на файл sd.ima, он занимает сейчас менее 4 МБ — т.е. WinImage создал динамический образ, виртуально он занимает 4 МБ, реально — 4 МБ минус свободное пространство.

Чтобы этот файл sd.ima можно было скормить Proteus-у, переименуйте его в sd.mmc. Теперь переходим в сам симулятор, добавляем в проект mega32, MMC-карту (она же SD в нашем случае) и терминал из вкладки виртуальные инструменты. В свойствах MMC-карты указываем объем 4 МБ и файл sd.mmc. Подключаем терминал к mega32, в его свойствах укажите baud rate, какой вы будете использовать в программе для UART. Подключаем SD-карту к контроллеру: DI->MOSI, DO->MISO, CLK->SCK, CS->SS(или другая, которая указана в директивах CS_LOW() и CS_HIGH()).

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

Итак, начинаем с самого простого:

***********I. ТОЛЬКО ЧТЕНИЕ КАРТЫ; БЕЗ ПОДДЕРЖКИ ДЛИННЫХ ИМЕН ФАЙЛОВ*********

Настройки

  • _FS_READONLY 1. Для начала выставляем его в 1, тем самым отгораживая себя пока что от лишних функций и файлов. Модуль выбрасывает функции f_write, f_sync, f_unlink, f_mkdir, f_chmod, f_rename, f_truncate, f_getfree — все, что связано с записью. (Кстати модуль FatFs с _FS_READONLY 1, хорошо укорачивается в размере и в итоге, помещается на ATmega16).

_CODE_PAGE нам не важна(оставьте какая есть), т.к. _USE_LFN(поддержка длинных имен файлов) для начала мы отключим (отключено по умолчанию).

Разбираемся с основными READ_ONLY-функциями модуля FatFs

Да, еще раз напомню, что на официальном сайте хорошо документированы все функции модуля, для некоторых приведены примеры на Си. Все доступные функции можно посмотреть на сайте или открываем файл ff.h. Все функции модуля (за исключением дополнительных, включаемых флагом _USE_STRFUNC: f_puts, f_printf и др.) возвращают FRESULT. В случае успешного выполнения функции возвращают (FRESULT)FR_OK = 0, в ином случае FRESULT = 0 — полный их перечень приведен над списком функций в файле ff.h.

Самое первое и главное, нужно карту инициализировать. Для этого вызывается функция

DSTATUS disk_initialize (
	BYTE drv		/* Physical drive nmuber (0) */
)

, определенная в diskio.c.

Возвращает …

/* Disk Status Bits (DSTATUS) */

#define STA_NOINIT  0x01  /* Drive not initialized */
#define STA_NODISK  0x02  /* No medium in the drive */
#define STA_PROTECT  0x04  /* Write protected */

…в случае определенных проблем с инициализацией карточки или 0x00 в случае успеха.

Далее «монтируется» логический раздел функцией

FRESULT f_mount (BYTE, FATFS*);  /* Mount/Unmount a logical drive */

Т.е. мы указываем модулю FatFS, с каким разделом носителя нужно работать (на SD-картах и USB-флешках обычно один раздел, а вот харды зачастую разбиты на несколько логических разделов).

Функция возвращает FR_OK=0 в случае успеха или FR_INVALID_DRIVE (The drive number is invalid) в противном случае.

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

Для этого нам потребуются 3 функции — f_open, f_read и f_close. Если у вас есть опыт работы с WinAPI, например, на C , или даже Pascal — то вам эти функции покажутся очень знакомыми.

FRESULT f_open (
  FIL* FileObject,       /* Pointer to the blank file object structure */
  const TCHAR* FileName, /* Pointer to the file neme */
  BYTE ModeFlags         /* Mode flags */
);

С помощью f_open мы открываем существующий файл или создаем новый — в зависимости от указанных флагов BYTE ModeFlags. (В случае READ_ONLY, флаги FA_WRITE   FA_OPEN_ALWAYS   FA_CREATE_NEW   FA_CREATE_ALWAYS по понятным причинам использовать нельзя.)

Первым параметром указывается ссылка на новую структуру file object (struct _FIL — файл ff.h).

Вторым — имя файла, третьим выставляем FA_READ.

После того, как файл успешно открыт (FR_OK), с ним можно работать.

FRESULT f_read (
  FIL* FileObject,    /* Pointer to the file object structure */
  void* Buffer,       /* Pointer to the buffer to store read data */
  UINT ByteToRead,    /* Number of bytes to read */
  UINT* ByteRead      /* Pointer to the variable to return number of bytes read */
);

Первым параметром указываем ссылку на заполненную предыдущей функцией структуру FileObject, вторым — ссылку на переменную(или массив переменных), куда будем записывать то, что читаем из файла. ByteToRead — сколько байт читаем из файла, и, ByteRead — указатель на переменную, в которую запишется, сколько было байт считано в итоге после завершения функции. Это нужно для контроля, если вдруг функция неожиданно 🙂 уткнется в конец файла, т.е. если мы находимся в 5-ти байтах от конца файла, а пытаемся читать 20. Тогда будет считано только 5, а 15 остануться нетронутыми. Я это покажу в примере ниже..

Если вы истинный джентельмен, то после использования файла его нужно закрыть:

FRESULT f_close (
  FIL* FileObject     /* Pointer to the file object structure */
);

Демонстрация чтения с карты

Итак, давайте создадим файл foo.txt в любой папке windows, запишем в него «Привет из файла!», сохраним. Теперь открываем наш образ sd-диска в WinImage (не поленитесь, и укажите, чтобы файлы .mmc открывались в WinImage по-умолчанию) и кидаем в него foo.txt, сохраняем образ диска, обязательно закрываем WinImage, чтобы доступ к образу был свободен для симулятора.

Код ошибки:  Не понятная кнопка и не понятный второй OBD разъем - Кайен клуб | Клуб владельцев Porsche Cayenne

Пишем программу:

~~~~~~~~~~~~~~~~~~~~~~
unsigned char buf[150]; //буфер для UART
static FRESULT f_err_code; //FRESULT для функций модуля
static FATFS FATFS_Obj; //структура - логический раздел
FIL fil_obj; //структура файла, с которым работаем
char var[30]=""; //буфер, сюда мы поместим то, что считаем из файла.
UINT ByteRead = 0;

int main(void)
{
	uart0_init(); //настраиваем UART. С UART работайте сами.
	sendStr("*************Чтение из файла*************"); //отправляю через UART строку на терминал. Эту функцию реализуем также сами, т.к. статья не про UART сегодня 🙂

	BYTE status = disk_initialize(0); //инициализируем карточку
	sprintf((char*)buf,"initialize return: %X",status); //возвратит 0 в случае успеха
	sendStr(buf);

	f_err_code = f_mount(0, &FATFS_Obj); //монтируем логический раздел
	sprintf((char*)buf,"f_mount return: %X",f_err_code); //возвратит FR_OK=0 в случае успеха
	sendStr(buf);

	f_err_code = f_open(&fil_obj, "foo.txt", FA_READ); //пытаемся открыть файл
	sprintf((char*)buf,"f_open return: %X",f_err_code); //возвратит FR_OK=0 в случае успеха, FR_NO_FILE=4, если файл не существует...(остальные значения см. в ff.h)
	sendStr(buf);

	f_err_code = f_read(&fil_obj,var,20,&ByteRead); //пытаемся читать 20 байтов с начала файла в переменную var
	sprintf((char*)buf,"f_read return: %X, byte read: %d",f_err_code,ByteRead); //возвратит FR_OK=0 в случае успеха, также выводим сколько байтов удалось считать
	sendStr(buf);

	sprintf((char*)buf,"var: %s",var);
	sendStr(buf);TX_NEWLINE; //выводим в терминал значение буфера

	f_close(&fil_obj); //закрываем файл
}

Строим проект, получаем и указываем в Proteus-е прошивку для контроллера. Запускаем.

На фото видно, что удалось считать лишь 16 байт из 20-ти, т.к. «заранее» был достигнут конец файла (в строке «Привет из файла!» как раз 16 байт).

***********II. ЧТЕНИЕ И ЗАПИСЬ НА КАРТУ; БЕЗ ПОДДЕРЖКИ ДЛИННЫХ ИМЕН ФАЙЛОВ*********

Настройки

Теперь давайте отвлечемся и подумаем, что происходит, когда мы создаем на жестком диске файл или папку. Операционная система вначале записывает аттрибуты файлапапки — дата создания, скрытый-системный и т.д. Откуда наш модуль будет знать, скока щас время? Правильно, нам нужен таймер (к слову —  т.е. получается, выставляя флаг _FS_READONLY 1, мы не только сильно экономим память нашего контроллера, но и нам не нужно тратить время на реализацию часов в программе или на подключение внешней микросхемы). Но в системе FATxx время записывается в специальном формате, а не в том как мы привыкли видеть. Поэтому нужна некоторая функция, которая будет преобразовывать время (попробуйте откомпилировать проект с _FS_READONLY 0, и вы увидите недостаток в виде одной функции get_fattime()).

Вот мы и подошли вплотную к файлам rtc.c, rtc.h.

В самом начале статьи я попросил вас эти файлы кинуть в папку с проектом, но не подключать их. Давайте откроем rtc.c в текстовом редакторе, в блокноте например. Сразу бросается в глаза I2C — да, для получения времени здесь используется внешняя микросхема с интерфейсом I2C. Часы реального времени, конечно, можно реализовать и самому внутри того же mega32. Однако статья не о том, поэтому мы подправим нужную нам функцию DWORD get_fattime() таким образом, чтобы она всегда возвращала одно и тоже время. Исходную функцию DWORD get_fattime() вы можете найти в архиве с примерами в файле main.c:

DWORD get_fattime ()
{
	RTC rtc;

	/* Get local time */
	rtc_gettime(&rtc); //определена в rtc.c

	/* Pack date and time into a DWORD variable */
	return	  ((DWORD)(rtc.year - 1980) << 25)
			| ((DWORD)rtc.month << 21)
			| ((DWORD)rtc.mday << 16)
			| ((DWORD)rtc.hour << 11)
			| ((DWORD)rtc.min << 5)
			| ((DWORD)rtc.sec >> 1);
}

Давайте изменим её, чтобы она всегда возвращала нам одно и тоже время. Т.е. rtc.c и rtc.h нам вообще не понадобятся, подключать их к проекту не нужно.

Вот такую функцию вставьте в программу:

DWORD get_fattime ()
{
	/* Pack date and time into a DWORD variable */
	return	  ((DWORD)(/*rtc.year*/2021 - 1980) << 25)
			| ((DWORD)/*rtc.month*/10 << 21)
			| ((DWORD)/*rtc.mday*/7 << 16)
			| ((DWORD)/*rtc.hour*/1 << 11)
			| ((DWORD)/*rtc.min*/26 << 5)
			| ((DWORD)/*rtc.sec*/6 >> 1);
}

Она всегда будет записывать в атрибуты созданияизменения файла дату 07.10.2021 01:26:06

_CODE_PAGE нам не важна(оставьте какая есть), т.к. _USE_LFN(поддержка длинных имен файлов) для начала мы отключим (оно отключено по умолчанию). Поэтому файл ccsbcs.c вначале я попросил к проекту не подключать.

Разбираемся с основными WRITE-функциями модуля FatFs

Из основных write-функций я бы выделил:

Странно, но функция копирования не реализована (v0.08a). Ну, впрочем не страшно, ничего сложного в ней нет, можно и самому написать, если надобность в этом будет.

Хочется отметить функцию  f_rename, которая

1) переименовывает файл, если OldName и будущее название файла NewName указаны в одной директории, или

2) переименовывает и перемещает файл, если OldName и будущее название файла NewName указаны в разных директориях.

Привожу пример с офиц. сайта:

    /* Файл будет переименован*/
    f_rename("oldname.txt", "newname.txt");

    /* Файл будет переименован и перемещен */
    f_rename("oldname.txt", "dir1/newname.txt");

С функциями f_unlink и f_mkdir все понятно, а вот f_write нужно разобрать поподробнее.

FRESULT f_write (
 FIL* FileObject,     /* Указатель на структуру file object, которую заполняет f_open*/
 const void* Buffer,  /* Указатель на буфер, содержимое которого записываем в файл*/
 UINT ByteToWrite,    /* Количество байт, которое хотим записать*/
 UINT* ByteWritten    /* Количество байт, которое успешно было записано*/
);

Итак, давайте запишем в файл foo.txt строку, но не в начало файла, а в конец, после строки «Привет из файла!».

Для этого нам потребуется сместить указатель перед записью с помощью функции f_lseek:

FRESULT f_lseek (
 FIL* FileObject,   /* Указатель на структуру file object, которую заполняет f_open*/
 DWORD Offset       /* Смещение от начала файла */
);

По идее нужно указывать смещение 16 байт, и тогда мы «перепрыгнем» строку «Привет из файла!».

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

Демонстрация записи на карту

~~~~~~~~~~~~~~~~~~~~~~
unsigned char buf[150]; //буфер для UART
static FRESULT f_err_code; //FRESULT для функций модуля
static FATFS FATFS_Obj; //структура - логический раздел
FIL fil_obj; //структура файла, с которым работаем
char* var="Здарова из программы"; // 🙂 Строка, которую запишем в конец файла
UINT ByteWrite = 0;

int main(void)
{
 uart0_init(); //настраиваем UART. С UART работайте сами.
 sendStr("*************Запись в файл*************"); //отправляю через UART строку на терминал. Эту функцию реализуем также сами, т.к. статья не про UART сегодня 🙂

 BYTE status = disk_initialize(0); //инициализируем карточку
 sprintf((char*)buf,"initialize return: %X",status); //возвратит 0 в случае успеха
 sendStr(buf);

 f_err_code = f_mount(0, &FATFS_Obj); //монтируем логический раздел
 sprintf((char*)buf,"f_mount return: %X",f_err_code); //возвратит FR_OK=0 в случае успеха
 sendStr(buf);

 f_err_code = f_open(&fil_obj, "foo.txt", FA_WRITE); //пытаемся открыть файл в режиме записи
 sprintf((char*)buf,"f_open return: %X",f_err_code); //возвратит FR_OK=0 в случае успеха, FR_NO_FILE=4, если файл не существует...(остальные значения см. в ff.h)
 sendStr(buf);

 f_err_code = f_lseek(&fil_obj,fil_obj.fsize);//смещаем указатель на кол-во байт, равное размеру файла. Т.е. переходим в конец файла. Заодним выведем размер файла до записи строки "Здарова из программы".
 sprintf((char*)buf,"f_lseek return: %X, size of file: %d",f_err_code,fil_obj.fsize);
 sendStr(buf);

 f_err_code = f_write(&fil_obj,var,strlen(var),&ByteWrite); //записываем в конец файла строку var. Пишем ровно столько байт, сколько букв(байт) в строке - strlen(var) = 20
 sprintf((char*)buf,"f_write return: %X, byte wrote: %d",f_err_code,ByteWrite); //возвратит FR_OK=0 в случае успеха, также выводим сколько байтов удалось записать
 sendStr(buf);

 f_close(&fil_obj); //закрываем файл
}

Строим проект, получаем и указываем в Proteus-е прошивку для контроллера. Запускаем.

Код ошибки:  Приложение для диагностики авто - OBD Авто Доктор Pro скачать на Андроид

Затем, открываем образ SD-карты и проверяем файл foo.txt:

размер стал 16 20=36 байт, содержимое — «Привет из файла!Здарова из программы». Кстати, посмотрите на дату изменения файла — 07.10.2021 01:26:06, как раз ее мы и указали в функции get_fattime().

***********III. ПОДДЕРЖКA ДЛИННЫХ ИМЕН ФАЙЛОВ*********

Для начала я продемонстрирую т.н. 8.3-имена файлов на примере получения списка файлов SD-карты.

Список файлов можно получить с помощью двух функций

DIR — аналог структуры FIL, только для директорий:

typedef struct _DIR {
 WORD  id;                                /* Owner file system mount ID */
 WORD  index;               /* Current index number */
 FATFS*            fs;                                /* Pointer to the owner file system object */
 DWORD           sclust;              /* Table start cluster (0:Static table) */
 DWORD           clust;                /* Current cluster */
 DWORD           sect;                /* Current sector */
 BYTE*  dir;                   /* Pointer to the current SFN entry in the win[] */
 BYTE*  fn;                                /* Pointer to the SFN (in/out) {file[8],ext[3],status[1]} */
#if _USE_LFN
 WCHAR*          lfn;                   /* Pointer to the LFN working buffer */
 WORD  lfn_idx; /* Last matched LFN index (0xFFFF:No LFN) */
#endif
} DIR;

В структуре высматривается макрос _USE_LFN, т.е. если поддержка длинных имен файлов включена, то названия помещаются в переменную lfn типа WIDE CHAR (unsigned short), иначе в fn типа BYTE (unsigned char) — имена типа 8.3: file[8],ext[3].

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

FRESULT scan_files (char* path)
{
 FRESULT res;
 FILINFO fno;
 DIR dir;
 int i;
 char *fn;
#if _USE_LFN
 static char lfn[_MAX_LFN * (_DF1S ? 2 : 1)   1];
 fno.lfname = lfn;
 fno.lfsize = sizeof(lfn);
#endif

 res = f_opendir(&dir, path);
 if (res == FR_OK) {
 i = strlen(path);
 for (;;) {
 res = f_readdir(&dir, &fno);
 if (res != FR_OK || fno.fname[0] == 0) break;
 if (fno.fname[0] == '.') continue;
#if _USE_LFN
 fn = *fno.lfname ? fno.lfname : fno.fname;
#else
 fn = fno.fname;
#endif
 if (fno.fattrib & AM_DIR) {
 sprintf(&path[i], "/%s", fn);
 res = scan_files(path);
 if (res != FR_OK) break;
 path[i] = 0;
 } else {
 /*printf("%s/%sn", path, fn);*/                 !!!!!! убрал
 unsigned char buf[_MAX_LFN * (_DF1S ? 2 : 1)   1]; !!!!!добавил
 sprintf((char*)buf,"%s/%s", path, fn);     !!!!!добавил
 sendStr(buf);   !!!!!добавил
 }
 }
 }

 return res;
}

Следующий вызов процедуры выведет все файлы, находящиеся на SD-карте — scan_files_orig(«»);

Демонстрация вывода 8.3(коротких) имен файлов — получение списка файлов SD-карты

~~~~~~~~~~~~~~~~~~~~~~
unsigned char buf[150]; //буфер для UART
static FRESULT f_err_code; //FRESULT для функций модуля
static FATFS FATFS_Obj; //структура - логический раздел

FRESULT scan_files (char* path)
{
 ~~~~~~~~~~~~~~~~~~~~~~
 ~~~~~~~~~~~~~~~~~~~~~~
}

int main(void)
{
 uart0_init();
 sendStr("*************Список файлов на SD(8.3)*************");

 BYTE status = disk_initialize(0); //инициализируем карточку
 sprintf((char*)buf,"initialize return: %X",status); //возвратит 0 в случае успеха
 sendStr(buf);

 f_err_code = f_mount(0, &FATFS_Obj); //монтируем логический раздел
 sprintf((char*)buf,"f_mount return: %X",f_err_code); //возвратит FR_OK=0 в случае успеха
 sendStr(buf);

 f_err_code = scan_files("");//выводим всё
 sprintf((char*)buf,"scan_files returns %d",f_err_code);
 sendStr(buf);TX_NEWLINE;
}

На скриншоте хорошо видны файлы с именами в формате 8.3:

Настройки

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

Поддержка длинных имен файлов включается в файле ff.h:

После взведения этого флага важной частью программы становится кодировка: When LFN is enabled, the code page must always be set correctly. По умолчанию стоит кодировка 932 — Japanese Shift-JIS. Поддержка ихних закорючек нам не нужна, поэтому кодировку надо сменить на другую, нужную нам из списка:

Внимание! Смена кодировки на 866 (Russian) не даст желаемого эффекта — русское название файла будет отображаться неверно. Причина в том, что типы character в avrlibc по умолчанию имеют кодировку UTF-8. Необходимо реализовывать поддержку Unicode в вашей программе — это проблема не простая и в этой статье затронута не будет.

Смените кодировку на 437(U.S.):

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

/* Unicode - OEM code conversion */
#if _USE_LFN
WCHAR ff_convert (WCHAR, UINT);
#endif

Демонстрация вывода длинных имен файлов — получение списка файлов SD-карты

Код программы оставляем тем же самым, как в предыдущем примере. Ну, изменим только заголовок

sendStr("*************Список файлов на SD(long names)*************"); 

Собираем проект, переходим в Proteus и запускаем симуляцию.

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

Проект в AVR STUDIO и PROTEUS 7.7 SP2 образ sd карты с расширением mmc (открыть можно в WinImage)
http://www.box.net/shared/fb2bhjkozu

Оцените статью
OBD
Добавить комментарий