2.Klipper开发篇:Klipper上位机源码分析

        从Klipper执行流程和指令传输路线理清Klipper上位机整个源码架构和运行逻辑,理解klipper是如何启动运行的?指令是从哪里获取,去哪里执行?

1.klipper服务启动

        Klipper启动最核心的服务是klipper.service,安装好klipper后,在/etc/systemd/system下有该服务的文件,该服务文件的[Service]段内容是(注意,下面~代表linux用户目录,实际样式不是这样):

[Service]
Type=simple
User=tronxy
RemainAfterExit=yes
WorkingDirectory=~/klipper
EnvironmentFile=~/printer_data/systemd/klipper.env
ExecStart=~/klippy-env/bin/python $KLIPPER_ARGS
Restart=always
RestartSec=10

        可见它实际上是执行~/printer_data/systemd/klipper.env中的命令,该命令是:

~/klipper/klippy/klippy.py /home/tronxy/printer_data/config/printer.cfg -I ~/printer_data/comms/klippy.serial -l ~/printer_data/logs/klippy.log -a ~/printer_data/comms/klippy.sock

        命令涉及到klippy.py文件,它是上位机主进程文件,printer.cfg是打印配置文件

命令行直接跟在klippy.py后面的是配置文件printer.cfg;

-I(大写i)指定虚拟终端,在klippy.py文件中解析时,默认为/tmp/printer,这里指~/printer_data/comms/klippy.serial ,可以直接向它发命令:

        echo "M114" >> ~/printer_data/comms/klippy.serial

        但只能通过浏览器或者KlipperScreen的控制台看到回显

-l(小写L)指定日志文件klippy.log

-a 指定API服务器的socket文件,这里指向~/printer_data/comms/klippy.sock

        它是一个Unix Socket用以和其它应用(如Moonraker)通信用的,可以编写一个Unix Socket客户端,向它向发送指令。

        klippy.py文件中的main函数会解析上面的命令行,读取printer.cfg配置文件,完成打印机各模块实例化,然后启动下位机串口通讯,等待响应操作请求。

2. 整体框架

        服务启动后,运行到klippy.py的main函数,执行流程如下:

        main()解析命令行 -> 命令行参数打包到start_args中 -> 创建反应器reactor -> 构造Printer对象 -> 执行Printer.run进入无限循环,main函数核心代码如下:

while 1:
    ...
    main_reactor = reactor.Reactor(gc_checking=True)
    printer = Printer(main_reactor, bglogger, start_args)
    res = printer.run()
    ...
        2.1 构造反应器reactor

        reactor是驱动整个klipper上位机运行的核心对象,它在reactor.py中被构造,这里有两种可能的reactor,源代码如下:

# Use the poll based reactor if it is available
try:
    select.poll
    Reactor = PollReactor
except:
    Reactor = SelectReactor

        它可能是PollReactor类构造的,也可能是SelectReactor构造的,区别就是PollReactor采用poll库,SelectReactor采用select库,poll是select进化版。

        无论是poll还是select,它们都监控sockets,open files, 或者 pipes(所有带fileno()方法的文件句柄)何时变成readable 和writeable, 或者通信错误,并返回文件句柄和事件(fd,event)。通常用它来监控多个sockets或pipes或文件是否有可读的信息然后处理这些信息,poll对IO文件不限制数量,select限制数量,较新版本的python都使用PollReactor。

        2.2 反应器reactor的核心功能

        printer对象运行run函数进入无限循环,最终是调用Reactor._dispatch_loop函数,也就是这个函数完成了整个klipper运行的循环。该函数的while循环体里,主要执行两大核心功能:

        _check_timers:检查对应函数register_callback注册到reactor中的延时执行函数时间是否到了,哪个函数时间到了就执行它。相当于并行的执行注册进来的回调函数,主要负责klipper的extra下各模块的回调。

        _poll.poll(waittime):通过poll库机制检查对应函数register_fd注册到reactor中的文件句柄是否可读或可写,然后调用对应的读取或写出回调函数。主要负责打印文件的读写,虚拟终端文件的读写,websocket的读写(与moonraker通信)等。

        核心代码如下:

# Main loop 主循环
    def _dispatch_loop(self):
        ...
        while self._process:
            timeout = self._check_timers(eventtime, busy)
            ...
            res = self._poll.poll(int(math.ceil(timeout * 1000.)))
            ...
            for fd, event in res:
                ...
                if event & (select.POLLIN | select.POLLHUP):
                    self._fds[fd].read_callback(eventtime)
                    ...
                if event & select.POLLOUT:
                    self._fds[fd].write_callback(eventtime)
                    ...
        2.3 klipper整体如何运行

        klipper根据配置文件,启用指定的模块,大部分模块都是在extra文件夹下,如printer.cfg中如果配置了[verify_heater],则extra下verify_heater.py文件就被启用。

        具体实现:一开始构造Printer对象时,注册了一个函数_connect到reactor中,待执行reactor核心循环后,回调_connect函数,该函数一开始就_read_config()->load_object()根据config.getsection加载对应的模块。

        每个被启用的模块都会构造相应的对象,同时注册回调函数到reactor中,这样就可以在执行Printer.run后被调用,进而各个模块都被连起来执行了。

3. 指令源及指令执行流程

        klipper有三大指令源,分别是虚拟终端,网络指令,打印文件。

        3.1 虚拟终端

        klipper默认虚拟终端(串口)是/tmp/printer,实际运行时,使用-I(大写的i)指定这个文件,klipper.service指定为~/printer_data/comms/klippy.serial。

        该虚拟终端文件在klippy.py的main函数中被打开,文件句柄保存在start_args['gcode_fd']中,传给Printer对象。

        Printer构造时,通过add_early_printer_objects函数加载了gcode.py中的GCodeDispatch类对象,保存在printer的objects中,名为gcode;同时也加载了GCodeIO类对象,保存在printer的objects中,名为gcode_io。

        加载GCodeIO过程中注册'gcode_fd'文件句柄到反应器reactor,回调函数_process_data处理虚拟文件读取的数据,并解析出命令调用gcode._process_commands函数执行指令,核心代码如下:

# Support reading gcode from a pseudo-tty interface
class GCodeIO:
    def __init__(self, printer):
        self.printer = printer
        ...
        self.fd = printer.get_start_args().get("gcode_fd")
        self.reactor = printer.get_reactor()
        ...
        if self.is_fileinput and self.fd_handle is None:
            self.fd_handle = self.reactor.register_fd(self.fd,
                                                      self._process_data)
        3.2 网络指令

        网络指令一般由显示屏/网页发出,指令传输流程如下:

        网页/显示屏指令->moonraker->printer_data/comms/klippy.sock->klipper/webhooks.py。

        webhooks.py中的WebHooks对象也是add_early_printer_objects加载到Printer中去的,加载过程中创建socket以及注册回调函数流程如下:
        ServerSocket类 -> 创建klippy.sock(路径由命令行-a指定)-> 注册register_fd函数_handle_accept到反应器reactor -> 函数_handle_accept监听到连接 -> 创建ClientConnection对象。
        ClientConnection类->注册socket文件句柄:process_received(接收)和_do_send(发送)到reactor中 -> process_received函数接收moonraker传进的指令 -> 解析指令,获取指令对应在WebHooks类中注册的回调函数 -> 执行函数 -> do_send反馈结果到moonraker。
        注意:WebHooks类在很多模块中都有一些注册它的函数,因此moonraker会因启用的配置不一样而可以被传进来的指令也不一样
        Gcode指令(method:gcode/script)->被GCodeHelper类注册到Webhooks中->执行_handle_script->调用gcode.py中GCodeDispatch类成员run_script->实际执行_process_commands函数。

class WebHooks:
    def __init__(self, printer):
        self.printer = printer
        ...
        self.sconn = ServerSocket(self, printer)

class ServerSocket:
    def __init__(self, webhooks, printer):
        self.printer = printer
        self.webhooks = webhooks
        self.reactor = printer.get_reactor()
        ...
        #创建socket
        self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
        self.sock.setblocking(0)
        self.sock.bind(server_address)
        self.sock.listen(1)
        #注册到reactor
        self.fd_handle = self.reactor.register_fd(
            self.sock.fileno(), self._handle_accept)
        ...
        3.3 打印文件

        printer.cfg配置[virtual_sdcard]启用模块klippy/extras/virtual_sdcard.py

        开始打印一个文件的流程如下:

        moonraker发送命令SDCARD_PRINT_FILE -> 执行virtual_sdcard.py的VirtualSD类成员cmd_SDCARD_PRINT_FILE -> 执行_load_file函数 -> 通过os.path定位到文件,io.open打开文件 -> 然后调用do_resume启动打印 -> 注册work_handler到reactor中 -> 不停的读取文件内容 -> 调用gcode.py中GCodeDispatch类成员run_script执行指令 -> 实际执行_process_commands函数。

4.指令执行过程

        4.1 指令传递过程

        GCodeDispatch类成员run_script -> 实际执行_process_commands函数 -> 解析命令(踢出;注释行,行号N,提取命令号gcode_handler,匹配已注册的命令,即通过函数register_command注册在成员变量gcode_handlers上的命令)-> 执行其回调函数。

handler = self.gcode_handlers.get(cmd, self.cmd_default)

handler(gcmd)

        4.2 G1指令

        G1是几乎所有移动指令(G2,G3孤形指令也会被转化为G1执行)的基本指令,打印文件中95%以上的指令都是G1指令,因此,了解G1指令如何执行,几乎可以全部了解整个运动指令的执行过程。

        G1指令在gcode_move.py中被注册的回调函数为cmd_G1,该函数解析命令传入的参数,获取移动的终点和速度,然后将参数传给ToolHead.move(),该函数会根据参数创建一个Move对象,然后将它加到(add_move)移动队列中,等待被执行。后续怎么执行一条移动指令,在另一篇文章中分析。

到此,整个klipper上位机核心功能分析完了,klipper的其它模块功能(比如温度控制,调平,复位等)可以到extra目录下找到相应的py模块文件,分析它的实现过程。

<think>我们正在处理用户的问题:在尝试使用3.3V串口(即通过UART)为MKS Monster8 V2刷写Klipper固件时,出现了`make: *** [src/stm32/Makefile:107: flash] Error 255`错误。根据引用内容,我们知道MKS Monster8 V2主板基于STM32F407芯片,并且之前有用户遇到Bootloader损坏的问题(引用[1])。此外,引用[3]提到了该主板支持Klipper固件。 错误分析: `Error 255`通常表示命令执行失败,在刷写过程中可能由多种原因引起,例如: 1. 串口连接不正确(如TX、RX接反,或者没有共地)。 2. 没有正确进入串口刷写模式(可能需要通过BOOT0跳线进入)。 3. 权限问题(Linux系统下串口设备需要用户权限)。 4. 刷写工具(如stm32flash)未安装或版本不兼容。 5. 编译的固件文件不存在或路径错误。 解决方案: 步骤1:检查硬件连接 - 确保使用3.3V电平的USB转TTL模块,连接方式如下: - TTL模块的TX 接 主板的RX(可能是PA10) - TTL模块的RX 接 主板的TX(可能是PA9) - TTL模块的GND 接 主板的GND - 注意:在连接时,主板不要通电。 步骤2:进入串口刷写模式 - 根据STM32F4的启动模式,需要将BOOT0设置为高电平(即接3.3V),BOOT1设置为低电平(接GND)。在MKS Monster8 V2主板上,通常有标注BOOT0的跳线帽,将其短接到高电平位置(或者用跳线帽短接BOOT0和3.3V引脚)。 - 然后给主板上电,此时主板进入系统存储器启动模式,可以通过串口刷写。 步骤3:安装刷写工具 - 在Klipper上位机(树莓派或Linux主机)上,确保安装了`stm32flash`工具: ```bash sudo apt update sudo apt install stm32flash ``` 步骤4:检查串口设备权限 - 将USB转TTL模块插入上位机,使用`ls /dev/ttyUSB*`或`ls /dev/ttyAMA*`等命令查看设备名,通常是`/dev/ttyUSB0`。 - 确保当前用户有权限访问该设备,可以将用户加入`dialout`组: ```bash sudo usermod -a -G dialout $USER ``` 然后注销重新登录。 步骤5:编译固件 - 在Klipper源码目录下,执行: ```bash make menuconfig ``` 配置如下: - Micro-controller Architecture: `STMicroelectronics STM32` - Processor model: `STM32F407` - Bootloader offset: `32KiB bootloader` (这是关键,因为MKS Monster8 V2的Bootloader大小为32KB,引用[1]中提到重刷Bootloader,所以这里必须选对) - Clock Reference: `8 MHz crystal` (不要选内部时钟) - Communication interface: 根据你的连接方式,如果是通过串口刷写,这里选择`USART3`(因为MKS Monster8 V2的串口3通常被用于刷写,但具体请参考主板手册,如果没有明确,可以尝试USART1或USART3,同时注意主板上标注的串口引脚) - 注意:在刷写时,我们使用串口,所以这里配置的通信接口就是刷写后Klipper使用的串口,但刷写本身是通过系统存储器自带的串口加载程序进行的,所以这里配置的串口应该和硬件连接一致。 - 保存配置,然后编译: ```bash make clean make ``` 编译完成后,会生成`klipper.bin`文件(位于`out/klipper.bin`)。 步骤6:刷写固件 - 使用stm32flash工具刷写,命令如下: ```bash stm32flash -w out/klipper.bin -v -g 0 /dev/ttyUSB0 ``` 其中: - `-w out/klipper.bin`:指定要写入的固件 - `-v`:校验写入内容 - `-g 0`:刷写完成后从地址0开始运行(即跳转到应用程序) - `/dev/ttyUSB0`:你的串口设备 - 如果出现权限问题,可以尝试使用`sudo`执行,但更建议解决权限问题。 步骤7:恢复启动模式 - 刷写完成后,断电,将BOOT0跳线帽恢复为低电平(即短接到GND或移除跳线帽),然后重新上电。 步骤8:连接Klipper - 重新上电后,Klipper固件应该运行在应用程序区,此时通过之前配置的串口与上位机通信。在`printer.cfg`中配置正确的串口路径,例如: ```ini [mcu] serial: /dev/serial/by-id/usb-Klipper_stm32f407xx_XXXXXX ``` 常见问题及解决: - 如果刷写过程中出现超时或通信错误,检查接线是否正确(特别是TX和RX是否交叉连接),以及BOOT0是否确实设置为高电平。 - 如果出现`stm32flash: Failed to init device.`,可能是串口设备选择错误,或者没有进入系统存储器启动模式。 - 如果刷写成功但无法连接,请检查Bootloader偏移量是否正确(必须为32KiB),以及通信接口配置是否和实际使用的串口一致(引用[1]中提到的Bootloader损坏问题,如果偏移量不对,可能需要重刷Bootloader)。 特殊情况: 如果按照以上步骤仍然无法刷写,可能需要使用DFU模式(USB口)或者ST-Link来刷写。引用[1]中提到重刷Bootloader,我们可以使用ST-Link来刷写官方的Bootloader(参考之前的回答),然后再尝试串口刷写。 重刷Bootloader步骤(使用ST-Link): 1. 准备ST-Link V2烧录器,连接SWD接口(SWDIO->PA13, SWCLK->PA14, GND->GND, 3.3V->3.3V)。 2. 使用STM32CubeProgrammer或OpenOCD擦除芯片并烧录Bootloader(地址0x8000000)。 3. 烧录完成后,再尝试通过串口刷写Klipper固件(此时Bootloader是好的,可以通过Bootloader的串口更新固件)。 注意:MKS Monster8 V2的Bootloader大小是32KB,所以Klipper配置中的偏移量必须选择32KiB bootloader。 最后,如果问题仍然存在,建议检查硬件(如晶振是否起振,电源是否稳定)以及固件配置(时钟源是否正确)。 希望这些步骤能帮助你解决问题。 相关问题: 1. 如何为 MKS Monster8 V2 使用 ST-Link 烧录 Bootloader? 2. 如何配置 Klipper 使用 MKS Monster8 V2 的 CAN 总线? 3. MKS Monster8 V2Klipper 中如何配置多个挤出机?</think>### 解决 MKS Monster8 V2 刷写 Klipper 固件报错 `make: *** [src/stm32/Makefile:107: flash] Error 255` #### 错误原因分析 该错误通常由以下原因导致: 1. **DFU 模式未正确进入**(未检测到 `0483:df11` 设备) 2. **权限问题**(Linux 系统 USB 设备访问受限) 3. **固件配置错误**(Bootloader 偏移量或时钟源设置错误) 4. **Bootloader 损坏**(需重刷 STM32F407 Bootloader)[^1] --- ### 解决方案 #### 步骤 1:强制进入 DFU 模式 1. **物理操作**: - 断开主板电源 - 用镊子短接主板 **BOOT0 触点**(标记为 `B0`) - 保持短接状态通电 → 等待 3 秒 → 移除短接 - 执行命令验证: ```bash lsusb | grep 0483:df11 # 应显示 "STMicroelectronics STM Device in DFU Mode" ``` #### 步骤 2:解决权限问题 ```bash # 添加 udev 规则 echo 'SUBSYSTEM=="usb", ATTR{idVendor}=="0483", MODE="0666"' | sudo tee /etc/udev/rules.d/99-stm32-dfu.rules sudo udevadm control --reload-rules sudo service klipper stop # 停止占用服务 ``` #### 步骤 3:验证固件配置 ```bash cd ~/klipper make menuconfig ``` 确保配置如下[^1][^3]: ```ini Processor model = STM32F407 Bootloader offset = 32KiB bootloader # V2 必须选此项! Clock Reference = 8 MHz crystal # 勿选内部时钟 Communication interface = USB (或实际使用的接口) ``` #### 步骤 4:重刷固件 ```bash make clean make sudo dfu-util -a 0 -d 0483:df11 -D out/klipper.bin --dfuse-address 0x08008000:leave ``` **成功标志**: ``` dfu-util: Device has DFU interface... dfu-util: Done! ``` #### 步骤 5:重刷 Bootloader(终极方案) 若仍报错,需用 **ST-Link 重刷 Bootloader**: 1. **接线**: ``` ST-Link Monster8 V2 SWDIO → PA13 SWCLK → PA14 GND → GND 3.3V → 3.3V (可选) ``` 2. 使用 [STM32CubeProgrammer](https://www.st.com/en/development-tools/stm32cubeprog.html): - 擦除全芯片 - 烧录官方 Bootloader 文件 `stm32f4x_0x8000000.bin`(地址 `0x8000000`)[^1] --- ### 配置参考(`printer.cfg`) ```ini [mcu] serial: /dev/serial/by-id/usb-Klipper_stm32f407xx_XXXXXX restart_method: command [temperature_sensor mcu] sensor_type: temperature_mcu min_temp: 0 max_temp: 100 ``` > **注**:刷写成功后,断电重启主板,在 `ls /dev/serial/by-id/` 中查找新设备 ID[^3] --- ### 预防措施 1. **优先尝试 TF 卡刷**: - 文件名必须为 **`mks_monster8.bin`**(全小写) - 使用 ≤32GB 的 **FAT32 格式** TF 卡 2. **环境检查**: ```bash sudo apt install dfu-util libusb-1.0-0-dev gcc-arm-none-eabi ``` > **关键提示**:V2 与 V1 的 **Bootloader 偏移量不同**(V2 必须选 `32KiB bootloader`)[^1][^3] ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值