Подключение sd карты к микроконтроллеру. Подключаем SD карту памяти к STM32 по SPI Команда cmd 18 sd карты

Разбираясь к контроллером 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 к микроконтроллеру STM32.

Казалось бы, памяти полно у контроллеров STM32F10x, зачем там еще дополнительная, но это впечатление обманчиво) Вот, например, надо нам на дисплей вывести пару-тройку разных изображений – формат 320*240 – то есть 76800 пикселей, каждому из которых соответствует целых 2 байта. Вот и получаем около 150 кБ на одну картинку. А это немало по меркам микроконтроллера, и не факт, что две разные картинки удастся запихать в его Flash память. Или надо нам хранить большие объемы информации, данные с какого-нибудь датчика, к примеру. Да еще так, чтобы эти данные были доступны и после отключения питания. Вот тут то нам и пригодится внешняя память. И отличным решением будет SD карта памяти или MMC. К слову в этой статье мы будем проводить опыты над картой micro SD .

Для начала пара слов о самой карте памяти, точнее о ее распиновке. Выглядит все это дело следующим образом:

Итак, что тут у нас? Ну сразу видно, что выводов у нее целых восемь штук. Назначение выводов следующее (слева направо):


Колонка SPI Mode нам намекает на то, что взаимодействует с микроконтроллером при помощи интерфейса SPI. НО! Мы пойдем по другому пути 😉 Все дело в том, что STM32 имеют на своем борту готовый периферийный модуль для работы именно с картами памяти, и называется он SDIO.

Вообще взаимодействие с картами памяти заключается в передаче им определенных команд. Некоторые команды требует наличия аргумента, некоторые нет. Команды можно найти в официальной документации на конкретную карту. Так вот встроенный модуль SDIO дает возможность значительно упростить процесс передачи команд, да и вообще процесс работы с внешними картами памяти. Например, вот регистр SDIO_CMD – туда мы просто напросто записываем код команды, которую хотим передать карте. Или вот статусный регистр SDIO_STA – там целых 24 флага на каждый чих, то есть для большого количества событий.

Кстати STM радует еще и добротной документацией на все это дело. Вот, к примеру, подробное описание инициализации для карты памяти SD (аналогично все описано для других типов карт):

Ну, собственно, пора перейти к практическому примерчику. Поковыряем-ка Standard Peripheral Library.

В файле stm32f10x_sdio.h по традиции находим структуры для всевозможной настройки – то есть для выбора источника тактового сигнала, частоты контроллера SDIO, настройки количества передаваемых байт. Там все так щедро откомментировано, что даже не хочется отдельно это повторять)) Просто смотрите:

typedef struct { uint32_t SDIO_ClockEdge; /* Specifies the clock transition on which the bit capture is made. This parameter can be a value of @ref SDIO_Clock_Edge */ uint32_t SDIO_ClockBypass; /* Specifies whether the SDIO Clock divider bypass is enabled or disabled. This parameter can be a value of @ref SDIO_Clock_Bypass */ uint32_t SDIO_ClockPowerSave; /* Specifies whether SDIO Clock output is enabled or disabled when the bus is idle. This parameter can be a value of @ref SDIO_Clock_Power_Save */ uint32_t SDIO_BusWide; /* Specifies the SDIO bus width. This parameter can be a value of @ref SDIO_Bus_Wide */ uint32_t SDIO_HardwareFlowControl; /* Specifies whether the SDIO hardware flow control is enabled or disabled. This parameter can be a value of @ref SDIO_Hardware_Flow_Control */ uint8_t SDIO_ClockDiv; /* Specifies the clock frequency of the SDIO controller. This parameter can be a value between 0x00 and 0xFF. */ } SDIO_InitTypeDef; typedef struct { uint32_t SDIO_Argument; /* Specifies the SDIO command argument which is sent to a card as part of a command message. If a command contains an argument, it must be loaded into this register before writing the command to the command register */ uint32_t SDIO_CmdIndex; /* Specifies the SDIO command index. It must be lower than 0x40. */ uint32_t SDIO_Response; /* Specifies the SDIO response type. This parameter can be a value of @ref SDIO_Response_Type */ uint32_t SDIO_Wait; /* Specifies whether SDIO wait-for-interrupt request is enabled or disabled. This parameter can be a value of @ref SDIO_Wait_Interrupt_State */ uint32_t SDIO_CPSM; /* Specifies whether SDIO Command path state machine (CPSM) is enabled or disabled. This parameter can be a value of @ref SDIO_CPSM_State */ } SDIO_CmdInitTypeDef; typedef struct { uint32_t SDIO_DataTimeOut; /* Specifies the data timeout period in card bus clock periods. */ uint32_t SDIO_DataLength; /* Specifies the number of data bytes to be transferred. */ uint32_t SDIO_DataBlockSize; /* Specifies the data block size for block transfer. This parameter can be a value of @ref SDIO_Data_Block_Size */ uint32_t SDIO_TransferDir; /* Specifies the data transfer direction, whether the transfer is a read or write. This parameter can be a value of @ref SDIO_Transfer_Direction */ uint32_t SDIO_TransferMode; /* Specifies whether data transfer is in stream or block mode. This parameter can be a value of @ref SDIO_Transfer_Type */ uint32_t SDIO_DPSM; /* Specifies whether SDIO Data path state machine (DPSM) is enabled or disabled. This parameter can be a value of @ref SDIO_DPSM_State */ } SDIO_DataInitTypeDef;

Отметим как в SPL реализована передача команд карте памяти. Для этих целей отведена отдельная структура SDIO_CmdInitTypeDef. В поле SDIO_CmdIndex вводим код команды, в поле SDIO_Argument – аргумент команды, также заполняем остальные поля. Осталось как то эти данные запихать в карту micro SD 😉 А для этого нам приготовили функцию:

SDIO_SendCommand (SDIO_CmdInitTypeDef *SDIO_CmdInitStruct)

В качестве аргумента передаем ей как раз таки созданную нами структуру. Для записи данных есть функция – SDIO_WriteData(uint32_t Data) . После вызова этой функции данные окажутся в специально предназначенном для этого регистре – SDIO_FIFO.

Вот так вот осуществляется работа с модулем SDIO в STM32F10x)

Теперь перейдем к практике наконец-то. Я снова буду работать с платой Mini STM32, поскольку добрые китайцы озадачились установкой на нее слота для карты памяти micro SD. Вот схема подключения разъема для карты к микроконтроллеру:

Для написания программы воспользуемся готовым примером для Keil’а – стащим оттуда два файла, в которых реализовано что-то вроде драйвера для работы с картами – это файлы sdcard.c и sdcard.h. Создаем новый проект, цепляем туда эти файлы, а кроме того, естественно, файлы CMSIS и SPL. Вот готовый проект, в который все уже добавлено – остается только написать код функции main())

В файле sdcard.c реализованы всевозможные функции для работы с картой памяти, нам теперь остается их только использовать 😉 Пишем код! Для примера запишем на micro SD 512 байт тестовых данных, а затем попробуем их считать:

// Цепляем нужные файлы #include "stm32f10x.h" #include "sdcard.h" /*******************************************************************/ // Массивы входных и выходных данных и переменная для хранения данных // о нашей карте uint8_t writeBuffer[ 512 ] ; uint8_t readBuffer[ 512 ] ; SD_CardInfo SDCardInfo; /*******************************************************************/ int main() { // Тестовые данные для записи for (uint16_t i = 0 ; i < 512 ; i++ ) { writeBuffer[ i] = i % 256 ; readBuffer[ i] = 0 ; } // Иницилизация карты SD_Init() ; // Получаем информацию о карте SD_GetCardInfo(& SDCardInfo) ; // Выбор карты и настройка режима работы SD_SelectDeselect((uint32_t ) (SDCardInfo.RCA << 16 ) ) ; SD_SetDeviceMode(SD_POLLING_MODE) ; // И вот наконец то запись и чтение SD_WriteBlock(0x00 , writeBuffer, 512 ) ; SD_ReadBlock(0x00 , readBuffer, 512 ) ; while (1 ) { } } /*******************************************************************/

Обратите внимание, что SD карта поддерживает запись блоками по 512 байт.

Если мы запустим программу под отладчиком, то увидим, что считанные данные соответствуют записанным =) Так что эксперимент можем считать удавшимся. На этом на сегодня заканчиваем, до скорых встреч!

Урок 33

Часть 1

SPI. Карта SD. FAT

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

int main ( void )

unsigned int i ;

Port_ini ();

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

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

Setpos (0,0);

Str_lcd ( "String 1" );

Setpos (2,1);

Str_lcd ( "String 2" );

Setpos (4,2);

Str_lcd ( "String 3" );

Setpos (6,3);

Str_lcd ( "String 4" );

Delay_ms (2000);

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

While (1)

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

unsigned char i ;

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

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

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

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

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

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

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

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

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

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

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

#include "main.h"

#define MOSI 3

#define MISO 4

#define SCK 5

#define SS 2

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

void port_ini ( void )

PORTD =0x00;

DDRD =0xFF;

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

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

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

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

void SPI_SendByte ( unsigned char byte )

{

}

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

void SPI_SendByte ( unsigned char byte )

unsigned char i ;

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

{

}

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

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

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

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

Михаил Русских

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

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

Карты памяти SD (Secure Digital) разработаны, в основном, для применения в портативных устройствах. Сегодня на рынке существует большое количество моделей, предоставляемых такими компаниями как SanDisc, и способных записывать с зависящей от класса скоростью данные объемом от 8 МБ до 2 ТБ. SD-карты, в отличие от MMC-карт, помимо обычной области хранения данных имеют специальную защищенную область, которая недоступна обычному пользователю. Для того чтобы устройство смогло прочитать записанную на SD-карту информацию в виде файлов, эта карта должна иметь определенную файловую систему. Наиболее предпочтительной в данном случае является система FAT, поскольку количество циклов записи/чтения для SD-карт ограничено, а системы вроде NTFS и ext являются журналируемыми, то есть предполагающими частые опросы.

Передача и прием данных осуществляется по интерфейсу SPI или по шине SD. На Рисунке 1 показано расположение выводов SD-карт различных типов, а в Таблице 1 представлено функциональное назначение этих выводов.

Таблица 1. Функциональное назначение выводов SD-карт

Интерфейс SPI

Интерфейс SD

Карты SDC

Карты miniSD

Карты microSD

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

Передача данных по шине SD

Связь с SD-картой осуществляется по 6 информационным линиям: 4 линиям данных (DAT0, DAT1, DAT2, DAT3), линии синхронизации (CLK), линии передачи команд (CMD). При подключении к контроллеру линии данных и линия передачи команд должны быть подтянуты к питанию, как показано на Рисунке 2.

Запись и чтение данных выполняются поблочно. Обычно блок состоит из 512 байтов. Передача данных и служебной информации основана на модели взаимодействия «команда-ответ». Перед передачей или приемом данных хост-контроллер должен отправить соответствующую команду по линии CMD. По этой же линии от SD-карты должен прийти ответ, после которого может быть осуществлено требуемое действие. Стандарт SD поддерживает присутствие нескольких ведомых на шине, в таком случае хост-контроллер может посылать команды широковещательно. Команда имеет четкую структуру, состоящую из 48 бит: 1 стартовый бит, 1 бит передачи (установлен в 1), 6 бит для индекса команды, 32 бита для аргумента, 7 бит для контрольной суммы и 1 стоповый бит. Ответ может иметь такую же 48-битную структуру (в этом случае бит передачи установлен в 0) или состоять из 136 битов (т. н. формат длинного ответа): 1 стартовый бит, 1 бит передачи (установлен в 0), 6 зарезервированных битов, 127 битов содержимого регистров CID (Card Identifiction Register) или CSD (Card Specific Data Register) и контрольной суммы, 1 стоповый бит.

В начале работы с картой хост-контроллер перезагружает карту с помощью команд GO_IDLE_STATE (CMD0) и IO_RW_DIRECT (CMD52) . Полный список команд с описанием можно найти в (приложение A). После перезагрузки карта восстановит свои настройки по умолчанию, и ее адрес будет равен RCA = 0x0001. Если к хосту подключено несколько карт, то с помощью команды SET_RELATIVE_ADDR (CMD3) он задает каждой карте собственный адрес. Также в начале работы выполняется проверка поданного на карту напряжения. Напряжение карты должно быть в строго установленном диапазоне от 2.7 до 3.6 В. В связи с этим хост-контроллер с помощью команды SEND_OP_COND (CMD1 или ACMD41 ) обязан узнать текущее напряжение на карте и прекратить с ней работу, если оно не входит в требуемый диапазон. Таков общий принцип инициализации карты, для выполнения которой, в зависимости от типа карты (MMC, SD, SD I/O), могут использоваться другие команды и выполняться дополнительные шаги, поэтому при реализации низкоуровневого интерфейса нужно внимательно прочитать документацию на карту и изучить команды CMD.

Во время записи хост передает карте один или несколько блоков, используя команду WRITE_BLOCK (CMD24) или WRITE_MULTIPLE_BLOCK (CMD25) , соответственно, при этом в конце каждого блока хост записывает контрольную сумму. Карта, запись данных для которой разрешена, всегда будет в состоянии принять блок(и), но если контрольная сумма не совпадет, то карта сообщит об ошибке и не запишет текущий блок в свою память, а в случае пакетной передачи следующие блоки будут проигнорированы.

Передать информацию хосту, то есть выполнить чтение, также можно одним блоком с помощью команды READ_SINGLE_BLOCK (CMD17) или пакетом из нескольких блоков с помощью команды READ_MULTIPLE_BLOCK (CMD18) . В случае пакетной передачи хост может прервать чтение в любое время, отправив команду STOP_TRANSMISSION (CMD12) . Если в ходе передачи нескольких блоков внутренний контроллер карты зафиксирует ошибку, то он остановит передачу данных хост-контроллеру, но оставит карту в режиме передачи данных. В таком случае хосту придется принудительно завершить чтение с помощью команды CMD12 , при этом в ответе на эту команду карта сообщит об ошибке чтения.

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

Драйвер stm324xg_eval_sdio_sd

Этот драйвер представляет собой библиотеку функций, с помощью которых можно реализовать высокоуровневый интерфейс для обмена данными с SD-картой. Драйвер поставляется для работы с отладочной платой STM324VG (UtilitiesSTM32_EVALSTM3240_41_G_EVAL), но после изменения функций, связывающих эту библиотеку с низкоуровневым интерфейсом SDIO он может функционировать со многими микроконтроллерами семейства STM32, в составе которых имеется модуль SDIO. Из предыдущего предложения следует, что к проекту должны быть подключены файлы из стандартной библиотеки Standard Peripheral Library , обеспечивающие функционирование модуля SDIO на низком уровне. Например, для микроконтроллеров серии STM32F4xx это будут файлы stm32f4xx_sdio.c и stm32f4xx_sdio.h .

Перед использованием функций драйвера программист должен связать этот драйвер с аппаратной частью, то есть настроить линии модуля SDIO. Для этого ему нужно в основном файле своей программы main.c создать функции SD_LowLevel_Init() (для включения модуля SDIO) и SD_LowLevel_DeInit() (для отключения модуля SDIO), в которых необходимо выполнить конфигурирование этих линий. Если программист желает использовать модуль прямого доступа к памяти для увеличения скорости работы с SD-картой, то ему также нужно будет создать функции SD_LowLevel_DMA_TxConfig() и SD_LowLevel_DMA_RxConfig() , отвечающие за передачу и прием данных с помощью модуля ПДП. В качестве примера инициализации четырех указанных функций можно взять код, имеющийся в файле stm324xg_eval.c из (UtilitiesSTM32_EVALSTM3240_41_G_EVAL).

Теперь рассмотрим основные функции для работы с SD-картой, предоставляемые этим драйвером. Для того, чтобы инициализировать карту, нужно вызвать функцию SD_Init() , которая настраивает модуль SDIO, обращаясь к SD_LowLevel_Init() , проверяет тип SD-карты, получает служебную информацию из регистров CID и CSD, задает скорость передачи данных (по умолчанию 24 МГц) и устанавливает ширину шины (4 бита). Прочитать данные, полученные с карты, можно с помощью функций SD_ReadBlock() (для чтения одного блока) и SD_ReadMultiBlocks() (для чтения нескольких блоков). Чтобы записать данные на карту, используют функции SD_WriteBlock() (для записи одного блока) и SD_WriteMultiBlocks() (для записи нескольких блоков). Эти функции чтения и записи принимают три аргумента: buffer (переменная-буфер, в которую должны помещаться данные для чтения и записи), address (адрес ячейки памяти SD-карты) и block_size (всегда 512, поскольку эти функции работают только с блоками длинной 512 байтов). Для стирания определенной области памяти карты имеется функция SD_Erase() . В качестве аргументов она принимает startaddr и endaddr. В первом указывается адрес ячейки памяти, с которой начнется стирание, а во втором - адрес ячейки, на которой стирание будет завершено.

Передача данных между модулем SDIO и памятью микроконтроллера может осуществляться как обычным путем через центральный процессор, так и через блок прямого доступа к памяти. Для выбора необходимого режима нужно в файле stm324xg_eval_sdio_sd.h раскомментировать либо строчку #define SD_POLLING_MODE (обычный режим), либо строчку #define SD_DMA_MODE (режим ПДП). После любой операции записи или чтения необходимо с помощью функции SD_GetStatus() проверять, завершила ли карта текущую операцию, и готова ли она к приему или отправке новых данных. Если эта функция вернула значение SD_TRANSFER_OK, то передача завершена успешно, возврат SD_TRANSFER_BUSY означает, что канал передачи занят, а возврат SD_TRANSFER_ERROR сообщает об ошибке передачи. Все эти значения определены в структуре SDTransferState , записанной в stm324xg_eval_sdio_sd.h . Если передача информации выполнялась через блок ПДП, то дополнительно нужно проверять, завершил ли контроллер ПДП все операции по пересылке данных. Это делается с помощью функции SD_ReadWaitOperation() . Также стоит отметить, что передача через блок ПДП завершается прерыванием, поэтому программисту необходимо организовать вызов функции SD_ProcessIRQ() в обработчике прерываний модуля SDIO SDIO_IRQHandler() и вызов функции SD_ProcessDMAIRQ() в обработчике прерываний DMA2 DMA2_Streamx_IRQHandler() .

Для лучшего понимания принципа работы драйвера stm324xg_eval_sdio_sd можно воспользоваться рабочим примером из папки SDIOSDIO_uSDCard (ProjectSTM32F4xx_StdPeriph_ExamplesSDIO). С помощью этой тестовой программы можно выполнить стирание определенной области памяти карты, запись данных по определенному адресу, чтение данных по этому же адресу с последующим сравнением отправленной и принятой информации. Идентичность буфера приема и буфера отправки является свидетельством того, что модуль SDIO, шина передачи информации и SD-карта функционируют нормально.

Таким образом, можно сказать, что SD-карты являются достойными конкурентами USB флеш-накопителей, когда речь заходит о проектировании малых встраиваемых систем, требующих хранения больших объемов данных. Передавать информацию с карты и на карту можно с помощью интерфейса SPI или по шине SD, которая разработана для этих целей и обеспечивает высокую скорость передачи. Реализация связи с картой существенно упрощается при использовании специальных драйверов, облегчающих работу с аппаратной частью и предлагающих программистам простые в использовании функции. Но, к сожалению, записанные в произвольном порядке данные не будут восприняты какой-либо операционной системой, например, Windows, поэтому в следующей части будет рассмотрен порядок работы с SD-картой, имеющей файловую систему FAT.

Список источников

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