背景:它是一个16位TI数字信号处理器(准确地说是TMS320F2812)。DSP是小终端。编译器是C2000 (它不支持打包指令)。我需要通过以太网通信不同大小的几个结构,从源到目的地。该协议的问题是需要打包数据(填充字节也将被视为信息)。
我目前计划做的事情:在结构中使用Bitfield
typedef struct
{
INT8U channel:4;
INT8U priority:4;
INT16U length;
INT16U address;
INT8U array[4];
} _MBXh;疑问:在这个特殊的结构中,"INT16U长度“将从一个新的对齐内存地址开始(如果我的理解是错误的,请纠正我)。因此,在“4+4优先级”之后将出现(16位-(INT8U)位=) 8位的填充。
即使使用编译器指令“->”,也会发生这种情况吗?(当然,这取决于编译器,但我的问题与我找不到信息的c99标准有关)。我发现c99提供了紧凑的包装。但是有了这个定义,我不明白"INT16U长度“是在"INT8U优先级”之后还是在8位填充之后立即开始。 结构中的Q2 ->数组不能分配位字段。如果数组中有8位元素,则数组中的每个元素都将填充8位,以便与16位处理器对齐。
另一种解决方案是使用char*指针在传输(或接收)时指向结构。
在本例中,Q3 ->需要手动“组合”INT8U通道和INT8U优先级。如果有很多的结构,这就变得困难了。如果这种理解是错误的,请纠正我。 Q4 ->请帮我找到一个更优雅的解决方案。我需要打包数据(包括结构中的位字段和数组),但我没有编译器指令。
发布于 2016-12-12 11:47:17
正如注释中所描述的那样,您应该将结构注释序列化(在写上)和反序列化(在读取时)到/从字节缓冲区。
有许多方法可以做到这一点。例如,内联函数(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字段使用了大端或网络字节顺序。
如果目标系统受到很大的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-12 11:08:12
例如,您应该序列化数据。
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;
}https://stackoverflow.com/questions/41099191
复制相似问题