Marlin 固件 TEENSY35_36 HAL 深度解析:面向 Teensy 3.5 / 3.6 的 Cortex-M4 平台移植指南
2026/9/13 11:53:28 网站建设 项目流程

Marlin 固件 TEENSY35_36 HAL 深度解析:面向 Teensy 3.5 / 3.6 的 Cortex-M4 平台移植指南

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

导读

本文以仓库内 TEENSY35_36 HAL 工作笔记 为核心,系统讲解 Marlin 固件如何为 PJRC Teensy 3.5(MK64FX512)与 Teensy 3.6(MK66FX1M0)两块 ARM Cortex-M4 芯片提供硬件抽象层。你将掌握该 HAL 的构建与测试流程、与上游 Teensyduino 框架的集成边界,以及 EEPROM 仿真、双 ADC 查表、Kinetis 看门狗、原生串口/SPI 封装等关键实现的底层原理,并了解移植维护时必须遵守的代码约定。

一、平台总览:一块 HAL 目录,两颗 Kinetis 芯片

Marlin/src/HAL/TEENSY35_36/是 Marlin 为Teensy 3.5Teensy 3.6共用的唯一 HAL 目录,两板均基于 ARM Cortex-M4:

开发板主控芯片Flash关键区别
Teensy 3.5MK64FX512512 KB容量更小,是移植的瓶颈约束
Teensy 3.6MK66FX1M01 MB容量翻倍,功能超集

两块芯片不通过运行时判断区分,而是由编译器宏决定:PlatformIO 的teensy35/teensy36板级 JSON 分别注入__MK64FX512__/__MK66FX1M0__宏。这一约定直接体现在 HAL.h 中:

#ifdef __MK66FX1M0__ #define IS_TEENSY36 1 #else // __MK64FX512__ #define IS_TEENSY35 1 #endif

由于 3.5 只有 512 KB Flash,社区维护的准则是:如果固件在 3.5 上放得下,3.6(1 MB)一定也没问题,因此 3.5 是一切适配工作的硬约束。

二、构建与测试闭环:mftest 与 teensy35/36 环境

2.1 推荐使用 mftest 验证

HAL 工作笔记明确要求使用mftest做验证,因为它会为每个测试目标重新生成Marlin/Configuration.h,而裸跑pio run -e无法可靠完成这一步:

cd "$(git rev-parse --show-toplevel)" buildroot/bin/mftest -t teensy35 -n1 -y # Teensy 3.5(MK64FX512) buildroot/bin/mftest -t teensy35 -n8 -y # 双 Z 限位(Dual-Z)压力测试配置

其中-t指定测试目标、-n指定第几个配置、-y跳过确认提示。

2.2 测试配置分布:只有 teensy35,没有 teensy36

该 HAL 的全部真实测试目标集中在buildroot/tests/teensy35/config-01config-08,共 8 套配置),它们全部声明motherboard = BOARD_TEENSY35_36。仓库中不存在buildroot/tests/teensy36/目录——3.6 构建与 3.5 共用同一 HAL 与板卡宏,唯一差异来自 PlatformIO 的board = teensy36(注入__MK66FX1M0__)。

因此:

  • 想覆盖共享代码:跑teensy35测试目标即可;
  • 想专门验证 3.6 专属路径:用构建环境pio run -e teensy36(对应 ini/teensy.ini 中的[env:teensy36]board = teensy36)。

板卡选择宏BOARD_TEENSY35_36定义在 Marlin/src/core/boards.h(编号 5101)。

2.3 强制重建的安全姿势

工作笔记特别警告:不要rm -rf .pio/build/...强制重建——仓库的跨配置写保护会拦截该命令且这样做不安全。正确做法是:

pio run -e <env> -t clean # 只清除构建产物

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

三、框架集成边界:上游 Teensyduino,而非仓库内置

与仓库自带的 AT32 框架不同,Teensy 的 Arduino 内核是PlatformIO 提供的上游 Teensyduino 软件包,并未 vendored 到buildroot/share/PlatformIO/下。其版本约束写在 ini/teensy.ini:

platform = teensy@~4.12.0

teensy_arm环境会拉取 Teensyduino 的cores/teensy3/,其中提供:

  • 寄存器头文件(kinetis.hpins_teensy.h);
  • 原生串口对象Serial1…SerialUSB、裸Serial
  • 原生SPIWire
  • avr/eeprom.h的仿真实现(见下文)。

Marlin 的胶水代码全部位于本 HAL 目录,外加板级引脚定义 Marlin/src/pins/teensy3/pins_TEENSY35_36.h。由于没有"安装包 vs 仓库"的镜像步骤(那条警告仅适用于 vendored 的 AT32 框架),构建层面的改动就是直接编辑Marlin/src/HAL/TEENSY35_36/下的源码与buildroot/脚本。

四、六个"源码里看不出来"的坑(Gotchas)

4.1 芯片由编译期宏选择,绝不运行时分支

HAL.h 用#ifdef __MK66FX1M0__区分 3.5/3.6,且目录内每个.cpp都以#if defined(__MK64FX512__) || defined(__MK66FX1M0__)包裹(参见 HAL.cpp、eeprom.cpp、timers.cpp)。3.5 与 3.6 的区别纯粹来自board = teensy35/board = teensy36两块 PlatformIO JSON——不要为芯片差异添加运行时分支

4.2 没有物理 EEPROM:内核在 Flash 中仿真

Kinetis 芯片没有物理 EEPROM,Teensyduino 在程序 Flash 中提供仿真 EEPROM。Marlin 的 eeprom.cpp 通过USE_WIRED_EEPROM#include <avr/eeprom.h>接入这套仿真,MARLIN_EEPROM_SIZE默认取E2END + 1

最关键的细节在write_data()中:由于 Flash 写周期很慢,HAL 在每写一个字节之间插入delay(2)/safe_delay(2),专门用来避免在长 EEPROM 写入期间触发看门狗复位。这段延迟是承重代码,移植时务必保留:

eeprom_write_byte(p, v); if (++written & 0x7F) delay(2); else safe_delay(2); // 避免长时间 EEPROM 写入触发看门狗

另外,写入前会先eeprom_read_byte(p)比较新旧值,只有字节确实变化才写——因为 EEPROM 只有约 10 万次写寿命,写后还会回读校验并打印STR_ERR_EEPROM_WRITE

4.3 裸 Kinetis 寄存器名来自 Teensyduino 内核

HAL.cpp 中直接引用的RCM_SRS0WDOG_TOVALHWDOG_STCTRLHWDOG_REFRESHADC0_SC3ADC1_SC3IRQ_FTM1NVIC_ENABLE_IRQ,以及 fastio.h 里的CORE_PIN…系列宏,全部由 Teensyduino 的cores/teensy3/定义,Marlin 不重定义它们。如果编译报找不到RCM_SRS0,说明选错了内核,而不是缺 Marlin 头文件。

4.4 双 ADC + 物理引脚→SC1A 查表

adc_start()用一个静态pin2sc1a[]数组(在 HAL.cpp 中)按物理引脚号索引 ADC 通道:数字专用引脚映射为255,此时adc_select = -1;表项最高位(0x80)标志 ADC1,否则走 ADC0:

const uint16_t pin = pin2sc1a[adc_pin]; if (pin == 0xFF) adc_select = -1; // 纯数字引脚 else if (pin & 0x80) { adc_select = 1; ADC1_SC1A = pin & 0x7F; } else { adc_select = 0; ADC0_SC1A = pin; }

adc_value()则依据adc_selectADC0_RAADC1_RA。引脚→ADC 通道的连线由 Teensy 3.5/3.6 硅片决定,若需修改必须对照 Teensyduino 的pins_teensy.h编号。此外adc_init()会调用analog_init()并等待 ADC0/ADC1 校准完成(ADC0_SC3 & ADC_SC3_CAL),最后使能IRQ_FTM1。HAL.h 中定义了 ADC 参考电压HAL_ADC_VREF_MV 3300与分辨率HAL_ADC_RESOLUTION 10

4.5 看门狗是 Kinetis WDOG,双写刷新序列

Teensy 3.x 没有独立的 CortexWDT,因此不能照搬 STM32 风格看门狗代码。Marlin 的看门狗实现(见 HAL.cpp):

#define WDT_TIMEOUT_MS TERN(WATCHDOG_DURATION_8S, 8000, 4000) // 4 或 8 秒超时 void MarlinHAL::watchdog_init() { WDOG_TOVALH = 0; WDOG_TOVALL = WDT_TIMEOUT_MS; WDOG_STCTRLH = WDOG_STCTRLH_WDOGEN; } void MarlinHAL::watchdog_refresh() { // 看门狗刷新序列(硬件强制顺序) WDOG_REFRESH = 0xA602; WDOG_REFRESH = 0xB480; }

刷新必须依次写0xA602再写0xB480WDOG_REFRESH,顺序由硬件强制,缺一不可。超时时间由WATCHDOG_DURATION_8S决定(4 秒或 8 秒)。

4.6 原生串口对象:无需自定义 Serial 全局

与 AT32 HAL 不同,Teensyduino 已内置Serial1…Serial3SerialUSB与裸Serial。HAL.h 只做转发声明:

#define Serial0 Serial #define _DECLARE_SERIAL(X) \ typedef ForwardSerial1Class<decltype(Serial##X)> DefaultSerial##X; \ extern DefaultSerial##X MSerial##X typedef ForwardSerial1Class<decltype(SerialUSB)> USBSerialType; extern USBSerialType USBSerial; #define SERIAL_INDEX_MIN 0 #define SERIAL_INDEX_MAX 3

SERIAL_PORTSERIAL_PORT_2/3MMU_SERIAL_PORTLCD_SERIAL_PORT在 HAL.cpp 中按需实例化,USBSerial包装原生SerialUSB不需要HardwareSerial& Serial = Serial1;这类别名 hack。

五、其余核心实现:计时器、SPI、快速 IO 与重启

5.1 FTM 计时器双通道分工

timers.h 与 timers.cpp 使用两块 FTM 硬件定时器:

角色定时器预分频时钟对应 ISR
步进电机(MF_TIMER_STEP=0)FTM0860 MHz / 8 = 7.5 MHzftm0_isr
温度采样(MF_TIMER_TEMP=1)FTM1460 MHz / 4 = 15 MHzftm1_isr

TEMP_TIMER_FREQUENCY固定为 1000 Hz。HAL_timer_start()按 65535 最大模数配置通道比较值;HAL_timer_disable_interrupt()NVIC_DISABLE_IRQ强制执行__DSB()/__ISB()内存屏障,确保中断真正被禁用(代码中注释引用了 ARM Cortex-M NVIC 禁用的经典坑)。ISR 前导HAL_timer_isr_prologue()负责清零计数与溢出/比较标志。

5.2 SPI:包装原生 SPIClass

MarlinSPI.h 一行完成别名:

using MarlinSPI = SPIClass;

HAL_SPI.cpp 通过SPISettings/SPI.transfer驱动<SPI.h>的全局SPI对象,速率映射完整覆盖 Marlin 的分级常量:

case SPI_FULL_SPEED: clock = 10000000; break; // 10 MHz case SPI_HALF_SPEED: clock = 5000000; break; // 5 MHz case SPI_QUARTER_SPEED: clock = 2500000; break; case SPI_EIGHTH_SPEED: clock = 1250000; break; case SPI_SPEED_5: clock = 625000; break; case SPI_SPEED_6: clock = 312500; break; default: clock = 4000000; // SPI 库默认 4 MHz

spiBegin()把 SD 引脚配置为 GPIO(SD_SS拉高、SCK 输出、MISO 输入、MOSI 输出)。注意:spiSendBlock()中仍残留直接写SPDR寄存器与SPI.transfer()混用的代码——功能可用,但不要把这个模式复制到别处

5.3 快速 IO:Cortex-M 位带操作

fastio.h 基于 ARM Cortex-M 的**位带(bit-band)**特性实现高速 IO:

#define GPIO_BITBAND_ADDR(reg, bit) (((uint32_t)&(reg) - 0x40000000) * 32 + (bit) * 4 + 0x42000000) #define GPIO_BITBAND(reg, bit) (*(uint32_t *)GPIO_BITBAND_ADDR((reg), (bit)))

WRITE/READ/TOGGLE/SET_INPUT/SET_OUTPUT等宏均展开为对CORE_PINxx_PORTCLEAR/PORTSET/CONFIG/DDRREG的单指令访问,并直接操作PORT_PCR_MUX/PORT_PCR_SRE/PORT_PCR_DSE引脚控制寄存器,避免 ArduinodigitalWrite的函数调用开销。

5.4 重启与复位源

reboot()调用 Teensyduino 内核提供的_reboot_Teensyduino_()(见 HAL.cpp),而非手写SCB->AIRCR跳转——保持使用内核辅助函数即可。get_reset_source()读取RCM_SRS0复位状态寄存器,映射为RST_POWER_ON(值 128)、RST_EXTERNAL(64)、RST_WATCHDOG(32)。

5.5 自由内存探测

freeMemory()通过链接器符号__bss_end__heap_start__brkval计算栈底与堆顶之间的可用 SRAM(HAL.cpp 末尾),用于运行时内存告警。

六、端限位中断:把轮询节省到底

endstop_interrupts.h提供基于attachInterrupt(..., CHANGE)的端限位中断方案。它的意义在于:没有中断时,端限位引脚必须在温度 ISR 里被endstops.update()持续轮询,大多数时候毫无变化、纯耗 CPU;启用后只有端限位真正跳变才调用endstops.update()。所有用到的限位引脚(X/Y/Z 各 MIN/MAX、双 Z、探针Z_MIN_PROBE、校准引脚、I/J/K/U/V/W 轴等)逐一挂上统一的中断处理函数endstop_ISR()。前提是这些引脚都能产生外部中断,可用buildroot/share/pin_interrupt_test/pin_interrupt_test.ino烧录验证。

七、维护约定与代码归属

从工作笔记可以提炼出以下必须遵守的约定:

  1. 宏守卫统一:所有 HAL 源文件以#if defined(__MK64FX512__) || defined(__MK66FX1M0__)包裹;该家族没有ARDUINO_ARCH_*守卫,芯片宏由 PlatformIO 板卡 JSON 注入。
  2. 改动归属清晰:HAL 逻辑改动放在Marlin/src/HAL/TEENSY35_36/,引脚改动放在Marlin/src/pins/teensy3/pins_TEENSY35_36.h,板卡选择BOARD_TEENSY35_36Marlin/src/core/boards.h
  3. 不 vendor 框架:本目录没有README.md,也没有 vendored 框架;框架/平台集成行为属于上游 Teensyduino,不要试图在buildroot/share/PlatformIO/打补丁。

八、关联资源:同族 HAL 与共享层

该 HAL 复用了Marlin/src/HAL/shared/的共享 API(serial_ports.hHAL_SPI.heeprom_api.h),并与其他两个 Teensy HAL 构成同族:

  • Marlin/src/HAL/TEENSY31_32/ — Teensy 3.1/3.2(不同硅片、不同宏);
  • Marlin/src/HAL/TEENSY40_41/ — Teensy 4.0/4.1(Cortex-M7,宏体系完全不同)。

理解这三个 HAL 的差异(尤其是宏命名与内核依赖),是维护 Marlin 多平台支持的捷径:TEENSY35_36 走 KinetisWDOG+ FTM + 位带 IO 的老派 Cortex-M4 路线,而 40_41 则是 NXP i.MX RT 的现代架构。

结语

TEENSY35_36 HAL 是"用最少胶水对接最强原生内核"的典型:编译期宏选芯片、复用 Teensyduino 的串口/SPI/EEPROM 仿真、只补上 Kinetis 特有的看门狗、FTM 定时器与位带快速 IO。无论你是要为新 Teensy 3.5/3.6 机器板卡适配 Marlin,还是想理解 Cortex-M4 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

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询