Some Random Thoughts on Marlin | Marlin随想

为了毕设做打印机研究3D打印机啊。

1 Marlin

Marlin是一种3D打印机的固件(Firmware)。所谓固件,就是刷进开发板(或者单片机,比如Arduino)的程序。我们用Arduino的时候,上传的程序就是固件。3D打印机有很多类型的,比如Makerbot,Ultimaker,ZCorp等等。其中最简便最普及的是Reprap的。Reprap是一类打印机,其中很多不同型号,其中比较有名的像Mendel, Prusa, Huxley。而它们共同的特点是由Reprap社区开发的,大多是开源的,自复制的。因此非常便于DIY和改装。

固件也是有很多种的,比较有名的就是Grbl,Sprinter,Teacup等等。Marlin是基于Grbl和Sprinter开发的,这两年非常的活跃。支持的板子和LCD型号极为丰富。参考一下Github上几种固件的活跃度。所以选择了这个固件来修改。

2. 初级配置

一般把Marlin拿过来用呢,改几个宏的参数就可以烧固件了。这在Marlin的文档中写的很清楚。我们改Configuration.h这个文件就可以了。

涉及到以下内容:

  • 打印机的驱动形式,Cartesian(最普通的), Delta(三角式的), CoreXY(一种比较紧凑的平面驱动方式)还是SCARA(机械臂?)
  • 驱动板,RAMPS? RUMBA? MKS?…….
  • 有多少个挤出头?
  • xyz轴的步长和速度?
  • endstop在哪里?
  • 温度设定?
  • LCD的型号?

这些都在Configuration.h中以宏的方式定义。举个例子,有一段长这样:


#define X_MIN_POS 0
#define Y_MIN_POS 0
#define Z_MIN_POS 0
#define X_MAX_POS 200
#define Y_MAX_POS 200
#define Z_MAX_POS 170

这个就是在定义XYZ的范围了。

稍微再深入一点的可以改Configuration_adv.h和针脚的文件比如pin_RAMPS.h(假设正在用ramps)


#define X_DUAL_STEPPER_DRIVERS
#define Y_DUAL_STEPPER_DRIVERS
#define Z_DUAL_STEPPER_DRIVERS
#define DUAL_X_CARRIAGE

比如有这么一个功能,可以在某个方向上有两个步进电机。还可以有两个打印头。

3. 深入修改

改代码。

有时候会涉及到需要修改GCode定义的情况,以我举例。

原来GCode中G0代码只能控制四个轴:XYZE,比如这样:


G0 X12.5 Y14.5 Z11.3 E2345

但是我在做的3DP原理的打印机还需要另外单独控制两个轴:供料平台W和扫子S,所以希望可以有这样的Gcode:


G0 X12.5 Y14.5 Z11.3 W11.3 S6 E2345

这就必须修改原始代码了。有两种方式顺藤摸瓜:

  1. 从最上层开始,看setup()和loop()是如何调用的。
  2. 搜索法,比如可以搜索G0,看看G0代码如何被处理。也可以搜索X_以及_X,看和XYZ轴相关的都有什么代码。

比如看loop(),发现用process_next_command()处理指令的。跳进去找到处理G0的叫做gcode_G0_G1(),这里面有两个相关方法gcode_get_destination()和prepare_move_to_destination()。
前一个仅仅是解析指令,把目标位置存到destination[XYZE]中。这里我们就需要修改了,原来XYZE定义为4,但我们需要让它是6。同样还要改LOOP_XYZE(i)这个预定义的循环,让它遍历6次。LOOP_XYZE(i)定义的上面正好写了轴的枚举,同样加上W_AXIS和S_AXIS
后一个又调用了两个,prepare_move_to_destination_cartesian()和set_current_to_destination(),后一个仅仅是一个内联函数,把destination拷到current中。

prepare_move_to_destination_cartesian()调用了line_to_destination(),而它是一个内联函数,实际使用的planner.buffer_line()

终于到了一个关键点,我们发现有一个叫planner的类,它其实就是做运动规划的一个类。planner.buffer_line()参数列表里只有XYZE四个轴,那么我们必须从这里开始修改这个函数了,让它再接受两个轴:W和S。

继续看,planner.buffer_line()调用了_buffer_line(),这个函数很长,在把各轴的运动目标放进buffer准备让stepper移动。通读一遍这个函数,把有XYZ的地方复制一下变成WS。
回头看一眼planner这个类,似乎里面还有几个方法比如_set_position_mm(),也是仅仅接受XYZE四个轴,虽然我们不知道谁调用了它,但保险起见同样修改一下吧。

到这里似乎线索断了,我们只是处理了运动目标啊,还没有让步进电机动啊!

我是强行搜索的X/Y/Z,看哪里有它们。最后发现了一个stepper.h/stepper.cpp的类,顾名思义是控制步进电机的!
读了一下发现了非常神奇的事情,在Marlin里步进电机的控制不是用我们平常arduino里在loop()中用digitalWrite(Pin, HIGH)的方法做的,而是用ISR(Interupt Service Routines)做的。事情一下子复杂起来。
尝试理解了一下,大概是说Arduino内部有一个timer,以很高的频率处理,我们可以直接利用这个timer控制程序。用ISR来控制步进电机,就可以让步进电机每隔一定时间走一步,所以其实是相当于步进电机有一个独立的线程控制运动,或者说相当于有第二个loop()处理步进电机运动。同样,Marlin里侦测温度也用了一个ISR,也就是还有一个线程处理温度侦测。

于是通读一遍stepper.h/stepper.cpp,把有XYZ的地方统统加上WS。

当然只改这些还不够,又强行搜索X/Y/Z,在Conditionals_post.h,Marlin.h,Marlin_main.cpp,stepper_indirection.h等等地方又修改了一些。最后的代码可以参考Marlin Forked by maajor,看第一次提交的修改就可以了。

最后总结一下,如果想要修改GCode定义需要作的事情,以G0为例:

  • 从gcode_G0_G1()开始,查看它的调用。更改对应函数。这一部分代码在解析GCode并得到运动目标。
  • 修改stepper.h/stepper.cpp,由于具体运动部分是ISR处理的,无法通过查看调用的方式得到,所以需要直接读ISR的代码,修改相关函数。
  • 强行搜索X_,_X查缺补漏增加宏定义。由于一般宏的命名都是包含_X _Y _Z,或者X_ Y_ Z_的,所以能找到。

很开心,修改完成以后发现G92,用来位置归0的也是可以多两个轴的。但是M114获取当前位置,还需要顺藤摸瓜看看代码,修改一下才能显示另外几个轴的数据。

参考资料:

MarlinFirware on Github
Marlin Forked by Maajor
Configuring Marlin
Marlin GCode
Arduino教程——外部中断的使用
自己控制 timer1 計時器定時做多件事(教程, 設定timer1 定时器)

发表评论

电子邮件地址不会被公开。 必填项已用*标注