Pull to refresh

Comments 23

template<typename Endpoints>
using EndpointsManager = EndpointsManagerBase<SortedUniqueEndpoints<Endpoints>,
  typename Sample<IsBidirectionalOrBulkDoubleBufferedEndpoint, SortedUniqueEndpoints<Endpoints>>::type,
  typename Sample<IsOutEndpoint, SortedUniqueEndpoints<Endpoints>>::type,
  typename Sample<IsBulkDoubleBufferedTxEndpoint, SortedUniqueEndpoints<Endpoints>>::type>;


Не хотел бы я увидеть такой код в продакшине.
А почему? Просто мне кажется, Вы выбрали не самый ужасный кусок кода:)
Про продакшн согласен наполовину: если все идет нормально, то нужды видеть это вроде особой и нет. Единственное, что нужно сделать сразу: заставить автора подробно закомментировать, что именно он имел ввиду в том или ином месте, чтобы спустя время другой программист мог что-то подправить.
UPD: в реальном проекте, если бы мне позволили такое в принципе писать, я бы раздробил длинные объявления, сделал бы так примерно это место:
using BidirectionalAndBulkDoubleBufferedEndpoints = typename Sample<IsBidirectionalOrBulkDoubleBufferedEndpoint, SortedUniqueEndpoints<Endpoints>>::type;
...
using EndpointsManager = EndpointsManagerBase<Endpoints, BidirectionalAndBulkDoubleBufferedEndpoints , ...>;

Из названий переменных должно быть понятно, что к чему.
Сложно поспорить с тем, что читать такой код нереально. При разработке дроблю на много мелких объявлений:
using X = Y;
Когда отладил и понял, что все правильно, соединяю. В основном писать шаблоны несложно (это обычно связка базовый класс + рекурсивная специализация). Напрягает только раскрытие variadic-ов, но ничего не поделать.
Касательно USB, огромные массивы дескрипторов с магическими числами читать пусть и несложно, но разобраться и тем более отредактировать ничуть не легче.
а как вы поддерживать этот код собираетесь? допустим через несколько месяцев, вы уже эту магию благополучно забудете, и тут внезапно придется в этом коде поправить баг, или запилить фичу?
А если это будете не вы, а коллега?
А ведь кто-то поддерживает STL…
В Microsoft STL поддерживает мистер STL (Stephan T. Lavavej).

Добавлю к нелюбви USB. В реальном изделии необходимо заиметь свои vid/pid. Из-за этого при небольшой серии проще поставить конвертер. И разработка также упрощается.

Спасибо, упустил это в статье, это весомый аргумент, обязательно добавлю. Хотя, как я понял, есть какие-то условно-бесплатные пары для домашних проектов (хотя для домашних я бы по традиции использовал dead/beef)
А что, если рандомные взять, а не платить деньги, могут привлечь как-то?
конфликт может быть в ОС с драйверами, там же по PID\VID идентификация идет. Что мешает зайти на сайт usb.org, найти список вендоров и взять VID производителя МК, используемого вами, оттуда. А PID уже придумать свой, но взять из диапазона 0xA000 — 0xFF00. Я сомневаюсь, что он занят.

для привлечения я думаю, достаточно взять чьи-нибудь PID\VID, собрать партию в миллион миллионов штук. В итоге, Вас смогут заметить и предъявить иск в нарушении прав, потери прибыли и чем-нибудь ещё.

На гитхабе где-то была репа, где раздают vid/pid бесплатно и совершенно официально

Конкретно могу сказать про STM. Если какой то коммерческий проект, то можно сделать запрос к ним и они поделятся PID/VID. Я запрашивал, мне прислали ответ:

We STMicroelectronics, hereby agree that:

"**** Ltd." company can use the original PID: 0x**** and VID: 0x**** pending to be licensed by USB-IF of "**** Ltd. ***" and solely for the purpose of “**** Ltd. *** using STM32F303VC silicon USB device microcontrollers” This license is revocable and nontransferable and no other rights are granted except those expressly stated here.

STM для CDC устройств вполне бесплатно раздает VID и PID. Полагаю, ничто не мешает их прикрутить к любому другому устройству на основе stm32, к тому же HID, например.
Может возникнуть конфликт драйверов.
Несколько месяцев назад успешно завершил написание своего USB CDC велосипеда на регистрах, тоже использовал C++, constexpr и шаблоны, правда у меня не настолько все зашаблонизировано.
Для размещения буферов мне показалось проще определить в линкере новую секцию для PMA, дальше достаточно определить обычную переменную с атрибутом __attribute__((section(".usb_pma"), used)) и она автоматом попадет куда нужно, без дополнительных телодвижений.
Правда окончательный адрес и размер буфера для записи в регистры все равно приходится вычислять на этапе выполнения т.к. память PMA мапится на шине контроллера кусками по 2 байта которые выровнены на границу 4 байта (проще говоря с пропусками, ну во всяком случае для STM32F303VCT6), а в constexpr невозможно делить адреса переменных.
Не очень понял про память с пробелами (то есть там реально 2 байта памяти, а потом 2 байта «ничего»? А что если разыменовать, например, как 4-байтовый int, все упадёт?).
В f0 по 2 байта адресация, что тоже несколько доставило сложностей: при копировании в буфер пришлось приводить к int16 и так копировать, но еще большее неудобство при отладке — окно memory показывает только половину, пришлось создать доп.буфер, копировать каждый раз содержимое PMA в этот буфер, и уже его смотреть.
Да, все верно, я не стал экспериментировать с чтением по 4 байта т.к. других проблем хватало, поэтому что будет в этом случае не знаю, да и в даташите про USB четко написано «Dedicated packet buffer memory SRAM access scheme: 1 x 16bit / word».
Поэтому у меня так же чтение/запись по 2 байта, только инкремент указателя на 2 приходится делать. И при чтении я тоже копирую из PMA во внешний буфер, хотя это делалось больше из соображений передачи данных которые могут превышать размер буфера в PMA.
Мне очень помогла эта статья, правда она на немецком и код у них довольно сильно запутан из-за того что все в кучу намешано, но некоторые моменты с логикой работы USB действительно удалось подсмотреть там.
Каждое использование тернарного оператора было бы неплохо выделить в статический метод, с хорошим названием. Должно облегчить понимание в разы. Init просто «порадовал». Вот, по моему, это и есть самое «темное» место… Поддерживать уйму комментов в коде это очень плохая затея, в проде видел, решалось переписыванием кода.
Спасибо за совет, согласен, так будет лучше, вынесу отдельно.
Каюсь, нагородил франкенштейна, хотя делов было на 2 минуты сделать лучше. Теперь инициализация выглядит так:
static void Init()
{
    memset(reinterpret_cast<void*>(BdtBase), 0x00, BdtSize);
    (InitTxAddressFieldInDescriptor<AllEndpoints>(), ...);
    (InitRxAddressFieldInDescriptor<BidirectionalAndBulkDoubleBufferedEndpoints>(), ...);
    (InitRxCountFieldInDescriptor<RxEndpoints>(), ...);
    (InitSecondRxCountFieldInDescriptor<BidirectionalAndBulkDoubleBufferedEndpoints>(), ...);
}


Каждый из методов простой, например:
template<typename Endpoint>
static void InitRxCountFieldInDescriptor()
{
    *reinterpret_cast<uint16_t*>(BdtBase + BdtCellOffset<Endpoint> + 2) = CalculateRxCountValue<Endpoint>();
}
Добавьте в сложности:
Кривые драйвера CDC под Windows 10, теряют данные. Если поставить стандартные от STM то все пучком.
Багованные библиотеки микроконтроллера, где не учтены все тонкости USB работы что иногда приводит к дедлоку эндпоинта.
Sign up to leave a comment.

Articles