我正在使用 Silicon Labs IDE 和 SDCC 编译器用 C 语言编写嵌入式设备的固件。该器件架构基于 8051 系列。 相关函数如下所示。该函数用于设置我的 MCU 上的端口以驱动步进电机。它由中断处理程序调用。大开关语句只是将端口设置为下一个电机步骤的正确值。该函数的底部部分查看来自霍尔效应传感器的输入和移动的多个步骤,以检测电机是否已停转。 问题是,出于某种原因,第二个 IF 语句看起来像这样
if (StallDetector > (GapSize + 20))
{
HandleStallEvent();
}
似乎总是被优化掉。如果我尝试在 HandleStallEvent()
调用处放置断点,IDE 会显示一条消息:“与此行号没有地址关联”。 我不太擅长阅读汇编来判断它在做什么,但我粘贴了下面的 asm 输出中的一个片段。任何帮助将不胜感激。
void OperateStepper(void)
{
//static bit LastHomeMagState = HomeSensor;
static bit LastPosMagState = PosSensor;
if(PulseMotor)
{
if(MoveDirection == 1) // Go clockwise
{
switch(STEPPER_POSITION)
{
case 'A':
STEPPER_POSITION = 'B';
P1 = 0xFD;
break;
case 'B':
STEPPER_POSITION = 'C';
P1 = 0xFF;
break;
case 'C':
STEPPER_POSITION = 'D';
P1 = 0xFE;
break;
case 'D':
STEPPER_POSITION = 'A';
P1 = 0xFC;
break;
default:
STEPPER_POSITION = 'A';
P1 = 0xFC;
} //end switch
}
else // Go CounterClockwise
{
switch(STEPPER_POSITION)
{
case 'A':
STEPPER_POSITION = 'D';
P1 = 0xFE;
break;
case 'B':
STEPPER_POSITION = 'A';
P1 = 0xFC;
break;
case 'C':
STEPPER_POSITION = 'B';
P1 = 0xFD;
break;
case 'D':
STEPPER_POSITION = 'C';
P1 = 0xFF;
break;
default:
STEPPER_POSITION = 'A';
P1 = 0xFE;
} //end switch
} //end else
MotorSteps++;
StallDetector++;
if(PosSensor != LastPosMagState)
{
StallDetector = 0;
LastPosMagState = PosSensor;
}
else
{
if (PosSensor == ON)
{
if (StallDetector > (MagnetSize + 20))
{
HandleStallEvent();
}
}
else if (PosSensor == OFF)
{
if (StallDetector > (GapSize + 20))
{
HandleStallEvent();
}
}
}
} //end if PulseMotor
}
...以及该函数底部部分的 asm 输出...
; C:\SiLabs\Optec Programs\HSFW_HID_SDCC_2\MotionControl.c:653: if(PosSensor != LastPosMagState)
mov c,_P1_4
jb _OperateStepper_LastPosMagState_1_1,00158$
cpl c
00158$:
jc 00126$
C$MotionControl.c$655$3$7 ==.
; C:\SiLabs\Optec Programs\HSFW_HID_SDCC_2\MotionControl.c:655: StallDetector = 0;
clr a
mov _StallDetector,a
mov (_StallDetector + 1),a
C$MotionControl.c$657$3$7 ==.
; C:\SiLabs\Optec Programs\HSFW_HID_SDCC_2\MotionControl.c:657: LastPosMagState = PosSensor;
mov c,_P1_4
mov _OperateStepper_LastPosMagState_1_1,c
ret
00126$:
C$MotionControl.c$661$2$8 ==.
; C:\SiLabs\Optec Programs\HSFW_HID_SDCC_2\MotionControl.c:661: if (PosSensor == ON)
jb _P1_4,00123$
C$MotionControl.c$663$4$9 ==.
; C:\SiLabs\Optec Programs\HSFW_HID_SDCC_2\MotionControl.c:663: if (StallDetector > (MagnetSize + 20))
mov a,_MagnetSize
mov r2,a
rlc a
subb a,acc
mov r3,a
mov a,#0x14
add a,r2
mov r2,a
clr a
addc a,r3
mov r3,a
clr c
mov a,r2
subb a,_StallDetector
mov a,r3
subb a,(_StallDetector + 1)
jnc 00130$
C$MotionControl.c$665$5$10 ==.
; C:\SiLabs\Optec Programs\HSFW_HID_SDCC_2\MotionControl.c:665: HandleStallEvent();
ljmp _HandleStallEvent
00123$:
C$MotionControl.c$668$2$8 ==.
; C:\SiLabs\Optec Programs\HSFW_HID_SDCC_2\MotionControl.c:668: else if (PosSensor == OFF)
jnb _P1_4,00130$
C$MotionControl.c$670$4$11 ==.
; C:\SiLabs\Optec Programs\HSFW_HID_SDCC_2\MotionControl.c:670: if (StallDetector > (GapSize + 20))
mov a,#0x14
add a,_GapSize
mov r2,a
clr a
addc a,(_GapSize + 1)
mov r3,a
clr c
mov a,r2
subb a,_StallDetector
mov a,r3
subb a,(_StallDetector + 1)
jnc 00130$
C$MotionControl.c$672$5$12 ==.
; C:\SiLabs\Optec Programs\HSFW_HID_SDCC_2\MotionControl.c:672: HandleStallEvent();
C$MotionControl.c$678$2$1 ==.
XG$OperateStepper$0$0 ==.
ljmp _HandleStallEvent
00130$:
ret
在我看来,从 asm 的外观来看,编译器没有优化第二个 if 语句,但如果是这样,为什么 IDE 不允许我在那里设置断点?也许这只是一个愚蠢的 IDE!
这就是所谓的“尾部调用优化”。
OperateStepper() 在调用 HandleStallEvent() 之后不会执行任何操作,因此返回它没有意义。 您只需对 RET 执行 RET,这会浪费指令AND堆栈槽。
阅读“Lambda:终极……”麻省理工学院人工智能实验室备忘录以了解更多详细信息。
在 HandleStallEvent() 例程上设置断点,而不是在调用上。
IF 语句没有被优化。这是它的代码。
C$MotionControl.c$670$4$11 ==.
; C:\SiLabs\Optec Programs\HSFW_HID_SDCC_2\MotionControl.c:670: if (StallDetector > (GapSize + 20))
mov a,#0x14 ; r2,r3 = 20 + GapSize
add a,_GapSize ; (adding a 16-bit number in two 8-bit steps)
mov r2,a
clr a
addc a,(_GapSize + 1)
mov r3,a
clr c
mov a,r2 ; subtracting in two 8-bit steps
subb a,_StallDetector
mov a,r3
subb a,(_StallDetector + 1)
jnc 00130$ ; jump if carry not set (fall through if carry set)
C$MotionControl.c$672$5$12 ==.
; C:\SiLabs\Optec Programs\HSFW_HID_SDCC_2\MotionControl.c:672: HandleStallEvent();
C$MotionControl.c$678$2$1 ==.
XG$OperateStepper$0$0 ==.
ljmp _HandleStallEvent ; it knows HandleStallEvent does not return!
00130$: ; or rather, it knows this handler cannot return, so there's no need to call.
ret
我注意到在倒数第三行中,发生了一些有趣的事情。它没有对
HandleStallEvent
进行函数调用。它正在跳远,所以它显然知道 HandleStallEvent
无法返回。我还看到,在上面的两行中,它定义了将行号与跳转指令相关联的汇编程序符号。因此,它有一个第 678 行的符号。如果 IDE 不允许您在第 678 行设置断点,也许您可以获取第 678 行的十六进制地址,并将其设置在该十六进制地址处。您可能会尝试的另一件事是在该行之前插入一个局部变量定义,例如 int breakhere = 1
,然后看看这是否为您提供了一些可以中断的指令。
顺便说一句,你可以看到CPU以8位数字来思考,所以如果你可以使用char而不是short,它将节省指令。节省的时间是否值得取决于机器执行此代码的时间百分比。
顺便说一句,如果你想榨取这只小狗的性能,我在做嵌入式工作时所依赖的是随机停止 IDE(或英特尔“蓝盒”ICE)。 这是相关内容。
您通常可以使用#pragma 语句调整优化器。 我不知道你的编译器的确切语法,但你应该能够在你的编译器/ide 附带的文档中找到它。
类似这样的事情
#pragma optimize( "", off )
//now the function which should not be optimized
#pragma optimize( "", on )
如何配置优化取决于编译器。 SDCC 的手册在第 3.28 节中列出了其优化选项。您可以在源代码级别使用命令行选项或编译指示。尝试全局禁用优化,看看是否能获得相同的效果。通常,在禁用优化的调试器中单步执行代码将消除无法设置断点的问题。如果这工作正常,您可以尝试使用编译指示在函数级别禁用可疑优化,以查看哪一个可能导致问题。