Продолжаем микро-цикл статей (в рамках макро-цикла, посвященного работе с STM32CubeMx), в которых мы разбираемся как настраивать разные режимы работы USB в микроконтроллерах STM32 при помощи CubeMx. И сегодня на очереди MSD (USB Mass Storage Device), а также работа с SD-картой памяти. То есть по сути реализуем свой собственный кардридер на контроллере STM32F10x. При этом карта памяти будет подключаться по интерфейсу SDIO.
Собственно, давайте перейдем в STMCubeMx и начнем все поэтапно настраивать. Первым делом задействуем внешний генератор тактовых импульсов:
Следующий шаг - активация интерфейса SDIO в 4-х битном режиме (именно так подключена карта памяти на моей плате). Заодно зададим предделитель для SDIO:
Предварительный этап конфигурации финишируем настройкой USB. Снова сначала включаем, затем выбираем нужный режим:
При включении какого-либо периферийного модуля CubeMx автоматически отмечает и инициализирует нужные выводы микроконтроллера, вручную этого делать не надо.
Итак, с этим все понятно, все настроено, давайте перейдем к настройкам тактирования. В очередной раз напоминаю, что этому была посвящена отдельная статья (ссылка), поэтому сейчас мы останавливаться на этом моменте не будем ) Здесь стоит обратить внимание только на один момент, а именно на тактирование USB. Так вот, на модуль USB должны обязательно приходить 48 МГц, иначе работать данная периферия не будет:
На этом, в принципе, и все, переходим к генерации исходного кода, а также файлов проекта. После окончания этой процедуры получаем полностью готовый и настроенный проект, но на этом наша работа не заканчивается, как и всегда, в проект надо будет внести изменения. Так что открываем IDE и переходим к редактированию.
CubeMx уже реализовал вызов функций инициализации 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 STORAGE_BLK_NBR 0x10000 #define STORAGE_BLK_SIZ 0x200
int8_t STORAGE_GetCapacity_FS(uint8_t lun, uint32_t *block_num, uint16_t *block_size) { /* USER CODE BEGIN 3 */ *block_num = STORAGE_BLK_NBR; *block_size = STORAGE_BLK_SIZ; return (USBD_OK); /* USER CODE END 3 */ }
То есть, в принципе, можно задать значения и руками через STORAGE_BLK_NBR
и STORAGE_BLK_SIZ
. Но мы пойдем по концептуально другому пути. Для того, чтобы узнать параметры использующейся SD-карты, мы можем использовать функцию HAL_SD_GetCardInfo()
. Она принимает на вход два аргумента, и все в тот же файл usbd_storage_if.c добавляем:
/* USER CODE BEGIN PRIVATE_VARIABLES */ HAL_SD_CardInfoTypeDef cardInfo; // <--- Добавлено /* USER CODE END PRIVATE_VARIABLES */ /** * @} */ /** @defgroup USBD_STORAGE_Exported_Variables * @brief Public variables. * @{ */ extern USBD_HandleTypeDef hUsbDeviceFS; /* USER CODE BEGIN EXPORTED_VARIABLES */ extern SD_HandleTypeDef hsd; // <--- Добавлено /* USER CODE END EXPORTED_VARIABLES */
В итоге получаем такую реализацию функции STORAGE_GetCapacity_FS()
:
int8_t STORAGE_GetCapacity_FS(uint8_t lun, uint32_t *block_num, uint16_t *block_size) { /* USER CODE BEGIN 3 */ HAL_SD_GetCardInfo(&hsd, &cardInfo); *block_num = cardInfo.BlockNbr; *block_size = cardInfo.BlockSize; return (USBD_OK); /* USER CODE END 3 */ }
Осталось осуществить работу с самыми важными функциями, отвечающими за чтение и запись данных - STORAGE_Read_FS()
и STORAGE_Write_FS()
. Используем соответствующие функции для взаимодействия с SD-картой:
HAL_SD_ReadBlocks()
HAL_SD_WriteBlocks()
/** * @brief . * @param lun: . * @retval USBD_OK if all operations are OK else USBD_FAIL */ 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, buf, blk_addr, blk_len, 10); return (USBD_OK); /* USER CODE END 6 */ } /** * @brief . * @param lun: . * @retval USBD_OK if all operations are OK else USBD_FAIL */ 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, buf, blk_addr, blk_len, 10); return (USBD_OK); /* USER CODE END 7 */ }
Вот, собственно, и все, здесь "10" - это величина таймаута для операций чтения/записи.
Собираем наш проект, прошиваем контроллер, подключаем плату к ПК и видим, что в системе у нас появилось запоминающее устройство, а также определился внешний накопитель, соответствующий используемой карте памяти. Таким образом, наша задача на сегодня решена 👍
И еще раз спасибо)
=)
Здравствуйте! На какой скорости осуществляется чтение/ запись? Full speed usb? И как всегда спасибо за статю!
Да, FS
Подумал сделать кардридер на stm32
Добрый день! Попробовал Ваш пример на stm32discoveryf4(stm32f407vg,keil), инициализацию и весь проект генерировал в stm32cube, карта 1gb transend. Не работает. Попробовал вручную задать размер карты, видит но чтение и запись не идут. Подскажите где искать проблему?
А сама по себе карта работает? Если вручную записать данные в какой-нибудь сектор, а потом считать?
Доброе утро! Да карта работает. 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);
}
Попробовал то же самое в IAR, то же самое Видит съемный диск G, дальше пишет нет доступа к G, неверная функция.
Нет, ну понятно, что не определяется через USB, карта же сама по себе, без USB, не работает.
Для начала можно попробовать другую карту просто вставить и убедиться, что проблема сохранится. И тогда надо будет по коду пройтись, проверить что там все что надо инициализируется правильно, потому что до сих пор периодически вылезают новые и новые баги Куба.
Огромное Вам спасибо! И за статью и за подсказки! Пойду искать другую карту.
Добрый день! Попробовал Ваш пример на карте SanDisk 8Gb SDHC, заработало! Все дело в командах видимо. Вот хорошая статья про SD карты http://habrahabr.ru/post/213803. Спасибо за Ваши статьи, пойду пробовать FatFS! Хотелось бы статью про emWin.
В чем измеряется SDCardInfo.CardCapacity ?
У меня для восьмигиговой карточки он равен 0x4
В новом 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 */
}
и тогда все работает!
Только вот сейчас ( на выходных ) решил собрать Card Reader и узнал что весьма ограниченное семейство Stm32 поддерживает SDIO и USB Device в одном контроллере.
Добрый день!
Кто-то собирал макет? Какая получилась результирующая скорость чтения данных с карты?
А зачем менять предделитель тактовой частоты для модуля SDIO?
Хотелось бы больше подробностей, зачем что-то делается..
З.Ы. За статьи спасибо огромное!
Частота общения с картой просто сугубо индивидуальный параметр, каждый ставит столько сколько ему надо, поэтому тут особо нечего описать дополнительно )
У меня на плате STM32F4-Discovery скорость записи - 145 кб/с..
Все сделано как в примере, чтение и запись работают. Но скорость записи низкая, пытаюсь понять, можно ли повысить и как.
На самом деле не факт, что удастся значительно увеличить... Можно попробовать частоту SDIO поднимать или, например, поколдовать с драйвером USB, если выяснится что проблема именно в USB.
Копирование с карты памяти идет очень быстро, разница с записью просто огромная.
Частоту SDIO не менял - оставил предделитель 0, как и было по умолчанию. Если не ошибаюсь, SDIO тактируется от шины APB2 - а там сейчас 84 МГц.
А что можно посмотреть в драйвере USB? И какие файлы ковырять?
Можно попробовать не по 512 байт писать на карту, а блоками по 1024.
HAL_SD_WriteBlocks позволяет писать блоками по 512 байт - так написано в комментариях функции..
Там тоже придется возможно разобраться поподробнее) В общем, у меня тоже как то стояла задача с MSD увеличить скорость, но была не карта памяти, а NAND-память. В драйвере NAND в итоге был изменен размер блоков (увеличен), аналогично и в MSD, поскольку там была возможность пожертвовать объемом памяти. Это дало увеличение скорости, но не фантастическое.
С SD еще сталкивался с тем, что карты разных производителей дают разную скорость, причем были карты на которых могло и в 2 раза отличаться.
Попробовал сделать проект для STM32F429 - изначально плата не завелась, ОС просто не видела подключения нового устройства. Дело оказалось в VBUS_SENSE, перевел в состояние Disable и ОС увидела новое устройство. Правда видит почему-то только в порту USB 3.0, в 2.0 - устройство неопознан, сбой запроса дескриптора.
Но это не все, увы - как кардридер плата не работает : после подключения к компу в Диспетчере Устройств она появляется сразу, а вот в Моем Компьютере иконка съемного диска появляется с большой задержкой и объем диска под иконкой не отображается. При попытке открыть его выдается сообщение об ошибке - Неверная функция.
Какой-то баг в функции STORAGE_GetCapacity_FS - потому как если убрать из нее реализацию, то после подключения платы сразу выскакивает сообщение ОС о том, что диск не форматирован и предлагает его отформатировать.
Наверно в функциях чтения/записи какая-то проблема. А если убрать STORAGE_GetCapacity_FS, то ОС сразу думает, что диск неотформатирован, а при попытке отформатировать скорее всего также будет ошибка.
Добрый день! на stm32f4disc код заработал сразу, только при записи возникает проблема- записанные данные не сохраняются на sd, после извлечения их нет, хотя сразу после записи файлы отображаются на карте и их можно прочитать. Не подскажете в какую сторону копать?
А файловая система нормально работает при последующих подключениях? Windows не предлагает отформатировать диск?
Да, файловая система работает нормально. Похоже проблема была в частоте при инициализации, увеличил делитель на этом этапе, сейчас все в порядке. Спасибо!
Собрал под кубом только для SD, без изменения тактовых частот по умолчанию. Работает, спасибо.
А запись.чтение только блочные?
Каким образом прочесть/записать файл?
Только блоками.
Добавить в кубе поддержку FatFS? Или ещё что-то?
Да, добавить FatFs.
Да, спасибо. Нашёл у вас на сайте статью про фат.
В новых версиях CubeMX нужно добавить строчку BSP_SD_Init(); чтобы все работало.
Проект, приведенный в статье выполнил на плате STM32F439NIH 6U.
Не понимаю... FatFS к проекту не подключал, но могу спокойно через проводник Windows открыть диск и прочитать/записать файлы....
На карте по всей видимости уже была отформатированная файловая система, соответственно Windows может с ней работать. А вот из программы работать с файлами можно только после подключения FatFs.
Добрый день!
Наткнулся на вашу статью и решил попробовать. У меня, увы, вылезает ошибка "Запуск этого устройства невозможен. (Код 10)" при подключении конструкции к USB. STM32F4-DISC + CubeMX + Keil5. Такое ощущение что ошибка в самой инициализации USB, но вот где конкретно.. =\
Добрый день!
Вот в комментариях к этой статье (https://microtechnics.ru/stm32cube-i-usb-virtual-com-port/) вроде бы было найдено решение похожей проблемы, надеюсь сработает.
Да действительно, дело оказалось в 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();
И всё будет хорошо 😉
Для тех у кого не получилось ни с измененнием делителя частоты карточки, ни с различным порядком инициализации, добавлении резистроров и т.д.
Использую новый хал, там где заменено BLOCK_SIZE на STORAGE_BLK_SIZ. после изменения на 512 - ничего не работает (видит неотформатированный диск), думал если гиговая SD шка то адресация по 1 байту и нужно STORAGE_BLK_SIZ = 1, но нет ни одна из карт не заводится ни 32 Гб ни 1Гб.
После вернул значение которое порекомендовал сам CubeMX, STORAGE_BLK_SIZ = 200, и любая карта начала определяться компом.
Одно смущает скорость ~500kb/s - чтение, 150kb/s - запись. Помоему это не дело, запись через ФатФС и то быстрее идет, нужно как то это решать...
Bur, вы что-то нибудь придумали по данной проблеме?
Так а нет ли здесь несостыковки в системах счисления?... Потому что 200 (значение, которое заработало) подозрительно смахивает на 512 )
0x200 = 512
Уважаемый Aveal.
Почему в примере используется #define BLOCK_SIZE 512 , а не SDCardInfo.CardBlockSize ???
Почему именно 512 ?
Вообще, в теории, должно работать и если установить 1024 или 2048, к примеру. При этом работать должно быстрее, но это менее оптимально с точки зрения экономии памяти.
Скорость передачи информации на SD карту обычным картридером составляет 10 МБ/сек. Контроллером, прошитым кодом как из данной статьи так и из готовых примеров от STM - 850 КБ/сек (100 MБ передал за 13 сек и 2 минуты соответственно).
Да, это известная проблема использования MSD на STM. Больше того - если вместо SD поставить более быструю NAND, то скорость все равно будет не особо шустрая.
Снова здравствуйте, уважаемый 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 - https://microtechnics.ru/stm32-i-usb-mass-storage-device/
Спасибо) Ваши статьи по данной тематике на этом сайте я все перечитал.
1) То есть, чтобы моя внутренняя flash память STM32 определилась на ПК как носитель, нужно файл usbd_storage_if.c отредактировать как тут, только как тут у вас?
https://microtechnics.ru/stm32-i-usb-mass-storage-device/
И все?
2) А какой тогда смысл в файловой системе тут, если ПК и так может STMку отформатировать?
1). Да.
2). Если задача как в статье, то смысла в файловой системе нет - собственно, FatFs и не добавлен в этом примере.
По поводу файловой системы. То есть файловая система мне понадобится только в том случае, если сама STM32 захочет работать с файлами, записанными на каком-то носителе? FLASH или SD карта к примеру. Так?
Да, только если из программы для МК надо будет работать с файлами.
Да, и вот еще в догонку
3) Как теперь подправленные в файле user_diskio.c фунции применить теперь правильно? Или все же привязано и просто вызываем f_mount и.т.д.
Очень бы хотелось подобный пример, только для работы с SDRAM.
А то 64Мбита пропадает, а как это реализовать в CubeMX не могу понять. Буду очень рад если подобный пример появится.
Приветствую. Как в режиме MSC задать имя устройства, которое в моем компьютере отображается, по умолчанию?
Я не уверен, что так получится сделать...
не могли бы Вы написать статью по работе с MMC картами через SPI/ очень нужно
Врядли получится в ближайшее время... Да и платы нет подходящей.
А почему адрес высчитывается следующим образом - blk_addr * BLOCK_SIZE?
Просто функции чтения/записи драйвера SD учитывают смещение в байтах, а в драйвере USB blk_addr - смещение в секторах.
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 🙂
Где взять проект?
У меня глючила запись пока не добавил проверку состояния карты после записи:
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_CardStateTypeDef state;
HAL_StatusTypeDef status = HAL_SD_WriteBlocks(&hsd, buf, blk_addr, (uint32_t) blk_len, 10);
if(status != HAL_OK) return (USBD_FAIL);
do{
state = HAL_SD_GetCardState(&hsd);
} while(state == HAL_SD_CARD_PROGRAMMING);
return (USBD_OK);
/* USER CODE END 7 */
}
По хорошему состояние надо проверять и при чтении, но работает и так.
Кроме того, они поменяли структуру SDCardInfo и теперь в STORAGE_GetCapacity_FS надо делать так:
*block_num = SDCardInfo.LogBlockNbr;
Скорость записи получилась 165 кБ/с.
Скорость чтения 310 кБ/с.
Благодарю за дополнения!
Небольшое уточнение. Указанные скорости получились в 1-битном режиме. В 4-битном несколько быстрее:
Запись 175 кБ/с.
Чтение 408 кБ/с.
Спасибо, без этого ожидания do {} while плохо работало. Пришлось вставлять и в STORAGE_Read_FS(), иначе с флешки читался какой-то мусор.
Здравствуйте! Я правильно понимаю, после того как прописаны эти функции, и USB подключен к компьютеру, он сам уже определяет устройство? А главная функция кроме инициализации ничего не делает? Объясните пожалуйста хоть примерно как это работает? Генерируется какое-то прерывание, вызывающее функции из usbd_storage_if.c ?
Добрый день!
Да, все верно, инициализируем, а дальше все работает на прерываниях.
А не могли бы Вы выложить код главной функции?
Делаю все так как у вас, только у меня не существует структуры extern HAL_SD_CardInfoTypedef SDCardInfo; поэтому я ее просто определяю без extern. И еще у меня не FS, а HS, но функции, которые надо прописывать куб сгенерировал аналогичными.
вот моя инициализация, скажите пожалуйста, чего не хватает?
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
//MX_FSMC_Init();
//MX_TIM6_Init();
// MX_I2C1_Init();
MX_SDIO_SD_Init();
MX_FATFS_Init();
BSP_SD_Init();
MX_USB_DEVICE_Init();
//Init_Ext_Periph();
USB_ON; //включение питания usb3320
USB_RES_OFF; //usb3320 работает в нормальном режиме
while (1)
{
}
}
STM, к сожалению, очень любят с изменением версии библиотек поменять названия типов и т. д., поэтому код становится несовместимым... Так что возможно HAL_SD_CardInfoTypedef теперь называется как-то иначе.
А в чем проблема проявляется? USB устройство не появляется в диспетчере устройств?
Именно, ничего не происходит.
Смотрю пример от ST там еще какие-то функции
пример 1
int main(void)
{
Set_System();
Set_USBClock();
Led_Config();
USB_Interrupts_Config();
USB_Init();
while (bDeviceState != CONFIGURED);
USB_Configured_LED();
while (1);
}
пример 2 из интеренета
int main(void)
{
HAL_Init();
SystemClock_Config();
USBD_Init(&USBD_Device, &MSC_Desc, 0);
USBD_RegisterClass(&USBD_Device, USBD_MSC_CLASS);
USBD_MSC_RegisterStorage(&USBD_Device, &USBD_DISK_fops);
USBD_Start(&USBD_Device);
while (1)
}
И непонятно пока что и куда. А у вас только MX_USB_DEVICE_Init();?
Да:
MX_GPIO_Init();
MX_SDIO_SD_Init();
MX_USB_DEVICE_Init();
Судя по коду Пример 1 со старыми библиотеками USB и, наверно, на SPL.
И Пример 2 тоже.
Скорее всего дело в подключении, у Вас какая плата?
Судя по поведению платы функции из usbd_storage_if.c вообще не вызываются. А должны при вызове загораться светодиоды.
Плата с STM32F207 на борту, а так же разъем под SD карту(SDIO) и разъем USB с микросхемой USB3320(ULPI). На карту силами контроллера уже пишутся данные. В кубе настраиваю USB_OTG_HS Device only. И подключаю библиотеку USB DEVICE MSC for HS
подключение микросхемы usb https://www.radiokot.ru/forum/download/file.php?id=313259
Скорее всего с электрической частью проблемы...
Мучался пару часов все делал как описано в посте, но не чего не получалось переменые
*block_num = SdCard.BlockNbr;
*block_size = SdCard.BlockSize;
не получали значения пока в main не добавил инициализацию SD
HAL_SD_Init(&hsd);
после этого все отлично заработало.
Странно, наверно в текущей версии HAL почему-то инициализация SD в сгенерированный код не попала...
Рад что заработало! =)
Забыл написать автору спасибо реально помогло).
Здравствуйте.
Есть вот такая плата https://stm32-base.org/boards/STM32F407VET6-STM32-F4VE-V2.0.html
Решил попробовать SDIO. Сделал настройки в CubeMX. Частота процессора максимальная — 168 мгц.
Ширину шины сделал 4 бит. Подтягивающие входы делать не стал.
Инициировал VCP. Саму SD карту. Среда Keil 5.27
Прошил. Пытаюсь читать секторы из MicroSD карты функцией HAL_SD_ReadBlocks и потом передавать по VCP.
Функция возвращает ОК, но в принятых массиве 512 байт какие-то странные значения. Явно не того содержания. При чём они не случайные. Самый характерный это нулевой блок. У него последние два байта реальные (какие и должны быть). Остальные нет. И почти все они равны 0. При чём если я задач чтение 512-го блока то визуально, словно считал 0-й блок. У 1024-го так же. И так далее. Все блоки не кратные 512 засыпаны нулями. Принимаю на стороне ПК так же из своего приложения. Приём и передача по USB нормальные. Я это проверял генерацией произвольного паттерна массива данных и они передаются корректно. И контроллер по USB получает запрос-пакет так же корректно.
Инициализация вот такая. Сделал даже деление на 168 (ClockDiv), чтобы был 1 Мгц. Бесполезно. Что там 0 стоит, что 1, что 3, 5, 10 и тд.
static 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 = 168;
if (HAL_SD_Init(&hsd) != HAL_OK)
{
Error_Handler();
}
if (HAL_SD_ConfigWideBusOperation(&hsd, SDIO_BUS_WIDE_4B) != HAL_OK)
{
Error_Handler();
}
}
Но, что характерно, считывание карты через HAL_SD_GetCardInfo проходит нормально. Я смотрю содержание структуры SD_Card_Info и они вполне похожи на правду.
Что не так с дальнейшим чтением, просто не могу понять. Ковыряюсь несколько дней и всё безрезультатно. Поставил с тоски даже Atollic TRUE Studio. По факту там получается тоже самое. При чём HAL_SD_ReadBlocks возвращает HAL_OK. Типа ОК товарищ, я тебе считала блок с карты. Дичь какая-то. При чём в CubeMX у RCC стоит восклицательный знак. И сказано "Parity disabled conflict SDIO:Mode SD 4 bits wide bus". Но, сгенерировать проект она даёт.
Может подскажите что?
Добрый вечер, а можете проект выслать?
Проект прислать в виде файла ioc? Или целиком архив исходников для Keil?
Лучше все сразу, чем больше, тем лучше )
Не очень понятно, как это тут сделать? Как пристыковать архив?
Можно на почту, https://microtechnics.ru/kontakty/
Отправил на данную почту (Aveal.MicroTechnics@gmail.com) архив проекта и проект CubeMX. Приложение для Win32 не высылал. Думаю, что смысла нет. Или будете проверять на реальной плате?
Но, моё письмо не дошло. Не могу пристыковать сюда даже отлуп вашего сервера. Вообще ничего не понимаю.
Но, моё письмо не дошло.
Отлуп удалось прислать только в виде скриншота. В виде текста, который мне пришёл обрано на почту не вышло. При нажатии на "Отправить комментарий" просто ничего не происходило.
gmail не пропускает exe, bin итд итп. Хотя сейчас уже и яндекс, mail.ru и все остальные тоже научились блокировать. Надо либо удалить собранные файлы из архива, либо выложить на диск или подобное и ссылкой кинуть.
В архиве нет ни exe ни bin однозначно. Недавно заказчикам отправлял новую сборку своей программы (там exe точно был). mail.ru точно спокойно отправляет. Скорее всего проблема на принимающей стороне.
Но, ещё более странно то, что отсюда нельзя было отпарвимть просто текст возвратного сообщения. В нём тоже может быть вирус?-)) Высылаю ссылку на архив https://cloud.mail.ru/public/4yXk/AffNYFbBs
Там hex )
По поводу текста в комментарии затрудняюсь сходу сказать... Попробую отловить проблему.
Ну hex то едва может быть вирусом для ПК. По моему это уже паранойя.-)
Ну тут я уже не могу ничего сделать) Если захвачу google - наведу порядок)
Заведите ещё вторую почту на mail.ru. Хуже уж точно не будет.
В архиве есть ряд лишних файлов. Например sdio_sdcard.h и sdio_sdcard.с. Это я уже искал что-то потом, после неудачной работы с HAL_SD_ReadBlocks. На них там можно не обращать внимания. Можно даже убрать из include
Если через HAL_SD_WriteBlocks() записать фиксированные данные в блок, затем сразу считать HAL_SD_ReadBlocks() - какой результат? Без USB.
Не пробовал так делать. Просто читал блоки. Ведь работать так же должно? Должно. И почему без USB? VCP довольно удобная вещь.
Потому что чтобы найти проблему нужно изолировать узлы друг от друга. Если подозревается проблема в карте, то надо проверить карту без чего-либо прочего.
Вставляю карту в ридер. Всё прекрасно читается. Это всё наблюдается на всех имеющихся у меня картах. Но, как USB может мешать работе SDIO? Ведь, если так, то это уже за гранью здравого смысла и тогда сделать устройство с USB не получится.
Если я просто в программе сгенерую массив как сочту нужным, то он по VCP так и передастся. То есть передача вполне нормально работает. Если, при чём я это сделаю до HAL_SD_ReadBlocks(), то после её вызова она это массив, как и положено, перезапишет, но какой-то просто непонятной фигнёй, где обычно всегда нули. Кроме блоков, номера которых кратны 512-ти. В этих кратных блоках могут быть изредка и нули. В общем, какая-то чушь. Пробовал MicroSD карты на 512 мб, 1 Гб, 2 гб. Подключал даже через сокет и полноформатные карты. Поведение схожее. То ли кривизна библиотеки HAL, то ли плата черт знает как разведена. Но, что удивительно, - атрибуты карты читаются верно. То есть по идее всё с платой хорошо.
Ну а что Вы ожидаете конкретно с карты считать? У меня нет уверенности, что считывается не то, поэтому и предлагаю записать заранее известные данные, чтобы считав убедиться, что считаны не они, а нечто иное.
Я пробовал читать их через WinHEX. например там в нулевом блоке есть сигнатура MSDOS и тд. Ничего похожего при чтения через плату там не наблюдается. Записал файл, где 256 блоков по 512 байт каждый и все байты там инкрементируются от 0 до 255.То есть нулевой этого файла блок содержит нули, первый единицы и так далее. И так до FF. нашёл в WinHEX где он начинается. Это 528-й блок по абсолютной адресации. Вставил в плату. Выбрал 528-й,- блок нули. Ну, так и должно быть. Выбрал 529-й. Должны быть 1-цы. А опять нули. И так далее. В 0-м блоке карты так же почти все нули. кроме последних двух байт. Они именно такие, как и должны быть. В нё же есть ещё несколько ненулевых байт, но они уже отсебятные.
Ну, или после инициализации карты по MX_SDIO_SD_Init и чтению её параметров по HAL_SD_GetCardInfo, до применения HAL_SD_ReadBlocks нужно что-то ещё в карту послать. Может быть какие-то команды. Смотрел осциллографом, такое ощущение, что всё работает (вернее пытается) в однобитном режиме, так как например на линиях DA2 или DAT3 никаких импульсов не заметил. Ну, возможно, что так как там одни нули летят (почти) то и их может и не быть.
Здесь изначально так и было все enable?
С bypass enable частота слишком большая.
hsd.Init.ClockDiv = 8; разве уже будет высокой? Но я ставил и в 168 не помогает.
Пришлите тогда пожалуйста проект, где блоки читаются корректно. У меня это что-то не получается.
Да, так и было. Делал hsd.Init.ClockBypass = SDIO_CLOCK_BYPASS_DISABLE; или hsd.Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_DISABLE; - не помогает.
bypass нивелирует работу этого предделителя. Надо начинать с настроек всех в disable, и делитель 8 я думаю вполне подходит.
Сделал вот так же.
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 = 10;
К сожалению, ничего не изменилось. Из нулевого блока карты читается вот это. См. скриншот. Что совсем далеко от действительности.
https://disk.yandex.ru/d/tDwLndVTOaru-A
Простейший проект набросал, должно работать, но, к сожалению, покопался - проверить не на чем.
Да, вот и плохо, что не на чем. Изменения настроек hsd эффекта не возымели. Содержимого 0-го, сектора, 512-го, 1024-го..... и тд. одинаковое, как на скриншоте. В других секторах вообще нули наглухо.
Когда же читаю атрибуты карты, то они вполне нормальные.
Type: 0
Version: 0
Class: 1525
Rel Address: 45928
Blocks Number: 996352
Block Size: 512
L Blocks Number: 996352
L Block Size: 512
Вообще жесть какая-то. Взял этот ваш проект. Скомпилил, прошил, запустил. Ну, так как у вас нет поддержки USB, то устройтво отобразилось как неизвестное, но и не важно. Ваша программа записала ведь инкрементально нулевой блок. Я потом опять заливаю туда свою программу с поддержкой VCP, дабы у себя считать, чтоже там в и тоге вышло. И вижу, что нулевой блок заполнился инкрементально, как и положено. Как это понять вообще? То есть нулевой блок стал читаться достоверно, только после того, в него была осуществлена запись из другой программы вообще. Что-то это вообще за гранью моего понимания.
Скриншот к какому варианту относится? Пока по-прежнему выглядит так, что проблема не в карте. В моем проекте если другие тестовые байты поставить, а затем считать под отладчиком, то что будет в readBuffer?
Я нашёл у себя один косяк. На счёт типа переменной номера блока карты. Он был всего лишь 8-ми битным. Но, в пределах 8-ми бит 0-й блок таким ведь по номеру и будет. Получается, что у WinHEX номерация блоков идёт как-то по другому, и когда я там считывал по его мнению 0-й блок, то по факту это был не 0-й блок.
Да, похоже на то.
Да, скорее всего так и было. Буду дальше разбираться. Вчера получалась скорость чтения от 10 до 20 мб в секунду в зависимости от карты при чтении по 128 блоков сразу. Есть ли у Вас опыт работы с STM32H750-й серий? На сколько я знаю, там SDIO ощутимо быстрее.
Не, на H7 не пробовал. От карты сильно будет зависеть, а ну в принципе так и вышло. Скорее всего лимитировать будут именно возможности самой карты.
Чтение у меня заработало, но наблюдаю ряд довольно странных вещей. Например беру карту формата SD Kingston объёмом 2 гб. Читаю группу блоков 128 штук и получаю скорость 20 мб в сек. Беру, уже карту явно более быструю SDHC Transcend объёмом 32 гб класс 10, и получаю скорость чтения всего 10 мб в сек. Но, читается и всё вроде бы нормально. Странно оно как-то. Беру, карту SDXC Transcend объёмом 64 гб класс тоже 10, получаю так же 10 мб в сек. Беру, карту явно ещё более быструю SDXC I Sandisk Ultra объёмом 64 гб класс тоже 10, получаю вообще 7 мб в сек. При чём на ней где-то с 50000 блока они перестают вообще читаться нормально. То есть сама функция HAL_SD_ReadBlocks возвращает код ошибки 1. Нифига не понимаю. Чем круче карта, тем скорость ниже, а на Ultra и вовсе ошибки чтения. hsd.Init.ClockDiv = 0; Но. не спасает, если поставить например hsd.Init.ClockDiv = 4; Просто тупо падает скорость чтения на всех картах, как и положено, а на Ultra продолжают быть всё те же ошибки чтения. То ли на ней как-то по иному сделана адресация блоков, что вообще как-то странно, то ли что? Не пойму. Беру Sandisk SDHC 8 Гб - около 10 мб в сек. Чуть не дотягивает. Беру Dahua SDXC 128 Гб с ячейками MLC, получаю опять чуть больше 10 мб в сек. Блоки все (в меру опробований - я просто сделал сделал скролер в программе для перемещения по блокам) читаются на нём без ошибок. При чём Dahua могут писать аж 60 мбайт в сек. Ну, тут понятно, что на данном кристалле не сделать. Самым быстрым оказался SD Kingston объёмом 2 гб. Туши свет. Или я что-то не понимаю. И почему-то на SDXC I Sandisk Ultra с определённого номера блоков прут ошибки чтения. Вот это вообще нифига не понятно.
Добавлю. При hsd.Init.ClockDiv = 0. CLK идёт на частоте 24 мгц. Проверил осциллографом. То есть по идее, если там передача полубайта по фронут и спаду, то значит 24 мбайт в сек должно быть. Ну есть ещё издержки в коде функции HAL_SD_ReadBlocks (там понаворочено много кода), но в итоге 20 мабат в сек на самой простой карте. При такой частоте это ещё можно как-то понять, но 10 мбайт в сек и ниже на более быстрых картах, это я не могу понять. А уж ошибки на ультре, это вообще что-то ненормальное.
На Ultra оказывается ошибка чтения блоков начинается а 65536 го блока. До него всё читается нормально. А потом ошикба 1, но бывают что и посл некоторые блоки читаются без ошибки. Карта рабочая. Снимаю на неё.
Как под отладчиком считать не знаю, к сожалению. Всё проверуочную информацию я обычно отсылаю или по UART, или VCP, и пишу для этого приложение для ПК и смотрю что там.
Добрый день, удалось скорость еще поднять?
Здравствуйте. Нет, не удалось. На ряде карт наблюдаются странности. При чём есть и такой эффект, что если я читаю допустим группу блоков, и потом её обратно записываю, то если я с указанными блолками делаю первый раз, то скорость записи какая-то очень уж низкая. Типа 300 к в сек. Если это делаю повторно, то скорость уже раз в 10-15 выше. Не очень понятно, с чем это связано. Перехожу на новый набор блоков, там тоже самое. Возвращаюсь например обратно к строму набору блоков, там скорость записи уже высокая. Словно первый раз время ушло на очистку. Но, по второму и т.д. разам ведь так же должно это быть.
Пропустил комментарий... Можно попробовать по функции записи пройтись по шагам, чтобы выяснить, какие конкретные операции/команды разное время выполняются.
Добрый день. Подскажите пожалуйста какую board с STM32F10x можно взять для повторения примера из данной статьи? Cпасибо за ответ.
Добрый день, как вариант - такую - ссылка
Но по большому счету - любую с разъемом под SD-карту и USB.