Подписаться на получение новых статей на почту:

Шаг№22. Файловая система Petit FatFs для AVR. Часть 7

Всем привет. Наконец то мы с Вами дошли до файловой системы Petit FatFs, для работы с картой памяти. Разобравшись в прошлой статье с заголовками, теперь можем спокойно подключать библиотеки, что и сделаем в этой статье. Немного вспомним что мы начали рассматривать SD карту и низкоуровневые операции в статье №19 (Микроконтроллер ввода-вывода. Обмен карты SD.). И вот я нашел время и силы, и запустил файловую систему в железе и в Proteus. А также попытался сделать первые шаги, для создания логгера на этом устройстве. Провел различные тесты. Ну обо всем по порядку. Разберем что же такое Petit FatFs, зачем, куда и с чем его «кушать».

Зачем нам нужна файловая система? Ответ прост: она упрощает, автоматизирует работу операционной системы с контроллером карты(если имеется), либо с драйвером(если отсутствует). Является определенным стандартным порядком, который включает в себя способ организации, хранения и именования данных на носителях информации. На сегодняшний день все похожие работы с применением микроконтроллеров и карт SD выполняются с применением файловой системы, мы начнем из самой простой и специально разработанной для микроконтроллеров tiny и др. — Petit FatFs. Данная библиотека была разработана некто ChaN, и ее полное описание находится на сайте этого автора (The Electronic Lives Manufacturing, http://elm-chan.org/fsw/ff/00index_p.html). Как видите, данная система является подмножеством системы FatFs и разработанная для 8-битных микроконтроллеров, с небольшими ресурсами памяти. В общем нет смысла переписывать характеристики, хочется немного забежать на перед и показать цифры сжираемой памяти этой чудо системы, на рисунке ниже, в программе я использовал три самые необходимые функции для логгера- запись, чтение и использование указателя, при следующих включенных define:

#define _USE_READ 1 /* 1:Enable pf_read () */
#define _USE_DIR 0  /*1:Enable pf_opendir () and pf_readdir () */
#define _USE_LSEEK 1 /* 1:Enable pf_lseek () */
#define _USE_WRITE 1 /* 1:Enable pf_write () */
#define _FS_FAT12 1   /*1:Enable FAT12 support */
#define _FS_FAT32 0   /*1:Enable FAT32 support */

Файловая система Petit FatFs

(Для работы в Poteus)

                     Сразу хочу извинится за не последовательность, ниже мы все разберем с нуля, а здесь я хочу показать возможность использования программы на том или ином контроллере. Просканировав интернет, я сделал вывод, что данная система хорошо используется для чтения файлов, например аудио. И в качестве использования логгера ее не рекомендуют. Но так как мы начали с Вами разрабатывать устройство на Atmega 8A, то хочется дойти до логического конца не меняя железа.

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

Можно сделать несложный вывод, используя предыдущую программу, наш код не влезет, т.к. контроллер не резиновый. Но на самом начальном этапе разработки (Статья №17), как Вы можете увидеть, я оставил в левой части «целину», где я и рассчитывал разместить еще один контроллер, микросхему реального времени и источник питания для времени. У меня в наличии лежит еще один такой же «камень», вот в него я и рассчитываю залить ф.с. и при дружной параллельной работе двух контроллеров будем дальше идти к цели.

И еще одна оговорка, начнем писать под Proteus, а под конец опишем работу в железе и подведем итог .

Шаги для запуска ф.с.:

1.       Что ж вернемся к главному, начнем разбираться с нуля, смело переходим по ссылке, выше, автора ChaN. Есть, перешли. Мы видим краткое описание, характеристики и т.п., опускаемся вниз страницы к ресурсам, и нажимаем на Download: Old Releases и выбираем версию за December 7, 2010 Petit FatFs R0.02a. Почему? Вы вправе качать какую пожелаете, но у меня в процессе работы, в железе, с версией 2014 года R0.03 возник конфликт, на этапе использования функции открытия файла, начало возвращать FR_NO_FILE. В процессе наладки в файле pff.c я дошол до функции dir_find и в ней до источника ошибки, дальше загруз. Кто, вдруг, в курсе не стесняемся, подсказываем и направляем.

c = dir[DIR_Name];   /* First character */
if (c == 0) { res = FR_NO_FILE; break; }

Поэтому немного поковыряв код, я решил использовать предыдущую версию Чана.

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

2.          Далее открываем  папку драйвера. В ней должно быть пять файлов: pff.c, pff.h, integer.h, diskio.h, diskio.c. Первые два, это драйвер файловой системы, третий – заголовок для переопределения типов, так легче работать, нас интересуют последние два, те которые отвечают за низкоуровневую операцию. Открываем и смотрим , что мы имеем. В исходнике у нас лежат заготовки функций, в заголовке определены глобальные переменные и функции. Низкоуровневые операции пользователь должен обеспечить сам. Т.к. мы с Вами рассмотрели основы основ, то позаимствуем более полный код низкоуровневых операций у автора, а именно в примерах.

3.       Разберем оба примера автора, – avr boot (работа с atmega ) and avr (attiny). И напишем свой. Открываем первый, нас интересует файл mmc.c это тот же diskio.c, который нам надо дописать. Его тоже откроем и сравним чего нам не хватает. Заголовочные файлы одинаковы. Рассматриваем исходники. Первым делом подключаем библиотеку pff.h – в ней находятся define, которые я показал выше с рисунком, вот они отвечают за включение в код той или иной функции, а также поддержки файловой системы. Я уже писал нам необходимы три.

#include «diskio.h»
#include «pff.h»

Далее, в примере,  идут 6-функций, которые прописаны в ассемблерном файле asmfunc.S. Я пытался подстроится под этот файл, но у меня не получилось. Значит напишем эти функции сами. Открываем asmfunc.S сравниваем и пишем. Перед этим определим в начале файла все используемые ножки контроллера.

#define PORT_CS PB1
#define PORT_CK PB5
#define PORT_DI PB3
#define PORT_DO PB4
#define PIN_SD PINB
#define PORT_SD PORTB
#define DDR_SD DDRB

Теперь определим функции.

void init_spi (void)        /*инициализация ножек для режима SPI*/
{
      DDR_SD = 0x2A;           /*0010 1010 – PB1, PB3, PB5 выход, PB4-вход*/
      PORT_SD = 0x1A;         /* PB1, PB3, PB5 – 1, PB4-подтягиваем сопртивление.*/
}

Следующие две функции, это просто установка 1 либо 0 для выбора режима карты. Мы писали про это в статье №19.

void deselect (void){
      PORT_SD |=_BV (PORT_CS);
}
void select (void){
      PORT_SD &= ~_BV (PORT_CS);

Две следующие это прием и передача байта информации, вот их мы можем смело скопировать из статьи №19, естественно заменив название функций и портов

/*Передача байта*/
void xmit_spi (BYTE data)
{
           for (BYTE i=0;i<8;i++)  /*Перебираем байт*/
           {
                    if ((data&0×80)==0×00) /*Если старший бит = 0*/
                              PORT_SD&=~_BV (PORT_DI);
                    else
                              PORT_SD|=_BV (PORT_DI);
                   data=data<<1; // сдвиг влево
                    PORT_SD|=_BV (PORT_CK); //Импульс или строб
                    asm («nop»); //Пауза в 1 такт
                   PORT_SD&=~_BV (PORT_CK);
            }
}
/*Прием байта*/
BYTE rcv_spi (void)
{
           BYTE otv=0; // инициализируем массив
           for (BYTE i=0;i<8;i++) // Сдвигаем влево
          {
                 PORT_SD|=_BV (PORT_CK);  //Фронт импульса
                 otv=otv<<1;
                 if ((PIN_SD&_BV (PORT_DO))!=0×00)    //Если состояние пина 1
                        otv=otv|0×01;
                 PORT_SD &=~_BV (PORT_CK);
                 asm («nop»);
            }
            return otv;
}
          И последняя это макрос времени, в ассемблере как то сложно все описано, мы поступим проще, просто подставим функцию времени под это имя:
#define dly_100us () _delay_us (100)
Как видно из функций выше, для задержек, добавим еще две библиотеки:
#include <util/delay.h>
#include <avr/interrupt.h>
Все, дальше в примере (avr boot), начиная со строки комментария
/*Module Private Functions*/
копируем все до конца. Единственное в функции инициализации я изменил эти строки
for (n = 100; n; n--) dly_100us (); /* 10ms delay */
for (n = 10; n; n--) deselect ();  /*80 Dummy clocks with CS=H */
на эти
for (n = 100; n; n--) dly_100us ();   /*10ms delay */
for (n = 100; n; n—)    /* 80 Dummy clocks with CS=H или больше*/
{
       PORT_SD|=_BV (PORT_CK);  //Импульс или строб
       asm («nop»); //Пауза в 1 такт
       PORT_SD&=~_BV (PORT_CK);
}

Как видите в файле не хватает функции записи. Поэтому открываем папку avr, здесь описана работа с контроллером tiny, и копируем функцию записи DRESULT disk_writep. Теперь можете сравнить файл шаблон diskio.c с дополненным. А также сравните функции с рассмотренными нами в статье №19. Вы увидите что они более гибкие, и позволяют работать с разными типами карт. Все файл с низкоуровневыми операциями готов. 

Скачать mmc.c ( Скачали: 181 чел. ) 

4. Заходим, опять на ту же страницу автора и смотри какие функции нам доступны в разделе Application Interface. Мы будем использовать 5-ть из них. Для простого логгера более чем достаточно. Да и память , не сильно “разгуляешься”. Для начала попробуем просто запустить проект в Proteus. Оказывается это не так просто. Разберем пошагово как запустить карту.

4.1           Создадим образ. Что бы использовать карту в Proteus, необходимо к карте привязать образ. Я использовал программу WinImage.  Устанавливаем — Запускаем программу – Файл – Создать – Заказной формат образа – ОК — Выбираем файловую систему FAT12/16 — В строке общее количество секторов, указываем под объем карты. Возьмем например 8 мБ. Укажем 16384 секторов.(1кб – 2 сектора, 1 мб – 2048 секторов) В строке общий размер образа отобразится 8192 кБ.

Образ в ProteusWinImage и Proteus

Рис. слева – настройка. Рис. справа – открытый образ с текстовым файлом.

Открываем созданный образ. В этот образ вставляем или перетаскиваем текстовый файл, заранее созданный и с выделенным размером.
4.2       Открываем свойства карты. В пункте Card Image File, необходимо выбрать образ файла, а также размер 8 Мб.

SD-карта в proteus настройки

4.3       Все приступаем к программе. Пробуем произвести запись в файл. Алгоритм следующий – Инициализация – Монтирование – Открытие – установка указателя – записать – Финализация – Демонтирование. Ниже расположен код программы, а также результат в текстовом файле.
/*Библиотеки файловой системы*/
#include «pff.h»
#include «diskio.h»
#include «integer.h»
/*начало основой программы*/
int main (void)
{
        char block[100]={"Привет"};    /*буффер записи/чтения данных на карту*/
/*Необходимо создать переменные для работы с файловой системой*/
        FATFS fs; /*Рабочая область (file system object) для логических дисков (из библиотеки) */
        FRESULT res; /*переменная перечиления, по умолчанию int, возвращает код результатат функции (библиотека pff.h)*/
        WORD bw;    /*Указатель на переменную для количества записанных байт*/
/*Функция инициализации повторяется, и используется для теса. Можна не инициализировать т.к. в функции ниже,pf_mount (&fs) проходит инициализация*/
        res=disk_initialize ();
        if (res!=0)
        { return 0; }
/*Монтируем том, для использования диска */
        res=pf_mount (&fs);
        if (res!=0)
        { return 0;}
/*далее необходимо открыть файл*/
        if ((res=pf_open («data.txt»)) != FR_OK)
        { return 0;}
/*Устанавливаем указатель на сектор, который необходимо прочитать*/
        pf_lseek (0);
/*Записываем данные*/
        pf_write (block,strlen (block),&bw);
/*Финализация, чтоб не потерять данные*/
        pf_write (0,0,&bw);
/*Демонтируем том*/
        pf_mount (0×00);
        return 0;
}

Результат программы на рис. Запись на SD карту в Proteus Как видите мы использовали функцию записи начиная с 0-го байта, т.е. 1-го сектора. Теперь немножко усложним задачу. Запишем также в первый сектор, прочитаем в буфер и запишем во второй сектор данные из первого, начиная с 512 –го байта, т.е. 2-го сектора. Для этого в программу добавим еще один буфер и функцию чтения:

#include «pff.h»
#include «diskio.h»
#include «integer.h»
int main (void)
{
     char block[100]={"Привет"};
     FATFS fs;
     FRESULT res;
     WORD bw;
     res=pf_mount (&fs);
     if (res!=0)
     { return 0;}
     if ((res=pf_open («data.txt»)) != FR_OK)
     { return 0;}
     pf_lseek (0);
     pf_write (block,strlen (block),&bw);
     pf_write (0,0,&bw);
     pf_lseek (0);
     pf_read (block1,100,&bw);
     pf_lseek (512);
     pf_write (block1,strlen (block1),&bw);
     pf_write (0,0,&bw)
     pf_mount (0×00);
     return 0;
}
Результат программы на рис.

Запись данных на SD-карту Petit FatFs        Тут следует запомнить, до записывать в сектор после финализации не получится, т.к. происходит заполнение нулями, то сектор просто пере запишется.

А вот например повторная запись до финализации, позволяет последовательно заполнять сектор, например при использовании кода ниже:

block1=”-83,16”;
for (char i=0; i<2;i++)
pf_write (block1,strlen (block1),&bw);
Получим следующий результат, тут я уже экспериментировал в железе:

Запись температуры на SD-картуВ железе ф.с. ведет себя также, единственный момент, если в железе мне не потребовалось включать поддержку FAT12

#define _FS_FAT12 0 /* 1:Enable FAT12 support

#define _FS_FAT32 0 /* 1:Enable FAT32 support

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

Теперь кратко о недостатках файловой системы:
— ф.с. не может создавать файл;
— не может быть увеличен размер файла;
— если позичия чтения/записи в файле (read/write pointer) не находится на границе сектора, оставшиеся байты до границы сектора будут заполнены нулями;
— после инициализации операции записи она обязательно должна быть финализирована, иначе записанные данные могут быть потеряны;
— нет возможности поиска последнего места записи файла.

Итак подытожим все. Для запуска файловой системы, необходимо создать текстовый файл в microSD, в нашем случае, увеличить размер, например пробелами, сохранить. Проделать 4-ри пункта, которые описывались выше. И все должно  заработать. Не забудьте в настроечном файле Makefile указать pff.c, mmc.c. Отдельно хочется опять сказать о ресурсах памяти. Мне пришлось отключить все остальные библиотеки , которые использовали в прошлой статье. Единственно что можно, то это отключить функцию чтению и включить библиотеку семи сегментного индикатора для наладки устройства в железе. Ресурс почти под завязку. В общем исходники я выложил,  экспериментируйте. Дальше все же попробуемu реализовать своеобразную базу данных в этой системе. Попробуем программно компенсировать часть недостатков этой системы. Чтоб не утомлять читателя длинным постом я перенесу тему в следующую статью. Всем спасибо за внимание. Критикуйте, предлагайте. Всем пока.

Скачать файлы ( Скачали: 423 чел. ) 

Просмотрено 12911 раз.

Я на Google+

Шаг№22. Файловая система Petit FatFs для AVR. Часть 7: 16 комментариев

  1. Спасибо. Интересно будет увидеть окончание проекта контроллера сбора данных на этой системе.

  2. Добрый день.

    Пробовал по Вашей методике FAT32. Правда только в Протеусе. Не работает.

    Запись спотыкается на границе кластера. Lseek выдает ошибку — disk error, за ним write — file not open. То есть форматируешь образ с размером кластера, напрмер, 4096 байт. При lseek=0,512,1024 и т.д. пишет. При lseek=4096, то есть когда нужно открывать следующий кластер, происходят указанные ошибки. Если форматируешь с кластером 2048, ошибка на 2048!

    Сейчас попробую чтение.

    Да, еще. Протеус, как отладчик, не дает трассировать в подключенных модулях. Т.е. ходить пошагово можно только в модуле, где main. Это не дает локализовать проблему. Однако, если в окно watch бросить структуру FATFS fs, то видно, что ее поле curr_clust (текущий кластер) сначала равно 3, а при переходе через границу кластера становится 4095! Что явно неправильно.

    FAT12/16 работает.

    Компилятор версии 3.12. Протеус — 7.6 SP4. Никто не сталкивался? может неправильно что-то делаю?

  3. С чтением — те же проблемы. Проект — минимальный, скачанный на этой странице. Что скажете?

  4. А, нет! Протеус заходит в модули, поэтому получилось потрассировать. Выяснилось, что неправильно определяется формат файловой системы. Он всегда ставит FAT12, и, понятно, если фактически в образе FAT32 — вылязят ошибки. Проблема чтения-записи уходит, когда принудительно в исходнике поставить тип системы FS_FAT32, минуя алгоритм вычисления этого типа. Где-то там ошибка. Пока не разобрался где. Это надо фундаментально вникать в дебри FAT16/32. А может winimage некорректный образ делать?

  5. Причем флешку в 8mB видит, как FAT12, а уже 16 mB видит как FAT16. Далее до 128 mB — тоже как FAT16. Больших флешек в протеусе в модели нету.

    Для FAT32 нету никаких ограничений по минимальному объему?

    Что-то мне подсказывает, что в железе на большой флешке все будет работать ...

    • Здравствуйте. Да Вы правы на большом объеме будет работать. Если Вы посмотрите статью №39, то я все таки собрал логгер на этой системе. Флешка 2 Гб. Конечно есть неудобство в системе Petit FatFs — это создание размера файла, и проблема дозаписи, последнее что я поборол, хоть немного и сыровато.Да и еще я использовал версию за 2010 год,т.к. с 2014 у меня не пошло была проблема. Так что с версиями если что то же надо поексперементировать.

  6. Здравствуйте. Спасибо за ответ.

    Насчет '' неудобства'' — так это не неудобство, а вполне разумная плата за малый объем кода. Поднять такого монстра, как fat, в четырех килобайтах, это еще постараться надо! Я вообще удивляюсь, что они сохранили в таких условиях функцию записи.

    Вот оно и глючит с определением типа fat. Посмотрите функцию монтирования. Там вывод о типе системы делается как-то по максимальному числу кластеров. Это очень упрощенно. Я бегло посмотрел описание fat. Каюсь, в викпедии. Там говорят, что определить тип системы однозначно, не так просто. Поэтому, если мы вставим реальную большую флешку с fat32, может и определит правильно.

    А так, для небольших поделок, вполне пойдет! Я думаю в полной версии fatfs таких упрощений нет, там все определится правильно.

    Просто этот глюк нигде не описан. Это очень странно. Я попробовал — и сразу грабли. Поэтому и написал эти коментарии, попутно разбираясь.

    А у кого еще глючило на флешках <=128 мб? Просто нигде не упоминается, ни на форумах не видел, ни в описании petit fatfs! Это вот и странно.

    Еще, конечно, компилятор у нас не безгрешный, с этим могут быть проблемы. Такие системы надо поднимать с оглядкой на это.

  7. Еще раз перечитал описание fat. Надо было сразу внимательнее! Оказалось, что для дисков менее 512 мб должна создаваться fat16. Они не могут быть fat32! Тем не менее winimage такое позволяет, что некорректно по спецификации fat. Отсюда и глюк. Так что все там у Чена правильно, посыпаю голову пеплом. Вопрос можно считать исчерпанным.

    P. S. Интересный у Вас сайт, что-то я его раньше не видел. Больших Вам, как говорится, творческих узбеков! )))

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

    Удобно для логгера и прочего...

  9. Уважаемый автор долго искал хорошую статью о том как работать с Fatfs и наконец то наткнулся на нее. Большое Вам спасибо за это! Завтра попробую реализовать все выше описанное на MSP430, надеюсь у меня все получиться. Хотел у Вас спросить есть ли рабочий пример инициализации и работы в SDHC картами?

    • Здравствуйте. Вы наверное обратили внимание, что это Petit FatFs. В общем принцип один. инициализацию я расписывал в статье 19. Рабочий пример мой Вам не пойдет, т.к. у себя я делал до запись программно. FatFs все умеет сама, так что терзайте.

  10. Уважаемый автор!

    Прошу Вас подсказать мне :

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

    /* Write partial sector */

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

    #if _USE_WRITE

    DRESULT disk_writep (

    const BYTE *buff, /* Pointer to the bytes to be written (NULL:Initiate/Finalize sector write) */

    DWORD sa /* Number of bytes to send, Sector number (LBA) or zero */

    )

    {

    DRESULT res;

    WORD bc;

    static WORD wc;

    res = RES_ERROR;

    if (buff)

    { /* Send data bytes */

    bc = (WORD) sa;

    while (bc && wc)

    { /* Send data bytes to the card */

    xmit_spi (*buff++);

    wc--; bc--;

    }

    res = RES_OK;

    }

    else

    {

    if (sa)

    { /* Initiate sector write process */

    if (!(CardType & CT_BLOCK)) sa *= 512; /* Convert to byte address if needed */

    if (send_cmd (CMD24, sa) == 0)

    { /* WRITE_SINGLE_BLOCK */

    xmit_spi (0xFF); xmit_spi (0xFE); /* Data block header */

    wc = 512; /* Set byte counter */

    res = RES_OK;

    }

    }

    else

    { /* Finalize sector write process */

    bc = wc + 2;

    while (bc--) xmit_spi (0); /* Fill left bytes and CRC with zeros */

    if ((rcv_spi () & 0x1F) == 0×05)

    { /* Receive data resp and wait for end of write process in timeout of 500ms */

    for (bc = 5000; rcv_spi () != 0xFF && bc; bc--) dly_100us (); /* Wait ready */

    if (bc) res = RES_OK;

    }

    deselect ();

    rcv_spi ();

    }

    }

    return res;

    }

    это процедура записи блока данных на SD. Так вот тупо взять и вставить все эти процедуры к себе в код не привели к желаемому результату, решил проблему обдуманным прочтением постов про инициализацию запись и чтение на SD карточку. Но для того чтобы использовать Petit FatFS всетаки необходимо привести свои процедуры чтения и записи к тем, что выложены как пример Вами. Так вот начал анализировать эту процедуру disk_writep и выяснил что запись в карту производится этапами в какой то этап команду 24 шлем блок данных и так далее (незнаю зачем именно так этот мистер Чен все это реализовал, но думаю он умный чувак и спорить я с ним не буду ), непонятно что именно делает непроиинициализированная перменная static WORD wc; Вроде она не проинициализированна а на нее в коде смотрит ветка :

    while (bc && wc)

    { /* Send data bytes to the card */

    xmit_spi (*buff++);

    wc--; bc--;

    }

    вроде в др ветке ее проинициализировали но на нее некто не смотрит :

    if (send_cmd (CMD24, sa) == 0)

    { /* WRITE_SINGLE_BLOCK */

    xmit_spi (0xFF); xmit_spi (0xFE); /* Data block header */

    wc = 512; /* Set byte counter */

    res = RES_OK;

    }

    В общем я в ступоре!

    Если Вам не трудно подскажите, проясните а!

    Благодарю Вас!

    • К сожалению занят другими проектами, поэтому нет времени разбирать код.

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

Ваш e-mail не будет опубликован.

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Subscribe without commenting