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

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

Разбираясь к контроллером STM32 встала задача подключения внешней памяти, а точнее SD карты, как наиболее простого и удобного средства хранения. И дабы облегчить кому-то жизнь, опишу, как это было сделано и приложу исходный код.

Для сохранения данных нужно место. Можно использовать flash-память микроконтроллера, но это уменьшает количество циклов записи. Можно EEPROM на шине I2C или SPI, но они, как правило, малого объема, и удобны для хранения только системных настроек. А вот использование карты памяти - это как раз то, что надо. Большой объем, удобство передачи информации, возможность передачи информации в контроллер (например прошивка).

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

Подключение.

Давайте посмотрим на распиновку карты памяти. Для работы по интерфейсу SPI помимо питания нам понадобятся сигналы CS, SCK, MISO, MOSI. На контроллере эти сигналы подключил к выводам:

  • PB12 - CS
  • PB13 - SCK
  • PB14 - MISO
  • PB15 - MOSI

Также нам понадобится работа с файловой системой на карте памяти. Для этого будем использовать библиотеку FatFS.

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

Для инициализации карты памяти используется функция

DSTATUS disk_initialize (
uint8_t drv /* Physical drive number (0) */
)

которая в свою очередь настраивает и инициализирует SPI интерфейс и SD карту.

Для считывания данных с карты используется функция

DRESULT disk_read (

uint8_t *buff, /* Pointer to the data buffer to store read data */


)

Для записи на карту

DRESULT disk_write (
uint8_t drv, /* Physical drive number (0) */
const uint8_t *buff, /* Pointer to the data to be written */
uint32_t sector, /* Start sector number (LBA) */
uint8_t count /* Sector count (1..255) */
)

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

Ну и в заключение пример работы, который создает директорию, в ней файл.

FRESULT f_err_code;
static FATFS FATFS_Obj;
UINT len; //для хранения количества реально записанных байт
if (disk_initialize (0) == 0)
if (f_mount(0, &FATFS_Obj) == 0) { //Mount Fat Fs

Res = f_mkdir ("0:WorkProj"); //создаем директорию
f_chdir ("0:/DIR");
char str="Hello, Word"; //записываемая строка
//создаем файл с именем FileName и открываем его для для записи
res = f_open(&fil_obj, "newfile.txt", FA_WRITE | FA_CREATE_ALWAYS);

//записываем строку в файл
res = f_write(&fil_obj, str, sizeof(str), &len);
// if(res) return res; //если произошла ошибка
//закрываем файл
f_close(&fil_obj);
}

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

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

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

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

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

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

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

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

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

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

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

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

Заключение

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