2016-12-12 6 views
0

背景: 16ビットTI DSP(正確にはTMS320F2812)です。 DSPはリトルエンディアンです。コンパイラはC2000(PACKEDディレクティブをサポートしていません)です。私は、さまざまなサイズの構造をイーサネット経由で送信元から送信先に伝達する必要があります。プロトコルに問題があると、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ビットのパディングが存在する。

Q1 - >これはコンパイラ指令 "pack"でも発生しますか? (もちろん、それはコンパイラに依存しますが、私の質問は、私が情報を見つけることができなかったc99標準に関連しています)。私が見つけたのは、c99がきつく詰まっていることでしたしかし、その定義では、 "INT16Uの長さ"が "INT8Uの優先順位"の直後か8ビットのパディング後に開始するかどうかは分かりません。

Q2 - 構造内の配列にはビットフィールドを割り当てることができません。配列内に8ビットの要素がある場合、配列のすべての要素は16ビットプロセッサに合わせるためにさらに8ビットで埋められます。

もう1つの代替ソリューションは、送信(または受信)時に構造体を指すためにchar *ポインターを使用することです。

Q3 - >この場合、私は手動でINT8UチャネルとINT8U優先度を "結合"する必要があります。宣言された構造が多いと難しくなります。この理解が間違っている場合は、私を修正してください。

Q4 - >問題をより洗練された方法で解決してください。データをパックする必要があります(ビットフィールドとARRAYS構造体を含む)が、私はコンパイラ指令を持っていません。

+3

逐語的にネットワークに書き込む計画は、最悪の場合のユースケースです。シリアライズ/デシリアライズするためのコードを書く必要があります。これにより、コンパイラが何をしているかに依存せずに各バイトを制御できるようになります。 – 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'のバイトが特定のフォーマットで格納されているのがわかります。したがって、受信機で正しく逆シリアル化することができます。 – 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は4つの下位ビットにあり、最初のデータバイトの上位4ビットにはpriorityです。

全体として、デスクトップアプリケーションの場合と、内部構造を使用する場合の最初のオプション(関数呼び出しごとの構造変換)をお勧めします。マイクロコントローラやその他の制約付きメモリの場合、私はこの最新のものをお勧めします。そのあなた構造のビットフィールドを使用して

(上記のコードのいずれもがテストされていません。あなたがタイプミスやバグやその他のエラーを見つけた場合は、コメントの私を知らせ、私は上記の例のコードを修正することができますしてください。)

関連する問題