ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Marlin LINUX HAL 深度指南:无 MCU 的原生主机构建、仿真运行与调试实践

Marlin LINUX HAL 深度指南:无 MCU 的原生主机构建、仿真运行与调试实践 Marlin LINUX HAL 深度指南无 MCU 的原生主机构建、仿真运行与调试实践【免费下载链接】MarlinMarlin is a firmware for RepRap 3D printers optimized for both 8 and 32 bit microcontrollers. Marlin supports all common platforms. Many commercial 3D printers come with Marlin installed. Check with your vendor if you need source code for your specific machine.项目地址: https://gitcode.com/GitHub_Trending/ma/MarlinMarlin 固件不仅面向 AVR/STM32 等真实主控板还在仓库中提供了一套无硬件依赖的 LINUX HALMarlin/src/HAL/LINUX/让整个固件以普通 C 控制台程序的形式在宿主机 CPU 上直接编译、运行与测试。本文以该目录下的维护者工作笔记AGENTS.md为核心骨架结合main.cpp、Gpio、eeprom、serial.h、ini/native.ini等源码与构建配置系统讲解 LINUX HAL 的构建测试流程、原生平台集成方式、十个源码里看不出来的关键实现细节以及编写该 HAL 代码时必须遵守的约定。读完本文你将能够独立完成 LINUX HAL 的编译验证、理解其仿真机制并能在修改 HAL 代码时避免踩坑。一、LINUX HAL 是什么Marlin 的无板子运行形态LINUX HAL 是 Marlin 中一个特殊的硬件抽象层它不是为某块真实主控板服务而是把 Marlin 整体编译成一个运行在宿主机上的普通 C 程序。与嵌入式构建相比它有如下本质区别没有 MCU没有 Arduino 框架。整个构建使用宿主机的 C 编译器-stdgnu17链接librt/libpthread程序入口是main()而不是 Arduino 的setup()/loop()封装HAL 选择由宏__PLAT_LINUX__决定HAL/platforms.h 将__PLAT_LINUX__映射到src/HAL/LINUX/它没有ARDUINO_ARCH_*定义用途是开发调试与自动化测试在没有硬件的情况下跑通固件逻辑、跑 Unity 单元测试而不是驱动真实电机和传感器。需要特别区分的是ini/native.ini中的simulator_linux_*环境构建的是另一个HAL——带 SDL2/GLM 图形界面的 NATIVE_SIM HAL。LINUX HAL 是无头headless控制台构建NATIVE_SIM 是 GUI 模拟器两者不要混淆。二、构建与测试闭环mftest 与两个关键构建环境LINUX HAL 的验证入口是仓库自带的mftest脚本。它有一个重要的职责按测试目标重新生成Marlin/Configuration.h这是裸pio run -e无法可靠做到的事情。因此维护者约定统一用mftest验证cd $(git rev-parse --show-toplevel) buildroot/bin/mftest -t linux_native -n1 -y # host-native build, EEPROM enabled buildroot/bin/mftest -t linux_native_test -n1 -y # build Unity unit tests其中-t指定目标-n1限制并行任务数-y自动确认。mftest内部对以lin开头的目标会选用linux_native环境见 buildroot/bin/mftest 中的lin*) TESTENVlinux_native分支。2.1linux_native主冒烟目标该环境定义在 ini/native.ini[env:linux_native] platform native framework build_flags ${common.build_flags} -D__PLAT_LINUX__ -stdgnu17 -ggdb -g -lrt -lpthread -D__MARLIN_FIRMWARE__ -Wno-expansion-to-defined build_src_flags -Wall -IMarlin/src/HAL/LINUX/include build_unflags -Wall -fsingle-precision-constant lib_ldf_mode off build_src_filter ${common.default_src_filter} src/HAL/LINUX要点解读platform native、framework 空不用任何 Arduino 核心与工具链 SDK唯一的平台就是宿主机编译器-D__PLAT_LINUX__让#ifdef __PLAT_LINUX__保护的所有 HAL 源码参与编译build_src_flags里的-IMarlin/src/HAL/LINUX/include确保本地定制的Arduino.h、serial.h、pinmapping.h优先于任何系统头文件被找到-lrt -lpthread计时器/时钟与多线程所需链接库build_src_filter在公共过滤规则上追加src/HAL/LINUX其余 Marlin 代码正常编译。它对应的测试配置是 buildroot/tests/linux_native/config-01.ini[config:base] motherboard BOARD_SIMULATED temp_sensor_bed 1 pidtempbed on eeprom_settings on baud_rate_gcode on可以看到主板选BOARD_SIMULATED、开启 EEPROM 设置对应前文命令注释中的 EEPROM enabled。在提交任何 LINUX HAL 修改前此目标必须保持绿色。2.2linux_native_test构建 Unity 单元测试[env:linux_native_test] extends env:linux_native extra_scripts ${common.extra_scripts} post:buildroot/share/PlatformIO/scripts/collect-code-tests.py build_src_filter ${env:linux_native.build_src_filter} tests lib_deps throwtheswitch/Unity^2.6.0 test_build_src true build_flags ${env:linux_native.build_flags} -Werror -DNO_USER_FEEDBACK_WARNING该环境在linux_native基础上叠加了 PlatformIO 的 Unity 测试框架tests源码、-Werror严格告警并通过collect-code-tests.py在构建后动态收集测试目标。仓库内的单元测试包括 tests/core/test_macros.cpp、tests/core/test_types.cpp、tests/feature/test_runout.cpp、tests/gcode/test_gcode.cpp 等。当你改动到有单元测试覆盖的 HAL 代码时必须跑这个环境。2.3 强制重建的正确姿势维护笔记明确警告不要用rm -rf .pio/build/...强制重建——跨 profile 写保护会拦截该操作而且本身不安全。正确做法是pio run -e env -t clean # 只清除构建产物或者干脆让mftest自动重建。三、原生平台集成一个普通 C 程序的运行模型因为 LINUX HAL 是纯 C 程序它的运行时模型与嵌入式完全不同核心代码集中在 main.cpp整体包在#ifdef __PLAT_LINUX__与#ifndef UNIT_TEST中。3.1 三个线程串口读写与仿真循环main()依次启动int main() { std::thread write_serial (write_serial_thread); // TX 环形缓冲 → stdout std::thread read_serial (read_serial_thread); // stdin → RX 环形缓冲 ... Clock::setFrequency(F_CPU); Clock::setTimeMultiplier(1.0); // some testing at 10x HAL_timer_init(); std::thread simulation (simulation_loop); // 虚拟硬件反馈循环 DELAY_US(10000); setup(); for (;;) { loop(); std::this_thread::yield(); } }write_serial_thread把usb_serial.transmit_buffer中待发送的字节逐个fputc到stdout然后yield()read_serial_thread用fgets从stdin读取输入上限 254 字节配合usb_serial.receive_buffer.free()逐字节写入 RX 环形缓冲simulation_loop构造Heater热端/热床与LinearAxisX/Y/Z/E 轴对象周期性调用其update()在软件层面更新引脚状态——这是 LINUX HAL 唯一的硬件反馈回路。因此G-code 必须通过进程的stdin/stdout管道输入或交互输入不存在 UART/USB 设备。3.2 仿真时钟与计时Clock::setFrequency(F_CPU)将仿真时钟频率设为F_CPUHAL.h 中定义为100000000UL即 100 MHz。HAL_timer_init()初始化定时器各线程用std::this_thread::yield()让出 CPU。Clock::setTimeMultiplier(1.0)还可以把时间乘数调成 10x 来加速测试。3.3 缺失系统函数的兜底Linux 上如果宿主系统没有strlcpymacOS 通过HAS_LIBBSD提供HAL 会退回本地实现MarlinHAL::_strlcpy定义在 HAL.cppHAL.h 中以#define strlcpy hal._strlcpy重定向无需依赖系统库。四、十个源码里看不出来的关键实现细节这是 AGENTS.md 的核心价值所在。以下每条都对应真实源码改动 HAL 代码前务必了解。4.1 没有真实引脚——GPIO 是内存软件数组Gpio::pin_count 255hardware/Gpio.h引脚只是内存中pin_map[]数组的索引而非物理线。pin_data结构体记录dir、mode、value和可选的Peripheral* cb回调。Gpio::set/get/clear/setMode/setDir等静态方法在操作数组的同时会生成GpioEvent含时间戳Clock::nanos()通知回调或日志器。fastio.h 中的所有宏都委托给Gpio::方法#define SET_DIR_INPUT(IO) Gpio::setDir(IO, 1) #define SET_DIR_OUTPUT(IO) Gpio::setDir(IO, 0) #define WRITE_PIN_SET(IO) Gpio::set(IO) #define WRITE_PIN_CLR(IO) Gpio::clear(IO) #define READ_PIN(IO) Gpio::get(IO) #define WRITE_PIN(IO,V) Gpio::set(IO, V)而 include/pinmapping.h 中PIN_EXISTS相关的GET_PIN_MAP_INDEX是恒等传递return pinPWM_PIN和INTERRUPT_PIN永远返回false。不要在这里添加任何硬件相关的引脚逻辑。4.2 串口是虚拟的走 stdioHalSerialinclude/serial.h是一对RingBufferuint8_t, 128模板环形缓冲容量 128 字节需为 2 的幂。关键行为host_connected构造时初始化为truewrite()在 TX 环形缓冲满时忙等while (!transmit_buffer.free());flushTX()会忙等 TX 缓冲清空MYSERIAL1即usb_serialHAL.cpp 中MSerialT usb_serial(...)由typedef Serial1ClassHalSerial MSerialT包装成 Marlin 串口体系。所以调试时把 G-code 文件管道进 stdin、从 stdout 读输出即可。4.3 EEPROM 就是一个磁盘文件eeprom.cpp仅在EEPROM_SETTINGS开启时编译把 EEPROM 模拟为当前工作目录下的eeprom.dat容量MARLIN_EEPROM_SIZE 0x10004 KB未定义时默认此值access_start()用fopen(rb)读文件文件不足 4 KB 时剩余部分以0xFF填充EEPROM 擦除值write_data/read_data直接操作内存缓冲buffer[]并累计crc16真正落盘发生在access_finish()它用fopen(wb)把整个缓冲写回文件。这意味着设置会在多次运行间通过eeprom.dat持久化工作目录里残留的旧eeprom.dat可能掩盖你的配置改动需要全新存储时直接删掉该文件这是文档明确建议的操作注意不要用rm -rf .pio之类的方式清理构建。4.4 没有真实 ADC / PID 反馈MarlinHAL::adc_value()HAL.cpp从模拟 GPIO 位值合成 10 位读数uint16_t MarlinHAL::adc_value() { const pin_t pin analogInputToDigitalPin(active_ch); if (!isValidPin(pin)) return 0; return uint16_t((Gpio::get(pin) 2) 0x3FF); // return 10bit value as Marlin expects }adc_start(ch)只记录active_chadc_ready()恒为true。真正的温度来自 hardware/Heater.h 的Heater对象它们挂在HEATER_0_PIN/TEMP_0_PIN、HEATER_BED_PIN/TEMP_BED_PIN等引脚上由simulation_loop每轮update()模拟升温/降温。4.5UNIT_TEST下不编译main()main.cpp 整体被#ifndef UNIT_TEST包裹。linux_native_test环境把 HAL 编译成库供 Unity 测试使用不链接main()——所以测试构建成功并不代表控制台入口存在。不要写只从main()触发的 HAL 运行时代码并指望它在测试环境下执行。4.6 线程 yield()模拟时序而非中断LINUX HAL 没有真实 ISRCRITICAL_SECTION_START/END为空宏HAL.hisr_on/isr_off是空操作isr_state()恒trueDELAY_CYCLES(x)展开为Clock::delayCycles(x)步进/仿真计时由Clockmain()里设为F_CPU和HAL_timer_init()驱动配合各线程std::this_thread::yield()。因此实时行为是近似的、受宿主机负载影响不适合做严格时序验证。4.7freeMemory()恒返回 0不同于嵌入式 HAL 报告剩余 SRAMLINUX HAL 的freeMemory()直接返回 0HAL.cppMarlinHAL::freeMemory()只是其包装。任何依赖返回值判断堆内存余量的逻辑在此无效。4.8 模拟输入固定为 16 路include/pinmapping.h 硬编码constexpr uint8_t NUM_ANALOG_INPUTS 16; constexpr uint8_t analog_offset NUM_DIGITAL_PINS - NUM_ANALOG_INPUTS; // 239模拟索引 0..15 映射到 255 路数字空间的顶部239..254。analogInputToDigitalPin/digitalPinToAnalogIndex是 constexpr 算术不是按板子的查表。4.9 SD/媒体基于宿主文件系统没有 SD 卡控制器。虽然 spi_pins.h 仍定义默认SD_SCK_PIN/SD_MISO_PIN/SD_MOSI_PIN50/51/52和SOFTWARE_SPI回退但实际媒体操作作用于宿主机文件系统。把 SD 操作当作宿主环境里的普通文件 I/O 即可。4.10 可选的 GPIO/位置 CSV 日志main.cpp 中默认注释掉的//#define GPIO_LOGGING开启后用IOLoggerCSV logger(all_gpio_log.csv)通过Gpio::attachLogger(logger)订阅全部引脚事件GpioEvent含时间戳、引脚号、事件类型另写axis_position_log.csv记录 X/Y/Z 轴位置变化仅在坐标变化时追加一行并flush。这是仅用于调试的手段会带来文件系统副作用除非在追踪引脚/轴活动否则保持关闭。五、编码约定与边界守卫宏所有 HAL 源码用#ifdef __PLAT_LINUX__保护而不是ARDUINO_ARCH_*每个.cpp都套用许可/骨架头。目录边界改动应留在Marlin/src/HAL/LINUX/及其hardware/、include/、inc/、u8g/子目录。这里没有板级引脚文件——BOARD_SIMULATED是唯一主板引脚映射就是固定的软件数组。串口对象没有 ArduinoSerial对象MYSERIAL1是HalSerial支撑的usb_serialHAL.h、HAL.cpp。提交前验证改动 HAL 代码后跑linux_native涉及单元测试则加跑linux_native_test确认绿色再提交。六、相关资源速查Marlin/src/HAL/NATIVE_SIM/孪生的 GUI 模拟器 HALSDL2/GLM通过simulator_linux_*环境构建与本文的 LINUX HAL 是不同目标Marlin/src/HAL/shared/本 HAL 复用的共享 APIeeprom_api、SPI 辅助、Marduino.hini/native.ini[env:linux_native]/[env:linux_native_test]定义另有 macOS/Windows 模拟器环境示例buildroot/tests/linux_native/config-01.iniBOARD_SIMULATED EEPROM 的测试配置单元测试入口tests/gcode/test_gcode.cpp、tests/feature/test_runout.cpp、tests/core/test_macros.cpp 等。结语LINUX HAL 是 Marlin 仓库中一个精巧的软件定义硬件层用 255 路内存引脚、stdio 虚拟串口、文件化 EEPROM 和线程化仿真循环把一整台 3D 打印机固件搬到了宿主机上。理解它的构建入口mftest双目标、运行模型main() 三线程和十个非显而易见的实现细节是安全修改该 HAL、为其补充单元测试的前提。下次动手前请记住两条铁律验证走mftest -t linux_native(_test)重建走pio run -t clean。【免费下载链接】MarlinMarlin is a firmware for RepRap 3D printers optimized for both 8 and 32 bit microcontrollers. Marlin supports all common platforms. Many commercial 3D printers come with Marlin installed. Check with your vendor if you need source code for your specific machine.项目地址: https://gitcode.com/GitHub_Trending/ma/Marlin创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表