0

我对用 C 语言写作比较陌生。我使用我在网上和印刷品中找到的资源自学了。这是我第一个真正的 C 编程项目。一定要热爱在职培训。

我正在用 C 语言编写一些用于德州仪器 C6701 数字信号处理器的代码。具体来说,我正在编写一组通信函数来通过串行端口进行接口。

我正在进行的项目有一个现有的数据包协议,用于通过串行端口发送数据。这是通过将指针传递给要传输的数据及其长度(以字节为单位)来实现的。我所要做的就是将要传输的字节写入内存中的“数组”(发送器将该字节序列复制到缓冲区中并传输它)。

我的问题与如何最好地格式化要传输的数据有关,我必须发送的数据由几种不同的数据类型(unsigned char、unsigned int、float 等)组成。我无法将所有内容扩展为浮点(或整数),因为我的通信带宽有限,并且需要保持数据包尽可能小。

我本来想用数组来格式化数据,

unsigned char* dataTx[10];
dataTx[0]=char1;
dataTx[1]=char2;
etc...

这会起作用,除非我的所有数据都是 char,有些是 unsigned int 或 unsigned short。

为了处理 short 和 int,我使用了位移(现在让我们忽略 little-endian 与 big-endian)。

unsigned char* dataTx[10];
dataTx[0]=short1>>8;
dataTx[1]=short1;
dataTx[2]=int1>>24;
dataTx[3]=int1>>16;
etc...

但是,我相信另一种(更好的?)方法是使用指针和指针算术。

unsigned char* dataTx[10]
*(dataTx+0) = int1;
*(dataTx+4) = short1;
*(dataTx+6) = char1;
etc...

我的问题最后)是,哪种方法(位移位或指针算术)是更可接受的方法?另外,一个跑得更快吗?(我也有运行时限制)。

我的要求:数据连续存储在内存中,没有间隙、中断或填充。

我对结构的了解还不够,还不知道结构是否可以作为解决方案。具体来说,我不知道一个结构是否总是连续且不间断地分配内存位置。我读到一些东西,表明它们分配 8 字节块,并可能引入填充字节。

现在我倾向于指针方法。感谢您阅读这篇似乎很长的帖子。

4

4 回答 4

0

您可能想要使用联合数组。

于 2011-08-24T21:23:56.173 回答
0

处理问题的最简单和最传统的方法是设置要发送的数据,然后将指向数据的指针传递给传输例程。最常见的例子是 POSIXsend()例程:

ssize_t send(int socket, const void *buffer, size_t length, int flags);

对于您的情况,您可以简化为:

ssize_t send(const void *buffer, size_t length);

然后使用类似的东西:

send(&int1, sizeof int1);
send(&short1, sizeof short1);

将其发送出去。您的情况的一个示例(但非常幼稚)实现可能是:

ssize_t send(const void *buffer, size_t length)
{
  size_t i;
  unsigned char *data = buffer;

  for (i = 0; i < length; i++)
  {
     dataTx[i] = data[i];
  }
}

换句话说,使用自动转换到void *然后返回到char *以逐字节访问您的数据,然后将其适当地发送出去。

于 2011-08-24T21:28:55.683 回答
0

长问题,我会尝试更短的答案。

不要继续 *(dataTx+4) = short1; 等等,因为这种方法可能会失败,因为大多数芯片可能只在某些对齐的位置上进行读/写。您可以按 16 位访问按 2 对齐的位置,在按 4 对齐的位置上访问 32 位,但举个例子:“int32 char8 int32” - 第二个 int32 的位置为 (dataTx+5) - 不是 4 字节对齐,你可能会得到“总线错误”或类似的东西(取决于你将使用的 CPU)。希望你理解这个问题。

第一种方式 - 如果您声明,您可以尝试 struct:

struct
{
    char a;
    int b;
    char c;
    short d;
};

你现在没有麻烦了,因为编译器本身会处理结构对齐。当然,请阅读编译器中与对齐相关的选项(如果这是 gcc,则简称为对齐),因为可能存在强制结构字段对齐或结构字段打包的设置。GCC 甚至可以定义每个结构的对齐(更多在这里)。

另一种方法是使用一些“类似缓冲区的方法”——比如在 Carl Norum 的回答帖子中(我不会重复那个答案),但也考虑在复制更多数据时使用 memcpy() 调用(例如 long long 或 string),因为这可能比逐字节复制更快。

于 2011-08-24T21:40:47.943 回答
0

通常您会使用位移方法,因为许多芯片不允许您将例如 4 字节整数复制到奇数字节地址(或者更准确地说,复制到从奇数字节地址开始的一组 4 字节)。这称为对齐。如果可移植性是一个问题,或者如果您的 DSP 不允许未对齐的访问,则需要进行移位。如果您的 DSP 因访问未对齐而导致性能显着下降,您可能会担心。

但是,我不会编写代码,其中不同类型的转换如图所示。我希望使用函数(可能是内联的)或宏来处理数据的序列化和反序列化。例如:

unsigned char dataTx[1024];
unsigned char *dst = dataTx;

dst += st_int2(short1, dst);
dst += st_int4(int1, dst);
dst += st_char(str, len, dst);
...

在函数形式中,这些函数可能是:

size_t st_int2(uint16_t value, unsigned char *dst)
{
    *dst++ = (value >> 8) & 0xFF;
    *dst   = value & 0xFF;
    return 2;
}

size_t st_int4(uint32_t value, unsigned char *dst)
{
    *dst++ = (value >> 24) & 0xFF;
    *dst++ = (value >> 16) & 0xFF;
    *dst++ = (value >>  8) & 0xFF;
    *dst   = value & 0xFF;
    return 4;
}

size_t st_char(unsigned char *str, size_t len, unsigned char *dst)
{
    memmove(dst, str, len);
    return len;
}

当然,这样的功能会让代码变得乏味;另一方面,它们也减少了出错的机会。您可以决定名称是否应该st_uint2()代替st_int2()-- 并且实际上,您可以决定长度应该是字节(如这里)还是位(如参数类型)。只要你始终如一且无聊,你就可以为所欲为。您还可以将这些函数组合成更大的函数来打包整个数据结构。

& 0xFF现代编译器可能不需要屏蔽操作 ( )。很久以前,我似乎记得它们对于避免某些平台上的某些编译器偶尔出现问题是必要的(因此,我的代码可以追溯到 1980 年代,其中包含此类屏蔽操作)。所述平台可能已经平静下来,所以我认为它们(仍然)在那里可能是纯粹的偏执狂。

请注意,这些函数以大端顺序传递数据。这些函数可以在 big-endian 和 little-endian 机器上“按原样”使用,并且数据将在这两种类型上正确解释,因此您可以使用此代码通过网络进行各种硬件通话,并且会有没有误会。如果您要传达浮点值,则必须更多地担心网络上的表示。尽管如此,您可能应该以平台中立的格式传输数据,以便芯片类型之间的互通尽可能简单。(这也是我使用带有数字的类型大小的原因;特别是“int”和“long”在不同平台上可能意味着不同的东西,但 4 字节有符号整数仍然是 4 字节有符号整数,

于 2011-08-24T21:48:57.573 回答