Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Рабочий пример работы SL811 в режиме host'a
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
Hexxx
Тема поднималась милион раз, но никто так и не удосужился запостить пример. Значит буду первым:
Код
#define MASTER_MODE             0x80

void SL811_Init(void)
{    
    SL811Write(cDATASet, 0xe0); //SOF Timeout Value
    SL811Write(cSOFcnt, 0x2e | MASTER_MODE);
    SL811Write(CtrlReg, 0x5);            
    SL811Write(EP0Status, PID_SOF);
    SL811Write(EP0Counter, 0);              //Set Endpoint Zero
    SL811Write(EP0Control, 0x01);            //Start the SOF
    SL811Write(IntEna, INSERT_REMOVE);      //USB-A, Insert/Remove, USB_Resume.
    SL811Write(IntStatus, INT_CLEAR);    //Clear Interrupt enable status
    
}

#define NO_ERROR 0
#define ERROR_TIMEOUT 1
#define ERROR_STALL 2
#define ERROR_OVERFLOW 3
#define ERROR_EP0_ERROR 4
#define ERROR_UNKNOWN 5


int SendData(void * pData, int DataSize)
{
  unsigned char result, intr, remainder,timeout;
  SL811BufWrite(EP0_Buf,pData,DataSize);

  SL811Write(EP0Status,(PID_SETUP | 0));  // PID + EP address
  SL811Write(EP0Counter,0);               // USB address
  SL811Write(EP0Address,EP0_Buf);         // buffer address, start with "data0"
  SL811Write(EP0XferLen,DataSize);        // data transfer length
  SL811Write(IntStatus,INT_CLEAR);        // clear interrupt status
  SL811Write(EP0Control,DATA0_WR);  

  timeout=10;
  while(1)
  {
    do
    {                                      
      intr = SL811Read(IntStatus);          // wait for interrupt to be done
      if((intr & USB_RESET) || (intr & INSERT_REMOVE))  //leave if device is removed
        return 1;                  
              
    } while (!(intr & USB_A_DONE)); // interrupt done !!!
  
    SL811Write(IntStatus,INT_CLEAR);         // clear interrupt status
    result = SL811Read(EP0Status);           // read EP0status register
    remainder = SL811Read(EP0Counter);       // remainder value in last pkt xfer
  
    if (result & EP0_ACK)
      return 0;  
    
    if (result & EP0_NAK)                  // NAK Detected
    {                            
        SL811Write(IntStatus,INT_CLEAR);        // clear interrupt status, need to
        SL811Write(EP0Control,DATA0_WR);            // re-arm and request for last cmd, IN token
        result = 0;                                     // respond to NAK status only
    }
    
    if (result & EP0_TIMEOUT)                // TIMEOUT Detected
    {                            
        if(timeout)
        {  
            timeout--;
        }
        else
          return ERROR_TIMEOUT;                    // exit on the timeout detected  
        
        SL811Write(IntStatus,INT_CLEAR);        // clear interrupt status, need to
        SL811Write(EP0Control,DATA0_WR);            // re-arm and request for last cmd again
    }
    
    if (result & EP0_STALL)                  // STALL detected
      return ERROR_STALL;                    // for unsupported request.
    
    if (result & EP0_OVERFLOW)                // OVERFLOW detected
      return ERROR_OVERFLOW;  
    
    if (result & EP0_ERROR)                  // ERROR detected
      return ERROR_EP0_ERROR;
  }
  
  
  return ERROR_UNKNOWN;
}

int GetReply(void * pData, int DataSize)
{
  unsigned char result, intr, remainder,timeout;
  
  SL811Write(EP0Status,(PID_IN | 0));  // PID + EP address
  SL811Write(EP0Counter,0);               // USB address
  SL811Write(EP0Address,EP0_Buf);         // buffer address, start with "data0"
  SL811Write(EP0XferLen,DataSize);        // data transfer length
  SL811Write(IntStatus,INT_CLEAR);        // clear interrupt status
  SL811Write(EP0Control,DATA0_RD);  
  
  timeout=10;
  while(1)
  {
    do
    {                                      
      intr = SL811Read(IntStatus);          // wait for interrupt to be done
      if((intr & USB_RESET) || (intr & INSERT_REMOVE))  //leave if device is removed
        return 1;                  
              
    } while (!(intr & USB_A_DONE)); // interrupt done !!!
  
    SL811Write(IntStatus,INT_CLEAR);         // clear interrupt status
    result = SL811Read(EP0Status);           // read EP0status register
    remainder = SL811Read(EP0Counter);       // remainder value in last pkt xfer
  
    if (result & EP0_ACK)
    {
      if(remainder!=DataSize)              
      {
        SL811BufRead(EP0_Buf, pData, DataSize);
        return 0;
      }
    }
    
    if (result & EP0_NAK)                  // NAK Detected
    {                            
        SL811Write(IntStatus,INT_CLEAR);        // clear interrupt status, need to
        SL811Write(EP0Control,DATA0_WR);            // re-arm and request for last cmd, IN token
        result = 0;                                     // respond to NAK status only
    }
    
    if (result & EP0_TIMEOUT)                // TIMEOUT Detected
    {                            
        if(timeout)
        {  
            timeout--;
        }
        else
          return ERROR_TIMEOUT;                    // exit on the timeout detected  
        
        SL811Write(IntStatus,INT_CLEAR);        // clear interrupt status, need to
        SL811Write(EP0Control,DATA0_WR);            // re-arm and request for last cmd again
    }
    
    if (result & EP0_STALL)                  // STALL detected
      return ERROR_STALL;                    // for unsupported request.
    
    if (result & EP0_OVERFLOW)                // OVERFLOW detected
      return ERROR_OVERFLOW;  
    
    if (result & EP0_ERROR)                  // ERROR detected
      return ERROR_EP0_ERROR;
  }
  
  return ERROR_UNKNOWN;
}

unsigned char Buffer[0x64];
void testSL811(void)
{
  int error;
  SetupPKG pkg;
  pkg.bmRequest=0x80;
  pkg.bRequest=0x06;
  pkg.wValue=0x200;
  pkg.wIndex=0x00;
  pkg.wLength=0xFF00;
  
  SL811ClockInit();
  SL811_Init();
  
  error=SendData(&pkg,sizeof(pkg));
  if (!error)
  {
    error=GetReply(Buffer,0x9);  
  }

}


Код не идеальный, надо доделывать функцию чтения. Он инитит хост в режиме low-speed и позволяет вам послать SETUP пакет GET_DESCRIPTOR и вычитать 9-байтов ответа от Slave устройства. Все нехватающие функции и define есть в cy3662/EZ811 development kit'e. Его можно взять вот тут
Hexxx
Я понял как заставить работать пример из EZ811 DK!

У них там неправильный endian в VendorCmd(). Вот правильный:
Код
setup.bmRequest  = bReq;
setup.bRequest   = bCmd;
setup.wValue     = WordSwap(wValue);
setup.wIndex     = WordSwap(wIndex);
setup.wLength    = wLen;
Hexxx
И все таки он глючит на чтении. Люди помогите, уже 2 недели бьюсь не могу понять как должно работать чтение. help.gif

Как я делаю:
Инициализирую SL811 в режим Full Speed:
Код
SL811Write(cSOFcnt,0xAE);       // Set up host & full speed direct and SOF cnt
SL811Write(cDATASet,0xE0);      // SOF Counter Low = 0xE0; 1ms interval
SL811Write(CtrlReg,0x05);       // Setup 48MHz and SOF enable

SL811Write(EP0Status,0x50);           // Setup SOF Token, EP0
SL811Write(EP0Counter,0x00);        // reset to zero count
SL811Write(EP0Control,0x01);           // start generate SOF or EOP

SL811Write(IntStatus,INT_CLEAR);


Шлю запрос на получение дескриптора конфигурации:
Код
SL811BufWrite(EP0_Buf,pData,DataSize);
SL811Write(EP0Status,(PID_SETUP | 0));
SL811Write(EP0Counter,0);               // USB address
SL811Write(EP0Address,EP0_Buf);         // buffer address, start with "data0"
SL811Write(EP0XferLen,DataSize);        // data transfer length
SL811Write(IntStatus,INT_CLEAR);        // clear interrupt status
SL811Write(EP0Control,sDATA0_WR);


Дальше жду пока закончится отправка и читаю статус:

Код
do
{                                      
  intr = SL811Read(IntStatus);          // wait for interrupt to be done
  if((intr & USB_RESET) || (intr & INSERT_REMOVE))  //leave if device is removed
     return ERROR_DISCONNECT;                  
            
} while (!(intr & USB_A_DONE)); // interrupt done !!!
  
SL811Write(IntStatus,INT_CLEAR);         // clear interrupt status
status = SL811Read(EP0Status);           // read EP0status register


Возращается (status & EP0_ACK)==EP0_ACK. Т.е. с отправкой все ok.

Далее читаю ответ. Я в снифере на компе поглядел, что должно прийти 0x43 байта. Размер EP0_Buf= 0x40 байт. Поэтому пытаюсь читать 0x40 байт:
Код
SL811Write(EP0Status, (PID_IN | 0));  // PID + EP address
SL811Write(EP0Counter, 0);               // USB address
SL811Write(EP0Address, EP0_Buf);         // buffer address, start with "data0"

if (ReqProc.BufferSize > 0x40)
  SL811Write(EP0XferLen, 0x40);
else
  SL811Write(EP0XferLen,BufferSize);        // data transfer length
  
SL811Write(IntStatus, INT_CLEAR);        // clear interrupt status
SL811Write(EP0Control, sDATA0_RD);


Снова жду результата:

Код
do
{                                      
  intr = SL811Read(IntStatus);          // wait for interrupt to be done
  if((intr & USB_RESET) || (intr & INSERT_REMOVE))  //leave if device is removed
     return ERROR_DISCONNECT;                  
            
} while (!(intr & USB_A_DONE)); // interrupt done !!!
  
SL811Write(IntStatus,INT_CLEAR);         // clear interrupt status
status = SL811Read(EP0Status);           // read EP0status register


Опять возращается (status & EP0_ACK)==EP0_ACK. Т.е. вроде как прочитало.

Читаю сколько там осталось дочитать:
Код
remainder = SL811Read(EP0Counter);


remainder = 0x30, получается что недочитано 0x30 байт! Что делать дальше? Как их дочитать? Почему оно их недочитало? В примере из EZ811 DK они вообще такие пакеты скипают.

Пробовал ради интереса прочитать то что лежит в EP0_Buf. Там лежит 0x10 байт, правильных, таких же как я видел в снифере на компе, а дальше мусор.

Еще в примере из EZ811 DK они зачем-то переключают буферы когда читают большой кусок, зачем?
И последнее, что такое payload? Как его определяют?
Hexxx
Так странно, куча людей. У многих уже проблемы с высоко уровневым кодом usb хоста. А ни на один вопрос по низкому уровню работы с SL811 никто не ответил blush.gif
Hexxx
Отвечает Александр Друзь!

Payload - это масимальный объем информации, который можно переслать/прочитать за 1 раз.
Регистр EP0Counter - возвращает сколько байт было недочитано. Причем, число которое оттуда возвращается - signed! То есть если вы прочитали EP0Counter, а там 0xFC, это означает что не прочитано -4 байта. То есть вы захотели прочитать 0xC байт а девайс вам ответил 0x10-ю байтами. Получатеся, что еще 4 байта не было вычитано, хотя они есть. А если вы захотели прочитать 0xC, а девайс вам вернул 0x8 байт, то EP0Counter будет = 4. Т.е. вы запросили на 4 байта больше чем ответил девайс.

Как узнать размер Payload для EP0?
Payload обычно можно узнать запросив DEVICE descriptor. Но как его узнать если девайс говорит NAK на запрос DEVICE descriptor'a? Экспериментальным путем. Даже если девайс не поддерживает запрос Device descriptor'a, он поддерживает запрос на CONFIGURATION descriptor в любом случае. Обычно тут девайс выдает большой объем информации. Пробуйте читать ответ и смотрите сколько байт будет недочитано. Начните с 8 и увеличивайте кратно 8: 8, 16, 24, 32, 64. При одном из значений Payload, EP0Counter будет == 0. Это значение и есть payload.

Фишка с чтением ответов девайса, длина которых > payload. Девайс действительно не даст вам прочесть данных больше чем payload, поэтому надо будет сделать еще N чтений. Но! Не пытайтесь дебажить код многократного чтения. Там дается очень короткий промежуток времени для дочитывания остальной части ответа. Когда пытаешься дебажить - первая часть читается нормально, а остальные так и не вычитываются.

Переключение буферов делается для быстроты работы, в один мы читаем с USB, а из второго мы считываем то, что было уже прочитано из USB при предидущем чтении. Про то как надо делать переключение буферов описано в Application Notes см. "double buffer"

Пример в EZ811 DK реально работоспособный. Очень важно делать speed_detect().

P.S. Почитал Application Notes по SL811. Много полезного. Приатачил.
Hexxx
Эпопея продолжается. Bulk-transfer не пашет.
Отправка данных в Output-endpoint работает на ура, всегда ACK. А вот с Input-endpoint'а ответ читается максимум 1 раз. Остальные попытки читать не проходят, на Input-endpoint всегда NAK. То есть отправил команду 1 раз - прочитал, все нормально. Отправил еще одну такую же команду - уже не отвечает. Перепосылать - бесполезно, читать пока не станет ACK - тоже. Оно всегда NAK.

Я уже конечно не надеюсь, но может кто-то кроме меня занимается программированием SL811. Ответьте плиз, почему такое может быть?
misyachniy
Не знаю что и посоветовать.
У меня был затык в том, что все операции нужно проводить со спецификации USB, а я не обращал внимание на установку DATA0/DATA1.
Hexxx
А меня опять оказался баг с переключением DATA0/DATA1. Исправил и ща уже отвечает и на Bulk transfer'ы. Я счастлив tort.gif
Hexxx
Начал пробовать подключать разные устройства. Кто-нить знает от чего может быть статут TIMEOUT на всех EP, кроме EP0?
Hexxx
А вообще когда проставляется TIMEOUT?
Hexxx
ап
Ailinor
Я решил так:
biggrin.gif
int Read(unsigned char *buff)
{
unsigned char status;
unsigned char bytesNotRead;
int read = 0;

for (int i = 0; i < 1000; ++i)
{
if ( DataRW(USB_DEVICE_ZERO, EpIn, EpInSize, EpInSize, &buff[read]) )
{
GetXferInfo(&bytesNotRead, &status);

if ( status & EP0_ACK )
{
read += EpInSize - bytesNotRead;
}

if ( (0 == bytesNotRead) && (0 != read) )
{
break;
}
}
}

return (read) ? read : -1;
}

Твоё мнение?


05.gif

Но не помогает. Цикл всё равно выполняется все 1000 раз а кусок буфера может прийти и позднее. А ответы действительно приходят кусками sad.gif Причём о том что должны быть ещё не узнать никак. Буду пробывать использовать стоповые байты sad.gif вместо
if ( (0 == bytesNotRead) && (0 != read) )
{
break;
}
Hexxx
Я тут тоже по этому вопросу мучаю разных людей. Один мне на примере АТ команд для мобил вот что ответил:

> If the data of one package is less than payload size, it means cellphone has no other data to return.

т.е. если в количество непрочитаных байт(remainder) != 0, значит телефон еще не начал отвечать.

> If the data of one package is equal to payload size. It means cellphone has more data, or has no more data. You should read the endpoint 0x8a again.

Если количество непрочитаных байт == 0 (remainder), значит надо вычитать те данные, которые есть IN endpoint'е, а потом пытаться читать еще, чтобы проверить это все или нет.

> If cellphone return ACK, you should receive the following data.
Если когда мы прочитали повторно, девайс сказал ACK - вычитываем то что есть, и читаем снова.

> If cellphone return NAK, it means cellphone has no other data to return by now.
Если девайс сказал NAK - данные закончились.
Hexxx
Кое-что еще выяснилось на счет таймаутов.
Если девайс собирается слать данные, но их еще нет он отвечает NAK. А если пытаешься читать с какого-то неактивного или не существующего эндпоинта - пишет TIMEOUT.
Но я-то читаю из существующих эндпоинтов...
Hexxx
А фишка оказалась была в том, что я не делал SetAddress(). Некоторым девайсам это необязательно. Они и без SetAddress() запросто в BULK режиме общаются. А вот тут получается что на EP0 реквесты отвечает, а на BULK endpoint'ы без SetAddress() не отвечает.

p.s. Выяснил только седня, за несколько часов с помощью хадварного USB снифера. Совет всем начинающим USB-программерам: покупайте USB снифер - сэкономите несколько месяцев соития с девайсом.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.