提高16位处理器的32位数学性能

时间:2011-11-04 20:45:00

标签: c performance 32-bit pic 16-bit

我正在为嵌入式设备开发一些固件,该设备使用16位PIC,工作频率为40 MIPS,并以C编程。系统将控制两个步进电机的位置,并始终保持每个电机的步进位置。每个电机的最大位置大约是125000步,所以我不能使用16位整数来跟踪位置。我必须使用32位无符号整数(DWORD)。电机以每秒1000步的速度移动,我设计了固件,以便在定时器ISR中处理步骤。计时器ISR执行以下操作:

1)将一个电机的当前位置与目标位置进行比较,如果它们是相同的,则设置isMoving标志为false并返回。如果它们不同,则将isMoving标志设置为true。

2)如果目标位置大于当前位置,向前移动一步,然后递增当前位置。

3)如果目标位置小于当前位置,向后移动一步,然后递减当前位置。

以下是代码:

void _ISR _NOPSV _T4Interrupt(void)
{
    static char StepperIndex1 = 'A';    

    if(Device1.statusStr.CurrentPosition == Device1.statusStr.TargetPosition)
    {
        Device1.statusStr.IsMoving = 0;
        // Do Nothing
    }   
    else if (Device1.statusStr.CurrentPosition > Device1.statusStr.TargetPosition)
    {
        switch (StepperIndex1)      // MOVE OUT
        {
            case 'A':
                SetMotor1PosB();
                StepperIndex1 = 'B';
                break;
            case 'B':
                SetMotor1PosC();
                StepperIndex1 = 'C';
                break;
            case 'C':
                SetMotor1PosD();
                StepperIndex1 = 'D';
                break;
            case 'D':
                default:
                SetMotor1PosA();
                StepperIndex1 = 'A';
                break;      
        }
        Device1.statusStr.CurrentPosition--;    
        Device1.statusStr.IsMoving = 1;
    }   
    else
    {
        switch (StepperIndex1)      // MOVE IN 
        {
            case 'A':
                SetMotor1PosD();
                StepperIndex1 = 'D';
                break;
            case 'B':
                SetMotor1PosA();
                StepperIndex1 = 'A';
                break;
            case 'C':
                SetMotor1PosB();
                StepperIndex1 = 'B';
                break;
            case 'D':
                default:
                SetMotor1PosC();
                StepperIndex1 = 'C';
                break;      
        }
        Device1.statusStr.CurrentPosition++;
        Device1.statusStr.IsMoving = 1;
    }   
    _T4IF = 0;          // Clear the Timer 4 Interrupt Flag.
}

当接收到移动请求时,目标位置在主程序循环中设置。 SetMotorPos行只是用于打开/关闭特定端口引脚的宏。

我的问题是:有没有办法提高此代码的效率?代码函数很好,如果位置是16位整数但是32位整数处理太多。该设备必须毫不犹豫地与PC通信,并且在写入时会有明显的性能损失。我真的只需要18位数学,但我不知道这样做的简单方法!任何建设性的意见/建议都会受到最高的赞赏。

5 个答案:

答案 0 :(得分:6)

警告:所有号码都已组成......

假设上述ISR有大约200(可能,更少)编译代码指令,其中包括在ISR之前和之后保存/恢复CPU寄存器的指令,每个指令需要5个时钟周期(可能是1到3)你每次呼叫其中的两个1000次,我们最终得到2 * 1000 * 200 * 5 =每秒2百万个时钟周期或2 MIPS。

你真的在其他地方消费了38 MIPS吗?

SetMotor * Pos *()函数中,唯一可能重要的是我无法看到它。他们做任何复杂的计算吗?它们是否与电机进行一些慢速通信,例如等他们回应发给他们的命令?

无论如何,使用32位整数而不是使用16位时,这样简单的代码会明显变慢。

如果你的代码很慢,找出花费的时间和费用,请对其进行分析。在ISR中生成方波脉冲信号(当ISR开始时变为1,当ISR即将返回时变为0)并用示波器测量其持续时间。或者做任何更容易找到的事情。测量在程序的所有部分花费的时间,然后在真正需要的地方进行优化,而不是您之前认为的那样。

答案 1 :(得分:1)

我认为,16位和32位算术之间的差异不应该那么大,因为你只使用增量和比较。但也许问题是每个32位算术运算意味着一个函数调用(如果编译器不能/不愿意内联更简单的操作)。

一个建议是自己做算术,打破两个Device1.statusStr.CurrentPosition,比如Device1.statusStr.CurrentPositionH和Device1.statusStr.CurrentPositionL。然后使用一些宏来执行操作,例如:

#define INC(xH,xL) {xL++;if (xL == 0) xH++;}

答案 2 :(得分:0)

我将摆脱StepperIndex1变量,而是使用CurrentPosition的两个低位来跟踪当前步骤索引。或者,在完整旋转(而不是每个步骤)中跟踪当前位置,因此它可以适合16位变量。移动时,只有在移动到阶段'A'时才增加/减少位置。当然,这意味着您只能定位每个完整的旋转,而不是每一步。

答案 3 :(得分:0)

抱歉,您使用的程序设计不好。

让我们检查16位和32位PIC24或PIC33 asm代码之间的区别......

16位增量

inc    PosInt16               ;one cycle

因此16位递增需要一个周期

32位增量

clr    Wd                     ;one cycle
inc    low PosInt32           ;one cycle
addc   high PosInt32, Wd      ;one cycle

和32增量需要三个周期。 总差异为2个周期或50ns(纳秒)。

简单的钙化将向您展示所有。你有每秒1000步和40Mips DSP 所以你每步有40000条指令,每秒1000步。绰绰有余!

答案 4 :(得分:0)

当你将它从16位更改为32位时,你是否更改了任何编译标志,以告诉它编译为32位应用程序。

您是否尝试使用32位扩展进行编译,但仅使用16位整数。你还有这样的表现下降吗?

可能只是从16位更改为32位,某些操作的编译方式不同,也许在两组编译的ASM代码之间做一个Diff,看看实际上有什么不同,是不是很多,或者只是几行

解决方案可能不是使用32位整数,只需使用两个16位整数, 当valueA为int16.Max然后将其设置为0然后将valueB增加1,否则只将ValueA增加1,当值B> = 3时,然后检查valueA> = 26696(或类似的东西取决于你是否使用unsigned或签署int16)然后你的电机检查在12500。

相关问题