Дёшево и сердито!

Азы работы с MMC

    MMC карты имеют достаточно простое управление. Память разбита на сектора по 512 байт. Карты форматируются так же, как обычные винчестеры под DOS: с 1 разделом, файловая система - FAT16. Чтение возможно как отдельными байтами, так и блоками.
    Напряжение питания карты должно быть в пределах +2.7в...+3.6В, скорость обмена до 20 мбит/с. При простое более 5мс карточка переводится в sleep режим с малым энергопотреблением, и выходит из него автоматически при возобновлении обмена.

Интерфейс карты

    MMC карты могут работать в двух режимах обмена - MultiMediaCard protocol и SPI protocol.Первый более скоростной, а в пользу второго говорит то, что много контроллеров имеют встроенный SPI интерфейс. Ниже приведена разводка MMC именно для работы в режиме SPI:

разводка MMCPINИмяФункция
1xCSВыбор кристалла
2DIВходные данные
3VSSЗемля
4VDDПитание +3.3в
5SCLKСинхронизация
6VSS2Земля
7DOВыходные данные

    Cигнал выборки имеет "0" активный уровень. SPI порт в управляющем контроллере (далее МК) должен быть настроен так, чтобы активным был передний фронт SCLK:
SPI обмен

    Сразу же напомню о том, что фактически передача байта из контроллера (DO) осуществляется параллельно с приёмом по другой линии (DI). Чтобы начать приём/передачу через SPI в PICе (в Master mode), в буффер приёмопередатчика SSPBUF нужно что-то записать, а после окончания пересылки из SSPBUF считать принятое. В простейшем случае обмен с картой имеет вид "команда - ответ":
команда-ответ
Обозначения:1 клетка - 8 бит; X - произвольно, Z - третье состояние, H - 0xFF, L - 0x00; Тр = 1÷8 байт, Тотв = 1÷many байт.
Внимание! Рисунки поясняют принцип обмена, поэтому масштабы не соблюдены!

    Обмен начинается с того, что МК выставляет сигнал 0 на xCS. Сначала посылаем 6 байт команды - последовательно пишем в вышеуказанный SSPBUF нужные байты, не забывая перед записью очередного дожидаться окончания передачи предыдущего (бит SSPSTAT,BF). Далее нужно дождаться ответа карты: пишем в SPI байт 0xFF и по окончании каждой передачи (контролируем SSPSTAT,BF) читаем принятое в SSPBUF. Первый байт, отличный от 0xFF, будет первым байтом ответа карточки (а для рассматриваемых ниже команд ответ всегда состоит из 1 байта).
    Данные, если требуется, передаются после ответа блоком заданной ранее длины:
команда-ответ-данные

Начало блока данных "ловится" так-же: его первый байт отличен от 0xff (см. ниже).После окончания обмена нужно подать 1 на xCS.

Команды и ответы

     Команда имеет длину 6 байт, передача всегда начинается со старшего бита. Пакет команды имеет следующий формат:
Позиция бита4746[45:40][39:8][7:1]0
Длина1163271
Значение'0''1'xxx'1'
Описаниестарт бит-№ командыаргументCRC7стоп бит

    При работе MMC в режиме SPI доступно около 15 команд, позволяющих во-первых получить полную информацию о типе и текущем состоянии карты, во-вторых производить запись и чтение данных. Рассмотрим всего лишь 4 команды, с помощью которых можно лишь читать данные с MMC блоками размером 1÷512 байт (мне для плеера вполне хватает):
Номер
комады
АргументОписание
CMD0нетgo_idle_state
Сброс
CMD1нетsend_op_cond
Инициализация
CMD16[31:0] длина блокаset_blocklen
установить размер блока
CMD17[31:0] адрес блокаread_single_block
прочитать блок размером, указанным set_block_len
Обратите внимание: все числа, аргументы и т.п. передаются начиная со старшего бита, а адрес блока - адрес первого байта блока.

    Ответ на любую из вышеприведённых команд состоит из одного байта, старший байт всегда равен 0. Другие биты - флаги ошибок:
Ответ

  1. В спящем режиме - карта находится в спящем режиме и выполняется процесс инициализации;
  2. Erase reset - стирание не выполнено, т.к операция прервана до исполнения;
  3. Недопустимая команда - обнаружен недопустимый номер команды;
  4. Ошибка CRC - последняя принятая команда не прошла проверку CRC;
  5. Erase_seq_error - ошибка в команде стирания;
  6. Ошибка адреса - блок пересекает границу физического сектора;
  7. Ошибка параметра - аргумент команды вне допустимых пределов для данной карты.
Ошибки №2,4,5 нам встречаться не должны. Для операции чтения CMD17 считываемый блок должен быть в пределах одного физического сектора: его размер не должен превышать 512 байт, а начало и конец располагаться в одном и том же секторе. Если это не выполнено, то появится ошибка №6
    Блок данных имеет длину от 4 до N+3 байт, где N - число, указанное в аргументе CMD16. Первый байт при передаче от MMC к МК равен 0xFE, далее следуют N байт запрашиваемой информации, а в конце - 2 байта CRC (их содержимое можно игнорировать, но прочесть нужно обязательно!). Если же при чтении произошёл сбой и карта не может предоставить данные, то вместо указанного блока передаётся 1 байт с флагом ошибки:
Ошибка данных

  1. Неизвестная ошибка - сбой по неизвестной причине;
  2. Ошибка СС - сбой внутреннего контроллера;
  3. Сбой ЕСС - алгоритм ЕСС не смог восстановить данные;
  4. Выход за границу - аргумент команды вне допустимых пределов;
  5. Карта блокирована - доступ не разрешён, т.к карта защищена паролем.

Инициализация карты

    После включения карта находится в режиме MultimediaCard protocol. Для перевода её в режим SPI надо отправить команду Сброс (CMD0) (не забывайте про xCS). В режиме SPI проверка CRC отключена по умолчанию, поэтому содержимое поля CRC7 игнорируется. Однако для CMD0 поле CRC7 нужно указать правильно. Поскольку команда не имеет меняющихся в процессе работы аргументов, то и специально вычислять это поле не обязательно: правильная CMD0 имеет вид: 0x40, 0x0, 0x0, 0x0, 0x0, 0x95. Далее карту необходимо проинициализировать. Для этого посылаем команду CMD1 до тех пор, пока в ответе карты бит0 (в спящем режиме) не сменится с 1 на 0. Это будет означать, что карта готова к работе. Теперь можно посылать прочие команды (у нас это CMD16 и CMD17).

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

    Самое интересное - это работа с файловой системой. Не буду сильно распространяться, т.к. в инете итак навалом информации о том, что такое FAT16 и с чем её едят. Кратенько расскажу о том, как использовать содержимое некоторых секторов в своих личных целях ;)
Структура диска:
MasterBootRecord
резервировано
PartitionBootRecord
FAT1
FAT2
Корневой каталог
кластер 002
кластер 003
***

MBR находится в нулевом секторе диска, позиции остальных частей будем вычислять.
Нулевой сектор, он же Master Boot Record диска, выглядит примерно так:
Позиция байтаДлина(байт)ОписаниеСодержимое
0х0446-0x0
0x1BE16описание разделасм.ниже
0x1CE16описание разделасм.ниже
0x1DE16описание разделасм.ниже
0x1EE16описание разделасм.ниже
0x1FE2подпись0x55,0xAA

Нам нужно описание первого раздела (то, что по смещению 0x1BE).Что там есть:
Позиция байтаДлина(байт)ОписаниеСодержимое
0x41тип файловой системы6=DOS 16-бит ФАТ
(возм. др. варианты)
0x84позиция 1 сектора разделаномер сектора
0xС2число секторов в разделеот 1 до макс. числа
секторов диска

Это, конечно, не всё, но этого вполне хватит. Как пример привожу то, что написано в моей карточке:
Offset      0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F

000001B0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 01   ................
000001C0   01 00 06 07 E0 D3 20 00  00 00 E0 D3 03 00 00 00   ....аУ ...аУ....
Здесь позиция PBR - сектор номер 0x00000020, а число секторов 0x0003D3E0 (все длинные числа записаны с младшего байта), Offset - смещение байта от начала диска. Не стоит забывать, что физический адрес PBR равен pbr_adr = 0x00000020 * 0x200 =0x00004000 (0x200 - число байт на сектор). Идём в Partition Boot Record раздела:
Позиция байтаДлина(байт)ОписаниеСодержимое
0хD1секторов на кластер, Sectors_per_ClusterXX(1...64)
0xE2число резервированных под PBR секторов, RezervX
0x101число таблиц FАТ, Number_of_Fat2
0x112число записей в корневом каталоге, Root_dir_entry512
0x162секторов на FАТ, Sectors_per_FATXXX
0x204всего секторовXXX
Опять-же здесь в помощь моя карточка:
Offset      0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F

00004000   EB 00 90 20 20 20 20 20  20 20 20 00 02 04 01 00   л.ђ        .....
00004010   02 00 02 00 00 F8 F5 00  20 00 08 00 20 00 00 00   .....шх. ... ...
00004020   E0 D3 03 00 80 00 29 A9  3D 30 FC 4E 4F 20 4E 41   аУ..Ђ.)©=0ьNO NA
    Мы видим 0x04 сектора на кластер, 0x00F5 секторов на каждую FAT, под PBR отведён 1 сектор, 512 записей в корневом каталоге. Этих четырёх чисел хватит, чтобы вычислить 3 важных смещения:
  • Fat_base = pbr_adr+0x200*Rezerv; (0x4200)
  • Root_base = Fat_base + Number_of_Fat * (Sectors_per_FAT * 0x200); (0x41600)
  • Cluster_base = Root_base + Root_dir_entry * root_entry_size - 2 * (Sectors_per_Cluster * 0x200); (0x45600)
    Fat_base - адрес нулевого байта FAT, Root_base - адрес нулевого байта корневого каталога, Cluster_base - адрес нулевого (не 002!!!) кластера (нулевого кластера нет, но так удобнее, см. ниже). В скобках - числа для моей MMC, root_entry_size - размер записи в корневом каталоге, равен 32 байта.
    Все смещения есть, осталось совсем немного. Разберёмся с FAT. Во первых всё пространство диска (после Root Dir) поделено на т.н. кластеры по "Sectors_per_Cluster" секторов в каждом. Во вторых 2 копии FAT идентичны, поэтому будем работать с первой. А FAT состоит из последовательности 2-х байтных слов (МЛАДШИЙ байт впереди). N-ое слово соответствует N-ому кластеру (N>1), и может содержать следующую информацию:
ЧислоЗначение
0002-FFEFномер следующего кластера в цепочке
FFF7это дефектный кластер
FFF8-FFFFэто последний кластер цепочки
0000,0001,FFF0-FFF6резервировано (и нам не интересно)

Исходя из разбиения диска на кластеры файл, будь он больше размера одного кластера, естественно содержит их несколько штук. Последовательность номеров кластеров, в которых записан файл, образует цепочку кластеров. Цепочка строится так: номер первого читается из записи в каталоге; в соответствующем слове FAT при этом указан следующий кластер. В слове для следующего - номер третьего и т.д. пока не достигается конец цепочки.
     Маленький организационный момент: первые 4 байта FAT - обязательная подпись, означает, что это начало FAT. Поэтому Нет нулевого и первого слова, Нет нулевого и первого кластеров, сразу за RootDir сидит кластер номер 0002.
И снова от теории к практике - придуманное мной для примера начало FAT:
Offset      0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F
00004200   F8 FF FF FF 03 00 05 00  06 00 04 00 07 00 FF FF   шяяя..........яя
     Первые 4 байта - подпись. Допустим, файл начинается со 2 кластера, Num = 2. Тогда адрес этого кластера adr = Cluster_base + Num * Sectors_per_cluster * 0x200. Кластер мы прочитали, а дальше читаем слово в FAT для этого кластера, 2 байта начиная с адреса adr = Fat_base + Num * 2 (0x4204). В нашем случае это 0003 - номер следующего кластера (см. таблицу). Расшифроывая таким образом содержимое Fat получим цепочку:
0002 - первый,0003,0005,0004,0006,0007 - последний.
Если разобраться - достаточно просто.

     Наконец заглянем в корневой каталог - там кроме имени файла, даты, времени, атрибутов, указан первый кластер файла и его размер в байтах. Каждая запись в корневом каталоге состоит из 32 байт. Нам нужно прочитать байты со смещением 0x00,0x02,0x1A-0x1B,0x1C-0x1F. Если нулевой байт не равен 0xE5, второй не равен 0x00 или 0x04, то 0x1B:0x1A - первый кластер файла, а 0x1F:0x1E:0x1D:0x1C - его размер в байтах. Пока плеер без экрана, имя файла получать не обязательно...

P.S.: Как выяснилось, SD карты полностью совместимы с карточками MMC, в т.ч. в режиме SPI. "Лишние" выводы в этом режиме не используются:

разводка SDPINИмяФункция
1xCSВыбор кристалла
2DIВходные данные
3VSSЗемля
4VDDПитание +3.3в
5SCLKСинхронизация
6VSS2Земля
7DOВыходные данные
8RSVРезерв
9RSVРезерв

    Единственно что стоит сделать - поставить подтягивающие сопротивления на выв. 8 и 9 (в р-не неск. деятков кОм).

    Основная часть составлена по материалам "MultiMedia Card product manual v5.2", с сайта SanDisk. Тем, кто захочет поподробнее разобрать данный вопрос, рекомендую обратиться к означенному документу. Datasheet на SD взят с того-же сайта :"SanDisk SD card product manual v1.9".
О замеченных очепятках и неточностях прошу писать мне.