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

STM4 USB CDC. Интересное поведение

Использую libopencm3. За основу взят вот этот пример CDC.

Размер фрейма для нашего ендпойнта настроен 64.

Написал простеньку логику: МК принимает фрейм, смотрит первый элемент и высылает назад статик массив размером, указанным в принятом байте.

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

Код:

static void cdcacm_data_rx_cb(usbd_device *usbd_dev, uint8_t ep)
{
    (void)ep;

    int len = usbd_ep_read_packet(usbd_dev, 0x01, USBio.buffer, 64);
    сdcacm_data_tx((uint8_t *)textBuf, USBio.buffer[0]); 
}

 

Посылаю число 0х20 - получаю 32 байта данных.

Посылаю число 0х3F - gjkexf. 63 байта в ответ.

Посылаю 0x40 - тишина. Еще раз - тишина. Теперь внимание! Посылаю 0х01 - получаю 129 байт.

Т.е. пришли все запрошенные за прошлые 3 раза данные.

Выходит, что если МК посылает кол-во байт, совпадающее с размером фрейма - данные где-то зависают.

 

Начал копать: посылает ли МК? Поставил коллбек по окончанию передачи, виду, что передача проходит.

Принимает ли ПК? К сообщению прикреплен скриншот USB сниффера.

Данные действительно где-то зависают. Буфер драйвера? Буфер USB контроллера?

 

Кто сталкивался?

 

post-62650-1445250922_thumb.png

 

UPD: нащел аналогичный вопрос на форуме СТ. Он там остался без ответа)

UPD2: Вот тут пояснили, что передача длиной равной размеру кадра должна дополняться передачей нулевой длины. Однако, такая логика не согласуется с логикой разрабов libopencm3, которые используют следующую логику для проверки занятости ендпойнта в функции отправки:

while(usbd_ep_write_packet(usbd_dev, 0x82, buf, len) == 0);

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

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

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


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

Тем не менее zero length packet нужен.

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

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


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

Тем не менее zero length packet нужен.

Я правильно понимаю, что без zero-length данные зависают на стороне приемника? Чем вообще ограничивается кол-во данных, которые можно протолкнуть без zero-packet?

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


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

Я правильно понимаю, что без zero-length данные зависают на стороне приемника?
Да. Окончанием пакета считается посылка с числом байт меньше размера конечной точки (ноль байтов под этот критерий попадает). Так написано в стандарте. К чему приводит несоблюдение стандартов - вы наблюдаете сами.

 

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


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

Я правильно понимаю, что без zero-length данные зависают на стороне приемника? Чем вообще ограничивается кол-во данных, которые можно протолкнуть без zero-packet?

Да.

Если вопрос про макс размер трансфера, то афаик зависит от операционки, а именно драйвера USB. Для винды так. Если вопрос про то что бы не вставлять ZLP, то тоже довольно очевидно: если передаете байтов картных пакету нужно добавить 1 фейковый байт к передаче. Но решение не лучшее. Лучше использовать/написать более вменяемую библиотеку.

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


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

Если вопрос про то что бы не вставлять ZLP, то тоже довольно очевидно: если передаете байтов картных пакету нужно добавить 1 фейковый байт к передаче. Но решение не лучшее. Лучше использовать/написать более вменяемую библиотеку.

Можно и наоборот: никогда не передавать больше 63 байт за раз.

Это сделать значительно проще, не нужно досконально изучать и переписывать всю библиотеку.

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


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

Это сделать значительно проще, не нужно досконально изучать и переписывать всю библиотеку.

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

 

drozel, у меня используется следующая схема:

usbd_poll() вызывается из прерывания, там есть два основных коллбэка:

- out ep складывает принятые данные в буфер. Оттуда основная задача по мере сил и возможностей выгребает данные.

- in ep пытается "достать" данные из буфера. Заполнение буфера и запуск передачи (однократный вызов write_ep()) - из основной задачи.

 

В эту схему пакет нулевой длины легко приделывается, никаких while (write_ep(...)) {} не нужно. Хотя, конечно, кто ж им мешал возвращать отрицательные значения?..

 

 

PS полез рыться на репозитарий libopencm3, обнаружил те же самые грабли, но по обмену control endpoint. Исправили ровно неделю назад.

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

До остальных эндпоинтов у них руки пока не дошли.

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


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

- out ep складывает принятые данные в буфер. Оттуда основная задача по мере сил и возможностей выгребает данные.

- in ep пытается "достать" данные из буфера. Заполнение буфера и запуск передачи (однократный вызов write_ep()) - из основной задачи.

У Вас там что, циклические буферы? По идее, еще семафоры предусмотреть придется..

У меня в прошлом проекте логика попроще:

приемник получает весь трансфер в массив и вызывает обработчик. Тот, в зависимости от первого байта вызывает нужный кейс, тот отрабатывает данные, затирает тот же буфер данными для передачи и возвращает длину. После вызывается отправка.

Ну это проект зацикленный на управлении с пк, никаких своих процессов.

В том проекте, кстати, использовался USB raw на stdlib. Скорость была просто ужасная. Ну и гемор с драйвером и самопальными PID VID. Решил перескочить на CDC и libopencm3.

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

До остальных эндпоинтов у них руки пока не дошли.

Ха.

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

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


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

Можно и наоборот: никогда не передавать больше 63 байт за раз.

Это сделать значительно проще, не нужно досконально изучать и переписывать всю библиотеку.

Рано или поздно появится необходимость в чем-то сложнее CDC, лучше к этому моменту иметь правильную библиотеку.

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


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

Интересно, что в stdlib контроль трансфера уже прописан в низкоуровневых функциях:

/* Program the transfer size and packet count
      * as follows: xfersize = N * maxpacket +
      * short_packet pktcnt = N + (short_packet
      * exist ? 1 : 0)
      */
      deptsiz.b.xfersize = ep->xfer_len;
      deptsiz.b.pktcnt = (ep->xfer_len - 1 + ep->maxpacket) / ep->maxpacket;
      
      if (ep->type == EP_TYPE_ISOC)
      {
        deptsiz.b.mc = 1;
      }       
    }
    USB_OTG_WRITE_REG32(&pdev->regs.INEP_REGS[ep->num]->DIEPTSIZ, deptsiz.d32);

Какого черта такое творится в libopencm3??

Я над, казалось бы, файлом верхнего уровня USB_cdc.c должен воротить логику протокола?

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

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


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

Еще вопрос: в случае передачи послыки с ПК через CDC кто организует ZLP, когда кол-во байт кратно 64?

Terminal шлет строку по одному символу в кадре, а NI VISA просто отправляет 64 без всяких ZLP.

 

Не могу составить нормальное представление работы CDC. Я полагал, что пользователь системы работает с портом, как с простым UART, засылая теда что хочет и когда хочет. А драйвер передает это в МК пакетами по 64 байта, трансферами по 1024. Т.е. если я пошлю 256 байт, уйдет 4 пакета и ZLP. Если отправлю 1025, уйдет 16 пакетов, потом МК поставит NACK и ПК будет ждать разрешения для досылки еще одного байта. И пытаюсь создать соответствующую логику на МК, которая будет формировать трансфер (ожидая ZLP или пакета меньше чем 64 байта), после чего выставлять force NAK и вызывать обработчик для данных.

 

Поправьте, в чем я не прав.

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

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


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

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

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

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

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

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

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

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

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

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