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

STM32F4Discovery USB_HID, посылка данных в PC

Добрый день!

 

Понадобилось организовать обмен данными стмки с пк на основе USB_HID.

За основу взял темплейт от TM с его либами.

 

В целом, туда-сюда пакетики бегают, но есть несколько крайне непонятных для меня моментов:

1. Коллбек USBD_HID_DataOut. Насколько я понял, прием данных происходит именно в нем. Но откуда достать сами данные? Во всех примерах используют переменную USB_Rx_Buffer, которая объявляется в том же файле. Но она нигде не заполняется, а при попадании в коллбек данные в ней чудесным образом оказываются. Как так?

 

2. Непонятно поведение: если буфер объявить внутри коллбека, то вместо значений из буфера отсылается мусор. Если же объявление сделать глобальным, то отсылаются верные значения. Почему так?

 

3. В коллбек вписана отправка ответного пакета. Но иногда (в основном после включения) ответный пакет отсылается лишь с 3-го раза. Почему такое может возникать?

 

Спасибо.

Поделиться сообщением


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

1. Очевидно её заполняет обработчик прерывний USB который не был найден.

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

3. Могут быть разные причины.

Поделиться сообщением


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

1. Очевидно её заполняет обработчик прерывний USB который не был найден.

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

3. Могут быть разные причины.

 

1. Так ведь обращение к ней нигде не производится. Я даже указателя нигде на нее не нашел. Может кто знает сей фокус?

2. Более-менее понятно.

3. Пока тоже неясно, разбираюсь.

 

Появился еще один вопрос:

Для отправки данных используется функция USBD_HID_SendReport(&USB_OTG_dev, buff, count).

И для того, чтобы послать буфер в 64 байта надо указывать count равным 65, а не 64 как казалось бы... Никто с таким не сталкивался?

Поделиться сообщением


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

64 байта - граница пакета, должна быть снабжена пустым пакетом с 0 данными, может дело в этом?

 

еще не забывайте что есть ДМА для заполнения массивов....

Поделиться сообщением


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

64 байта - граница пакета, должна быть снабжена пустым пакетом с 0 данными, может дело в этом?

 

Не совсем понял: т.е., чтобы отослать 64 байта, надо отослать 65 с последним нулевым? Или в 64 байтах последний болжен быть нулевой?

 

Хорошо, давайте разбираться детально :)

 

Вот есть такой код отправки ответа:

#define buf_size  8
uint8_t buf[buf_size];

static uint8_t  USBD_HID_DataOut (void  *pdev, uint8_t epnum) {
  uint16_t USB_Rx_Cnt;
  uint8_t i = 0;
  
  /* Get the received data buffer and update the counter */
  USB_Rx_Cnt = ((USB_OTG_CORE_HANDLE*)pdev)->dev.out_ep[epnum].xfer_count; // pointer to usb packet

  if(USB_Rx_Buffer[1] == 0x01) {
    TM_DISCO_LedOn(LED_GREEN);
  }
  else if(USB_Rx_Buffer[1] == 0x00) {
    TM_DISCO_LedOff(LED_GREEN);
  }
  
  for(i = 0; i<buf_size; i++) buf[i] = i;
  TM_USB_HIDDEVICE_SendCustom(buf, buf_size);
  
  /* Prepare Out endpoint to receive next packet */
  DCD_EP_PrepareRx(pdev, HID_OUT_EP, USB_Rx_Buffer, MAX_DATA_LENGTH);
  
  return USBD_OK;
}

 

Т.е., в ответ на прием пакета я отправляю 8 байт со счетчиком внутри.

TM_USB_HIDDEVICE_SendCustom() - это просто обертка над USBD_HID_SendReport(&USB_OTG_dev, buff, count).

 

Я ожидаю, что на пк примется этот 8-байтный ответ, однако ничего не принимается. Смотрим сниффером что получается:

post-67084-1440650779_thumb.png

Ответы приходят (правда почему-то только с 3-го раза после включения девайса), длина 8 байт, вроде все как заказывали.

 

 

Смотрим како-нить пакетик:

post-67084-1440650783_thumb.png

Сниффер говорит что пакет принят успешно.

 

Однако, принимающая прога на компе в упор ничего не видит.

Вот репу уже сломал, прям руки опускаются. И что-то мне подсказывает что дело, скорее всего, в дескрипторах. Вроде я их пилил-пилил, но может чего не допилил. Посмотрите опытным взглядом:

post-67084-1440651195_thumb.png

Там по идее заложено 64 байта туда, 64 байта обратно и все.

Изменено пользователем ilkz

Поделиться сообщением


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

Опа-ля, йа креведко розобралсо.

 

Дело было вот в чем:

1. Если мы хотим отправить N Байт, то реально надо отправить на 1 байт больше, т.е. N+1.

2. В дескрипторе репорта должно быть указано количество отправляемых элементов (REPORT_COUNT) равное N.

3. Первым байтом в отправляемых данных должен быть REPORT ID.

4. И усё.

 

Теперь понятны слова Golikov A. про 65 байт.

 

Осталось понять кто заполняет USB_Rx_Buffer.

Изменено пользователем ilkz

Поделиться сообщением


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

1. Если мы хотим отправить N Байт, то реально надо отправить на 1 байт больше, т.е. N+1.

....

Теперь понятны слова Golikov A. про 65 байт.

Вы поняли неправильно. Конечная точка имеет ограничение на длину пакета. В вашем случае оно равно 64 байта. Если вам нужно передать больше - вы передаете несколько пакетов. Но как вторая сторона определит, что вы уже закончили передачу? А очень просто: если пришел пакет с длиной, меньшей, чем максимальная длина для этой рабочей точки, то он последний. А что делать, если вам надо передать ровно столько данных, какова максимальная длина пакета? Получив такой пакет вторая сторона будет ждать продолжения. Поэтому передающая в этом и только в этом случае посылает следом пакет с нулевой длиной, т.е. без данных (zlp, zero-length payload). Но это все внутренние дела стека., вас они волновать не должны.

 

REPORT ID - это совсем другая опера. Если вы в дескрипторах сообщили, что будете отсылать несколько разных типов сообщений (reports), то вы должны в начале каждого передавать дополнительный байт с идентификатором. Если вы анонсировали только один вид сообщений, то второй стороне не нужно отделять его от других и этот байт с идентификатором не передается.

Поделиться сообщением


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

Вы поняли неправильно. Конечная точка имеет...

 

REPORT ID - это совсем другая опера. Если вы в дескрипторах сообщили, что будете отсылать несколько разных типов сообщений (reports), то вы должны в начале каждого передавать дополнительный байт с идентификатором. Если вы анонсировали только один вид сообщений, то второй стороне не нужно отделять его от других и этот байт с идентификатором не передается.

 

 

С первым понятно, спасибо за внятное разъяснение.

Со вторым: если в репорте используется один IN и один OUT, то это считается за два вида сообщений или за один?

Поделиться сообщением


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

Со вторым: если в репорте используется один IN и один OUT, то это считается за два вида сообщений или за один?
Рапорты считаются отдельно для каждой конечной точки. IN и OUT - разные конечные точки, между разными конечными точками рапорты идентифицирутся по номеру конечной точки.

Поделиться сообщением


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

Сергей Борщ, ясно.

 

Последний пока что вопрос: кто же все-таки заполняет USB_Rx_Buffer?

Пока что единственное что я нашел связанное с буфером приема/передачи - это указатель xferr_buff в структуре USB_OTG_hc (файл usb_core.h). Но как-то оно не то...

Поделиться сообщением


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

Последний пока что вопрос: кто же все-таки заполняет USB_Rx_Buffer?
Заполняется она в прерывании. Где-то в процессе инициализации конечной точки в стек передается адрес и размер этого буфера. Но это уже не ко мне. Я вместо этого ужаса написал свой стек на плюсах. Поищите по файлам все упоминания USB_Rx_Buffer, наверняка наткнетесь на функцию инициализации. А дальше уже копайте от нее.

 

Поделиться сообщением


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

Искал - пусто.

 

Сергей Борщ, хотите прикол?

Было (это единственное объявление данной переменной на весь проект, сидит в том же файле что и код ниже):

__ALIGN_BEGIN uint8_t USB_Rx_Buffer   [MAX_DATA_LENGTH] __ALIGN_END;

 

Меняю на:

__ALIGN_BEGIN uint8_t SuperMegaBuffer   [MAX_DATA_LENGTH] __ALIGN_END;

 

, соответственно, в коллбеке приема:

static uint8_t  USBD_HID_DataOut (void  *pdev, uint8_t epnum) {
  uint16_t USB_Rx_Cnt;
  uint8_t i = 0;
  
  USB_Rx_Cnt = ((USB_OTG_CORE_HANDLE*)pdev)->dev.out_ep[epnum].xfer_count; // pointer to usb packet

  USB_Rx_Buffer[0] = 0x01;
  TM_USB_HIDDEVICE_SendCustom(USB_Rx_Buffer, 65);

  DCD_EP_PrepareRx(pdev, HID_OUT_EP, USB_Rx_Buffer, MAX_DATA_LENGTH);
  return USBD_OK;
}

 

меняю на:

static uint8_t  USBD_HID_DataOut (void  *pdev, uint8_t epnum) {
  uint16_t USB_Rx_Cnt;
  uint8_t i = 0;
  
  USB_Rx_Cnt = ((USB_OTG_CORE_HANDLE*)pdev)->dev.out_ep[epnum].xfer_count; // pointer to usb packet
  
  SuperMegaBuffer[0] = 0x01;
  TM_USB_HIDDEVICE_SendCustom(SuperMegaBuffer, 65);
  
  DCD_EP_PrepareRx(pdev, HID_OUT_EP, SuperMegaBuffer, MAX_DATA_LENGTH);
  return USBD_OK;
}

 

и все, больше ничего и нигде не трогаю. И что же я вижу? А то, что чудесным, мать его, образом - в SuperMegaBuffer оказываются принятые данные. КАААК?

Поделиться сообщением


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

КАААК?

Видимо как-то отсюда:

  TM_USB_HIDDEVICE_SendCustom(USB_Rx_Buffer, 65);

Тут же вы передаете адрес этого буфера стеку. Копайте эту функцию.

Поделиться сообщением


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

Видимо как-то отсюда:

Тут же вы передаете адрес этого буфера стеку. Копайте эту функцию.

 

Весь цимес в том, что если убрать строки

  TM_USB_HIDDEVICE_SendCustom(USB_Rx_Buffer, 65);
  DCD_EP_PrepareRx(pdev, HID_OUT_EP, USB_Rx_Buffer, MAX_DATA_LENGTH);

 

,то волшебный буфер все равно оказывается заполнен принятыми данными )))

Поделиться сообщением


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

Весь цимес в том, что если убрать строки
Чудес не бывает :) Не верю. Что-то вы не перекомпилировали или не то зашили

 

Поделиться сообщением


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

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

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

Гость
К сожалению, ваш контент содержит запрещённые слова. Пожалуйста, отредактируйте контент, чтобы удалить выделенные ниже слова.
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

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

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

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

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

×
×
  • Создать...