работает с uint8_t на MCU без типа данных uint8_t - PullRequest
0 голосов
/ 08 ноября 2018

Я разработчик встраиваемого программного обеспечения и хочу подключиться к внешнему устройству. Это устройство отправляет данные через SPI. Структура этих данных предопределена производителем внешнего устройства и не может быть отредактирована. Производитель предоставляет несколько заголовочных файлов со многими определениями типов всех данных, отправляемых через SPI. Производитель также предлагает API для правильной обработки полученных пакетов (у меня есть доступ к источнику этого API).

Теперь к моей проблеме: Структуры с определением типов содержат много типов данных uint8_t. К сожалению, наш MCU не поддерживает типы данных uint8_t, потому что самый маленький тип имеет ширину 16 бит (так что даже символ имеет 16 бит).

Для правильного использования API структуры должны быть заполнены данными, полученными через SPI. Поскольку входящие данные являются байтовыми пакетами, мы не можем просто скопировать эти данные в структуру, потому что наши структуры используют 16-битные для этих 8-битных типов. В результате нам нужно выполнить много бит-операций для правильного назначения полученных данных.

ПРИМЕР: (производители typedef struct)

typedef struct NETX_COMMUNICATION_CHANNEL_INFOtag
{
  uint8_t   bChannelType;              //uint16_t in our system
  uint8_t   bChannelId;                //uint16_t in our system
  uint8_t   bSizePositionOfHandshake;  //uint16_t in our system
  uint8_t   bNumberOfBlocks;           //uint16_t in our system
  uint32_t  ulSizeOfChannel;           
  uint16_t  usCommunicationClass;      
  uint16_t  usProtocolClass;           
  uint16_t  usProtocolConformanceClass;
  uint8_t   abReserved[2];             //uint16_t in our system
} NETX_COMMUNICATION_CHANNEL_INFO;

Кто-нибудь может придумать легкий способ обойти эту проблему? Я действительно не хочу писать отдельную операцию битового сдвига для каждого полученного типа пакета. (Производительность / время / пространственно-отходы)

Моя идея (использование битовых полей для вставки 2xuint8_t в uint16_t или 4xuint8_t в uint32_t)

typedef struct NETX_COMMUNICATION_CHANNEL_INFOtag
{
  struct packet_uint8{
    uint32_t  bChannelType              :8;
    uint32_t  bChannelId                :8;
    uint32_t  bSizePositionOfHandshake  :8;
    uint32_t  bNumberOfBlocks           :8;
  }packet_uint8;
  uint32_t  ulSizeOfChannel;               
  uint16_t  usCommunicationClass;          
  uint16_t  usProtocolClass;               
  uint16_t  usProtocolConformanceClass;    
  uint16_t  abReserved;                    
} NETX_COMMUNICATION_CHANNEL_INFO;

Теперь я не уверен, будет ли работать это решение, поскольку порядок битов внутри битового поля не обязательно соответствует порядку в исходном файле. (или если все битовые поля имеют одинаковый размер?)

Надеюсь, я достаточно хорошо описал проблему, чтобы вы ее поняли.

Спасибо и всего наилучшего.

Ответы [ 3 ]

0 голосов
/ 08 ноября 2018

В вашем руководстве по компилятору должно быть описано, как расположены битовые поля. Прочитайте это внимательно. Существует также нечто, называемое __attribute__((byte_peripheral)), которое должно помочь в разумной упаковке битовых полей в устройствах с отображенной памятью.


Если вы не уверены в битовых полях , просто используйте uint16_t для этих полей и макрос доступа с битовыми сдвигами, например

#define FIRST(x) ((x) >> 8)
#define SECOND(x) ((x) & 0xFF)

...
    uint16_t channel_type_and_id;
...

int channel_type = FIRST(x->channel_type_and_id);
int channel_id = SECOND(x->channel_type_and_id);

Тогда вам просто нужно быть уверенным в порядке следования байтов платформы. Если вам нужно изменить порядок байтов, который MCU поддерживает? Вы можете просто переопределить эти макросы.


Скорее всего, битовое поле все еще будет реализовано в терминах битовых сдвигов, поэтому не будет большой экономии - а если бы было функциями доступа к байтам для регистров, тогда компилятор знал бы, что оптимизировать x & 0xff использовать их

0 голосов
/ 08 ноября 2018

В соответствии с документацией компилятора, доступ к байту осуществляется через встроенные функции

Для доступа к данным с шагом 8 бит используйте встроенные функции __byte () и __mov_byte (), описанные в разделе 7.5.6.

Если вы хотите, вы можете создать новый тип для инкапсуляции доступа к байтам - что-то вроде пары байтов или класса TwoByte, размер которого будет равен 16 битам.

Для вдохновения посмотрите, как шаблонный класс std::bitset реализован в STL для аналогичной задачи.https://en.cppreference.com/w/cpp/utility/bitset

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

0 голосов
/ 08 ноября 2018

Подход битового поля может работать на практике. Хотя вам нужен какой-то способ проверить или убедиться, что он правильно упакован для вашей целевой платформы. Подход с битовыми полями не будет переносимым, поскольку вы сами заявляете, что порядок битовых полей зависит от платформы.

...