Пт мар 26, 2021 22:19:03
Я ваш код особо не смотрел. Вроде бы прием-передачу ep0 вы обрабатываете не так, как остальные endpoint'ы, а ручным вызовом функций. Тут да, особого выигрыша не будет. Но если логика обработки у них та же, что у других, может стоит все же колбэк повесить? По крайней мере я такой логикой руководствовался.8 байт размера прошивки в моём случае.
Сб мар 27, 2021 05:54:00
Сб мар 27, 2021 09:31:20
А как уменьшение количества сравнений должно увеличить код? Раз уж для каждой точки (включая 0) нужно вызывать персональный обработчик, зачем еще и нулевую отдельно обрабатывать.А потом удивляться, чего это код больше получается?
- Код:
if (istr & USB_ISTR_CTR) // Прерывание передача завершена
switch(istr & (USB_ISTR_EP_ID | USB_ISTR_DIR) )
case 0x00: // EP0 CTR_TX
...
case 0x10: // EP0 CTR_RX
if(USB->EP0R & USB_EP_SETUP)
...
else
...
case 0x01: // EP1 CTR_TX
...
case 0x11: // EP1 CTR_RX
if(ep_tx_q_cnt>65)
inline constexpr USB_CDC_VCP_CONFIGURATION_DESCRIPTOR ConfigDescriptor
RCC->AHBENR = (0?RCC_AHBENR_ADC34EN:0) |
RCC->AHBENR = (0*RCC_AHBENR_ADC34EN) |
#define EP0_SIZE_RX 0x8400 // размер буфера 64
#define EP2_TX_BUF_SIZE 64
#define EP2_RX_BUF_SIZE 64
Сб мар 27, 2021 13:21:33
if(ep_tx_q_cnt>65)
inline constexpr USB_CDC_VCP_CONFIGURATION_DESCRIPTOR ConfigDescriptor
RCC->AHBENR = (0?RCC_AHBENR_ADC34EN:0) |
RCC->AHBENR = (0*RCC_AHBENR_ADC34EN) |
(2*RCC_AHBENR_ADC34EN)
#define EP0_SIZE_RX 0x8400 // размер буфера 64
#define EP2_TX_BUF_SIZE 64
#define EP2_RX_BUF_SIZE 64
Сб мар 27, 2021 18:44:01
Два сравнения или выбор адреса из таблицы, переход по этому адресу, возврат и сама таблица. Надеювь, таблица хоть константна?
if(USB->ISTR & USB_ISTR_CTR){
uint8_t epnum = USB->ISTR & USB_ISTR_EP_ID;
if(USB_EPx(epnum) & USB_EP_CTR_RX){ //OUT
epfunc_out[epnum](epnum);
ENDP_CTR_RX_CLR(epnum);
}
if(USB_EPx(epnum) & USB_EP_CTR_TX){//IN
epfunc_in[epnum](epnum | 0x80);
ENDP_CTR_TX_CLR(epnum);
}
}
Я тестировал все типы конечных точек, так что Custom-HID, MSD, keyboard+mouse, Audio, CDC. Обязательно буду тестировать составные устройства, так что точек будет много и неизвестно сколько.Можно подумать, каждый день количество конечных точек меняете. Сколько классов вы лично сделали? Я штуки 4. Больше двух точек сверх ep0 ни разу не использовал.
Из комментариев к коду мне показалось что это отправка через ep0 пакета больше 64 байт. Естественно, у меня такое есть, только ZLP обозначается специальным указателем ZLPP. Собственно, такого не может не быть, поскольку для ep0 допустим размер 8 байт, а туда не то что Configuration или HID, но даже DeviceDescriptor не влезет.Про многие места это неправда. Конкретно 65 касается очереди отправки в ep0, которой у вас вообще нет. Размер ep0 меняться никогда не будет.
Ну вам же надо сначала создать структуру, а потом заполнить ее значениями. И для нового класса делать и то и другое снова. Не похоже чтобы это добавляло какой-то безопасности по сравнению с байтовым массивом.Зачем в двух местап. Структура определена типом и никогда не меняется. Это определяется классом. А вот в месте её заполнения компилятор мне будет бить по рукам, если я формат нарушу.
И я предпочитаю в одном месте видеть что затактировано, а что нет, чем рыскать по коду. Плюс это одна запись в регистр на всю прошивку - экономия.
Без RM правильность ни первого, ни второго проверить невозможно, так что разницы никакой.
usb_epdata[epnum].usb_rx_addr = lastaddr;
if(size < 64){
usb_epdata[epnum].rx_blocksize = 0;
usb_epdata[epnum].rx_num_blocks = size / 2;
}else{
usb_epdata[epnum].rx_blocksize = 1;
if(size < 32)size = 32;
usb_epdata[epnum].rx_num_blocks = size / 32 - 1;
}
epfunc_out[epnum] = func;
ENDP_STAT_RX(epnum, USB_EP_RX_VALID);
Тогда зачем настраивать точки?Кто-то запрещает не использовать буфер?
Си-шный код читать еще проще.Оно и так работает хорошо и ещё читаемо неподготовленным форумчанином.
Вс мар 28, 2021 00:26:42
if(USB->ISTR & USB_ISTR_CTR){
uint8_t epnum = USB->ISTR & USB_ISTR_EP_ID;
if(USB_EPx(epnum) & USB_EP_CTR_RX){ //OUT
epfunc_out[epnum](epnum);
ENDP_CTR_RX_CLR(epnum);
}
if(USB_EPx(epnum) & USB_EP_CTR_TX){//IN
epfunc_in[epnum](epnum | 0x80);
ENDP_CTR_TX_CLR(epnum);
}
}
Вс мар 28, 2021 09:30:20
То есть для двух-трех точек ваш вариант быстрее, для более чем 5 - мой.С несколькими конечными точками это проигрывает по коду. Не забываем, что я в классах, у меня всё инлайнится только в путь.
На самом деле, всё хуже чем я думал. Много лишних чтений регистров. И зачем передавать в обработчик номер точки, если он в таблице однозначно к ней привязан.
А вот это надо проверить.Применение uint8_t на 32-битном контроллере там где можно лучше избегать - могут порождаться лишние инструкуции усечения разрядности.
Если задать размер ep8 в 8 байт, то 64 за раз туда писать нельзя.Да ладно. 64 только в путь.
Да, но он специфичен для каждого устройства, да еще и размер меняться может. Более того, для половины полей там заданы константы, а еще четверть менять просто смысла нет (вроде того же потребления от шины или частоты опроса).Формат дескрипторов определён спецификацией.
Зато при формировании самой структуры ошибиться проще. А менять их приходится при одних и тех же условиях (изменение типа устройства, эксперименты с ним).Заполнить типизированную структуру без ошибок куда проще, чем какой-то байтовый массив сформировать.
То есть вы тратите время на поиск настроек по разным местам именно потому что вам это не нравится?!Для меня пустая трата времени - рыскать по коду чтобы найти что включено, а что нет.
Это проще с архитектурной точки зрения: меньше дробления одного функционала по разным файлам. А то, что тратится 2 байта оперативки и несколько тактов процессора - это делается единственный раз при инициализации, не то место, где надо такты экономить.Зачем тащить в рантайм то что считается в компайлтайме? Это же всё константные данные.
Вы же смотрели мой код? Там не так уж много макросов, и названия им вроде бы даны разумные.Это действительно так, пока вы его макросами не обложили.
ради чего? В С++ натащили слишком много всякого разного, причем бессистемно. ИМХО ему совместимость с Си слишком мешает.Изучите С++
"Типизировать данные" это вы про константые байтовые массивы, которые программа даже не обрабатывает? Обратите внимание, там везде комментарии проставлены, безопасности они добавляют не меньше прямой типизации. И при этом гораздо проще изменяется. Не знаю как вам, а мне сейчас, пока осваиваю USB, это важно.Во-первых, это не про мой код. Во-вторых, какие-то двойные стандарты. Типизировать данные ни-ни, а константы будь добр обозвать? Ну вот захотел я увидеть значение 65 в этом месте - имею право.
Вс мар 28, 2021 10:13:25
Вс мар 28, 2021 10:51:26
Вс мар 28, 2021 11:41:38
А как иначе обработчик определит номер точки, которую он обрабатывает?Это никак не связано.
Такое ощущение что вы что-то себе придумали и теперь героически опровергаете.Это каким боком к моему коду относится? Эта очередь только для ep0, у неё размер 64 байта. Где я в какую-то гипотетическую 8-байтовую ep8 писал 64 байта?
Как обычно - пропустить какое-то поле. Вы ведь именно это имели в виду под "ошибиться при заполнении байтового массива"? Потому что по-другому ошибиться там сложно.Не согласен. Как можно ошибиться при описании структуры перенося её из спецификации? В спецификации всё до имён полей прописано.
Как я и опасался - вы что-то себе придумали и начали это опровергать. Эта ветка началась со сравнения вашего кода с моим, где для ep0 можно выставить любой размер.Это каким боком к моему коду относится? Эта очередь только для ep0, у неё размер 64 байта.
Ну вот чтобы смотреть настройку какого-нибудь SPI вам придется смотреть не только условный spi.cpp, но плюс к этому еще SystemInit. И в main.cpp не забыть поправить когда добавляете использование новой периферии.Что? Я всегда знаю где посмотреть тактирование каких устройств включено, а каких нет. В функции SystemInit сразу за настройкой PLL
То есть по-вашему "Кстати, это одна из причин почему разница объема меня не пугает." это "удивляетесь почему размер прошивки больше"? Я ведь сразу сказал что разница в десяток байт объема кода - не то, что меня волнует.У вас это уже не первое "не то место где надо такты экономить". Почему тогда удивляетесь, что размер прошивки больше?
Макросы, макросы, макросы... Вот она жесть Си. На С++ можно в компайлтайме очень много чего сделать, чего макросы никогда не сделают.
И, вот так незадача, наткнуться на комментарий, который это описывает. Опять же, если знаете альтернативу лучше - почему сами ей не воспользовались? Естественно, дублирование кода лучшей альтернативной не является.Да ладно. Что делает sizeof знает любой программист и видит это прямо тут и сейчас. Что делает наркоманский ARRLENx надо лезть смотреть/проверять.
Ну и написание в заголовочном файла кода иначе чем косяками С++ не объяснить.
Ну что за бред? Где как не в заголовочном файле описывать типы?
Ну вот и я говорю, что ваша "типизация" особо безопасности не добавляет.Скажите честно, вы С++ знаете?[/quoe]Нет. Знаю как создавать примитивные классы, но на этом все. Глубже копать желания нет, потому что см. выше про неструктурированную кучу парадигм.Комментарий, в отличии от компилятора, никогда не найдёт вам ошибку.
0xFF, //iManufacturer
.iManufacturer = 0xFF,
А тут ваш подход только мешает: ошибиться можно не в одном месте, а в двух. И для изменения тоже лазить в два места. И эта иллюзия защиты на практике мешает больше, чем если сразу сказано, что вся ответственность на программисте.пока осваиваю USB, это важно.
Тем более, когда код постоянно меняется, постоянный контроль ошибок только помогает.
Идеи на счет буферизации ISTR или EPnR выглядят интересно, стоит проверить. Но по большей части ваши возражения основаны на том, что вы мой код толком не смотрели.Я разве запрещаю? Наоборот, пытаюсь помочь<...> Не надо на меня обижаться.
Вс мар 28, 2021 12:35:38
Вс мар 28, 2021 13:21:52
Еще раз: если один обработчик используется для нескольких точек, как он отличит какую именно ему подсунули? Да хоть для тех же сдвоенных UART'ов.Его положение в таблице известно на этапе компиляции, значит и номер известен. Зачем его в рантайме по регистрам гонять?
Вот только там точная структура далеко не всегда написана. И, опять же, особой разницы нет скопировать ли структуру в массив байтов или в структуру, а инициализацию наложить после.Даже обезьянку можно научить копировать структуры прямо из спецификации прямо так как там написано.
Я у вас вижу отдельно объявление структуры, отдельно инициализацию. Или тут как с EP2, было лень удалять после тестов? Хорошо, какой из этих двух этапов можно безболезненно удалить чтобы не настраивать одно и то же в двух местах?Не придумывайте, нет у меня никакого дублирования кода.
Не вижу я у вас решения лучше. Костыль с объявлением структуры - вижу, решения нет.Воспользовался. sizeof от типа структуры самое понятное и надёжное решение.
А в ваше варианте что? Реализация функций.
Где??? Это шаблонный класс, читай тип. Его данные и методы работы с ними.
А, то есть вы сторонник все писать в одном общем файле. Тогда вопросов не имею.Это вы сами так решили или кто-то подсказал?
Расскажите сначала как заинлайнить функцию из другой библиотеки. Единица трансляции на то и единица трансляции что не предполагает влезание во внутренности.Может всё таки расскажете как заинлайнить функцию из другой единицы трансляции?
Скорее напоминает фанатов scanf_s и прочих псевдо-безопасных функций. Которые вроде бы говорят "используй нас, мы безопасные" а на практике оказываются ничуть не безопаснее того же scanf'а, а то и хуже - за счет меньшего тестирования.И эта иллюзия защиты на практике мешает больше, чем если сразу сказано, что вся ответственность на программисте.
Что-то обезьяну с гранатой напоминает.
То есть принимать конструктивную критику к сведению не хотите. Ваше право, не буду настаивать.Вы мой код тоже, похоже, не смотрели. Иначе не могли не заметить как всё обложено перечислениями.
Вс мар 28, 2021 13:40:44
Вс мар 28, 2021 14:37:41
struct TEST
{
static inline void Foo() { __NOP(); }
};
int main()
{
TEST::Foo();
for(;;);
}
//int main()
//{
// TEST::Foo();
main:
Nop
// for(;;);
??main_0:
B.N ??main_0
//}
struct TEST
{
static inline constexpr uint32_t CountSetBits(uint32_t value)
{
uint32_t bits = 0;
while (value)
{
value &= value - 1;
bits++;
}
return bits;
}
};
int main()
{
volatile uint32_t x = TEST::CountSetBits(0xAA);
for(;;);
}
//int main()
//{
main:
SUB SP,SP,#+4
// volatile uint32_t x = TEST::CountSetBits(0xAA);
MOVS R0,#+4
STR R0,[SP, #+0]
// for(;;);
??main_0:
B.N ??main_0
//}
Вс мар 28, 2021 14:51:41
Вс мар 28, 2021 16:41:05
... не компилируется. Как и ожидалось. Ну либо приведите полный код всех файлов.Легко и непринуждённо. Допустим, это библиотека
Все 10000 или сколько их там, включая с расширяемой структурой (те же аудио) и составные?Структуры всех дескрипторов как общие для всех, так и специфичные для классов лежат себе в заголовочном файле
Я не задавал вопроса. Я указал вам, что это ограничение языка. И ограничение Си выглядит более логичным, чем С++.Вы сами ответили на свой вопрос.
Вс мар 28, 2021 18:40:46
Вс мар 28, 2021 20:20:56
Речь шла о разных единицах трансляции. Попросту говоря - разных файлах. Инлайнить в общем файла (в том числе из заголовочников, естественно) все умеют.Это даже не смешно. Это и есть полный код, который надо компилировать С++ компилятором.
Конечно. Слить воедино три дескриптора, скопипащенных из других проектов гораздо проще, чем заранее писать и тестировать 27 разных вариантов. А если готовых проектов элементарных устройств нет, их так и так делать. Мало ли какие подводные камни встретятся.А альтернатива? Тупо по спецификации?
Так я в С++ и не лезу. И сразу объясняю почему - потому что С++ объективно сложнее Си. В частности, плюсовик всегда может прочитать код Си-шника, а вот обратное неверно.Я думаю, что вы не можете объективно об этом судить, не зная обоих языков сразу.
Вс мар 28, 2021 21:19:21
inline constexpr USB_DEVICE_DESCRIPTOR DeviceDescriptor =
{
.bLength = sizeof(DeviceDescriptor), // размер дескриптора
.bDescriptorType = DescriptorType::DEVICE,
.bcdUSB = 0x0200, // usb 2.0
.bDeviceClass = 0x00, // класс описан в дескрипторе интерфейса
.bDeviceSubClass = 0x00, // подкласс описан в дескрипторе интерфейса
.bDeviceProtocol = 0x00, // протокол описан в дескрипторе интерфейса
.bMaxPacketSize0 = 0x40, // 64
.idVendor = 0x0483, // VID
.idProduct = 0x572A, // PID
.bcdDevice = 0x0200, // ревизия
.iManufacturer = 0x01, // индекс строки с названием производителя
.iProduct = 0x02, // индекс строки с названием устройства
.iSerialNumber = 0x03, // индекс строки с серийным номером устройства
.bNumConfigurations = 0x01 // количество поддерживаемых конфигураций
};
Вс мар 28, 2021 22:31:04
Вообще-то, по умолчанию библиотека это "динамическая библиотека" *.so (*.dll) или в крайнем случае статическая *.a (*.lib) и уже в последнюю очередь "любой набор файлов, который можно куда-то подключить".На что я вам показал как элементарно из библиотеки инлайнится. Просто потому что библиотека находится в заголовочном файле
см. выше: библиотека это скомпилированный файл.Только полноценную библиотеку в заголовочном файле на С++ написать можно и нужно, а на Си фиг вам.
Скажите, вы кого пытаетесь обмануть?Да уж, заметно. Ещё на этой странице темы вы "лечили", что нельзя код в заголовочном файле писать, да типы структур порывались удалять.
Я не знаю зачем вы столь упорно мусолите эту тему.Но вот почему-то порассуждать о недостатках это завсегда пожалуйста.