【问题标题】:How to invert the bits of stored at memory address?如何反转存储在内存地址的位?
【发布时间】:2019-07-18 20:58:40
【问题描述】:

我正在尝试通过光耦合器电平转换器发送 SPI 数据。 (基本上是把3.3v信号转成5v信号)

在这个过程中,电平转换器翻转所有位(反转逻辑),而不是使用另一个反相器来重新对齐信号,我认为简单地从 SPI 端口发送反转位会更容易。

为此,我将片选上的模式设置为高电平有效并更改了时钟极性。现在我试图对我发送的数据进行位翻转。

  unsigned short* tx;
  unsigned short* rx;
  while(!feof(data_file)){
    fread(buffer, 2, 80, data_file);
    tx = buffer;
    for(int i = 0; i < 80; i++){
      (*tx)= ~(*tx); //added this line
      spi.transfer((unsigned char*) tx,(unsigned char*) rx, 2);
      (*rx)= ~(*rx); //also added this line
      printf("0x%04x\n", *rx);
      pinControl.selectChip(i%8);
      usleep(5);
    }

在添加这个之前,有很好的干净输出 收到的数据:

0x04cb
0x04cb
0x04cb
0x04cb
0x04cb
0x04cb
0x04cb
0x050b
0x050b
0x050b
0x050b

反转后:

0xfd36
0x02c9
0xfd36
0x02c9
0xfd36
0x02c9
0xfd36
0x02c9
0xfd36
0x02c9
0xfd36
0x02c9
0xfd36
0x02c9

注意我发送的数据是单调递增的,所以第二个输出不正确。

我的假设是

(*tx) = ~(*tx)

实际上并没有反转位。

所以我尝试验证这个理论。

  unsigned short* tx;
  unsigned short* rx;
  (*tx)=4;
  std::cout << *tx << endl;
  std::cout << ~(*tx) << endl;
  (*tx) = ~(*tx);
  std::cout << (*tx);

产量:

4
-5 <-- this is ~4 correct
65531 <-- this is unexpected

【问题讨论】:

  • rx 指向哪里?它是未初始化的。 buffer 是什么?而while feof 总是错的。
  • 不相关:有机会时给Why is “while (!feof(file))” always wrong?一读
  • 您的问题不在于反转位,这是可以高度自信地声称的事情。 minimal reproducible example发布后可以说更多。
  • 另请注意,65531 是 无符号 16 位整数,其位模式与带符号的 16 位二进制补码整数相同,值为 -5。
  • 示例代码读取(最多)80 个unsigned shorts,但随后输出第一个 80 次(正负反转),忽略其余部分。

标签: c++ beagleboneblack spi ioctl


【解决方案1】:

您的两个代码示例中的问题是指针使用不正确。 您正确地声明了指针,但没有为它们分配有效的内存地址。

例如,当你这样做时:

unsigned short* tx;
(*tx)=4;

tx 包含一些随机地址,因为您没有分配它。 因此,当您取消引用它并尝试将 4 放在下一行时,没有办法知道这个 4 的实际位置。

tx 中的值可能为 0 或接近它,您的程序将崩溃 (SEGFAULT)。 或者,其他一些代码可能正在使用该内存,并会覆盖该值,这可以解释为什么您突然得到 65531,尽管它与之前的值无关。

在带有 SPI 调用的代码示例中,存在两个问题:

  1. 你永远不会在循环中推进 tx 指针,所以你总是反转并发送相同的两个字节。

  2. 您没有为rx 指针分配任何有效内存,因此与您的测试一样,数据会随机进入,您的程序可能会随机崩溃。

最后,最大的问题是:你是如何声明buffer 的? 如果以与rxtx 相同的方式声明,则即使从文件中读取也可能无法正常工作。

这是您的代码应该可以工作的版本:

unsigned short tx;
unsigned short rx;

while(fread(&tx, 2, 1, data_file) > 0) {
   tx = ~tx;
   spi.transfer(&tx, &rx, 2);
   rx = ~rx; //also added this line
   printf("0x%04x\n", rx);
   pinControl.selectChip(i % 8);
   usleep(5);
}

【讨论】:

  • 有点奇怪,代码之前在某些情况下工作但后来停止了......也许它不能正常工作,但似乎只是这样。尝试您的解决方案后,它现在似乎工作正常。谢谢!
  • @DustinK 该代码工作了一些时间,因为有时,在您的程序加载之前留在内存中的随机值对于您的程序需要执行的操作来说“足够好”。这就是 C 和 C++ 的工作方式:它们允许您读取和写入属于程序的任何内存,并在为变量赋值之前使用变量,但结果是不可预测的。评论中没有足够的字符来进一步解释,但谷歌“缓冲区溢出”以了解更多关于这种恶作剧的信息。
【解决方案2】:

rx 是一个未初始化的指针。它恰好指向一些恰好有效的可访问内存位置。你需要把它指向一个缓冲区,就像你对tx所做的那样。

您的程序(假定)从文件中读取 80 个字的数据,因此其意图大概是通过spi.transfer 传输这些数据。然而,你的循环,虽然它计算了 80 个字,但并没有增加 txrx 指针。

因此,您正在做的是反复反转 buffer[0] 的位,tx 始终指向。同样,rx 一直指向相同的未知内存位置,这是切换的。

您看到的重复值对:

0xfd36
0x02c9

实际上是彼此的按位倒数。如果你知道十六进制数字和二进制 nybbles 的对应关系,这就很明显了。

您还必须测试fread 的返回值。 fread 返回它能够读取的元素数量,可能低至零。

while (!feof(...)) 模式不正确。对于尚未尝试输入操作的新打开的流,feof(f) 始终为 false。

stdio 范式是必须单独测试 I/O 操作的返回值。如果输入操作失败,那可能是由于数据结束,或者由于某些 I/O 错误。在流 f 输入失败后,feof(f)ferror(f) 两个标志之一为真,以区分数据结束和 I/O 错误。

【讨论】:

  • 无符号短* tx;无符号短* rx; tx = (unsigned short*)calloc(1,2); rx = (unsigned short*)calloc(1,2);那是原始代码,尝试发布悬崖笔记版本,但这就是我实例化原始指针的方式
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-25
  • 2011-04-18
  • 1970-01-01
  • 2020-02-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多