【问题标题】:Arduino compilation weirdnessArduino编译怪异
【发布时间】:2013-12-24 07:18:39
【问题描述】:

我正在开发 OSX Mavericks、标准 Arduino.app 和 Arduino 2560 板。

这是迄今为止我遇到过的最奇怪的错误

长话短说,这段代码:

  #include "Arduino.h"
  #include "Bitstreamer.h"
  #include "TimerOne.h"
  #include "BusyWaitByteProtocol.h"



  Bitstreamer::Bitstreamer(int pin)
  {
    _pin = pin;
    stage = 0;
    bitholder = new boolean[8];
    bitindex = 0;
    bp = new BusyWaitByteProtocol (128);
  }


  Bitstreamer* Bitstreamer::Daemonholder = NULL;
  void Bitstreamer::RunDaemon()
  {
    Bitstreamer::Daemonholder->Run();
  }

  void Bitstreamer::Listen()
  {
     myTurn = false;
     Init();
  }

  void Bitstreamer::Shout()
  {
     myTurn = true;
     Init();
  }


  void Bitstreamer::Init()
  {
     Daemonholder = this;
     Timer1.initialize(cycleRate);
     Timer1.attachInterrupt(RunDaemon); 
     stage++;
     screamcounter = 0;
     bitcounter = 0;
     lastbit = false;
  }

  void Bitstreamer::Run()
  {
    if (stage == 1)
    {
      //We are initializing the protocol. If we are the shouter, scream for a bit. If we are the listener, wait for scream to end.
      //The scream really just serves as an initial syncronizer.
      if (myTurn)
      {
        pinMode(_pin, OUTPUT);
        digitalWrite(_pin, HIGH);
      }
      else
      {
         pinMode(_pin, INPUT);
      }
      stage++;
    }
    else if (stage == 2)
    {
      if (myTurn)
      {
        screamcounter++;
        if (screamcounter > 100)
        {
          digitalWrite(_pin, LOW);
          stage++;
        }
      }
      else
      {
          int state = digitalRead(_pin);
          if (state == 1)
          {
            screamcounter++;
          }
          else
          {
            if (screamcounter > 30)
            {
              stage++;
            }
            screamcounter = 0;
          }
      }
    }
    else if (stage == 3)
    {
      if (myTurn)
      {
        bitcounter++;
        if (bitcounter == 0)
        {
            digitalWrite(_pin, bitholder[bitindex]?HIGH:LOW);
        }
        else
        {
          if (bitcounter >= resendrate)
          {
            bitcounter = -1;
            bitindex++;
          }
          if (bitindex >= 8)
          {
            bitindex = 0;
            uint8_t nb = bp->RequestByte();
            ReadBits(nb, bitholder);
            //Swap turns as well
          }
        }
      }
      else
      {
        boolean state = (digitalRead(_pin) != 1);
        if (state != lastbit)
        {
          float bits = ((float)bitcounter)/((float)resendrate);
          int finalbits = round(bits);

          debugstring = String(debugstring + String(state) + String(" ") + String(finalbits) + String("\n"));

          //Process received bits
          for (int i = 0; i < finalbits; i++)
          {
            bitholder[bitindex] = state;
            bitindex++;
            if (bitindex >= 8)
            {
              bitindex = 0;
              uint8_t nb = WriteBits(bitholder);
              bp->ProcessByte(nb);
              //Swap turns
            }
          }

          lastbit = state;
          bitcounter = 0;
        }
        else
        {
          bitcounter++;
        }
      }
    }

    /*
    if (myTurn)
    {
      if (hitcount < hits + 3)
      {
        digitalWrite(_pin, ((hitcount++)<hits)?HIGH:LOW);
      }
      else
      {
        hitcount = 0;
        hits++;
        lhits = hits;
      }
    }
    else
    {
      int state = digitalRead(_pin);
      if (state == 0)
      {
        hitcount++;
      }
      else
      {
        hitcount = 0;
        hits++;
      }
      //lhits = hits;
      if (hitcount >= 3)
      {
        lhits = hits;
        hits = 0;
      }
    }
    */
  }

  byte Bitstreamer::Read()
  {
    //Load from incoming byte buffer
    return 0;  
  }

  void Bitstreamer::Write(byte b)
  {
    //Send to outgoing byte buffer
  }

  void Bitstreamer::ReadBits(uint8_t b,  boolean* bitarray)
  {
    //DRAGONHERE: For loop would have been less code, but required initialization of another variable, and slightly more complicated logic for the processor
    //Also, not having it in a for loop makes it easier for others to understand
    bitarray[0] = (b & 1) != 0;    // 1 = 00000001, IE 1st bit. (1 & b) lets only 1st bit through. != 0 checks if zero.
    bitarray[1] = (b & 2) != 0;    // 1 = 00000010, IE 2nd bit. (2 & b) lets only 2nd bit through. != 0 checks if zero.
    bitarray[2] = (b & 4) != 0;    // 1 = 00000100, IE 3rd bit. (4 & b) lets only 3rd bit through. != 0 checks if zero.
    bitarray[3] = (b & 8) != 0;    // 1 = 00001000, IE 4th bit. (8 & b) lets only 4th bit through. != 0 checks if zero.
    bitarray[4] = (b & 16) != 0;   // 1 = 00010000, IE 5th bit. (16 & b) lets only 5th bit through. != 0 checks if zero.
    bitarray[5] = (b & 32) != 0;   // 1 = 00100000, IE 6th bit. (32 & b) lets only 6th bit through. != 0 checks if zero.
    bitarray[6] = (b & 64) != 0;   // 1 = 01000000, IE 7th bit. (64 & b) lets only 7th bit through. != 0 checks if zero.
    bitarray[7] = (b & 128) != 0;  // 1 = 10000000, IE 8th bit. (128 & b) lets only 8th bit through. != 0 checks if zero.

    //PLEASE NOTE: These explainations are based on the assumption that the bit order on Arduino is MSB-Left.
    //Notably, this method of exploding bits will actually dynamically adjust to the bit order of the given system.
  }

 uint8_t Bitstreamer::WriteBits(boolean* b)
 {
    //DRAGONHERE: same as above
    return
    (b[0]? 1 : 0) +
    (b[1]? 2 : 0) +
    (b[2]? 4 : 0) +
    (b[3]? 8 : 0) +
    (b[4]? 16 : 0) +
    (b[5]? 32 : 0) +
    (b[6]? 64 : 0) +
    (b[7]? 128 : 0);
}

编译正常,上传到 Arduino,并按预期工作。

但是这段代码:

#include "Arduino.h"
  #include "Bitstreamer.h"
  #include "TimerOne.h"
  #include "BusyWaitByteProtocol.h"



  Bitstreamer::Bitstreamer(int pin)
  {
    _pin = pin;
    stage = 0;
    bitholder = new boolean[8];
    bitindex = 0;
    bp = new BusyWaitByteProtocol (128);
  }


  Bitstreamer* Bitstreamer::Daemonholder = NULL;
  void Bitstreamer::RunDaemon()
  {
    Bitstreamer::Daemonholder->Run();
  }

  void Bitstreamer::Listen()
  {
     myTurn = false;
     Init();
  }

  void Bitstreamer::Shout()
  {
     myTurn = true;
     Init();
  }


  void Bitstreamer::Init()
  {
     Daemonholder = this;
     Timer1.initialize(cycleRate);
     Timer1.attachInterrupt(RunDaemon); 
     stage++;
     screamcounter = 0;
     bitcounter = 0;
     lastbit = false;
  }

  void Bitstreamer::Run()
  {
    if (stage == 1)
    {
      //We are initializing the protocol. If we are the shouter, scream for a bit. If we are the listener, wait for scream to end.
      //The scream really just serves as an initial syncronizer.
      if (myTurn)
      {
        pinMode(_pin, OUTPUT);
        digitalWrite(_pin, HIGH);
      }
      else
      {
         pinMode(_pin, INPUT);
      }
      stage++;
    }
    else if (stage == 2)
    {
      if (myTurn)
      {
        screamcounter++;
        if (screamcounter > 100)
        {
          digitalWrite(_pin, LOW);
          stage++;
        }
      }
      else
      {
          int state = digitalRead(_pin);
          if (state == 1)
          {
            screamcounter++;
          }
          else
          {
            if (screamcounter > 30)
            {
              stage++;
            }
            screamcounter = 0;
          }
      }
    }
    else if (stage == 3)
    {
      if (myTurn)
      {
        bitcounter++;
        if (bitcounter == 1)
        {
            digitalWrite(_pin, bitholder[bitindex]?HIGH:LOW);
        }
        else
        {
          if (bitcounter >= resendrate)
          {
            bitcounter = 0;
            bitindex++;
          }
          if (bitindex >= 8)
          {
            bitindex = 0;
            uint8_t nb = bp->RequestByte();
            ReadBits(nb, bitholder);
            //Swap turns as well
          }
        }
      }
      else
      {
        boolean state = (digitalRead(_pin) != 1);
        if (state != lastbit)
        {
          float bits = ((float)bitcounter)/((float)resendrate);
          int finalbits = round(bits);

          debugstring = String(debugstring + String(state) + String(" ") + String(finalbits) + String("\n"));

          //Process received bits
          for (int i = 0; i < finalbits; i++)
          {
            bitholder[bitindex] = state;
            bitindex++;
            if (bitindex >= 8)
            {
              bitindex = 0;
              uint8_t nb = WriteBits(bitholder);
              bp->ProcessByte(nb);
              //Swap turns
            }
          }

          lastbit = state;
          bitcounter = 0;
        }
        else
        {
          bitcounter++;
        }
      }
    }

    /*
    if (myTurn)
    {
      if (hitcount < hits + 3)
      {
        digitalWrite(_pin, ((hitcount++)<hits)?HIGH:LOW);
      }
      else
      {
        hitcount = 0;
        hits++;
        lhits = hits;
      }
    }
    else
    {
      int state = digitalRead(_pin);
      if (state == 0)
      {
        hitcount++;
      }
      else
      {
        hitcount = 0;
        hits++;
      }
      //lhits = hits;
      if (hitcount >= 3)
      {
        lhits = hits;
        hits = 0;
      }
    }
    */
  }

  byte Bitstreamer::Read()
  {
    //Load from incoming byte buffer
    return 0;  
  }

  void Bitstreamer::Write(byte b)
  {
    //Send to outgoing byte buffer
  }

  void Bitstreamer::ReadBits(uint8_t b,  boolean* bitarray)
  {
    //DRAGONHERE: For loop would have been less code, but required initialization of another variable, and slightly more complicated logic for the processor
    //Also, not having it in a for loop makes it easier for others to understand
    bitarray[0] = (b & 1) != 0;    // 1 = 00000001, IE 1st bit. (1 & b) lets only 1st bit through. != 0 checks if zero.
    bitarray[1] = (b & 2) != 0;    // 1 = 00000010, IE 2nd bit. (2 & b) lets only 2nd bit through. != 0 checks if zero.
    bitarray[2] = (b & 4) != 0;    // 1 = 00000100, IE 3rd bit. (4 & b) lets only 3rd bit through. != 0 checks if zero.
    bitarray[3] = (b & 8) != 0;    // 1 = 00001000, IE 4th bit. (8 & b) lets only 4th bit through. != 0 checks if zero.
    bitarray[4] = (b & 16) != 0;   // 1 = 00010000, IE 5th bit. (16 & b) lets only 5th bit through. != 0 checks if zero.
    bitarray[5] = (b & 32) != 0;   // 1 = 00100000, IE 6th bit. (32 & b) lets only 6th bit through. != 0 checks if zero.
    bitarray[6] = (b & 64) != 0;   // 1 = 01000000, IE 7th bit. (64 & b) lets only 7th bit through. != 0 checks if zero.
    bitarray[7] = (b & 128) != 0;  // 1 = 10000000, IE 8th bit. (128 & b) lets only 8th bit through. != 0 checks if zero.

    //PLEASE NOTE: These explainations are based on the assumption that the bit order on Arduino is MSB-Left.
    //Notably, this method of exploding bits will actually dynamically adjust to the bit order of the given system.
  }

 uint8_t Bitstreamer::WriteBits(boolean* b)
 {
    //DRAGONHERE: same as above
    return
    (b[0]? 1 : 0) +
    (b[1]? 2 : 0) +
    (b[2]? 4 : 0) +
    (b[3]? 8 : 0) +
    (b[4]? 16 : 0) +
    (b[5]? 32 : 0) +
    (b[6]? 64 : 0) +
    (b[7]? 128 : 0);
}

编译失败并报错:

/Applications/Arduino.app/Contents/Resources/Java/hardware/tools/avr/bin/../lib/gcc/avr/4.3.2/../../../../avr /lib/avr6/crtm2560.o: 在 .text.__vector_36 部分中定义的函数 __vector_default': (.vectors+0x90): relocation truncated to fit: R_AVR_13_PCREL against symbol__vector_36' core.a(HardwareSerial.cpp.o)

区别?你会笑的……

工作 段代码中,我有:

Line #98: if (bitcounter == 0)
Line #106: bitcounter = -1;

not-working 代码段中,我有:

Line #98: if (bitcounter == 1)
Line #106: bitcounter = 0;

这实际上是仅有的两个区别。坦率地说,我不明白为什么以善良的名义这不起作用。更好的是:

Line #98: if (bitcounter == 0)
Line #106: bitcounter = 0;

还有:

Line #98: if (bitcounter == 1)
Line #106: bitcounter = -1;

甚至:

Line #98: if (bitcounter == 0)
Line #106: bitcounter = 1;

所有工作。正是 1 和 0 的唯一组合,按此顺序,导致我的编译器发疯。

有人能够解释这里到底出了什么问题吗?我对此感到非常不安,我只能想到发生这种情况的一个原因——也许我的程序在 arduino 上占用了太多内存?没有。原来的工作代码占用了,我引用:

二进制草图大小:10,178 字节(最大 258,048 字节)

很抱歉,我只是不明白如何在我的程序中更改两个常量会使最终的二进制大小总共增加 247,870 字节。

那么,我到底做错了什么?我是否偶然发现了编译器中的一些疯狂错误?这是我的代码以某种方式编译为与一些实施不佳的开发人员信号相同的二进制模式的结果,该信号调整了 Arduino 上的程序上传接收器系统上的设置?或者对这个谜团有更合乎逻辑的答案吗?

【问题讨论】:

    标签: c++ compiler-errors arduino


    【解决方案1】:

    在另一个 Arduino 问题中,我看到这种爆炸是由于内联而发生的。不过,我不确定这是唯一的罪魁祸首。 (如果您认为可能,请参阅此链接以了解禁用函数内联的语法:How can I tell gcc not to inline a function?

    更有可能的是,您超出了条件分支的相对分支位移。你有一个 huge 嵌套的 if-else 子句。您可以尝试将它们分开或以其他方式展平那里的层次结构。例如,将每个顶级子句(阶段 1、阶段 2、...)移动到私有方法,并从 if 的主体中调用这些方法。也许也标记那些noinline

    为什么改变两个常量会让你越界?我不知道底层汇编语言的变幻莫测,但就代码大小而言,生成或测试某些常量通常比其他常量更便宜。例如,通常情况下,使用 0 比任何其他值都便宜得多。


    以下 SO 问题提供了有关 AVR 平台上相关调用的更多详细信息以及建议的解决方案:AVR linker error, "relocation truncated to fit"

    【讨论】:

    • 有趣!我对分支位移理论持怀疑态度。为什么?如果我将第 98 行设置为“bitcounter == 1”,将第 106 行设置为“bitcounter = 0;”,则编译失败。但如果我不理会第 98 行,并将第 106 行更改为“bitcounter = -1;”然后它编译就好了。在这种情况下,我不会更改程序的逻辑结构。不过,很高兴知道这可能会成为一个潜在的问题!我将尝试将我的顶级子句移动到单独的函数中,并让你知道它是如何进行的。
    • @GeorgesOatesLarsen:顺便说一句,这个 SO 问题提供了更多关于 AVR 上的相对与长跳和调用的信息,并且直接相关:stackoverflow.com/questions/8188849/… 我将在上面添加链接。
    • @GeorgesOatesLarsen :我想我应该补充一点,链接器的行为也很重要。即使嵌套的 if 相互之间很好,链接器也会根据代码的大小和其他因素对代码块进行重新排序,因此一个函数中代码大小的微小变化可能会导致两个不相关的函数“相距太远”。我在其他工具链中多次遇到这种情况;它不是 Arduino 或 AVR 甚至 GNU 工具链独有的。
    • Joe Z 啊,这似乎是正在发生的事情。虽然为什么“bitcounter = 0;”大小与“bitcounter = -1;”不同?
    • @GeorgesOatesLarsen :实际上,您想做相反的事情:禁用该标志。我在上面给出的链接说:相关选项是 "Use rjmp/rcall (limited range) on >8k devices (-mshort-calls)" 需要取消选中 以防止命名错误。
    猜你喜欢
    • 2011-11-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多