[英]Increasing performance of 32bit math on 16bit processor
我正在為嵌入式設備開發一些固件,該設備使用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位數學,但我不知道這樣做的簡單方法! 任何建設性的意見/建議都將非常受歡迎。
警告:所有號碼都已組成......
假設上面的ISR有大約200(可能,更少)編譯代碼指令,那些包括在ISR之前和之后保存/恢復CPU寄存器的指令,每個指令占用5個時鍾周期(可能是1到3)並且你調用其中2個每秒1000次,我們最終得到2 * 1000 * 200 * 5 =每秒2百萬個時鍾周期或2 MIPS。
你真的在其他地方消費38 MIPS嗎?
唯一可能重要的是我無法看到它,就是在SetMotor * Pos *()函數內部完成的事情。 他們做任何復雜的計算嗎? 他們是否與電機進行一些慢速通信,例如等待它們響應發送給它們的命令?
無論如何,使用32位整數而不是使用16位時,這種簡單的代碼會明顯變慢,這是值得懷疑的。
如果您的代碼很慢,請找出花費的時間和數量,然后對其進行分析。 在ISR中生成方波脈沖信號(當ISR開始時變為1,當ISR即將返回時變為0)並用示波器測量其持續時間。 或者做任何更容易找到的事情。 測量在程序的所有部分花費的時間,然后優化真正需要的地方,而不是您之前認為的那樣。
我認為,16位和32位算術之間的差異不應該那么大,因為你只使用增量和比較。 但也許問題是每個32位算術運算意味着一個函數調用(如果編譯器不能/不願意進行簡單操作的內聯)。
一個建議是通過將Device1.statusStr.CurrentPosition和Device1.statusStr.CurrentPositionH和Device1.statusStr.CurrentPositionL中的Device1.statusStr.CurrentPosition分開來自己做算術。 然后使用一些宏來執行操作,例如:
#define INC(xH,xL) {xL++;if (xL == 0) xH++;}
我將擺脫StepperIndex1
變量,而是使用CurrentPosition
的兩個低位來跟蹤當前步驟索引。 或者,在完整旋轉(而不是每個步驟)中跟蹤當前位置,因此它可以適合16位變量。 移動時,只有在移動到階段'A'時才增加/減少位置。 當然,這意味着您只能定位每個完整的旋轉,而不是每一步。
抱歉,您使用的程序設計不好。
我們來檢查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步。 綽綽有余!
當您將其從16位更改為32位時,您是否更改了任何編譯標志,以告訴它編譯為32位應用程序。
您是否嘗試使用32位擴展進行編譯,但僅使用16位整數。 你還有這樣的性能下降嗎?
可能只是通過從16位更改為32位,某些操作的編譯方式不同,也許在兩組編譯的ASM代碼之間進行差異,看看實際上有什么不同,它是很多還是只有幾行。
解決方案可能是使用32位整數而不是使用兩個16位整數,當值A為int16.Max然后將其設置為0然后將valueB遞增1,否則只是將ValueA加1,當值B> = 3時然后檢查valueA> = 26696(或類似的東西取決於你是否使用unsigned或signed int16)然后你的電機檢查在12500。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.