2016-12-12 11 views
0

배경 : 16 비트 TI DSP (정확한 TMS320F2812)입니다. DSP는 리틀 엔디안입니다. 컴파일러는 C2000 (PACKED 지시문을 지원하지 않음)입니다. 이더넷을 통해 원본에서 대상까지 다양한 크기의 여러 구조를 전달해야합니다. 프로토콜이되는 문제는 패킹 된 데이터를 필요로합니다 (패딩 된 바이트도 정보로 간주됩니다).C에서 "PACKED"컴파일러 지시문을 사용하지 않고 데이터 패킹

내가 현재 할 계획 무엇 : 를 사용하여 비트 필드를 구조에

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

: 이 특정 구조에서이은 "INT16U 길이가"(경우에 저를 수정하시기 바랍니다 새로운 정렬 된 메모리 주소에서 시작됩니다 나의 이해는 틀리다). 따라서 "INT8U 우선 순위"다음에 (16 비트 - (4 + 4) 비트 =) 8 비트가 채워집니다.

1 ->이 문제는 컴파일러 지시문 "pack"에서도 발생합니까? (당연히 컴파일러에 달려 있지만 내 질문은 정보를 찾을 수없는 c99 표준과 관련이있다). 내가 찾은 것은 c99가 단단한 포장을 제공한다는 것입니다. 그러나 "INT16U 길이"가 "INT8U 우선 순위"바로 뒤 또는 8 비트 패딩 이후에 시작되는지는 정의에 따라 이해할 수 없습니다.

Q2 -> 구조 내부의 배열에는 비트 필드를 할당 할 수 없습니다. 배열 내에 8 비트 요소가 있으면 배열의 모든 요소는 16 비트 프로세서에 정렬하기 위해 8 비트 더 채워집니다.

다른 대안은 char * 포인터를 사용하여 전송 (또는 수신)시 구조체를 가리키는 것입니다.

Q3 ->이 경우에는 수동으로 INT8U 채널과 INT8U 우선 순위를 "결합"해야합니다. 선언 된 구조가 많으면 어려워 질 것입니다. 이 이해가 잘못되면 나를 바로 잡으십시오.

Q4 -> 제발 좀 더 우아한 해결책을 도와주세요. 데이터를 압축 할 필요가 있지만 (구조체 안에 비트 필드와 배열 포함) 컴파일러 지시문이 없습니다.

+3

구조에 비트 필드를 사용하여 당신을 축 어적으로 네트워크에 작성하려는 계획은 가능한 최악의 유스 케이스입니다. serialize/de-serialize 할 코드를 작성해야 각 바이트를 제어 할 수 있고 컴파일러가 수행하는 작업에 의존하지 않아도됩니다. – unwind

+0

비트 채우기가 절대로 (또는 항상) 있습니다. 패딩은 바이트 만 고려합니다. 비트 필드는 전체 바이트 수를 차지합니다. 'channel'은 1 바이트입니다. –

+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'의 바이트가 특정 형식으로 저장되어 있음을 알 수 있습니다. 따라서 수신기에서이를 deserialize 할 수 있습니다. – 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]; 
} 

이것은 바이트 순서를 지정하는 것이 쉽기 때문에 특히 유용합니다. 위의 예에서는 lengthaddress 필드에 대해 빅 엔디안 또는 네트워크 바이트 순서를 사용합니다.

대상 시스템이 매우 RAM 제약이있는 경우 전처리 매크로를 사용하여 "압축"필드에 직접 액세스하는 것이 좋은 옵션입니다. 이렇게하면 메모리는 적지 ​​만 CPU 리소스는 많이 사용됩니다. (이하 "포장"분야도 여기에 빅 엔디안 또는 네트워크 바이트 순서를 사용합니다.) 실제로

#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) 

, 당신은 많은 이러한 구조를 가지고 특히, 이들의 조합이 작동합니다.

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; 
} 

다음, 당신은을 사용하여 당 구조 현장 접근을 쓰기, 낮은 니블, 높은 니블 또는 16 비트 필드 : 첫째, 각 유형 필드의에 액세스하는 일부 소형 함수를 작성 도우미 기능 이상 :

#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은 첫 번째 데이터 바이트의 네 개의 상위 비트에 있습니다.

전반적으로 데스크톱 응용 프로그램의 경우 첫 번째 옵션 (함수 호출 당 구조체 변환)과 내부 구조를 사용하는 것이 좋습니다. 마이크로 컨트롤러 및 기타 제한된 메모리 케이스의 경우이 최신 버전을 사용하는 것이 좋습니다.

(위의 코드 중에 테스트하지 않습니다 당신은 오타 나 버그 나 기타 오류를 발견하면. 댓글에서 나를 알려 주시기 바랍니다, 그래서 위의 예제 코드를 수정할 수 있습니다.)