16

开源Marlin2.x源代码架构学习笔记

 3 years ago
source link: https://blog.csdn.net/morixinguan/article/details/117433549
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

点击上方「嵌入式云IOT技术圈」,选择「置顶公众号」第一时间查看嵌入式笔记!

最近两个月MBA美帝在职研的课程即将结束,经过个人的努力,目前两门课:全球商务、定量决策均分绩点如下,基本上逼近满分(凡尔赛了,哈哈哈),光鲜亮丽的背后,带领小组始终冲在了班级的最前面,同时也付出了不少精力和汗水,通宵了不知道多少个夜晚,交完学期项目以后就结课啦,开始进入下一门课的学习!所以开始有时间可以折腾新东西啦:(不少同学应该知道我开了个知识星球,专门分享这些内容,这里就不好放出来啦,有兴趣和我说即可)

931b91d14099238d2c4e138c44611a6e.png

c125b3f2ad2b0694803f58041c596b8a.png

Marlin相当庞大,自2.x版本开始,陆陆续续开始集成了32位机,主流的STM32、LPC等高性能MCU逐渐取代了以Arduino2560等8位机的3D打印机方案。Marlin,庞大到让每个初学者开始学习看代码都会觉得闻风丧胆,当然我刚开始接触的时候也不例外,但是不管是学什么东西,只要把大方向掌握了,那么细节的东西用到了再慢慢研究也不迟,同时,Marlin的文档非常少,对于初学者来说,是非常不友好的!以下这些介绍也是Marlin刚开放不久的说明文档,笔者英语水平有限,如有翻译失误,尽情纠正与谅解!

1、Marlin代码工程架构

以下是原版的Marlin固件解压下来的显示情况:

81822647286156de279a80f11e855037.png

当使用 PlatformIO 构建Marlin时,它会在.pio此处创建一个文件夹,并且在shell中使用git的时候,这 是当前的工作目录,如下图所示:

c27d3042227cb6b6bb8973a3c6f3996d.png

1.2、 buildroot 文件夹

该目录包含开发人员的帮助脚本、字体工具、CI测试工具、Marlin图标和其它数据

1.3、 buildroot/share/PlatformIO 文件夹

电路板定义、环境变量、链接脚本和构建脚本会存放在这个地方,这个文件夹内的构建环境会经常引用 ini文件夹

1.4、 ini 文件夹

包含按 MCU 类型组织的所有 PlatformIO 环境设置, platformio.ini 文件将这些引入为 PlatformIO 提供构建/上传/调试设置,有一个文件特别有用:即是Marlin自定义构建脚本 features.ini 在构建开 始时用于根据启用的功能过滤源文件,因此构建能够完成得更快。

5318965ab3af9fa442c3568f83b5dc89.png

1.5、 Marlin 文件夹

构建Marlin时,配置文件就放在这个位置:

e091ec21971745f6e378bc5af802ec73.png

1.6、 Marlin/src 文件夹

包含Marlin应用程序,Marlin基于 Arduino 框架进行开发,所以它含有 Arduino 框架的setup() 和 loop() 功能。

2、Marlin应用源代码Marlin/src 文件夹

9a7f5d81849d3c8c8ea6501f211cc9fc.png

Marlin应用源代码主要由以上文件夹组成,核心的入口程序文件是 MarlinCore.cpp ,MarlinCore.h 会做一些宏和变量以及函数的声明,在这个文件中可以找到基于 Arduino 框架的 setup() 和 loop() 函 数,其余的文件夹包含的源码主要的作用如下:

  • core文件夹

cb6c60005d569a00f9530be57144a1cd.png

包含Marlin源文件需要的类型、宏、使用程序功能等等,大多数源文件只需要包含 inc/MarlinConfig.h 就可以确保这些文件按预期顺序包含在内。

  • feature文件夹

c2a061405c20c6a9381ca0d76626d6b7.png

包含一些可选功能的支持代码,当然这个文件夹下的代码有些功能是非常简单的,只需要添加一个 G代码或者在公共代码中插入更改即可完成。如果一个特性需要定义一个类或者一组函数,这些附 加的文件将放在这个位置。

  • gcode 文件夹

0e3b66a80184e3f54c5dd0c5d9bfd531.png

包含GCodeParser 类的定义以及所有G代码命令的实现(有一些不在这里实现,但大部分是的),这 些功能都被包装在一个名为 GcodeSuite 的类里,G代码实现的文件被捆绑在多个类别的子文件夹 中,这些文件被命令为具体的G代码,因此可以使用 IDE 的查找功能找到它们。

  • HAL文件夹

5aeedad5e60c1914621ebe72ba300a65.png

每个控制器系列都提供控制硬件的功能,但并非所有控制器系列都使用相同的接口, Marlin2.x 版本实现了硬件抽象层,更好的屏蔽了平台的差异性,使其它的平台更好的兼容 Arduino 框架。

  • inc文件夹

0c74de060676b1867ae56f428d4944f4.png

包含了Marlin版本、配置条件等等的基本内容,请注意,每个HAL还包含自己的 Conditionals*.h 和 SanityCheck.h文件 。

  • lcd 文件夹

ebd1c5bc3df6b87e7335543de155664c.png

所有与 LCDTFTOLED、编码器、按钮和串行控制器的相关代码都放在这里,语言翻译通常仅 适用于外部控制器,因此语言翻译也放在这里。

  • libs文件夹

e53a52bd563ab9f5308e396d0ad227cc.png

任何通用数据函数实现或者与硬件库代码都放在这里,如蜂鸣器代码、 CRC16 校验和实现、 3X3 矩阵、数字到字符串的转换功能、用于二进制传输的 HeatShrink ,甚至还有几个 EEPROM 。

  • module文件夹

23f72e3dce33261cb76fa432829811fb.png

这里定义了机器的所有典型功能,包括 3D 打印机拥有的所有的组件,例如:加热器和传感器、热 床探测、路径规划算法、将命令转换为分段运动的高级运动功能、将毫米分段快速转换为步进块的 运动路径规划,以及将块段转换为中断时序和STEP信号的步进器 ISR 。

  • pins文件夹

1f69540e9fb0da51bc59f6f8b30e6b8d.png

Marlin所有的板定义都在这个文件夹中,这里有不同的硬件架构,每个架构下的每块板都有自己独 特的引脚文件, pins.h 根据 MOTHERBOARD 设置进行包含,由于 pins.h 是 MarlinConfig.h 的包 含文件注意,因此它不会包含在其它的地方。

2cee3c858935a00683b564a3778f10e9.png

在这里您可以找到所有实现实际文件和文件夹的高级文件系统代码。 CardReader 类是Marlin用 于导航目录、打开G-code文件和从SD卡(或其他媒体)打印的主界面。自从Marlin 2.0.8以来,所有 的媒体类型都派生自 DiskIODriver 抽象类。

3、Marlin的配置

马林是高度可配置的。您将在源代码的许多地方发现应用配置选项来打开和关闭代码、更改行为和提供 值。

  • Marlin源文件如何获得它需要的所有配置值?

首先,当您构建一个c++程序时,第一阶段是分别构建所有的. C和 .cpp 文件。这些文件中的每一个都 必须是完整的,并且每个文件都负责包含它需要的头文件。在大型项目中,这可能会很乏味,所以很多 编译器都允许您创建“预编译头文件”( .PCH 文件),包括所有公共头文件。Marlin不使用 PCH 文件,而是 使用以类似方式工作的常规头文件。

在inc、core、HAL和pins文件夹中头文件的包含顺序很重要,因为每个头文件都是建立在其前身文件之 上的。为了确保始终遵循正确的包含顺序,任何需要配置和条件文件(直到conditionals_ad.h)的代码都 必须包含 inc/ marlinconfigpreh ,而任何需要完全实现的硬件配置的代码都必须包含 inc/MarlinConfig.h

让我们仔细看看每个文件包含的头文件。

3.1、 MarlinConfigPre.h

556d1c7eff7a318855ad54d6e018f43a.png

3.2、 MarlinConfig.h

158eb3f330885283d967757e9c3aa850.png

4、一个典型的源文件

将这些内容放在一起,典型的源文件至少将包含 marlinconfigpreh ,以便它可以预先检查一些配置 值。只有在需要时,才会包含其他头文件。有些源文件包含一些特性的头文件是很常见的。

5、典型的头文件

Marlin头文件不会像源文件一样使用 #if…#endif 来包装。相反,如果不需要头文件,那么它就不会被 包含。Marlin还避免使用c风格的 #ifdef 包装器,并且只在自己的头文件上使用 #pragma 一次。

当该特性被禁用时,一些头文件将提供空函数。这使得在单个点关闭东西更容易,并且可以使其他地方 的代码更整洁。

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 信号。

平台还将为Serial UART和其他设备定义中断,所以Marlin必须小心选择它使用的中断和计时器。

9、G代码的处理

接下来,让我们看看如何处理 G 代码并遵循程序流程。

  • 0.紧急解析器

当命令队列被阻塞,但是机器需要用户以某种形式输入时,您可以做什么?紧急解析器是一个简单的状态机,它在串行代码的低级别上运行,监视某些命令。当它看到 M108、M112 等时,它立即采取行动来处理代码。

  • 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代码

所有的G-code处理程序都封装在 GcodeSuite 类中(例如, GcodeSuite::G28() ),尽管有一些在其他地方实现。G-code处理程序是一个简单的没有返回值的void方法。它不是从函数调用中获取参数,而是查询 GCodeParser 类来检查参数并读取它们的值。例如,处理程序使用parser.seen('X')来检查'X'参数是否存在,然后调用parser.value_float()以浮点数的形式获取其数值。请参阅gcode/parser.h获取所有可用的方法。

G-code处理程序几乎可以做任何事情,所以它们被分成单独的文件,每个文件只包含它需要的头文件。所有处理程序必须包括gcode.h,这将包括parser.h

  • 5.命令阻塞

当一个 G1 命令的移动队列被加入时,它就被认为是完成的,所以它可以立即返回。在实践中,特别是 在床水平启用时,每一次线性移动都有可能填满规划队列,并阻塞长时间等待空间打开。

当一个命令需要等待计划器或用户反馈中的空闲空间等内容时,它将调用空闲函数以保持机器的活动和 运行。idle函数甚至会将传入的命令读取到队列中,但是由于idle函数并不会分发g代码或推进队列,所 以在处理程序结束并返回之前,队列不能获得任何空的命令。

步进电机驱动在3D打印应用的学习笔记(一)

光固化3D打印悬空和支撑讲解

3D打印过程与最近的学习成果

两个最常用的3D打印机切片软件

3D打印机marlin固件框架与GCode命令总结

3D打印机Marlin固件串口功能解析和程序移植

让野火F103开发板支持Marlin2.0固件是什么体验?3D打印主控板成员+1

C语言映射表在嵌入式串口解析、UI设计中的应用(值得收藏并实践的精华帖)

觉得本次分享的文章对您有帮助,随手点[在看]并转发分享,也是对我的支持。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK