9

这与微控制器有关,但考虑将其发布在这里,因为这是算法和数据类型的问题,而不是任何硬件问题。我会解释这个问题,以便没有任何硬件知识的人仍然可以参与:)

  1. 在微控制器中有一个 10 位分辨率的模数转换器。(它将输出一个介于 0 和 1023 之间的值)

  2. 我需要使用串行端口将此值发送到 PC。

  3. 但是一次只能写入 8 位。(您需要写入字节)。这是微控制器的限制。

  4. 所以在上述情况下,至少我需要发送 2 个字节。

  5. 我的 PC 应用程序只是读取一系列数字进行绘图。所以它应该捕获两个连续的字节并重新构建数字。但是在这里我们也需要一个分隔符。但定界符仍然具有 0 - 255 之间的 ascii 值,那么它将混淆该过程。

那么最简单的方法是什么?我应该将值作为字符序列发送吗?

Ex : 1023 = "1""0""2""3" Vs "Char(255)Char(4)"

总之,我需要以最快的方式通过 Serial 发送一系列 10 位数字。:)

4

4 回答 4

15

您需要发送 10 位,并且因为您一次发送一个字节,所以您必须发送 16 位。最大的问题是速度有多重要,发送者和接收者的同步程度如何?根据这些条件,我可以想到 3 个答案。

常规采样,未知连接点

如果设备一直在运行,您不确定何时连接(您可以在序列中的任何时间加入)但采样率低于通信速度,因此您不必关心大小我想我'可能会这样做如下。假设您尝试发送十位abcdefghij(每个字母一位)。

pq0abcde然后我会发送错误检查位pq1fghij的位置pq位置。这边走:

  • 不需要分隔符(您可以通过 0 或 1 来判断您正在读取哪个字节)
  • 您绝对可以发现任何 1 位错误,因此您了解不良数据

我正在努力寻找一个好的两位纠错码,所以我想我只是为位 2,3 和 4(0,上面的 ab)制作 pa 奇偶校验位,为 5 6 和 7(c, d、e 以上)。举个例子可能会更清楚。

  1. 假设我要发送 714 = 1011001010。
  2. 拆分为 2 10110 , 01010
  3. 添加位以指示第一个和第二个字节 010110、101010
  4. 计算每一半的奇偶校验:p0=par(010)=1, q0=par(110)=0, p1=par(101)=0, q1=par(010)=1
  5. 字节然后是 10010110, 01101010

然后,您可以检测到许多不同的错误情况,如果您失去同步,可以快速检查您正在发送的字节,并且在微控制器中没有任何操作需要很长时间(我会使用 8 条目查找表进行奇偶校验)。

密集数据,已知连接点

如果您知道读取器与写入器同时启动,只需将 4 个十位值作为 5 个字节发送即可。如果您总是一次读取 5 个字节,那么没有问题。如果您想节省更多空间,并且已经拥有良好的样本数据,我会使用哈夫曼编码进行压缩。

密集数据,未知连接点

在 7 个字节中,您可以发送 5 个 10 位值和 6 个备用位。像这样发送 5 个值:

  • 字节 0:0(7 位)
  • 字节 1:1(7 位)
  • 字节 2:1(7 位)
  • 字节 3:1(7 位)
  • 字节 4:0(7 位)
  • 字节 5:0(7 位)
  • 字节 6:(8 位)

然后,每当您看到最高有效位连续出现 3 个 1 时,您就知道您有字节 1、2 和 3。这个想法在 56 中浪费了 1 个位,因此可以提高效率,但您必须发送更多一次的数据。例如(5 个连续的,120 位以 16 个字节发送):

  • 字节 0:0(7 位)7
  • 字节 1:1(7 位)14
  • 字节 2:1(7 位)21
  • 字节 3:1(7 位)28
  • 字节 4:1(7 位)35
  • 字节 5:1(7 位)42
  • 字节 6:0(7 位)49
  • 字节 7:(8 位)57
  • 字节 8:(8 位)65
  • 字节 9:(8 位)73
  • 字节 10:(8 位)81
  • 字节 11:0(7 位)88
  • 字节 12:(8 位)96
  • 字节 13:(8 位)104
  • 字节 14:(8 位)112
  • 字节 15:(8 位)120

这是一个非常有趣的问题!

于 2010-10-19T08:10:59.307 回答
7

最好的方法是将数据转换为 ASCII 字符串并以这种方式发送 - 它使调试更加容易,并且避免了各种通信问题(某些控制字符的特殊含义等)。

如果您确实需要使用所有可用带宽,那么您可以将 4 个 10 位值打包成 5 个连续的 8 位字节。您需要小心同步。

于 2010-10-19T08:01:53.400 回答
4

由于您指定了“最快的方式”,我认为排除了将数字扩展到 ASCII 的可能性。

在我看来,可以通过以下编码获得代码简单性和性能的良好折衷:

两个 10 位值将像这样以 3 个字节编码。

前 10 位值位 := abcdefghij

第二个 10 位值位 := klmnopqrst

要编码的字节:

1abcdefg
0hijklmn
0_opqrst

还有一位 (_) 可用,可用于对所有 20 位进行奇偶校验以进行错误检查或仅设置为固定值。

一些示例代码(将 0 放在位置 _ 处):

#include <assert.h>
#include <inttypes.h>

void
write_byte(uint8_t byte);    /* writes byte to serial */

void
encode(uint16_t a, uint16_t b)
{
  write_byte(((a >> 3) & 0x7f) | 0x80);
  write_byte(((a & 3) << 4) | ((b >> 6) & 0x7f));
  write_byte(b & 0x3f);
}

uint8_t
read_byte(void);  /* read a byte from serial */

void
decode(uint16_t *a, uint16_t *b)
{
  uint16_t x;

  while (((x = read_byte()) & 0x80) == 0)  {}  /* sync */
  *a = x << 3;

  x = read_byte();
  assert ((x & 0x80) == 0); /* put better error handling here */

  *a |= (x >> 4) & 3;
  *b = x << 6;

  x = read_byte();
  assert ((x & 0xc0) == 0); /* put better error handling here */

  *b |= x;
}
于 2010-10-19T08:45:22.127 回答
0

我通常使用起始字节和校验和,在这种情况下是固定长度,所以发送 4 个字节,接收方可以查找起始字节,如果接下来的三个加起来是一个已知数量,那么这是一个很好的数据包,取出中间两个字节,如果不继续寻找。接收器始终可以重新同步,并且不会浪费 ascii 的带宽。Ascii 是您的另一个选择,一个不是数字的起始字节,可能是十进制的四个数字。十进制在微控制器中绝对不是有趣的,所以从像 X 这样的非十六进制开始,然后是三个字节,其中包含您的数字的十六进制 ascii 值。搜索 x 检查接下来的三个字节,希望是最好的。

于 2010-10-19T12:08:13.173 回答