0

背景:这是一个 16 位 TI DSP(准确地说是 TMS320F2812)。DSP是小端的。编译器是 C2000(不支持 PACKED 指令)。我需要通过以太网从源到目的地通信几个不同大小的结构。问题是协议需要 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 位元素,那么数组中的每个元素都将再填充 8 位以与 16 位处理器对齐。

另一种替代解决方案是在传输(或接收)时使用 char* 指针指向结构。

Q3 -> 在这种情况下,我需要手动“组合”INT8U 通道和 INT8U 优先级。如果声明了许多结构,这将变得困难。如果这种理解是错误的,请纠正我。

Q4 - >请帮助我更优雅地解决问题。我需要打包数据(包括结构内的位域和数组),但我没有编译器指令。

4

2 回答 2

1

正如评论中所描述的展开,您应该将结构注释序列化(写入时)和反序列化(读取时)到/来自字节缓冲区。

有很多方法可以做到这一点。例如,内联函数 (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以上对andaddress字段使用大端或网络字节顺序。

如果目标系统非常受 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)

在实践中,特别是如果您有许多这样的结构,这些结构的组合将起作用。首先,您编写一些紧凑的函数来访问每种类型的字段:低半字节、高半字节或 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四个高位中。

总的来说,我建议桌面应用程序的第一个选项(每个函数调用的结构转换),以及无论如何使用内部结构的情况。对于微控制器和其他内存受限的情况,我推荐这个最新的。

(以上代码均未测试。如果发现拼写错误或错误或其他错误,请在评论中通知我,以便我修复上面的示例代码。)

于 2016-12-12T11:47:17.157 回答
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;
}
于 2016-12-12T11:08:12.277 回答