首页 > 解决方案 > 如何使用 gcc 内联汇编代码访问成员变量

问题描述

所以我最近偶然发现了博客文章 NeoPixels Revealed:如何(不需要)生成精确定时的信号并支持github 项目,现在我试图将这段代码的核心封装到一个 c++ 类中,以便我可以访问各种 neopixel来自多个 arduino uno 数字引脚的字符串。

为此,我创建了一个受保护的成员变量 (m_PixelChannel),它存储访问灯串所需的引脚。但是,我无法让汇编代码识别成员变量。下面是我试图使工作的代码(这或多或少是来自 github 项目的原始代码的直接复制粘贴,并在其前添加了一个类名):

// send a bit to the string. We must to drop to asm to enusre that the complier does
// not reorder things and make it so the delay happens in the wrong place.
inline void fastNeoPixels::sendBit(bool bitVal) {

  if (bitVal) { // 0 bit
    asm volatile(
        "sbi %[port], %[bit] \n\t" // Set the output bit
        ".rept %[onCycles] \n\t"   // Execute NOPs to delay exactly the specified number of cycles
        "nop \n\t"
        ".endr \n\t"
        "cbi %[port], %[bit] \n\t" // Clear the output bit
        ".rept %[offCycles] \n\t"  // Execute NOPs to delay exactly the specified number of cycles
        "nop \n\t"
        ".endr \n\t" ::
        [port] "I"(_SFR_IO_ADDR(PIXEL_PORT)),
        [bit] "r"(m_PixelChannel),
        // [bit] "I" (PIXEL_STRING0),
        [onCycles] "I"(NS_TO_CYCLES(T1H) - 2), // 1-bit width less overhead  for the actual bit setting, note that this delay could be longer and everything would still work
        [offCycles] "I"(NS_TO_CYCLES(T1L) - 2) // Minimum interbit delay. Note that we probably don't need this at all since the loop overhead will be enough, but here for correctness
    );
  } else { // 1 bit
    // **************************************************************************
    // This line is really the only tight goldilocks timing in the whole program!
    // **************************************************************************
    asm volatile(
        "sbi %[port], %[bit] \n\t" // Set the output bit
        ".rept %[onCycles] \n\t"   // Now timing actually matters. The 0-bit must be long enough to be detected but not too long or it will be a 1-bit
        "nop \n\t"                 // Execute NOPs to delay exactly the specified number of cycles
        ".endr \n\t"
        "cbi %[port], %[bit] \n\t" // Clear the output bit
        ".rept %[offCycles] \n\t"  // Execute NOPs to delay exactly the specified number of cycles
        "nop \n\t"
        ".endr \n\t" ::
        [port] "I"(_SFR_IO_ADDR(PIXEL_PORT)),
        [bit] "r" (m_PixelChannel),
        // [bit] "I" (PIXEL_STRING0),
        [onCycles] "I"(NS_TO_CYCLES(T0H) - 2),
        [offCycles] "I"(NS_TO_CYCLES(T0L) - 2)
    );
  }  // if (bitVal)...

  // Note that the inter-bit gap can be as long as you want as long as it doesn't exceed the 5us reset timeout (which is A long time)
  // Here I have been generous and not tried to squeeze the gap tight but instead erred on the side of lots of extra time.
  // This has thenice side effect of avoid glitches on very long strings becuase
}

我确信是 m_PixelChannel 变量导致了问题;与我想的约束有关,因为我可以通过取消注释 PIXEL_STRING0 代码行来让它再次工作。或者,我可以将值作为参数传递给方法并使用“n”约束代码使其工作(正如我已经成功完成的那样),但我认为我不应该将参数传递给具有已经访问了该值...

我尝试了以下约束代码,但没有成功:“n”、“o”、“I”、“m”、“+m”、“r”和“g”。

显然我错过了一些东西。有人可以为我指出正确的方向来完成这项工作吗?

标签: c++gccarduinoavrinline-assembly

解决方案


问题是 SBI 指令的操作数必须是常量(立即值)。所以唯一有效的约束是I,值必须是一个常数。没有办法设置变量位。

如果要设置变量位,则必须使用 switch 语句之类的东西来选择 8 个不同指令中的一个。


推荐阅读