Браузеры. Программы. Социальные сети. Сервисы

Браузеры. Программы. Социальные сети. Сервисы

» » Распиновка Secure Digital (SD) card. Подключение sd карты к микроконтроллеру Распайка разъема sd карты памяти

Распиновка Secure Digital (SD) card. Подключение sd карты к микроконтроллеру Распайка разъема sd карты памяти

В устройствах на микроконтроллерах для хранения больших объемов данных используется внешняя память. Если требуется хранить единицы мегабайт, то подойдут микросхемы последовательной флэш памяти. Однако для больших объемов (десятки -сотни мегабайт) обычно применяются какие-нибудь карты памяти. В настоящий момент наибольшее распространение получили 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-х битном режимах.

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

SD cards are based on the older Multi Media Card (MMC) format, but most are physically slightly thicker than MMC cards. They also boast higher data transfer rates. DRM features are available but are little-used. SD cards generally measure 32 mm × 24 mm × 2.1 mm, but can be as thin as 1.4 mm, just like MMC cards.

There are different speed grades available. They are referred to with the same nx notation as CD-ROMs; a multiple of 150 kB/s. Devices with SD slots can use the thinner MMC cards, but the standard SD cards will not fit into the thinner MMC slots. MiniSD and MicroSD cards can be used directly in SD slots with an adapter. There are readers which allow SD cards to be accessed via many connectivity ports such as USB, FireWire.

Pin SD Mode SPI Mode
Name Type Description Name Type Description
1 CD/DAT3 I/O/PP Card detection / Connector data line 3 CS I Chip selection in low status
2 CMD PP Command/Response line DI I Data input
3 Vss1 S GND VSS S GND
4 Vdd S Power supply VDD S Power supply
5 CLK I Clock SCLK I Clock
6 Vss2 S GND VSS2 S GND
7 DAT0 I/O/PP Connector data line 0 DO O/PP Data output
8 DAT1 I/O/PP Connector data line 1 RSV
9 DAT2 I/O/PP Connector data line 2 RSV

SD cards interface is compatible with standard MMC card operations. All SD memory and SDIO cards are required to support the older SPI/MMC mode which supports the slightly slower four-wire serial interface (clock, serial in, serial out, chip select) that is compatible with SPI ports on many microcontrollers. Many digital cameras, digital audio players, and other portable devices probably use MMC mode exclusively. MMC mode does not provide access to the proprietary encryption features of SD cards, and the free SD documentation does not describe these features. As the SD encryption exists primarily for media producers, it is not of much use to consumers who typically use SD cards to hold unprotected data.

There are three transfer modes supported by SD: SPI mode (separate serial in and serial out), one-bit SD mode (separate command and data channels and a proprietary transfer format), and four-bit SD mode (uses extra pins plus some reassigned pins) to support four bit wide parallel transfers. Low speed cards support 0 to 400 kbit/s data rate and SPI and one-bit SD transfer modes. High speed cards support 0 to 100 Mbit/s data rate in four-bit mode and 0?25 Mbit/s in SPI and one-bit SD modes.

SD cards security features includes:

  • Copyright protection mechanism with the SDMI standard (Secure Digital Music Initiative)
  • Integrated CPRM file protection and encryption system (CPRM is a Content Protection for Recordable Media)

Урок 33

Часть 1

SPI. Карта SD. FAT

Сегодня мы продолжим нашу любимую тему по интерфейсу SPI. Закончили мы с данной шиной друг к другу контроллеров Atmega8a и ATTtiny2313. А сегодня мы по данному интерфейсу попробуем подключить к микроконтроллеру по данной шине карту памяти SD (Secure Digital) .

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

Тем не менее мы посмотрим распиновку ножек карты по обоим типам

Ну, так как нас интересует второй тип, с ним и будем разбираться.

А разбираться тут особо не в чем. Все эти аббревиатуры нам известны. Здесь все стандартные ножки интерфейса SPI и ничего тут лишнего нет.

Теперь вообще про карту. Данная карта нам позволяет хранить данные, тип памяти у неё FLASH, который по сравнению с памятью типа EEPROM также является энергонезависимым, то есть при отключении питания данные никуда не пропадают, а остаются храниться. Также данная память имеет отличия, мы с ними познакомимся в процессе программирования. Одно из главных отличий то, что мы уже как в память EEPROM в данную память не можем записать один байт. Теоретически то конечно можем, но только запишутся туда либо только единички из нашего байта либо только нули в зависимости от типа FLASH — NOR или NAND. То есть прежде чем писать байт, нужно его стереть, а в силу организации данной памяти, стирать мы можем только блоками, вот и писать следовательно также только блоками. Но зато есть величайшее отличие от EEPROM — это цена. Она в разы дешевле, даже порой на порядки за одну хранящуюся единицу инфорамции (за мегабайт, за гигабайт). Поэтому у памяти FLASH как правило всегда гораздо больший объём информации.

Существуют 4 типа SD, но это мы изучим немного позднее.

Подключим данную карту пока в протеусе

Здесь всё просто. На самом деле не совсем так. Нужны ещё резисторы

Данные резисторы нужны для того, чтобы обеспечить соответствующие уровни, так как карта питается от 3,3 вольт. Вообще по технической документации от 2,7 до 3,6 вольт.

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

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

Также у нас подключен дисплей, как и на по расширению функционала библиотеки дисплея.

Вот так у нас всё выглядит в практической схеме

Вот так вот выглядит модуль с держателем

Найти такой модуль можно везде, стоит он копейки. Тот модуль, который конектится по SDIO, стоит дороже. Мы видим также, что на модуле уже установлена микросхема для понижения напряжения до 3,3 вольта. А подключаем питание мы только на контакт 5 вольт, а на 3,3 не подключаем ничего

Также на модуле установлены все делители для уровней, то есть данный модуль рассчитан именно на подключение к 5-вольтовым устройствам.

А флеш-карту для тестов я откопал на 32 мегабайта, именно мегабайта а не гигабайта

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

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

Удалим ненужные строки, в main() у нас останется только во это

int main ( void )

unsigned int i ;

Port_ini ();

LCD_ini (); //инициализируем дисплей

Clearlcd (); //очистим дисплей

Setpos (0,0);

Str_lcd ( "String 1" );

Setpos (2,1);

Str_lcd ( "String 2" );

Setpos (4,2);

Str_lcd ( "String 3" );

Setpos (6,3);

Str_lcd ( "String 4" );

Delay_ms (2000);

// for (i=0;i<=22;i++) {str80_lcd(buffer2+i*20);_delay_ms(1000);}

While (1)

Так как мы посимвольно не будем выводить текст, то можно будет в переменной обойтись типом char

unsigned char i ;

Теперь ещё один нюанс.

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

Создать его не сложно. Одним из способов является создание с помощью программы WinImage.

Мы в ней стандартно создаём новый файл с помощью пункта меню File — > New. Выбираем в диалоге самый последний пункт и жмём "OK"

Для теста в протеусе нам вполне хватит размера 4 мегабайта, поэтому поменяем в следующем диалоге поле с номером секторов, а также выберем формат FAT12/16, потому что с 32-битной файловой системой немного другая специфика работы, и также нажмём "OK"

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

Затем мы сохраняем наш созданный файл с помощью пункта меню File -> Save As. И сохраняем мы его в ту папку, где у нас находится сохранённый проект протеуса. Назовём файл и нажмём "Сохранить"

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

Путь нам никакой не нужен, так как файл у нас находится в папке с проектом. Жмём "ОК".

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

Добавим макроподстановки для ножек порта

#include "main.h"

#define MOSI 3

#define MISO 4

#define SCK 5

#define SS 2

Добавим код для инициализации ножек в функцию инициализации портов

void port_ini ( void )

PORTD =0x00;

DDRD =0xFF;

PORTB |=(1<< SS )|(1<< MISO )|(1<< MOSI );

DDRB |=(1<< SS )|(1<< MOSI )|(1<< SCK );

Мы оставляем на вход ножку MISO, так как по умолчанию все биты в регистре равны нулю, и мы его просто не трогает. Также мы включаем сразу высокий уровень в MOSI и SS, а к MISO подтягиваем резистор.

Напишем функцию передачи байта по шине SPI

void SPI_SendByte ( unsigned char byte )

{

}

Добавим переменную для цикла и сам цикл

void SPI_SendByte ( unsigned char byte )

unsigned char i ;

for ( i =0; i <8; i ++) //движемся по битам байта

{

}

Я думаю, понятно почем мы считаем до 8, так как битов мы передаём именно 8.

Ну и начнём их передавать потихоньку.

Проверим сначала самый левый бит, выделив его из всего байта маскированием, и, если он у нас равен 1, то выставим 1 и на шине MOSI, а если 0 — то не трогаем шину

for ( i =0; i <8; i ++) //движемся по битам байта

Как видно из рисунка после передачи кадра команды необходимо продолжать чтение байтов (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-карты используются повсюду. Они втыкаются в ноутбуки, планшеты, телефоны, видеокамеры, роутеры, фоторамки, диктофоны, электронные книги, mp3-плееры, одноплатные компьютеры и даже квадрокоптеры — словом, они везде. Часто о них думают, как об относительно медленных устройствах, способных хранить пару гигабайт информации. Однако в наши дни уже доступны SD-карты объемом 512 Гб и скоростью чтения-записи 90 Мбайт/сек (не мегабит!). Теоретически же объем хранимой информации ограничен 2 Тб. А чем еще прекрасны SD-карты, это тем, что с ними можно работать по незамысловатому протоколу, основанному на SPI.

Немного матчасти

«SD» расшифровывается как «Secure Digital». Причем тут безопасность не знает никто. Внутри SD-карты находится обычная flash-память и микроконтроллер, осуществляющий общение с внешним миром. То есть, в первом приближении, это точно такая же non-volatile память, как и SPI flash .

SD-карты бывают трех типов. Карты SDSC (SC = Standard Capacity) позволяют хранить до 2 Гб информации и используют файловую систему FAT12 или FAT16. Эти карты морально устарели, в магазинах их найти непросто, да и по цене они сопоставимы с картами большего объема. Кроме того, они используют протокол, несколько отличающийся от протокола SDHC/SDXC-карт. В силу названных причин, с этого момента про существование SDSC мы забудем. К современным типам карт относятся SDHC (HC = High Capacity), использующие файловую систему FAT32 и способные хранить до 32 Гб данных, а также SDXC (XC = eXtended capacity), использующие exFAT и имеющие объем до 2 Тб. С точки зрения протокола эти карты неотличимы. Разница заключается только в файловой системе, выбор которой диктуется спецификацией.

Разумеется, ничто не мешает отформатировать SDHC карту под exFAT, или SDXC карту под какой-нибудь ZFS. Но ваш смартфон или фотоаппарат, вероятно, не сможет работать с такой картой.

Fun fact! Встречаются поддельные SDHC карты, которые на самом деле являются SDSC. В обычном магазине вы такие, скорее всего, не найдете, а вот на eBay налететь можно. Если вам предлагают купить типа SDHC карту объемом всего лишь 1 Гб , она наверняка на самом деле является SDSC.

Карты разделяют на различные классы, в зависимости от минимальной последовательной скорости записи (обратите внимание на выделение курсивом). Класс скорости обозначают в стиле C4 (class 4) или V30 (class 30). В обоих случаях цифра означает скорость в Мбайт/сек. Отличие C от V заключается только в том, что V намекает на пригодность карты для записи видео высокого разрешения. Еще встречаются маркировки U1 и U3 для 10 Мбайт/сек и 30 Мбайт/сек соответственно, где U означает Ultra High Speed. C10, V10 и U1 — это одно и то же.

SD-карты бывают разных форм-факторов — SD, MiniSD и MicroSD. MiniSD сегодня практически не встречаются. Многие карты выпускаются в форме MicroSD с переходником в обычный SD-формат. Это позволяет покупателям использовать карты с различными устройствами.

На следующем фото изображена моя небольшая коллекция SD и MicroSD-карт, а также модулей для подключения их к отладочным платам (Arduino, Nucleo и подобным):

Все представленные здесь модули работают одинаково хорошо. Если сомневаетесь, какой брать — берите тот, что изображен слева внизу. Он позволяет работать как с SD, так и с MicroSD-картами (через переходник), а также имеет дополнительные пины для подключения логического анализатора . Модуль не составляет труда найти на eBay. Еще встречаются модули вообще без резисторов, стабилизаторов напряжения и так далее, имеющие только слот для подключения карты и пины. С ними некоторые карты работать не будут! Далее станет понятно, почему. Наконец, модуль легко спаять из адаптера для MicroSD-карт. Далее будет рассказано, как.

Подключение SD-карты

Ниже изображена распиновка SD и MicroSD-карт (иллюстрация позаимствована отсюда):

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

  • Ни в коем случае не подавайте 5 В на пин VDD! Все SD-карты гарантировано работают от 3.3 В. Некоторые при этом также могут работать и от 5 В, но это не гарантируется. Если подать 5 В, вы рискуете спалить вашу дорогую карточку на 128 Гб, после чего ее останется только выкинуть;
  • По тем же соображениям, если ваш проект использует пятивольтовую логику, крайне рекомендуется использовать конвертер уровней, например TXS0108E (даташит );
  • Платы Arduino имеют пин 3V3, но не могут подавать на него большой ток. Если запитать SD-карту от этого пина, можно словить забавные глюки. Например, карта будет нормально работать в одиночестве, но переставать работать при подключении к плате TFT-экранчика на базе ST7735 , чья подсветка также питается от 3V3. Поэтому, если вы проектируете модуль или Arduino-шилд, используйте понижающий стабилизатор напряжения на 3.3 В вроде AMS1117 , питаемый от 5 В;
  • Пин DO (он же MISO) должен быть обязательно подтянут к плюсу через резистор на 10 кОм или около того. Некоторые карты просто не будут стартовать без этого резистора. Например, я наблюдал такое поведение на картах производства Sony;

Теперь становится понятно, почему простые модули, имеющие только слот для подключения карты, не очень подходят. Также теперь ясно, как сделать модуль для подключения MicroSD-карт из адаптера. Отмечу, что пины с землей (VSS1 и VSS2) в адаптере, как правило, уже соединены между собой, поэтому дополнительно соединять их проводочком не требуется. На всякий случай стоит перепроверить, соединены ли пины, прозвонив их мультиметром.

Тонкости протокола

Хорошее описание протокола было найдено в статье How to Use MMC/SDC на сайте elm-chan.org. Здесь я не вижу смысла ее пересказывать. Заинтересованные читатели могут ознакомиться с оригиналом, а также с полной реализацией протокола для микроконтроллеров STM32 в исходниках к данному посту. Вместо пересказа я лишь пробегусь по основным моментам. Также отмечу, что в статье я не нашел упоминание нескольких крайне важных нюансов, про которые будет рассказано далее.

Итак, типичная команда выглядит как-то так:

Команды всегда имеют формат 01xxxxxx, и в соответствии со значением битов xxxxxx называются CMD0, CMD1, и так далее до CMD63. Следом за командой идут 4 байта аргумента, за которыми идет байт в формате yyyyyyy1 с семибитным CRC. Контрольные суммы по умолчанию выключены и проверяются только для первых нескольких команд на этапе инициализации. В остальных же случаях CRC заполняется единицами.

Большинство команд получают в ответ один байт, так называемый R1:

/*
R1: 0abcdefg
||||||`- 1th bit (g): card is in idle state
|||||`-- 2th bit (f): erase sequence cleared
||||`--- 3th bit (e): illigal command detected
|||`---- 4th bit (d): crc check error
||`----- 5th bit (c): error in the sequence of erase commands
|`------ 6th bit (b): misaligned addres used in command
`------- 7th bit (a): command argument outside allowed range
(8th bit is always zero)
*/

Если старший бит ответа равен единице, значит SD-карта еще обрабатывает запрос. Иногда за R1 следуют дополнительные данные. Также в определенных ситуациях в протоколе фигурируют data tokens (байты 0 x FC, 0 x FE), stop transaction token (0 x FD), error token и data response. Детали не слишком захватывающие, к тому же, они хорошо описаны на elm-chan.org и их можно изучить по коду. Куда интереснее то, чего в статье нет или обозначено не слишком явно.

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

static int SDCARD_ReadBytes(uint8_t * buff, size_t buff_size) {
// make sure FF is transmitted during receive
uint8_t tx = 0xFF ;
while (buff_size > 0 ) {
HAL_SPI_TransmitReceive(& SDCARD_SPI_PORT, & tx, buff, 1 ,
HAL_MAX_DELAY) ;
buff++;
buff_size--;
}

return 0 ;
}

Во-вторых, в статье верно описано, что в определенных случаях карта может помечать себя занятой (busy), притягивая MISO к земле. В таких ситуациях нужно дождаться готовности карты. Но на практике оказалось, что проверку на готовность нужно выполнять перед каждой командой, даже если в текущих обстоятельствах карта не может быть занятой. То есть, по сути, перед каждой командой нужно посылать 0 x FF (на иллюстрации с форматом команд этот момент опущен). Иначе некоторые карты отказываются работать. В частности, я наблюдал такое поведение у карт производства SanDisc.

Соответствующая проверка:

static int SDCARD_WaitNotBusy() {
uint8_t busy;
do {
if (SDCARD_ReadBytes(& busy, sizeof (busy) ) < 0 ) {
return - 1 ;
}
} while (busy != 0xFF ) ;

return 0 ;
}

Fun fact! Понять я это смог, подглядев в Arduino-библиотеку SD. Пользуясь случаем, отмечу, что библиотека эта в целом довольно скверная. Мне не кажется очень хорошей идеей мешать в одну кучу код для SDSC и SDHC/SDXC карт, как сделано в этой библиотеке. Также я заметил, что в ней почему-то отсутствует поддержка CMD18 (READ_MULTIPLE_BLOCK), несмотря на то, что CMD25 (WRITE_MULTIPLE_BLOCK) реализована. И еще библиотека отказалась работать с некоторыми имеющимися у меня картами, несмотря на то, что код, написанный мной с нуля, прекрасно с ними работает. Вот и пользуйся после этого готовыми библиотеками!

Наконец, в третьих, карта может делить SPI-шину с другими устройствами. Понятно, что в этом случае первым делом после запуска прошивки нужно пометить все устройства, как неактивные, подав соответствующее напряжение, обычно высокое, на пины CS. После чего уже можно спокойно общаться с каждым устройством по отдельности, не беспокоясь, что какое-то другое устройство по ошибке решит, что обращались с нему. Но проблема заключается в том, что SD-карта определенным образом интерпретирует данные, передаваемые по SPI, даже не являясь выбранным устройством. Если конкретнее, то при инициализации карты нужно передать 74 или больше единицы (например, 10 байт 0 x FF) с высоким напряжением на CS. По этой причине карта либо должна жить на отдельной шине, либо инициироваться перед всеми остальными устройствами. Иначе карта может отказаться работать, я проверял.

Получившаяся библиотека

В ходе изучения мной протокола SD-карт была написана библиотека для STM32, реализующая этот протокол. Библиотека основана на HAL и имеет следующий интерфейс:

#define SDCARD_SPI_PORT hspi1
#define SDCARD_CS_Pin GPIO_PIN_5 // Arduino shield: D4
#define SDCARD_CS_GPIO_Port GPIOB
extern SPI_HandleTypeDef SDCARD_SPI_PORT;

// call before initializing any SPI devices
void SDCARD_Unselect() ;

// all procedures return 0 on success, < 0 on failure
// size of block == 512 bytes

int SDCARD_Init() ;
int SDCARD_GetBlocksNumber(uint32_t * num) ;
int SDCARD_ReadSingleBlock(uint32_t blockNum, uint8_t * buff) ;
int SDCARD_WriteSingleBlock(uint32_t blockNum, const uint8_t * buff) ;

// Read Multiple Blocks
int SDCARD_ReadBegin(uint32_t blockNum) ;
int SDCARD_ReadData(uint8_t * buff) ;
int SDCARD_ReadEnd() ;

// Write Multiple Blocks
int SDCARD_WriteBegin(uint32_t blockNum) ;
int SDCARD_WriteData(const uint8_t * buff) ;
int SDCARD_WriteEnd() ;

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

Заключение

В качестве источников дополнительной информации я бы рекомендовал следующие:

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

Вооружившись полученными сегодня знаниями, можно реализовать много безумных идей. Например, можно сделать RAID из SD-карточек, или устройство с интерфейсом SD-карты, сжимающее и/или шифрующее данные. Или вообще отправляющее их на сервер по беспроводной связи. Конечно же, совершенно не был затронут вопрос работы с файловыми системами. Ему будет посвящена одна из следующих заметок.

На этом у меня пока все. А доводилось ли вам использовать SD-карты в своих проектах, и если да, то для каких задач?