Скачал исходник.
Вывалились ошибки:
stm32 nok1100 disp.axf: Error: L6218E: Undefined symbol USART_Cmd (referred from main.o).
stm32 nok1100 disp.axf: Error: L6218E: Undefined symbol USART_GetFlagStatus (referred from main.o).
stm32 nok1100 disp.axf: Error: L6218E: Undefined symbol USART_Init (referred from main.o).
stm32 nok1100 disp.axf: Error: L6218E: Undefined symbol USART_ReceiveData (referred from main.o).
stm32 nok1100 disp.axf: Error: L6218E: Undefined symbol USART_SendData (referred from main.o).

Добавил в проект библиотеку для работы с USART — скомпилилось (неплохо было бы все же об этом упомянуть в статье, а?), но карта не инициализируется. Пробовал менять местами выводы MISO и MOSI — результат нулевой…

В проекте использую Standard Peripheral Library + заметно упоминание в тексте и коде об UART’е, думал это само собой влечет использование USART библиотеки. Используемые библиотеки из SPL перечислил. Чуть позже думаю выложить проект целиком.
Есть ли диалог по UART? Что за карта? Она живая? Пробовали ли другие карты?

Ситуация — человек, ни разу ДО того не сталкивавшийся с STM32, находит в интернете Ваши статьи, начинает их читать. Ставит необходимый софт и читает Ваши статьи, последовательно одну за другой, попутно компилируя примеры и проверяя работоспособность на плате STM32VL Discovery (как это у Вас в первой статье сказано). В первом примере все разжевано и разложено по полочкам — какие файлы библиотек куда положить, как структуру проекта оформить, какие опции в свойствах проекта включить/выключить. Все понятно, все прекрасно. Статья для новичков. В статье про подключение SD-карт — полная противоположность, как в той песне — «догадайся, мол, сама…». Мало того — на схеме подключения карты указаны пины процессора, хотя по идее должны быть указаны пины разъемов платы STM32VL Discovery (коли уж повествование началось на ее базе). Думаю, что в статьях должна прослеживаться некая преемственность, раз уж начали опираться с первой статьи на STM32VL Discovery — на ее базе и остальное повествование продолжайте. Так будет намного проще для читающих Ваши статьи.

По УАРТу данные в терминалку падают, в обратную не пробовал.

В процессе написания ответа взял другую карту — не инитится. Поменял местами выводы MISO и MOSI на разъеме дискавери — заработало. Так что в схемке ошибочка наблюдается. Кстати, вот тут http://mycontroller.ru/stm32-sd-card-podklyuchenie/ схема подключения правильная. Подключение карты к плате STM32VL Discovery получается таким: карта пин 1 — PA4, карта пин 2 — PA7, карта пин 5 — PA5, карта пин 7 — PA6. Заработала одна старая карточка (16 Мб) и новая (2 Гб). Старая карточка 8 Мб (раритет, однако 😉) от видеокамеры JVC инициализироваться не захотела.

Пожелание: если не затруднит, описывайте подробнее добавления/изменения в проекте и т.п.. Опытным товарищам это можно и не читать, а неопытным будет ясно и не будут возникать всякие глупые вопросы. 🙂

ЗЫ. Блоки данных пишутся/читаются на/с карты. Спасибо за статью!

Там идет специальная обучающая серия и она пронумерована, вот в ней пытаюсь максимально разжевать. Обычные же статьи могу быть, как обзорные/поверхностные, т.е. самое основное, т.к. материала очень много и всего не упомянуть, а могут быть так же развернутыми. К примеру: эта статья появилась раньше, чем рассказал об UART’е и SPI, не откладывать же материал на потом? да и когда будет этот потом и будет ли вообще?

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

Ответ на пожелание — постараюсь.

В схеме ошибка подтвердилась, вечером информацию обновлю. Спасибо, что заметили.

Спасибо, все отлично работает на F103RB, кингстон сдхц на 4гб скушалась без приреканий. Правда я не сразу заметил, что у меня на плате PA4 — это слейв-селект не для карточки, отладка слегка затянулась))

Вы написали что: SD_init() – инициализация SPI и карты памяти
в случае неудачи возвращает 0.
Разве не наоборот, в случае успешной инициализации, возвращается ноль, иначе - единица?

Ошибся, исправим. Спасибо, что заметили.

А вы пробовали SDHC карту? Она инициализируется но не получается не прочитать не записать, плюс не понятно как ее адресовывать,что то в интернете толком ни чего не нашел

Пробовал SDHC Qumo 8GB — чтение успешно, запись не помню проверял ли. Другие карты не пробовал.

странно, у меня кингстон SDHC на 2гб не пишется и не читается

Ещё может от карточки зависеть, т.е. повезет — не повезет.

Скажите, пожалуйста, а можно ли как-то прочитать данные, записанные на sd карту (без файловой системы), с компьютера. Я имею ввиду использование каких-либо программ для восстановления данных с флешек и пр? Дело в том, что с FatFS постоянно возникают проблемы (с низкоуровневыми функциями, скорее всего). Или чинить их или искать программы.

Такого ПО не искал — на примете нечего нет.

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

Было бы куда интереснее найти/сочинить универсальный драйвер для картридеров которые есть сейчас в любом буке для использования слотов CD/MMC вкачестве универсального SPI интерфейса / программатора, естественно с эмуляцией СОМ порта потому как практически весь софт программаторов работает через СОМ/LPT и практически все железки теперь имеют биос/загрузчики через SPI или общается через него, вот тогда бы вам народ памятник поставил (хотя бы виртуальный).
Удивляюсь почему до сих пор никто такого драйвера не написал, не думаю что это невозможно или архи сложно — «хардвару обходим сфтварой а софтвару — хардварой» , однако сколько не рыл в инете такого драйвера не нашел, а самому писать — слабо.

Хорошая статья! Можно увидеть весь проект с описанной инициализацией карточки?

А к статье разве он не прикреплен?

Хм… думал все основные файлы проекта выложил, вечерком постараюсь добавить.
На счёт сайта — да, печаль, хороший был ресурс, но уже, как с год помер и полноценной копии материалов с того ресурса нигде найти не могу.
Тот сайт, по библиотеке FatFS, на самом деле занимался переводом аналогичного ресурса, библиотеку можете скачать от туда — FatFs — Generic FAT File System Module , плюс там есть ещё её обновление от 14 ноября текущего года.

Все основные файлы проекта выложены, остаётся только standard peripheral library поставить.

Добрый вечер!
Интересует вопрос создания и записи данных в текстовый файл на флешке с периодичностью в 100кГц.

Есть камера DCR-DVD203E ПИШИТ на мини сд диск …задача убрать сд и установить блок записываюший на флеш карту 32-64 ГБ.. ЕСТЬ ВНАЛИЧИИ НО КАК СДЕЛАТЬ???? ВОПРОС— СМОГЛИ БЫ СДЕЛАТЬ И ЦЕНА ВОПРОСА

По обоим вопросам не чем помочь не смогу.

«Если вы обнаружили, что обратная связь не работает, просьба сообщить об этом через комментарии».
Сообщаю 😉

Сайт работает исправно, всё что приходит вижу. Это меня завалило работой по всем фронтам. И поэтому уже с пару недель на сайт не отвечаю. Скоро одни дела завершу и на недели со временем должно будет быть чуть посвободнее, отвечу на всё, что накопилось.
1. В идеале лучше использовать простые карты памяти, не HD. Но статистики по картам у меня нет.
2. Перепроверьте распиновку карты памяти по другим ресурсам в интернете, кто-то когда то замечал у меня ошибку, не помню подтвердил её и исправил или нет.

Понятно, извиняюсь за нетерпение 🙂
Спасибо за ответ, если найду ошибку — напишу в чем было дело.

Хм, проблема оказалась в интересном месте. На одном форуме нашел статью человека, у которого была схожая проблема. На ножках, задействованных в SPI1, также висит JTAG. Так вот оказалось, что он включается автоматически после Reset, несмотря на то, что я нигде его не включаю как альтернативную функцию. В статье предлагалось решение этой проблемы (), я же просто использовал другой SPI. После чего стало возможным отослать 74 синхроимпульса.
P.S. Правда дальше все равно не заработало 🙂 После отсылки запроса на программный сброс(с пустым аргументом) ответа на команду не приходит 🙁 Будем искать еще ошибки.

И это снова я. И опять ошибка возникала из-за неправильной работы ножки. В качестве ножки SlaveSelect я выбрал ту, которая указана в ДШ как NSS для SPI2. Настроил ее на ее на управление программно. Сброс/установку ножки осуществлял с помощью функции SPI_NSSInternalSoftwareConfig() (так написано в библиотеке SPL). Так вот ножка не изменяла своего значения! Более того, когда я перестал использовать эту функцию и стал напрямую менять бит через регистр порта BSRR, то ножка все равно постоянно была выставлена! Причины я не знаю. Возможно, что все дело в режиме программного управления NSS. Поэтому когда я стал использовать для SlaveSelect другой пин, то все заработало — ответы стали приходить.
P.S. Правда дальше все равно не заработало….Опять… 🙂
Поэтому снова обращение за советом к уважаемому автору (когда он разгребётся с работой) 🙂
Ситуация такая: на команду SEND_IF_COND приходит ответ 0x01 — значит флешка v2 — вроде бы логично — флешка куплена неделю назад и навряд ли она v1. Идем дальше: отсылаю ACMD41, т.е. APP_CMD (получаю ответ 0x01 — вроде тоже все норм) и APP_SEND_OP_COND — получаю ответ 0x05. Имеем illegal command error, значит флешка не воспринимает такую команду. Решил попробовать отослать просто CMD1(вдруг карта версии v1), т.е. SEND_OP_COND — получаю ответ 0x01. Т.е. карточка никак не хочет инициализироваться, т.к. ждем 0x00. Может ли быть еще другие варианты последовательности инициализации?

Решил проблему 🙂 Долго гуглил, находил различные решения (причем некоторые были противоположны друг другу:О) но ничего не помогало. Увидел вот эту ссылку http://stackoverflow.com/questions/2365897/initializing-sd-card-in-spi-issues — решил что это не поможет и стал дальше искать, а зря…. Так как позже, отчаявшись, решил пробовать все решения и добавил несколько команд spi_read(); в начале функции SD_sendCommand() и карточка наконец инициализировалась 🙂
P.S. В той же ссылке есть другой совет — «send ACMD41 with the bit set for the voltage you’re supplying the card with», и этот человек утверждает, что ему это помогло, хотя в Physical Layer Simplified Specification Version 4.10 говорит про аргумент ACMD41 так:
Argument Reserved bit HCS Reserved bits
Command Description — Sends host capacity support information and activates the card’s initialization process. Reserved bits shall be set to ‘0’
что явно противоречит выше сказанному. Так что не всем советам стоит верить. Возможно, что и моим тоже 🙂

И снова здравствуйте! 🙂 В ходе использования карточкой возник вопрос: а как отследить что мы пишем в неверный сектор? Я имею ввиду, например, на карте 1000000 секторов, а мы пытаемся записать в 1000001. Карта это спокойно воспринимает — присылает valid-ный R1-response на команду (0x00) и valid-ный Data-response после приема данных (0xE5, но это тоже самое что 0x05, ведь значимые только младшие 7 бит).
P.S. Причем при попытке считать из неверного сектора R1-response 0x40, т.е. Parameter Error.

stm32f103 контроллер? У них столкнулся с подобной проблемой, только в другом ключе и использовал не SPI. Решение проблемы нашел, в рамках одного из будущих материалов опишу его на сайте.
По остальным вопросам отправил Вам письмо.

Alex_EXE, благодарю за письмо. Всеми ссылками, которые Вы указали, активно пользовался и до этого 🙂 Кроме первой 🙂 Но она тоже выглядит полезной и ее стоит добавить в закладки. Я так понимаю Вы давали ссылку в этой статье на нее, но только на нерабочую версию, поэтому решил добавить ее в комментарии: http://mycontroller.ru/old_site/category/vneshnie-ustroystva/karta-pamyati-sd/default.htm
Теперь по поводу чем дело кончилось:) К сожалению, ответа на него я не нашел 🙁 Нет, проблему-то я решил, правда другим способом, но мне кажется что это лишние действия и было бы лаконичнее и красивее, если бы карта выдавала Parameter Error на попытку записать в неверный сектор. Возможно, где-то в закоулках даташита, все-таки скрывается ответ и его нужно просто очень внимательно прочитать 🙂 А вот об этом речь пойдет дальше, в следующем комменте 🙂

Так вот. Как я думал решить проблему. Чтобы не писать в несуществующий сектор нужно просто знать их количество и проверять не вышли ли мы за предел 🙂 Знаю, банально, но решать проблему как-то надо 🙂
Есть такой регистр CSD (The Card-Specific Data register) В нем определенные биты отвечают за размер карты C_SIZE, при чём: memory capacity = (C_SIZE+1) * 512KByte. Т.е. отсюда можно посчитать количество секторов. Читается он с помощью CMD9 (SEND_CSD). Нашел наверно с десяток ссылок (например https://my.st.com/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/Attachments/18065/stm32_eval_spi_sd.c) в которых был написан один и тот же(по смыслу, а не скопипащенный 🙂) алгоритм чтения этого регистра. Суть сводилась к следующему:
Reading the contents of the CSD register in SPI mode is a simple read-block transaction (при чем в даташите есть точно такая же строка, но абзац на этом не заканчивается, но видимо дальше никто не читает 🙂). В итоге, во все ссылках такая последовательность действий:
CS в ноль -> шлем команду CMD9 с пустым аргументом -> ждём R1(0x00) -> ждём Data Token(0xFE) -> читаем 16 байт регистра -> читаем 2 байта CRC -> CS в единицу. Всё, данные получили, осталось только их распарсить.
ОК, пишу код, запускаю. На команду отвечает R1(0x00), всё норм. Вместо Data Token(0xFE) приходит 0x7F, а дальше идут 0xFF. Приехали. Начинаю гуглить, искать что означает 0x7F и кто с этим сталкивался. Вообщем потратил кучу времени и всё в пустую. Потом смотрю на свой код команды чтения сектора и стоп! — в CMD17 сначала шлем команду, а потом CS в ноль, а тут наоборот почему-то, хотя везде сказано что что CMD9 та же самая CMD17. Думаю дай-ка попробую так. Ииии…нет 🙁 всё равно не получаем 0xFE. НО, если раньше дальше шли 0xFF, то теперь какие-то непонятные байты. Странно, непонятно…И тут у меня появилась мысль и полез я в даташит. И что же я там увидел, в следующей части абзаца, после строки про simple read-block transaction. А вот что:
The card will respond with a standard response token followed by a data block of 16 bytes suffixed with a 16-bit CRC.
Т.е. в даташите для команды CMD9 ни про какой Data Token не сказано! Т.е. сразу после R1(0x00) идут байты регистра! Вот что значат эти непонятные байты. Проверил — действительно, 18 байт (16 + CRC), а потом уже пошли 0xFF. Распарсил байты и проверил провел по даташиту все сходится, это он — CSD. И размер карты тоже сошелся. Ну дальше дело техники посчитать количество секторов.
Так что вот, мой так сказать опыт по этому вопросу, может кому пригодится, чтобы не трать (как мне дурачку) такое количество времени на поиски ошибок на пустом месте 🙁Alex_EXE пишет 22.10.2016 в 20:28

Кто знает, эта библиотека подойдет чтобы подключить к discovery флешку от mxic 25l на 32Мб?

Как видно из рисунка после передачи кадра команды необходимо продолжать чтение байтов (Ncr) от microSD до получения ответа (R1), при этом уровень CS должен быть активным "0".

В зависимости от индекса команды ответ может быть не только R1 (см. набор основных команд) на CMD58 ответ R3 (R1 и завершающее 32-битное значение OCR), а некоторым командам нужно больше времени NCR и они ответ будет R1b . Это ответ R1, за которым идет флаг занятости (сигнал на линии "DO" удерживается картой в низком уровне, пока продолжается внутренний процесс). Контроллер хоста должен ждать окончания процесса, пока "DO" не перейдет в состояние высокого уровня (т.е. дождаться 0xFF). А так же R2 при запросе состояния регистра STATUS.

Ответ R1 содержит 1 байт, его структуру можно посмотреть в таблице ниже. Ответ R2 состоит из двух байт, первый байт R1 и второй R2 (см. таблицу структуры R2). А ответ R3 соответственно из 5 байт.


Ответ R1 при значении 0х00 означает успешное завершение команды, иначе будет установлен соответствующий флаг.

Структура ответа R1.


Структура ответа R2.


Инициализации в режиме SPI.

После сброса и подачи питания карта по умалчиванию устанавливается в режим работы по протоколу MMC (Serial Peripheral Interface), для перевода в режим SPI необходимо сделать следующее:

  1. После достижения питания 2.2 В, подождать не менее миллисекунды, установить на линиях DI и CS высокий уровень и выдать около 80 импульсов на вывод CLK. После такой процедуры карта будет готова принять родную команду.
  2. Послать команду CMD0 (программный сброс). Карта должна ответить (R1) с установленным битом ожидания (0x01).
  3. Послать команду CMD1 (для начала инициализации карты). Ждать ответа 0х00 для подтверждения завершения процесса инициализации.

Напомню, что команда CMD0 должна содержать корректное поле CRC. Рассчитывать нет смысла, так как аргументов в этой команде нет, по этому оно постоянно и имеет значение 0х95. Когда карта войдет в режим SPI, функция CRC будет отключена и не будет проверяться. Опция CRC может быть снова включена командой CMD59.

В результате команда CMD0 будет выглядеть так: 0х40,0х00,0х00,0х00,0х00,0х95.

  • индекс команды - 0х40.
  • аргумент- 0х00,0х00,0х00,0х00.
  • CRC-0х95.

Что касается 80 импульсов, то их можно сформировать передавая по SPI значение 0хFF 10 раз подряд при установленных высоких уровнях на линиях DI и CS.

После простоя более 5 мс карта памяти переходит в энергосберегающий режим, и способна принимать только команды CMD0, CMD1 и CMD58. По этому процесс инициализации (CMD1) необходимо практически каждый раз повторять при чтении/записи блока данных или делать проверку состояния карты.

Для SDC-карт в случае отклонения команды CMD1 рекомендуется использовать команду ACMD41.

Сам процесс инициализации может занять относительно длительное время (в зависимости от объема карты) и может достигать сотен миллисекунд.

Чтение и запись блока данных.

По умолчанию в режиме SPI обмен между микроконтроллером и картой ведется блоками по 512 байт, по этому для записи даже одного байта придется сначала прочитать весь блок и изменив байт перезаписать обратно. Размер блока может быть изменен в регистре CSD карты памяти.

Воизбежания ошибки адресации при выполнении команд чтения/записи необходимо что бы адрес указывался четко начала сектора. Для этого можно сбрасывать бит "0" 3 байта адреса сектора, т.е. делать его четным, а младший всегда должен иметь значение 0х00.

Чтение блока данных.

Алгоритм чтения блока данных следующий:

  • После подтверждения инициализации передаем команду CMD17 (ответ R1), с адресом необходимого сектора.
  • Передаем 0xFF до получения стартового байта 0xFE .
  • Принимаем блок данных (по умалчиванию 512 байт) и 2 байта CRC.

Значение CRC не обязательно, но процедура принятия (передача 0хFF от МК) необходима.

Чтение блока.


Запись блока данных.

Алгоритм записи блока данных следующий:

  • Если простой карты был более 5 мс передаем команду CMD1 (ответ R1).
  • После подтверждения инициализации передаем команду CMD24 (ответ R1), с адресом необходимого сектора.
  • Передаем стартовый байт 0xFE .
  • Передаем блок данных (по умалчиванию 512 байт) и 2 байта CRC.
  • Получаем байт подтверждения записи.
  • Ждем окончания записи (изменения байта 0х00).

Блок данных может быть меньше 512 байт при изменении длины блока командой CMD16.

Значение CRC не обязательно, но процедура передачи любыми значениями необходима.

Оценку простоя можно программно и не делать, а сразу давать команду инициализации. При программной реализации столкнулся с некорректной записью, почему то все байты были записаны в сектор со сдвигом влево. Проблему удалось решить, только передавая стартовый бит (0xFЕ) два раза.

Запись блока.


Байт подтверждения при записи блока данных.


Запись/чтение нескольких блоков подряд.

При помощи команд CMD18 , CMD25 можно прочитать/записать несколько блоков подряд или так называемое многоблочное чтение/запись. Если не было задано количество блоков, то процесс чтения/записи можно остановить командами CMD12 при чтении, а так же передачей маркера "Stop Tran " при записи соответственно.

Практическое применение.

Практическое применение карт памяти довольно широко. В последней своей конструкции задействовал microSD для записи показаний с различных датчиков (температуры, сигнализации) в течении дня каждый час. Данные сохраняются следующим образом:

  • Год берется последние две цифры - это соответствует первому (главному) байту адреса сектора карты памяти.
  • Месяц, две цифры - это соответствует второму, старшему байту адреса сектора карты памяти.
  • День, две цифры умножаются на 2 (во избежание наезда вне границы сектора) - это третий, средний байт адреса сектора карты памяти.
  • Младший, четвертый байт соответственно всегда "0".

В результате упрощается поиск данных по дате, достаточно просто перевести запрос в адрес сектора и выполнить чтение с карты. При таком методе данные можно хранить в течении нескольких лет. Правда есть и недостатки, остается достаточно много неиспользованного места. Хотя при желании можно использовать для других задач.

Кому надо скину фрагмент кода на ассемблере для 18 пиков.

Вопросы можно задать на ..

Михаил Русских

Приводится краткая информация о SD-картах, даются основы принципа работы шины SD, поясняется порядок использования драйвера stm324xg_eval_sdio_sd, предназначенного для упрощения связи микроконтроллеров семейства STM32 с SD-картами

Некоторые встраиваемые системы должны иметь возможность хранения больших объемов информации. Например, регистраторы различных физических величин и параметров (ток, напряжение, температура, географические координаты) должны сохранять полученные с датчиков показания на определенном носителе, который впоследствии можно было бы извлечь из устройства и подключить к компьютеру для просмотра данных в удобном для пользователя виде. Для решения такой задачи наиболее очевидным кажется использование USB флеш-накопителей. Они широко распространены на рынке и позволяют хранить большие объемы информации. Но для некоторых малых встраиваемых систем первостепенным фактором является компактность, и габариты этих накопителей могут оказаться чрезмерными. Кроме того, не все микроконтроллеры оснащены модулем USB, и разработчик системы может довольно долго разбираться с USB-стеком, что может увеличить время работы над проектом. В связи с этим хорошей альтернативой USB является использование SD-карт, которые очень компактны и позволяют обмениваться данными с помощью распространенного интерфейса SPI или специального интерфейса для SD-карт.

Карты памяти SD (Secure Digital) разработаны, в основном, для применения в портативных устройствах. Сегодня на рынке существует большое количество моделей, предоставляемых такими компаниями как SanDisc, и способных записывать с зависящей от класса скоростью данные объемом от 8 МБ до 2 ТБ. SD-карты, в отличие от MMC-карт, помимо обычной области хранения данных имеют специальную защищенную область, которая недоступна обычному пользователю. Для того чтобы устройство смогло прочитать записанную на SD-карту информацию в виде файлов, эта карта должна иметь определенную файловую систему. Наиболее предпочтительной в данном случае является система FAT, поскольку количество циклов записи/чтения для SD-карт ограничено, а системы вроде NTFS и ext являются журналируемыми, то есть предполагающими частые опросы.

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

Таблица 1. Функциональное назначение выводов SD-карт

Интерфейс SPI

Интерфейс SD

Карты SDC

Карты miniSD

Карты microSD

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

Передача данных по шине SD

Связь с SD-картой осуществляется по 6 информационным линиям: 4 линиям данных (DAT0, DAT1, DAT2, DAT3), линии синхронизации (CLK), линии передачи команд (CMD). При подключении к контроллеру линии данных и линия передачи команд должны быть подтянуты к питанию, как показано на Рисунке 2.

Запись и чтение данных выполняются поблочно. Обычно блок состоит из 512 байтов. Передача данных и служебной информации основана на модели взаимодействия «команда-ответ». Перед передачей или приемом данных хост-контроллер должен отправить соответствующую команду по линии CMD. По этой же линии от SD-карты должен прийти ответ, после которого может быть осуществлено требуемое действие. Стандарт SD поддерживает присутствие нескольких ведомых на шине, в таком случае хост-контроллер может посылать команды широковещательно. Команда имеет четкую структуру, состоящую из 48 бит: 1 стартовый бит, 1 бит передачи (установлен в 1), 6 бит для индекса команды, 32 бита для аргумента, 7 бит для контрольной суммы и 1 стоповый бит. Ответ может иметь такую же 48-битную структуру (в этом случае бит передачи установлен в 0) или состоять из 136 битов (т. н. формат длинного ответа): 1 стартовый бит, 1 бит передачи (установлен в 0), 6 зарезервированных битов, 127 битов содержимого регистров CID (Card Identifiction Register) или CSD (Card Specific Data Register) и контрольной суммы, 1 стоповый бит.

В начале работы с картой хост-контроллер перезагружает карту с помощью команд GO_IDLE_STATE (CMD0) и IO_RW_DIRECT (CMD52) . Полный список команд с описанием можно найти в (приложение A). После перезагрузки карта восстановит свои настройки по умолчанию, и ее адрес будет равен RCA = 0x0001. Если к хосту подключено несколько карт, то с помощью команды SET_RELATIVE_ADDR (CMD3) он задает каждой карте собственный адрес. Также в начале работы выполняется проверка поданного на карту напряжения. Напряжение карты должно быть в строго установленном диапазоне от 2.7 до 3.6 В. В связи с этим хост-контроллер с помощью команды SEND_OP_COND (CMD1 или ACMD41 ) обязан узнать текущее напряжение на карте и прекратить с ней работу, если оно не входит в требуемый диапазон. Таков общий принцип инициализации карты, для выполнения которой, в зависимости от типа карты (MMC, SD, SD I/O), могут использоваться другие команды и выполняться дополнительные шаги, поэтому при реализации низкоуровневого интерфейса нужно внимательно прочитать документацию на карту и изучить команды CMD.

Во время записи хост передает карте один или несколько блоков, используя команду WRITE_BLOCK (CMD24) или WRITE_MULTIPLE_BLOCK (CMD25) , соответственно, при этом в конце каждого блока хост записывает контрольную сумму. Карта, запись данных для которой разрешена, всегда будет в состоянии принять блок(и), но если контрольная сумма не совпадет, то карта сообщит об ошибке и не запишет текущий блок в свою память, а в случае пакетной передачи следующие блоки будут проигнорированы.

Передать информацию хосту, то есть выполнить чтение, также можно одним блоком с помощью команды READ_SINGLE_BLOCK (CMD17) или пакетом из нескольких блоков с помощью команды READ_MULTIPLE_BLOCK (CMD18) . В случае пакетной передачи хост может прервать чтение в любое время, отправив команду STOP_TRANSMISSION (CMD12) . Если в ходе передачи нескольких блоков внутренний контроллер карты зафиксирует ошибку, то он остановит передачу данных хост-контроллеру, но оставит карту в режиме передачи данных. В таком случае хосту придется принудительно завершить чтение с помощью команды CMD12 , при этом в ответе на эту команду карта сообщит об ошибке чтения.

Организация правильной работы шины SD является достаточно трудоемким процессом, поскольку помимо общих ситуаций, описанных выше, необходимо предусматривать различные нюансы в поведении карты и реализовывать дополнительные обработчики ошибок. Поэтому наиболее рациональным решением будет использование специальных библиотек, позволяющих разработчику не вдаваться в подробности передачи данных на аппаратном уровне и существенно сократить время разработки. Одной из таких библиотек, о которой пойдет речь ниже, является библиотека-драйвер stm324xg_eval_sdio_sd для популярных микроконтроллеров , позволяющая существенно упростить работу с модулем SDIO, предназначенным для взаимодействия с SD-картами.

Драйвер stm324xg_eval_sdio_sd

Этот драйвер представляет собой библиотеку функций, с помощью которых можно реализовать высокоуровневый интерфейс для обмена данными с SD-картой. Драйвер поставляется для работы с отладочной платой STM324VG (UtilitiesSTM32_EVALSTM3240_41_G_EVAL), но после изменения функций, связывающих эту библиотеку с низкоуровневым интерфейсом SDIO он может функционировать со многими микроконтроллерами семейства STM32, в составе которых имеется модуль SDIO. Из предыдущего предложения следует, что к проекту должны быть подключены файлы из стандартной библиотеки Standard Peripheral Library , обеспечивающие функционирование модуля SDIO на низком уровне. Например, для микроконтроллеров серии STM32F4xx это будут файлы stm32f4xx_sdio.c и stm32f4xx_sdio.h .

Перед использованием функций драйвера программист должен связать этот драйвер с аппаратной частью, то есть настроить линии модуля SDIO. Для этого ему нужно в основном файле своей программы main.c создать функции SD_LowLevel_Init() (для включения модуля SDIO) и SD_LowLevel_DeInit() (для отключения модуля SDIO), в которых необходимо выполнить конфигурирование этих линий. Если программист желает использовать модуль прямого доступа к памяти для увеличения скорости работы с SD-картой, то ему также нужно будет создать функции SD_LowLevel_DMA_TxConfig() и SD_LowLevel_DMA_RxConfig() , отвечающие за передачу и прием данных с помощью модуля ПДП. В качестве примера инициализации четырех указанных функций можно взять код, имеющийся в файле stm324xg_eval.c из (UtilitiesSTM32_EVALSTM3240_41_G_EVAL).

Теперь рассмотрим основные функции для работы с SD-картой, предоставляемые этим драйвером. Для того, чтобы инициализировать карту, нужно вызвать функцию SD_Init() , которая настраивает модуль SDIO, обращаясь к SD_LowLevel_Init() , проверяет тип SD-карты, получает служебную информацию из регистров CID и CSD, задает скорость передачи данных (по умолчанию 24 МГц) и устанавливает ширину шины (4 бита). Прочитать данные, полученные с карты, можно с помощью функций SD_ReadBlock() (для чтения одного блока) и SD_ReadMultiBlocks() (для чтения нескольких блоков). Чтобы записать данные на карту, используют функции SD_WriteBlock() (для записи одного блока) и SD_WriteMultiBlocks() (для записи нескольких блоков). Эти функции чтения и записи принимают три аргумента: buffer (переменная-буфер, в которую должны помещаться данные для чтения и записи), address (адрес ячейки памяти SD-карты) и block_size (всегда 512, поскольку эти функции работают только с блоками длинной 512 байтов). Для стирания определенной области памяти карты имеется функция SD_Erase() . В качестве аргументов она принимает startaddr и endaddr. В первом указывается адрес ячейки памяти, с которой начнется стирание, а во втором - адрес ячейки, на которой стирание будет завершено.

Передача данных между модулем SDIO и памятью микроконтроллера может осуществляться как обычным путем через центральный процессор, так и через блок прямого доступа к памяти. Для выбора необходимого режима нужно в файле stm324xg_eval_sdio_sd.h раскомментировать либо строчку #define SD_POLLING_MODE (обычный режим), либо строчку #define SD_DMA_MODE (режим ПДП). После любой операции записи или чтения необходимо с помощью функции SD_GetStatus() проверять, завершила ли карта текущую операцию, и готова ли она к приему или отправке новых данных. Если эта функция вернула значение SD_TRANSFER_OK, то передача завершена успешно, возврат SD_TRANSFER_BUSY означает, что канал передачи занят, а возврат SD_TRANSFER_ERROR сообщает об ошибке передачи. Все эти значения определены в структуре SDTransferState , записанной в stm324xg_eval_sdio_sd.h . Если передача информации выполнялась через блок ПДП, то дополнительно нужно проверять, завершил ли контроллер ПДП все операции по пересылке данных. Это делается с помощью функции SD_ReadWaitOperation() . Также стоит отметить, что передача через блок ПДП завершается прерыванием, поэтому программисту необходимо организовать вызов функции SD_ProcessIRQ() в обработчике прерываний модуля SDIO SDIO_IRQHandler() и вызов функции SD_ProcessDMAIRQ() в обработчике прерываний DMA2 DMA2_Streamx_IRQHandler() .

Для лучшего понимания принципа работы драйвера stm324xg_eval_sdio_sd можно воспользоваться рабочим примером из папки SDIOSDIO_uSDCard (ProjectSTM32F4xx_StdPeriph_ExamplesSDIO). С помощью этой тестовой программы можно выполнить стирание определенной области памяти карты, запись данных по определенному адресу, чтение данных по этому же адресу с последующим сравнением отправленной и принятой информации. Идентичность буфера приема и буфера отправки является свидетельством того, что модуль SDIO, шина передачи информации и SD-карта функционируют нормально.

Таким образом, можно сказать, что SD-карты являются достойными конкурентами USB флеш-накопителей, когда речь заходит о проектировании малых встраиваемых систем, требующих хранения больших объемов данных. Передавать информацию с карты и на карту можно с помощью интерфейса SPI или по шине SD, которая разработана для этих целей и обеспечивает высокую скорость передачи. Реализация связи с картой существенно упрощается при использовании специальных драйверов, облегчающих работу с аппаратной частью и предлагающих программистам простые в использовании функции. Но, к сожалению, записанные в произвольном порядке данные не будут восприняты какой-либо операционной системой, например, Windows, поэтому в следующей части будет рассмотрен порядок работы с SD-картой, имеющей файловую систему FAT.

Список источников

Всем доброго дня! Сегодня мы поговорим о подключении карты памяти SD к микроконтроллеру STM32.

Казалось бы, памяти полно у контроллеров STM32F10x, зачем там еще дополнительная, но это впечатление обманчиво) Вот, например, надо нам на дисплей вывести пару-тройку разных изображений – формат 320*240 – то есть 76800 пикселей, каждому из которых соответствует целых 2 байта. Вот и получаем около 150 кБ на одну картинку. А это немало по меркам микроконтроллера, и не факт, что две разные картинки удастся запихать в его Flash память. Или надо нам хранить большие объемы информации, данные с какого-нибудь датчика, к примеру. Да еще так, чтобы эти данные были доступны и после отключения питания. Вот тут то нам и пригодится внешняя память. И отличным решением будет SD карта памяти или MMC. К слову в этой статье мы будем проводить опыты над картой micro SD .

Для начала пара слов о самой карте памяти, точнее о ее распиновке. Выглядит все это дело следующим образом:

Итак, что тут у нас? Ну сразу видно, что выводов у нее целых восемь штук. Назначение выводов следующее (слева направо):


Колонка SPI Mode нам намекает на то, что взаимодействует с микроконтроллером при помощи интерфейса SPI. НО! Мы пойдем по другому пути 😉 Все дело в том, что STM32 имеют на своем борту готовый периферийный модуль для работы именно с картами памяти, и называется он SDIO.

Вообще взаимодействие с картами памяти заключается в передаче им определенных команд. Некоторые команды требует наличия аргумента, некоторые нет. Команды можно найти в официальной документации на конкретную карту. Так вот встроенный модуль SDIO дает возможность значительно упростить процесс передачи команд, да и вообще процесс работы с внешними картами памяти. Например, вот регистр SDIO_CMD – туда мы просто напросто записываем код команды, которую хотим передать карте. Или вот статусный регистр SDIO_STA – там целых 24 флага на каждый чих, то есть для большого количества событий.

Кстати STM радует еще и добротной документацией на все это дело. Вот, к примеру, подробное описание инициализации для карты памяти SD (аналогично все описано для других типов карт):

Ну, собственно, пора перейти к практическому примерчику. Поковыряем-ка Standard Peripheral Library.

В файле stm32f10x_sdio.h по традиции находим структуры для всевозможной настройки – то есть для выбора источника тактового сигнала, частоты контроллера SDIO, настройки количества передаваемых байт. Там все так щедро откомментировано, что даже не хочется отдельно это повторять)) Просто смотрите:

typedef struct { uint32_t SDIO_ClockEdge; /* Specifies the clock transition on which the bit capture is made. This parameter can be a value of @ref SDIO_Clock_Edge */ uint32_t SDIO_ClockBypass; /* Specifies whether the SDIO Clock divider bypass is enabled or disabled. This parameter can be a value of @ref SDIO_Clock_Bypass */ uint32_t SDIO_ClockPowerSave; /* Specifies whether SDIO Clock output is enabled or disabled when the bus is idle. This parameter can be a value of @ref SDIO_Clock_Power_Save */ uint32_t SDIO_BusWide; /* Specifies the SDIO bus width. This parameter can be a value of @ref SDIO_Bus_Wide */ uint32_t SDIO_HardwareFlowControl; /* Specifies whether the SDIO hardware flow control is enabled or disabled. This parameter can be a value of @ref SDIO_Hardware_Flow_Control */ uint8_t SDIO_ClockDiv; /* Specifies the clock frequency of the SDIO controller. This parameter can be a value between 0x00 and 0xFF. */ } SDIO_InitTypeDef; typedef struct { uint32_t SDIO_Argument; /* Specifies the SDIO command argument which is sent to a card as part of a command message. If a command contains an argument, it must be loaded into this register before writing the command to the command register */ uint32_t SDIO_CmdIndex; /* Specifies the SDIO command index. It must be lower than 0x40. */ uint32_t SDIO_Response; /* Specifies the SDIO response type. This parameter can be a value of @ref SDIO_Response_Type */ uint32_t SDIO_Wait; /* Specifies whether SDIO wait-for-interrupt request is enabled or disabled. This parameter can be a value of @ref SDIO_Wait_Interrupt_State */ uint32_t SDIO_CPSM; /* Specifies whether SDIO Command path state machine (CPSM) is enabled or disabled. This parameter can be a value of @ref SDIO_CPSM_State */ } SDIO_CmdInitTypeDef; typedef struct { uint32_t SDIO_DataTimeOut; /* Specifies the data timeout period in card bus clock periods. */ uint32_t SDIO_DataLength; /* Specifies the number of data bytes to be transferred. */ uint32_t SDIO_DataBlockSize; /* Specifies the data block size for block transfer. This parameter can be a value of @ref SDIO_Data_Block_Size */ uint32_t SDIO_TransferDir; /* Specifies the data transfer direction, whether the transfer is a read or write. This parameter can be a value of @ref SDIO_Transfer_Direction */ uint32_t SDIO_TransferMode; /* Specifies whether data transfer is in stream or block mode. This parameter can be a value of @ref SDIO_Transfer_Type */ uint32_t SDIO_DPSM; /* Specifies whether SDIO Data path state machine (DPSM) is enabled or disabled. This parameter can be a value of @ref SDIO_DPSM_State */ } SDIO_DataInitTypeDef;

Отметим как в SPL реализована передача команд карте памяти. Для этих целей отведена отдельная структура SDIO_CmdInitTypeDef. В поле SDIO_CmdIndex вводим код команды, в поле SDIO_Argument – аргумент команды, также заполняем остальные поля. Осталось как то эти данные запихать в карту micro SD 😉 А для этого нам приготовили функцию:

SDIO_SendCommand (SDIO_CmdInitTypeDef *SDIO_CmdInitStruct)

В качестве аргумента передаем ей как раз таки созданную нами структуру. Для записи данных есть функция – SDIO_WriteData(uint32_t Data) . После вызова этой функции данные окажутся в специально предназначенном для этого регистре – SDIO_FIFO.

Вот так вот осуществляется работа с модулем SDIO в STM32F10x)

Теперь перейдем к практике наконец-то. Я снова буду работать с платой Mini STM32, поскольку добрые китайцы озадачились установкой на нее слота для карты памяти micro SD. Вот схема подключения разъема для карты к микроконтроллеру:

Для написания программы воспользуемся готовым примером для Keil’а – стащим оттуда два файла, в которых реализовано что-то вроде драйвера для работы с картами – это файлы sdcard.c и sdcard.h. Создаем новый проект, цепляем туда эти файлы, а кроме того, естественно, файлы CMSIS и SPL. Вот готовый проект, в который все уже добавлено – остается только написать код функции main())

В файле sdcard.c реализованы всевозможные функции для работы с картой памяти, нам теперь остается их только использовать 😉 Пишем код! Для примера запишем на micro SD 512 байт тестовых данных, а затем попробуем их считать:

// Цепляем нужные файлы #include "stm32f10x.h" #include "sdcard.h" /*******************************************************************/ // Массивы входных и выходных данных и переменная для хранения данных // о нашей карте uint8_t writeBuffer[ 512 ] ; uint8_t readBuffer[ 512 ] ; SD_CardInfo SDCardInfo; /*******************************************************************/ int main() { // Тестовые данные для записи for (uint16_t i = 0 ; i < 512 ; i++ ) { writeBuffer[ i] = i % 256 ; readBuffer[ i] = 0 ; } // Иницилизация карты SD_Init() ; // Получаем информацию о карте SD_GetCardInfo(& SDCardInfo) ; // Выбор карты и настройка режима работы SD_SelectDeselect((uint32_t ) (SDCardInfo.RCA << 16 ) ) ; SD_SetDeviceMode(SD_POLLING_MODE) ; // И вот наконец то запись и чтение SD_WriteBlock(0x00 , writeBuffer, 512 ) ; SD_ReadBlock(0x00 , readBuffer, 512 ) ; while (1 ) { } } /*******************************************************************/

Обратите внимание, что SD карта поддерживает запись блоками по 512 байт.

Если мы запустим программу под отладчиком, то увидим, что считанные данные соответствуют записанным =) Так что эксперимент можем считать удавшимся. На этом на сегодня заканчиваем, до скорых встреч!

BigPack 25 февраля 2014 в 22:02

Полнофункциональный драйвер SDHC карты памяти для Stm32f4 (часть 1)

  • Системное программирование

Для чего эта статья?


Все эмбеддеры, рано или поздно, сталкиваются с проблемой нехватки ПЗУ микроконтроллера для своих проектов. Ну банально, Вам нужно разработать систему управления простеньким ЧПУ станком, где управляющая программа хранится на самом девайсе или систему сбора данных, скажем, от датчиков какого-нибудь эксперимента – очевидно, что микроконтроллер изначально не предназначался для хранения таких массивов информации.

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

О Secure Digital (SD) картах


Не буду переписывать Википедию – приведу здесь основные сведения и виды SD карт памяти и их характеристики.
Secure Digital формат – популярный (пожалуй, самый популярный на сегодняшний день) формат flash памяти для использования, в основном, в портативных устройствах. Внутри каждой такой карточки имеется, собственно, микросхема flash памяти (Memory Core) и, связывающий ее с внешним миром контроллер, имеющий 8 регистров. Задачи последнего – аппаратная реализация внешних интерфейсов, поддержка информации о карте (тип, емкость, класс скорости еще куча других характеристик), контроль электропитания, и, конечно, управление самой микрухой памяти (адресация, чтение, запись, очистка и оганизация порядка 80 команд управления).

Формат SD был основан компаниями Panasonic, SanDisk и Toshiba на основе MMC карт. Позже эти компании создали организацию SD Card Association, в настоящее время занимающуюся разработкой и продвижением технологии SD. Основной документ, в котором досконально описан интерфейс, протокол, команды, регистры карточек - Physical Layer Simplified Specification (Copyright 2001-2006 SD Group (Panasonic, SanDisk, Toshiba) and SD Card Association). Именно эту информацию используют всякие R&D центры при разработки аппаратного и программного обеспечения своих будущих девайсов. Сам файлик благополучно лежит в свободном доступе в инете, и скачать его не предоставляется никаких сложностей. Так вот, в соответствии с этим документом, существуют следующие типы карт памяти:
SD карты (или еще SDSC (Secure Digital Standard Capacity)) – первое поколение карт памяти. Ограничение по объему – 2 Гб. Минимальный размер адресуемого пространства – 1 байт.
SDHC карты (Secure Digital High Capacity) – карты памяти повышенной емкости (до 32 Гб). Имеют существенное отличие от первого типа, а именно, адресация происходит блоками по 512 байт и никто в этом мире не может изменить это значение. Иными словами, нельзя просто так взять и записать, к примеру, 71 байт информации: минимальный размер пачки, повторюсь, 512 байт. Особо не копал, почему так, но есть личное мнение, что это из-за используемого 32-битного адресного пространства контроллера и из-за того, что карты памяти обычно форматируются под ту или иную файловую систему, размер кластера которой удобно сочетается с такими блоками. Еще у SDHC карт дугой процесс инициализации, о котором поговорим чутка по позже.
SDXC (Secure Digital eXtended Capacity) – карты памяти расширенной емкости – теоретически аж до 2Tб памяти. Адресация тоже по 512 байт. Вот оно и получается при 32-битном пространстве: (2^32)*512 = 2 Тб.

На каждое поколение карт существуют спецификации, и при этом в каждом документе на более новое поколение описывается инфа о старых – то есть они «толстеют» с каждым обновлением продукта. Так что скачиваем Physical Layer Simplified Specification самой последней версии и находим там все, что надо для работы со всеми поколениями карт. Кроме этого, карты памяти делятся на несколько классов по скорости чтения/записи данных. Ну, а что касается всяких там mini-, microSD, microSDXC и т.д. – это всего лишь другой размер корпуса и распиновка – никаких внутренних отличий от карточек стандартных габарит.

А теперь важно: ВНЕ зависимости от типа карты, емкости, ее производителя, типа корпуса, цвета и магазина, где вы ее купили – все Security Digital карты имеют одинаковые интерфейсы взаимодействия с внешним миром. Команды, механизмы инициализации – разные, да, но интерфейсы – ОДИНАКОВЫЕ. Именно это позволяет напофиг воткнуть в фотик как SD, так и SDHC карту памяти. Ну, вот и пришел момент обсудить язык карточки, а точнее аж три: SD и UHS-II (нэйтив спикер) и «язык универсальной микропроцессорной коммуникации, который сейчас знает каждая нерезаная собака микроконтроллер» - SPI .

Интерфейс карты памяти


Как было сказано выше, Security Digital карты имеют три внешних интерфейса: SD, UHS-II и SPI. Первые являются «родными» каналами обмена данными с хостом, и, как следствие, позволяют реализовать полнофункциональное, полноскоростное взаимодействие. SPI же не поддерживает ряда команд и не дает максимальной скорости обмена данными, зато он есть во всех микроконтроллерах (и в современных и в старых моделях), что делает возможным без особых проблем приконнектить карточку ко всему, что плохо лежит. Существует масса статей о том, как это сделать. Но, с развитием микропроцессорной техники, с уменьшением нанометров в технологическом процессе производства камней, SPI интерфейс, как средство коммуникации с SD картой постепенно отмирает. Действительно, если ваш МК поддерживает аппаратную реализацию SD протокола, будите ли Вы связываться с менее функциональной альтернативой? Судьба послала мне на проект камень Stm32f4 от STMicroelectronics, в котором как раз таки и имеется периферийный модуль SDIO (Security Digital Input Output), аппаратно реализующий и интерфейс, и протокол карточки.

Так что же такое SD протокол и с чем его едят? Ключевых понятий тут три:
команда – последовательность битов, воспринимаемых контроллером карточки и призывающих его к тому или иному действию;
отклик – ответ контроллера карты на команду. Он может содержать как общую информацию (статус карты, текущее состояние различных внутренних модулей и т.д.), так и, собственно, ожидаемую хостом информацию (запросили в команде идентификатор карты – получили его в отклике);
данные – ну тут без комментариев.

Но прежде, чем посмотрим на логику протокола, обратимся к физике интерфейса (очень обзорно).


Pin 4 – питание карточки;
Pin 3, 6 – земля;
Pin 5 – тактовый сигнал;
Pin 2 – линия команд и откликов;
Pin 1, 7, 8, 9 – линии 4-битной шины данных.

Все посылки карточке и обратно есть последовательности битов, строго синхронизированные с тактовым сигналом, передаваемым по линии CLK . Рекомендуемые частоты описаны в спецификации на карту и имеют различное значение, в зависимости от ее типа и класса скорости. Отмечу только, что для любой карты инициализация проходит на очень малой (по сравнению с передачей данных) частоте. Шина данных может быть 1-битной (работает только D0) или 4-битной – это конфигурируется при инициализации. Важно, что для SD карт со стороны хоста линии данных и команд должны быть Push-Pull и быть подтянуты к питанию через резисторы 4.5 – 10 кОм. Тактовую шину тоже нужно подтянуть к питанию.

Ну, а теперь к протоколу!
Бывает несколько вариантов обмена информацией хост – карта.

1) Команды без данных.
Все команды делятся на требующие и не требующие отклик .

Как видно из рисунка, если нам (хосту) нужно послать команду, не требующую отклика – просто шлем ее. Если же, команда подразумевает некий ответ, шлем, а затем ждем ответа. Почти все команды и отклики проверяются контрольной суммой, как со стороны хоста, так и со стороны карты. Ну, посмотрим на формат команды:

Кадр состоит из 48 бит. Первый – старт бит – всегда нуль. Затем, говорим, что данные направляются от хоста к карте и посылаем команду с аргументом. Да, да, команда состоит из индекса и аргумента. После команды обязательно шлем 7-битную контрольную сумму, вычисляемую по алгоритму циклически избыточного кода (CRC) и завершаем посылку стоп битом. Команды бывают двух типов: CMD (базовые команды) и ACMD (Application-Specific Command) . Они могут быть с аргументом и без, иметь отклик и не иметь. Всего существует порядка 80 команд (не считал точно, может и больше) и каждая из них подробно описана в спецификации. Мы остановимся лишь на некоторых, необходимых для основной работы с карточкой (инициализация, чтение, запись). Индекс команды – это та цифра, которая идет после символов CMD или ACMD. Под него отведено 6 бит и 32 бита аргумента команды, если таковой требуется.

Важное пояснение по поводу ACMD : пространство их индексов пересекается с индексами CMD команд, поэтому, чтобы контроллер воспринял команду именно, как Application-Specific, ей должна предшествовать CMD55 !

Отклик (если требуется) – тоже целая тема, хотя бы, потому что их пять типов.

R1 (normal response command) – длина 48 бит. Пожалуй, самый популярный отклик.


Содержит в себе старт бит, бит направления передачи (от карты к хосту), 6 битов индекса команды, побудившей на генерацию отклика, статус карты и, конечно же, контрольную сумму со стоп битом. Всю информацию в отклике этого типа несет 32 битное поле статуса карты . В спецификации тщательно и добросовестно расписано, что означает каждый бит этого статуса (карта занята/свободна, блокирована/разблокирована, текущее состояние автомата передачи данных, готовность к тому или иному действию и многое другое).

R1b – такой же формат, как и в случае R1 отклика, только передает еще флаг занятости (busy) по линии данных.

R2 (CID, CSD register) – длинной в 136 бит отклик передает хосту содержимое CID и CSD регистров контроллера карточки.


Здесь вся полезная информация содержится в 127 битном поле, в которое помещается либо содержимое CID (в случае, если это отклик на CMD2 или CMD10 команду), либо содержимое CSD регистра (в случает посыла CMD9 команды). Так что же это за регистры такие, что под них специальные команды придуманы, да еще и с таким длинным откликом?
CID (Card identification data) – как видно из названия, содержит всю идентификационную информацию о карте (серийный номер, производитель, дата изготовления и др…). CSD (Card-specific data) – вся техническая информация о карте (объем памяти, размер блоков чтения/записи, максимальные скоростные характеристики, максимальные характеристики по потребляемому току в различных режимах и многое другое). Именно эту информацию использует хост мобилы или камеры для получения всей информации о вставленной карточке.

R3 – длиной в 48 бит, приходит как ответ на команду ACMD41 и несет в себе информацию о содержимом OCR (Operation Conditions Register) регистра карты.


ACMD41 – команда инициализации карты. После ее посыла необходимо ожидать данного отклика, который будет говорить об успешном завершении процесса инициализации и сообщать содержимое регистра OCR (доступный диапазон напряжений, тип карты памяти, и флаг занятости).

R6 (Published RCA response) – содержит в себе RCA (Relative card address) карты и некоторые статус биты.


Шина предполагает подключение нескольких карт к одному хосту. Поэтому очень важно такое понятие, как собственный адрес карты на шине. Это и есть содержимое RCA регистра.

R7 (Card interface condition) – 48 битовый отклик на команду CMD8.


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

2) Данные.
Напомню (это было сказано давно…), мы рассмотрели посыл команд и получение отклика от карты. Теперь самое время разобраться с тем, как же слать, собственно, данные. Повторюсь, делается это блоками по 512 байт (для SDHC карт) - все адресное пространство карты разбито на 512 байтовый ячейки . Посылке данных всегда должна предшествовать специальная команда, говорящая контроллеру карты о том, что данные вот-вот уже пойдут. А идут они, как я уже говорил – по 1- или 4-битной шине. Посмотрим на формат посылки данных к хосту от карты (чтение).

Возможны два режима передачи данных: одним блоком (block read operation) и несколькими блоками сразу (multiple block read operation). В любом случае, старт передачи и ее завершение происходят по специальной команде, обратите внимание, с откликом.

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

Инициализация SD Карты памяти


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

ВАЖНО : инициализация проводится на низко скоростном режиме! Частота клока карты не более 400 кГц!!!

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

Помните, я говорил, что напряжение нужно валидировать? Правильно! Нужно сказать карте, на каком вольтаже мы работаем и выслушать от нее все по этому поводу. Шлем CMD8 с аргументом, в котором биты 11:8 означают напряжение хоста и биты 7:0 check pattern (проверочный шаблон) – любой, спецификация рекомендует слать 10101010 . Биты напряжения ставятся в соответствии с таблицей:

Ну, у нас все очень даже определено и далеко не Low Voltage Range. Stm32f4 выдает как раз напряжение в диапазоне 2.7 – 3.6 V, так что ставим 1 на восьмом бите аргумента. Итого, имеем команду с аргументом 110101010 . Отправили. Прочекали, что все отправилось хорошо и ждем ответа, он не заставит нас делать это долго. В спецификации увидели, что ответ на эту команду – R7 типа.
Если мы его так и не дождались, то дальнейшая команда ACMD41 решит, как именно нас надули – подсунули карту версии 1.X стандартной емкости или вообще не SD карту. Правда есть вероятность, что мы просто что-то не так делаем. Но не будем о грустном, и предположим, что флешка все таки ответила. Если с напряжением все хорошо, карта довольна, мы довольны, ответ будет содержать в себе все то, что мы отправили в аргументе, то есть 110101010 . Это называется valid esponse . Если так, переходим к дальнейшему шагу, иначе – опять же – либо надули, либо где-то косяк.

Дождались 110101010 , и пришло время непосредственной инициализации – команды ACMD41 . И тут вспоминаем ВАЖНОСТЬ : чтобы сказать карточке, что команда не простая, а ACMD, отправим сперва CMD55 . В аргументе обязательно указываем, адрес той карты, для которой эта команда предназначена. Но стоп, у нас, ведь, пока нет адреса, мы его не знаем. Ничего, узнаем … но по позже, а пока пишем нули и шлем. Получив ответ типа R1 удостоверяемся, что карта готова принимать ACMD и только после этого шлем 41 индекс! Команда идет с аргументом, в котором на месте 30-ого бита указываем 1, что говорит о поддерживаемости хостом SDHC карт и напряжения хоста на месте 23:0 битов (см. содержимое OCR регистра). Ответ ждем R3 типа. Здесь нам важно получить 1 на месте 31ого бита в пачке ответа, несущей содержимое OCR регистра карты (флаг busy). Это будет говорить о том, что карта завершила процесс инициализации. Виду того, что этот процесс длится долго (гораздо дольше, чем тактовый цикл микроконтроллера), необходимо слать ACMD41 в цикле до тех пор, пока не получим ответ со снятым флагом занятости. Как только это случилось, чекам 30 бит, и, если он единица, то имеем карту повышенной емкости SDHC или SDXC, и карту стандартной емкости SD в противном случае. Если же мы ждали, ждали, а флаг бизи все висит и висит, то, как и в описанном выше случае – либо неподходящая карта, либо (скорее всего) наш косяк.

Далее шлем CMD2 - без аргумента и смотрим на ответ R2 . В этом случае он будет нести информацию о содержимом CID регистра, и мы сможем вычитать ID производителя, серийный номер карты и прочую информацию.

И, наконец, заключительный шаг – получение адреса карты (RCA - relative
address
). Как оно уже упоминалось, к одной шине может быть подключено несколько карт, поэтому каждая должна иметь свой уникальный локальный адрес. Шлем CMD3 и получаем ответ типа R6 , в котором в младших 16 битах содержится статус карты, а в старших – новый RCA адрес. Отныне, для доступа к нашей карточке, мы должны будем звать ее по имени, то есть по RCA адресу.

Опциональный пункт. По умолчанию карта работает с 1-битной шиной данных, что, ясное дело, медленнее, чем с 4-х битной. Если мы хотим достичь максимального быстродействия – шлем ACMD6 , с предшествующей CMD55 , конечно же. Но прежде, нужно перевести карту в состояние Transfer State (см. ниже) командой CMD7 с RCA в качестве аргумента. В аргументе ACMD6 на месте самого первого бита пишем 1 – если хотим включить 4-битный мод и 0 – для отключение. Ответ R1 скажет об успешном проведении операции.

Пример инициализации SDHC карты
В данном примере используется самодельная функция посылки команды, написанная под периферию Stm32F4.
char SDIO_send_command(char index, unsigned int arg, char resp_type, unsigned int *resp);
index – индекс команды;
arg - аргумент;
resp type – тип отклика (0 – без отклика, 1 – короткий (48 бит) отклик, 2 – длинный (136 бит) отклик);
resp - массив откликов (в случае короткого отклика информацию несет первый элемент массива, в случае длинного – 4 элемента).
Команда возвращает 0, в случае успешной операции посыла команды и приема ответа и код ошибки в противном случае.

Char SDHC_card_initialization(unsigned int *RCA) { char result; unsigned int RESP; result = SDIO_send_command(0, 0, 0, RESP); //Посылаем CMD0, дабы обнулить карты if (result != 0) return result; //Чекаем на успех result = SDIO_send_command(8, 0x1AA, 1, RESP); //Посылаем CMD8 с аргументом 110101010 if ((result != 0) || (RESP != 0x1AA)) return 4; //Чекаем на успех while(!(RESP&(1<<31))) //Ждем, пока флаг бизи не слезет { result = SDIO_send_command(55, 0, 1, RESP); //Шлем CMD55, тем самым, говоря, что потом будет ACMD if (result != 0) return result; result = SDIO_send_command(0x29, 0x40020000, 1, RESP); //Шлем ACMD41 if (result != 0) return result; } result = SDIO_send_command(2, 0, 3, RESP); //Шлем CMD2 и получаем инфу о карте if (result != 0) return result; result = SDIO_send_command(3, 0, 1, RESP); //Шлем CMD3 и получаем RCA номер if (result != 0) return result; SDIO->CLKCR = (0x02<<0)|(1<<11)|(1<<8)|(1<<14); //Наращиваем клок (в части 2 - подробнее) *RCA = (RESP & (0xFFFF0000)); //Маскируем отклик и получаем RCA result = SDIO_send_command(7, *RCA, 1, RESP); //Выбираем нашу карту if (result != 0) return result; result = SDIO_send_command(55, *RCA, 1, RESP); //Шлем CMD55, тем самым, говоря, что потом будет ACMD if (result != 0) return result; result = SDIO_send_command(6, 0x02, 1, RESP); //Шлем ACMD6 c аргументом 0x02, установив 4-битный режим if (result != 0) return result; if (RESP != 0x920) return 1; else return 0; //Убеждаемся, что карта находится в готовности работать с трансфером return 0; }
Запускаем код, убеждаемся, что в ответе пришел НУЛЬ и завершаем инициализацию. Все, можем работать с памятью и писать/считывать информацию.

Обмен данными


Здесь всем рулит SD Memory Card State Diagram (data transfer mode).


Существует 6 статусов карты в этом режиме и узнать их можно в отклике R1 на месте 12:9 битов. Обратимся к спецификации.


Stand by State (stby) – устанавливается после инициализации вместо Idle State.
Transfer State (tran) – режим передачи данных.
Receive Data State (rcv) – ожидание пачки данных от хоста.
Programming State (prg) – запись принятой пачки во flash.
Sending Data State (data) – посылка пачки данных хосту.
Disconnect State (dis) – используется для выбора другой карты командой CMD7.

Запись данных на карту
Итак, после успешной инициализации мы находимся в состоянии tran , во всяком случае, должны находиться. Смотрим по диаграмме: для того, чтобы перейти на состояние rcv , нужно послать команду CMD24 с адресом 512 байтной ячейки, которую хотим записать. Послали. Карта перешла в режим ожидания данных. Далее начинаем кидать ей информацию по шине данных, пока не перекинем все 512 байт или не пошлем команду CMD12 (стоп передачи). После завершения акта, карточка сама переедет в состояние prg и пробудет там некоторое время (пока данные запишутся). Ждем.… Как имено ждем? А посылаем ей в цикле CMD13 с адресом карты в аргументе, до тех пор, пока не вернется в отклике R1 типа статус tran . Когда это, наконец, случилось можно слать очередную пачку данных, вновь послав CMD24. Кроме того, существует еще режим записи несколькими блокам сразу (CMD25) и другие режимы – за подробностью – в спецификацию.
Чтение данных
Дабы выполнить обратную процедуру, в первую очередь, убеждаемся, что карта стоит в tran . Шлем CMD17 с адресом RCA в аргументе. Если все пройдет успешно – карточка переедет в состояние data и начнет выдавать на линии данных информацию, опять же 512 байтным блоком. Задача хоста в это время внимательно слушать линию и считывать данные. Как только посылка закончится, карта сама переедет в статус tran . Думаю, не стоит и говорить о том, что считывание так же как и запись возможна несколькими блоками сразу.

Не буду приводить в этой статье листинг программы чтения/записи, так как он, в отличии от подпрограммы инициализации слишком сильно завязан на железе SDIO модуля микроконтроллера Stm32f4, а это – тема второй части статьи.

Эта статья также доступна на следующих языках: Тайский

  • Next

    Огромное Вам СПАСИБО за очень полезную информацию в статье. Очень понятно все изложено. Чувствуется, что проделана большая работа по анализу работы магазина eBay

    • Спасибо вам и другим постоянным читателям моего блога. Без вас у меня не было бы достаточной мотивации, чтобы посвящать много времени ведению этого сайта. У меня мозги так устроены: люблю копнуть вглубь, систематизировать разрозненные данные, пробовать то, что раньше до меня никто не делал, либо не смотрел под таким углом зрения. Жаль, что только нашим соотечественникам из-за кризиса в России отнюдь не до шоппинга на eBay. Покупают на Алиэкспрессе из Китая, так как там в разы дешевле товары (часто в ущерб качеству). Но онлайн-аукционы eBay, Amazon, ETSY легко дадут китайцам фору по ассортименту брендовых вещей, винтажных вещей, ручной работы и разных этнических товаров.

      • Next

        В ваших статьях ценно именно ваше личное отношение и анализ темы. Вы этот блог не бросайте, я сюда часто заглядываю. Нас таких много должно быть. Мне на эл. почту пришло недавно предложение о том, что научат торговать на Амазоне и eBay. И я вспомнила про ваши подробные статьи об этих торг. площ. Перечитала все заново и сделала вывод, что курсы- это лохотрон. Сама на eBay еще ничего не покупала. Я не из России , а из Казахстана (г. Алматы). Но нам тоже лишних трат пока не надо. Желаю вам удачи и берегите себя в азиатских краях.

  • Еще приятно, что попытки eBay по руссификации интерфейса для пользователей из России и стран СНГ, начали приносить плоды. Ведь подавляющая часть граждан стран бывшего СССР не сильна познаниями иностранных языков. Английский язык знают не более 5% населения. Среди молодежи — побольше. Поэтому хотя бы интерфейс на русском языке — это большая помощь для онлайн-шоппинга на этой торговой площадке. Ебей не пошел по пути китайского собрата Алиэкспресс, где совершается машинный (очень корявый и непонятный, местами вызывающий смех) перевод описания товаров. Надеюсь, что на более продвинутом этапе развития искусственного интеллекта станет реальностью качественный машинный перевод с любого языка на любой за считанные доли секунды. Пока имеем вот что (профиль одного из продавцов на ебей с русским интерфейсом, но англоязычным описанием):
    https://uploads.disquscdn.com/images/7a52c9a89108b922159a4fad35de0ab0bee0c8804b9731f56d8a1dc659655d60.png