您好,登錄后才能下訂單哦!
之前在arduino上的實現(xiàn)的新的步進電機算法,需要移植到32位MKE06K128上。這個任務(wù)聽上去事實而非,整個Marlin固件還涉及別的部分,比如PID加熱控制模塊,舵機模塊,串口指令讀取代碼。但是我們cocky的Teamleader很反感移植Marlin。沒辦法,這一步我的理解,只要能實現(xiàn)plan_buffer_line就算完成任務(wù)。進一步簡化,只要實現(xiàn)一個軸的plan_buffer_line就算完成任務(wù)。
硬件連接部分:
我們cocky的Teamleader是個能干的人,很快畫出并焊好了MKE06K128的電路板供我當(dāng)作開發(fā)板使用。Freescale有基于eclipse的專用開發(fā)環(huán)境Kinetis Design Studio,簡稱KDS,KDS既集成了MKE的交叉編譯器,而且以插件的形式提供了“專家系統(tǒng)”,能夠可視化的配置硬件管腳,并且生成示意圖方便review(如下圖),看上去很酷。
這個專家工具在調(diào)試硬件和快速實現(xiàn)階段也非常方便。我在電路板上接上Jlink,并且連接上A4988和步進電機,在排除掉A4988的損壞之后。編寫一個簡答的定時器程序,并把周期寫成3.2KHz(請參考arduino上的實現(xiàn)中關(guān)于3.2K的來歷),步進電機就應(yīng)該以1rad/s的速度轉(zhuǎn),如果不是,就要用示波器和萬用表排除芯片,電路板,電源和Jlink的故障可能。如果是,就為下一步編寫固件代碼打下了堅實的基礎(chǔ)。最終調(diào)試好的硬件連接圖如下:
使用jlink時注意,
在系統(tǒng)上電的情況下,需要去掉jlink上供電的跳線帽,否則無法進行刷寫工作。
即使脈沖寬度很窄,也可以驅(qū)動電機,而且窄脈沖的噪音很小。
A4988的EN路上拉到電路5V,需要軟件將其拉低,步進電機才能正常驅(qū)動,否則驅(qū)動電路不使能。
軟件實現(xiàn)部分:
首先實現(xiàn)plan_buffer_line()函數(shù),需要編寫%Planner和%Stepper兩個模塊。前者管理隊列,后者管理步進電機的中斷響應(yīng)驅(qū)動。和Marlin固件的區(qū)別在于,這里的%Planner不必計算執(zhí)行的各個速度節(jié)點,而是只需要設(shè)置穩(wěn)定后的速度值,%Stepper會動態(tài)的計算出瞬時速度。
隊列和隊列指針都在%Planner模塊中定義為全局變量,由于聲明了external 關(guān)鍵字,當(dāng)其他項目包含planner.h時就會引入該變量,即等效于該全局變量為全項目全局變量。
%Planner.cpp中:
block_t block_buffer[BLOCK_BUFFER_SIZE]; volatile unsigned char block_buffer_head; volatile unsigned char block_buffer_tail;
另外設(shè)置了幾個強制內(nèi)聯(lián)函數(shù):
FORCE_INLINE block_t *plan_get_current_block();//讀取當(dāng)前block函數(shù) FORCE_INLINE bool blocks_queued() { return (block_buffer_head != block_buffer_tail); }//隊列是否非空
在這里:
#define FORCE_INLINE attribute((always_inline)) inline
告訴編譯器,設(shè)置為強制內(nèi)聯(lián)型;對于此,Cpp的語法解釋是:
inline關(guān)鍵字僅僅是建議編譯器做內(nèi)聯(lián)展開處理,而不是強制。在gcc編譯器中,如果編譯優(yōu)化設(shè)置為-O0,即使是inline函數(shù)也不會被內(nèi)聯(lián)展開,除非設(shè)置了強制內(nèi)聯(lián)(attribute((always_inline)))屬性。
關(guān)于內(nèi)聯(lián)函數(shù),補充一點基礎(chǔ)知識:
在內(nèi)聯(lián)函數(shù)內(nèi)不允許用循環(huán)語句和開關(guān)語句。否則會被編譯器當(dāng)作普通函數(shù)。
在%stepper.cpp中定義了:
block_t *current_block //當(dāng)前運動實例
由于沒有在stepper.h中定義相應(yīng)的extern 類型,所以該變量為模塊內(nèi)的private全局變量。
即使是使用32位單片機,也不應(yīng)該在中斷響應(yīng)函數(shù)中進行浮點運算,否則中斷頻率會被大大拖慢。所以%stepper函數(shù)的最終結(jié)果為:
void ST_PULSE_TI_OnInterrupt(void) { /* Write your code here ... */ #ifndef HARDWARE_DEBUG_MODE if (!current_block) { current_block = plan_get_current_block(); } #define DISTANCE_COUNT_RESET current_block->rounds_count_per_mstep-=current_block->one_micro_step_mm #define DISTANCE_IS_ONESTEP current_block->rounds_count_per_mstep>=current_block->one_micro_step_mm #define DRIVE_PULSE E0_STE_SetVal(); \ E0_STE_ClrVal() /*==========generate a pulse when a step accumulated===========*/ if (DISTANCE_IS_ONESTEP) { DRIVE_PULSE; DISTANCE_COUNT_RESET; } /*update the new speed and ... */ if (current_block) { if (current_block->rounds_behind + current_block->rounds_ahead < current_block->rounds) { // accumulate the rounds and the microstep_count_for_rounds current_block->rounds_behind += current_block->instance_rate; current_block->rounds_count_per_mstep +=current_block->instance_rate; //when speed climbing up case if (current_block->instance_rate < current_block->nominal_rate) { current_block->instance_rate += current_block->acceleration; } //when hold the nominal_rate case else { //make sure the rate remains nominal_rate current_block->instance_rate = current_block->nominal_rate; } // update the rounds left for all the three case. current_block->rounds_ahead=current_block->instance_rate/2/current_block->acceleration*current_block->instance_rate; } //when speed slipping down case else if (current_block->instance_rate > current_block->exit_rate) { current_block->rounds_behind+= current_block->instance_rate; current_block->rounds_count_per_mstep +=current_block->instance_rate; current_block->instance_rate -= current_block->acceleration; } // at the end of the block else if (current_block->instance_rate <= current_block->exit_rate) { current_block->instance_rate = 0; current_block->nominal_rate = 0; current_block->rounds_ahead = 0; current_block->acceleration = 0; current_block->rounds = 0; current_block->rounds_count_per_mstep = 0; //ST_PULSE_TI_Disable();//current_block should update current_block = NULL; } } #endif }
浮點數(shù)主要來自兩方面,一是每個中斷響應(yīng)函數(shù)中的單位時間delta t;另一個是轉(zhuǎn)每秒這個單位中,轉(zhuǎn)往往是小數(shù)。為了消滅浮點數(shù),我們對單位進行了轉(zhuǎn)換:
void plan_buffer_line(const float e, float feed_rate) { //void plan_buffer_line(const float x, const float y, const float z, const float e, float feed_rate, const uint8_t extruder){ // e is unit of round // feedrate is unit of round per second // push a block into pipeline block_t *block = &block_buffer[block_buffer_head]; //update the ini value of the block block->nominal_rate =(unsigned int)(feed_rate*ST_PULSE_FREQ); block->rounds = (unsigned int)(e - position[E_AXIS])*FLOAT_FACTOR; block->entry_rate = 0; block->exit_rate = 0; block->acceleration = (unsigned int)(DEFAULT_ACCELERATION); block->direction_bits = e > position[E_AXIS] ? 1 : 0; block->one_micro_step_mm=(unsigned int)FLOAT_FACTOR/MICROSTEP/RESOLUTION; ST_PULSE_TI_Enable(); //Enable the Interruption for stepper control and plannerS // Move buffer head block_buffer_head = next_block_index(block_buffer_head); //block_buffer_head=block_buffer_head+1; // Update position position[E_AXIS] = e; }
其中,在configuration.h中定義了轉(zhuǎn)換宏:
#define ST_PULSE_FREQ 10000 #define FLOAT_FACTOR (ST_PULSE_FREQ*ST_PULSE_FREQ)
由此可見,為了在計時器中斷響應(yīng)函數(shù)中不出現(xiàn)浮點數(shù),必須要給位移,速度和加速度乘以一個因子。而為了防止程序中出現(xiàn)變量超出整型大小而溢出,計時器頻率不能太高。具體的范圍,又受限于打印機的位移范圍(往往是0~200mm)。這種算法,數(shù)據(jù)結(jié)構(gòu)和處理器浮點運算性能局限性的共同作用,產(chǎn)生了最終的代碼,非常的經(jīng)典。
正是由于Team leader質(zhì)疑修改步進電機算法,我才能發(fā)現(xiàn)這么多隱藏在理所當(dāng)然中的深刻限制和工程智慧,感謝他的偏執(zhí)。同時我更加深刻理解了Marlin固件中步進電機算法的合理和經(jīng)典。
(完)
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。