Всем доброго дня! Сегодня мы поговорим о подключении SD-карты к микроконтроллеру STM32.
Казалось бы, памяти у контроллеров STM32F10x и так много, зачем там еще дополнительная, но это впечатление обманчиво ) Вот, например, надо нам на дисплей вывести пару-тройку разных изображений - формат 320*240 - то есть 76800 пикселей, каждому из которых соответствует целых 2 байта. Вот и получаем около 150 кБ на одну картинку. А это немало по меркам микроконтроллера, и не факт, что две разные картинки удастся запихать в его Flash-память.
Или надо нам хранить большие объемы информации, данные с какого-нибудь датчика, к примеру. Да еще так, чтобы эти данные были доступны и после отключения питания. Вот тут то нам и пригодится внешняя память. И отличным решением будет SD-карта. К слову в этой статье мы будем проводить опыты над картой microSD.
Для начала пара слов о самой карте памяти, точнее о ее распиновке. Выглядит все это дело следующим образом:
Итак, что тут у нас? Ну сразу видно, что выводов у нее целых восемь штук. Назначение выводов следующее:
Колонка SPI Mode нам намекает на то, что SD-карта взаимодействует с микроконтроллером при помощи интерфейса SPI. НО! Мы пойдем по другому пути. Все дело в том, что STM32 имеют на своем борту готовый периферийный модуль для работы именно с картами памяти, и называется он SDIO.
Вообще взаимодействие с картами памяти заключается в передаче им определенных команд. Некоторые команды требует наличия аргумента, некоторые нет. Команды можно найти в официальной документации. Так вот встроенный модуль SDIO дает возможность значительно упростить процесс передачи команд, да и вообще процесс работы с внешними картами памяти. Например, вот регистр SDIO_CMD - туда мы просто напросто записываем код команды, которую хотим передать карте. Или вот статусный регистр SDIO_STA - там целых 24 флага на каждый чих, то есть для большого количества событий.
Кстати STM радует еще и добротной документацией на все это дело. Вот, к примеру, подробное описание инициализации для карты памяти SD (аналогично все описано для других типов карт):
Время традиционной вставки: поскольку компания STMicroelectronics прекратила поддержку библиотеки SPL, которая использовалась в этом курсе, я создал новый, посвященный работе уже с новыми инструментами, так что буду рад видеть вас там - STM32CubeMx. Кроме того, вот глобальная рубрика по STM32, а также статья на смежную тему из нового курса: STM32 и SD-карта. Настройка FatFs в STM32CubeMx.
Ну, собственно, пора перейти к практическому примерчику. Поковыряем-ка SPL.
В файле 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 - аргумент команды, также заполняем остальные поля. Осталось как-то эти данные запихать в карту памяти. А для этого нам приготовили функцию:
- SDIO_SendCommand (SDIO_CmdInitTypeDef *SDIO_CmdInitStruct).
В качестве аргумента передаем ей как раз таки созданную нами структуру. Для записи данных есть функция - SDIO_WriteData(uint32_t Data). После вызова этой функции данные окажутся в специально предназначенном для этого регистре - SDIO_FIFO.
Теперь перейдем наконец-то к практике . Я снова буду работать с платой Mini STM32, поскольку добрые китайцы озадачились установкой на нее слота для microSD. Вот схема подключения разъема для карты к микроконтроллеру:
Для написания программы воспользуемся готовым примером для Keil'а - стащим оттуда два файла, в которых реализовано что-то вроде драйвера для работы с картами - это файлы sdcard.c и sdcard.h. Создаем новый проект, цепляем туда эти файлы, а кроме того, естественно, файлы CMSIS и SPL. Вот готовый проект, в который все уже добавлено - остается только написать код функции main():
В файле sdcard.c реализованы всевозможные функции для работы с картой памяти, нам теперь остается их только использовать. Пишем код, и для примера запишем на microSD 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 байт.
Если мы запустим программу под отладчиком, то увидим, что считанные данные соответствуют записанным, так что эксперимент можно считать удавшимся ) В дальнейшем можно добавить к проекту поддержку файловой системы FatFs, как, например тут или тут.
На этом на сегодня заканчиваем, до скорых встреч!