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

Arlleex

Свой
  • Постов

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

  • Посещение

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

    14

Arlleex стал победителем дня 6 апреля

Arlleex имел наиболее популярный контент!

Репутация

131 Очень хороший

2 Подписчика

Информация о Arlleex

  • Звание
    Гуру
    Гуру

Контакты

  • ICQ
    Array

Посетители профиля

17 696 просмотров профиля
  1. Вы правы, косяк в этом есть. До какого-то момента писал так - вот и попал кусок сюда в том виде в каком есть. Но на самом деле сколько своего писал, сколько чужого лопатил - пока что ни разу не удалось нарваться на конфликт дефайнов)) Возможно, потому, что в моих исходниках мало подключений сторонних библиотек. А те, что подключаются, не пересекаются названиями.
  2. Ды кто его теперь знает. Исторически в конец всегда запихивал. А сейчас понимаю, что с точки зрения языка программирования это очень выходит костыльно.
  3. Из крайнего проекта (извернулся трюком с 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); } } Ну вот визуально - полная шляпа выходит)) А хотелось копеечка к копеечке - все заполнили и отправили, без хитрых представлений и закулисных допущений.
  4. Вот я и хотел, чтобы визуально 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??? }
  5. Разумное число - у всех разное, поэтому его либо заранее обговаривать в ТЗ, либо пытаться сделать так, чтобы программист вообще об этом не думал - т.е. технически победить ограничение.
  6. Нет не так. Структурой я описываю все содержимое кадра - хедеры, данные и т.д., в том числе CRC. Обрамление кадра - совершенно другая процедура, ее структарми не опишешь. Да и незачем.
  7. Я такие документики тоже делаю - только для такого рода девайсов, где потенциальный заказчик - любой с планеты Земля. А когда надо внутри отдела между парой-тройкой разрабов договориться - никакой писаниной заниматься времени нет.
  8. Нет. Никаких 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() }
  9. Да. Так делают, например, топовые гиганты типа интел/амд/нвидиа. И да, я не видел ни одной фирмы, где прям по серьезке садились бы считать байты ОЗУ, которые потребуются для задачи, ибо хотя бы найти челвоека, который это умеет и потом будет в случае чего отвечать - та еще задача. Обычно смотрят по верхушкам - устраивает ли МК/проц по интерфейсам и их скоростям, режимам и т.д. Можно ли его купить в обозримом будущем в тех же объемах, что он продается сейчас. У инженера обычно в комплекте где-то в районе пятой точки есть чуйка, которая подсказывает ему, хватит ли ему ресурсов или будет маловато. Чуйка обучается опытом а потом подтверждается расчетами.
  10. ... или стартап-файл не под свой контроллер налутал))
  11. Есть, конечно)) Но эта ошибка из разряда "вернуть адрес локальной переменной из функции" - т.е. бывалым разработчиком не допустится🙂 Операции с 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().
  12. Отключать то зачем? Скорее, просто следить за когерентностью данных и вовремя их флушить/инвалидировать. Я тоже могу представить контекст, в котором эти сентенции уместны. Но они точно не уместны при тезисном описании в википедиях и тем более не применимы в технических дискуссиях за свод нерушимых правил, преклоняясь перед авторитетом дяди в очках с задней обложки книжки. На всякий случай, повторюсь🙂 Я цифру 1024 написал от балды, это лишь для примера было. На самом деле у меня отправляемые пакеты создаются по мере необходимости к отправке, т.е. на стеке. А вот под принимаемые пакеты как раз статически выделяется буфер-агрегатор (в виде Си-шного union) под самый длинный возможный пакет. Т.е. если максимально возможный принимаемый пакет будет, например, 1000 байт - будет создан буфер на 1000 байт или чуть больше для локальных требований к выравниванию и т.д. В особых случаях, когда памяти почти нет (ОЗУ), а линия связи позволяет отправлять данные порционно - то можно так и делать. Например, тот же SLIP по UART-у можно не кодировать предварительно, а кодировать по мере отправки очередного символа. С тем же езернетом так не получится - внутри одного езернет-кадра будь добр подготовить все нужные данные сразу.
  13. Именно. Профессоры обычно только и печатали книжки, не имея ни малейшего представления о реальном коде. Туда же книжки Макконела и Мартина - т.е. в жопито😉 Посмотрел цитаты великого цитируемого (с моими скромными комментами): За любым столом после 3-й рюмки уже обычно начинаются пространные рассуждения о смысле жизни и трехэтажная философия. Здесь из этого же разряда - просто потому, что "классики" больше никакой нет. За неимением цитируют вот таких вот деятелей. P.S. И кстати, а причем тут щас вообще оптимизация? Я вроде о ней даже не говорил. Нет, ну я конечно стараюсь сразу более менее вменяемо подходить к написанию кода - т.е. там, где можно сразу сделать хорошо - делаю. Другой подход мне уже не приживется, ибо это, как и любовь к говнокодингу - либо навсегда, либо никогда. Сегодня вот на моем смартфоне при открытии камеры вываливалось сообщение "Невозможно подключиться к камере". Б%ять! И никакая перезагрузка не помогает. Калькулятор весит 100 мегов - скоро, видимо, и считать правильно перестанет. И так со всем программным, что меня окружает в нынешнем мире. Вот такая философия с преждевременной оптимизацией и породила покрасчиков кнопок, которым похрен на юзеров, которым для моргания лымпочкой нужна ардуина на распбери пай, не меньше.
  14. Прошу прощения, не обратил внимания, что указатель. Вы правы. Однако да, мой компайлер обозвал меня представителем европейского модного движения за VLA внутри структуры А так - это почти то, что я хотел🙂 А разница? В одном проекте - по прерываниям делал, в текущем - DMA принимает. Как это влияет на разбор байтового потока? Зачем его знать? DMA настраивается в кольцевой режим. Задача, обслуживающая канал приема, читает эти байты и отдает его фреймеру SLIP-а, а тот уже на выходе выдает пакеты данных. Потому что структуру в ее самом девственном виде проще читать и давать ее другим разработчикам, дабы понимали ее формат при поддержке протоколов общения. Когда передается 100500 разных классов, начиненных еще каким-то функционалом - становится вообще-то затруднительно сходу въехать в порядок разложения этой порнографии на байтики в реальном проводе. Структура весьма самодокументируема - сразу понятно, что за чем идет. В классе, в котором помимо данных набито еще 100 строк всяких методов и перегрузок, разобраться тяжелее сходу.
×
×
  • Создать...