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

AHTOXA

Свой
  • Постов

    4 033
  • Зарегистрирован

  • Посещение

  • Победитель дней

    3

Весь контент AHTOXA


  1. Можно crc засунуть в Header, раз уж он у всех пакетов есть. Насколько я понимаю, и N будет там же, в Header. А в конце будет byte[maxLen]. Ну а почему бы и не написать, если небольшой? 🙂
  2. Это по-каковски? Если с английского, то яндыкс-переводчик пишет agile[ˈæʤaɪl]прил
  3. О, оказывается всё ещё сложнее: отображение зависит от темы. В моей теме (Electronix Neoclassic (по умолчанию)) - сейчас не видно ни уровня, ни звания. А если я включаю тему "Electronix Classic 4.5.2", то видно уровень (над аватаркой), причём мой уровень по-прежнему "фанат дивана" 🙂 А отображение звания, как я понимаю, сейчас уже отключили.
  4. Заработало частично. Поменял звание на "труженик села" - сбоку от моих сообщений поменялось, в профиле осталось старое. Очистил (ввёл пустую строку) - перестало отображаться "Звание: xxx" около сообщений. Остался я вообще без звания. Но, что интересно, в профиле осталось старое ("фанат дивана"). А при наведении на него мышкой всплывает подсказка "Гуру 6/6". В принципе, я доволен, мне главное хотелось убрать "фанат дивана" 🙂
  5. Раньше как-то можно было, начиная с определённого уровня. Я поменял на "фанат дивана". Сейчас вот подумал, что я уже не такой фанат дивана, как раньше, хотел поменять на что-то более актуальное, и не нашёл где. Это я плохо искал, или убрали такую возможность?
  6. На VL (value line) отдельный RM, RM041. Это сильно урезанные контроллеры, без USB, и с частотой до 24МГц (пишу по памяти) Это STM32F10X_MD.
  7. Я судил по изменениям в inline-ассемблере IAR-а. Сейчас он выглядит вот так: __attribute__((__always_inline__)) inline void set_interrupt_state(status_reg_t status) { asm volatile ( "MSR PRIMASK, %0\n" : : "r"(status) :"memory" ); } Ну то есть один-в-один gcc. Этож-ж-ж неспроста? 🙂 Короче, мысль моя вот в чём: в 90% случаев должно хватить простого reg; или, для большей понятности (void)reg; Но на всякий случай лучше проверить дизассемблер.
  8. Жуть какая! 🙂 Интересно, с 2010 года хоть какие-то из компиляторов, которые отбрасывают чтение volatile-переменной поменяли своё мнение? (Подозреваю, что IAR поменял, он же теперь вроде бы на LLVM сделан).
  9. Я думаю, это изменение скорости (минус 100Кб/с).
  10. Это стили кодирования пишут для групп программистов. Когда в одном проекте несколько человек. Когда пишешь один - вообще пофиг как именуешь и форматируешь. Главное чтоб тебе самому было понятно. Но даже для себя одного полезно сформулировать эти правила, чтобы потом их придерживаться. Это поможет потом поддерживать проект, когда нужно будет вернуться к нему через год-другой. Из этого тоже можно извлечь пользу: сразу видно, что идёт вызов библиотечного кода 🙂
  11. Ну да, блоком. Или вынести этот кусок в inline-функцию.
  12. А они редакторе подсвечиваются по-разному. (Я использую Eclipse для embedded-проектов, QtCreator для ПК-проектов). Префиксы мне не нравятся. Нет гарантий, что префикс соответствует реальному типу. Сменил тип - забыл про префикс. Гораздо надёжнее не полагаться на префиксы, а использовать возможности современных IDE. Навёл мышку на название типа - получил всплывающую подсказку с кусочком объявления. Если там doxigen- комментарий перед типом, то и он отобразится. Единственное место, где я оставил префиксы - это переменные-члены класса. Они у меня называются m_var; Если интересно, вот рекомендации по кодированию одного из моих старых проектов. Вот, кстати, идеальное место для применения RAII. Создаётся тип CriticalSection, в конструкторе вызываются cpu_alloc_critical_section_context(csc) и cpu_entry_critical_section(csc), а в деструкторе - cpu_leave_critical_section(csc). Тогда функция будет выглядеть так: inline void init() { CriticalSection cs(csc); SPI3->CR1 = SPI_CR1_PRESET_VAL; SPI3->CR2 = SPI_CR2_RXDMAEN | SPI_CR2_TXDMAEN; SPI3->SR = 0x0; SPI3->CRCPR = 0x0; SPI3->I2SCFGR = 0x0; SPI3->I2SPR = 0x0; __DSB(); } Это немного короче, но главное - нет шанса забыть освободить критическую секцию. Даже если где-то посреди функции сделать return;
  13. Если поместить конструкцию using namespace в функцию, то область её действия заканчивается в конце функции: namespace Test { struct Foo { int flag }; } auto makeFoo() { using namespace Test; return Foo {1}; } int main() { auto foo = makeFoo(); // здесь не действует using namespace Test } А в целом, идеологически, все эти пространства имён для того и делались, чтобы виртуально "удлиннить" идентификаторы. Поэтому логично, что запись получается более многословной. Можно от этого огорчаться, можно пытаться сократить запись, а можно просто принять это, и использовать. Запись вида sGPIOCmdDesc descriptor = {sGPIOCmdDesc::GPIO_CTRL, sGPIOCmdDesc::SET_EN_DISPLAY}; не выглядит ужасной, зато сразу видно, что мы не перепутали enum-ы. А если вместо enum использовать enum class, то мы также можем быть уверены, что мы не перепутали местами аргументы.
  14. Это если он не нагружен. Если же нагружен, то при обесточивании он может остаться в том положении, в котором он был на момент обесточивания (типа, на 3/16 шага). В этом случае при возобновлении подачи питания он может сместиться в положение полного шага, которое ему даст драйвер при включении. Но даже в этом случае пара двигателей скорее всего сместится синхронно, потому что в момент обесточивания она с большой вероятностью была в одинаковом положении. У меня у 3D-принтера на оси Z такая конструкция - два мотора. Ничего вроде не перекашивается.
  15. Это всё понятно. Но всё же, реальная-то память под эти элементы массива где-то выделена? То есть, по сути, мы "наводим" наш "безразмерный массив" на лежащий где-то реальный массив. Поэтому я предложил два варианта: либо вместо массива без указания размера указать его максимальный размер (да хоть стопиццот, нам без разницы, потому что реальная длина указана в структуре), либо использовать современный класс span, специально заточенный для таких целей (по сути это и есть указатель + длина). то есть или так: struct sUDPAckReadFIFOCAN1{ Header header; size_t frameSize; sCANFrame frame[100500]; }; или так: struct sUDPAckReadFIFOCAN1{ Header header; std::span<sCANFrame> frames; }; ЗЫ. Во втором случае, конечно не удастся просто привести указатель на буфер приёма/передачи к этой структуре. Это может быть как минусом, так и плюсом, в зависимости от обстоятельств.
  16. А зачем "псевдо"? Ведь данные всё равно лежат в реальном массиве. Его и используйте. Если есть несколько таких структур, различающихся только размерами массивов, можно приводить к максимальному. Ну или взять std::span из c++20. Хотя это не отменяет надобности в массиве для собственно хранения данных.
  17. Тут согласен, иногда хочется иметь какой-то базовый класс со значениями по умолчанию. В принципе, можно сделать вот так: struct CommandInfoTwoParam { struct Params { uint32_t param1; uint32_t param2; }; }; template <> struct CommandInfo<Command::SendData> : public CommandInfoTwoParam {}; Это почти идеально: лишнее дублирование (почти) отсутствует, но тем не менее каждая команда явно описана.
  18. То, что вы описали, почти всегда не нужно. А нужно - чёткое и консистентное описание протокола обмена. Можно составить документ, таблицу, типа код команды - структура параметров запроса - структура параметров ответа. И потом надеяться, что не забудешь внести в неё изменения при добавлении/изменении команды. А можно сделать самодокументирующийся код, как я описал выше. Там всё сразу в одном месте. И никак не получится при добавлении команды "забыть" добавить реализацию шаблона. Будет ошибка компиляции. Меня такой подход многократно выручал. Особенно он удобен, когда приходится возвращаться к проекту после длительного перерыва, и всё уже полностью выветрилось из головы.
  19. Я как раз и агитирую за отдельные структурки. Чтобы для каждой команды было чётко указано, какова структура параметров. Всё это можно вынести в один *.h - файл, и получить самодокументированный код. Кстати, и в этом подходе можно вынести одинаковые структуры "за скобки": struct OneParam { uint32_t param1; } template <> struct CommandInfo<Command::GetInfo> { using Params = OneParam; };
  20. Почему "нет конечно". Конечно да:) Потому что потом, при необходимости изменить структуру sTxSendCAN не придётся искать все её псевдонимы и дорабатывать их. Лучше уж сразу для каждой команды прописать подходящую структуру. Можно использовать плюсовый механизм traits. Типа такого: // перечень всех команд enum Command : uint32_t { Reset = 0, GetInfo = 1, SendData = 2, }; // шаблон структуры, содержащей информацию о команде template<Command cmd> struct CommandInfo; // специализации шаблона для каждой команды (можно туда добавлять всё что угодно: тайм-аут обработки, структуры-параметры и так далее) template <> struct CommandInfo<Command::Reset> { struct Params {}; }; template <> struct CommandInfo<Command::GetInfo> { struct Params { uint32_t param1; }; }; template <> struct CommandInfo<Command::SendData> { struct Params { uint32_t param1; uint32_t param2; }; }; void send(...) { } // и вот как будет выглядеть выбор нужной структуры для заданной команды: void sendData() { using CommandData = CommandInfo<Command::SendData>; using Params = typename CommandData::Params; Params params; send(Command::SendData, &params, sizeof(Params)); } При добавлении новой команды для неё сразу же создаём специализацию шаблона, содержащего инфу о команде. И далее оперируем этой информацией (на обоих концах протокола обмена).
  21. ABS же. Вполне промышленный пластик, из него и штампуют тоже. А PLA конечно несерьёзно, и по температуре слабоват.
  22. Речь идёт не о замене трёх строчек, а о Причём всё это, как правильно написал @Arlleex, пишется один раз, потом выносится в заголовочный файл, и в дальнейшем просто используется.
  23. Да там всё просто, кроме магии с распаковкой пакета параметров. Но она тоже постижима 🙂 template <typename ... Operations> // здесь список типов произвольной длины void modifyReg(volatile uint32_t& reg, Operations&& ... ops) // здесь список параметров, каждый параметр имеет тип из списка типов { auto val = reg; (..., (void)ops.apply(val)); // а вот это - распаковка. оператор "запятая" поочерёдно применяется к результату вызова функции apply() для каждого объекта из списка ops reg = val; } Можно ещё через лямбды (wandbox), но этот способ мне меньше нравится.
  24. Если нужно, чтобы это всё реализовывалось за одну RMW операцию с регистром, то делаем через вспомогательную переменную, и убираем volatile в параметрах функций apply(): template <typename ... Operations> void modifyReg(volatile uint32_t& reg, Operations&& ... ops) { auto val = reg; (..., (void)ops.apply(val)); reg = val; } вот ссылка на wandbox, поиграться. UPD: вот ещё ссылка, немножко подрихтовал: wandbox 2
×
×
  • Создать...