news 2026/9/13 11:09:08

Marlin SAMD21 HAL 深入解析:架构、构建验证与移植避坑指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Marlin SAMD21 HAL 深入解析:架构、构建验证与移植避坑指南

Marlin SAMD21 HAL 深入解析:架构、构建验证与移植避坑指南

【免费下载链接】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

本篇技术指南以Marlin/src/HAL/SAMD21/AGENTS.md为核心,系统讲解 Marlin 固件中 SAMD21(Atmel SAMD21J18,ARM Cortex-M0+)硬件抽象层(HAL)的整体架构、构建与测试流程、框架集成方式,以及源码中不易察觉的七类关键"陷阱"。读者将掌握 SAMD21 HAL 的串口/SPI/ADC/EEPROM 实现原理、mftest验证命令的正确用法,以及新增 SAMD21 板卡环境时必须满足的编译前提,可直接用于二次开发与移植实践。

一、SAMD21 HAL 概览与仓库布局

SAMD21 HAL 位于仓库 Marlin/src/HAL/SAMD21/,由 Bart Meijer(brupje)开发,基于 Giuliano Zaro 的 SAMD51 HAL 改写而来(见 HAL.h)。该目录承担了 Marlin 在 SAMD21 平台上的全部底层能力,包括:

  • 串口:原生 USB-CDC 与硬件 UART(SERCOM)封装;
  • SPI:基于上游SPIClass的硬件 SPI 驱动(MarlinSPI.h、HAL_SPI.cpp);
  • ADC:12 位 ADC 自由运行扫描与中断采集(HAL.cpp);
  • 定时器/伺服/引脚:timers.cpp、Servo.cpp、fastio.h;
  • EEPROM 后端:eeprom/ 下的eeprom_wired.cppeeprom_flash.cppeeprom_qspi.cpp三种实现;
  • u8g LCD 驱动:u8g/ 下的 I2C/硬件 SPI 例程。

与 HAL 配套的板级引脚文件位于 Marlin/src/pins/samd/,目前包含四块板卡:

引脚文件对应板卡
pins_MINITRONICS20.hReprapWorld Minitronics v2.0(SAMD21J18)
pins_BRICOLEMON_V1_0.hBricolemon V1.0
pins_BRICOLEMON_LITE_V1_0.hBricolemon Lite V1.0
pins_RAMPS_144.hRAMPS 1.4.4 适配

构建环境的接入点则分散在两处:ini/samd21.ini定义平台环境,buildroot/share/PlatformIO/scripts/SAMD21_minitronics20.py提供按环境定制的构建脚本。

二、构建与测试:用mftest验证 SAMD21 目标

SAMD21 家族目前只有一个真实测试目标SAMD21_minitronics20,它是 SAMD21 最严格(也是唯一)的约束条件——只要它构建通过并保持绿色,就代表该家族整体得到覆盖。

2.1 执行验证命令

仓库推荐使用mftest进行验证,因为它会按目标重新生成Marlin/Configuration.h,而裸执行pio run -e无法可靠地做到这一点:

cd "$(git rev-parse --show-toplevel)" buildroot/bin/mftest -t SAMD21_minitronics20 -n1 -y # ReprapWorld Minitronics 2.0, SAMD21J18

其中-t指定测试目标,-n1表示仅执行一次,-y自动确认。测试目标的真实配置位于 buildroot/tests/SAMD21_minitronics20/config-01.ini,其[config:base]段落给出了该板卡的参考配置,例如:

motherboard = BOARD_MINITRONICS20 serial_port = -1 x_driver_type = DRV8825 y_driver_type = DRV8825 z_driver_type = DRV8825 e0_driver_type = DRV8825 bltouch = on endstop_interrupts_feature = on filament_runout_sensor = on lin_advance = on eeprom_settings = on reprapworld_graphical_lcd = on

该配置开启了 BLTouch、限位中断、断料检测、线性提前、EEPROM 设置、图形 LCD 等一揽子特性,是 SAMD21 上"功能全开"的构建压力测试。

注意:不要把SAMD51_grandcentral_m4(buildroot/tests/SAMD51_grandcentral_m4/config-01.ini)与 SAMD21 混淆,前者属于 SAMD51 家族,两者架构并不相同。

2.2 清理与重建规则

禁止使用rm -rf .pio/build/...强制重建——交叉配置的写保护会拦截该操作,且本身不安全。正确做法是:

pio run -e SAMD21_minitronics20 -t clean

或者直接让mftest自行触发重建。

2.3 环境配置要点

SAMD21_minitronics20是 SAMD21 唯一的 PlatformIO 环境,定义于 ini/samd21.ini:

[env:SAMD21_minitronics20] platform = atmelsam board = minitronics20 build_flags = ${common.build_flags} -std=gnu++17 -DUSBCON -DUSBD_USE_CDC -D__SAMD21__ -DARDUINO_SAMD_MINITRONICS20 -Wno-deprecated-declarations -DDEBUG -IMarlin/src/HAL/SAMD21/u8g build_unflags = -std=gnu++11 build_src_filter = ${common.default_src_filter} +<src/HAL/SAMD21> extra_scripts = ${common.extra_scripts} pre:buildroot/share/PlatformIO/scripts/SAMD21_minitronics20.py debug_tool = atmel-ice

buildroot/share/PlatformIO/scripts/SAMD21_minitronics20.py是 per-env 额外脚本,其作用仅是根据MARLIN_FEATURES中的RX_BUFFER_SIZE/TX_BUFFER_SIZE调整串口缓冲区BUILD_FLAGS(取两者与 350 的最大值),不涉及其他逻辑改动。

三、框架与平台集成:上游atmelsam,并非内置副本

与 AT32 等平台不同,SAMD21 的 Arduino 核心是PlatformIO 上游atmelsam平台framework-arduino-samd-*,例如framework-arduino-samd-reprap/...-adafruit),仓库内没有Marlin 自带的 vendored 副本(buildroot/share/PlatformIO/下不存在)。因此:

  • Marlin 特有的胶水代码分布在三处:
    1. Marlin/src/HAL/SAMD21/——HAL 本体;
    2. Marlin/src/pins/samd/——板级引脚定义;
    3. buildroot/share/PlatformIO/scripts/SAMD21_minitronics20.py 与 ini/samd21.ini——构建胶水。
  • 因为框架来自上游,所以不存在"mirror-to-installed-package"(镜像到已安装包)警告——该警告只适用于 vendored 的 AT32 核心。

四、源码中不明显的七个"陷阱"(Gotchas)

这是 AGENTS.md 的核心实战部分,以下逐条结合源码印证。

4.1 编译器宏是__SAMD21__,由构建参数注入,而非核心定义

HAL 的所有源码都通过#ifdef __SAMD21__门控编译,包括 HAL.cpp、HAL_SPI.cpp 以及eeprom/下的各实现。但上游 Arduino SAMD 核心定义的宏是ARDUINO_ARCH_SAMD并不定义__SAMD21__。正是 ini/samd21.ini 通过-D__SAMD21__注入该宏。

移植铁律:新增任何 SAMD21 板卡环境,必须在build_flags中包含-D__SAMD21__,否则该 HAL 目录下的一切代码都不会参与编译。

4.2 主串口是原生 USB-CDC,而非 UART

HAL.h 中:

typedef ForwardSerial1Class< decltype(SerialUSB) > DefaultSerial1; extern DefaultSerial1 MSerialUSB; typedef ForwardSerial1Class< decltype(Serial1) > DefaultSerial2; typedef ForwardSerial1Class< decltype(Serial2) > DefaultSerial3; extern DefaultSerial2 MSerial0; extern DefaultSerial3 MSerial1; ... #define USB_SERIAL_PORT(...) MSerialUSB

DefaultSerial1绑定SerialUSB(即MSerialUSB),而Serial1/Serial2通过 SERCOM 映射到硬件 UART(MSerial0/MSerial1)。构建参数中的-DUSBCON -DUSBD_USE_CDC(ini/samd21.ini)使SerialUSB得以存在。

因此测试配置 config-01.ini 中serial_port = -1表示放弃 UART、完全依赖 USB。若某块板卡没有原生 USB,必须重新指定串口端口,否则无法与主机通信。

4.3 软件 SPI 是硬性#error

HAL_SPI.cpp 中:

#if ANY(SOFTWARE_SPI, FORCE_SOFT_SPI) #error "Software SPI not supported for SAMD21. Use Hardware SPI." #else

即启用SOFTWARE_SPIFORCE_SOFT_SPI会直接编译失败。MarlinSPI只是SPIClass的别名(MarlinSPI.h),即上游 SAMD SERCOM SPI 驱动。spiSendBlock()(HAL_SPI.cpp)注释中声称"Uses DMA",但实际调用的是阻塞式SPI.transfer()——这在功能上没有问题,只是不要指望 SD 卡路径获得 DMA 卸载

硬件 SPI 的速率映射(HAL_SPI.cpp):

Marlin 速率常量实际时钟
SPI_FULL_SPEED8 MHz
SPI_HALF_SPEED4 MHz
SPI_QUARTER_SPEED2 MHz
SPI_EIGHTH_SPEED1 MHz
SPI_SIXTEENTH_SPEED500 kHz
SPI_SPEED_5250 kHz
SPI_SPEED_6125 kHz
默认4 MHz

4.4 EEPROM 后端按板卡选择,不自动探测

SAMD21 HAL 的 eeprom/ 目录提供三种PersistentStore实现:

  1. I2C EEPROM(eeprom_wired.cpp):经USE_WIRED_EEPROM使能,但对 SAMD21 而言该宏是显式#error"USE_WIRED_EEPROM emulation Not implemented on SAMD21");
  2. Flash 模拟 EEPROM(eeprom_flash.cpp):经FLASH_EEPROM_EMULATION使能,通过 NVMCTRL 按 ROW 擦除、按页写入,数据保存在__attribute__((__aligned__(256)))flashdata静态数组中,容量由MARLIN_EEPROM_SIZE决定;
  3. QSPI EEPROM(eeprom_qspi.cpp + QSPIFlash.cpp):经QSPI_EEPROM使能,代码已写好但同样以#error "QSPI_EEPROM emulation Not implemented on SAMD21"拒绝(eeprom_qspi.cpp),当前没有任何板卡选用。

各板卡的实际默认(见 pins_MINITRONICS20.h):

//#define FLASH_EEPROM_EMULATION //#define I2C_EEPROM // EEPROM on I2C-0 #define MARLIN_EEPROM_SIZE 500U // 4000 bytes
  • BRICOLEMON_*RAMPS_144默认I2C_EEPROM
  • MINITRONICS20保持FLASH_EEPROM_EMULATION注释状态,并定义MARLIN_EEPROM_SIZE 500U(注意:注释中"4000 bytes"的表述与 500U 存在出入,实际容量以MARLIN_EEPROM_SIZE为准);
  • QSPI_EEPROM已编码但当前无板卡启用。

4.5WDT宏与 CMSIS 结构体字段冲突

在 HAL.cpp 中,get_reset_source()的函数体被特意包裹在宏推送/弹出中:

#pragma push_macro("WDT") #undef WDT // Required to be able to use '.bit.WDT'. Compiler wrongly replace struct field with WDT define uint8_t MarlinHAL::get_reset_source() { return 0; } #pragma pop_macro("WDT")

原因在于REG_WDT_CTRL会解析为.bit.WDT,而预处理器会错误地把WDT宏替换进结构体字段。不要试图通过重命名字段来"修复"此问题——必须保留宏 push/pop 结构。

4.6 ADC 是自由运行的 INPUTSCAN 扫描,传感器顺序被重映射

HAL.h 定义:

#define HAL_ADC_FILTERED 1 // Disable Marlin's oversampling. The HAL filters ADC values. #define HAL_ADC_VREF_MV 3300 #define HAL_ADC_RESOLUTION 12 #define HAL_ADC_AIN_START ADC_INPUTCTRL_MUXPOS_PIN3 #define HAL_ADC_AIN_NUM_SENSORS 3

adc_init()(HAL.cpp)将 12 位 ADC 配置为 16 位均值结果(32 次采样累加、ADJRES(4)GAIN_DIV2)、自由运行模式,并扫描 3 个连续 AIN(ADC_INPUTCTRL_INPUTSCAN(HAL_ADC_AIN_LEN))。SAMD21 的 ADC 只能扫描连续的 AIN,因此:

  • ADC_Handler()通过ADC->INPUTCTRL.bit.INPUTOFFSET将结果写入adc_results[pos]数组(HAL.cpp);
  • adc_start()在应用层把引脚的 AIN 重新映射回数组索引(HAL.cpp):pos = PIN_TO_INPUTCTRL(pin) - HAL_ADC_AIN_START + 1,当pos等于传感器数时归零。

此外HAL_ADC_FILTERED 1意味着Marlin 自身的过采样被关闭——HAL 层已经完成了滤波(32 次平均),上层无需重复处理。

4.7 48 MHz Cortex-M0+,无 FPU

SAMD21 最高主频 48 MHz,且没有硬件浮点单元。在热路径(如运动规划、温度 PID)中应尽量使用整数/定点运算。另外,freeMemory()(HAL.cpp)实现为基于_sbrk/__bss_end__的堆尾估算:

int freeMemory() { int free_memory, heap_end = (int)_sbrk(0); return (int)&free_memory - (heap_end ?: (int)&__bss_end__); }

这只是一个粗略的堆尾 hack,并非真正的栈检查,不应据此判断栈溢出风险。

五、SAMD21 代码约定(Conventions)

  1. 所有 HAL 源码均由#ifdef __SAMD21__门控——新增源文件或新板卡环境时务必注入-D__SAMD21__(见 4.1)。
  2. 修改边界清晰:HAL 改动放入 Marlin/src/HAL/SAMD21/,板级引脚改动放入 Marlin/src/pins/samd/,构建胶水放入 buildroot/share/PlatformIO/scripts/(per-env)或 ini/samd21.ini。
  3. 原生 USB CDC 是预期的主机链路:默认将SerialUSB/MSerialUSB视为主端口,除非板卡显式迁移到 UART。

六、看门狗与复位语义(补充实现细节)

虽然 AGENTS.md 未展开,但 HAL.cpp 中的看门狗实现值得了解:watchdog_init()将 GCLK2 配置为 1.024 kHz(对 32.768 kHz OSCULP32K 分频),喂给 WDT,并设置 4 秒(WATCHDOG_DURATION_8S时 8 秒)复位超时;watchdog_refresh()通过写入WDT_CLEAR_CLEAR_KEY清狗。注意:初始化后必须至少每 4 秒刷新一次,否则 SAMD21 会进入紧急复位流程。MarlinHAL::reboot()直接调用NVIC_SystemReset()重启固件。

七、与共享 HAL 的协作关系

SAMD21 HAL 复用了 Marlin/src/HAL/shared/ 中的共享 API(eeprom_api、SPI 辅助函数等),例如PersistentStoreeeprom_exclude_size语义、HAL_SPI.h的接口声明都来自共享层。在阅读 SAMD21 特定实现时,需要与共享目录对照理解接口约定。

结语

SAMD21 HAL 是一个"麻雀虽小、五脏俱全"的移植范本:上游框架 + 少量胶水 + 严格宏门控是其骨架,USB-CDC 主串口、SERCOM SPI、自由运行 ADC、按板卡选择的 EEPROM 后端是其血肉,而__SAMD21__宏注入、WDT宏冲突规避、软件 SPI 拒绝等约定则是所有后续移植者必须遵守的边界条件。以mftest -t SAMD21_minitronics20为唯一验证闸门,即可在新增板卡时快速确认家族完整性。

【免费下载链接】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),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/13 11:08:20

Python毫米波雷达数据处理:从帧解析到点云可视化全链路实践

简介&#xff1a;一份基于Python的毫米波雷达数据处理与可视化完整项目&#xff0c;面向毕业设计、课程设计及项目开发中需要解决雷达数据解析与目标跟踪可视化的读者。项目包含雷达数据采集日志、日志解析程序、数据可视化程序以及车辆跟踪算法&#xff0c;并附带README文档说…

作者头像 李华
网站建设 2026/9/13 11:07:45

内容运营必备AI技能与认证体系全解析

1. 内容运营岗位的AI技能需求分析内容运营岗位的核心职责是通过优质内容实现用户增长、品牌传播和商业转化。随着AI技术的普及&#xff0c;内容运营人员需要掌握以下AI相关技能&#xff1a;内容生成与优化&#xff1a;利用AI工具辅助文案创作、标题优化和内容结构化数据分析&am…

作者头像 李华
网站建设 2026/9/13 11:06:21

CSP-J真题能力诊断:从C++语法到算法思维的实战拆解

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/13 11:04:30

从单体拆分为微服务后,我们的响应时间反而变长了?

去年我们把一个运营了五年的单体应用拆成了十二个微服务。拆分前&#xff0c;下单接口平均响应180毫秒&#xff1b;拆分后&#xff0c;同样的接口&#xff0c;P99直接飙到1.2秒。团队一度怀疑是不是拆错了。复盘了两个月&#xff0c;终于把响应时间压回200毫秒以内。这篇文章把…

作者头像 李华