STM32Cube и USB. Mass Storage + SD Card.

Продолжаем микроцикл статей (в рамках цикла, посвященного работе с STM32Cube), в которых мы разбираемся как настраивать разные режимы работы USB в микроконтроллерах STM32 при помощи Cube 😉 И сегодня на очереди MSD (Mass Storage Device), а также работа с SD-картой памяти. То есть сегодня мы реализуем свой собственный кардридер на микроконтроллере STM32F10x.

Работа с картой памяти

Как я уже упомянул во вступлении к статье, сегодня я буду использовать микроконтроллер семейства STM32F10x, а карта памяти будет подключаться  по интерфейсу SDIO. Собственно, давайте перейдем в Cube и начнем все поэтапно настраивать =)

Первым делом задействуем внешний генератор тактовых импульсов, а также включаем USB и SDIO. Кроме того, выбираем нужный нам режим работы USB, а именно — Mass Storage Class:

Настройка USB

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

Кроме того, у меня на плате используется так называемый USB_DISCONNECT_PIN — вывод, который отвечает за программное подключение/отключение USB. Эта обязанность возложена на PA10, поэтому я дополнительно проинициализировал эту ножку для работы в режиме выхода.

Итак, с этим все понятно, все настроено, давайте перейдем к настройкам тактирования. В очередной раз напоминаю, что этому была посвящена отдельная статья (ссылка), поэтому сейчас мы останавливаться на этом моменте не будем 😉
Здесь стоит обратить внимание только на один момент, а именно на тактирование USB. Так вот, на модуль USB должны обязательно приходить 48 МГц, иначе работать данная периферия не будет:

Тактирование USB

Следующим на очереди будет окно дополнительной конфигурации выбранной периферии в STM32Cube, но там мы почти ничего не будем менять =) Изменим только предделитель частоты для тактирования модуля SDIO:

Использование карты памяти SD

На этом в принципе и все, переходим к генерации исходного кода, а также файлов проекта.

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

Первым делом я сбрасываю USB_DISCONNECT_PIN в нулевое состояние для активации подключения по USB:

/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_SDIO_SD_Init();
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_10, GPIO_PIN_RESET);
MX_USB_DEVICE_Init();

Как видите, Cube уже реализовал вызов функций инициализации интерфейсов USB и SDIO, но, вопреки ожиданиям, никакой связи этих интерфейсов в проекте нет, и ее нам нужно реализовать самим. Для этого переходим в файл usbd_storage_if.c и находим там ряд функций:

STORAGE_Init_FS,
STORAGE_GetCapacity_FS,
STORAGE_IsReady_FS,
STORAGE_IsWriteProtected_FS,
STORAGE_Read_FS,
STORAGE_Write_FS,
STORAGE_GetMaxLun_FS,
(int8_t *)STORAGE_Inquirydata_FS

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

И первая на очереди — STORAGE_GetCapacity_FS(). В этой функции мы должны определить количество блоков памяти, а также размер одного блока. Начнем с размера блока и определим его следующим образом:

#define BLOCK_SIZE                       512

Для того, чтобы узнать полный размер карты памяти мы можем использовать функцию HAL_SD_Get_CardInfo(). Эта функция принимает на вход два аргумента, которые уже определены в файле main.c. Поскольку мы хотим их использовать в другом файле, то добавляем:

extern SD_HandleTypeDef hsd;
extern HAL_SD_CardInfoTypedef SDCardInfo;

Итак, с размером карты памяти мы разобрались, а как же нам узнать количество блоков? А все просто — делим общий размер на размер одного блока, и в итоге получаем следующую реализацию функции:

int8_t STORAGE_GetCapacity_FS (uint8_t lun, uint32_t *block_num, uint16_t *block_size)
{
  /* USER CODE BEGIN 3 */   
  HAL_SD_Get_CardInfo(&hsd, &SDCardInfo);
  *block_num  = SDCardInfo.CardCapacity / BLOCK_SIZE;
  *block_size = BLOCK_SIZE;
  return (USBD_OK);
  /* USER CODE END 3 */ 
}

Теперь нам осталось создать самые важные функции, а именно функции чтения/записи — STORAGE_Read_FS() и STORAGE_Write_FS(). Здесь мы просто возьмем готовые функции для работы с SD картой и просто приведем аргументы к нужному нам виду:

/*******************************************************************************
* Function Name  : STORAGE_Read_FS
* Description    : 
* Input          : None.
* Output         : None.
* Return         : None.
*******************************************************************************/
int8_t STORAGE_Read_FS (uint8_t lun, 
                        uint8_t *buf, 
                        uint32_t blk_addr,                       
                        uint16_t blk_len)
{
  /* USER CODE BEGIN 6 */ 
  HAL_SD_ReadBlocks(&hsd, (uint32_t*)buf, (uint64_t)(blk_addr * BLOCK_SIZE), BLOCK_SIZE, blk_len);
 
  return (USBD_OK);
  /* USER CODE END 6 */ 
}
 
/*******************************************************************************
* Function Name  : STORAGE_Write_FS
* Description    :
* Input          : None.
* Output         : None.
* Return         : None.
*******************************************************************************/
int8_t STORAGE_Write_FS (uint8_t lun, 
                         uint8_t *buf, 
                         uint32_t blk_addr,
                         uint16_t blk_len)
{
  /* USER CODE BEGIN 7 */ 
  HAL_SD_WriteBlocks(&hsd, (uint32_t*)buf, (uint64_t)(blk_addr * BLOCK_SIZE), BLOCK_SIZE, blk_len);
 
  return (USBD_OK);
  /* USER CODE END 7 */ 
}

Вот, собственно, и все! 😉

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

Таким образом, наша задача решена и мы получили работающий кардридер =)

На сегодня на этом мы заканчиваем, в ближайшее время вновь вернемся к обсуждению разных режимов работы USB в STM32Cube, так что до скорого!

Понравилась статья? Поделись с друзьями!

STM32Cube и USB. Mass Storage + SD Card.: 64 комментария
  1. Здравствуйте! На какой скорости осуществляется чтение/ запись? Full speed usb? И как всегда спасибо за статю!

  2. Добрый день! Попробовал Ваш пример на stm32discoveryf4(stm32f407vg,keil), инициализацию и весь проект генерировал в stm32cube, карта 1gb transend. Не работает. Попробовал вручную задать размер карты, видит но чтение и запись не идут. Подскажите где искать проблему?

    • А сама по себе карта работает? Если вручную записать данные в какой-нибудь сектор, а потом считать?

  3. Доброе утро! Да карта работает. HAL_SD_Get_CardInfo ничего не выдает, если прописать поля
    *block_num и *block_size вручную то определяется карта указанного размера. Потом выходит сообщение что карта должна быть форматирована. Пробую форматировать, выходит сообщение форматирование не может быть завершено. Похоже что hal_sd функции записи и чтения тоже не работают.
    Вот инициализация, которую мне stm32cube сгенерировал:
    void MX_SDIO_SD_Init(void)
    {
    hsd.Instance = SDIO;
    hsd.Init.ClockEdge = SDIO_CLOCK_EDGE_RISING;
    hsd.Init.ClockBypass = SDIO_CLOCK_BYPASS_DISABLE;
    hsd.Init.ClockPowerSave = SDIO_CLOCK_POWER_SAVE_DISABLE;
    hsd.Init.BusWide = SDIO_BUS_WIDE_1B;
    hsd.Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_DISABLE;
    hsd.Init.ClockDiv = 3;
    HAL_SD_Init(&hsd, &SDCardInfo);

    HAL_SD_WideBusOperation_Config(&hsd, SDIO_BUS_WIDE_4B);

    }

  4. Попробовал то же самое в IAR, то же самое Видит съемный диск G, дальше пишет нет доступа к G, неверная функция.

    • Нет, ну понятно, что не определяется через USB, карта же сама по себе, без USB, не работает.

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

  5. Огромное Вам спасибо! И за статью и за подсказки! Пойду искать другую карту.

  6. Добрый день! Попробовал Ваш пример на карте SanDisk 8Gb SDHC, заработало! Все дело в командах видимо. Вот хорошая статья про SD карты http://habrahabr.ru/post/213803. Спасибо за Ваши статьи, пойду пробовать FatFS! Хотелось бы статью про emWin.

  7. В чем измеряется SDCardInfo.CardCapacity ?

    У меня для восьмигиговой карточки он равен 0x4

  8. В новом CubeMX уже поменяли переменные

    BLOCK_SIZE на STORAGE_BLK_SIZ

    , теперь все так должно быть:

    int8_t STORAGE_GetCapacity_FS (uint8_t lun, uint32_t *block_num, uint16_t *block_size)
    {
    /* USER CODE BEGIN 3 */
    HAL_SD_Get_CardInfo(&hsd, &SDCardInfo);
    *block_num = SDCardInfo.CardCapacity / STORAGE_BLK_SIZ;
    *block_size = STORAGE_BLK_SIZ;
    return (USBD_OK);
    /* USER CODE END 3 */
    }

    и

    int8_t STORAGE_Read_FS (uint8_t lun,
    uint8_t *buf,
    uint32_t blk_addr,
    uint16_t blk_len)
    {
    /* USER CODE BEGIN 6 */
    HAL_SD_ReadBlocks(&hsd, (uint32_t*)buf, (uint64_t)(blk_addr * STORAGE_BLK_SIZ), STORAGE_BLK_SIZ, blk_len);
    return (USBD_OK);
    /* USER CODE END 6 */
    }

    и тогда все работает!

  9. Только вот сейчас ( на выходных ) решил собрать Card Reader и узнал что весьма ограниченное семейство Stm32 поддерживает SDIO и USB Device в одном контроллере.

  10. Добрый день!
    Кто-то собирал макет? Какая получилась результирующая скорость чтения данных с карты?

  11. А зачем менять предделитель тактовой частоты для модуля SDIO?
    Хотелось бы больше подробностей, зачем что-то делается..

    З.Ы. За статьи спасибо огромное!

    • Частота общения с картой просто сугубо индивидуальный параметр, каждый ставит столько сколько ему надо, поэтому тут особо нечего описать дополнительно )

  12. У меня на плате STM32F4-Discovery скорость записи — 145 кб/с..

    Все сделано как в примере, чтение и запись работают. Но скорость записи низкая, пытаюсь понять, можно ли повысить и как.

    • На самом деле не факт, что удастся значительно увеличить… Можно попробовать частоту SDIO поднимать или, например, поколдовать с драйвером USB, если выяснится что проблема именно в USB.

  13. Копирование с карты памяти идет очень быстро, разница с записью просто огромная.

    Частоту SDIO не менял — оставил предделитель 0, как и было по умолчанию. Если не ошибаюсь, SDIO тактируется от шины APB2 — а там сейчас 84 МГц.

    А что можно посмотреть в драйвере USB? И какие файлы ковырять?

      • HAL_SD_WriteBlocks позволяет писать блоками по 512 байт — так написано в комментариях функции..

        • Там тоже придется возможно разобраться поподробнее) В общем, у меня тоже как то стояла задача с MSD увеличить скорость, но была не карта памяти, а NAND-память. В драйвере NAND в итоге был изменен размер блоков (увеличен), аналогично и в MSD, поскольку там была возможность пожертвовать объемом памяти. Это дало увеличение скорости, но не фантастическое.

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

  14. Попробовал сделать проект для STM32F429 — изначально плата не завелась, ОС просто не видела подключения нового устройства. Дело оказалось в VBUS_SENSE, перевел в состояние Disable и ОС увидела новое устройство. Правда видит почему-то только в порту USB 3.0, в 2.0 — устройство неопознан, сбой запроса дескриптора.

    Но это не все, увы — как кардридер плата не работает : после подключения к компу в Диспетчере Устройств она появляется сразу, а вот в Моем Компьютере иконка съемного диска появляется с большой задержкой и объем диска под иконкой не отображается. При попытке открыть его выдается сообщение об ошибке — Неверная функция.

    Какой-то баг в функции STORAGE_GetCapacity_FS — потому как если убрать из нее реализацию, то после подключения платы сразу выскакивает сообщение ОС о том, что диск не форматирован и предлагает его отформатировать.

    • Наверно в функциях чтения/записи какая-то проблема. А если убрать STORAGE_GetCapacity_FS, то ОС сразу думает, что диск неотформатирован, а при попытке отформатировать скорее всего также будет ошибка.

  15. Добрый день! на stm32f4disc код заработал сразу, только при записи возникает проблема- записанные данные не сохраняются на sd, после извлечения их нет, хотя сразу после записи файлы отображаются на карте и их можно прочитать. Не подскажете в какую сторону копать?

    • А файловая система нормально работает при последующих подключениях? Windows не предлагает отформатировать диск?

  16. Да, файловая система работает нормально. Похоже проблема была в частоте при инициализации, увеличил делитель на этом этапе, сейчас все в порядке. Спасибо!

  17. Собрал под кубом только для SD, без изменения тактовых частот по умолчанию. Работает, спасибо.
    А запись.чтение только блочные?

    Каким образом прочесть/записать файл?

  18. В новых версиях CubeMX нужно добавить строчку BSP_SD_Init(); чтобы все работало.

  19. Проект, приведенный в статье выполнил на плате STM32F439NIH 6U.
    Не понимаю… FatFS к проекту не подключал, но могу спокойно через проводник Windows открыть диск и прочитать/записать файлы….

    • На карте по всей видимости уже была отформатированная файловая система, соответственно Windows может с ней работать. А вот из программы работать с файлами можно только после подключения FatFs.

  20. Добрый день!
    Наткнулся на вашу статью и решил попробовать. У меня, увы, вылезает ошибка «Запуск этого устройства невозможен. (Код 10)» при подключении конструкции к USB. STM32F4-DISC + CubeMX + Keil5. Такое ощущение что ошибка в самой инициализации USB, но вот где конкретно.. =\

      • Да действительно, дело оказалось в Heap_Size. В случае использования Keil’a его нужно в ручную переправить на 0x00000800 или больше. На всякий случай скину ссылку ( https://blog.brichacek.net/wp-content/uploads/2015/10/STM32F4-and-USB.pdf ). «Кардридер» при этом опознался системой, но сам диск при этом так и недоступен для чтения. Windows предлагает форматировать диск а потом ссылается на невозможность этого. Сама по себе карточка вполне жива Trancend MicroSD HC 8gb. Я пробовал несколько вариантов функции STORAGE_GetCapacity_FS(), тот что вы в посте указывали и то что был упомянут в камментах, собственно разницы ни какой не заметил.

      • Ещё раз спасибо за помощь, я разобрался со всем, на всякий случай для других, если они столкнутся с теми же проблемами:

        1) В случае если используется Keil в файле startup_stm32f(вашмк)xx.s Heap_Size EQU 0x00000200 заменить на Heap_Size EQU 0x00000800. Таким образом избавляемся от ошибки с кодом 10 при инициализации USB.

        2) В случае если используются длинные проводники напрямую припаянные к SD карте (в моём случае к адаптеру), все выводы SDIO должны быть подтянуты к питанию непосредственно возле самой карточки резисторами от 10 до 100к.

        3) Если используется FATFS процедура инициализации в main выглядит следующим образом:

        MX_GPIO_Init();
        MX_SDIO_SD_Init();
        MX_FATFS_Init();

        BSP_SD_Init();

        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_10, GPIO_PIN_RESET);

        MX_USB_DEVICE_Init();

        И всё будет хорошо 😉

  21. Для тех у кого не получилось ни с измененнием делителя частоты карточки, ни с различным порядком инициализации, добавлении резистроров и т.д.
    Использую новый хал, там где заменено BLOCK_SIZE на STORAGE_BLK_SIZ. после изменения на 512 — ничего не работает (видит неотформатированный диск), думал если гиговая SD шка то адресация по 1 байту и нужно STORAGE_BLK_SIZ = 1, но нет ни одна из карт не заводится ни 32 Гб ни 1Гб.
    После вернул значение которое порекомендовал сам CubeMX, STORAGE_BLK_SIZ = 200, и любая карта начала определяться компом.
    Одно смущает скорость ~500kb/s — чтение, 150kb/s — запись. Помоему это не дело, запись через ФатФС и то быстрее идет, нужно как то это решать…

    • Так а нет ли здесь несостыковки в системах счисления?… Потому что 200 (значение, которое заработало) подозрительно смахивает на 512 )
      0x200 = 512

  22. Уважаемый Aveal.

    Почему в примере используется #define BLOCK_SIZE 512 , а не SDCardInfo.CardBlockSize ???

    Почему именно 512 ?

    • Вообще, в теории, должно работать и если установить 1024 или 2048, к примеру. При этом работать должно быстрее, но это менее оптимально с точки зрения экономии памяти.

  23. Скорость передачи информации на SD карту обычным картридером составляет 10 МБ/сек. Контроллером, прошитым кодом как из данной статьи так и из готовых примеров от STM — 850 КБ/сек (100 MБ передал за 13 сек и 2 минуты соответственно).

    • Да, это известная проблема использования MSD на STM. Больше того — если вместо SD поставить более быструю NAND, то скорость все равно будет не особо шустрая.

  24. Снова здравствуйте, уважаемый Aveal. У меня опять вопрос. Данный пример демонстрирует применение режима Mass Storage Class для USB, однако данное устройство ведь без не будет корректно отображаться без FATFS. Повторю мою задачу. Нужно чтобы STMка по USB определилась как дисковый накопитель и при этом в режиме FATFS. В общем вот мои вопросы:
    1) Как я понял, чтобы использовать режим Mass Storage Class для USB Device, устройству нужно объяснить, что за область памяти мы хотим для этих целей использовать (в моем случае внутренняя flash память). Для этого мы открываем файлик usbd_storage_if.c прописываем там все необходимое. Я пока адаптировал только функцию STORAGE_GetCapacity_FS и на компе все равно не стало видно, сколько памяти у моего устройства. Правильно ли я рассуждаю?
    2) По идее, после того, как я объяснил USB, с какой памятью надо взяимодействовать, нужно прикрутить FATFS. Как я понял, для этих целей куб с создал файлик user_diskio.c. Что там нужно непосредственно прописать? В ваших примерах все это по отдельности есть, но для проекта из CubeMX и функций режима MSC нету. Подскажите
    Заранее спасибо за ответ!

    • Добрый день!

      Нужно отредактировать все функции в usbd_storage_if.c для начала. А в драйвере FatFs даже нет необходимости в проекте — можно отформатировать средствами Windows накопитель и внутренняя память будет определяться как носитель с Fat. И сразу ответ на третий вопрос — функция FATFS_LinkDriver() как раз связывает указатели на функции с диском.

      И вот еще пример с Flash и MSD, но без Cube — http://microtechnics.ru/stm32-i-usb-mass-storage-device/

      • Спасибо) Ваши статьи по данной тематике на этом сайте я все перечитал.
        1) То есть, чтобы моя внутренняя flash память STM32 определилась на ПК как носитель, нужно файл usbd_storage_if.c отредактировать как тут, только как тут у вас?
        http://microtechnics.ru/stm32-i-usb-mass-storage-device/
        И все?
        2) А какой тогда смысл в файловой системе тут, если ПК и так может STMку отформатировать?

        • 1). Да.
          2). Если задача как в статье, то смысла в файловой системе нет — собственно, FatFs и не добавлен в этом примере.

          • По поводу файловой системы. То есть файловая система мне понадобится только в том случае, если сама STM32 захочет работать с файлами, записанными на каком-то носителе? FLASH или SD карта к примеру. Так?

  25. Да, и вот еще в догонку
    3) Как теперь подправленные в файле user_diskio.c фунции применить теперь правильно? Или все же привязано и просто вызываем f_mount и.т.д.

  26. А то 64Мбита пропадает, а как это реализовать в CubeMX не могу понять. Буду очень рад если подобный пример появится.

  27. Приветствую. Как в режиме MSC задать имя устройства, которое в моем компьютере отображается, по умолчанию?

  28. не могли бы Вы написать статью по работе с MMC картами через SPI/ очень нужно

    • Просто функции чтения/записи драйвера SD учитывают смещение в байтах, а в драйвере USB blk_addr — смещение в секторах.

  29. Hello 😉

    You’re tutorials are great, and I want to very thank you. But in this case, i’ve got a problem, i use STM32F4Discovery and after configure everything and connect USB to PC, i got a messages from windows that «A request for the USB device descriptor failed.»

    Can you help me, please 🙂

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *