Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: помогите понять usb enumeration
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
PriBoris
3 дня бьюсь, не могу ничего понять
физику usb я знаю, раньше успешно сделал функцию на tms5509a
но не могу понять что от меня хочет lpc2368
(среда crossworks)

почему-то весь процесс затыкается после установки адреса (SET_ADDRESS)

(1) я принял SETUP c get descriptor (DEVICE_DESCRIPTOR)
(2) отослал IN c 18 байтами дескриптора
(3) получил пустой OUT
(4) принял SETUP с set address
(5) отослал пустой IN
(6) в прерывании по успешной высылке IN устанавливаю адрес
UsbWriteCommandByte(USB_SIE_COMMAND_SetAddress,UsbDeviceAddress+(1<<7));
и на этом все заканчивается
точнее при некоторых условиях через десяток секунд ПК опять спрашивает DEVICE_DESCRIPTOR, но процесс дальше не идет

куда копать не понимаю
событие reset не приходит
по идее у меня какая-то чехарда или недоделка в stall/unstall, но перепробовав кучу вариантов не смог найти работоспособного

посмотрите пожалуйста код
может что-то не так делаю

ниже конспективно код с комментариями
пока обрабочиков прерываний нет, только поллинг

В StartUsb инициализация :
Код
void StartUsb(void)
{
  rUSBClkCtrl=(1<<1)|(1<<4);
  while(1) if ((rUSBClkSt)==((1<<1)|(1<<4))) break;

  rUSBReEp = 0;

  rUSBReEp |= (1<<0); // realize EP0
  rUSBEpInd = 0; // control OUT
  rUSBMaxPSize = 64;
  while(1) if (((rUSBDevIntSt)&(1<<8))==(1<<8)) break;
  rUSBDevIntClr = (1<<8); // EP_RLZED

  rUSBReEp |= (1<<1); // realize EP1
  rUSBEpInd = 1; // control IN
  rUSBMaxPSize = 64;
  while(1) if (((rUSBDevIntSt)&(1<<8))==(1<<8)) break;
  rUSBDevIntClr = (1<<8); // EP_RLZED

  rUSBEpIntPri = 0x00000000;
  rUSBDevIntEn =  (1<<USBDEVINT_TxENDPKT)|
                  (1<<USBDEVINT_RxENDPKT)|
                  (1<<USBDEVINT_DEV_STAT)|
                  (1<<USBDEVINT_EP_SLOW)|
                  (1<<USBDEVINT_ERR_INT);
  rUSBDevIntClr = 0xFFFFFFFF;
  rUSBEpIntEn = (1<<0)|(1<<1);
  rUSBEpIntClr = 0xFFFFFFFF;

  UsbWriteCommandByte(USB_SIE_COMMAND_SetAddress,0+0x80);
  UsbWriteCommandByte(USB_SIE_COMMAND_SetAddress,0+0x80);

  w0 = UsbReadCommandWord(USB_SIE_COMMAND_ReadTestRegister);
  if (w0 != 0xa50f) while(1);
}


В основном теле опрашиваются события, ошибки, прерывания от точек:
Код
     // check error interrupt
      if ( (UsbDevIntStatus&(1<<USBDEVINT_ERR_INT)) != 0 )
      {
        UsbErrorCode = UsbReadCommandByte( USB_SIE_COMMAND_GetErrorCode );
        UsbErrorStatus = UsbReadCommandByte( USB_SIE_COMMAND_ReadErrorStatus );
        UsbErrorCounter++;
        rUSBDevIntClr = (1<<USBDEVINT_ERR_INT); // ERR_INT
      }
      // check status change event
      if ( (UsbDevIntStatus&(1<<USBDEVINT_DEV_STAT)) != 0 )
      {
        UsbDeviceStatus = UsbReadCommandByte( USB_SIE_COMMAND_GetDeviceStatus );
        if ((UsbDeviceStatus&(1<<3))!=0)
          if ((UsbDeviceStatus&(1<<2))!=0) {/**/};
        if ((UsbDeviceStatus&(1<<4))!=0) { /**/};
        rUSBDevIntClr = (1<<USBDEVINT_DEV_STAT);
      }
      // endpoint interrupt
      if ( (UsbDevIntStatus&(1<<USBDEVINT_EP_SLOW)) != 0 )
      {
        UsbEpIntStatus = rUSBEpIntSt;
        if ( (UsbEpIntStatus&(1<<0))!=0 ) UsbType1ProcessEp0Rx();
        if ( (UsbEpIntStatus&(1<<1))!=0 ) UsbType1ProcessEp0Tx();
        rUSBDevIntClr = (1<<USBDEVINT_EP_SLOW);
      }


Обработчик OUT ep0:
Код
void UsbType1ProcessEp0Rx(void)
{
      UsbCounterEp0Rx++;

      rUSBCtrl = (1<<0)|(0<<2); // RD_EN, LOG_ENDPOINT=0
      UsbRxPacketLength = UsbReadRxPacketLength();
      UsbReadPacket((WORD)UsbRxPacketLength);
      rUSBDevIntClr = (1<<USBDEVINT_RxENDPKT); // RxENDPKT
      rUSBCtrl = 0;
      UsbClearBufferResult = UsbReadCommandByte(USB_SIE_COMMAND_ClearBuffer);
      UsbSelectEndpoint0 = UsbReadCommandByte(USB_SIE_COMMAND_SelectEndpoint0);
      rUSBEpIntClr = (1<<0);

          if ( (UsbSelectEndpoint0&(1<<2)) != 0 ) // setup packet
          {
            UsbCounterEp0Setup++;
            UsbSetupMaxLength = *(WORD*)(&(UsbRxPacket[6]));
            switch(*(WORD*)(&(UsbRxPacket[0])) & 0xFF80)
            {
              //-------------------------------------------------------------
              case 0x0680: // get descriptor
                switch(*(WORD*)(&(UsbRxPacket[2])) & 0xFF00)
                {
                  //-------------------------------------------------------------
                  case USB_DEVICE_DESCRIPTOR_TYPE:
                    UsbCounterEp0Setup1++;
                    rUSBCtrl = (1<<1)|(0<<2);
                    UsbSendPacket(_UsbType1DeviceDescriptor,18);
                    rUSBCtrl = 0;
                    UsbWriteCommand(USB_SIE_COMMAND_SelectEndpoint1);
                    UsbWriteCommand(USB_SIE_COMMAND_ValidateBuffer);
                    rUSBDevIntClr = (1<<USBDEVINT_TxENDPKT);
                    break;
                  //-------------------------------------------------------------
                  case USB_CONFIGURATION_DESCRIPTOR_TYPE:
                    rUSBCtrl = (1<<1)|(0<<2);
                    if (UsbSetupMaxLength>=32)
                      UsbSendPacket(_UsbType1ConfigurationDescriptor,32);
                    else
                      UsbSendPacket(_UsbType1ConfigurationDescriptor,UsbSetupMaxLength);
                    rUSBCtrl = 0;
                    UsbSelectEndpoint1 = UsbReadCommandByte(USB_SIE_COMMAND_SelectEndpoint1);
                    UsbWriteCommand(USB_SIE_COMMAND_ValidateBuffer);
                    rUSBDevIntClr = (1<<USBDEVINT_TxENDPKT);
                    break;
                  //-------------------------------------------------------------
                  case USB_STRING_DESCRIPTOR_TYPE:
                    switch(*(WORD*)(&(UsbRxPacket[2])) & 0x00FF)
                    {
                      case 0:
                        rUSBCtrl = (1<<1)|(0<<2);
                        UsbSendPacket(_UsbLanguage,4);
                        rUSBCtrl = 0;
                        UsbSelectEndpoint1 = UsbReadCommandByte(USB_SIE_COMMAND_SelectEndpoint1);
                        UsbWriteCommand(USB_SIE_COMMAND_ValidateBuffer);
                        rUSBDevIntClr = (1<<USBDEVINT_TxENDPKT);
                        break;
                      case 1:
                        rUSBCtrl = (1<<1)|(0<<2);
                        UsbSendPacket(_UsbStringManufacturer,50);
                        rUSBCtrl = 0;
                        UsbSelectEndpoint1 = UsbReadCommandByte(USB_SIE_COMMAND_SelectEndpoint1);
                        UsbWriteCommand(USB_SIE_COMMAND_ValidateBuffer);
                        rUSBDevIntClr = (1<<USBDEVINT_TxENDPKT);
                        break;
                      case 2:
                        rUSBCtrl = (1<<1)|(0<<2);
                        UsbSendPacket(_UsbStringProduct,50);
                        rUSBCtrl = 0;
                        UsbSelectEndpoint1 = UsbReadCommandByte(USB_SIE_COMMAND_SelectEndpoint1);
                        UsbWriteCommand(USB_SIE_COMMAND_ValidateBuffer);
                        rUSBDevIntClr = (1<<USBDEVINT_TxENDPKT);
                        break;
                      default:
                        UsbStallEp0Rx();
                        UsbStallEp0Tx();
                        break;
                    }
                    break;
                  //-------------------------------------------------------------
                  default:
                    UsbStallEp0Rx();
                    UsbStallEp0Tx();
                    break;
                  //-------------------------------------------------------------
                }
                break;
              //-------------------------------------------------------------
              case 0x0500: // set address
                UsbCounterEp0Setup2++;
//                UsbUnstallEp0Rx();
                UsbUnstallEp0Tx();
                UsbDeviceAddress = 0x7F & (*(WORD*)(&(UsbRxPacket[2])));
                rUSBCtrl = (1<<1)|(0<<2);
                rUSBTxPLen = 0;
                rUSBCtrl = 0;
                rUSBDevIntClr = (1<<USBDEVINT_TxENDPKT);
                UsbWriteCommand(USB_SIE_COMMAND_SelectEndpoint1);
                UsbWriteCommand(USB_SIE_COMMAND_ValidateBuffer);
                break;
              //-------------------------------------------------------------
              case 0x0900: // set configuration
                UsbUnstallEp0Tx();
                rUSBCtrl = (1<<1)|(0<<2);
                rUSBTxPLen = 0;
                rUSBCtrl = 0;
                UsbSelectEndpoint1 = UsbReadCommandByte(USB_SIE_COMMAND_SelectEndpoint1);
                UsbWriteCommand(USB_SIE_COMMAND_ValidateBuffer);
                rUSBDevIntClr = (1<<USBDEVINT_TxENDPKT);
                UsbConfigured = 1;
                break;
              //-------------------------------------------------------------
              default:
                UsbStallEp0Rx();
                UsbStallEp0Tx();
                break;
              //-------------------------------------------------------------
            }          
          }
          else // data OUT packet
          {
            UsbCounterEp0Data++;
          }

}


Обработчик IN ep0:
Код
void UsbType1ProcessEp0Tx(void)
{
          UsbCounterEp0Tx++;
          rUSBEpIntClr = (1<<1);
          if (UsbDeviceAddress!=0)
          {
            UsbWriteCommandByte(USB_SIE_COMMAND_SetAddress,UsbDeviceAddress+(1<<7));
            UsbWriteCommandByte(USB_SIE_COMMAND_SetAddress,UsbDeviceAddress+(1<<7));
            UsbDeviceAddress = 0;
          }
          if (UsbConfigured!=0)
          {
            while(1);
            UsbConfigured=0;
            UsbStatus = USB_STATUS_TYPE1_CONFIGURED;

            UsbWriteCommandByte(USB_SIE_COMMAND_ConfigureDevice,1);
/*... */
          }
}


На всякий случай вспомогательные вещи:
Код
void UsbStallEp0Rx(void){
  UsbWriteCommandByte(USB_SIE_COMMAND_SetEndpointStatusEndpoint0,1);}
void UsbUnstallEp0Rx(void){
  UsbWriteCommandByte(USB_SIE_COMMAND_SetEndpointStatusEndpoint0,0);}
void UsbStallEp0Tx(void){
  UsbWriteCommandByte(USB_SIE_COMMAND_SetEndpointStatusEndpoint1,1);}
void UsbUnstallEp0Tx(void){
  UsbWriteCommandByte(USB_SIE_COMMAND_SetEndpointStatusEndpoint1,0);}


BYTE __attribute__ ((aligned (4))) _UsbType1DeviceDescriptor[18] = {
            0x12,                //;BYTE bLength : 8;
            0x01,                //;BYTE bDescriptorType : 8;
            0x00,0x02,            //;WORD bcdUSB : 16;
            0x00,                //;BYTE bDeviceClass : 8;
            0x00,                //;BYTE bDeviceSubclass : 8;
            0x00,                //;BYTE bDeviceProtocol : 8;
            64,                    //;BYTE bMaxPacketSize : 8;
            0x34,0x12,            //;WORD idVendor : 16;
            0x78,0x56,            //;WORD idProduct : 16;
            0x00,0x00,            //;WORD bcdDevice : 16;
            1,                    //;BYTE iManufacturer : 8;
            2,                    //;BYTE iProduct : 8;
            0,                    //;BYTE iSerialNumber : 8;
            1,                    //;BYTE iNumConfigurations : 8;
};
ar__systems
Лоджиком пробовали посмотреть что на шине происходит, все ли там так как вы описываете на словах?
PriBoris
Цитата(ar__systems @ Oct 14 2009, 04:35) *
Лоджиком пробовали посмотреть что на шине происходит, все ли там так как вы описываете на словах?

Нет лоджика. А снифферы энумерацию не показывают.
PriBoris
Вычитал, что несмотря на то, что нога CONNECT на плате незадействована, устанавливать бит CON все равно надо.
Код
UsbWriteCommandByte(USB_SIE_COMMAND_SetDeviceStatus,1);

Появилось событие ресет после первого запроса get descriptor, точнее после связки SETUP-IN(18байт)-OUT(0байт).
Но все остальная картина с энумерацией осталась такой-же, т.е. затык после установки адреса.
Еще вопрос: Кто-нибудь понимает почему в примерах от keil установка адреса делается двумя командами ?
Код
void USB_SetAddress (DWORD adr) {
  WrCmdDat(CMD_SET_ADDR, DAT_WR_BYTE(DEV_EN | adr)); /* Don't wait for next */
  WrCmdDat(CMD_SET_ADDR, DAT_WR_BYTE(DEV_EN | adr)); /*  Setup Status Phase */
}
Diz
Предположение: при разгребании прерывании от enpdoint-ов всего один раз опрашивается rUSBEpIntSt. Если за время
обработки rx endpoint-а пришли еще прерывания от tx, то они будут похерены при rUSBDevIntClr = (1<<USBDEVINT_EP_SLOW).
Что-то подобное было, подробностей уже не помню.

У меня сделано так:
CODE

// endpoint interrupt
if ( Stat & EP_SLOW )
{
ulong pendIrqs;
while ( (pendIrqs = EP_INT_STAT))
{
do
{
ulong epNum = ntz( pendIrqs); // Get position of setted bit
EP_INT_CLR = 1 << epNum;
usb_wait_irq(CDFULL);
ulong epStat = CMD_DATA; // Reading status is obligatory
if ( epIntHandler[epNum] )
epIntHandler[epNum]( epNum, epStat); // Call handler

} while (pendIrqs &= pendIrqs - 1);
}
DEV_INT_CLR = EP_SLOW;
}
misyachniy
Цитата(PriBoris @ Oct 14 2009, 21:58) *
Вычитал, что несмотря на то, что нога CONNECT на плате незадействована, устанавливать бит CON все равно надо.
Код
UsbWriteCommandByte(USB_SIE_COMMAND_SetDeviceStatus,1);

Появилось событие ресет после первого запроса get descriptor, точнее после связки SETUP-IN(18байт)-OUT(0байт).
Но все остальная картина с энумерацией осталась такой-же, т.е. затык после установки адреса.
Еще вопрос: Кто-нибудь понимает почему в примерах от keil установка адреса делается двумя командами ?
Код
void USB_SetAddress (DWORD adr) {
  WrCmdDat(CMD_SET_ADDR, DAT_WR_BYTE(DEV_EN | adr)); /* Don't wait for next */
  WrCmdDat(CMD_SET_ADDR, DAT_WR_BYTE(DEV_EN | adr)); /*  Setup Status Phase */
}



Раньше вы писали

(1) я принял SETUP c get descriptor (DEVICE_DESCRIPTOR)
(2) отослал IN c 18 байтами дескриптора
(3) получил пустой OUT

Тут
http://interface.centraltreasure.com/files...utshell_pdf.pdf

На 30 странице

A common Windows enumeration involves the following steps,
1. The host or hub detects the connection of a new device via the device's pull up resistors on the
data pair. The host waits for at least 100ms allowing for the plug to be inserted fully and for power
to stabilise on the device.
2. Host issues a reset placing the device is the default state. The device may now respond to the
default address zero.
3. The MS Windows host asks for the first 64 bytes of the Device Descriptor.
4. After receiving the first 8 bytes of the Device Descriptor, it immediately issues another bus reset.
5. The host now issues a Set Address command, placing the device in the addressed state.
6. The host asks for the entire 18 bytes of the Device Descriptor.
7. It then asks for 9 bytes of the Configuration Descriptor to determine the overall size.
8. The host asks for 255 bytes of the Configuration Descriptor.
9. Host asks for any String Descriptors if they were specified.


То есть вам нужно при разборе событий от USB, в первую очередь проверять наличие USB Bus Reset.
Если событие произошло - отключать передачу дескриптора по IN(я так понимаю дескриптор в одну посылку не влазит)

Кейла у меня нет, но подозреваю, что требуется задержка после смены адреса.
vanner
Показывают снифферы энумерацию, например, USBlyzer, USBTrace.
PriBoris
Цитата(Diz @ Oct 14 2009, 23:27) *
Предположение: при разгребании прерывании от enpdoint-ов всего один раз опрашивается rUSBEpIntSt. Если за время
обработки rx endpoint-а пришли еще прерывания от tx, то они будут похерены при rUSBDevIntClr = (1<<USBDEVINT_EP_SLOW).
Что-то подобное было, подробностей уже не помню.

Спасибо, тоже сейчас аккуратно сделал что-то подбное, но не помогло. Проблема не в этом.
Цитата(misyachniy)
То есть вам нужно при разборе событий от USB, в первую очередь проверять наличие USB Bus Reset. Если событие произошло - отключать передачу дескриптора по IN(я так понимаю дескриптор в одну посылку не влазит)

Я всякие разные примеры перелопачивал, но ничего подобного не встречал никогда. Судя по описанию, даже если такая реакция и необходима, то контроллер сам это сделает автоматически.
On a bus reset, the device will automatically go to the default state. In the default state:
• Device is unconfigured.
• Will respond to address 0.
• Control endpoint will be in the Stalled state.
• All endpoints are unrealized except control endpoints EP0 and EP1.
• Data toggling is reset for all endpoints.
• All buffers are cleared.
• There is no change to the endpoint interrupt status.
• DEV_STAT interrupt is generated.

Еще раз еще более вдумчиво перечитал описание, вывел отладочную информацию на uart.
Нашел кучу мелких недочетов, в основном касающихся чтения и записи пакетов нулевой длины.
После анализа логов главная причина неудачной энумерации совершенно четко была локализована - при чтении дескрипторов контроллер не выдавал ACK (точнее выдавал его только в первый раз) на DATA нулевой длины (из-за занятости приемного буфера).

Было:
void UsbType1ProcessEp0Rx(void)
{
...
UsbClearBufferResult = UsbReadCommandByte(USB_SIE_COMMAND_ClearBuffer);
UsbSelectEndpoint0 = UsbReadCommandByte(USB_SIE_COMMAND_SelectEndpoint0);
Надо:
void UsbType1ProcessEp0Rx(void)
{
...
UsbWriteCommand(USB_SIE_COMMAND_SelectEndpoint0);
UsbWriteCommand(USB_SIE_COMMAND_ClearBuffer);

Сейчас энумерация заработала. Большое спасибо всем откликнувшимся!
ar__systems
Цитата(PriBoris @ Oct 13 2009, 23:13) *
Нет лоджика. А снифферы энумерацию не показывают.


Хорошо что все заработало, но лоджиком обзаведитесь smile.gif USB LA стоит 500 баксов - самые полезные 500 баксов которые я когда либо потратил. USB он правда не понимает, но можно лог экспортировать, а декодер USB я сам написал за несколько часов.

http://tech-tools.com/dv_main.htm
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.