Интернет. Безопасность. Программы. Полезные советы

Работа с SD картой. Подключение к микроконтроллеру. Ч1. Восстановление данных с монолитных SD и MicroSD карт Подключение микроконтроллеров к sd карте

Несколько лет назад в мае 2011 создателем культовой игры «Elite» Дэвидом Бребеном был представлен первый концепт одноплатного компьютера Raspberry Pi. Этот момент стал переломным в моей жизни. Идея сделать компьютер размером с флеш-накопитель лежала на поверхности, но большой размах получила лишь с помощью компании Raspberry Pi Foundation.

Уже 25 июля 2011 в производство отдается альфа версия компьютера. К сожалению, у проекта изменился концепт, и теперь он позиционируется как компьютер размером с кредитную карту. Несмотря на это обстоятельство за ним следили миллионы людей. Эффекту толпы повиновался и я, ежедневно проверяя официальную страницу проекта. Началось длительное и мучительное ожидание «чуда», которое случилось 29 февраля 2012 года – старт продаж.

Купить Raspberry Pi можно было через сеть Farnell либо у RS Components . Как оказалось, 29 февраля можно было сделать лишь предзаказ. В наличии этих плат не было ни у одной из контор. Первая партия устройств составляла всего 10000 экземпляров, поэтому, учитывая ажиотаж вокруг проекта, оформить заказ было очень тяжело. Однако, преодолев все трудности, в 14:06 того же дня компьютер был куплен за 46.73 фунтов стерлингов у Farnell’а.

Так долго не выполняли ни один из моих зарубежных заказов. Меня крайне огорчил тот момент, что Farnell, взяв за доставку 20 фунтов, отправил посылку 29 мая 2012 года (спустя 2 месяца) обычной почтой без номера для отслеживания. На удивление, симбиоз Королевской и Российской почты доставил посылку в целости и сохранности уже 22 июня. Это была самая желанная посылка за последние несколько месяцев, поэтому, не выдержав напряжения, мне пришлось отпроситься с работы и бежать на почту.

Рассказывать про то, как настраивать Raspberry Pi для первого запуска нет смысла. Со статьей на эту тему я опоздал на пару лет, уже много строк об этом написано на других ресурсах, а на Youtube выложено достаточное количество видеоматериалов. Я же хочу рассказать о существенном для меня недостатке в конструкции – неудобном расположении разъема для SD карты. Когда карта вставлена, она сильно выпирает за пределы платы, что портит вид самодельного корпуса.

Есть два варианта решения этой проблемы: подпаять переходник SD->microSD параллельно разъему, установленному на Raspberry Pi (как сделать такую операцию можно почитать в статье на Хабре), или использовать переходник Low-profile MicroSD->SD. Первый вариант для меня просто недопустим – ну не поднимается рука паять плату, т.к. боюсь испортить товарный вид своей Малинки. Считаю, что оптимальным выбором является все же использование Low-profile переходника.

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

Готовы? Переходим от слов к делу. Чтобы правильно сделать переходник, изучим спецификацию на SD и microSD карты. Все, что необходимо для изготовления я попытался систематизировать в таблице 1, 2.

Таблица 2: Цоколевка карт памяти SD

Соединив соответствующие контакты на картах памяти, и, объединив Vss1, Vss2, получаем электрическую принципиальную схему переходника.

Чтобы изготовить переходник нам потребуется:
1) Держатель для microSD (CF TFC-WPCE-08 MICROSD CARD) – 52.22 руб.
2) Кусок двустороннего фольгированного стеклотекстолита площадью порядка 4 см 2 (2% от позиции СТЕКЛОТЕКСТОЛИТ ФОЛЬГИР.1.5ММ 2-Х СТОРОН) – 3 руб.
3) Материалы для монтажа (хлорное железо, олово, флюс) – 10 руб.

Тогда себестоимость проекта составит 65 рублей 22 копейки и некоторое количество свободного времени. Для того чтобы удешевить конструкцию можно заменить держатель карты microSD на CF TFC-WPCER-08 MICROSD CARD . К сожалению, этой позиции не оказалось в наличии на складе ЗАО «Промэлектроника», поэтому я приобрел более дорогой вариант. Обращаю Ваше внимание на то, что если Вы замените тип держателя, то воспользоваться моим шаблоном для ЛУТ (лазерно-утюжной технологии) у Вас может не получиться.

Для проектирования печатной платы я использовал Autocad, т.к. мой любимый SprintLayout не смог порадовать наличием нужного шаблона. Для любителей видоизменить печатную плату Вы можете скачать исходник в формате DWG , а если такой необходимости нет, то – заготовку в формате PDF (рекомендую перед нанесением шаблона из PDF сверить размеры).

После того, как шаблон перенесен на плату, рекомендую просверлить отверстия диаметром 0,5 мм для перехода с одного слоя на другой.

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

Делать переходник самим или покупать – выбирать Вам. Чтобы выбор был более осознанным, специально для Вас я подобрал несколько ссылок для покупки:
1) Raspberry Pi Premium Low-profile MicroSD (TF) to SD Card Adapter. Protect Board.
2) Raspberry Pi Low-profile MicroSD to SD Card Adapter, SD card won"t get damaged!!
3) MicroSD to "short" SD / RS-MMC adapter. For Everdrive carts, Raspberry Pi, etc.
4) Low-profile MicroSD to SD Card Adapter for Raspberry Pi.
5) SD card adapter for Raspberry pi REV2 +free shipping.

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

Для самостоятельной сборки устройства Вам необходимо скачать следующие файлы:
1. Исходник платы для ЛУТ в формате DWG
2. Плата для ЛУТ в формате PDF

Удачи в Ваших начинаниях!!!

Мы выводили картинку на дисплей с 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); }

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

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 и т.п. карт памяти. Если на вашей монолитной поломанной флешке остались важные файлы которые хотелось бы вернуть - обращайтесь к нам!

Сколько себя помню всегда любил читать, но на дворе 21 век и порой необходимую литературу можно найти только в интернете. Да и читать электронные книги можно на электронном устройстве типа планшет, компьютер или ридер. В итоге получилось небольшое устройство, которое может читать текстовые файлы с SD или microSD карты и выводить их содержимое на экран.

Мозгом устройства служит микроконтроллер Atmega32 работающий на частоте 8МГц. МК тактируется от внешнего кварца на 8МГц, в качестве экрана устройства я применил небольшой ЖК индикатор WH1604A на контроллере HD44780 разрешением 4 строки по 16 знакомест каждая. Кнопки использовал обычные тактовые, что касается SD карты для её подключения к микроконтроллеру я использовал резисторные делители чтобы согласовать логические уровни.

Принципиальная схема устройства:

На схеме распиновка верна только для SD карты или SD адаптера, для подключения других карт пользуйтесь их распиновкой!

Устройство поддерживает SD, miniSD и microSD карты памяти размером до 4ГБ отформатированные в файловой системе FAT, FAT16. Следует помнить что устройство не поддерживает каталоги поэтому все файлы нужно записывать только в корень флешки. Текстовые файлы должны быть в обычном txt формате и без форматирования, имена файлов должны быть не длинее 8 знаков (не считая расширения).

При включении устройства на дисплее появится заставка:

Если SD карта не установлена в устройство, неправильно подключена, или ещё чего то появится следующие сообщение:

Если же всё в порядке, то появится главное меню:

Пользуясь кнопками вы можете войти в пункт "Обзор файлов" где можете выбрать нужный вам для прочтения файл.

В пункте "Настройки" вы можете выбрать с каким расширением файлы отображать в "Обзоре файлов".

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

Прошивку для устройства я писал в среде BASCOM-AVR с использованием библиотеки AVRDOS, прошивка занимает всего 30% программной памяти микроконтроллера поэтому есть место для творчества. Внутри устройство собрано на двух печатных платах: на одной расположен МК с обвесом, на другой разъём под SD карту и согласующие цепочки логических уровней.

Вот фото устройства внутри:

Для питания я использовал Ni-Cd аккумулятор на 4.8В, 600мАч. После прошивки микроконтроллера необходимо установить следующие фьюз-биты:

Список радиоэлементов

Обозначение Тип Номинал Количество Примечание Магазин Мой блокнот
U1 МК AVR 8-бит

ATmega32

1 В блокнот
D1, D2 Выпрямительный диод

1N4001

2 В блокнот
С1, С2 Конденсатор 22 пФ 2 В блокнот
С3 Электролитический конденсатор 100 мкФ 1 В блокнот
С4 Конденсатор 100 нФ 1 В блокнот
R1 Резистор

10 кОм

1 В блокнот
R2-R4 Резистор

4.7 кОм

3 В блокнот
R5-R7 Резистор

1 кОм

3 В блокнот
LCD1 LCD-дисплей LM014L 1 В блокнот
X1 Кварц 8 МГц 1 В блокнот
Кнопка 4 В блокнот
Выключатель 1

Обновлено18.12.15. Всем привет. Сегодня мы продолжим разработку контроллера сбора данных, а именно сохранение информации непосредственно на карту SD . В прошлой статье была налажена работа термометра. Теперь эту информацию по времени, при подключении в дальнейшем часов реального времени(статья №29 ), мы будем заносить на карту памяти, получив своеобразную базу данных. А также в дальнейшем перенесем эту информацию на ПК (статья №42), в базу данных под управлением MySQL (статья №48), через небольшое приложение на Java (статья №44). Но сперва разберемся что такое SD – карта и как с ней работать. Начнем с краткого обзора истории. Предшественником Flash-памяти является одна из энергонезависимых видов памяти , типа , которая зарекомендовала себя и используется в микроконтроллерах. Flash-память возникла в ходе потребности увеличения емкости и изменения технологии стирания (в случае с памятью EPROM). Поэтому в 1984 году инженер компании Toshiba Фудзио Масуокой изменил технологию стирания, что в свою очередь решил недостатки предшественников Flash-памяти. Хочется добавить, что далее данная память начала делится по внутреннему устройству соединения ячеек в массив и алгоритмами чтения-записи – это NOR- и NAND-технология. А также различие по количеству хранимых битов в элементарной ячейке. Это SLC-устройства (single-levelcell), т.е. однобитовые ячейки различают только два уровня заряда на плавающем затворе. И MLC- устройства (multi–levelcell) — многобитовые ячейки различают больше уровней заряда. Второй тип приборов дешевле и более ёмкий, чем SLC-приборы, однако с большим временем доступа и меньшим максимальным количеством перезаписей (около 10 тыс. и 100 тыс. — SLC).

Вообще устройства технологии NOR — это двумерная матрица проводников, что позволяет получить более быстрый доступ к каждой ячейки памяти, но при этом площадь ячейки считается большой, поэтому данная технология используется для памяти программ микропроцессоров и для хранения небольших вспомогательных данных, сюда же можно включить и специализированные микросхемы начальной загрузки компьютеров
(POST и BIOS), процессоров ЦОС и программируемой логики.Типовые объёмы - от 1 кбайта до 1 Мбайта.
Второй тип устройства — NAND-технология — трехмерный массив имеет малую площадь ячейки, но относительно длительный доступ сразу к большой группе ячеек. Используется для больших объемов памяти. Вот с этой памятью мы и будем работать.
Но перед этим хочется сказать об недостатке. Как и у всего есть свой срок использования, так и у памяти есть ресурс износа. Производители в гонке за емкостью и лидерством на рынке, всегда упускают такой показатель как качество, т.к. он не совместим с высокой ценой. Так возвращаясь к износу хочется отметить что срок хранения информации при использовании MLC-устройств составляет примерно 5 лет, что связанно с накоплением необратимых изменений при изменении заряда. Если брать память NAND c SLC-устройства, то они являются более качественными, и соответственно дорогими. Стоит отметить что срок хранения информации очень во многом зависит от температуры, гамма-радиации и частиц высокой энергии.
Выше было сказано, что недостаток карты это ограниченное количество циклов перезаписей. Когда мы будем использовать файловую систему для управления файлами, то должны знать что такие системы записывают данные в одно место, естественно расходую ресурс выделенной области в итоге вывода ее из строя и соответственно уменьшая емкость. Для этого типа памяти используется NAND-контроллер, который должен равномерно распределять износ. Однако для удешевления устройств контроллер может и не использоваться, а его работу будет выполнять программный NAND-драйвер в операционной системе. После этого открытия, многие компании занялись разработкой своих стандартов портативных карт.

Далее перейдем непосредственно к рассмотрению карты.
Secure Digital Memory Card (SD) - формат карт памяти, разработанный для использования в основном в портативных устройствах. Чтобы разобраться в ее работе мы будем использовать спецификацию, которая описывает данный стандарт и называется SD Specifications ver3.01.
Первое что нам необходимо, так это разобраться как работать с этой картой, как подключить и прочее. Сначала выберем карту. Для экспериментов я взял microSD емкостью 2Гб, стандарт емкости SDSC. Шина карты может работать по двум протоколам SD и SPI. Хочется отметить что данная карта это своего рода модификация карты MMC, где (в карте SD) основное внимание было уделено системе безопасности. Поэтому алгоритм работы по протоколу SPI такой же, ну и конечно же они односторонне совместимы. Т.е мы можем в слот SD карты вставить MMC, но не наоборот.

На рисунке ниже представлена схема подключения карты SD по протоколу SPI .
Данный интерфейс позволяет обмениваться данными на высокой скорости, задействовав при этом минимальное количество выводов микроконтроллера, которые оснащены модулем SPI. С этого момента начнем использовать спецификацию. Первое что нас интересует- выбор режима. Разберемся в тонкостях на рис. ниже из раздела 6.4.1.1 представлена диаграмма напряжения питания и последовательность посылки команды. Здесь четко видно что после включения карты необходимо выждать несколько миллисекунд (1мс + от 0.1 до 35 мс(нарастание)) на стабилизацию. В течении этого времени на CS, MOSI линии должна быть подана 1. Далее происходит задержка инициализации максимум 1 мс, при подаче на вход CLK 74 импульсов (тактов), после чего должна идти команда CMD0. Перейдем к главе 7 где четко описана последовательность действий.

Диаграмма напряжения питания

SPI протокол выбирается после включения питания и подачи команды сброса CMD0. Сама по себе карта SD работает в режиме SD. Вход в режим осуществляется если сигнал SC при подаче команды CMD0 будет 0. При переходе в режим SPI карта ответит форматом R1 (рисунок ниже). Формат ответа представляет собой байт (зависит от команды см. таблицу 7.3 в спецификации) с флагами определяющие состояние карты. Правильные ответы для нас это будет 1 (в случае команды CMD0) и 0 во всех других случаях.
1-й бит – режим ожидания
2-й – ошибка стирания
3- й – неизвестная команда
4-й – ошибка команды
5-й – ошибка в последовательности стирания
6-й –ошибка адреса
7-й – ошибка аргуента

В процессе сброса, карта должна ответить 0×01, что соответствует первому биту.

В спецификации есть четкая последовательность инициализации для SPI. Для чего используется команда CMD8 для проверки рабочего состояния карты, где происходит довольно не простой алгоритм проверки. Далее команда CMD58 для определения типа карты SDSD или SDHC и SDXC. А также команда CMD41 для запуска и проверки инициализации. Довольно не простой процесс инициализации с проверками, но я думаю что для простой записи данных можно использовать более упрощенный процесс. В разделе 7.2.7. говорится, что в режиме ожидания единственно допустимые команды для карточки CMD41, CMD8, CMD58, CMD59 , а также для карт (толстых 2.1мм) памяти SD CMD1, который идентичен команде CMD41. В стандарте эта команда считается запрещенной для инициализации, и используется исключительно для различия карт 1,4мм и 2,1мм.
Пойдем более простым путем и используем команду CMD1. Все выше описанное отобразим в коде в функции инициализации, но перед этим рассмотрим формат команды. Каждая команда или блок данных состоят из восьми битных байтов, которые выравниваются по сигналу CLK. Т.е. каждая команда выравнивается по границе 8 тактов. Сообщения SPI состоят из команды, ответа и данных. Вся связь контролируется микроконтроллером. Все команды имеют длину 6 байт. Передача начинается с первого левого бита.

На рисунке ниже представлен формат команды.


Старт бит – с 0 начинается любая команда. Передаваемый бит – тоже всегда равна 1.
Индекс – непосредственно передаваемая команда.
Аргумент- для каждой команды аргумент указан в таблице спецификации.
CRC – проверка избыточности кода. По умолчанию в режиме SPI она отключена. Поэтому мы ее используем только для команды CMD0, которая посылается до входа в режим и имеет значение CRC 0×95.
Стоп бит - конец передаваемой команды.
Что ж приступим к написанию кода.
Начнем с необходимых 2-х функций: передача и прием байта.
1. Передача байта карте.
void trans_byte_sd (unsigned char data)// передаем массив битов
{
for (unsigned char i=0;i<8;i++) //Перебираем байт
{
if ((data&0×80)==0×00) //Если старший бит = 0
PORTB&=~_BV (PB3); //Выставить MOSI (DI) -0
else
PORTB|=_BV (PB3); //1
data=data<<1; // сдвиг влево
PORTB|=_BV (PB5); //Импульс или строб
asm («nop»); //Пауза в 1 такт
PORTB&=~_BV (PB5);
}
}
2. Прием байта микроконтроллером.
unsigned char receive_byte_sd (void) // Возвращаем ответ
{
unsigned char data = 0; // инициализируем массив
for (unsigned char i=0; i<8; i++)
{
PORTB|=_BV (PB5); //Фронт импульса
data=data<<1; // Сдвигаем влево
if ((PINB&_BV (PB4))!=0×00) // Если состояние пина 1
data=data|0×01;
PORTB&=~_BV (PB5); //0
asm («nop»);
}
return data; // Возвращаем ответ
}

Из выше описанных, основных, функций начнем писать дальнейший код. Далее пропишем функцию передачи команды. Здесь хочется обратить внимания, на то, что Вы можете передавать все 5-ть аргументов: непосредственно саму команду и 4-аргумента отвечающих за адрес ячеек памяти самой карты. Что касается 6-го байта, то CRC при входе в режим SPI отключается (по умолчанию) и значение постоянно равно 0×95, которое используется только для CMD0, когда карта не в режиме. Включить проверку кода можно командой CMD58. Для экспериментов я передаю два аргумента.

3.Передача команды.
unsigned char comand_sd (char CMD, char arg) /*передаем команду и адрес к которому обращаемся и возвращаем ответ*/
{
long int i=0; // переменная для счетчика
unsigned char r1; // ответ карты
trans_byte_sd (CMD); // команда
trans_byte_sd (0×00);
trans_byte_sd (0×00);
trans_byte_sd (arg); // передача адреса
trans_byte_sd (0×00);
trans_byte_sd (0×95); // Передача CRC
/* После передачи команды ждем ответа формата R1.Каждой команде соответствует свой ответ*/
/* Цикл для ожидания получения ответа за определенное время*/
do
{
r1=receive_byte_sd ();
i++;
}while (((r1&0×80)!=0×00)&&(i<0xffff)); /* Как только старший бит байта не равен 0 и i не превышает 65 535 тактов*/
return r1; // Возвращаем ответ
}
4. И нициализация карты.

Теперь мы можем прописать инициализацию карты. Кратко программа описывается следующим образом: первое что необходимо, так это перевести карту в режим SPI. При подаче питания карта устанавливается в режим SD. Для выбора режима SPI на вход CS подается логический 0, в это же время подается команда сброса CMD0 и инициализации CMD1 на вход карты MOSI. Обратим внимание что команда начинается от шестнадцатеричного 0×40, к которому необходимо прибавить номер команды CMD в шестнадцатеричном виде.

unsigned char spi_card_init (void) // функция возвращает ответ
{
unsigned char r1; // переменная для приема ответа
long int i =0; // переменная для счетчика
_delay_ms (10); // небольшая задержка для стабилизации напряж.
PORTB|=_BV (PB1); //CS, устанавливаем 1, при подаче тактов
PORTB|=_BV (PB3); //линия подачи команд — 1 MOSI (DI)
for (unsigned char i=0; i<80;i++) // посылаем более 74 импульса
{
PORTB|=_BV (PB5); //CLK — 1
asm («nop»); //задержка в один такт
PORTB&=~_BV (PB5); //CLK — 0
}
PORTB&=~_BV (PB1); /* условие для входа в режим SPI линия CS должна быть равна 0 */
r1=comand_sd (0×40,0×00); // CMD0=0×40, адрес без разницы
if (r1!=0×01) return 4; //коды ошибок можете ставить любые
trans_byte_sd (0xff); /* посылаем строб, своеобразная пауза перед приемом ответа */
do // цикл приема ответа от карты
{
r1=comand_sd (0×41,0×00); /* посылаем команду инициализации */
trans_byte_sd (0xff); // пауза
i++; // счетчик
}while ((r1!= 0)&&(i<65535)); /*пока не получен ответ 0 и количество циклов не превышает 0xffff */
if (i>=0xffff) return 5; /* возвращаем ошибку если превысило время опроса */
return 0;//Возвращаем 0 в случае успешной инициализации
}

Следующий важный момент, в спецификации пишется, что информация передается блоками, по 512 бит, причем если карта SDSC как в нашем случае, то длину блока можн0 установить от 1 до 512 бит командой CMD16. По умолчанию 512 бит. Далее опишем две функции приема и передачи блоков. В спецификации даны блок-диаграммы, опираясь на которые мы напишем код.

Передача блока информации на карту.

За передачу ЕДИНСТВЕННОГО блока отвечает команда CMD24. После подачи команды, ждем ответ После чего следует стартовый байт, который подготавливает контроллер карты к приему информации, по окончанию карта отвечает байтом о состоянии передачи, который описан в главе 7.3.3.1. Т.е. правильный ответ должен быть= 5. Также ждем освобождения шины для дальнейшей передачи.

Байт отзыва о состоянии передачи.

В разделе 7.3.3.2 описывается формат передаваемого блока
unsigned char receive_block_sd (char* block, char arg) /* передаем массив для записи данных и адрес к которому обращаемся*/
{
long int i = 0;
unsigned char r1;
r1=comand_sd (0X51,arg); //CMD17
if (r1!=0×00) return 5; //Выйти, если ответ не 0×00
trans_byte_sd (0xff);
do //Ждем начала пакета данных
{
r1=receive_byte_sd ();
i++;
}while ((r1!= 0xfe)&&(i<65535));
if (i>=0xffff) return 5;
for (int i=0;i<512;i=i+1) //прием данных
block[i] = receive_byte_sd ();
receive_byte_sd (); //байт CRC
receive_byte_sd (); //байт CRC
return 0;
}

Перед тем как использовать программу, рассмотрим аппаратную часть. Как мы говорили, выше, что карта совместима с микроконтроллером в режиме SPI. Отметим следующие нюансы работы с картой:
1. Сопряжение логических уровней, необходимо при разном напряжении питания SD-карты и микроконтроллера AVR. Можно использовать резистивный делитель напряжения, который является линейным,т.е. напряжение на выходе зависит от напряжения на входе. А можно параллельный параметрический стабилизатор напряжения на стабилитроне, тоже что и первый вариант,только в нижнем плече используется стабилитрон, который является нелинейным делителем, и следит за опорным напряжением за счет своих свойств при повышении входного напряжения уменьшать внутреннее сопротивление,и наоборот.
Я использовал второй вариант. В схеме ниже на сигнальной линии сопротивления являются балластными(токоограничители), на вход делителя поступает напряжение 4,5 – 5 В, а выходное снимается с нижнего плеча делителя. Токоограничители необходимы для защиты карты и другой периферии при сбоях микроконтроллера. При хорошо отлаженном устройстве в них нет необходимости.

Заметьте, что линия MISO не нуждается в согласовании, т.к. работает только в одну сторону от карты к микроконтроллеру.
2. Второй момент, я не использую проверку наличия карты и защиты записи. У кого то есть эти контакты в слотах, у кого то нет.
3. Последний момент- питание. Либо ВЫ питаете 3.3 вольта всю схему, включительно с микроконтроллером, либо ставите делитель на вход схемы, не очень надежно. Либо стабилизатор 3.3 вольта, как я и сделал на микросхеме LP2980 . Важным моментом здесь является электролитический (танталовый) конденсатор, который защищает микроконтроллер от сброса при просадках напряжения.
Ниже представлена программа и результат. Как всегда, я стараюсь использовать одну программу постоянно ее изменяя. Данный код взят из статьи №5 (семисегментный индикатор).

#include
#include
#include
#include
//макросы для работы с индикатором
#define a 128
#define b 32
#define c 8
#define d 2
#define e 1
#define f 64
#define g 16
#define dp 4

// Переменные

char block ={}; //буфер записи/чтения данных на карту
short unsigned int j, k = 0; //в макросе прерывания
unsigned char Slot; // Массив чисел для отображения на индикаторе

//Объявляем функции

void trans_byte_sd (unsigned char data); // функция передачи байта
unsigned char receive_byte_sd (void); //Функция приема байта
unsigned char comand_sd (char,char); // функция передачи команды
unsigned char spi_card_init (void); //Функция инициализации карты памяти
unsigned char receive_block_sd (char* block, char arg); //Функция приема блока
unsigned char trans_block_sd (char* block, char arg); //Функция передачи блока
// Инициализации индикатора
void Slot_init ()
{…………………….};
// Переменные для отображения цифр
char Elem1, Elem2, Elem3;
// Вывод на индикатор
void Display (float i)
{ …………………………... }
int main (void) //начало основой программы
{
DDRB = 0x2A; //0010 1010 – PB1, PB3, PB5
DDRD = 0xff; //все выводы порта — выходы
PORTD = 0×00; //устанавливаем 0
PORTB |= 0хdb; //1101 1011 (PB0,1,3,4,6,7)
Slot_init ();
sei (); // либо SREG |= (1 << 7); разрешить общее прерывание
//инициализация таймера Т0
TIMSK = (1</*Флаг разрешения по переполнению таймера счетчика Т0*/
TCCR0 = (0< //1000000/8 = 125000
unsigned char temp;
int i;
for (i=0;i<512;i=i+1)
block[i]= i; //записываем в буфер
spi_card_init (); //инициализация
trans_block_sd (block,0×04); //отправляем данные карте
//Обнуляем буфер
for (int i=0;i<512;i=i+1)
block[i]=0;
// Считаем данные с карты
receive_block_sd (block, 0×04); ; //Функция приема байта
for (int i=0;i<512;i=i+1)
{
char otv;
otv = block[i];
Display (otv);
_delay_ms (100);
}
//Запишем по адресу в память 0
for (int i=0;i<512;i=i+1)
block[i]=0;
unsigned char comand_sd (char,0×00); //функция передачи команды
trans_block_sd (block,0×04); //отправляем данные карте
}
//Вывод на индикатор
ISR (TIMER0_OVF_vect)
{ ……………. }

Важный момент — это таймауты. Важно следить за временем чтения записи и стирании карты, так как может зависнуть микроконтроллер в режиме ожидания ответа карты. В спецификации четко описаны таймауты карты. Простой карты длится 5 мс, после чего переходит в энергосберегающий режим, в котором допустимы следующие команды CMD0, CMD1, CMD41 и CMD58. Поэтому при превышении лимита простоя передаем CMD1, ответ и дальше работаем с картой.
Внизу представлено два скриншота из программы WinHex , с помощью которой мы можем посмотреть содержимое ячеек памяти. Программа работает следующим образом: Записываем данные в буфер, оправляем карте, обнуляем буфер, считываем данные с карты в буфер и выводим на дисплей тем самым убеждаемся в передачи данных карте. Смотрим содержимое карты, обнуляем буфер, записываем 0 в карту и опять открываем содержимое карты, тем самым убеждаемся в работоспособности программы и схемы. Как всегда незабываем о мелочах, таких как не допайка, не большие трещенки в дорожках и др., что может забрать львинную долю времени. Поэтому если есть под руками осциллограф, то непременно используйте его для наладки. В статье №24 я привел небольшой пример диагностики карты на всех этапах ее работы. мы познакомимся с датчиком влажности и температуры DHT11. После чего начнем записывать данные (температуру и влажность) в текстовый файл, своеобразную базу данных. Пока на этом все. Всем пока.