USBВ MSD реализация

У нас на плате контроллер STM32F205VG и память AT45DB321E . Эта плата подключается к ПК и прикидывается флэшкой.

Чтобы понять как работает USBD девайс на STM32 , который эмулирует USB флэш память, придется :

1. Воспользоваться какой-нибудь программой мониторинга USB портов на ПК ( у нас под Windows).

2. Поставить printf (SWO) на всех значимых USB функциях (а еще лучше на всех функциях) типа USBD_LL_Transmit, USBD_Ctl.... SWO похоже все это без труда потянет (без зависаний).

3. Не будем разделять работу с флэш памятью и USB MSD на разные потоки (НЕ используем FreeRTOS), т.к. все-равно HAL реализация USBD MSD девайса не предполагает использование потоковой модели, т.к. USBD работает из прерывания.

Прерывания USB, коллбек функции

Откуда вызываются коллбеки

Коллбеки - это функции , которые нам надо заполнить своим кодом работы с флэш памятью AT45.

STORAGE_Read_FS

Источник,откуда вызывается callback функция - это прерывание от USB канала. Итак все тайное становится явным.

OTG_FS_IRQHandler  [stm32f2xx_it.c]  первый обработчик прерывания

    HAL_PCD_IRQHandler   //  идет разбор флагов прерывания из USB канала. Далее по коллбекам обрабатываем их раздельно.

      HAL_PCD_DataInStageCallback

        USBD_LL_DataInStage

            USBD_MSC_DataIn (см. указатель на функцию с структуре USBD_MSC)

               MSC_BOT_DataIn   (MSC_BOT_DataOut|MSC_BOT_CBW_Decode) 

                  SCSI_ProcessCmd 

                    SCSI_Read10 

                       SCSI_ProcessRead 

                        STORAGE_Read_FS = (pdev->pUserData->Read) ----------------- здесь наши коллбеки

                           SCSI_ProcessRead  (MSC_BOT_SendData  или MSC_BOT_SendCSW или USBD_CtlSendData или USBD_CtlContinueSendData или USBD_CtlSendStatus)

                               USBD_LL_Transmit

                                  HAL_PCD_EP_Transmit 

                                      USB_EPStartXfer 

Коллбек функция STORAGE_Read_FS вызывается из прерывания OTG_FS_IRQHandler .

Нам надо успеть прочитать(записать) из микросхемы памяти AT45 512байт и поскольку делается это естественно в прерывании , то возникает много вопросов.

По любому из прерывания надо выходить как можно быстрее.

Но если STORAGE_Read_FS завершается с любым результатом >=0 , то сразу идет отсылка данных хосту. А мы их еще не прочитали (надо примерно 3мс). А если надо записать, или еще круче стереть. Что все ждем в прерывании?

И как ни странно - ответ - ДА!

Что будет , если в коллбеке STORAGE_Read_FS сделать задержку более 1мс, а точнее 0.960652050 .. 1.465651240 = 0.52 секунды!

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

Главное во всех функциях вызываемых из STORAGE_Read_FS, надо убрать _IT функции , т.е. не использовать функции основанные на прерываниях.

Также НЕ следует выделять работу с микросхемой флэш памяти в отдельный поток , т.к. переключится на этот поток не получится из прерывания (под FreeRTOS).


0.960652050,DATA0,,,,0x55 0x53 0x42 0x43 0xA0 0x19 0x43 0xF6 0x00 0x02 0x00 0x00 0x80 0x00 0x0A 0x28 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00,0x1D03
0.960675990,ACK,,,,,
.....
1.465651240,DATA0,,,,0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF,0x40FE

Почему так работает? Потому,что контроллер продолжает во время (пока мы сидим STORAGE_Read_FS) нормально отвечать NAK-ом (типа : я занят):

0.960517930,IN,0x23,0x01,,,0x0D
0.960521220,NAK,,,,,
....

То есть ответ NAK,,,,, реализуется на аппаратном уровне,как-то еще до контроллера прерываний.

файлы, файлы, файлы

Выбираем спокойный день и ковыряемся в исходниках кода , сгенерированного Cube Mx для STM32F205VG USBD MSD. В каждой функции добавляем printf ().

Все , что нам надо здесь :

Весь код открытый , нам надо просто разобраться в этой каше.

USB Core

usbd_ioreq.c
usbd_core.c
usbd_ctlreq.c

MSC - Mass Storage Controller

usbd_msc_bot.c
usbd_msc.c
usbd_msc_data.c
usbd_msc_scsi.c

Конфигурация и Callbacks функции

usb_device.c
usbd_conf.c
usbd_desc.c
usbd_storage_if.c

еще это

stm32f2xx_hal_pcd.c
stm32f2xx_ll_usb.c


Ищем и читаем даташиты с https://www.usb.org/documents?search=mass&items_per_page=50

И вот теперь становится кое-то явным , что от нас скрыто


А вот тоже маленький , но уже в текстовом представлении из того же LA1010.


0.825396840,OUT,0x20,0x01,,,0x05
0.825400110,DATA0,,,,0x55 0x53 0x42 0x43 0x10 0xDA 0xDA 0x04 0x00 0x02 0x00 0x00 0x80 0x00 0x0A 0x28 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00,0xB1B7
0.825424030,ACK,,,,,
0.825440180,IN,0x20,0x01,,,0x05

... тут повторы IN / NAK

0.829039290,IN,0x20,0x01,,,0x05
0.829042550,DATA0,,,,0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF,0x40FE
0.829095670,ACK,,,,,
         все данные переданы
0.829098080,IN,0x20,0x01,,,0x05
0.829101390,NAK,,,,,

... опять повторы IN / NAK

0.830468990,IN,0x20,0x01,,,0x05
0.830472290,NAK,,,,,
0.830474730,IN,0x20,0x01,,,0x05
0.830478030,NAK,,,,,
0.830500410,IN,0x20,0x01,,,0x05
0.830503710,NAK,,,,,

... и далее опять повторы IN / NAK

Получается так :

OUT,0x20,0x01,,,0x05 хост посылает команду
IN,0x20,0x01,,,0x05 - это запрос хоста - ну , что готовы дынные?
NAK - (и их несколько) это ответ девайса, что он принял , но не данные у него еще не готовы
SOF - (иногда) это команда от хоста (просто каждую 1 милисекунду посылается по протоколу)
DATA1 это девайс передает наконец-то данные на очередной запрос IN,0x20,0x01,,,0x05 (FF FF FF ... это флэшка отдает содержание первой своей страницы) это STORAGE_Read_FS
ACK это девайс рапортует, что данные передал

И дальше опять
IN,0x20,0x01,,,0x05
NAK
............ много раз

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

Периоды следования запросов IN,0x20,0x01,,,0x05 - намного меньше 1 милисекунды.

Выводы

Из выше проверенного понятно пока одно : исходники из CubeMX с HAL рабочие , т.е. флэшку мы реализовали.

Она форматируется нормально и туда даже можно записать сразу файл.

Но если все делать в потоках (по взрослому) , то надо все исходники переписывать и работать на уровне регистров USB.


Ниже выкладываю как обычно выстраданный проект на Atollic True Studio.

Файлы для скачивания

* Mass Storage Class datasheet [pdf]

* STM32F205VGT6_MSD_AT45 Atollic True Studio [zip]
с SWO трассировкой, контроллер STM32 работает как флэшка , которую подключают к ПК