您好,登錄后才能下訂單哦!
對于入行一年的我來說,桌面級FDM打印機很多功能細節,我還沒能仔細了解:藏在固件代碼背后的信息內容,我也遠沒有完全消化。這正說明一個簡單的道理:消化一段代碼的最有效方法是對其移植或者重寫。換言之,僅僅是走馬觀花的瀏覽一遍,除非自己曾經編寫過類似程序,很難能透徹的領會固件代碼的精髓。特別是對Marlin這個數百名(可能不準確)開源工程師貢獻和維護的大型固件項目。
Marlin的步進電機驅動子系統,是由中斷響應函數實現的。如果是恒定速度的步進電機驅動,實現就和這句話一樣簡單。不過對于3D打印機系統,x,y軸的運動往往速度變化非常頻繁:不僅在每次更新位置的速度不同,而且每一段位移的速度也需要經歷加速,恒速和減速階段。這是由機械系統的慣性特征決定的:如果不同動作之間的速度銜接不好,會對電路系統造成強大的電流沖擊。特別是3D打印過程,這種速度的變化每次打印任務都數以萬計,這就意味著電路壽命將大打折扣。
Marlin系統的速度銜接,基于leib Ramp Algorithm[1],這是一個支撐步進電機速度和控制器計數器頻率關系的算法理論,由IBM的工程師于1994年發表并于2004年在控制器內實現。這里算法實現的關鍵在于路徑規劃器(planner)。路徑規劃器的設計意味著,程序在執行步進電機的動作之前,就已經計算好了整個過程的速度曲線。后面就只是Stepper模塊忠實地執行。
在機器層面,這樣的設計減少了中斷響應函數中的運算量,這對于單片機來說非常友好。同時3D打印機的機械運動相比控制器的16M主頻來說要慢很多,路徑規劃器相比直接驅動,增加了一個運動緩存。這樣就能夠有效的利用控制器的高頻率,里面蘊藏著“空間換取時間”的思想。
在代碼層面,planner的本質在于對于一個FIFO的管理。使用C++的結構體指針數據結構能夠非常優雅的實現這個緩存的創建和管理:planner.h:
typedef struct { // Fields used by the bresenham algorithm for tracing the line long steps_x, steps_y, steps_z, steps_e; // Step count along each axis unsigned long step_event_count; // The number of step events required to complete this block long accelerate_until; // The index of the step event on which to stop acceleration long decelerate_after; // The index of the step event on which to start decelerating long acceleration_rate; // The acceleration rate used for acceleration calculation unsigned char direction_bits; // The direction bit set for this block (refers to *_DIRECTION_BIT in config.h) float nominal_speed; // The nominal speed for this block in mm/sec float entry_speed; // Entry speed at previous-current junction in mm/sec float max_entry_speed; // Maximum allowable junction entry speed in mm/sec float millimeters; // The total travel of this block in mm float acceleration; // acceleration mm/sec^2 unsigned char recalculate_flag; // Planner flag to recalculate trapezoids on entry junction unsigned char nominal_length_flag; // Planner flag for nominal speed always reached // Settings for the trapezoid generator unsigned long nominal_rate; // The nominal step rate for this block in step_events/sec unsigned long initial_rate; // The jerk-adjusted step rate at start of block unsigned long final_rate; // The minimal rate at exit unsigned long acceleration_st; // acceleration steps/sec^2 unsigned long fan_speed; #ifdef BARICUDA unsigned long valve_pressure; unsigned long e_to_p_pressure; #endif volatile char busy; } block_t; block_t block_buffer[BLOCK_BUFFER_SIZE]; // A ring buffer for motion instfructions volatile unsigned char block_buffer_head; // Index of the next block to be pushed volatile unsigned char block_buffer_tail;
volatile 關鍵字確保了隊列頭和隊列尾被不同函數訪問過程中,編譯器不會因為優化和丟失更改行為。block_t類型的指針可以方便的方位結構體內任何元素。在后面的planner規劃動作plan_buffer_line()中,代碼可以用非常優雅的結構體指針來完成。
每當3D打印機解析到位移指令的時候,plan_buffer_line()函數就被調用。在里面新的block_t首先被創建,并且排入隊列的隊尾;然后執行calculate_trapezoid_for_block(),計算新的block_t的關鍵速度節點及其對應的step數目;接著更新隊列里面所有block_t的連接速度:之前隊尾的block_t的收尾速度和相關速度節點會被更新。最后調用st_wake_up()保證stepper執行的中斷打開。
而在%steppper中,ISR函數負責在主循環之外,執行隊列里可能存在的所有block_t。在ISR中,首先由plan_get_current_block()讀取隊列首的block_t,然后按照結構成員的step數,調用STEP_ADD和STEP_IF_COUNTER兩個宏來執行x,y,z三軸的運動。ISR每執行一次,三路各發出一個脈沖,并通過lamp ramp算法更新,根據下一個速度值來更新OCR1A寄存器來設定下一次中斷響應的周期。
整個軟件C++實現妙至毫巔。建議大家在win環境使用eclipse來查看Cpp工程。eclipse對條件預編譯的支持非常完美,#if def能夠準確顯隱,可讀性非常好(見下圖)。全局查詢,go to define等功能也很完備。
另外,eclipse有豐富的快捷鍵支持看代碼:用ctrl+shift+G就能查到變量的引用;F12就能查到變量的定義。Alt+左鍵 /右鍵回到之前的鼠標位置和之后的鼠標位置;用CTRL+SHIFT+P就能尋找括號的另外一半。
附Marlin項目
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。