Перейти к содержанию

V-Usb Проблема С Прерываниями


Рекомендуемые сообщения

Всем привет!

Разбираюсь с подключением AVR к USB с использованием библиотеки V-USB на стороне контроллера и средствами hid.dll на стороне винды.

Идея сделать дебажный вывод (строк) на комп через USB.

Покопавшись пару дней смог доставать сообщения из контроллера через GET_REPORT. Следущий этап - USB прерывания. Да, я знаю что USB это хост ориентированый протокол и то, что через Interrupt In можно прокачать только 8 байт. Поэтому когда мне необходимо отправить сообщение в сторону компа я посылаю "пустое" прерывание, которое сигнализирует хосту что неплохо было бы данные забрать.

Код со стороны контроллера (интересные его части)

typedef struct
{
   uchar reportID;
   uchar msgLen;
   char message[30];
} debugMsg_t;

const uchar DEBUG_INT_MSG_ID = 1;
const uchar DEBUG_MESSAGE_ID = 2;
const PROGMEM char usbHidReportDescriptor[] = {
   0x06, 0x00, 0xff, // USAGE_PAGE (Generic Desktop)
   0x09, 0x01, // USAGE (Vendor Usage 1)
   0xa1, 0x01, // COLLECTION (Application)
   0x15, 0x00, // LOGICAL_MINIMUM (0)
   0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (255)
   0x75, 0x08, // REPORT_SIZE (8)
   // The message data
   0x85, DEBUG_MESSAGE_ID, // REPORT_ID (2)
   0x95, sizeof(debugMsg_t)-1, // REPORT_COUNT (31) - extra byte for ReportID
   0x09, 0x00, // USAGE (Undefined)
   0xb2, 0x02, 0x01, // FEATURE (Data,Var,Abs,Buf)

   // Interrupt (with no data inside) indicates that data is available at debugMsg_t report
   0x85, DEBUG_INT_MSG_ID, // REPORT_ID (1)
   0x95, 0x01, // REPORT_COUNT (7) - Fake
   0x09, 0x00, // USAGE (Undefined)
   0x81, 0x02, // INPUT (Data,Var,Abs)

   0xc0 // END_COLLECTION
};

static uchar idleRate; /* repeat rate for keyboards, never used for mice */
/*volatile*/ debugMsg_t messageToSend;
volatile bool messageSent = true;
static uchar interruptMsg[2];

usbMsgLen_t usbFunctionSetup(uchar data[8])
{
   usbRequest_t *rq = (usbRequest_t *)data;
   if((rq->bmRequestType & USBRQ_TYPE_MASK) == USBRQ_TYPE_CLASS) // HID class request
   {

       if(rq->bRequest == USBRQ_HID_GET_REPORT) // wValue: ReportType (highbyte), ReportID (lowbyte)
       {
           if(rq->wValue.bytes[0] == DEBUG_MESSAGE_ID) //switch the report ID
           {
              usbMsgPtr = (usbMsgPtr_t)&messageToSend;
              messageSent = true;
              return sizeof(messageToSend);
          }
     }
     else if(rq->bRequest == USBRQ_HID_GET_IDLE)
     {
         usbMsgPtr = (usbMsgPtr_t)&idleRate;
         return 1;
      }
      else if(rq->bRequest == USBRQ_HID_SET_IDLE)
      {
          idleRate = rq->wValue.bytes[1];
      }
      else
      {
      }
   }
   else
   {
        // ignore vendor type requests, we don't use any
   }

   return 0;
}


void DebugMsg(const char * msg)
{
   // Data is too large to be transferred via interrupt. So we prepare the data at messageToSend
   // and send fake interrupt so that host can retrieve the data via standard GetReport.
   strncpy(messageToSend.message, msg, sizeof(messageToSend.message)); 
   messageToSend.msgLen = strlen(messageToSend.message);
   messageToSend.reportID = DEBUG_MESSAGE_ID;
   messageSent = false;

   while(!usbInterruptIsReady())
       usbPoll();

   interruptMsg[0] = DEBUG_INT_MSG_ID;
   usbSetInterrupt(interruptMsg, 2);

   while(!messageSent)
   {
       wdt_reset();
       usbPoll();

        if(!(PIND & (1<<PIND5)) && usbInterruptIsReady())
       {
           usbSetInterrupt(interruptMsg, 2);
       }

   }
}

Код приема:

int usbGetReport(usbDevice_t *device, int reportType, int reportNumber, char *buffer, int *len)
{
HANDLE handle = (HANDLE)device;
BOOLEAN rval = 0;
DWORD bytesRead;
switch(reportType){
case USB_HID_REPORT_TYPE_INPUT:
 buffer[0] = reportNumber;
 rval = ReadFile(handle, buffer, *len, &bytesRead, NULL);
 if(rval)
	 *len = bytesRead;
 break;
case USB_HID_REPORT_TYPE_OUTPUT:
 break;
case USB_HID_REPORT_TYPE_FEATURE:
 buffer[0] = reportNumber;
 rval = HidD_GetFeature(handle, buffer, *len);
 break;
}
return rval == 0 ? USB_ERROR_IO : 0;
}

void MainWindow::on_receiveButton_clicked()
{
if(!handle)
 return;
// Wait for the interrupt
ui->logArea->append("\nWaiting for the interrupt");
char intMsg[2];
int len = 2;
int err = usbGetReport(handle, USB_HID_REPORT_TYPE_INPUT, DEBUG_INT_MSG_ID, intMsg, &len);
if(err)
{
 ui->logArea->append(QString("Error code %1, last error %2").arg(err).arg(GetLastError()));
 return;
}
ui->logArea->append(QString("Received interrupt %1 bytes").arg(len));
debugMsg_t msgBuf;
len = sizeof(msgBuf);
msgBuf.reportID = DEBUG_MESSAGE_ID;
ui->logArea->append("\nGetting the data");
err = usbGetReport(handle, USB_HID_REPORT_TYPE_FEATURE, DEBUG_MESSAGE_ID, (char*)&msgBuf, &len);
ui->logArea->append(QString("Received %1 bytes").arg(len));
ui->logArea->append(QString("Error code %1, last error %2").arg(err).arg(GetLastError()));
ui->logArea->append(QString::fromLatin1(msgBuf.message, msgBuf.msgLen));
}

Проблема №1

Первый вызов usbGetReprot (который внутри вызывает ReadFile) виснет, не смотря на то, что прерывание со стороны контроллера ушло. После того как я один раз с помощью кнопки на плате контроллера посылаю дополнительное прерывание (строка if(!(PIND & (1<<PIND5)) && usbInterruptIsReady())) все начинает работать как надо.

Зачем нужно дополнительное прерывание?

Проблема №2

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

Где копать?

ЗЫ Извините за код в столбик - это так он скопировался на сайт. Видимо табы глотаются

Изменено пользователем graf_alex
Ссылка на комментарий
Поделиться на другие сайты

Реклама: ООО ТД Промэлектроника, ИНН: 6659197470, Тел: 8 (800) 1000-321

Сам спросил - сам отвечаю. Видимо нужно было 2 дня бодаться, потом выговорится и найти нешение :)

В общем, проблема оказалась не в коде, а в общем подходе к передаче. Вызов DebugMsg() у меня стоял сразу после инициализации ЮСБ, но на стороне компа еще никто не слушает наши сообщения. Видимо драйвер просто глотает все прерывания, если на той стороне interrupt in pipe никого нет.

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

Проблема 2 пока все еще актуальна

Ссылка на комментарий
Поделиться на другие сайты

20% скидка на весь каталог электронных компонентов в ТМ Электроникс!

Акция "Лето ближе - цены ниже", успей сделать выгодные покупки!

Плюс весь апрель действует скидка 10% по промокоду APREL24 + 15% кэшбэк и бесплатная доставка!

Перейти на страницу акции

Реклама: ООО ТМ ЭЛЕКТРОНИКС, ИНН: 7806548420, info@tmelectronics.ru, +7(812)4094849

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.
Примечание: Ваш пост будет проверен модератором, прежде чем станет видимым.

Гость
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Ответить в этой теме...

×   Вставлено с форматированием.   Восстановить форматирование

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

Загрузка...
  • Последние посетители   0 пользователей онлайн

    • Ни одного зарегистрированного пользователя не просматривает данную страницу
×
×
  • Создать...