2. 理解Marlin固件目录结构与主循环

任务说明

本案例的目标是:在Marlin固件源码中,理清顶层目录的组织方式,准确找到主循环所在文件,并理解一次主循环从准备到执行的完整调用路径。完成本案例后,你应能独立回答“Marlin启动后,CPU反复执行的循环在哪里、每轮循环做了什么”这一问题。

目录结构定位

Marlin 2.x 的源码根目录下,核心代码集中在 Marlin/ 子目录中。以下是与主循环直接相关的关键路径:

  • Marlin/src/ — 全部固件源码
  • Marlin/src/MarlinCore.cpp — 包含 main() 函数与 setup()、loop() 的顶层控制
  • Marlin/src/MarlinCore.h — 相关函数声明与全局状态
  • Marlin/src/module/ — 运动控制、温度、步进电机等核心模块
  • Marlin/src/gcode/ — G代码解析与执行器
  • Marlin/src/HAL/ — 硬件抽象层,不同平台(AVR、STM32、ESP32等)的底层实现

主循环文件与运行流程

主循环并不在单独的 loop() 文件中,而是由 MarlinCore.cpp 中的 main() 函数启动后,调用 Arduino 框架的 setup() 和 loop()。其中 loop() 函数内部调用 MarlinIdle(),这是每轮主循环的核心入口。真正的“每轮循环做什么”由 MarlinIdle() 内部分发到各模块的 idle() 回调中。

一次主循环的典型执行顺序如下:

  1. loop() 被调用(由 HAL 层定时触发);
  2. 进入 MarlinIdle(),首先调用 manage_heaters() 处理温度控制;
  3. 调用 manage_inactivity() 检查超时与急停状态;
  4. 调用各模块的 idle()(如步进电机、LCD、看门狗等);
  5. 检查是否有新的G代码命令在缓冲区中,若有则调用 GCodeParser 解析并执行;
  6. 循环结束,等待下一轮 loop() 触发。
Marlin 主循环调用路径 MarlinCore.cpp main() → setup() loop() 每轮调用 MarlinIdle() module/temperature.cpp manage_heaters() module/planner.cpp manage_inactivity() module/stepper.cpp stepper.idle() gcode/parser.cpp GCodeParser::parse() 虚线框内为每轮主循环实际执行的关键模块

关键代码定位

在 MarlinCore.cpp 中,主循环的核心实现如下(已省略非关键代码):

// MarlinCore.cpp 中的主循环入口
void loop() {
  // 每轮主循环都从这里开始
  MarlinIdle();
}

void MarlinIdle() {
  // 1. 温度管理
  thermalManager.manage_heaters();
  
  // 2. 空闲超时与急停检测
  planner.manage_inactivity();
  
  // 3. 各模块的周期性任务
  stepper.idle();
  lcd.idle();
  
  // 4. 处理G代码命令队列
  parser.parse();
}

验证方法

要验证上述理解是否正确,请按以下步骤操作:

  1. 在本地克隆Marlin仓库:git clone https://github.com/MarlinFirmware/Marlin.git
  2. 打开 Marlin/src/MarlinCore.cpp,搜索 void loop() 和 void MarlinIdle(),确认函数存在。
  3. 在 MarlinIdle() 函数体内,检查是否调用了 thermalManager.manage_heaters()、planner.manage_inactivity()、stepper.idle() 等函数。
  4. 在 Marlin/src/module/stepper.cpp 中搜索 void Stepper::idle(),确认其存在。

判定标准:若上述函数均能在对应文件中找到,且 MarlinIdle() 中的调用顺序与正文描述一致,则说明已正确定位主循环并理解其运行流程。