如何使用 gcc 内联汇编代码访问成员变量
How to access member variable with gcc inline assembler code
所以我最近偶然发现了博客 post
NeoPixels Revealed: How to (not need to) generate precisely timed signals and supporting github project 现在我正试图将这段代码的核心封装到 c++ class 中,这样我就可以从多个 arduino uno 数字引脚访问各种新像素字符串。
为此,我创建了一个受保护的成员变量 (m_PixelChannel),用于存储访问灯串所需的引脚。但是,我无法获得识别成员变量的汇编代码。下面是我正在尝试使用的代码(它或多或少是 github 项目原始代码的直接复制粘贴,前面添加了 class 名称):
// 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"。
显然我遗漏了一些东西。有人可以给我指出正确的方向来完成这项工作吗?
问题是 SBI 指令的操作数必须是常量(立即值)。所以唯一有效的约束是 I
并且该值必须是常量。无法设置变量位。
如果你想设置一个变量位,你必须使用类似于 switch 语句的东西来 select 8 种不同的指令之一。
我决定我真的不喜欢我在对 Chris Dodd 的回复的评论中使用的模板<> 方法。所以在多次迭代之后,我能够弄清楚如何让它工作......
void sendBit( bool bitVal ) {
volatile uint8_t _hi = *m_PixelPORT | m_PinMask;
volatile uint8_t _lo = *m_PixelPORT & ~m_PinMask;
if ( bitVal ) {
asm volatile (
"st %a[port], %[hi]" "\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"
"st %a[port], %[lo]" "\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] "+e" (m_PixelPORT)
: [hi] "r" (_hi),
[lo] "r" (_lo),
[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 {
asm volatile (
"st %a[port], %[hi]" "\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"
"st %a[port], %[lo]" "\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] "+e" (m_PixelPORT)
: [hi] "r" (_hi),
[lo] "r" (_lo),
[onCycles] "I" (NS_TO_CYCLES(T0H) - 2),
[offCycles] "I" (NS_TO_CYCLES(T0L) - 2)
);
}
}
其中 m_PinMask = _BV(digital_pin);
请注意,对 sbi/cbi 的调用已被对 [=21= 的调用所取代]st 和约束类型的变化。
应用这些更改后,代码将执行我想要执行的操作,同时保持在 bit-bang 过程的时序要求内。
再次感谢 Chris 为我指明了正确的方向!
所以我最近偶然发现了博客 post NeoPixels Revealed: How to (not need to) generate precisely timed signals and supporting github project 现在我正试图将这段代码的核心封装到 c++ class 中,这样我就可以从多个 arduino uno 数字引脚访问各种新像素字符串。
为此,我创建了一个受保护的成员变量 (m_PixelChannel),用于存储访问灯串所需的引脚。但是,我无法获得识别成员变量的汇编代码。下面是我正在尝试使用的代码(它或多或少是 github 项目原始代码的直接复制粘贴,前面添加了 class 名称):
// 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"。
显然我遗漏了一些东西。有人可以给我指出正确的方向来完成这项工作吗?
问题是 SBI 指令的操作数必须是常量(立即值)。所以唯一有效的约束是 I
并且该值必须是常量。无法设置变量位。
如果你想设置一个变量位,你必须使用类似于 switch 语句的东西来 select 8 种不同的指令之一。
我决定我真的不喜欢我在对 Chris Dodd 的回复的评论中使用的模板<> 方法。所以在多次迭代之后,我能够弄清楚如何让它工作......
void sendBit( bool bitVal ) {
volatile uint8_t _hi = *m_PixelPORT | m_PinMask;
volatile uint8_t _lo = *m_PixelPORT & ~m_PinMask;
if ( bitVal ) {
asm volatile (
"st %a[port], %[hi]" "\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"
"st %a[port], %[lo]" "\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] "+e" (m_PixelPORT)
: [hi] "r" (_hi),
[lo] "r" (_lo),
[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 {
asm volatile (
"st %a[port], %[hi]" "\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"
"st %a[port], %[lo]" "\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] "+e" (m_PixelPORT)
: [hi] "r" (_hi),
[lo] "r" (_lo),
[onCycles] "I" (NS_TO_CYCLES(T0H) - 2),
[offCycles] "I" (NS_TO_CYCLES(T0L) - 2)
);
}
}
其中 m_PinMask = _BV(digital_pin);
请注意,对 sbi/cbi 的调用已被对 [=21= 的调用所取代]st 和约束类型的变化。
应用这些更改后,代码将执行我想要执行的操作,同时保持在 bit-bang 过程的时序要求内。
再次感谢 Chris 为我指明了正确的方向!