龙芯俱乐部开源技术社区

 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
热搜: 活动 交友 discuz
查看: 17168|回复: 11

3D打印机Marlin固件的详细分析

[复制链接]

9

主题

62

帖子

2848

积分

金牌会员

Rank: 6Rank: 6

积分
2848
发表于 2016-4-13 08:25:59 | 显示全部楼层 |阅读模式
本帖最后由 sigaofeng 于 2016-4-13 09:17 编辑

本帖出处:http://blog.sina.com.cn/s/blog_679933490102vv8z.html

Marlin固件的编写采用的是Arduino IDE编写环境,并是在Arduino开源硬件的支持下编写的,这个开源硬件的好处是提供所以硬件上的外设部分的驱动,并且调用简单,还有就是支持C++的编写环境。话说我一直觉得用C++来编写嵌入式程序会比C要好,因为可以把所以外设驱动打包成一个大类,这样在调用的时候就可以直接例化类和调用了,相比于C,其的可读性会好些。当然这些也只是我的个人看法,或许会比较幼稚。

​      言归正传,Marlin固件固件主要由四个部分组成,主程序控制部分(G指令解析与处理)、串口通信部分、PID温度调控部分、和运动控制部分,当然还有其他的,比如:液晶显示,SD卡读取等等。

      固件中的运行过程主要有三个部分:主程序里面进行命令解析和处理,定时器1中断进行步进电机的控制和打印头的运动控制,定时器0中断则进行温度的检测和加热控制。这三个部分也都和​固件中的主要部分相关。

1、程序工作原理

​      Marlin固件分前后台系统。前台系统即主程序,后台系统则是中断程序。在Marlin中用到了两个定时器中断:定时器0和定时器1。

         主程序在前台的任务有:

1、  与上位机进行通信,获得G指令

2、  进行G指令解析,区分指令内容及指令参数,并将参数换算为整数

3、  G指令的分类执行

4、  温度管理、限位开关和LCD的控制

       在G指令的分类执行中,Marlin固件用到了队列的概念,将每条G指令变成一个运动block,一个block包含的与该G指令下运动所需的所有参数(终点坐标、速度、加速度、挤出丝量等)。在前台系统中,程序只负责对G指令进行分析,并计算和生成一个block,并将block打入block缓冲池中。其他,则是与上位机进行交互。

       定时器0主要负责对挤出头的温度进行控制。在主程序中设置好定时器0的定时时间,等待定时中断的产生,并在中断中进行温度检测控制。

       定时器1主要负责对步进电机进行控制,是Marlin中运动控制部分的核心。在主程序中先设置好定时器1的初始定时时间,等待中断后,在中断执行block中的运动。首先从block缓冲池中取一个block,分析该block中的运动参数,设置运动方向,然后调控步进电机运动。定时器1的定时时间就是步进电机的运动速度,所以在控制步进电机的速度方面,可以通过时间计算来实现。每个中断执行一个block中的1步或几步,这个参数在系统初始化时进行相关设置。

2、主程序控制部分   

     主程序控制部分程序流程图如下图所示:主要实现的功能也就是获取G指令,解析指令,处理指令,相关温度和活动轴管理。​

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

9

主题

62

帖子

2848

积分

金牌会员

Rank: 6Rank: 6

积分
2848
 楼主| 发表于 2016-4-13 08:27:50 | 显示全部楼层
本帖最后由 sigaofeng 于 2016-4-13 09:26 编辑

在Get_command函数中主要是一些字符串的处理,获得相应字符串及相关参数等等,在process_command函数中则是一个大的switch-case语句,用来处理相关指令集等。Mange_heater函数则是PID温度控制部分。
3、运动控制部分
    ​  定时器1主要负责对步进电机进行控制,是Marlin中运动控制部分的核心。在主程序中先设置好定时器1的初始定时时间,等待中断后,在中断执行block中的运动。首先从block缓冲池中取一个block,分析该block中的运动参数,设置运动方向,然后调控步进电机运动。定时器1的定时时间就是步进电机的运动速度,所以在控制步进电机的速度方面,可以通过时间计算来实现。每个中断执行一个block中的1步或几步,这个参数在系统初始化时进行相关设置。
    ​  运动控制部分定时器1中断流程图如下:


   

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

9

主题

62

帖子

2848

积分

金牌会员

Rank: 6Rank: 6

积分
2848
 楼主| 发表于 2016-4-13 08:29:01 | 显示全部楼层
本帖最后由 sigaofeng 于 2016-4-13 09:26 编辑

bresenham算法   
在运动控制部分中用到了Bresenham算法,具体原理及实现过程如下:

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

9

主题

62

帖子

2848

积分

金牌会员

Rank: 6Rank: 6

积分
2848
 楼主| 发表于 2016-4-13 08:29:38 | 显示全部楼层
算法实现流程图:

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

9

主题

62

帖子

2848

积分

金牌会员

Rank: 6Rank: 6

积分
2848
 楼主| 发表于 2016-4-13 08:30:45 | 显示全部楼层
速度控制
       Marlin固件中对每个一个运动block块都会有一个速度算法控制,因为步进电机在启动进的速度不能太大,必须要有一个加速的过程,因此,在速度控制时,Marlin固件采用的是梯形曲线速度控制算法。关于其速度的实现,则是通过控制定时器1的中断频率来实现,中断频率越快,输出的频数信号越频繁,步进电机速度越快。
       具体调用的函数如下:
       plan_buffer_liner()
       ——> ​calculate_trapezoid_for_block(block, block->entry_speed/block->nominal_speed,
  safe_speed/block->nominal_speed);
 
       ——>planner_recalculate()
      说明如下:
      plan_buffer_liner()函数主要计算运动控制块block_t中的相关变量的值。​
 ​     calculate_trapezoid_for_block(block, block->entry_speed/block->nominal_speed,  safe_speed/block->nominal_speed);
 ​      在plan_buffer_line()最后被调用,此函数的功能在于计算block的进入速度和退出速度,这只是初始计算,planner_recalculate_trapezoids()是进入速度和退出速度的最终计算,它将所有block的梯形曲线连接起来。这个函数最主要是计算加速阶段、匀速阶段以及减速阶段的位移,当达不到额定速度时,匀速阶段的位移为零。​
       planner_recalculate()​
      函数体为:
  {
                   planner_reverse_pass();​
                  planner_forward_pass();
                  planner_recalculate_trapezoids()   
}
在plan_buffer_line()中,调用完calculate_trapezoid_for_block ()后调用此函数,planner_recalculate()中包含了三个函数,planner_reverse_pass()是从后往前依次检查并计算block的进入速度,planner_forward_pass()是从前往后依次检查并计算block的进入速度。其实质是这两个函数分别负责处理不同block的情况而存在的;再加上plan_buffer_line()中对block的进入速度的计算,利用planner_recalculate_trapezoids()可将所有块两两相连。
block_t结构体
typedef struct {
  long steps_x, steps_y, steps_z, steps_e;  每个坐标轴所需走的步数
  unsigned long step_event_count;       完成这个block所需走的步数,steps_x, steps_y, steps_z, steps_e的最大值
  long accelerate_until;   梯形曲线中的加速距离,单位steps
  long decelerate_after;    加速和匀速的距离,单位steps
  long acceleration_rate;     加速率,用来计算加速度
  unsigned char direction_bits;     这个block的方向位,“1”反向,“0”正向,每一个位代表一个轴的方向 
  unsigned char active_extruder;       所用到的有效的挤出头
  float nominal_speed;             额定速度,即梯形曲线的匀速阶段的速度
  float entry_speed;      进入速度,即从上一个block进入到这个block的速度
  float max_entry_speed;      最大进入速度,进入速度不能超过这个值
  float millimeters;                   总路程,单位mm
  float acceleration;加速度,单位mm/sec^2
  unsigned char recalculate_flag;       连接处重新计算梯形速度曲线的标志
  unsigned char nominal_length_flag;     能达到额定速度的标志
 
  // Settings for the trapezoid generator梯形速度曲线产生器的设置参数
  unsigned long nominal_rate;        这个block的单位为steps/sec的额定速度
  unsigned long initial_rate;      梯形曲线的初始速度/进入速度,单位steps/sec
  unsigned long final_rate;       梯形曲线的退出速度,单位steps/sec
  unsigned long acceleration_st;     单位为steps/sec^2的加速度
  unsigned long fan_speed;风扇速度
  volatile char busy;正在处理这个block的标志位,“1”表示正在执行这个block
} block_t;
4、PID温度控制
       Marlin固件在进行初始化之后进入loop()循环,和运动控制部分一样,PID温度控制也可看为前后台系统,后台接收上位机传过来的G代码,通常为M190(设置打印床温度并通过while循环等待打印床达到目标温度)、M104(设置挤出头目标温度但不等待)和M109(设置挤出头温度并通过while循环等待挤出头达到目标温度)。loop()调用process_commands()针对解析过来的G代码执行设置目标温度并且调用manage_heater()计算出一个输出值。而挤出头和打印床的加热和保持温度恒定的任务则需要前台的ISR(TIME0)中断服务程序根据manage_heater()计算出的输出值来完成。
       在Marlin固件中,PID算法的采样周期T= ((16.0 * 8.0)/(F_CPU / 64.0 / 256.0)),而TIME0在比较定时中断模式下的中断时间t=128Clocks/ F_CPU,可算出T/t=16384。即在一个采样周期内,TIMEO共产生16384此中断,ISR(TIME0)通过连续不断的产生中断来控制加热开关并维持系统温度的稳定性和正确性。
      固件中,只有挤出头的加热采用了PID算法进行精确控制,而打印床只是运用了继电器开关来控制温度,当前温度<</span>目标温度±偏差温度(2℃),则继电器开进行加热,否则,继电器关进行冷却。​

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

9

主题

62

帖子

2848

积分

金牌会员

Rank: 6Rank: 6

积分
2848
 楼主| 发表于 2016-4-13 08:31:44 | 显示全部楼层
结合Marlin固件中有效的G代码和由打印机使用的切片软件产生的G代码文件来看,PID温度控制只用到了M104、M190和M109。而M104只负责设置挤出头的目标温度,M190和M109才是真正分别对打印床和挤出头加热达到目标温度的G代码。从process_commands()开始进入PID温度控制模块,由状态机转入到M190和M109的功能部分,由一个while循环利用当前温度和目标问的条件来等待挤出头和打印床上升到目标温度直至稳定。
       系统的加热过程类似于PWM波形,后台manage_heater()计算出的输出值相当于PWM的占空比,输出值越大,加热开关打开的时间越长;输出值越小,加热开关打开的时间越短;输出值为零,则关掉加热开关。综合来看,可称其为软件实现的PWM。
       总体来看,PID温度控制部分主要可分为manage_heater()和ISR(TIME0)这两部分,manage_heater()体现出PID算法,负责计算输出值;ISR(TIME0)根据输出值负责控制加热开关和维持系统温度,体现出PWM的特性;而PID_autotune()只是温度控制部分的一个小的功能函数,用来调整P、I、D三个参数值,需要调用M303来执行,而在实例切片中产生的G_code来看,并没有调用M303。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

9

主题

62

帖子

2848

积分

金牌会员

Rank: 6Rank: 6

积分
2848
 楼主| 发表于 2016-4-13 08:32:29 | 显示全部楼层
 soft_pwm和soft_pwm_bed分别为挤出头和打印床的温度控制输出值,二者的加热与否需要根据这两个值来判断。manage_heater()只负责计算输出值,挤出头的温度控制输出值由PID算法计算得出,而打印床的温度控制输出值只根据当前温度和目标温度的比较来设定,小于则输出值为128,大于则为0。加热的任务需要中断服务程序ISR(TIME0)根据这个输出值来控制加热开关。
       ISR(TIME0)内部主要由测温和加热控制组成。测温部分采用状态机形式,每个时刻状态,程序做不同的事,具体可参见Marlin固件代码。​

28

主题

260

帖子

16万

积分

论坛元老

Rank: 8Rank: 8

积分
169558
发表于 2016-4-13 09:24:49 | 显示全部楼层
感谢楼主分享。
即使是转帖,即使看不懂,也要顶一顶!

9

主题

62

帖子

2848

积分

金牌会员

Rank: 6Rank: 6

积分
2848
 楼主| 发表于 2016-4-13 09:31:10 | 显示全部楼层
本帖最后由 sigaofeng 于 2016-4-17 22:35 编辑

多看几遍慢慢就懂了,看完本帖再看这本书,

marlinsourcecode马琳固件精读.pdf

http://pan.baidu.com/s/1kVQw8Dp




48

主题

257

帖子

4920

积分

新手上路

Rank: 1

积分
4920
发表于 2016-4-22 14:28:40 | 显示全部楼层
超赞!
我相信我再多看几篇也能看懂
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|龙芯俱乐部开源技术社区

GMT+8, 2024-4-24 16:36 , Processed in 0.108919 second(s), 22 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表