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

Arlleex

Свой
  • Постов

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

  • Посещение

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

    14

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

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

Репутация

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

2 Подписчика

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

Контакты

  • ICQ
    Array

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

17 724 просмотра профиля
  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-у можно не кодировать предварительно, а кодировать по мере отправки очередного символа. С тем же езернетом так не получится - внутри одного езернет-кадра будь добр подготовить все нужные данные сразу.
×
×
  • Создать...