2016-12-12 12 views
0

Фон: Это 16-битный TI DSP (TMS320F2812, если быть точным). DSP является малоподвижным. Компилятор - C2000 (который НЕ поддерживает директиву PACKED). Мне нужно передать несколько структур различных размеров от источника к месту назначения через ethernet. Проблема, являющаяся протоколом, требует данных PACKED (заполненные байты также будут рассматриваться как информация).Данные об упаковке без использования директивы компилятора «УПАКОВАНО» в C

Я в настоящее время планирую сделать: Используйте битовые в структурах

typedef struct 
{ 
    INT8U channel:4; 
    INT8U priority:4; 
    INT16U length; 
    INT16U address; 
    INT8U array[4]; 
} _MBXh; 

Сомнения: В данной структуре «длина INT16U» будет начинаться с новым выровненным адресом памяти (пожалуйста, поправьте меня, если мое понимание ошибочно). Следовательно, после «приоритета INT8U» будет заполнение (16 бит - (4 + 4) бит =) 8 бит.

Q1 -> Это происходит даже с директивой «pack»? (конечно, это зависит от компилятора, но мой вопрос связан со стандартом c99, на котором я не мог найти информацию). Я обнаружил, что c99 обеспечивает плотную упаковку. Но с этим определением я не понимаю, будет ли «INT16U length» запускаться сразу после «приоритета INT8U» или после 8-битного заполнения.

Q2 -> Массивы внутри структур не могут быть назначены битполам. Если в массиве есть 8-битные элементы, то каждый элемент массива будет дополнен еще 8 битами для согласования с 16-разрядным процессором.

Другим альтернативным решением является использование указателя char * для указания на структуру во время передачи (или приема).

Q3 -> В этом случае мне необходимо вручную «комбинировать» канал INT8U и приоритет INT8U. Что станет сложным, если объявлено много заявленных структур. Пожалуйста, исправьте меня, если это понимание неверно.

Q4 -> Пожалуйста, помогите мне с более элегантным решением проблемы. Мне нужно упаковать данные (включая битовые поля и ARRAYS внутри структур), но у меня нет директивы компилятора.

+3

Использование битовых полей в структуре, которая вам планировать писать дословно в сети - это, пожалуй, самый худший возможный случай использования. Вы должны написать код для сериализации/де-сериализации, чтобы вы могли контролировать каждый байт и не зависели от того, что делает компилятор. – unwind

+0

Существует никогда (или всегда) дополнение битов. Заполнение относится только к байтам. Битовые поля занимают целый ряд байтов, например, 'channel' - один байт. –

+0

@unwind - Это правильно. Я хотел бы сериализовать/де-сериализовать. Но как это сделать в общем случае, если существует 10 различных структур разных размеров и переменных? Можете ли вы помочь мне подумать об альтернативе? Все ответы до сих пор являются специфическими для этой структуры ... Но получение универсальной работы для любой структуры - это то, о чем я не могу думать (ограничение моих мозгов). –

ответ

0

Вы должны сериализовать данные, например

static unsigned char * Ser_uint16_t(unsigned char *p, uint16_t value) 
{ 
    *p++ = (value >> 8u) & 0xFFu; 
    *p++ = (value >> 0u) & 0xFFu; 

    return p; 
} 

void serialize(INT8U channel, 
       INT8U priority, 
       INT16U length, 
       INT16U address, 
       INT8U array[4] 
       unsigned char *out_buffer) 
{ 
    out_buffer++ = ((channel & 0x0F) << 4) | (priority & 0x0F); 
    out_buffer = Ser_uint16_t(out_buffer, length); 
    out_buffer = Ser_uint16_t(out_buffer, address); 
    memcpy(out_buffer, array, 4); 
} 

и де-сериализации их на другой стороне, как

static uint16_t DeSer_uint16_t(unsigned char **p) 
{ 
    uint16_t x = 0; 

    x |= ((uint16_t)(*p)[0]) << 8u; 
    x |= ((uint16_t)(*p)[1]) << 0u; 

    *p += 2; 

    return x; 
} 
+0

Работает ли ваш код с большим или малозначимым? –

+0

@PaulOgilvie Да, как вы можете видеть, байт 'unit16_t' хранится в определенном формате. Таким образом, вы можете десериализовать его правильно на приемнике. – LPs

0

Как размотки, описанной в комментарии, вы должны вместо сериализации (при записи) и десериализации (по чтению) комментариев структуры в/из байтового буфера.

Существует несколько способов сделать это. Например, встроенные функции (C99 static inline), макросы препроцессора, отдельные функции для каждого поля, общая функция для бит-пакетов и т. Д.

Наиболее распространенным вариантом является упаковка и распаковка байт-массивов из/в внутренние структуры. Например, для внутренне используемой структуры

struct mbxh { 
    INT8U channel:4; 
    INT8U priority:4; 
    INT16U length; 
    INT16U address; 
    INT8U array[4]; 
}; 

static void pack_mbxh(unsigned char *const dst, const struct mbxh *src) 
{ 
    dst[0] = src->channel | ((src->priority) << 4); 
    dst[1] = src->length >> 8; 
    dst[2] = src->length; 
    dst[3] = src->address >> 8; 
    dst[4] = src->address; 
    dst[5] = src->array[0]; 
    dst[6] = src->array[1]; 
    dst[7] = src->array[2]; 
    dst[8] = src->array[3]; 
} 

static void unpack_mbxh(struct mbxh *dst, const unsigned char *const src) 
{ 
    dst->channel = src[0] & 15U; 
    dst->priority = (src[0] >> 4) & 15U; 
    dst->length = (src[1] << 8) | src[2]; 
    dst->address = (src[3] << 8) | src[4]; 
    dst->array[0] = src[5]; 
    dst->array[1] = src[6]; 
    dst->array[2] = src[7]; 
    dst->array[3] = src[8]; 
} 

Это особенно полезно, потому что это делает его тривиальным, чтобы указать порядок следования байтов; в приведенном выше примере используется порядок байтов или байтов сети для полей length и address.

Если целевая система очень ограничена оперативной памятью, использование макросов препроцессора для прямого доступа к «упакованным» полям часто является хорошим вариантом. Это использует меньше памяти, но больше ресурсов ЦП. (Обратите внимание, что «упакованные» поля используют тупоконечник или сетевой порядок байт здесь тоже.)

#define mbxh_get_channel(data) ((data)[0] & 15U) 
#define mbxh_get_priority(data) ((data)[0] >> 4) 
#define mbxh_get_length(data) ((((INT16U)(data)[1]) << 8) | ((INT16U)(data)[2])) 
#define mbxh_get_address(data) ((((INT16U)(data)[3]) << 8) | ((INT16U)(data)[4])) 
#define mbxh_get_array(data, i) ((data)[i]) 

#define mbxh_set_channel(data, value)         \ 
     do {               \ 
      (data)[0] = ((data)[0] & 240U) | ((INT8U)(value)) & 15U); \ 
     } while (0) 

#define mbxh_set_priority(data, value) \ 
     do {       \ 
      (data)[0] = ((data)[0] & 15U) | (((INT8U)(value)) & 15U) << 4); \ 
     } while (0) 

#define mbxh_set_length(data, value)   \ 
     do {         \ 
      (data)[1] = ((INT16U)(value)) >> 8; \ 
      (data)[2] = (INT8U)(value);   \ 
     } while (0) 

#define mbxh_set_address(data, value)   \ 
     do {         \ 
      (data)[3] = ((INT16U)(value)) >> 8; \ 
      (data)[4] = (INT8U)(value);   \ 
     } while (0) 

#define mbxh_set_array(data, index, value) \ 
     do {         \ 
      (data)[(index)] = (INT8U)(value); \ 
     } while (0) 

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

static INT8U get4u_lo(const INT8U *const ptr) 
{ 
    return (*ptr) & 15U; 
} 

static INT8U get4u_hi(const INT8U *const ptr) 
{ 
    return (*ptr) >> 4; 
} 

static INT16U get16u(const INT8U *const ptr) 
{ 
    return (((INT16U)ptr[0]) << 8) | ptr[1]; 
} 

static void set4u_lo(INT8U *const ptr, INT8U val) 
{ 
    *ptr &= 240U; 
    *ptr |= val & 15U; 
} 

static void set4u_hi(INT8U *const ptr, INT8U val) 
{ 
    *ptr &= 15U; 
    *ptr |= (val % 15U) << 4; 
} 

static void set16u(INT8U *const ptr, INT16U val) 
{ 
    ptr[0] = val >> 8; 
    ptr[1] = val; 
} 

Далее, вы написать полевые аксессоров за структуры с использованием выше вспомогательные функции:

#define mbxh_get_channel(data) get4u_lo((INT8U *)(data)+0) 
#define mbxh_get_priority(data) get4u_hi((INT8U *)(data)+0) 
#define mbxh_get_length(data) get16u((INT8U *)(data)+1) 
#define mbxh_get_address(data) get16u((INT8U *)(data)+3) 
#define mbxh_get_array(data, i) ((data)[5+(i)]) 

#define mbxh_set_channel(data, v) set4u_lo((INT8U *)(data)+0, (v)) 
#define mbxh_set_priority(data, v) set4u_hi((INT8U *)(data)+0, (v)) 
#define mbxh_set_length(data, v) set16u((INT8U *)(data)+1, (v)) 
#define mbxh_set_address(data, v) set16u((INT8U *)(data)+3, (v)) 
#define mbxh_set_array(data, i, v) ((data)[5+(i)] = (v)) 

Как и во всех примерах, приведенных в этом ответе, выше тоже использовать тупоконечника или сетевой порядок байтов данных. channel находится в четырех младших разрядах и priority в четырех старших разрядах первого байта данных.

В целом, я рекомендую первый вариант (преобразование структуры для вызова функции) для настольных приложений и для случаев, когда вы используете внутреннюю структуру. Для микроконтроллеров и других случаев с ограниченной памятью я рекомендую эту последнюю.

(Ни один из приведенной выше коды не проверяются. Если вы обнаружили опечатку или ошибку или другие ошибки, пожалуйста, сообщите мне в комментариях, так что я могу исправить пример код выше.)

 Смежные вопросы

  • Нет связанных вопросов^_^