Jump to content

    

USB и sam7s. Нужен простой проект с использованием прерываний

Всем добрый вечер.

На основе проекта usb-device-hid-transfer-project из примеров IAR 5.4 и собственных знаний по USB пишу своё HID-устройство.

В цело половина этапа энумерации уже преодолена (реализована передача общих запросов, осталась передача запроса класса устройства).

Но даже эта половина запросов работает нестабильно.

В процессе энумерации host может 3 раза запросить device descriptor а потом перевести устройство в suspend (хотя я вижу что я отправил этот дескриптор). Или какой-либо другой дескриптор (но чаще именно на device descriptor). Или после запроса device descriptor наступает bus reset.

Также бывает что все запросы (те которые уже реализованы) проходят гладко и у меня даже появляется неопознанное hid-устройство.

Подозреваю что это косяки моей программы (пример из IAR вроде работает хотя и ужасно запутан) - возможно в реализации обработки прерываний от UDP.

 

Интересует ссылка на какой-либо работающий (и не такой запутанный как иаровский) проект (под любой компилятор) с ипользованием USB в sam7s но обязательно с прерываниями.

Хочу сравнить реализацию обработчика прерываний и найти "10 отличий".

 

По-поводу своего проекта - помимо прерываний UDP используется PIT (раз в 1мС), USART0 (115200, в процессе энумерации через него выводятся различные сообщения), PIO (тоже по прерываниям но они практически не задействованы). Startup-код уверяет что возможны вложенные прерывания.

 

Не может-ли большое кол-во источников прерываний и возможность вложенных прерываний влиять на процесс энумерации? Ведь некоторые этапы энумерации ограничены во времени а вложенные прерывания задерживаю работу обработчика прерываний UDP или чтото в этом духе.

 

Вобщем буду благодарен всем кто даст ссылки на проекты или что-либо посоветут по организации работы UDP.

Share this post


Link to post
Share on other sites
Интересует ссылка на какой-либо работающий (и не такой запутанный как иаровский) проект (под любой компилятор) с ипользованием USB в sam7s но обязательно с прерываниями.

Хочу сравнить реализацию обработчика прерываний и найти "10 отличий".

Хорошо, а без прерываний ваш код работает?

 

Не может-ли большое кол-во источников прерываний и возможность вложенных прерываний влиять на процесс энумерации? Ведь некоторые этапы энумерации ограничены во времени а вложенные прерывания задерживаю работу обработчика прерываний UDP или чтото в этом духе.

Ограничения 50-500мс. Не успеть нужно еще постараться.

 

Косяк скорее не в прерывании, а в каком-нибудь косяке типа установки DIR после сброса RXSETUP или TXPKTRDY после сброса TXCOMP.

Share this post


Link to post
Share on other sites
Хорошо, а без прерываний ваш код работает?

Без прерываний не пробовал. Писал сразу с использованием прерываний UDP.

 

 

Косяк скорее не в прерывании, а в каком-нибудь косяке типа установки DIR после сброса RXSETUP или TXPKTRDY после сброса TXCOMP

 

Вот мой обработчик прерываний от Endpoint 0.

DIR у меня устанавливается один раз после приёма SETUP-пакета перед подтверждением прерывания.

TXPKTRDY перед подтверждением TXCOMP.

 

/*******************************************************************/
//обработчик прерывания от EP0
void ep0Handler(void)
{
   unsigned int status = AT91C_BASE_UDP->UDP_CSR[0];

   //PutString0("EP0 handler\r\n");

   //in-трансакция - устройство шлёт данные на хост
   if ((status & AT91C_UDP_TXCOMP) != 0) 
   {
       PutString0("TXCOMP\r\n");

       // EP0 в состоянии передачи?
       if (epsCTRL[0].status == UDP_ENDPOINT_SENDING) 
       {
         // проверяем завершение передачи
         if(epsCTRL[0].tx_flag == 0)
         {
           epsCTRL[0].tx_flag = EP0_WritePayload(); //записываем следующий блок данных с проверкой статуса

           AT91C_BASE_UDP->UDP_CSR[0] |= AT91C_UDP_TXPKTRDY; //устанавливаем флаг начала передачи
           AT91C_BASE_UDP->UDP_CSR[0] &= ~AT91C_UDP_TXCOMP; //подтверждаем прерывание

         }
         else
         {
           //передача завершена
           //переводим конечную точку в исходное состояние
           epsCTRL[0].status = UDP_ENDPOINT_IDLE;
           epsCTRL[0].pData = 0;
           epsCTRL[0].totalLen = 0;
           epsCTRL[0].sendLen = 0; 
           epsCTRL[0].tx_flag = 0; 

           //присваиваем устройству назначенный адрес
           if(setAddr.flag)
           {
             setAddr.flag = 0;
             SetAddress(setAddr.address); //устанавливаем назначенный адрес
           }

           PutString0("TX complet\r\n");
           AT91C_BASE_UDP->UDP_CSR[0] &= ~(AT91C_UDP_TXCOMP); //подтверждаем прерывание

         }
       }
       else 
       {
           // подтверждаем прерывание
           PutString0("Error Wr\r\n");
           AT91C_BASE_UDP->UDP_CSR[0] &= ~(AT91C_UDP_TXCOMP); //подтверждаем прерывание
       }
   }


   // OUT packet received
   if ((status & (AT91C_UDP_RX_DATA_BK0 | AT91C_UDP_RX_DATA_BK1)) != 0) 
   {
     PutString0("UDP_RXDATA\r\n");

     AT91C_BASE_UDP->UDP_CSR[0] &= ~(AT91C_UDP_RX_DATA_BK0|AT91C_UDP_RX_DATA_BK1);
   }

   // STALL sent
   if ((status & AT91C_UDP_STALLSENT) != 0) 
   {
     PutString0("STALLSENT\r\n");
     AT91C_BASE_UDP->UDP_CSR[0] &= ~(AT91C_UDP_STALLSENT);

     // If the endpoint is not halted, clear the STALL condition
     if (epsCTRL[0].status != UDP_ENDPOINT_HALTED) 
     {
       AT91C_BASE_UDP->UDP_CSR[0] &= ~(AT91C_UDP_FORCESTALL);
     }
   }

   // принят setup-пакет 
   if ((status & AT91C_UDP_RXSETUP) != 0) 
   {

     PutString0("RXSETUP:\r\n");

     USBGenericRequest request; //структура хранения текущего запроса

     //считываем setup-пакет
     ep0ReadRequest(&request);

     //определяем направление перед подтверждением приёма setup-пакета
     if (GetDirection(&request) == USBGenericRequest_IN) 
     {
       AT91C_BASE_UDP->UDP_CSR[0] |= AT91C_UDP_DIR;
     }

     //подтверждаем приём setup-пакета
     AT91C_BASE_UDP->UDP_CSR[0] &= ~AT91C_UDP_RXSETUP;

     //передаём принятый пакет на обработку
     ep0RequestHandler(&request);
   }

}
/*************************************************/

 

Сама передача данных начинается таким образом:

 

//------------------------------------------------------------------------------
//пересылка данных через EP0
//передаём указатель на данные и кол-во байт для передачи
char EP0_Write( const char *pData, unsigned int dLength)
{
  //PutString0("EP0 send data\r\n");

  //сохраняем указатель на данные, кол-во байт для пересылки
  //и обнуляем счётчик переданных байт
  epsCTRL[0].status = UDP_ENDPOINT_SENDING;
  epsCTRL[0].pData = pData;
  epsCTRL[0].totalLen = dLength; //всего байт на передачу
  epsCTRL[0].sendLen = 0; //всего послано байт
  epsCTRL[0].tx_flag = 0; //флаг того что передача завершена

  //ожидаем готовности к передаче
  while((AT91C_BASE_UDP->UDP_CSR[0]&AT91C_UDP_TXPKTRDY)==AT91C_UDP_TXPKTRDY);
  epsCTRL[0].tx_flag = EP0_WritePayload(); //записываем блок данных
  AT91C_BASE_UDP->UDP_CSR[0] |= AT91C_UDP_TXPKTRDY; //инициируем передачу

  // разрешаем прерывание от конечной точки
  //для EP0 оно разрешено всегда
  AT91C_BASE_UDP->UDP_IER = 1 << 0;

  return 1;
}

 

В процессе эксперементов обнаружил интересное явление.

 

Вот моя функция загрузки данных в фифо конечной точки.

 

/****************************************/
//запись блока данных в EP0
unsigned char EP0_WritePayload(void)
{
   int int_path, fract_path;

   //вычисляем целую часть от того сколько осталось послать
   int_path = (epsCTRL[0].totalLen - epsCTRL[0].sendLen)/EP0_SIZE;

   //AT91C_BASE_UDP->UDP_CSR[0] |= AT91C_UDP_DIR;

   if(int_path > 0)
   {
     //есть больше чем EP0_SIZE

     for(unsigned char i=0; i<EP0_SIZE; i++)
     {
       AT91C_BASE_UDP->UDP_FDR[0] = *(epsCTRL[0].pData + epsCTRL[0].sendLen + i);
     }

     epsCTRL[0].sendLen += EP0_SIZE; //увеличиваем кол-во переданных байт на 8

AT91C_BASE_UDP->UDP_CSR[0] |= AT91C_UDP_DIR;

     return 0; //признак - ещё не все данные переданы
   }
   else
   {
     //осталась часть пакета, если она больше 0 то дописываем байты и выдаём признак что это последняя передача
     //иначе просто выдаём признак последняя передача чтобы послать пустой пакет

     fract_path = (epsCTRL[0].totalLen - epsCTRL[0].sendLen)%EP0_SIZE; //вычисляем дробную часть от того сколько осталось послать
     if(fract_path > 0)
     {
       //дописываем остаток пакета

       for(unsigned char i=0; i<fract_path; i++)
       {
         AT91C_BASE_UDP->UDP_FDR[0] = *(epsCTRL[0].pData + epsCTRL[0].sendLen + i);
       }


       epsCTRL[0].sendLen += fract_path; //увеличиваем кол-во переданных байт на дробную часть

AT91C_BASE_UDP->UDP_CSR[0] |= AT91C_UDP_DIR;

     }

      return 1; //возвращаем признак что все данные переданы

   }


}

 

В ней я принудительно устанавил флаг AT91C_BASE_UDP->UDP_CSR[0] |= AT91C_UDP_DIR после загрузки данных.

Так вот если я устанавливаю этот флаг так как сейчас в функции (по веткам условия int_path > 0 и fract_path > 0 т.е. реально чтото записываю в фифо) то получаю такой лог запросов:

 

CONNECT PULLUP
Start of programm
WAKEUP or RXRSM
WAKEUP or RXRSM
ENDBUSRES
RXSUSP
WAKEUP or RXRSM
ENDBUSRES

RXSETUP: 
80 06 00 01 00 00 40 00 
EP0: new request -  standart Get Descriptor Device
UDP_RXDATA
ENDBUSRES

RXSETUP: 
00 05 01 00 00 00 00 00 
EP0: new request -  standart Set AddressTX complet

RXSETUP: 
80 06 00 01 00 00 12 00 
EP0: new request -  standart Get Descriptor Device
TX complet
UDP_RXDATA

RXSETUP: 
80 06 00 02 00 00 09 00 
EP0: new request -  standart Get Descriptor Configuration
TX complet
UDP_RXDATA

RXSETUP: 
80 06 00 03 00 00 FF 00 
EP0: new request -  standart Get Descriptor String
TX complet
UDP_RXDATA

RXSETUP: 
80 06 03 03 09 04 FF 00 
EP0: new request -  standart Get Descriptor String
TX complet
UDP_RXDATA

RXSETUP: 
80 06 00 02 00 00 FF 00 
tring
TX complet
UDP_RXDATA

RXSETUP: 
80 06 00 01 00 00 12 00 
EP0: new request -  standart Get Descriptor Device
TX complet
UDP_RXDATA

RXSETUP: 
80 06 00 02 00 00 09 00 
EP0: new request -  standart Get Descriptor Configuration
TX complet
UDP_RXDATA

RXSETUP: 
80 06 00 02 00 00 29 00 
EP0: new request -  standart Get Descriptor Configuration
TX complet
UDP_RXDATA

RXSETUP: 
00 09 01 00 00 00 00 00 
EP0: new request -  standart Set ConfigurationTX complet

RXSETUP: 
21 0A 00 00 00 00 00 00 
EP0: new request -  unsupported
STALLSENT

RXSETUP: 
81 06 00 22 00 00 60 00 
EP0: new request -  standart unsupported
STALLSENT

RXSETUP: 
81 06 00 22 00 00 60 00 
EP0: new request -  standart unsupported
STALLSENT

RXSETUP: 
81 06 00 22 00 00 60 00 
EP0: new request -  standart unsupported
STALLSENT
RXSUSP

 

Если же я устанавливаю флаг AT91C_BASE_UDP->UDP_CSR[0] |= AT91C_UDP_DIR; перед каждым return

( для ветки int_path > 0 запись всегда осуществляется, а вот дробная часть пакета реально записывыается только если fract_path > 0 иначе формируется пакет нулевой длины ) то лог сокращается:

 

CONNECT PULLUP
Start of programm
WAKEUP or RXRSM
WAKEUP or RXRSM
ENDBUSRES
RXSUSP
WAKEUP or RXRSM
ENDBUSRES

RXSETUP: 
80 06 00 01 00 00 40 00 
EP0: new request -  standart Get Descriptor Device
UDP_RXDATA
ENDBUSRES

RXSETUP: 
00 05 01 00 00 00 00 00 
EP0: new request -  standart Set AddressTX complet

RXSETUP: 
80 06 00 01 00 00 12 00 
EP0: new request -  standart Get Descriptor Device
TX complet
UDP_RXDATA

RXSETUP: 
80 06 00 02 00 00 09 00 
EP0: new request -  standart Get Descriptor Configuration
TX complet
UDP_RXDATA

RXSETUP: 
80 06 00 03 00 00 FF 00 
EP0: new request -  standart Get Descriptor String
TX complet
UDP_RXDATA

RXSETUP: 
80 06 03 03 09 04 FF 00 
EP0: new request -  standart Get Descriptor String
TX complet
UDP_RXDATA

RXSETUP: 
80 06 00 02 00 00 FF 00 
tring
TX complet
UDP_RXDATA

RXSETUP: 
80 06 00 01 00 00 12 00 
EP0: new request -  standart Get Descriptor Device
TX complet
UDP_RXDATA

RXSETUP: 
80 06 00 02 00 00 09 00 
EP0: new request -  standart Get Descriptor Configuration
TX complet
UDP_RXDATA

RXSETUP: 
80 06 00 02 00 00 29 00 
EP0: new request -  standart Get Descriptor Configuration
TX complet
UDP_RXDATA

RXSETUP: 
00 09 01 00 00 00 00 00 
EP0: new request -  standart Set ConfigurationTX complet
RXSUSP

 

Т.е. во втором логе отсутствуют запросы класса!!!

Но в обоих случаях проходит стабильная энумерация!!!

 

Логика подсказывает мне что нужно оставить установку флага AT91C_BASE_UDP->UDP_CSR[0] |= AT91C_UDP_DIR; только после фактической записи в фифо. Хотелось бы услышать мнение знающих людей по всему что я тут написал :)

Share this post


Link to post
Share on other sites
Логика подсказывает мне что нужно оставить установку флага AT91C_BASE_UDP->UDP_CSR[0] |= AT91C_UDP_DIR; только после фактической записи в фифо.

Нет, не нужно.

 

Почитайте в документации о правилах установки/сброса битов UDP_CSR.

Share this post


Link to post
Share on other sites

Могу заслать работающий код - там FreeRTOS и USB Compisite device - HID joystick + custom.

 

Только чур меня не спрашивать, что там и как, я уже и сам не помню ;)

Если надо, отпишите в личку.

Share this post


Link to post
Share on other sites

Спасибо Kitsok за желание помочь но со свои usb-устройством я уже разобрался :)

Правда ещё почитаю про установку/сброс битов UDP_CSR (о чём говорил aaarrrr) чтобы окончательно всё довести до ума.

 

Если ваш проект не секретный то выкладывайте сюда - думаю многим будет интересно посмотреть на него :)

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this