开源Marlin2.x源代码架构学习笔记
扫描二维码
随时随地手机看文章
点击上方「嵌入式云IOT技术圈」,选择「置顶公众号」第一时间查看嵌入式笔记!
最近两个月MBA美帝在职研的课程即将结束,经过个人的努力,目前两门课:全球商务、定量决策均分绩点如下,基本上逼近满分(凡尔赛了,哈哈哈),光鲜亮丽的背后,带领小组始终冲在了班级的最前面,同时也付出了不少精力和汗水,通宵了不知道多少个夜晚,交完学期项目以后就结课啦,开始进入下一门课的学习!所以开始有时间可以折腾新东西啦:(不少同学应该知道我开了个知识星球,专门分享这些内容,这里就不好放出来啦,有兴趣和我说即可)
Marlin相当庞大,自2.x版本开始,陆陆续续开始集成了32位机,主流的STM32、LPC等高性能MCU逐渐取代了以Arduino2560等8位机的3D打印机方案。Marlin,庞大到让每个初学者开始学习看代码都会觉得闻风丧胆,当然我刚开始接触的时候也不例外,但是不管是学什么东西,只要把大方向掌握了,那么细节的东西用到了再慢慢研究也不迟,同时,Marlin的文档非常少,对于初学者来说,是非常不友好的!以下这些介绍也是Marlin刚开放不久的说明文档,笔者英语水平有限,如有翻译失误,尽情纠正与谅解!
1、Marlin代码工程架构
以下是原版的Marlin固件解压下来的显示情况:当使用 PlatformIO 构建Marlin时,它会在.pio此处创建一个文件夹,并且在shell中使用git的时候,这 是当前的工作目录,如下图所示:1.2、 buildroot 文件夹
该目录包含开发人员的帮助脚本、字体工具、CI测试工具、Marlin图标和其它数据1.3、 buildroot/share/PlatformIO 文件夹
电路板定义、环境变量、链接脚本和构建脚本会存放在这个地方,这个文件夹内的构建环境会经常引用 ini文件夹1.4、 ini 文件夹
包含按 MCU 类型组织的所有 PlatformIO 环境设置, platformio.ini 文件将这些引入为 PlatformIO 提供构建/上传/调试设置,有一个文件特别有用:即是Marlin自定义构建脚本 features.ini 在构建开 始时用于根据启用的功能过滤源文件,因此构建能够完成得更快。1.5、 Marlin 文件夹
构建Marlin时,配置文件就放在这个位置:1.6、 Marlin/src 文件夹
包含Marlin应用程序,Marlin基于 Arduino 框架进行开发,所以它含有 Arduino 框架的setup()
和 loop()
功能。2、Marlin应用源代码Marlin/src 文件夹
Marlin应用源代码主要由以上文件夹组成,核心的入口程序文件是MarlinCore.cpp
,MarlinCore.h
会做一些宏和变量以及函数的声明,在这个文件中可以找到基于 Arduino
框架的 setup()
和 loop()
函 数,其余的文件夹包含的源码主要的作用如下:- core文件夹
inc/MarlinConfig.h
就可以确保这些文件按预期顺序包含在内。- feature文件夹
- gcode 文件夹
GCodeParser
类的定义以及所有G代码命令的实现(有一些不在这里实现,但大部分是的),这 些功能都被包装在一个名为 GcodeSuite
的类里,G代码实现的文件被捆绑在多个类别的子文件夹 中,这些文件被命令为具体的G代码,因此可以使用 IDE 的查找功能找到它们。- HAL文件夹
Marlin2.x
版本实现了硬件抽象层,更好的屏蔽了平台的差异性,使其它的平台更好的兼容 Arduino
框架。- inc文件夹
Conditionals*.h
和 SanityCheck.h
文件 。- lcd 文件夹
LCD
、TFT
、OLED
、编码器、按钮和串行控制器的相关代码都放在这里,语言翻译通常仅 适用于外部控制器,因此语言翻译也放在这里。- libs文件夹
- module文件夹
- pins文件夹
pins.h
根据 MOTHERBOARD
设置进行包含,由于 pins.h
是 MarlinConfig.h
的包 含文件注意,因此它不会包含在其它的地方。- sd 目录
CardReader
类是Marlin用 于导航目录、打开G-code
文件和从SD卡(或其他媒体)打印的主界面。自从Marlin 2.0.8
以来,所有 的媒体类型都派生自 DiskIODriver
抽象类。3、Marlin的配置
马林是高度可配置的。您将在源代码的许多地方发现应用配置选项来打开和关闭代码、更改行为和提供 值。- Marlin源文件如何获得它需要的所有配置值?
conditionals_ad.h
)的代码都 必须包含 inc/ marlinconfigpreh
,而任何需要完全实现的硬件配置的代码都必须包含 inc/MarlinConfig.h
。让我们仔细看看每个文件包含的头文件。3.1、 MarlinConfigPre.h
3.2、 MarlinConfig.h4、一个典型的源文件
将这些内容放在一起,典型的源文件至少将包含 marlinconfigpreh ,以便它可以预先检查一些配置 值。只有在需要时,才会包含其他头文件。有些源文件包含一些特性的头文件是很常见的。/**
* (c) 2021 Marlin Firmware
* A typical Marlin source.cpp file.
*/
#include "inc/MarlinConfigPre.h"
#if ENABLED(MY_COOL_FEATURE)
#if ENABLED(EXTENSIBLE_UI)
#include "lcd/extui/ui_api.h"
#endif
#endif // MY_COOL_FEATURE
5、典型的头文件
Marlin头文件不会像源文件一样使用#if…#endif
来包装。相反,如果不需要头文件,那么它就不会被 包含。Marlin还避免使用c风格的 #ifdef
包装器,并且只在自己的头文件上使用 #pragma
一次。/**
* (c) 2021 Marlin Firmware
* A typical Marlin header.h file.
*/
#pragma once
extern int my_feature_var;
void do_feature_stuff();
当该特性被禁用时,一些头文件将提供空函数。这使得在单个点关闭东西更容易,并且可以使其他地方 的代码更整洁。/**
* (c) 2021 Marlin Firmware
* A typical Marlin header.h file.
*/
#pragma once
#include "inc/MarlinConfigPre.h"
#if ENABLED(MY_COOL_FEATURE)
extern int my_feature_var;
void do_feature_stuff();
#else
inline void do_feature_stuff() {}
#endif
6、Marlin的构建过程
一个Marlin式的结构可能需要一段时间,但它像其他任何草图一样工作。Marlin中的所有 .cpp 文件及 其依赖项都将被编译,以及它们包含的任何内容。使用 PlatformIO 的Marlin构建将使用 ini 文件夹中 的文件以及buildroot/share/PlatformIO
中的脚本,根据您的配置过滤掉未使用的源文件,从而使 其更快。7、程序和命令流程
Marlin程序的执行从MarlinCore.cpp
开始,setup()
函数初始化,loop()
函数主循环,就像 Arduino 草 图一样。loop()
函数非常小,主要负责调用idle()
,然后在队列前面运行下一个G-code命令。Marlin中的大多数任务都是通过空闲函数执行的,该函数调用 manage_inactivity
和 thermalManager.manage_heater
。您将看到许多对空闲的调用,因为所有等待循环都使用它来保持 机器运行。如果Marlin太长时间没有呼叫空闲,看门狗就会被触发,为了安全重新启动机器。这个程序架构需要一些注意,因为我们不希望某个函数被idle本身调用,直到堆栈爆炸为止。在Marlin 只有少数的再入守卫,所以在实践中它工作得很好。从空闲状态跟踪函数调用,可以很直观地看到Marlin是如何使所有设备和特性在程序上下文中运行的。一些特性一直在进行活动,但是Marlin所做的大部分工作都是由G-code命令发起的。8、中断服务例程
Marlin定义了一些中断服务例程( ISRs ):- 步进 ISR 反复运行,通过向步进电机的STEP和DIR引脚发送脉冲以高速移动规划队列和步进电机。这种中断的频率与移动速度有关。
- 温度 ISR 以接近 1KHz 的频率读取温度传感器,并在读数准备好时向主程序发送信号。它还管理不 需要非常高基频的加热器和风扇配置的软件/慢 PWM 。
- 终止 ISR 可以被激活,如果终止引脚是中断能力的。它只在结束引脚的输入状态改变时触发。
- Tone Timer由 Arduino 为某些平台定义,由Marlin为其他平台定义。它处理脉冲压电蜂鸣器来创 建音调,它运行的频率是当前音调的两倍。
- 伺服定时器提供了用于伺服系统的 PWM 信号。
9、G代码的处理
接下来,让我们看看如何处理 G 代码并遵循程序流程。- 0.紧急解析器
- 1.从Serial和SD读取
manage_inactivity
函数调用queue.get_available_commands()
,该函数检查即时缓冲区,查询串行 端口,并读取活动的SD打印文件,将行复制到命令队列中,目的是使其充满。- 2.弹出G代码
queue.advance()
获取队列前面的命令并立即运行它。在命令完成之前,Marlin不会返回到loop()
。注意```queue.advance() ````在队列之前运行内部命令,因此Marlin可以命令自身在常规命令流中 执行一些操作。- 3.预扫描G代码
queue.advance()
选择了下一个命令,它就调用解析器对G-code行进行预处理。预处理程序验证行号和校验和,如果一切正常,它会在调用特定的G-code处理程序之前对参数进行快速预扫描。- 4.处理G代码
GcodeSuite::G28()
),尽管有一些在其他地方实现。G-code处理程序是一个简单的没有返回值的void方法。它不是从函数调用中获取参数,而是查询 GCodeParser 类来检查参数并读取它们的值。例如,处理程序使用parser.seen('X')
来检查'X'参数是否存在,然后调用parser.value_float()
以浮点数的形式获取其数值。请参阅gcode/parser.h
获取所有可用的方法。G-code处理程序几乎可以做任何事情,所以它们被分成单独的文件,每个文件只包含它需要的头文件。所有处理程序必须包括gcode.h
,这将包括parser.h
。- 5.命令阻塞
往期精彩
步进电机驱动在3D打印应用的学习笔记(一)光固化3D打印悬空和支撑讲解3D打印过程与最近的学习成果
两个最常用的3D打印机切片软件
3D打印机marlin固件框架与GCode命令总结
3D打印机Marlin固件串口功能解析和程序移植
让野火F103开发板支持Marlin2.0固件是什么体验?3D打印主控板成员 1
C语言映射表在嵌入式串口解析、UI设计中的应用(值得收藏并实践的精华帖)
觉得本次分享的文章对您有帮助,随手点
[在看]
并转发分享,也是对我的支持。