Подключение sdhc карты к avr. Работа с SD картой

Существует множество различных типов носителей данных на основе так называемой флеш-памяти. Мы пользуемся обычными флешками для передачи файлов друг-другу, micro-SD картами для увеличения свободного места в смартфонах, даже вместо старого доброго жесткого диска в современных ноутбуках используем SSD носители — ту же флеш-память. Флеш-память не имеет движущихся частей, в отличие от старинных дискет и более новых жестких дисков. Скорость чтения и записи такой памяти выше чем у всех прежних носителей, а энергопотребление — наоборот ниже. Другими словами, если мы хотим в наших электронных устройствах и роботах хранить какие-то данные, то рационально будет воспользоваться именно флеш-памятью. Зачем может понадобиться карта памяти? Например, для того, чтобы записывать на неё данные с различных датчиков нашего устройства. Кроме самих показаний датчиков, рационально еще записывать время съема этих показаний — это называется журналированием. Таким образом, подключив к Ардуино датчики температуры, влажности и давления, а также часы реального времени и карту памяти мы сможем сделать настоящую погодную станцию! Разберем как именно карта памяти подключается к Ардуино и каким образом осуществляется её запись и чтение.

1. Подключение модуля micro-SD карт к Ардуино

В этом уроке мы будем читать и записывать данные на micro-SD карту. В плане подключения в Ардуино, модуль micro-SD ничем не отличается от модуля для обычных SD карт. Модуль подключается к Ардуино по SPI шине, а значит нужно соединить уже знакомые по другим урокам контакты в стандартном порядке:
Модуль micro-SD карт GND VCC CS MOSI MISO SCK
Ардуино Уно GND +5V 4 11 12 13
Принципиальная схема
Внешний вид макета

2. Программа для чтения micro-SD карты

Чтобы проверить работу устройства, напишем простую программу, которая будет лишь считывать с карты служебную информацию: тип карты, тип файловой системы, размер первого раздела и список файлов на нём. #include #include Sd2Card card; SdVolume volume; SdFile root; const int chipSelect = 4; void setup() { Serial.begin(9600); Serial.print("\nInitializing SD card..."); if (!card.init(SPI_HALF_SPEED, chipSelect)) { // неверное подключение или карта неисправна Serial.println("initialization failed"); return; } else { // всё ок! Serial.println("Wiring is correct and a card is present."); } // считываем тип карты и выводим его в COM-порт Serial.print("\nCard type: "); switch (card.type()) { case SD_CARD_TYPE_SD1: Serial.println("SD1"); break; case SD_CARD_TYPE_SD2: Serial.println("SD2"); break; case SD_CARD_TYPE_SDHC: Serial.println("SDHC"); break; default: Serial.println("Unknown"); } // инициализация файловой системы if (!volume.init(card)) { // неверная файловая система Serial.println("Could not find FAT16/FAT32 partition."); return; } // считываем тип и вычисляем размер первого раздела uint32_t volumesize; Serial.print("\nVolume type is FAT"); Serial.println(volume.fatType(), DEC); Serial.println(); volumesize = volume.blocksPerCluster(); // блоков на кластер volumesize *= volume.clusterCount(); // кластеров volumesize *= 512; // 512 байтов в блоке, итого байт.. Serial.print("Volume size (bytes): "); Serial.println(volumesize); Serial.print("Volume size (Kbytes): "); volumesize /= 1024; Serial.println(volumesize); Serial.print("Volume size (Mbytes): "); volumesize /= 1024; Serial.println(volumesize); Serial.println("\nFiles found on the card (name, date and size in bytes): "); root.openRoot(volume); // выводим список файлов root.ls(LS_R | LS_DATE | LS_SIZE); } void loop(void) { } Загружаем программу в Ардуино и открываем монитор COM-порта:
Если появилась подобная информация, значит с картой и модулем всё в порядке. Можно приступать к дальнейшей работе.

3. Программа для записи данных на micro-SD карту

Теперь попробуем создать новый файл на карте и записать туда простую фразу «Hello from robotclass». #include #include const int chipSelect = 4; void setup() { Serial.begin(9600); if (!SD.begin(chipSelect)) { Serial.println("Card failed, or not present"); return; } // строка, которую мы запишем в файл String dataString = "Hello from RobotClass"; // открываем файл, в который будет записана строка File dataFile = SD.open("test.txt", FILE_WRITE); if (dataFile) { // записываем строку в файл dataFile.println(dataString); dataFile.close(); Serial.println("Success!"); } else { // выводим ошибку если не удалось открыть файл Serial.println("error opening file"); } } void loop() { } Загружаем программу. Затем выключаем Ардуино, достаем микро-SD карту из модуля и проверяем на компьютере её содержимое. В корне карты должен появиться файл test.txt с нашей фразой «Hello from RobotClass». Примечание! Имя файла, которое указывается в функции open не должно содержать более 8 букв (не включая расширение).

4. Программа для чтения данных с micro-SD карты

Наконец, прочитаем файл test.txt и выведем в COM-порт текст из него. #include #include const int chipSelect = 4; void setup() { Serial.begin(9600); if(!SD.begin(chipSelect)){ Serial.println("initialization failed!"); return; } // открываем файл для чтения File myFile = SD.open("test.txt"); if (myFile) { // считываем все байты из файла и выводим их в COM-порт while (myFile.available()) { Serial.write(myFile.read()); } // закрываем файл myFile.close(); } else { // выводим ошибку если не удалось открыть файл Serial.println("error opening test.txt"); } } void loop() { } Загружаем программу и открываем монитор COM-порта. На экране должен появиться весь текст из файла test.txt

Заключение

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

В большинстве проектов с Ардуино необходимо устанавливать вспомогательное хранилище для медиафайлов, будь это видео или аудио. Если возникает необходимость хранить и обрабатывать информацию, а стоковой флеш-памяти плат вам не хватает, то есть несколько решений.

Можно докупить специальную плату, обеспечивающую быстрый доступ к данным, но и стоящую не малых денег. Если же у вас нет желания тратиться, то расширить стоковую память платы можно и с arduino card SD, дабы не было проблем с записью и стиранием стоковых утилит с чипов. Однако далеко не все новички знают, как правильно подключать флешку к чипу и что для этого нужно. Давайте разберёмся во всех нюансах и узнаем, какие подводные камни такого решения вас поджидают.

Что необходимо знать при подключении SD card к Arduino

Прежде чем подсоединять что-либо, следует изучить нюансы, которые будут подстерегать вас на каждом шагу. Первое, с чем вам придётся столкнуться, это вольтаж карт памяти, ведь они рассчитаны на 3.3 В, что является стандартом.

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

Чтобы не сжечь всю плату, достаточно прикупить преобразователи переменного и постоянного тока, лучше всего подойдут приборы под HEF4050 и 74FHC125 базу.

  1. SPI. Универсален, и подойдёт для любого микроконтроллера, а подключение требует всего четыре пина.
  2. SDIO. Хороший выбор, если необходима высокая скорость передачи информации, но с arduino microsd реализуется крайне тяжело.

Учитывайте и формат карты, он не влияет ровным счётом ни на что, кроме размера вашей конечной поделки. Если необходимо серьёзно урезать габариты, то хорошим выбором станет microSD.

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

Большинство устройств на сегодняшний день пользуются форматами NTFS, FAT32 и FAT16. Но чем сложнее система, тем больше памяти для работы arduino SD придётся выделить, что чревато последствиями в виде подлагиваний и плохой скорости отклика.

Схема подключения SD card к Arduino

Предварительно отформатировав карту памяти, приступайте к её подключению.

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

Плата расширений потребуется под микроконтроллеры до 5 В, учитывайте это при её выборе. Главное достоинство Ардуино – простота, и подключение вспомогательных модулей не стало исключением. Лучшим выбором станет именно структура запуска через hardware SPI пины, дабы не усложнять новичкам жизнь. Нам потребуются 13, 12 и 11 цифровые пины, а также четвёртые, чтобы наладить «chip\slave select» цепь. Под это, зачастую, берётся 10 пин, но если вы знаете, что делаете, можете выбрать и более подходящий.


Обратите внимание на то, что в зависимости от форм-фактора и типа платы Ардуино, точки подключения могут варьироваться. Например, для меги необходимо соединить цепь с 50, 51, 52 и 53 слотом.

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

Еще один вариант наглядной схемы:

В конце процедуры необходимо замкнуть CD в заземление, так система не сможет инициализировать карту памяти. Но, в случае необходимости, всегда можно применить резисторы в 10 кОм и вспомогательные порты, однако мы не будем останавливаться на этом пине, так как сейчас он нам не нужен.

Загрузка библиотек и запуск SD card на Arduino

Чтобы подсоединиться к SD карте и свободно передавать на неё данные, потребуется написать немало кода, однако здесь нам поможет встроенная библиотека SD.

Библиотеку по работе с картами памяти можно найти на официальном сайте производителя микроконтроллера: https://www.arduino.cc/en/Reference/SD

Открыв подменю «образцов», найдите заготовку «cardinfo», именно её мы не будем использовать в качестве прописной функции при загрузке информации. Скетч пригодится лишь для проверки, опознаётся ли дополнительная память на устройстве. Проверяйте chipSelect, учитывая, что подключение идёт через 10 пин.

#include const int chipSelect = 4; void setup() { Serial.begin(9600); while (!Serial) { ; // wait for serial port to connect. } Serial.print(“Initializing SD card…”); pinMode(10, OUTPUT); //iniot SD card if (!SD.begin(chipSelect)) { Serial.println(“Card failed, or not present”); return; } Serial.println(“card initialized.”); } void loop() { String dataString = “”; // read three sensors and append to the string for (int analogPin = 0; analogPin < 3; analogPin++) { int sensor = analogRead(analogPin); dataString += String(sensor); if (analogPin < 2) { dataString += “,”; } } // open the file. File dataFile = SD.open(“data.txt”, FILE_WRITE); // if the file is available, write to it: if (dataFile) { dataFile.println(dataString); dataFile.close(); } // if the file isn’t open else { Serial.println(“error opening data.txt”); } }

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

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

Заключение

SD-карта – необходимый элемент любой системы, в которой вы собираетесь использовать медиафайлы. Это будет хорошим решением как для приборов под «умный дом», так и для самодельных плееров. Однако не забывайте, что качество конечного продукта приравнивается к качеству его худшего компонента, и не стоит экономить на SD-картах.

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, а это – тема второй части статьи.

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

Распиновка SD и microSD карт

SD карты могут работать в двух режимах - SD и SPI . Назначение выводов карт и схема подключения зависит от используемого режима. У 8-и разрядных микроконтроллеров AVR нет аппаратной поддержки SD режима, поэтому карты с ними обычно используются в режиме SPI. В 32-х разрядных микроконтроллерах на ядре ARM, например AT91SAM3, интерфейс для работы с картами в SD режиме есть, поэтому там можно использовать любой режим работы.

Назначение контактов SD карты в SD режиме


Назначение контактов SD карты в SPI режиме

Назначение контактов microSD карты в SD режиме



Назначение контактов microSD карты в SPI режиме



Подключение SD и microSD карт к микроконтроллеру в SPI режиме

Напряжение питания SD карт составляет 2.7 - 3.3 В. Если используемый микроконтроллер запитывается таким же напряжением, то SD можно подключить к микроконтроллеру напрямую. Расово верная схема, составленная путем изучения спецификаций на SD карты и схем различных отладочных плат, показана на рисунке ниже. По такой схеме подключены карты на отладочных платах фирм Olimex и Atmel .

На схеме обозначены именно выводы SD карты, а не разъема.


L1 - феррит или дроссель, рассчитанный на ток >100 мА. Некоторые его ставят, некоторые обходятся без него. А вот чем действительно не стоит пренебрегать, так это полярным конденсатором C2. Потому что при подключении карты происходит бросок тока, напряжение питания "просаживается" и может происходить сброс микроконтроллера.

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

Упрощенный вариант схемы (без подтягивающих резисторов) показан на рисунке ниже. Эта схема проверена на практике и используется в платах фирмы Microelectronika. Также она используется во многих любительских проектах, которые можно найти в сети.



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

Если микроконтроллер запитывается напряжением отличным от напряжения питания SD карты, например 5 В, то нужно согласовать логические уровни . На схеме ниже показан пример согласования уровней карты и микроконтроллера с помощью делителей напряжения. Принцип согласования уровней простой - нужно из 5-и вольт получить 3.0 - 3.2 В.



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

Если использовать для согласования уровней буферную микросхему, например CD4050 или 74AHC125, этих недостатков можно избежать. Ниже приведена схема, в которой согласование уровней выполняется с помощью микросхемы 4050. Это микросхема представляет собой 6 неинвертирующих буферов. Неиспользуемые буферы микросхемы "заглушены".

Подключение microSD карт аналогичное, только у них немного отличается нумерация контактов. Приведу только одну схему.



На схемах я рассматривал подключение SD карт к микроконтроллеру напрямую - без разъемов. На практике, конечно, без них не обойтись. Существует несколько типов разъемов и они друг от друга немного отличаются. Как правило, выводы разъемов повторяют выводы SD карты и также содержать несколько дополнительных - два вывода для обнаружения карты в разъеме и два вывода для определения блокировки записи. Электрически эти выводы с SD картой никак не связаны и их можно не подключать. Однако, если они нужны, их можно подключить как обычную тактовую кнопку - один вывод на землю, другой через резистор к плюсу питания. Или вместо внешнего резистора использовать подтягивающий резистор микроконтроллера.

Подключение SD и microSD карт к микроконтроллеру в SD режиме

Ну и для полноты картины приведу схему подключения SD карты в ее родном режиме. Он позволяет производить обмен данными на большей скорости, чем SPI режим. Однако аппаратный интерфейс для работы с картой в SD режиме есть не у всех микроконтроллеров. Например у Atmel`овских ARM микроконтроллеров SAM3/SAM4 он есть.



Шина данных DAT может использоваться в 1 битном или 4-х битном режимах.

Продолжение следует...

Как видно из рисунка после передачи кадра команды необходимо продолжать чтение байтов (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 пиков.

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

 

Возможно, будет полезно почитать: