深入Marlin固件(更新中。。。)

前言

Marlin是一款开源3D打印机固件,相信自己DIY过打印机的人对这个固件都不会陌生吧!目前市面上的桌面级3D打印机也都或多或少会有他的影子。Marlin的强大之处在于支持多种不同结构的3D打印机(如:xyz直角结构,coreXY、SCARA、三角洲等结构),支持多种硬件电路板,支持多种语言还附加了一些额外功能,如:自动调平等。正是这些强大的功能让Marlin固件的源代码看起来就比较难懂了。

最初接触3D打印是在大学的时候了,当时一直想自己做一台自己的3D打印机。后面接触到了Marlin固件和RAMPS控制板,熟悉过之后便有了将它移植到STM32的想法。之后便开始了对Marlin固件的漫漫学习之路,当时网上关于Marlin的资料还很少,不过就那些很少的资料中还是给到了我很大的帮助,我还加了很多3D打印机DIY的群向群中的各位大虾请教不懂得问题。后面到要做毕设的时候,我就直接找到毕设的老师商量,最终确定让我毕设做3D打印机的题目。最后经过风雨(查阅资料,各种请教,移植的代码,绘制的PCB,搭建机械结构)之后终于见到了彩虹(打印机原型机成功问世)。对于屏幕菜单部分当时也想直接移植Marlin固件的屏幕菜单的结构,当时看到各种宏定义,直接当场崩溃。只能另寻他路参考了一篇12864菜单设计的帖子,参照这个菜单结构完成了打印机的菜单设计。但当时遇到一个问题感觉用这个结构解决不是很好—SD卡中的文件的显示(我不知道SD卡中有多少个文件,如何提前确定当前菜单的条目数呢?)。

最近有些时间,想再看看Marlin固件源码,将之前没有理解的地方再深入了解一下,竟然无意中看到了game的文件,点击进入竟然是游戏的文件,直接把游戏的宏打开下载到RAMPS板中,真的有游戏可以玩。这一下又燃起了我对Marlin固件屏幕这一部分的兴趣。现在再看这些代码,比之前看起来轻松了一些。不过最新版的代码还是抽象性太高不好理解,我就回到了之前的1.0.2-2版本(更接近于C代码)的固件来看。看懂了直接将该部分的代码移植到STM32平台上测试了一下,扩展性上确实要比之前的那套菜单结构要好一些(该菜单结构是基于条目设计的,再将条目封装成菜单。而之前的菜单结构看是基于菜单页设计的)。但这套菜单结构需要单独维护大量的绘制函数和功能函数。不过总体来说这套菜单的结构还是优于之前的结构的。在当前菜单结构下,Marlin固件引入了u8glib(一种单色屏的GUI库),使得屏幕可以显示5行(lcd12864)而且状态菜单可以绘制各种动画效果。这样一看是不是,这就为后面写这种单色屏菜单提供了一种很好的思路呢。

本想这样就可以了,我又突发奇想—是不是可以将Marlin固件拆分成各个组件呢,既可以学习其中涉及到的算法知识又可以为后续写其他代码预留基础何乐而不为呢?

一、拆分那些结构呢?

循环队列
字符串处理
Marlin
显示
通信
核心
自动调平
菜单结构
串口通信/SD卡通信
获取G代码
解析G代码
运动规划
步进控制
温度控制
梯形加减速算法
S形加减速算法
Bresenham画线算法
PID控制算法
Auto Bed Leveling
Unity Bed Leveling
Mesh Bed Leveling
3点调平算法
线性调平算法
双线性调平算法

二、菜单结构

以下部分便是Marlin固件菜单结构的核心代码了。

/* Helper macros for menus */
#define START_MENU() do { \if (encoderPosition > 0x8000) encoderPosition = 0; \if (encoderPosition / ENCODER_STEPS_PER_MENU_ITEM < currentMenuViewOffset) currentMenuViewOffset = encoderPosition / ENCODER_STEPS_PER_MENU_ITEM;\uint8_t _lineNr = currentMenuViewOffset, _menuItemNr; \bool wasClicked = LCD_CLICKED;\for(uint8_t _drawLineNr = 0; _drawLineNr < LCD_HEIGHT; _drawLineNr++, _lineNr++) { \_menuItemNr = 0;
#define MENU_ITEM(type, label, args...) do { \if (_menuItemNr == _lineNr) { \if (lcdDrawUpdate) { \const char* _label_pstr = PSTR(label); \if ((encoderPosition / ENCODER_STEPS_PER_MENU_ITEM) == _menuItemNr) { \lcd_implementation_drawmenu_ ## type ## _selected (_drawLineNr, _label_pstr , ## args ); \}else{\lcd_implementation_drawmenu_ ## type (_drawLineNr, _label_pstr , ## args ); \}\}\if (wasClicked && (encoderPosition / ENCODER_STEPS_PER_MENU_ITEM) == _menuItemNr) {\lcd_quick_feedback(); \menu_action_ ## type ( args ); \return;\}\}\_menuItemNr++;\
} while(0)
#define MENU_ITEM_DUMMY() do { _menuItemNr++; } while(0)
#define MENU_ITEM_EDIT(type, label, args...) MENU_ITEM(setting_edit_ ## type, label, PSTR(label) , ## args )
#define MENU_ITEM_EDIT_CALLBACK(type, label, args...) MENU_ITEM(setting_edit_callback_ ## type, label, PSTR(label) , ## args )
#define END_MENU() \if (encoderPosition / ENCODER_STEPS_PER_MENU_ITEM >= _menuItemNr) encoderPosition = _menuItemNr * ENCODER_STEPS_PER_MENU_ITEM - 1; \if ((uint8_t)(encoderPosition / ENCODER_STEPS_PER_MENU_ITEM) >= currentMenuViewOffset + LCD_HEIGHT) { currentMenuViewOffset = (encoderPosition / ENCODER_STEPS_PER_MENU_ITEM) - LCD_HEIGHT + 1; lcdDrawUpdate = 1; _lineNr = currentMenuViewOffset - 1; _drawLineNr = -1; } \} } while(0)

程序中涉及到的变量的含义:

encoderPosition //记录了编码器位置的变量(如:编码器左转编码器位置加一,编码器右转编码器位置减一)
ENCODER_STEPS_PER_MENU_ITEM //编码器转几个脉冲对应于菜单一个条目
encoderPosition / ENCODER_STEPS_PER_MENU_ITEM //相当于记录了按键的位置
currentMenuViewOffset //屏幕顶行显示的条目对应于当前所有菜单条目的的偏移量(菜单开始显示的顶行)
_lineNr //当前需要绘制和处理的菜单条目
_menuItemNr //每个菜单中条目的索引
_drawLineNr //LCD显示行的索引(如:0-3)
wasClicked //记录了确认按键时候按下
lcdDrawUpdate //lcd绘制更新的标志位

通过START_MENU的宏函数开始创建菜单,通过MENU_ITEM的宏函数向该菜单中增加条目,通过END_MENJ的宏函数结束当前菜单的创建。MENU_ITEM的宏函数需要调用以下两个函数为为菜单的条目生成绘制和处理代码。

lcd_implementation_drawmenu_[type](sel,row,label,arg...)
menu_action_[type](arg...)

这意味着我们每创建一个条目就要维护这样的两个函数。通过对菜单类型进行划分可以一定程度上减少一部分该工作。对于Marlin固件菜单的类型大致分为以下几类:子菜单、编辑菜单、返回菜单、G代码菜单、功能菜单。

对于Marlin的菜单部分基本理解了上面的菜单结构整个菜单的框架就已经清晰明了了。对于该菜单结构移植到STM32的测试代码可以移步到:https://github.com/Apex-yuan/STM32F103_MARLIN_MENU具体的测试屏幕用的是lcd12864(ST7920)。

三、自动调平

对于自动调平功能上述框图中列出的是最新版的Marlin固件所支持的调平方式。自动调平功能的本质原理并不复杂,复杂的是整个调平过正中的流程,这流程是为了确保整个调平过程中的安全。这里先简单对以下两种调平方式简单分析,这两种调平方式要却保打印床是一个平整的平面。

3点调平方式

我们根据之前学过的只是,很容易知道3个不在同一直线上的点可以确定一个平面,该方式正是利用的这个原理。

  1. 首先利用探针探测3个不在同一直线3个点的坐标。
  2. 求取平面的法向量
  3. 计算该平面的方向量与理论平面法向量之间的旋转矩阵
  4. 以后每次打印的目标点都通过该旋转矩阵变换便可以变换为相对于当前平面的相对位置。

单线性网格调平方式

在高中大家应该都会学过线性回归分析的课程,中间用到了最小二乘法的知识,这里其实利用的也是这方面的原理。只不过我们当时使用一系列的平面点来拟合直线,而我们现在是利用一系列的空间点来拟合平面。

  1. 探测一系列的点
  2. 通过最小二乘法的优化算法求取该平面的法向量
  3. 计算该平面的方向量与理论平面法向量之间的旋转矩阵
  4. 以后每次打印的目标点都通过该旋转矩阵变换便可以变换为相对于当前平面的相对位置。

对于调平背后的算法而言,如果不深入研究,可以将它视为一个黑箱,只需知道输入和输出即可。

深入Marlin固件相关推荐

  1. marlin固件烧录教程_i3型3D打印机制作详解――Marlin固件介绍

    Marlin固件下载 基本配置 使用 Arduino IDE 打开 marlin.ino,切换到 Configuration.h 即可查看并修改该文件.或者使用任何一款文本编辑器(notepad,no ...

  2. 机械臂——arduino、marlin固件、printrun软件

    最近了解到,在市面上大多数机械臂控制都采用的arduino这个开源硬件来控制的,而我发现既然会单片机,就没有必要采用arduino来控制了,arduino只是一种为了简化编程而开发一种软硬件控制平台, ...

  3. Marlin固件之—:基础入门与测试

    一.Marlin的简单介绍 Marlin固件是一个3D打印的开源固件,3D打印固件有许多,Marlin最为健全和强大,当然相对也会复杂一些.使用Gcode控制爱,Gcode是数控机床等工控控制使用范围 ...

  4. Marlin固件之二:源代码详解与移植

    由于需要进行固件定制化,Marlin固件太过于强大和紧凑,我对这个固件进行了裁剪,只剩下主枝干,实现功能的定制和裁剪.以下的代码详解是基于我已经移植在stm32上面的一个程序进行的.

  5. marlin固件烧录教程_Marlin固件配置教程详解

    首先从Marlin固件GitHub下载固件源代码,也可从Makeboard网盘下载. Marlin固件主要分为两个版本,一个是1.0.2-2版本,是稳定版,已经一年没怎么更新了,功能比较少,网上的教程 ...

  6. 3D打印机Marlin固件双Z轴设置

    3D打印机Marlin固件双Z轴设置 在3D打印机Marlin固件的最新版本2.1.1中,设置双Z轴和老版本有一些改动.记录一下如何在最新版本的Marlin固件中设置双Z轴. 以MKS GEN_L V ...

  7. Marlin固件显示其他Gcode文件扩展名

    Marlin固件支持Gcode文件的打印.可是,有时候我们并不喜欢Gcode的扩展名.不过也是受切片软件的限制,我们切片出来的文件一般都是Gcode后缀的扩展名. 假设,只是假设有需要,如果你想改掉后 ...

  8. 3D打印机DIY之三------Arduino开发环境构建和Marlin固件配置

    一.Arduino开发环境 1.搜索Arduino IDE下载安装即可. 2.使用VS code开发Arduino Arduino IDE很简单,很多功能都没有,使用起来很不方便.因此建议使用Visu ...

  9. marlin固件烧录教程_【打印虎】Marlin firmware RepRap Prusa i3 3D打印机固件配置教程.pdf...

    [打印虎]Marlin firmware RepRap Prusa i3 3D打印机固件配置教程 [打印虎原创]RepRap_Prusa_i3_3D 打印机固件 Marlin_firmware 配置教 ...

  10. 3D打印机如何使用marlin固件控制普通57闭环步进电机-“教程”

    本次教程中主板型号为MKS GENL V2.1,创客之家的 探究原因是在b站看见大佬飞翔的AMX用雷赛57闭环步进做3D打印,但是可惜的是大佬刷的klipper固件,我用的marlin固件 在大佬评论 ...

最新文章

  1. 搞机器学习,Python和R哪个更合适?
  2. Fedora安装Samba与windows共享文件
  3. windows phone (23) ScrollViewer元素
  4. 为什么静态成员、静态方法中不能用this和super关键字
  5. C/C++ 文件的后缀名
  6. 【转】c#处理3种json数据的实例
  7. [原创]软件测试工具简介及下载地址(不定时更新)
  8. linux 网站获取数据失败,AnyBackup-Linux 环境下 Sybase 授权数据库实例时,提示错误:获取实例失败...
  9. 一行代码如何隐藏 Linux 进程?
  10. 2017-09-16
  11. 1091 N-自守数 (15 分)—PAT (Basic Level) Practice (中文)
  12. [转]android刷新后R.java不见了
  13. matlab 向量转置,matlab中向量和矩阵怎么转置 值得收藏
  14. CSS样式实现雷达扫描动效
  15. 交换机及IP地址(概念)
  16. jquery 源码分析系列1
  17. 成果展示 | 大数据应用开发平台DWF
  18. 更多 ViewBinding 的封装思路
  19. 小小靖Java成长日记02
  20. 第101个五四青年节节目观后感

热门文章

  1. python计算无穷级数求和_[探求无穷级数求和的几种常用方法]无穷级数求和常用公式...
  2. 十二进制加计数器-20151112
  3. python语言def_python中def的含义
  4. 机器学习(聚类七)——层次聚类的优化算法
  5. 服务器频繁重启怎么解决
  6. 【Excel文件合并工具】
  7. 3D打印路径填充算法 -- Connected Fermat Spirals for Layered Fabrication
  8. 【BP回归预测】鲸鱼算法优化BP神经网络回归预测(多输入单输出)【含Matlab源码 1554期】
  9. 一杯茶的时间,上手第三方登录类库 JustAuth
  10. 矢量网络分析仪(矢网)组成和原理简介