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

Arlleex

Свой
  • Постов

    5 813
  • Зарегистрирован

  • Посещение

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

    14

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


  1. Нет, с точки зрения аппаратной реализации мне как раз понятно все. Я говорил об историческом наследии именно в программном обеспечении, реализующим всякие ширпотребные протоколы.
  2. Можете наглядно показать, что Вы имели в виду? Ибо я читаю это как попытаться запихнуть структуру с FAM в другую структуру, и дописать за ней CRC. Но любые попытки сделать так тщетны, т.к. нельзя использовать FAM-структуры как член другой структуры.
  3. Вы правы, косяк в этом есть. До какого-то момента писал так - вот и попал кусок сюда в том виде в каком есть. Но на самом деле сколько своего писал, сколько чужого лопатил - пока что ни разу не удалось нарваться на конфликт дефайнов)) Возможно, потому, что в моих исходниках мало подключений сторонних библиотек. А те, что подключаются, не пересекаются названиями.
  4. Ды кто его теперь знает. Исторически в конец всегда запихивал. А сейчас понимаю, что с точки зрения языка программирования это очень выходит костыльно.
  5. Из крайнего проекта (извернулся трюком с union, но это все равно громоздко и муторно): // это с компа надо отправить в железку, а та отправит в свой RS-485 len байт struct sTxMsgRS485SendCmd { sMsgHdr hdr; eMsgUID uid; u32 ifNum; u32 len; union { u8 byte[MAX_RS485_TX_SIZE + sizeof(u32)]; // - эта добавка sizeof() как раз для CRC-поля u32 crc[(MAX_RS485_TX_SIZE + sizeof(u32)) / sizeof(u32)]; // а это чтоб представить весь буфер как набор u32 crc[] }; }; ... void send() { sTxMsgRS485SendCmd tx; tx.hdr.srcAddr = HOST_ADDR; tx.hdr.dstAddr = MBC_ADDR; tx.uid = WORK_REQ_MBC_SEND_RS485; tx.ifNum = 0; u32 const len = /* считал с окошка программы */; u32 i = 0; while(i < len && i < MAX_RS485_TX_SIZE) tx.byte[i] = /* чтение с окошка ввода */()[i++]; tx.len = i; if(i > 0) { i = (i + 3) / 4; // ровняю по u32 u32 const size = sizeof(tx) - (MAX_RS485_TX_SIZE - i * 4); // началось в деревне лето... считаю общий размер кадра tx.crc[i] = crc_Calc(&tx, size / 4 - 1); // фокус с union <- вот он /* ethernet_send() */(&tx); } } Ну вот визуально - полная шляпа выходит)) А хотелось копеечка к копеечке - все заполнили и отправили, без хитрых представлений и закулисных допущений.
  6. Вот я и хотел, чтобы визуально CRC присутствовала в структуре, чтобы и мне (спустя время) и другому человеку было понятно, что содержится в теле пакета. В чем текущее неудобство. Пример. К МК подцеплен езернет и CAN, и пусть это будет некий преобразователь интерфейсов. Езернет цепляется к компу. Комп периодически поллит входящую очередь CAN-а, для чего шлет пакет-запрос READ_CAN_FIFO. МК по этому запросу формирует ответ в виде структуры struct ReadCANAck { Header hdr; CANFrame frame[]; }; Вопрос - где эту структуру создать в памяти. Понимаем, что нижестоящие функции отправки в сеть требуют структуру "целиком", т.е. непрерывным массивом известной длины. Значит можно запихнуть эту структуру в union #define MAX_SEND_MESSAGE_SIZE MAX_ETH_UDP_PAYLOAD_SIZE union SendMessage { ReadCANAck readCANAck; ... ; // возможно, другие "форматки" для других команд u8 buffer[MAX_SEND_MESSAGE_SIZE]; }; Мы заранее не знаем, сколько там в CAN-е может храниться принятых кадров, поэтому после заполнения хедера ответа, мы читаем N кадров CAN-FIFO и заполняем свободное место пэйлоада езернет-фрейма, чуть ли не вот так static SendMessage Tx; void sendCANRxFIFO() { // не насилуем стек - готовим кадр в одном месте Tx.readCANAck.hdr.cmd = READ_CAN_FIFO; // сколько там в CAN фреймов пришло? u32 const frameCnt = CAN.getFrameCnt(); // запихиваем CAN-кадры из его FIFO, пока место в буфере отправки в езернет еще есть for(u32 i = 0; i < frameCnt; ++i) { if(/* места в езернет-фрейме еще хватает ? */) Tx.readCANAck.frame[i] = CAN.readFrame(); } } и вот теперь: беда. Мы договорились, что в протоколе обмена все кадры имеют CRC последним членом. Тут (примера ради) его не было показано. Но этот CRC подразумевался. В структуре ReadCANAck его нельзя после frame[] поставить. И это порождает 2 проблемы: 1) визуально нет поля CRC в пакете - т.е. или я, или сторонний разраб, которому я дам эту структуру, может "забыть", что там еще CRC считать надо и заполнять (а еще и место в памяти выделять!); 2) после заполнения основных указанных полей, в силу "отсутствия" явного описания CRC в структуре отправки - мы должны костыльными приведениями типов и адресацией залезть за последний записанный frame и вставить туда CRC. Это может (и будет!) выглядеть плохо! Так как сходу не будет понятно, с какого лешего мы пишем "за пределы структуры" какую-то там CRC: void sendCANRxFIFO() { ... u32 i; // запихиваем CAN-кадры из его FIFO, пока место в буфере отправки в езернет еще есть for(i = 0; i < frameCnt; ++i) { if(/* места в езернет-фрейме еще хватает ? */) Tx.readCANAck.frame[i] = CAN.readFrame(); } *(u32 *)&Tx.readCANAck.frame[i] = calcCRC32(&Tx, /* правильно посчитать len */); // <- через пару месяцев: WTFFFFFFFFFFFF??? }
  7. Разумное число - у всех разное, поэтому его либо заранее обговаривать в ТЗ, либо пытаться сделать так, чтобы программист вообще об этом не думал - т.е. технически победить ограничение.
  8. Нет не так. Структурой я описываю все содержимое кадра - хедеры, данные и т.д., в том числе CRC. Обрамление кадра - совершенно другая процедура, ее структарми не опишешь. Да и незачем.
  9. Я такие документики тоже делаю - только для такого рода девайсов, где потенциальный заказчик - любой с планеты Земля. А когда надо внутри отдела между парой-тройкой разрабов договориться - никакой писаниной заниматься времени нет.
  10. Нет. Никаких CRC writeTxFIFO() не считает и не дописывает. Условно: void sendTemperature() { ExchDesc<Temperature> t; t.hdr.dst = HOST_PC; t.temp = getTemperature(); t.crc = CRC32(&t, sizeof(t) - 4); SLIP.send(&t, sizeof(t)); // вот внутри нее содержимое 't' обернется в кадр и вызовется writeTxFIFO() }
  11. Да. Так делают, например, топовые гиганты типа интел/амд/нвидиа. И да, я не видел ни одной фирмы, где прям по серьезке садились бы считать байты ОЗУ, которые потребуются для задачи, ибо хотя бы найти челвоека, который это умеет и потом будет в случае чего отвечать - та еще задача. Обычно смотрят по верхушкам - устраивает ли МК/проц по интерфейсам и их скоростям, режимам и т.д. Можно ли его купить в обозримом будущем в тех же объемах, что он продается сейчас. У инженера обычно в комплекте где-то в районе пятой точки есть чуйка, которая подсказывает ему, хватит ли ему ресурсов или будет маловато. Чуйка обучается опытом а потом подтверждается расчетами.
  12. ... или стартап-файл не под свой контроллер налутал))
  13. Есть, конечно)) Но эта ошибка из разряда "вернуть адрес локальной переменной из функции" - т.е. бывалым разработчиком не допустится🙂 Операции с DMA либо блокирующие (костыль, не делаю так никогда), либо функция отправки пишет в буфер, из которого DMA спокойно будет отправлять, а из той функции можно смело выходить. Я стремлюсь всегда разделять уровни кода, поэтому все очень просто становится читать и поддерживать. Вот так выглядит интерфейс чтения/записи в UART #ifndef _UART_HPP_ #define _UART_HPP_ #include "macros.h" namespace nsUART { void init(); u32 readRxFIFO (u8 dst[], u32 len); s32 writeTxFIFO(u8 src[], u32 len); } #endif И совершенно не важно, что передача и прием реализованы как кольцевой буфер с задействованным DMA как на прием, так и на передачу. Задача (или цикл) парсера вызывает readRxFIFO() и разгребает входящий поток байтов: если нащупала что-то осмысленное - вызывает функцию разбора пакетов. Если хотим отправить данные - вызываем соотв. функцию и передаем ей эти данные, она их кодирует, обрамляет как надо и вызывает writeTxFIFO().
  14. Отключать то зачем? Скорее, просто следить за когерентностью данных и вовремя их флушить/инвалидировать. Я тоже могу представить контекст, в котором эти сентенции уместны. Но они точно не уместны при тезисном описании в википедиях и тем более не применимы в технических дискуссиях за свод нерушимых правил, преклоняясь перед авторитетом дяди в очках с задней обложки книжки. На всякий случай, повторюсь🙂 Я цифру 1024 написал от балды, это лишь для примера было. На самом деле у меня отправляемые пакеты создаются по мере необходимости к отправке, т.е. на стеке. А вот под принимаемые пакеты как раз статически выделяется буфер-агрегатор (в виде Си-шного union) под самый длинный возможный пакет. Т.е. если максимально возможный принимаемый пакет будет, например, 1000 байт - будет создан буфер на 1000 байт или чуть больше для локальных требований к выравниванию и т.д. В особых случаях, когда памяти почти нет (ОЗУ), а линия связи позволяет отправлять данные порционно - то можно так и делать. Например, тот же SLIP по UART-у можно не кодировать предварительно, а кодировать по мере отправки очередного символа. С тем же езернетом так не получится - внутри одного езернет-кадра будь добр подготовить все нужные данные сразу.
  15. Именно. Профессоры обычно только и печатали книжки, не имея ни малейшего представления о реальном коде. Туда же книжки Макконела и Мартина - т.е. в жопито😉 Посмотрел цитаты великого цитируемого (с моими скромными комментами): За любым столом после 3-й рюмки уже обычно начинаются пространные рассуждения о смысле жизни и трехэтажная философия. Здесь из этого же разряда - просто потому, что "классики" больше никакой нет. За неимением цитируют вот таких вот деятелей. P.S. И кстати, а причем тут щас вообще оптимизация? Я вроде о ней даже не говорил. Нет, ну я конечно стараюсь сразу более менее вменяемо подходить к написанию кода - т.е. там, где можно сразу сделать хорошо - делаю. Другой подход мне уже не приживется, ибо это, как и любовь к говнокодингу - либо навсегда, либо никогда. Сегодня вот на моем смартфоне при открытии камеры вываливалось сообщение "Невозможно подключиться к камере". Б%ять! И никакая перезагрузка не помогает. Калькулятор весит 100 мегов - скоро, видимо, и считать правильно перестанет. И так со всем программным, что меня окружает в нынешнем мире. Вот такая философия с преждевременной оптимизацией и породила покрасчиков кнопок, которым похрен на юзеров, которым для моргания лымпочкой нужна ардуина на распбери пай, не меньше.
  16. Прошу прощения, не обратил внимания, что указатель. Вы правы. Однако да, мой компайлер обозвал меня представителем европейского модного движения за VLA внутри структуры А так - это почти то, что я хотел🙂 А разница? В одном проекте - по прерываниям делал, в текущем - DMA принимает. Как это влияет на разбор байтового потока? Зачем его знать? DMA настраивается в кольцевой режим. Задача, обслуживающая канал приема, читает эти байты и отдает его фреймеру SLIP-а, а тот уже на выходе выдает пакеты данных. Потому что структуру в ее самом девственном виде проще читать и давать ее другим разработчикам, дабы понимали ее формат при поддержке протоколов общения. Когда передается 100500 разных классов, начиненных еще каким-то функционалом - становится вообще-то затруднительно сходу въехать в порядок разложения этой порнографии на байтики в реальном проводе. Структура весьма самодокументируема - сразу понятно, что за чем идет. В классе, в котором помимо данных набито еще 100 строк всяких методов и перегрузок, разобраться тяжелее сходу.
  17. Это будет подтягивать динамическое выделение памяти, такое меня уже не устроит. Ух тут сколько всего написали (это хорошо, дискуссия жива🙂) - однако я внесу немножко понятностей. Не важно, что за протокол, важно то, что он пакетный: длина пакета выкорчевывается внеполосной сигнализацией на уровне проводов или (для примитивных байтовых интерфейсов) промежуточный канальный фреймер разбирает поток байтов на кадры/пакеты. Короче говоря, я всегда знаю, сколько данных мне прилетело в пакете. Пакет, как обычно, уже содержит заголовок со всякими типами содержимого для идентификации этого пакета и т.д. Шмалять в сеть пакеты фиксированной длины здесь очень избыточно - это точно красный флаг. Так вот. У меня девайс состоит из множества плат, каскадно соединенных друг с другом порой сильно разными интерфейсами. Пакет данных со входа до самой дальней платы может пройти пару-тройку интерфейсных мостов со своими протокольно-зависимыми особенностями. Каждая "промежуточная" плата имеет механизм проброса "не своих" пакетов данных туда дальше в "паровозик". Чтобы облегчить ПО каждой платы, она детально даже не разбирает "не свои" пакеты, а как есть его пересылает дальше. Где-то закладывать CRC не имело бы смысла, а где-то, из-за этого "прозрачного режима", CRC пакету нужен. Например: девайс состоит из двух плат (1) и (2), между ними прокинут UART. С платы (1) наружу девайса торчит Ethernet, воткнутый в комп. С компа шлю данные плате (2) - и, казалось бы, езернет уже гарантирует целостность своего фрейма, т.е. можно не вносить контрольную сумму для данных, но данные ведь предназначены плате (2), а чтобы туда попасть, данные должны преобразоваться в UART платой (1) и транслироваться на плату (2). Этот UART - байтовый, поэтому поверх него работает SLIP. Но для контроля целостности данных на приеме платой (2) пакет нужно долполнить контрольной суммой. Что и делает сразу отправляющая сторона перед засовыванием в езернет. Насчет положения CRC в структуре: да, можно в хедер ее пихнуть. Однако это маааленький такой костыль с тасованием расположения, который конкретно в данном случае вполне поможет (за счет наличия в Си и (с позволения компиляторов) C++ FAM в структурах). Да, удобно, если КС в конце - сверил - и результат будет 0. Оданко по затратам на вычисление это будет почти один-в-один с пробегом по хедеру и по данным и последующим сравнением полученной КС с той, что в хедере лежит (в случае размещения КС в хедере). Исторически у меня так завелось, что хедеры могут быть разных размеров, и поэтому КС размещалась строго в конце, несмотря на то, что размер кадра плавающий. Меня полностью устраивает этот вариант и поныне. И я хотел, чтобы несколько "костыльно" выглядящее определение структуры с гибким массивом в конце наконец обрело лаконичный вид в C++. Мне не нужна динамическая память - мне нужно как-то сказать компайлеру, чтобы на статически выделенном куске создавал структуру, в которой после гибкого массива расположен еще какой-то элемент. Условно, чтобы это выглядело примерно так (как я это вижу): struct FlexFrame { Header hdr; u8 data[]; // <- гибкий массив, а значит дальше можно ставить только элементы с атрибутом "гибкого положения" u32 crc __attribute__((flexible)); // положение этого члена "гибкое", в зависимости от объема поля data при создании структуры }; ... static u8 RxPacketMemory[1024]; // статически выделенная память для создания "гибких" структур // вызвали с len == 10 void sendPacket(u32 len) { // тут нужен был бы синтаксис объявления структуры, например FlexFrame frame __attribute__((RxPacketMemory, len)); // компилятор думает, что frame имеет data[10], размещая frame в памяти RxPacketMemory frame.crc = ...; // компилятор для доступа к полю crc использует соответствующее смещение, полученное на основе аргумента функции }
  18. Работает все прекрасно и у меня. И 15 лет назад тоже все работало. Меня интересовало, додумалось ли C++ сообщество до чего-то более удобоваримого в этом плане. P.S. Какие указатели в протоколе передачи данных??? Вы о чем? Вроде понятно, что данные снабжаются КС и уже улетают в провод. В дизасм смотреть или даже не пытаться?🙂 P.S. Я с МК тоже отправляю, так что способ ищу легковесный. Если нет - мне проще (как всегда, собственно) написать свой небольшой класс.
  19. Я от того и спрашиваю, что задача тривиальная, а решалась всегда "криво"🙂 Но, видимо, нужно создать класс-оболочку над неким массивом известной длины (достаточной, чтобы вместить нужное количество данных) и с ним уже работать. Длину не нужно передавать, т.к. сам протокол умеет детектировать ее по механизму кадрирования нижних уровней. ИМХО, ужасное решение🙂 Потому что у меня десятки разных управляющих структур бегают, с разным содержимым и длиной. Контрольную сумму логично располагать где-то в одном конкретном месте для всех структур: и это либо начало кадра, либо самый его хвост. Иначе, чтобы определить положение КС, нам надо "узнать", что это за структура, а лезть в ее поля, не проверив КС - нельзя. У меня "узаконен" формат с CRC в хвосте. Алгоритм приема и отбраковки битых кадров становится универсальным.
  20. Можно ли в C++ как-то красиво описать структуру с гибким положением последнего элемента? Допустим, есть структура struct TxRS485 { Header hdr; u8 byte[N]; u32 crc; } N должно будет стать известно в рантайме, поэтому шаблоны тут вряд ли помогут. Задача простая (и применяемая ну совсем повсюду): из поля ввода юзер-данных считать строчку, запихнуть ее в структуру с определенным паттерном (снабдив заголовками и т.д.), последним элементом должен "приклеиться" CRC-чек. Вроде все просто, однако как будто нет решения, кроме как по старинке "клеить" вручную хедер с данными, а потом к этому всему дописывать контрольную сумму.
  21. У китайцев чуть более прогнозируемая ситуация: если купить становится затруднительно у основного поставщика, то по инерции можно еще полгода точно ехать с помощью других продаванов, в это время подготавливая почву для внедрения новой аппаратной платформы. Другое дело - например, евреи - i.MX6 (по крайней мере, нужные нам SoM) пропали за месяц от слова "навсегда".
  22. У манагеров всегда все просто. Это вечно недовольным инженерам вечно то конденсаторов не хватает, то дорожки не лезут.
  23. К этому и сводится большая часть программирования, несмотря на рядом стоящую помойку под названием стандартная библиотека.
×
×
  • Создать...