Кто любит RISC в жизни, заходим, не стесняемся.
Ответить

Re: STM32 и USB (практика)

Пт мар 26, 2021 22:19:03

Колбэк у меня вызывается в любом случае. Разница в том общий он будет или нет.
То есть можно пожертвовать небольшим количеством флешки и написать альтернативную функцию, которая начнет вызываться вместо первой. То есть там не будет ни проверки, ни присвоения, ни битовой магии.
8 байт размера прошивки в моём случае.
Я ваш код особо не смотрел. Вроде бы прием-передачу ep0 вы обрабатываете не так, как остальные endpoint'ы, а ручным вызовом функций. Тут да, особого выигрыша не будет. Но если логика обработки у них та же, что у других, может стоит все же колбэк повесить? По крайней мере я такой логикой руководствовался.

Re: STM32 и USB (практика)

Сб мар 27, 2021 05:54:00

может стоит все же колбэк повесить?
А потом удивляться, чего это код больше получается? Так и до HAL с его пятикратной разницей можно распухнуть. EP0 исключительная благодаря спецификации USB и обработка у неё исключительная всегда. Её обработчик это метод класса, написанный в заголовочном файле, а значит полностью заинлайнится в тело обработки прерывания. Выбрать свитчом из пары точек быстрее, чем сходить в колбэк.

У меня они на начальном этапе тоже были виртуальными, что фактически те же колбэки, вызываемые через таблицу виртуальных методов, только не вручную, а компилятором автоматически. Но ведь класс usb-устройства определён и по ходу работы не меняется. Состав всех EP и их обработчиков известен на этапе компиляции. Никакая виртуализация тут не нужна и только мешает компилятору оптимизировать. В обработчике сетап запросов я всё же применил чистую виртуальную функцию, чтобы вынести обработку зависящих от реализуемого usb-класса запросов на верхний слой абстакции. Но даже её умные компиляторы встраивают, а не вызывают через таблицу виртуальных методов, так как видят на этапе компиляции, что она единственно возможная. Хотя, некоторые компиляторы и пытаются соорудить таблицу виртуальных методов, я считаю это приемлемой платой за разделение кода на уровни и возможность пименять один и тот же алгоритм энумерации для разных usb-классов и разных контроллеров.

Собственно я в качестве примера и выложил два разных класса, чтобы показать как происходит разбиение на слои. Точно так же легко я подменяю код "классического" stm32-usb на код контроллера с usb otg, да и любого другого контроллера вообще. Это что-то типа "семиуровневой модели osi", только в стеке usb.

Рекомендую, так же посмотреть мой обработчик сетап-запросов. Там всё гораздо проще. Что-то у вас много "судорога" в этом месте. Не бойтесь свитчей и циклов, компиляторы умеют их оптимизировать куда лучше программиста. Например, мой цикл обработки строковых дескрипторов компилятор запросто разворачивает, факитически превращая в свитч.

Re: STM32 и USB (практика)

Сб мар 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

Я правильно понимаю, что для добавления новых конечных точек придется лезть в ядро и дописывать stm32_usb.c?
if(ep_tx_q_cnt>65)

У вас во многих местах используются магические константы. Но ведь и EP0_RX_BUF_SIZE и другие именованные константы тоже есть. Хорошо бы их как-то объединить.
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

Запись вот этой штуки мне не нравится абсолютно. Там нет даже комментария что 0x8400 это набор битовых полей (1<<15) | ( ((size/32)-1)<<10)
#define EP2_TX_BUF_SIZE 64
#define EP2_RX_BUF_SIZE 64

Настройку вижу. А обработчика не нашел.

Re: STM32 и USB (практика)

Сб мар 27, 2021 13:21:33

А как уменьшение количества сравнений должно увеличить код?
Два сравнения или выбор адреса из таблицы, переход по этому адресу, возврат и сама таблица. Надеювь, таблица хоть константна?

Я правильно понимаю, что для добавления новых конечных точек придется лезть в ядро и дописывать stm32_usb.c?
Ядро - громко сказано. Можно подумать, каждый день количество конечных точек меняете. Сколько классов вы лично сделали? Я штуки 4. Больше двух точек сверх ep0 ни разу не использовал.

if(ep_tx_q_cnt>65)

У вас во многих местах используются магические константы. Но ведь и EP0_RX_BUF_SIZE и другие именованные константы тоже есть. Хорошо бы их как-то объединить.
Про многие места это неправда. Конкретно 65 касается очереди отправки в ep0, которой у вас вообще нет. Размер ep0 меняться никогда не будет. 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

Запись вот этой штуки мне не нравится абсолютно. Там нет даже комментария что 0x8400 это набор битовых полей (1<<15) | ( ((size/32)-1)<<10)
Без RM правильность ни первого, ни второго проверить невозможно, так что разницы никакой.


#define EP2_TX_BUF_SIZE 64
#define EP2_RX_BUF_SIZE 64

Настройку вижу. А обработчика не нашел.
Кто-то запрещает не использовать буфер?

В принципе, я могу обвешать этот код шаблонами по самое небалуйся. Только зачем? Я этот код часто показываю на форуме, где знатоков С++ по пальцам пересчитать. Оно и так работает хорошо и ещё читаемо неподготовленным форумчанином.

Re: STM32 и USB (практика)

Сб мар 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);
    }
  }

Можно подумать, каждый день количество конечных точек меняете. Сколько классов вы лично сделали? Я штуки 4. Больше двух точек сверх ep0 ни разу не использовал.
Я тестировал все типы конечных точек, так что Custom-HID, MSD, keyboard+mouse, Audio, CDC. Обязательно буду тестировать составные устройства, так что точек будет много и неизвестно сколько.
Про многие места это неправда. Конкретно 65 касается очереди отправки в ep0, которой у вас вообще нет. Размер ep0 меняться никогда не будет.
Из комментариев к коду мне показалось что это отправка через ep0 пакета больше 64 байт. Естественно, у меня такое есть, только ZLP обозначается специальным указателем ZLPP. Собственно, такого не может не быть, поскольку для ep0 допустим размер 8 байт, а туда не то что Configuration или HID, но даже DeviceDescriptor не влезет.
Зачем в двух местап. Структура определена типом и никогда не меняется. Это определяется классом. А вот в месте её заполнения компилятор мне будет бить по рукам, если я формат нарушу.
Ну вам же надо сначала создать структуру, а потом заполнить ее значениями. И для нового класса делать и то и другое снова. Не похоже чтобы это добавляло какой-то безопасности по сравнению с байтовым массивом.
И я предпочитаю в одном месте видеть что затактировано, а что нет, чем рыскать по коду. Плюс это одна запись в регистр на всю прошивку - экономия.

Ну, это на любителя. Я предпочитаю включать тактирование периферии именно там, где периферия настраивается.
А экономия на инициализации - пустая трата времени. Этот код выполняется единственный раз.
Без 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);

Кто-то запрещает не использовать буфер?
Тогда зачем настраивать точки?
Оно и так работает хорошо и ещё читаемо неподготовленным форумчанином.
Си-шный код читать еще проще.
Ну и пренебрегать имнованными константами все же не стоит, читаемость они повышают.

Re: STM32 и USB (практика)

Вс мар 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);
    }
  }
На самом деле, всё хуже чем я думал. Много лишних чтений регистров. И зачем передавать в обработчик номер точки, если он в таблице однозначно к ней привязан. Применение uint8_t на 32-битном контроллере там где можно лучше избегать - могут порождаться лишние инструкуции усечения разрядности. Это же прерывание, тут чем быстрее, тем лучше.

поскольку для ep0 допустим размер 8 байт, а туда не то что Configuration или HID, но даже DeviceDescriptor не влезет.
Да ладно. 64 только в путь.

Ну вам же надо сначала создать структуру, а потом заполнить ее значениями. И для нового класса делать и то и другое снова. Не похоже чтобы это добавляло какой-то безопасности по сравнению с байтовым массивом.
Формат дескрипторов определён спецификацией. Описать формат данных как тип это самое обычное дело в программировании. Положил в заголовочный файл и пользуешься по необходимости. Заполнить типизированную структуру без ошибок куда проще, чем какой-то байтовый массив сформировать. Байтом больше, байтом меньше, съехали поля или нет - хрен его знает. А так всё по строго определённым полям разложено. Определить тип(класс) и создать объект этого типа - обычная практика программирования. Наличие типа, описывающего формат данных, всегда лучше его отсутствия. Не понимаю, почему такие элементарные вещи вызывают вопросы.

И для нового класса делать и то и другое снова.
Там этих дескрипторов вагон и тележка, одним больше, одним меньше. Определил тип, положил в заголовочный файл и пусть себе лежит, хоть для всех классов разом, жрать не просит. Когда надо воспользовался.


Ну, это на любителя. Я предпочитаю включать тактирование периферии именно там, где периферия настраивается.
Я разве против? А я предпочитаю периферию настраивать в одном месте. Ответите взаимностью?

А экономия на инициализации - пустая трата времени. Этот код выполняется единственный раз.
Для меня пустая трата времени - рыскать по коду чтобы найти что включено, а что нет. Экономия в размере кода это лишь побочный эффект моего подхода. Запись одной константы в регистр против множества RMW. Но опять же, я не против вашего подхода, знаю много программистов, делающих так же. Хватает флэша, да и ладно.

В моем коде так вообще в рантайме настраивается:
Зачем тащить в рантайм то что считается в компайлтайме? Это же всё константные данные.

Тогда зачем настраивать точки?
Я не настраиваю точки. Это всего одна лишняя запись в таблице дескрипторов буферов. Проблема тут лишь в том, что не дошли руки эту запись под constexpr утащить. Понимаете, я хоть и люблю С++, но переписывать на него те места, где не получишь выигрыша в чём либо, особого смысла нет. А этот кусок был изначально на Си написан.

Си-шный код читать еще проще.
Это действительно так, пока вы его макросами не обложили. Только обусловлено это сильно ограниченными возможностям языка по сравнению с С++. А расширенные возможности требуют расширенного синтаксиса. Почему-то Си программисты поголовно считают, что должны понимать код на языке, которого не знают. Изучите С++ и тоже будете понимать.

Ну и пренебрегать имнованными константами все же не стоит, читаемость они повышают.
Во-первых, это не про мой код. Во-вторых, какие-то двойные стандарты. Типизировать данные ни-ни, а константы будь добр обозвать? Ну вот захотел я увидеть значение 65 в этом месте - имею право.

Re: STM32 и USB (практика)

Вс мар 28, 2021 09:30:20

С несколькими конечными точками это проигрывает по коду. Не забываем, что я в классах, у меня всё инлайнится только в путь.
То есть для двух-трех точек ваш вариант быстрее, для более чем 5 - мой.
На самом деле, всё хуже чем я думал. Много лишних чтений регистров. И зачем передавать в обработчик номер точки, если он в таблице однозначно к ней привязан.

Так уж и много?
Чтобы можно было один обработчик к нескольким точкам привязать.
Применение uint8_t на 32-битном контроллере там где можно лучше избегать - могут порождаться лишние инструкуции усечения разрядности.
А вот это надо проверить.
Да ладно. 64 только в путь.
Если задать размер ep8 в 8 байт, то 64 за раз туда писать нельзя.
Формат дескрипторов определён спецификацией.
Да, но он специфичен для каждого устройства, да еще и размер меняться может. Более того, для половины полей там заданы константы, а еще четверть менять просто смысла нет (вроде того же потребления от шины или частоты опроса).
Опять напоминаю про аудио устройства: там путь прохождения сигнала можно здорово изменить, и все это прописывается в дескрипторе.
Заполнить типизированную структуру без ошибок куда проще, чем какой-то байтовый массив сформировать.
Зато при формировании самой структуры ошибиться проще. А менять их приходится при одних и тех же условиях (изменение типа устройства, эксперименты с ним).
Для меня пустая трата времени - рыскать по коду чтобы найти что включено, а что нет.
То есть вы тратите время на поиск настроек по разным местам именно потому что вам это не нравится?!
Зачем тащить в рантайм то что считается в компайлтайме? Это же всё константные данные.
Это проще с архитектурной точки зрения: меньше дробления одного функционала по разным файлам. А то, что тратится 2 байта оперативки и несколько тактов процессора - это делается единственный раз при инициализации, не то место, где надо такты экономить.
Хотя может и напишу альтернативный вариант с настройками на макросах.
Это действительно так, пока вы его макросами не обложили.
Вы же смотрели мой код? Там не так уж много макросов, и названия им вроде бы даны разумные.
Тот же ARRLENx как минимум удобнее sizeof.
Ну и написание в заголовочном файла кода иначе чем косяками С++ не объяснить.
Изучите С++
ради чего? В С++ натащили слишком много всякого разного, причем бессистемно. ИМХО ему совместимость с Си слишком мешает.
Во-первых, это не про мой код. Во-вторых, какие-то двойные стандарты. Типизировать данные ни-ни, а константы будь добр обозвать? Ну вот захотел я увидеть значение 65 в этом месте - имею право.
"Типизировать данные" это вы про константые байтовые массивы, которые программа даже не обрабатывает? Обратите внимание, там везде комментарии проставлены, безопасности они добавляют не меньше прямой типизации. И при этом гораздо проще изменяется. Не знаю как вам, а мне сейчас, пока осваиваю USB, это важно.
Как и возможность настройки как можно большего числа параметров, включая размер ep0.

Re: STM32 и USB (практика)

Вс мар 28, 2021 10:13:25

ради чего? В С++ натащили слишком много всякого разного, причем бессистемно.

Это в С не хватает много всякого разного и ничего с этим не поделаешь, а в C++ из всего многообразия дополнительных возможностей можно использовать только то, что нравится.

Re: STM32 и USB (практика)

Вс мар 28, 2021 10:51:26

То есть для двух-трех точек ваш вариант быстрее, для более чем 5 - мой.
В целом да, но не всё так однозначно. Можно наиболее интенсивно нагруженные вызовы поставить в начало проверки и они будут вызываться быстрее. Именно там где это особенно надо.

Так уж и много?
Посчитаем? Только на приведённом вами фрагменте кода. USB->ISTR читается дважды, на ресете и вэйкапах ещё раза три? EPnR читается два раза. Плюс RMW на очистке CTR_RX и внутри обработчика должен быть RMW для установки RX_VALID. Оба RMW легко объединить.

Чтобы можно было один обработчик к нескольким точкам привязать.
Это никак не связано.

А вот это надо проверить.
А чего там проверять. Параметры в функцию передаются через 32-разрядный регистр. Его надо на входе усечь. Оптимизатор, конечно, может увидеть, что при вызове вы маску не выходящую за 8 бит накладываете, а может и не увидеть. Как звёзды лягут, одним словом.

Если задать размер ep8 в 8 байт, то 64 за раз туда писать нельзя.
Это каким боком к моему коду относится? Эта очередь только для ep0, у неё размер 64 байта. Где я в какую-то гипотетическую 8-байтовую ep8 писал 64 байта? Фантазии какие-то. Очереди для ep1 и выше для каждого класса надо делать по разному.

Зато при формировании самой структуры ошибиться проще.
Не согласен. Как можно ошибиться при описании структуры перенося её из спецификации? В спецификации всё до имён полей прописано.

То есть вы тратите время на поиск настроек по разным местам именно потому что вам это не нравится?!
Что? Я всегда знаю где посмотреть тактирование каких устройств включено, а каких нет. В функции SystemInit сразу за настройкой PLL. В любой моей прошивке. А сразу за тактированием настройка GPIO. Это вам надо лазить по разным местам, зачем своих тараканов мне приписывать?

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

Хотя может и напишу альтернативный вариант с настройками на макросах.
Макросы, макросы, макросы... Вот она жесть Си. На С++ можно в компайлтайме очень много чего сделать, чего макросы никогда не сделают.

Тот же ARRLENx как минимум удобнее sizeof.
Да ладно. Что делает sizeof знает любой программист и видит это прямо тут и сейчас. Что делает наркоманский ARRLENx надо лезть смотреть/проверять.

Ну и написание в заголовочном файла кода иначе чем косяками С++ не объяснить.
Ну что за бред? Где как не в заголовочном файле описывать типы? А описание класса это и есть создание типа. При том что вполне может оказаться, что код будет выполняться вообще в компайлтайме. А если это шаблон, то кода до специализации вообще не существует. И инлайнится такой код на раз два. Может расскажете что нужно сделать на Си, чтобы заинлайнить функцию из другой единицы трансляции.

ради чего? В С++ натащили слишком много всякого разного, причем бессистемно. ИМХО ему совместимость с Си слишком мешает.
Скажите честно, вы С++ знаете? А то почему-то так красочно о недостатках С++ рассказывают те, кто его не знают. А вот достоинства можно оценить, только изучив. Достоинства многократно перевешивают сложность.

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

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

Как и возможность настройки как можно большего числа параметров, включая размер ep0.
Я разве запрещаю? Наоборот, пытаюсь помочь, подкидывая непривычные для вас идеи. В споре рождается истина. Не надо на меня обижаться. Мне нравится как вы накинулись на USB, респект!

Re: STM32 и USB (практика)

Вс мар 28, 2021 11:41:38

Это никак не связано.
А как иначе обработчик определит номер точки, которую он обрабатывает?
Это каким боком к моему коду относится? Эта очередь только для ep0, у неё размер 64 байта. Где я в какую-то гипотетическую 8-байтовую ep8 писал 64 байта?
Такое ощущение что вы что-то себе придумали и теперь героически опровергаете.
Не согласен. Как можно ошибиться при описании структуры перенося её из спецификации? В спецификации всё до имён полей прописано.
Как обычно - пропустить какое-то поле. Вы ведь именно это имели в виду под "ошибиться при заполнении байтового массива"? Потому что по-другому ошибиться там сложно.
Это каким боком к моему коду относится? Эта очередь только для ep0, у неё размер 64 байта.
Как я и опасался - вы что-то себе придумали и начали это опровергать. Эта ветка началась со сравнения вашего кода с моим, где для ep0 можно выставить любой размер.
Что? Я всегда знаю где посмотреть тактирование каких устройств включено, а каких нет. В функции SystemInit сразу за настройкой PLL
Ну вот чтобы смотреть настройку какого-нибудь SPI вам придется смотреть не только условный spi.cpp, но плюс к этому еще SystemInit. И в main.cpp не забыть поправить когда добавляете использование новой периферии.
У вас это уже не первое "не то место где надо такты экономить". Почему тогда удивляетесь, что размер прошивки больше?
То есть по-вашему "Кстати, это одна из причин почему разница объема меня не пугает." это "удивляетесь почему размер прошивки больше"? Я ведь сразу сказал что разница в десяток байт объема кода - не то, что меня волнует.
Макросы, макросы, макросы... Вот она жесть Си. На С++ можно в компайлтайме очень много чего сделать, чего макросы никогда не сделают.

Вот только в реальной жизни я такого не видел - только говорят что можно. А потом получается либо дублирование кода, как у вас, либо нечитаемый ужас на шаблонах.
Да ладно. Что делает sizeof знает любой программист и видит это прямо тут и сейчас. Что делает наркоманский ARRLENx надо лезть смотреть/проверять.
И, вот так незадача, наткнуться на комментарий, который это описывает. Опять же, если знаете альтернативу лучше - почему сами ей не воспользовались? Естественно, дублирование кода лучшей альтернативной не является.
Ну и написание в заголовочном файла кода иначе чем косяками С++ не объяснить.

Ну что за бред? Где как не в заголовочном файле описывать типы?

А в ваше варианте что? Реализация функций. Вот этого в заголовочных файлах быть не должно.
Скажите честно, вы С++ знаете?[/quoe]Нет. Знаю как создавать примитивные классы, но на этом все. Глубже копать желания нет, потому что см. выше про неструктурированную кучу парадигм.
Комментарий, в отличии от компилятора, никогда не найдёт вам ошибку.
Ну вот и я говорю, что ваша "типизация" особо безопасности не добавляет.
Код:
0xFF, //iManufacturer
.iManufacturer      = 0xFF,

Никакой разницы где ошибаться. Ну хорошо, в 16- или 24-битных полях немножко безопасности добавляется. Но необходимость редактировать в двух местах синхронно это с успехом перебивает.
пока осваиваю USB, это важно.

Тем более, когда код постоянно меняется, постоянный контроль ошибок только помогает.
А тут ваш подход только мешает: ошибиться можно не в одном месте, а в двух. И для изменения тоже лазить в два места. И эта иллюзия защиты на практике мешает больше, чем если сразу сказано, что вся ответственность на программисте.
Я разве запрещаю? Наоборот, пытаюсь помочь<...> Не надо на меня обижаться.
Идеи на счет буферизации ISTR или EPnR выглядят интересно, стоит проверить. Но по большей части ваши возражения основаны на том, что вы мой код толком не смотрели.
Обижаться на конструктивные комментарии? Если так делать, никуда не продвинешься.
Кстати, вам бы тоже следовало об этом подумать. Та же замена магических констант на именованные по сути ничего не стоит, но восприятие кода и гибкость улучшит.

Re: STM32 и USB (практика)

Вс мар 28, 2021 12:35:38

А как иначе обработчик определит номер точки, которую он обрабатывает?
Его положение в таблице известно на этапе компиляции, значит и номер известен. Зачем его в рантайме по регистрам гонять?

Как обычно - пропустить какое-то поле.
Даже обезьянку можно научить копировать структуры прямо из спецификации прямо так как там написано.


А потом получается либо дублирование кода, как у вас, либо нечитаемый ужас на шаблонах.
Не придумывайте, нет у меня никакого дублирования кода. Вы что, раньше определения типа структуры никогда не видели? А нечитаемость шаблонов это только от незнания вами языка. Я вот покитайски не умею читать. Но это же моя проблема, а не китайского, правда?

Опять же, если знаете альтернативу лучше - почему сами ей не воспользовались?
Воспользовался. sizeof от типа структуры самое понятное и надёжное решение.

Естественно, дублирование кода лучшей альтернативной не является.
Это у вас какая-то навязчивая идея. Нет никакого дублирования кода.


А в ваше варианте что? Реализация функций.
Где??? Это шаблонный класс, читай тип. Его данные и методы работы с ними. Он не порождает кода. До момента инстанциации. Либо вообще не пораждает, если выполняется в constexpr контексте.

Вот этого в заголовочных файлах быть не должно.
Это вы сами так решили или кто-то подсказал? Такой подход даёт очень много плюсов. Может всё таки расскажете как заинлайнить функцию из другой единицы трансляции?

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

Кстати, вам бы тоже следовало об этом подумать. Та же замена магических констант на именованные по сути ничего не стоит, но восприятие кода и гибкость улучшит.
Вы мой код тоже, похоже, не смотрели. Иначе не могли не заметить как всё обложено перечислениями. Причём не просто Cи-шный enum, который ничего не ограничивает, а enum class. Строгое типизирование. И т.д.

Re: STM32 и USB (практика)

Вс мар 28, 2021 13:21:52

Его положение в таблице известно на этапе компиляции, значит и номер известен. Зачем его в рантайме по регистрам гонять?
Еще раз: если один обработчик используется для нескольких точек, как он отличит какую именно ему подсунули? Да хоть для тех же сдвоенных UART'ов.
Даже обезьянку можно научить копировать структуры прямо из спецификации прямо так как там написано.
Вот только там точная структура далеко не всегда написана. И, опять же, особой разницы нет скопировать ли структуру в массив байтов или в структуру, а инициализацию наложить после.
Не придумывайте, нет у меня никакого дублирования кода.
Я у вас вижу отдельно объявление структуры, отдельно инициализацию. Или тут как с EP2, было лень удалять после тестов? Хорошо, какой из этих двух этапов можно безболезненно удалить чтобы не настраивать одно и то же в двух местах?
Воспользовался. sizeof от типа структуры самое понятное и надёжное решение.
Не вижу я у вас решения лучше. Костыль с объявлением структуры - вижу, решения нет.
А в ваше варианте что? Реализация функций.

Где??? Это шаблонный класс, читай тип. Его данные и методы работы с ними.

Еще раз. Исполняемый код у вас где прописан? В файлах с исполняемым кодом (*.c/*.cpp) или в заголовочниках (*.h/*.hpp). То-то и оно, что в заголовочниках исполняемый код не пишут.
Если, конечно, позволяет язык. Те же макросы или некоторые классы из заголовочника вынуть не выйдет - ну так это ограничения языка.
Это вы сами так решили или кто-то подсказал?
А, то есть вы сторонник все писать в одном общем файле. Тогда вопросов не имею.
Может всё таки расскажете как заинлайнить функцию из другой единицы трансляции?
Расскажите сначала как заинлайнить функцию из другой библиотеки. Единица трансляции на то и единица трансляции что не предполагает влезание во внутренности.
И эта иллюзия защиты на практике мешает больше, чем если сразу сказано, что вся ответственность на программисте.
Что-то обезьяну с гранатой напоминает.
Скорее напоминает фанатов scanf_s и прочих псевдо-безопасных функций. Которые вроде бы говорят "используй нас, мы безопасные" а на практике оказываются ничуть не безопаснее того же scanf'а, а то и хуже - за счет меньшего тестирования.
Вы мой код тоже, похоже, не смотрели. Иначе не могли не заметить как всё обложено перечислениями.
То есть принимать конструктивную критику к сведению не хотите. Ваше право, не буду настаивать.

Re: STM32 и USB (практика)

Вс мар 28, 2021 13:40:44

Еще раз. Исполняемый код у вас где прописан? В файлах с исполняемым кодом (*.c/*.cpp) или в заголовочниках (*.h/*.hpp). То-то и оно, что в заголовочниках исполняемый код не пишут.

Если код шаблонный, то он практически весь будет в хедерах, у меня соотношение хедеров к .cpp файлам примерно 1 к 25 :)

Re: STM32 и USB (практика)

Вс мар 28, 2021 14:37:41

Я у вас вижу отдельно объявление структуры, отдельно инициализацию. Или тут как с EP2, было лень удалять после тестов? Хорошо, какой из этих двух этапов можно безболезненно удалить чтобы не настраивать одно и то же в двух местах?
Зачем что-то удалять? Структуры всех дескрипторов как общие для всех, так и специфичные для классов лежат себе в заголовочном файле, проверенные, жрать не просят. Когда надо берёшь и используешь. Считайте это частью USB-библиотеки.

Еще раз. Исполняемый код у вас где прописан? В файлах с исполняемым кодом (*.c/*.cpp) или в заголовочниках (*.h/*.hpp). То-то и оно, что в заголовочниках исполняемый код не пишут.
Если, конечно, позволяет язык. Те же макросы или некоторые классы из заголовочника вынуть не выйдет - ну так это ограничения языка.
Вы сами ответили на свой вопрос. Если в Си это ограничение языка и надо код в макросы прятать, да ещё оборачивать его всякими while, то в С++ это обычная возможность языка. Возможность, дающая много плюшек. Посмотрите стандартную библиотеку шаблонных классов или boost какой-нибудь - они полностью написаны в заголовочных файлах. Это норма для С++. Вы просто мыслите не на том языке программирования, вот и всё.

А, то есть вы сторонник все писать в одном общем файле. Тогда вопросов не имею.
Это из чего следует? У меня всё в одном файле написано? Вроде нет, всё структурировано по классам, устройствам и т.д. А вот то что при компиляции все заголовочные файлы в одной единице трансляции собираются это хорошо для оптимизации. Никогда не слышали, как перед компиляцией все исходники сливают в один суперфайл и компилируют его, чтобы оптимизатор видел больше кода?

Расскажите сначала как заинлайнить функцию из другой библиотеки.
Легко и непринуждённо. Допустим, это библиотека
Код:
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       
//}
Заинлайнилось, чёрт побери :)

Добавлено after 12 minutes 56 seconds:
Или вот ещё пример.
Код:
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       
//}
И это мы ещё до шаблонов не дошли!

Добавлено after 13 minutes 55 seconds:
Если код шаблонный, то он практически весь будет в хедерах, у меня соотношение хедеров к .cpp файлам примерно 1 к 25 :)
Если честно, то я ещё помню свою ломку по этому поводу. Но когда начинаешь программировать на настоящем С++, а не на Си с классами, то быстро отпускает. Так что, батхед по этому поводу у Си программистов понятен, они мыслят Си-шными стереотипами.

Re: STM32 и USB (практика)

Вс мар 28, 2021 14:51:41

Если честно, то я ещё помню свою ломку по этому поводу. Но когда начинаешь программировать на настоящем С++, а не на Си с классами, то быстро отпускает.

Я уже модули жду, будут в GCC 11 который должен выйти в апреле-мае...

Re: STM32 и USB (практика)

Вс мар 28, 2021 16:41:05

Легко и непринуждённо. Допустим, это библиотека
... не компилируется. Как и ожидалось. Ну либо приведите полный код всех файлов.
Структуры всех дескрипторов как общие для всех, так и специфичные для классов лежат себе в заголовочном файле
Все 10000 или сколько их там, включая с расширяемой структурой (те же аудио) и составные?
Такой файл использовать точно не будут - в нем постареешь пока нужный шаблон найдешь.
Вы сами ответили на свой вопрос.
Я не задавал вопроса. Я указал вам, что это ограничение языка. И ограничение Си выглядит более логичным, чем С++.

Re: STM32 и USB (практика)

Вс мар 28, 2021 18:40:46

... не компилируется. Как и ожидалось. Ну либо приведите полный код всех файлов.
Это даже не смешно. Это и есть полный код, который надо компилировать С++ компилятором.

СпойлерИзображение
build.png
(53.23 KiB) Скачиваний: 27


Такой файл использовать точно не будут - в нем постареешь пока нужный шаблон найдешь..
А альтернатива? Тупо по спецификации? Ну-ну.

Я указал вам, что это ограничение языка. И ограничение Си выглядит более логичным, чем С++.
Я думаю, что вы не можете объективно об этом судить, не зная обоих языков сразу.

Re: STM32 и USB (практика)

Вс мар 28, 2021 20:20:56

Это даже не смешно. Это и есть полный код, который надо компилировать С++ компилятором.
Речь шла о разных единицах трансляции. Попросту говоря - разных файлах. Инлайнить в общем файла (в том числе из заголовочников, естественно) все умеют.
А альтернатива? Тупо по спецификации?
Конечно. Слить воедино три дескриптора, скопипащенных из других проектов гораздо проще, чем заранее писать и тестировать 27 разных вариантов. А если готовых проектов элементарных устройств нет, их так и так делать. Мало ли какие подводные камни встретятся.
Я думаю, что вы не можете объективно об этом судить, не зная обоих языков сразу.
Так я в С++ и не лезу. И сразу объясняю почему - потому что С++ объективно сложнее Си. В частности, плюсовик всегда может прочитать код Си-шника, а вот обратное неверно.

Re: STM32 и USB (практика)

Вс мар 28, 2021 21:19:21

Речь шла о разных единицах трансляции.
Неправда ваша. Спрашивали вы ровно вот это
Расскажите сначала как заинлайнить функцию из другой библиотеки.
На что я вам показал как элементарно из библиотеки инлайнится. Просто потому что библиотека находится в заголовочном файле и элементарно подключается к любой единице трансляции. Это совершенно обычно для С++. Надеюсь, вы не опуститесь до обвинений, что я для примера код поместил в том же файле, а не подключил его с помощью #include?

Инлайнить в общем файла (в том числе из заголовочников, естественно) все умеют.
Только полноценную библиотеку в заголовочном файле на С++ написать можно и нужно, а на Си фиг вам.

Так я в С++ и не лезу.
Да уж, заметно. Ещё на этой странице темы вы "лечили", что нельзя код в заголовочном файле писать, да типы структур порывались удалять.

И сразу объясняю почему - потому что С++ объективно сложнее Си. В частности, плюсовик всегда может прочитать код Си-шника, а вот обратное неверно.
Но вот почему-то порассуждать о недостатках это завсегда пожалуйста. У плюсов есть недостатки, но они совершенно не там где вы их видите со стороны. Ну правда, принять преимущество возможность писать код в заголовочном файле за недостаток это курам на смех.

Добавлено after 27 minutes 44 seconds:
И не только код, кстати. Вот так объявленный в заголовочном файле дескриптор будучи подключенным во сколько угодно единиц трансляции в прошивку попадёт ровно один раз. Без всяких extern и #ifdef. Просто потому что это ++.
Спойлер
Код:
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        // количество поддерживаемых конфигураций
};

Re: STM32 и USB (практика)

Вс мар 28, 2021 22:31:04

На что я вам показал как элементарно из библиотеки инлайнится. Просто потому что библиотека находится в заголовочном файле
Вообще-то, по умолчанию библиотека это "динамическая библиотека" *.so (*.dll) или в крайнем случае статическая *.a (*.lib) и уже в последнюю очередь "любой набор файлов, который можно куда-то подключить".
Так что попытка подмены понятий не засчитывается.
Только полноценную библиотеку в заголовочном файле на С++ написать можно и нужно, а на Си фиг вам.
см. выше: библиотека это скомпилированный файл.
Но даже для недо-библиотек - на Си это не то чтобы нельзя сделать, просто не обычно нужно: раздельную компиляцию не просто так придумали.
Впрочем, для некоторой периферии вроде 44780 или семисегментника я на заголовочниках делал, просто чтобы настройки проще было менять. Но по-хорошему так делать не надо.
Да уж, заметно. Ещё на этой странице темы вы "лечили", что нельзя код в заголовочном файле писать, да типы структур порывались удалять.
Скажите, вы кого пытаетесь обмануть?
Но вот почему-то порассуждать о недостатках это завсегда пожалуйста.
Я не знаю зачем вы столь упорно мусолите эту тему.
Я уже объяснил почему ни С++, ни сама эта тема мне неинтересна. Опровержений вы не привели.
Ответить