Top.Mail.Ru

STM32 и USB. Реализация USB Mass Storage Device.

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

Собственно, давайте перейдем в STMCubeMx и начнем все поэтапно настраивать. Первым делом задействуем внешний генератор тактовых импульсов:

HSE

Следующий шаг - активация интерфейса SDIO в 4-х битном режиме (именно так подключена карта памяти на моей плате). Заодно зададим предделитель для SDIO:

Настройка SDIO в STM32CubeMx

Предварительный этап конфигурации финишируем настройкой USB. Снова сначала включаем, затем выбираем нужный режим:

Включение USB
Выбор режима работы USB в STM32CubeMx

При включении какого-либо периферийного модуля 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" - это величина таймаута для операций чтения/записи.

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

Подписаться
Уведомить о
guest

132 комментариев
Старые
Новые
Межтекстовые Отзывы
Посмотреть все комментарии
Elis
Elis
9 лет назад

И еще раз спасибо)

Alexey
Alexey
9 лет назад

Здравствуйте! На какой скорости осуществляется чтение/ запись? Full speed usb? И как всегда спасибо за статю!

Паша
Паша
9 лет назад

Подумал сделать кардридер на stm32

Gleb
Gleb
9 лет назад

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

Gleb
Gleb
9 лет назад

Доброе утро! Да карта работает. 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);

}

Gleb
Gleb
9 лет назад

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

Gleb
Gleb
9 лет назад

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

Gleb
Gleb
9 лет назад

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

RiseOfDeath
RiseOfDeath
9 лет назад

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

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

Сергей
Сергей
9 лет назад

В новом 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 в одном контроллере.

Роман
Роман
9 лет назад

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

Marat
Marat
8 лет назад

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

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

Marat
Marat
8 лет назад

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

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

Marat
Marat
8 лет назад

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

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

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

Marat
Marat
Ответ на комментарий  Aveal
8 лет назад

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

Marat
Marat
8 лет назад

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

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

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

Roman
Roman
8 лет назад

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

Roman
Roman
8 лет назад

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

Хлуденьков
Хлуденьков
8 лет назад

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

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

Хлуденьков
Хлуденьков
8 лет назад

Добавить в кубе поддержку FatFS? Или ещё что-то?

Хлуденьков
Хлуденьков
8 лет назад

Да, спасибо. Нашёл у вас на сайте статью про фат.

Александр
Александр
8 лет назад

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

Aleksey
Aleksey
8 лет назад

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

Сергей
Сергей
8 лет назад

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

Сергей
Сергей
Ответ на комментарий  Aveal
8 лет назад

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

Сергей
Сергей
Ответ на комментарий  Aveal
8 лет назад

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

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();

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

Bur
Bur
8 лет назад

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

Aleksey
Aleksey
Ответ на комментарий  Bur
8 лет назад

Bur, вы что-то нибудь придумали по данной проблеме?

Aleksey
Aleksey
8 лет назад

Уважаемый Aveal.

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

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

Aleksey
Aleksey
8 лет назад

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

Дмитрий
Дмитрий
8 лет назад

Снова здравствуйте, уважаемый 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 нету. Подскажите
Заранее спасибо за ответ!

Дмитрий
Дмитрий
Ответ на комментарий  Aveal
8 лет назад

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

Дмитрий
Дмитрий
Ответ на комментарий  Aveal
8 лет назад

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

Дмитрий
Дмитрий
8 лет назад

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

Илья
8 лет назад

Очень бы хотелось подобный пример, только для работы с SDRAM.

Илья
8 лет назад

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

Дмитрий
Дмитрий
8 лет назад

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

RusikOk
RusikOk
8 лет назад

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

Marat
Marat
8 лет назад

А почему адрес высчитывается следующим образом - blk_addr * BLOCK_SIZE?

teager
teager
8 лет назад

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 🙂

Nik
Nik
7 лет назад

Где взять проект?

peano
peano
6 лет назад

У меня глючила запись пока не добавил проверку состояния карты после записи:
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 кБ/с.

peano
peano
Ответ на комментарий  peano
6 лет назад

Небольшое уточнение. Указанные скорости получились в 1-битном режиме. В 4-битном несколько быстрее:
Запись 175 кБ/с.
Чтение 408 кБ/с.

tim
tim
Ответ на комментарий  peano
3 лет назад

Спасибо, без этого ожидания do {} while плохо работало. Пришлось вставлять и в STORAGE_Read_FS(), иначе с флешки читался какой-то мусор.

Lim
Lim
6 лет назад

Здравствуйте! Я правильно понимаю, после того как прописаны эти функции, и USB подключен к компьютеру, он сам уже определяет устройство? А главная функция кроме инициализации ничего не делает? Объясните пожалуйста хоть примерно как это работает? Генерируется какое-то прерывание, вызывающее функции из usbd_storage_if.c ?

Lim
Lim
Ответ на комментарий  Aveal
6 лет назад

А не могли бы Вы выложить код главной функции?
Делаю все так как у вас, только у меня не существует структуры 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)
{
}
}

Lim
Lim
Ответ на комментарий  Aveal
6 лет назад

Именно, ничего не происходит.
Смотрю пример от 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();?

Lim
Lim
Ответ на комментарий  Aveal
6 лет назад

Судя по поведению платы функции из 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

Igor
Igor
5 лет назад

Мучался пару часов все делал как описано в посте, но не чего не получалось переменые
*block_num = SdCard.BlockNbr;
*block_size = SdCard.BlockSize;
не получали значения пока в main не добавил инициализацию SD
HAL_SD_Init(&hsd);
после этого все отлично заработало.

Igor
Igor
5 лет назад

Забыл написать автору спасибо реально помогло).

Heinrich
2 лет назад

Здравствуйте.

Есть вот такая плата 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". Но, сгенерировать проект она даёт.
Может подскажите что?

Heinrich
Ответ на комментарий  Aveal
2 лет назад

Проект прислать в виде файла ioc? Или целиком архив исходников для Keil?

Heinrich
Ответ на комментарий  Aveal
2 лет назад

Не очень понятно, как это тут сделать? Как пристыковать архив?

Heinrich
Ответ на комментарий  Aveal
2 лет назад

Отправил на данную почту (Aveal.MicroTechnics@gmail.com) архив проекта и проект CubeMX. Приложение для Win32 не высылал. Думаю, что смысла нет. Или будете проверять на реальной плате?

Heinrich
Ответ на комментарий  Aveal
2 лет назад

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

Heinrich
Ответ на комментарий  Aveal
2 лет назад

Но, моё письмо не дошло.

Microtechnics.jpg
Heinrich
Ответ на комментарий  Aveal
2 лет назад

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

Heinrich
Ответ на комментарий  Aveal
2 лет назад

В архиве нет ни exe ни bin однозначно. Недавно заказчикам отправлял новую сборку своей программы (там exe точно был). mail.ru точно спокойно отправляет. Скорее всего проблема на принимающей стороне.

Heinrich
Ответ на комментарий  Aveal
2 лет назад

Но, ещё более странно то, что отсюда нельзя было отпарвимть просто текст возвратного сообщения. В нём тоже может быть вирус?-)) Высылаю ссылку на архив https://cloud.mail.ru/public/4yXk/AffNYFbBs

Heinrich
Ответ на комментарий  Aveal
2 лет назад

Ну hex то едва может быть вирусом для ПК. По моему это уже паранойя.-)

Heinrich
Ответ на комментарий  Aveal
2 лет назад

Заведите ещё вторую почту на mail.ru. Хуже уж точно не будет.

Heinrich
Ответ на комментарий  Aveal
2 лет назад

В архиве есть ряд лишних файлов. Например sdio_sdcard.h и sdio_sdcard.с. Это я уже искал что-то потом, после неудачной работы с HAL_SD_ReadBlocks. На них там можно не обращать внимания. Можно даже убрать из include

Heinrich
Ответ на комментарий  Aveal
2 лет назад

Не пробовал так делать. Просто читал блоки. Ведь работать так же должно? Должно. И почему без USB? VCP довольно удобная вещь.

Heinrich
Ответ на комментарий  Aveal
2 лет назад

Вставляю карту в ридер. Всё прекрасно читается. Это всё наблюдается на всех имеющихся у меня картах. Но, как USB может мешать работе SDIO? Ведь, если так, то это уже за гранью здравого смысла и тогда сделать устройство с USB не получится.

Heinrich
Ответ на комментарий  Aveal
2 лет назад

Если я просто в программе сгенерую массив как сочту нужным, то он по VCP так и передастся. То есть передача вполне нормально работает. Если, при чём я это сделаю до HAL_SD_ReadBlocks(), то после её вызова она это массив, как и положено, перезапишет, но какой-то просто непонятной фигнёй, где обычно всегда нули. Кроме блоков, номера которых кратны 512-ти. В этих кратных блоках могут быть изредка и нули. В общем, какая-то чушь. Пробовал MicroSD карты на 512 мб, 1 Гб, 2 гб. Подключал даже через сокет и полноформатные карты. Поведение схожее. То ли кривизна библиотеки HAL, то ли плата черт знает как разведена. Но, что удивительно, - атрибуты карты читаются верно. То есть по идее всё с платой хорошо.

Heinrich
Ответ на комментарий  Aveal
2 лет назад

Я пробовал читать их через WinHEX. например там в нулевом блоке есть сигнатура MSDOS и тд. Ничего похожего при чтения через плату там не наблюдается. Записал файл, где 256 блоков по 512 байт каждый и все байты там инкрементируются от 0 до 255.То есть нулевой этого файла блок содержит нули, первый единицы и так далее. И так до FF. нашёл в WinHEX где он начинается. Это 528-й блок по абсолютной адресации. Вставил в плату. Выбрал 528-й,- блок нули. Ну, так и должно быть. Выбрал 529-й. Должны быть 1-цы. А опять нули. И так далее. В 0-м блоке карты так же почти все нули. кроме последних двух байт. Они именно такие, как и должны быть. В нё же есть ещё несколько ненулевых байт, но они уже отсебятные.

Heinrich
Ответ на комментарий  Aveal
2 лет назад

Ну, или после инициализации карты по MX_SDIO_SD_Init и чтению её параметров по HAL_SD_GetCardInfo, до применения HAL_SD_ReadBlocks нужно что-то ещё в карту послать. Может быть какие-то команды. Смотрел осциллографом, такое ощущение, что всё работает (вернее пытается) в однобитном режиме, так как например на линиях DA2 или DAT3 никаких импульсов не заметил. Ну, возможно, что так как там одни нули летят (почти) то и их может и не быть.

Heinrich
Ответ на комментарий  Aveal
2 лет назад

 hsd.Init.ClockDiv = 8; разве уже будет высокой? Но я ставил и в 168 не помогает.

Heinrich
Ответ на комментарий  Aveal
2 лет назад

Пришлите тогда пожалуйста проект, где блоки читаются корректно. У меня это что-то не получается.

Heinrich
Ответ на комментарий  Aveal
2 лет назад

Да, так и было. Делал hsd.Init.ClockBypass = SDIO_CLOCK_BYPASS_DISABLE; или  hsd.Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_DISABLE; - не помогает.

Heinrich
Heinrich
Ответ на комментарий  Aveal
2 лет назад

Сделал вот так же.
 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;
К сожалению, ничего не изменилось. Из нулевого блока карты читается вот это. См. скриншот. Что совсем далеко от действительности.

SDIO_00.jpg
Heinrich
Heinrich
Ответ на комментарий  Aveal
2 лет назад

Да, вот и плохо, что не на чем. Изменения настроек 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

Heinrich
Heinrich
Ответ на комментарий  Aveal
2 лет назад

Вообще жесть какая-то. Взял этот ваш проект. Скомпилил, прошил, запустил. Ну, так как у вас нет поддержки USB, то устройтво отобразилось как неизвестное, но и не важно. Ваша программа записала ведь инкрементально нулевой блок. Я потом опять заливаю туда свою программу с поддержкой VCP, дабы у себя считать, чтоже там в и тоге вышло. И вижу, что нулевой блок заполнился инкрементально, как и положено. Как это понять вообще? То есть нулевой блок стал читаться достоверно, только после того, в него была осуществлена запись из другой программы вообще. Что-то это вообще за гранью моего понимания.

Heinrich
Heinrich
Ответ на комментарий  Aveal
2 лет назад

Я нашёл у себя один косяк. На счёт типа переменной номера блока карты. Он был всего лишь 8-ми битным. Но, в пределах 8-ми бит 0-й блок таким ведь по номеру и будет. Получается, что у WinHEX номерация блоков идёт как-то по другому, и когда я там считывал по его мнению 0-й блок, то по факту это был не 0-й блок.

Heinrich
Heinrich
Ответ на комментарий  Aveal
2 лет назад

Да, скорее всего так и было. Буду дальше разбираться. Вчера получалась скорость чтения от 10 до 20 мб в секунду в зависимости от карты при чтении по 128 блоков сразу. Есть ли у Вас опыт работы с STM32H750-й серий? На сколько я знаю, там SDIO ощутимо быстрее.

Heinrich
Heinrich
Ответ на комментарий  Aveal
2 лет назад

Чтение у меня заработало, но наблюдаю ряд довольно странных вещей. Например беру карту формата 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 с определённого номера блоков прут ошибки чтения. Вот это вообще нифига не понятно.

Heinrich
Heinrich
Ответ на комментарий  Aveal
2 лет назад

Добавлю. При hsd.Init.ClockDiv = 0. CLK идёт на частоте 24 мгц. Проверил осциллографом. То есть по идее, если там передача полубайта по фронут и спаду, то значит 24 мбайт в сек должно быть. Ну есть ещё издержки в коде функции HAL_SD_ReadBlocks (там понаворочено много кода), но в итоге 20 мабат в сек на самой простой карте. При такой частоте это ещё можно как-то понять, но 10 мбайт в сек и ниже на более быстрых картах, это я не могу понять. А уж ошибки на ультре, это вообще что-то ненормальное.

Heinrich
Heinrich
Ответ на комментарий  Aveal
2 лет назад

На Ultra оказывается ошибка чтения блоков начинается а 65536 го блока. До него всё читается нормально. А потом ошикба 1, но бывают что и посл некоторые блоки читаются без ошибки. Карта рабочая. Снимаю на неё.

Heinrich
Heinrich
Ответ на комментарий  Aveal
2 лет назад

Как под отладчиком считать не знаю, к сожалению. Всё проверуочную информацию я обычно отсылаю или по UART, или VCP, и пишу для этого приложение для ПК и смотрю что там.

Heinrich
Heinrich
Ответ на комментарий  Aveal
2 лет назад

Здравствуйте. Нет, не удалось. На ряде карт наблюдаются странности. При чём есть и такой эффект, что если я читаю допустим группу блоков, и потом её обратно записываю, то если я с указанными блолками делаю первый раз, то скорость записи какая-то очень уж низкая. Типа 300 к в сек. Если это делаю повторно, то скорость уже раз в 10-15 выше. Не очень понятно, с чем это связано. Перехожу на новый набор блоков, там тоже самое. Возвращаюсь например обратно к строму набору блоков, там скорость записи уже высокая. Словно первый раз время ушло на очистку. Но, по второму и т.д. разам ведь так же должно это быть.

Miha
Miha
2 лет назад

Добрый день. Подскажите пожалуйста какую board с STM32F10x можно взять для повторения примера из данной статьи? Cпасибо за ответ.

132
0
Оставьте комментарий! Напишите, что думаете по поводу статьи.x