Электронная читалка своими руками на микроконтроллере ATmega32. Подключение sd карты к микроконтроллеру Распиновка sd и microsd

Мы выводили картинку на дисплей с sd карточки, но в ней были упущены некоторые моменты, первый - подключение самой карточки, второй - была рассмотрена лишь часть функций библиотеки Petit FatFs , давайте остановимся на этих моментах подробнее.

Общение с карточкой возможно по одному из двух интерфейсов, SPI или SD .



Надо сказать, что SD интерфейс может работать в однобитном и четырёхбитном режимах.

Схема подключения карточки по SPI стандартная и выглядит следующим образом, не используемые выводы карточки нужно с помощью резистора 10К подтянуть к питанию.


Но в любительских конструкциях зачастую пренебрегают подтягивающими резисторами, упрощая схему подключения.

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

На схемах, изображённых выше видно, что для работы карточке необходимо 3.3 вольта, соответственно, в линиях передачи данных напряжение не должно выходить за диапазон 0 – 3.3 вольт и тут возникает вопрос, что делать если МК питается от 5 вольт?
Ответ прост, надо согласовать линии передачи данных, а сделать это можно с помощью обычного резистивного делителя.


На схеме видно, что линию MISO согласовывать не надо так, как по этой линии данные передаются от карточки к МК .
На самом деле, мало кто подключает карточку напрямую к МК, гораздо удобнее подключить к МК разъём для карточки или купить шилд с разъемом и всей необходимой обвязкой.

С подключением разобрались, давайте теперь рассмотрим как пользоваться библиотекой Petit FatFs , которая предназначена для 8-битных микроконтроллеров с малым размером памяти.

Библиотека состоит из 5 файлов:
integer.h - заголовочный файл в котором описаны основные типы данных.

diskio.h - заголовочный файл в котором объявлены прототипы низкоуровневых функций для работы с диском и статусные коды, которые они возвращают.

diskio.c - в этом файле должны быть реализованы низкоуровневые функции, изначально там "заглушки".

pffсonf.h - конфигурационный файл.

pff.h - заголовочный файл в котором объявлены прототипы функций взаимодействия с файловой системой диска.

pff.c - файл содержит реализации функций для взаимодействия с файловой системой диска.

Видно, что для того чтобы библиотека заработала необходимо реализовать низкоуровневые функции. Но если речь идет о AVR или PIC, для них сайте можно скачать пример работы с библиотекой, в котором есть файл mmc , в нем уже реализованы низкоуровневые функции. Также необходимо задать конфигурацию библиотеки в файле pff.h и написать функции необходимые для работы SPI.

Функции Petit FatFs.

FRESULT pf_mount (FATFS*) - функция монтирует/демонтирует диск. Эту функцию необходимо вызывать до начала работы с диском, если вызвать функцию с нулевым указателем диск демонтируется. Функция может быть вызвана в любой момент времени.

Параметры
FATFS* fs - указатель на объект типа FATFS, описание этой структуры можно посмотреть в файле pff.h. Нам надо всего лишь объявить переменную такого типа.

Возвращаемые значения:
FR_OK (0)
FR_NOT_READY - устройство не может быть инициализировано
FR_DISK_ERR - возникла ошибка во время чтения с диска
FR_NO_FILESYSTEM - на диске нет правильного раздела FAT

FATFS fs;//объявляем объект типа FATFS //монтируем диск if (pf_mount(&fs) == FR_OK) { //диск смонтирован, работаем с ним //демонтируем диск pf_mount(NULL); } else { //не удалось смонтировать диск }

FRESULT pf_open (const char* path) - функция открывает существующий файл. После того как файл открыт с ним можно работать, то есть читать из него и записывать в него. С открытым файлом можно работать до тех пор, пока не будет открыт другой файл. Функция может быть вызвана в любой момент времени.

Параметры
const char* path - указатель на строку, указывающую путь к файлу. Путь надо указывать полностью относительно корневой директории, разделяя директории слэшем.

Возвращаемые значения:
FR_OK (0) - возвращается в случае успешного выполнения функции
FR_NO_FILE - файл не найден
FR_DISK_ERR - ошибка диска
FR_NOT_ENABLED - диск не был смонтирован

FATFS fs;//объявляем объект типа FATFS //монтируем диск if (pf_mount(&fs) == FR_OK) { //открываем файл лежащий в корневой директории if(pf_open("hello.txt") == FR_OK) { //делаем что-то } //открываем файл лежащий в папке new if(pf_open("new/hello.txt") == FR_OK) { //делаем что-то } //демонтируем диск pf_mount(NULL); } else { //не удалось смонтировать диск }

FRESULT pf_read(void* buff, WORD btr, WORD* br) - функция читает указанное количество байт из файла и сохраняет их в буфер. Если количество прочитанных байт меньше чем указано, значит был достигнут конец файла.
#define _USE_READ 1

Параметры:
void* buff - указатель на буфер, в котором сохраняются прочитанные данные
WORD btr - количество байт, которые нужно прочитать
WORD* br - указатель на переменную, в которой хранится количество прочитанных байт.

Возвращаемые значения:
FR_OK (0) - возвращается в случае успешного выполнения функции
FR_DISK_ERR - ошибка диска
FR_NOT_OPENED - файл не был открыт
FR_NOT_ENABLED - диск не был смонтирован

FATFS fs;//объявляем объект типа FATFS BYTE buff;//буфер для чтения файла WORD br; //счетчик прочитанных байт //монтируем диск if (pf_mount(&fs) == FR_OK) { //открываем файл лежащий в корневой директории if(pf_open("hello.txt") == FR_OK) { //читаем из него 10 байт pf_read(buff, 10, &br); if(br != 10) { //если br не равно 10 //значит мы достигли конца файла } } }

FRESULT pf_write(const void* buff, WORD btw, WORD* bw) - функция позволяет записывать данные в открытый файл. Для того чтобы функция работала в файле pffconf.h надо записать
#define _USE_WRITE 1

Параметры:
void* buff - указатель на буфер, который хотим записать, нулевое значение финализирует запись
WORD btw - количество байт, которые хотим записать
WORD* bw - указатель на переменную, хранящий количество байт, которые удалось записать. Анализируя, эту переменную можно узнать был ли достигнут конец файла.

Возвращаемые значения:
FR_OK (0) - возвращается в случае успешного выполнения функции
FR_DISK_ERR - ошибка диска
FR_NOT_OPENED - файл не был открыт
FR_NOT_ENABLED - диск не был смонтирован

Из-за того, что библиотека рассчитана на микроконтроллеры с малым объемом памяти, эта функция имеет ряд ограничений:

  • нельзя создавать новые файлы, а записывать можно только в существующие
  • нельзя увеличивать размер файла
  • нельзя обновить временную метку
  • операцию записи можно начать/остановить только на границе сектора
  • файловый атрибут "только для чтения" не может запретить запись

Для того чтобы понять предпоследний пункт, надо знать, что память карточки разбита на блоки(сектора) по 512 байт и запись можно начать только с начала сектора . Таким образом если мы хотим записать 1000 байт, то первый сектор запишется полностью, а во второй запишется только 488 байт, а оставшиеся 24 байта заполнятся нулями.

Для записи в открытый файл надо выполнить следующие действия:

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

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

FRESULT pf_lseek(DWORD offset) - устанавливает указатель чтения/записи в открытом файле. Устанавливать указатель можно абсолютным или относительным смещением, для абсолютного смещения необходимо передать в функцию число
pf_lseek(5000);
для относительного, передать значение указателя на текущую позицию fs.fptr и величину смещения
pf_lseek(fs.fptr + 3000);
Для того чтобы функция работала в файле pffconf.h надо записать
#define _USE_LSEEK 1

Параметры:
DWORD offset - количество байт, на которые нужно сместить указатель.

Возвращаемые значения:
FR_OK (0) - возвращается в случае успешного выполнения функции
FR_DISK_ERR - ошибка диска
FR_NOT_OPENED - файл не был открыт

Записать данные в файл можно следующим образом.
FATFS fs;//объявляем объект типа FATFS BYTE buff;//буфер для чтения файла WORD br; //счетчик прочитанных байт //монтируем диск if (pf_mount(&fs) == FR_OK) { //открываем файл лежащий в корневой директории if(pf_open("hello.txt") == FR_OK) { //устанавливаем указатель на первый сектор pf_lseek(0); //записываем pf_write(buff, 10, &br); //финализируем запись pf_write(0, 0, &br); } }

Также оставляю тут кусок реально работающего кода, в котором используются все выше описанные функции.
#define F_CPU 8000000UL #define buff_size 10 #include #include #include "diskio.h" #include "pff.h" #include "spi.h" FATFS fs;//объявляем объект типа FATFS BYTE read_buff;//буфер для чтения файла BYTE write_buff = "hello word";////буфер для записи в файл UINT br; //счетчик прочитанных байт int main(void) { //монтируем диск if (pf_mount(&fs) == FR_OK) { //открываем файл лежащий в папке new if(pf_open("new/hello.txt") == FR_OK) { //устанавливаем указатель записи pf_lseek(0); //записываем pf_write(write_buff, buff_size, &br); //финализируем запись pf_write(0, 0, &br); //устанавливаем указатель чтения pf_lseek(0); //читаем то, что записали pf_read(read_buff, buff_size, &br); if(br != buff_size) { //если br не равно buff_size //значит мы достигли конца файла } } //демонтируем диск pf_mount(NULL); } while(1) { } }

FRESULT pf_opendir(DIR* dp, const char * path) - функция открывает существующую директорию и создает указатель на объект типа DIR, который будет использоваться для получения списка файлов открытой директории.
Для того чтобы функция работала в файле pffconf.h надо записать
#define _USE_DIR 1

Параметры:
DIR *dp - указатель на переменную типа DIR.

const char * path - указатель на строку, которая содержит путь к директории, директории разделяются слэшем

Возвращаемые значения:
FR_OK (0) - возвращается в случае успешного выполнения функции
FR_NO_PATH - не удалось найти путь
FR_NOT_READY - не удалось инициализировать диск
FR_DISK_ERR - ошибка диска
FR_NOT_ENABLED - диск не был смонтирован

//объявляем переменные FATFS fs; DIR dir; //монтируем диск pf_mount(&fs); //открываем директорию pf_opendir(&dir, "MY_FOLDER");

FRESULT pf_readdir(DIR* dp, FILINFO* fno) - функцию позволяет прочитать содержимое директории. Для этого нужно открыть директорию с помощью функции pf_opendir() и вызывать pf_readdir(). Каждый раз при вызове функция будет возвращать название объекта(папки/файла) лежащего в указанной директории. Когда она пройдется по всем объектам, вернет нулевую строку в элементе массива fno.fname.
Для того чтобы функция работала в файле pffconf.h надо записать
#define _USE_DIR 1

Параметры:
DIR *dp - указатель на переменную типа DIR, которая должна быть предварительно объявлена

FILINFO *fno - указатель на переменную типа FILINFO, которая должна быть предварительно объявлена.

Возвращаемые значения:
FR_OK - успешное завершение функции
FR_DISK_ERR - ошибка диска
FR_NOT_OPENED - не открыта директория

FATFS fs; FRESULT res; FILINFO fno; DIR dir; //монтируем диск pf_mount(&fs); //открываем директорию res = pf_opendir(&dir, MY_FOLDER); //читаем содержимое директории for(;;){ res = pf_readdir(&dir, &fno); //проверяем не возникло ли ошибок при чтении // и есть ли еще файлы в указанной директории if ((res != FR_OK) || (fno.fname == 0)){ break; } //выводим удобным способом fno.fname usart_sendStr(fno.name); usart_sendStr(/r); }

Ну и напоследок оставлю тут рабочий проект

Сегодня 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-карты в своих проектах, и если да, то для каких задач?

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

В проект изначально использовалась карта памяти microSD объемом 1 ГБайт. Микроконтроллер - ATmega8 или ATmega32, работающий на частоте 8 МГц от внутреннего RC осциллятора. Кроме того, для подключения макета к персональному компьютеру для мониторинга данных использовался интерфейс RS-232. Для преобразования логических уровней интерфейса используется микросхема MAX232 . Для питания схемы необходим стабилизированный источник питания 3.3 В (микросхема MAX232 рассчитана на напряжение питания 5 В, однако, как показала практика, сохраняет работоспособность при 3.3 В). Подключение карты памяти по 7-проводной схеме, согласно распиновке (см. рис).

Принципиальная схема для микроконтроллера ATmega8.

Подтягивающие резисторы R1, R2 номиналом 51 кОм интерфейса SPI придают лучшую стабильность при работе с различными картами. Стабилитроны D1, D2 предназначены для защиты карты памяти при работе внутрисхемного программатора (ISP). Выводы микросхемы MAX232 VCC и GND на схемах не указаны, но их необходимо подкличить к соответствующим точкам схемы.

Принципиальная схема для микроконтроллера ATmega32

Принципиальная схема для микроконтроллера ATmega32 (добавлены часы реального времени на микросхеме DS1307)

Как вы заметили, питание последнего варианта устройства осуществляется от источника 12 В, а на плате установлены два регулятора напряжения 5.0 В (LM7805) и 3.3 В (LM1117-3.3). Для питания интерфейса SD карты используется 3.3 В, вся остальная часть схемы питается от источника 5.0 В. Микросхема часов реального времени DS1307 в стандартном включении и подключена к интерфейсу I2C микроконтроллера.

Сперва был изучен «сырой» формат передачи данных, на примере операций чтения любого блока данных, чтения и записи нескольких блоков данных, стирания нескольких блоков, записи данных в любой блок памяти SD. Устройство, собранное на макетной плате, подключалось к компьютеру по интерфейсу RS-232. Для отображения прочитанных данных с карты памяти, а также для ввода и записи данных на карту используется программа HyperTerminal (или аналогичная) на компьютере.

После удачной реализации обмена данными без спецификации, карта памяти была отформатирована (FAT32) в операционной системе Windows XP, затем на карту были записаны несколько текстовых файлов, директорий и другие типы файлов (в корневую директорию карты). После этого были написаны подпрограммы и функции по работе с файловой системой FAT32 для чтения файлов, для получения списка файлов на карте памяти (с использованием HiperTerminal), для получения информации о полном и свободном объеме памяти.

Вид окна программы HiperTerminal с функциями по работе с картой памяти SD:

Пользователю предлагаются свыше 10 опций по работе с картой памяти (для варианта с часами).

Опции 0 - 4 - это низкоуровневые функции. Gосле использования опций 0 - 3 Вам необходимо переформатировать карту перед использованием FAT32 подпрограмм.
Опции 5 - 9 - относятся к файловой системе FAT32. На данный момент поддерживаются только короткие имена файлов (8 Байт - имя файла, 3 Байта - расширение файла). Если будут записаны файлы с длинными именами, то они будут отображены в терминальной программе в коротком формате. Для тестирования этих опций не забудьте отформатировать карту в файловой системе FAT32, записать несколько директорий и текстовых файлов.

Описание опций:

0 - Erase Blocks - стирание выбранного количества блоков начиная с указанного.
1 - Write Single Block - запись данных в блок с определенным адресом. Данные вводятся с клавиатуры в программе Hiperterminal;
2 - Read Single Block - чтение данных с блока с определенным адресом. Прочитанные данные отображаются в окне терминальной программы;
3 - Writing multiple blocks - запись нескольких блоков, начиная с определенного адреса;
4 - Reading multiple blocks - чтение нескольких блоков, начиная с определенного адреса.

Примечание. Здесь функции работы с несколькими блоками (опции 3 и 4) отключены из-за нехватки памяти микроконтроллера ATmega8, поскольку эти функции не нужны для тестирования файловой системы FAT32. Для включения этих опций необходимо удалить макрос в файле SD_routines.h (#define FAT_TESTING_ONLY). И, если Вы используете ATmega8, на время тестирования опций 3 и 4 библиотека FAT32 может быть удалена с целью освобождения памяти микроконтроллера.

5 - Get File List - отображает список доступных директорий и файлов с занимаемым ими объемом памяти (в корневой директории карты);
6 - Read File - чтение указанного файла и отображение содержимого в окне терминальной программы;
7 - Create File - создать/добавить файл с указанным именем;
8 - Delete File - удалить все файлы файл с указанным именем;
9 - Read SD Memory Capacity - информация о полном и свободном объеме карты памяти (используется FSinfo сектор SD карты).

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

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

Artem Makarov aka Robin

27.09.2014

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

Восстановление монолитных SD карт памяти

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

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

Дорожки и распиновка монолитной SD карты

Видны контактные площадки, к которым подключены шина данных, chip enable, read/write busy, питание и т.п. Разумеется ничего не промаркировано, и даташитов, в которых подробно расписано, что куда подключать, в свободном доступе так же нету. Распиновку можно отыскать либо взяв точно такую же исправную флешку (а их великое множество типов, и найдя такой же по виду условный SD Kingston, можно получить внутри совершенно по другому сделанный девайс) и вооружившись логическим анализатором кропотливо изыскивать что куда и зачем. Либо купив распиновку у человека/конторы, которые такую работу за тебя уже сделали.

В итоге получается нечто такое:

Или такое:

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

а когда нужная маска XOR подобрана и применена, то сектор приобретает осмысленный вид:

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

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

Безусловно, многие операции достаточно автоматизированы, но тем не менее объем работ при восстановлении данных с монолитов (монокристаллов) весьма велик. Далеко не каждый инженер или компания, восстанавливающая информацию, горит желанием с такими работами связываться. И ценник на такого рода восстановление весьма далёк от понятия "бюджетный".

Вот еще один случай на примере восстановления SD Sandisk - такой же монолит, только внутри чуть по-другому сделан:

Готово для чтения

Восстановление MicroSD флешек

А вот как выглядят контактные площадки на Micro SD карточке. Сразу нужно оговориться, что это только несколько примеров из множества вариантов компоновки.

А вот вариант распиновки монолитной карты памяти Memory Stick Pro Duo

Вот - не сказать что монолит, но и не обычная USB флешка. Микросхема памяти (кристалл) залита компаундом (клеем).

А вот как выглядит монолитная карта памяти Olympus XD Picture card, с которой потребовалось восстановить фотоснимки:

Восстановление поломанных Микро СД

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

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

Лаборатория "Хардмастер" одна из немногих, имеющих опыт и квалификацию в восстановлении данных с монолитных USB, SD, microSD, Memory Stick и т.п. карт памяти. Если на вашей монолитной поломанной флешке остались важные файлы которые хотелось бы вернуть - обращайтесь к нам!

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)
Вверх