22 августа 2015 г.

Как работать с SD / MMC картой через SPI - Теория

SD карты (Secure Digital) - это формат карт флэш памяти. Был представлен в 1999 как улучшенный MMC. Стандарт SD регулирует "SD Association" (SDA).  На их сайте можно найти документацию на стандарт.

Я постараюсь вкратце расписать, как использовать эту карту для хранения и считывания информации с помощью SPI. О том как работать с SPI расписано здесь.

Рис.1 SD карты различных размеров
По стандарту, у SD карт есть 3 возможных физических размера: стандартный, mini и micro:

Рис.2 различные типы размеров SD карт с замерами
Есть 4 поколения карт памяти, которые отличаются друг от друга размером хранимой информации, считыватели карт младших поколений не работают с более поздними версиями.

  • SD 1.0 — от 8 МБ до 2 ГБ;
  • SD 1.1 — до 4 ГБ;
  • SDHC — до 32 ГБ;
  • SDXC — до 2 ТБ.

Есть 3 способа общения с SD картой:

  • Шина SPI
  • Однобитовый режим шины SD
  • Четырёх битовый режим шины SD

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

Рис.3 Распиновка различных карт памяти
Режим SPI
MMCSDminiSDmicroSDИмяВход/ВыходОписание
1
1
1
2
nCS
Вх
Выбор режима SPI (негативная логика)
2
2
2
3
DI
Вх
Ввод данных SPI [MOSI]
3
3
3
VSS
.
Земля
4
4
4
4
VDD
.
Питание
5
5
5
5
CLK
Вх
тактовый сигнал [SCLK]
6
6
6
6
VSS
.
Земля
7
7
7
7
DO
Вых
Вывод данных SPI [MISO]
8
8
8
NC
.
Не используется
9
9
1
NC
.
Не используется
10
NC
.
Зарезервировано
11
NC
.
Зарезервировано

Если использовать SPI то SD карта питается от 3.3 В, может потреблять от 20 до 100 мА при передаче данных. В режиме покоя потребляет меньше 1 мА. Максимальная скорость передачи 12 мб/с (Частота CLK 25 МГц). Теперь мы знаем как подключить карту, осталось разобраться как с ней общаться.

В сети есть несколько открытых библиотек для работы с FAT (16/32), к примеру: FatFs, в таком случае можно считывать данные с компьютера, проверяются повреждённые блоки, память и ресурсы карты расходуются разумнее, ведь файловая система используется не просто так.

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

Можно создать текстовый документ, записать в него нужную информацию, открыть SD карту с помощью Hex-редактора, (к примеру: HxD) посмотреть в каком блоке (кластере) находится информация, (может быть она записана в нескольких блоках) считать её.

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

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

SPI используется всего лишь как интерфейс, данные передаются по "урезанной" версии стандартного протокола SD карты.

Протокол передачи данных

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

Рис.4 Считывание 1 блока данных (512 байт)
Рассмотрим что здесь изображено. Сперва хост по линии DataIn (MOSI) посылает команду карте. Рассмотрим из чего состоит "команда":

Рис.5 Формат команды
Команда состоит из 48 бит, начальный бит = "0", затем "1" означает, что это команда от хоста.
Биты передаются, начиная со старших разрядов.
Content - содержимое команды вместе с адресом или параметром (38 бит). Первые 6 бит определяют команду, остальные 32 это параметр команды, к примеру, ячейку с каким адресом прочитать из памяти.
CRC (Cyclic Redundancy Code) - помогает избегать ошибок при передаче, у каждой команды есть свой CRC код (контрольная сумма).
Стоп бит = "1".

Response - это ответ карты на команду, если он есть (есть команды без ответа), то он может быть 1 из 4 видов (R1, R2, R3, R6):

Рис.6 Формат ответа
Ответ может состоять из 48 или 136 бит. Передаются, также начиная со старших разрядов. Сперва начальный бит = "0". Затем "0" означающий, что это команда от карты. В содержимом может быть либо значение регистра, который мы хотели считать, стоит отметить, что регистр RCA не доступен в режиме SPI (R3), либо "зеркальный" код команды и информация о состоянии.

Data block - это 512 байт данных, которые мы хотели считать, после них идёт контрольная сумма (CRC) для проверки содержимого.

Команды

Теперь рассмотрим, какие команды доступны в режиме SPI, команда состоит из 6 бит и определяется её индексом, чтобы отправить SD карте команду CMD0 нужно передать "000000", для CMD39 это будет "100111" (передаётся, начиная со старшего разряда). Сразу скажу, что большинство команд для нас не имеет значения, если мы хотим только считать и записать несколько байт.

Индекс командыАргументОтветОписание
CMD0[31:0] мусорR1Перезагрузка карты памяти
CMD1[31] Зарезервировано
[30] HCS
[29:0] Зарезервировано
R1Карта отправляет хосту информацию о ёмкости и включает процесс инициализации.
HCS работает после команды
CMD8. Если хост поддерживает карты SDHC и SDXC выставить "1", иначе "0".
Зарезервированные биты должны быть выставлены в '0'.
CMD6[31] Режим
0:Проверить функцию
1:Изменить функцию
[30:8] Зарезервировано
(все '0')
[7:4] функциональная группа 2 для системных команд
[3:0] функциональная группа 2 для режима доступа
R1Смотреть в даташите.
CMD8[31:12] Зарезервировано
(все '0')
[11:8] Напряжение питания (VHS)
[7:0] проверочный образец
R7Отправляет карте напряжение питания и спрашивает может ли она работать при таком питании.
CMD9[31:0] мусорR1Спрашивает у карты её информацию "о карте" (CSD) .
CMD10[31:0] мусорR1Спрашивает у карты её ID (CID).
CMD12[31:0] мусорR1bЗаставляет карту прекратить передавать данные в режиме считывания нескольких блоков.
CMD13[31:0] мусорR2Просит карту отправить её статус.
CMD16[31:0] Длина блокаR1Для карт SDSC, длина блока выставляется этой командой. Для SDHC и SDXC карт, длина блока фиксирована и равна 512 байт. Длина команды CMD42 выставляется этой командой.
CMD17[31:0] адрес данныхR1Считывает блок размером выставленным командой CMD16.
CMD18[31:0] адрес данныхR1Передача блоков данных из карты хосту до тех пор, пока хост не отправит команду
CMD12.
CMD24[31:0] адрес данныхR1Записывает блок данных размером выставленным командой CMD16.
CMD25[31:0] адрес данныхR1Непрерывно записывает блоки данных до тех пор пока карта не отправит байт "stop tran"
CMD27[31:0] мусорR1Программирует биты регистра CSD.
CMD28[31:0] адрес данныхR1bЕсли у карты есть функции защиты от записи, эта команда выставляет бит защиты для выбранной адресной группы,\. Свойства защиты от записи прописаны в данных карты (WP_GRP_SIZE).
SDHC и SDXC карты не поддерживают эту команду.
CMD29[31:0] адрес данныхR1bЕсли у карты есть функции защиты от записи, эта команда очищает бит защиты выбранной адресной группы.
SDHC и SDXC карты не поддерживают эту команду.
CMD30[31:0] адрес для защиты от записиR1Если у карты есть функции защиты от записи, эта команда просит карту отправить бит защиты выбранной адресной группы. SDHC и SDXC карты не поддерживают эту команду.
CMD32[31:0] адрес данныхR1Выставляет адрес первого блока, который нужно стереть.
CMD33[31:0] адрес данныхR1Выставляет адрес последнего блока, который нужно стереть. 
CMD38[31:0] мусорR1bочистка всех блоков от первого до последнего выбранных предыдущими командами.
CMD42[31:0] Зарезервировано
(все '0')
R1Используется для выставления/сброса пароля или блокировки/разблокировки карты. Размер блока определяется командой CMD16.
CMD55[31:0] мусорR1Сообщает карте что следующая команда, это команда особого назначения.
CMD56[31:1] мусор
[0]: RD/WR
R1Используется для  записи или считывания блока данных для стандартных или дополнительных команд.
В случае SDSC карт, размер блока определяется командой
CMD16. Для SDHC и SDXC карт длина блока фиксирована и равна 512 байт.
CMD58[31:0] мусорR3Считывает регистр OCR.
CCS бит, выставлен в OCR[30].
CMD59[31:1] мусор
[0:0] CRC настройка
R1Включает "1" или выключает "0" CRC.

CRC

Вместе с командой нам нужно отправлять CRC код для проверки, он вычисляется с помощью алгоритма. Если использовать библиотеки для работы с SD картами и FAT, то там это уже всё реализовано. Если работать с нуля, нужно для каждой команды вычислять CRC код. Разберёмся, как это делать.

В SD картах используются CRC7 и CRC16 проверки, 16 редко используется, но смысл там тот же что и в 7, так что рассмотрим её, если нет желания возиться с CRC кодами их можно отключить командой CMD59.

В алгоритме используется что-то похожее на деление многочленов столбиком. Нам нужно взять содержимое посылки и бит определяющий от кого идёт передача (хост- карта или наоборот), у нас есть двоичное число из 39 бит, это число сдвигаем влево 7 раз (дописываем 7 нулей справа), т.к. CRC7. В различных версиях CRC нужно сдвигать на разное число бит. Теперь у нас есть число из 46 бит, его нужно разделить (по правилам двоичной арифметики) на полином данной CRC версии. У CRC это x^7 + x^3 + 1.  В двоичном коде - "10001001". Полученный после деления остаток и будет нашим CRC7 кодом, который нужно передать вместе с сообщением.

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

Подсчитаем CRC код для команды CMD0:

  • берём содержимое команды вместе с её индексом и битом, обозначающим от кого передача (0 в данном случае) получаем (индекс команды 0, аргумент 0):                                                          1 000000 00000000000000000000000000000000 
  • Сдвигаем число на 7 разрядов влево, получаем                                                                                                      1 000000 00000000000000000000000000000000 0000000
  • Полученное число делим на CRC7 полином "10001001" и берём остаток:                           1 000000 00000000000000000000000000000000 0000000 % 10001001 = 1001010
Согласно даташиту именно такой CRC код у команды CMD0.


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

Нам нужно записать наше число, сдвинуть его на 7 разрядов влево, затем сдвинуть наш полином таким образом, чтобы его крайняя левая "1" была под крайней левой "1" нашего большого числа. Затем произвести между двумя числами XOR (если разные значения в 1 разряде выставить 1, иначе 0). С полученным числом проделать то же самое (сдвинуть под крайнюю единицу полином, XOR) до тех пор пока у изначального числа (пока мы его не сдвигали влево на 7 разрядов) не будут все 0, иначе до тех пор пока биты [max : 8] не будут равны "0". Оставшиеся 7 бит и есть наш остаток.

Рассмотрим это на примере:
CRC-7 полином = [1 0 0 0 1 0 0 1]
Сообщение (после сдвига на 7 разрядов) = [1 1 0 0 0 0 0 1] [1 0 0 0 0 0 0 0] 0 0 0 0 0 0 0

Рис.7 Подсчёт остатка
Остаток 0x17.

Можно найти готовый код для подсчёта CRC7 значений, есть довольно много реализаций, к примеру: вот и вот.

Ответы "Responce"

Теперь рассмотрим, какие есть форматы ответов SD карты на команды (Responce).

Чаще всего используется ответ R1, он состоит из 8 бит, старший бит всегда выставлен в "0". Ошибка обозначается "1" в соответствующем бите. Структура R1 выглядит следующим образом:

Рис.8 Формат ответа R1
  • (0 бит) in idle state - карта в состоянии ожидания и проводит процесс инициализации.
  • (1) erase reset - команда отмены удаления данных из памяти пришла до того как удаление началось.
  • (2) illegal command - неизвестный код команды.
  • (3) com crc error - ошибка проверки CRC кода.
  • (4) erase sequence error - ошибка в последовательных командах удаления.
  • (5) address error - неверный адрес.
  • (6) parameter error - неверный параметр.
R1b формат такой же, как R1, но с дополнительной информацией о готовности карты, это информация может быть произвольного размера. "0" означает что карта "занята", любое значение отличное от 0 означает, что карта готова к следующей команде.

В ответ на CMD13, карта отправляет R2, в дополнении к ошибкам в R1 здесь добавлено несколько новых:

Рис.9 Формат ответа R2
  • (0 бит) Card is locked - карта заблокирована пользователем.
  • (1) Write protect erase skip | lock/unlock command failed - была попытка удалить блок, защищённый от записи, либо был введён неправильный пароль при блокировке/разблокировке карты.
  • (2) Error - стандартная или неизвестная ошибка произошла во время операции.
  • (3) CC error - ошибка контроллера карты.
  • (4) Card ECC failed - ECC код был использован, но не получилось восстановить данные.
  • (5) Write protect violation - была попытка записи в блок, защищенный от записи.
  • (6) Erase param - неправильно выбраны адреса для очистки блоков.
  • (7) Out of range - параметр выходит за границы карты.
Формат R3 состоит из 40 бит, первый байт это формат R1, затем следует значение регистра OCR.

Формат R7 используется когда мы меняем напряжение, но SPI может работать только при 3.3 В. В отличие от SD интерфейса, так что R7 можно не рассматривать.


При работе с блоками данных используются особые "маркеры" (token) для сообщении о ходе передачи данных.

Рис.10 Запись нескольких блоков в карту
На каждый записанный блок карта посылает ответный маркер:

Рис.11 Маркер записи блока данных
Значение битов "Status" следующее:
  • "010" - данные приняты.
  • "101" - данные отклонены из-за ошибки в CRC коде.
  • "110" - данные отклонены из-за ошибки в процессе записи.
Также есть маркеры определяющие начало и конец записи:

Рис.12 Маркер начала записи/считывания 1 блока, считывания нескольких блоков

Рис.13 Маркер начала записи нескольких блоков
При работе с несколькими блоками маркер посылается каждый раз перед передачей очередного блока данных.

Рис.14 Маркер обозначающий конец записи.
Последний маркер используется только при записи нескольких блоков, при считывании используется команда CMD12.

Если карта не сможет прочитать блок данных, она отправит маркер ошибки, там обозначены те же биты, что и в R2:

Рис.15 Маркер ошибки считывания.
Биты обозначающие ошибки как в маркерах так и в ответах R* очищаются после того как они были переданы хосту, их не нужно очищать вручную.

Внутренние регистры

В режиме SPI мы можем считать 3 регистра OCR CID и CSD. Рассмотрим, что в них хранится.

Регистр OCR отвечает за доступные режимы питания карты:

Рис.16 Содержимое регистра OCR.
В битах [23:15] единица означает, что карта поддерживает данное напряжение.

Регистр CID хранит информацию от производителя:

Рис.17 Содержимое регистра OCR
Регистр CSD состоит из 127 бит и хранит техническую информацию, максимальный ток, скорость записи, размер максимального блока для работы и т.д. Он может отличаться от карты к карте в зависимости от размера. Не буду его расписывать в документации на это потратили 11 страниц.

У карты больше регистров, но они не доступны в режиме SPI.

Заключение

Этой информации должно хватить, чтобы понять, как работать с SD картой. Но я рекомендую использовать готовые библиотеки для работы с FAT файловой системой, конечно нужно больше памяти и RAM, но зато не нужно разбираться с работой на низком уровне, остаётся лишь использовать готовые функции.

Популярная библиотека FatFs. Также у них есть версия меньшего размера для 8 битных МК Petit FAT.





Источники:
en.wikipedia.org
ru.wikipedia.org
sdcard.org -- Документация
my-cool-projects.blogspot.com -- CRC7
users.ece.cmu.edu/~koopman -- CRC7
www.pololu.com -- CRC7

5 комментариев :

  1. Каким образом, Рис.7, b1110100 = 0x17 ?!
    Спасибо.

    ОтветитьУдалить
  2. Заказал на ибее карты сандиск 8Гига класс 4 и нифига они не работают по SPI, компьютер их видит и читает-пишет как надо..Карты старые 64Мб сандиск и 32Гб самсунг прекрасно работают по SPI ,использую Fatfs

    ОтветитьУдалить