скрытое меню

Когда срабатывает прерывание от USB

Как не хочется снисходить с уровня программирования HAL до уровня регистров, но все-таки это когда-то надо сделать.

Несмотря на то , что все проекты в первой попытке работают : USB-HID, USB-VCOM, USB-MSD, USB-RNDIS...

Предлагаю посмотреть как выпорлняется программы на примере HAL STM32 VCOM .

контроллер OTG_FS

В STM32 есть контроллер OTG_FS (Full Speed) . OTG_HS (High Speed) - это отдельный контроллер, мы его рассматривать не будем.

Почитал datasheet и немного подвис :

Оказывается USB DP и DM - это не просто выводы наружу с прерыванием по приходу одного байта.

Внутри OTG_FS контроллера есть RAM (1.25 килобайт), есть EP0, 3 конечные точки IN , 3 конечные точки OUT.

Есть несколько FIFO буферов, которые могут хранить несколько пакетов.

Гарантируется максимальная полоса пропускания USB до 1 фрейма (1 мс) без вмешательства системы. То есть контроллер OTG_FS довольно таки самостоятельное устройство...

HNP - Host Negotiation Protocol
SRP - Session Request Protocol

Ближе к делу

Контроллер записывает 32-битные слова в OTG_FS push-регистры Tx-FIFO.
Соответственно принимает из pop регистров Rx-FIFO.

Tx-FIFO имеется для каждой конечной точки IN.
Rx-FIFO имеется для каждой конечной точки IN и OUT.

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

HAL_PCD_IRQHandler
  HAL_PCD_SetupStageCallback
    USBD_LL_SetupStage
      USBD_StdDevReq
        USBD_GetDescriptor // запрос главного дескриптора 
          USBD_FS_DeviceDescriptor
        USBD_CtlSendData
          USBD_LL_Transmit
            HAL_PCD_EP_Transmit
              USB_EP0StartXfer | USB_EPStartXfer
                  (DIEPTSIZ  , XFRSIZ , PKTCNT)

      USBD_StdItfReq 
        USBD_CtlSendStatus  // запрос статуса
          USBD_LL_Transmit
            HAL_PCD_EP_Transmit
               USB_EP0StartXfer | USB_EPStartXfer
                    (DIEPTSIZ  , XFRSIZ , PKTCNT)

  HAL_PCD_DataOutStageCallback 
    USBD_LL_DataOutStage
      USBD_CtlContinueRx
        CDC_Receive_FS // это вариант прихода байт данных
          USB_EPStartXfer

В USB_EPStartXfer и USB_EP0StartXfer мы как-бы переходим с ожиданию следующего пакета.

Что вызывает прерывание при приеме байтов

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

Какие это регистры в первую очередь?

Информацию по регистрам прочитал с http://microsin.net/programming/arm-working-with-usb/stm32-usb-otg-full-speed.html .

Регистр прерываний GINTSTS

Этот регистр первый в обработчике прерывания USBD_OTG_ISR_Handler.

Это регистр прерываний приложения для событий системного уровня.

WKUPINT (бит 31): Resume/remote WaKeUP detected INTerrupt. Это прерывание выставляется, когда на USB было определено возобновление (resume).

SRQINT (бит 30): Session ReQuest/new session detected INTerrupt. Это прерывание выставляется, когда уровень VBUS в допустимом диапазоне для B-устройства.

CIDSCHG (бит 28): Connector ID Status CHanGe. Ядро установит этот бит, когда поменялось состояние сигнала идентификации коннектора (connector ID).

IPXFR (бит 21): Incomplete Periodic transFeR.

В режиме устройства INCOMPISOOUT: INCOMPlete ISOchronous OUT transfer. В режиме устройства ядро установит это прерывание, чтобы показать как минимум одну изохронную конечную точку OUT, на которой не завершена транзакция в текущем фрейме. Это прерывание установится вместе с битом прерывания окончания периодического фрейма (бит EOPF, который находится в этом регистре).

IISOIXFR (бит 20): Incomplete ISOchronous IN transFeR. Ядро установит это прерывание, чтобы показать как минимум одну изохронную конечную точку IN, транзакция которой не завершена в текущем фрейме. Это прерывание установится вместе с битом прерывания окончания периодического фрейма (бит EOPF, который находится в этом регистре).

OEPINT (бит 19): OUT EndPoint INTerrupt. Ядро установит этот бит, чтобы показать ожидающее прерывания на одной из конечных точек OUT (режим устройства). Приложение должно прочитать регистр OTG_FS_DAINT, чтобы определить точный номер конечной точки OUT, на которой произошло прерывание, и затем прочитать соответствующий регистр OTG_FS_DOEPINTx для точного определения причины прерывания. Для очистки этого бита приложение должно очистить соответствующий бит статуса регистра OTG_FS_DOEPINTx.

IEPINT (бит 18): IN EndPoint INTerrupt. Ядро установит этот бит, чтобы показать ожидающее обработки прерывание на одной из конечных точек IN (в режиме устройства). Приложение должно прочитать регистр OTG_FS_DAINT, чтобы точно определить номер конечной точки, для которой произошло прерывание, и затем прочитать соответствующий регистр OTG_FS_DIEPINTx, чтобы точно определить причину прерывания. Чтобы очистить этот бит, приложение должно очистить соответствующий бит статуса регистра OTG_FS_DIEPINTx.

EOPF (бит 15): End Of Periodic Frame interrupt. Показывает, что в текущем фрейме достигнут период, указанный в поле интервала периодического фрейма (бит PFIVL) регистра OTG_FS_DCFG.

ISOODRP (бит 14): ISOchronous OUT packet DRoPped interrupt. Ядро установит этот бит, когда не смогло записать изохронный пакет OUT packet в RxFIFO, потому что в RxFIFO не было достаточно места для размещения пакета максимального размера конечной точки isochronous OUT.

ENUMDNE (бит 13): ENUMeration DoNE. Ядро установит этот бит, чтобы показать завершение быстрой энумерации (speed enumeration). Приложение должно прочитать регистр OTG_FS_DSTS, чтобы получить скорость энумерации.

USBRST (бит 12): USB ReSeT. Ядро установит этот бит, когда определит сброс по шине USB.

USBSUSP (бит 11): USB SUSPend. Ядро установит этот бит, чтобы показать детектирование приостановки (suspend) на USB. Ядро входит в состояние Suspended, когда нет активности на линиях данных в течение 3 мс.

ESUSP (бит 10): Early SUSPend. Ядро установит этот бит, чтобы показать детектирование состояния ожидания (Idle state) на USB для 3 мс.

GONAKEFF (бит 7): Global OUT NAK EFFective. Показывает, что бит Set global OUT NAK (бит SGONAK) в регистре OTG_FS_DCTL, установленный приложением, вступил в действие для ядра. Бит GONAKEFF может быть очищен запись бита Clear global OUT NAK (бит CGONAK) в регистре OTG_FS_DCTL.

GINAKEFF (бит 6): Global IN non-periodic NAK EFFective. Показывает, что бит Set global non-periodic IN NAK (бит SGINAK) в регистре OTG_FS_DCTL, установленный приложением, вступил в действие для ядра. Таким образом, ядро произвело выборку бита Global IN NAK, установленного приложением. Бит GINAKEFF можно очистить путем очистки бита (бит CGINAK) в регистре OTG_FS_DCTL.

Это прерывание необязательно означает, что через USB было отправлено отрицательное подтверждение (NAK handshake).
Бит STALL имеет приоритет над битом NAK.

RXFLVL (бит 4): RxFIFO non-empty. Показывает, что в RxFIFO есть как минимум один пакет, ожидающий чтения.

SOF (бит 3): Start Of Frame. В режиме устройства ядро установит этот бит, чтобы показать прием через USB токена SOF.

OTGINT (бит 2): OTG INTerrupt. Ядро установит этот бит, чтобы показать событие протокола OTG. Приложение должно прочитать регистр OTG Interrupt Status (OTG_FS_GOTGINT), чтобы точно определить событие, вызвавшее это прерывание. Чтобы очистить этот бит, приложение должно очистить соответствующий бит статуса в регистре OTG_FS_GOTGINT.

MMIS (бит 1): Mode MISmatch interrupt. Ядро установит этот бит, когда приложение пытается получить доступ к регистру, к которому в этом режиме доступ невозможен. Например, ядро находится в режиме устройства USB, и при этом обращается к регистру режима хоста. Доступ к регистру завершается на AHB ответом OKAY, однако внутренне игнорируется ядром, и не оказывает влияние на работу ядра.

CMOD (бит 0): Current MODe of operation. Показывает текущий режим работы ядра.
0: режим устройства USB.
1: режим хоста USB.

И вот тут уже приходит понимание , что большую часть рутины делает контроллер OTG_FS. Например NAK посылает он сам (OTG_FS), а в главную программу выдает только прерывание. И это не наша забота слать NAK в USB много раз, пока мы заняты.

На начальном этапе мы анализируем OEPINT , IEPINT , SOF , RXFLVL , ENUMDNE , MMIS, USBRST.

Регистр OTG_FS_DIEPINTx

Регистр OTG_FS_DIEPTSIZ

Приложение должно изменять этот регистр перед разрешением конечной точки 0. Как только конечная точка 0 разрешена соответствующим битом в регистре управления конечной точкой 0 (бит EPENA в регистре OTG_FS_DIEPCTL0), этот регистр модифицирует ядро. Приложение может только читать этот регистр, как только ядро очистило бит разрешения конечной точки.

Например у нас мы меняем в USB_EP0StartXfer.

Смещение адреса: 0x910

PKTCNT (биты 20:19): PacKeT CouNT. Показывает общее количество пакетов USB, составляющих размер транзакции (Transfer Size) данных для конечной точки 0. Это поле декрементируется каждый раз, когда пакет (максимального размера или короче) прочитан из TxFIFO.

Биты 18:7 зарезервированы и должны сохраняться в состоянии сброса (все нули).

XFRSIZ (биты 6:0): Transfer size. Показывает размер транзакции в байтах для конечной точки 0. Ядро генерирует прерывание только после того, как оно исчерпало объем передаваемых данных. Размер транзакции может быть установлен на максимальный размер пакета конечной точки, чтобы прерываться по окончанию каждого пакета. Ядро декрементирует это поле каждый раз, когда пакет перемещается из внешней памяти в TxFIFO.

Регистр GRXSTSP

FRMNUM (24:21): FRaMe NUMber. Это младшие значащие 4 бита номера фрейма USB, в котором был принят пакет. Это поле поддерживается только когда поддерживаются изохронные конечные точки OUT.

PKTSTS (биты 20:17): PacKeT STatuS. Показывает состояние принятого пакета.

0001: Global OUT NAK (срабатывает прерывание).
0010: принят пакет данных OUT.
0011: завершена транзакция OUT (срабатывает прерывание).
0100: завершена транзакция SETUP (срабатывает прерывание).
0110: принят пакет данных SETUP.
Другие значения: зарезервировано.

DPID (биты 16:15): Data PID. Показывает Data PID принятого пакета данных OUT.

00: DATA0
10: DATA1
01: DATA2
11: MDATA

BCNT (биты 14:4): Byte CouNT. Показывает количество байт данных принятого пакета.

EPNUM (биты 3:0): EndPoint NUMber (биты 3:0): CHannel NUMber. Показывает номер конечной точки, которой принадлежит текущий принятый пакет.