前言
原创文章,转载引用请务必注明链接,水平有限,如有疏漏,欢迎指正交流。【思兼 sjqlwy#gmail.com 2021年11月21日】
本文主要是我最近在 Klipper 上安装使用 ADXL345 所做的一些实验与资料的汇总,由于内容比较多,会分成几部分,并且会有简要版和完整版,有时间的建议阅读后者,可以学到各种知识。这次介绍外接 stm32f103c6t6 使用加速度计。
本文你将了解到的知识:
- 连接 DXL345 加速度计至 MCU 控制器
- STM32F103/F4 的 Bootloader 及 Application 烧写
- 如何通过阅读 Klipper 的源码,找到预定义的功能引脚(SPI/I2C/UART/ADC 等)
- 通过实践解惑:MCU 与 MPU 之间的 UART 通讯是否是加速度计采样数据的传输瓶颈
- 通过实践解惑2:MCU 的 软件模拟SPI 相比 硬件SPI 是否对加速度计采样造成性能瓶颈
加速度计与输入整形器
相信了解 Klipper 固件的朋友,都会对他的 Input Shaping 特性印象深刻,它可以极大减少机器震动所带来的各种问题,从而提高打印质量。
【图1:开启输入整形前后。可以看到振纹/鬼影明显减少,Source: thingiverse 】。
此特性分为两部分,一是测量机器的共振频率,二是通过输入整形器来减少振动所带来的影响。更多信息可查看 输入整形(Input Shaping) - 知乎 (zhihu.com)。 本文主要 ADXL345 在共振频率测量的中应用。
输入信号整形(Input Shaping)是数控机床中减少振动的开环控制技术,其作法是产生一命令信号来消除其自身的振动。较早命令信号产生的振动会由后面命令信号产生的振动所抵消。输入信号整形的作法是将脉冲(称为输入整形器)和任意命令信号进行卷积。接着用整形后的命令来驱动系统。若整形器的脉冲选择得当的话,整形后的命令产生的残余振动会比原始命令的振动要小。脉冲的振幅以及时间会依照系统的自然振动频率以及阻尼比决定。输入信号整形可以调整到对系统参数的误差有高强健性。
——Source: Wikipedia
1、问题由来与解决方案
-
官方文档默认是使用 RPi 直接连接加速度计。这就带来一个问题,首先很多时候大家都会把 RPi 封在下面,专门掏出来接加速度计很麻烦。
-
SPI 属于板内通讯,理想通讯距离不超过15cm,实际测试 50cm 以内都可以接受。从底部到挤出头距离太长,可能会影响数据准确性,甚至会造成读取错误。
因此,我设想的理想方案是拉出来一个小的独立 MCU(下文称 2nd MCU),通过数据线(UART协议)与 RPi 连接,然后此 MCU 靠近挤出头,一是使用灵活,二是可以避免过长的信号线,减少噪音和干扰,提高准确度;三是加速度计可以日常固定在挤出头上做运动分析。最后就是之前有些朋友使用 N1 和玩客云等做上位机,这些设备没有 SPI 接口,借助这块小 MCU,可以解决加速度计的连接问题。
2、2nd MCU 控制板选择与实现
我们看看 官方文档 怎么说的:
ADXL345 can be connected to a Raspberry Pi directly, or to an SPI interface of an MCU board (it needs to be reasonably fast).
......
You need to connect ADXL345 to your Raspberry Pi via SPI. Note that the I2C connection, which is suggested by ADXL345 documentation, has too low throughput and will not work.
......
However, you can also connect two accelerometers simultaneously, though they must be connected to different boards (say, to an RPi and printer MCU board), or to two different physical SPI interfaces on the same board (rarely available).
再看看 配置模板 中关于 [adxl345]
的内容:
# printer.cfg
[adxl345]
cs_pin:
# The SPI enable pin for the sensor. This parameter must be provided.
#spi_bus:
#spi_software_sclk_pin:
#spi_software_mosi_pin:
#spi_software_miso_pin:
# See the "common SPI settings" section for a description of the
# above parameters.
由此我们可知:
- 除了官方示例直接接到 RPi 之外,我们也可以接到 MCU 上。
- 对 MCU 的要求是
性能要好
- 支持
software SPI
连接加速度计,也就是软件模拟 SPI接口。
【Klipper 部分 MCU 性能测试 source: klipper3d.org】
我们知道,SPI 分为硬件 SPI 和软件模拟 SPI,相比之下,前者性能更好,后者由于需要占用系统资源进行模拟,性能差些,而且要求宿主 MCU 运行速度速度要快,也就是所谓的 (it needs to be reasonably fast)
,后面我们的测试也证明了这点。那是否有硬件 SPI 的控制器,就可以使用 Arduino 8位主板了呢?我们也不清楚,需要验证。本着质优价廉的原则,2nd MCU 有以下几个方案:
-
使用现有 3D 打印机主板上的硬件 SPI 接口
-
使用额外的一块 MCU 控制器,目前有看到用 Arduino Nano 实现的,后文会对这种方案进行验证和补充(结论:不建议使用)
-
针对2,既然都选择额外的 MCU 了,为什么不选择性能更强的 32位 控制器呢?
下面分述之,分别涉及介绍不同的知识。
2.1 使用 3D 打印机主板本身的硬件 SPI 接口
首先我们要知道主板有哪些硬件 SPI 接口,使用那些引脚。做过或了解嵌入式开发的朋友肯定听过引脚复用(Pin MUX),Klipper 作为一个程序,肯定也对 MCU 的引脚资源进行了定义,找到相关源代码,就可以知道具体功能的引脚定义。
本部分设计知识:
引脚复用:有时候芯片上的引脚资源不够用或是为了更高效率的利用引脚资源;就可能会用到引脚复用。物理上,同样的一组引脚,可以被设置(通过软件配置对应的寄存器去控制)为不同的功能,这样就可以实现可以根据实际需要,在不同的情况下,使用同一组引脚实现不同的功能。——source:elecfans.com
2.1.1 查找引脚功能定义
我们可以在 Klipper/src
文件夹内找到相关定义,这里以经典的 MKS Gen L v2.1
主板为例,其采用 16MHz 的 atmega2560 控制芯片。我们找到 klipper/src/avr/spi.c
,对应内容如下:
DECL_ENUMERATION("spi_bus", "spi", 0);
#if CONFIG_MACH_atmega168 || CONFIG_MACH_atmega328 || CONFIG_MACH_atmega328p
static const uint8_t MISO = GPIO('B', 4), MOSI = GPIO('B', 3);
static const uint8_t SCK = GPIO('B', 5), SS = GPIO('B', 2);
DECL_CONSTANT_STR("BUS_PINS_spi", "PB4,PB3,PB5");
#elif CONFIG_MACH_atmega644p || CONFIG_MACH_atmega1284p
static const uint8_t MISO = GPIO('B', 6), MOSI = GPIO('B', 5);
static const uint8_t SCK = GPIO('B', 7), SS = GPIO('B', 4);
DECL_CONSTANT_STR("BUS_PINS_spi", "PB6,PB5,PB7");
#elif CONFIG_MACH_at90usb1286 || CONFIG_MACH_at90usb646 \
|| CONFIG_MACH_atmega32u4 || CONFIG_MACH_atmega1280 \
|| CONFIG_MACH_atmega2560
static const uint8_t MISO = GPIO('B', 3), MOSI = GPIO('B', 2);
static const uint8_t SCK = GPIO('B', 1), SS = GPIO('B', 0);
DECL_CONSTANT_STR("BUS_PINS_spi", "PB3,PB2,PB1");
#endif
同时对照下图 Mega2560 的引脚图,可以确认它只有一个硬件 SPI 接口,名称 spi
,对应引脚为 PB3/PB2/PB1/PB0
。此外,我们后面会发现有些设备不止一个硬件 spi 接口,我们需要知道每个 spi 对应的名称和引脚。
注意:
- spi.c 的引脚顺序为:MISO/MOSI/SCK,SS引脚如果没有定义,则可以使用任意 GPIO 引脚。
2.1.2 如何找到 3D 打印机主板上的对应引脚
这个需要找到设备的引脚图(Pinout)和原理图(Schematic),而这方面赞一下创客基地,我们可以非常方便地在 GitHub 上查到 对应资料。
从这里可以看到,硬件 SPI 都位于 EXP2 拓展口,实际上,大多数主板的 EXP1/EXP2 拓展引脚都沿用了 Ramps 的标准,而这个 SPI 引脚就是给 SD 卡插槽使用的(部分 SD 卡使用 SDIO 接口)。所以我们可以使用这四个引脚来连接加速度计。接线方法其实我们在设置 LCD 屏幕时也见过。
# MKS GenL Onboard SPI via EXP2
[adxl345]
cs_pin: EXP2_4
# 启用硬件 SPI,注意名称要填对
spi_bus: spi
axes_map: x,y,z
# 启用软件模拟 SPI,请勿使用,仅用作引脚接法参考。
#spi_software_sclk_pin: EXP2_2 # SCK-SCL
#spi_software_mosi_pin: EXP2_6 # MOSI-SDA
#spi_software_miso_pin: EXP2_1 # MISO-SDO
注意:
- 即使连接的是硬件 SPI 的引脚,经测试发现,当定义
spi_software_*_pin
时,也是软件模拟的 SPI,传输速度和性能会变差,后面会具体讲
- ADXL345 加速度计一般支持 3.3v 和 5v 供电,需要根据主板信号电平决定,比如 Arduino 8bit 一般是5v,32位主板和树莓派一般是 3.3v,请勿接错避免损坏设备或者造成信号失真/噪音。采用 5v 电平信号时抗干扰能力会比 3.3v 更强一些。
- 加速度计的 xyz 轴请和打印机各轴平行,如果不对应请通过
axes_map:
参数进行映射,因为我们发现多数时候 xy 方向的共振频率不一致。但实际测试只要平行就行,TODO: 重新验证。
- 使用 MKS Robin Nano v2.x 主板的朋友请注意,官方给的 EXP2 引脚定义图有误,接线方式是一样的。
- 因为3D打印机主板工作繁忙,所以我们建议用 硬件SPI接口降低资源占用。相比树莓派有限的活动性,主板的位置基本固定,这种方法使得信号线过长,小机器可以试试。
- 加速度计的默认采样率是 3200Hz,同时还要通过 Serial 接口以默认最大 250000 波特率 与上位机通讯,对主板性能有一定要求,可能会系统超负载导致测试数据失真,32位主板会好些。
2.2 Arduino Uno/Nano 及树莓派 Pico
Uno/Nano 基于 avr atmega328p
,有 1 个硬件 SPI,好处是尺寸较小,价格便宜,放置位置自如,避免过长的连接线,缺点是 8bit 控制器,性能差。
上位替代可以选择 Raspberry Pi Pico,价格同样是25元左右,小巧,性能更强,引脚更多。可能会有朋友问,那个 ESP8266/ESP32 是不是更好,还有无线,好是好,但是 Klipper 还不支持呀!
具体方法可以参考: 曲线救国,arduino-nano刷klipper并使用加速度计 ,但是有几点需要说明:
- 配置文件里请不要使用文中所示的软件模拟 SPI,占用较多系统资源,并且会发生超载,占满 ADXL345 的 FIFO 区域。
- Arduino Nano 使用 5v 电平信号,不是 3.3v
- 加速度计方向要与 xyz 轴平行
# Arduino Uno 328p
[adxl345]
# 注意引脚需要 mcu 名称
cs_pin: 2nd:pb2
# 启用硬件 SPI,注意名称要填对,并且不需要添加 mcu 名称
spi_bus: spi
axes_map: x,y,z
2.3 STM32F103C8T6 最小系统板
原本准备买树莓派Pico 进行测试了,聊天之间,想到更具性价比的设备来了,就是 STM32F103 最小系统,它采用 F103C8T6/C6T6 芯片,32bit,72MHz 时钟频率,价格更低,性能更强。首先来看看 Klipper 对于 STM32 的 引脚定义:
DECL_ENUMERATION("spi_bus", "spi2", 0);
DECL_CONSTANT_STR("BUS_PINS_spi2", "PB14,PB15,PB13");
DECL_ENUMERATION("spi_bus", "spi1", 1);
DECL_CONSTANT_STR("BUS_PINS_spi1", "PA6,PA7,PA5");
DECL_ENUMERATION("spi_bus", "spi1a", 2);
DECL_CONSTANT_STR("BUS_PINS_spi1a", "PB4,PB5,PB3");
#if CONFIG_MACH_STM32F0 || CONFIG_MACH_STM32F2 || CONFIG_MACH_STM32F4
DECL_ENUMERATION("spi_bus", "spi2a", 3);
DECL_CONSTANT_STR("BUS_PINS_spi2a", "PC2,PC3,PB10");
#endif
#ifdef SPI3
DECL_ENUMERATION("spi_bus", "spi3", 4);
DECL_CONSTANT_STR("BUS_PINS_spi3", "PB4,PB5,PB3");
#if CONFIG_MACH_STM32F4
DECL_ENUMERATION("spi_bus", "spi3a", 5);
DECL_CONSTANT_STR("BUS_PINS_spi3a", "PC11,PC12,PC10");
#ifdef SPI4
DECL_ENUMERATION("spi_bus", "spi4", 6);
DECL_CONSTANT_STR("BUS_PINS_spi4", "PE13,PE14,PE12");
#elif defined(GPIOI)
DECL_ENUMERATION("spi_bus", "spi2b", 6);
DECL_CONSTANT_STR("BUS_PINS_spi2b", "PI2,PI3,PI1");
#endif
#endif
#endif
可以看到 STM32 拥有多个硬件 SPI 接口,在最小系统上可以根据需求和对照原理图选择,目前看来是一个理想选择,但是问题来了,最小系统是白板,也没有 SD卡插槽,如何给他刷入 Klipper 固件呢?
3、为 STM32 主板刷入 bootloader 及 firmware
想要在 2nd MCU 上使用加速度计,需要刷入 Klipper 固件。这里主要参考 Klipper 文档 和 OctoPrint-FirmwareUpdater 。
STM32单片机支持3种程序下载方式,根据原理不同可分为ISP串口下载(使用USB-TTL接PA9、PA10)、SWD下载(使用ST-LINK接PA13、PA14)、JTAG下载(使用JLINK接PA13、PA14、PA15、PB3、PB4)。
- BOOT0 为独立引脚
- BOOT1 默认是 PB2 引脚
- 3.3v serial 默认是 PA9 |Tx1 和 PA10 |Rx1
- 此外 F103 和 F4 有一些区别,后者默认支持 USB DFU 模式。
3.1 STM32F103C8T6 最小系统烧录方式
今天的主角我们上面介绍过了。F103 出厂自带 ROM (只读存储器),支持通过 3.3v 串口为其刷入引导器(bootloader)和程序(application)。一般的 3D打印机主板都支持 microSD 卡的方式烧录/更新固件(卡刷),但是我们手头的最小系统没有板载 mSD 卡插槽,只能另想办法。F103 可以通过串口和USB模式烧录固件,两者选一即可。
3.1.1 使用 3.3v Serial 接口烧录
- 支持 bootloader 和固件烧写
- 进入 Serial 烧录模式的方法是
boot0
置高电位,boot1
置低电位,然后重置设备(重新上电或者按下 Reset 键)
- 可以使用 stm32flash 工具进行刷写,该工具支持 Windows/Linux/MacOS 等系统
- 硬件上需要 USB转TTL 模块(注意选择 3.3v 电平);或者使用树莓派等具有 UART 通讯能力的设备,不过需要 进行一些设置 ,见下文。
- 刷入完毕后,需要将
boot0
和boot1
置低电位以进入程序(例如 Klipper 固件)。
- 编译 Klipper 固件时请选择
No bootloader
sudo stm32flash -w ~/klipper/out/klipper.bin -v -g 0 /dev/ttyAMA0
3.1.2 使用 USB 接口烧录(需要 bootloader)
由于我们上面说过,F103 默认不支持 USB 方式烧录,需要先使用 Serial 模式烧入其他引导器才可以,这里可以尝试 stm32duino bootloader 。
-
注意 stm32duino-bootloader 关联 Arduino_STM32
,和 Arduino IDE 提供的 Arduino_Core_STM32
是不一样的。
-
查看原理图可知,本最小系统与 Blue Pill
开发板一样,板载LED接在PC13引脚。所以下载 bootloader 文件:bootloader_only_binaries/generic_boot20_pc13.bin,放入下面的 stm32flash 文件夹内
-
如图所示设置 boot0 和 boot1(上面的是boot0,下面的是boot1)
-
通过 USB-TTL 串口模块等连接最小系统。接线方式如下:
TTL模块 |
3.3v |
GND |
RX |
TX |
最小系统 |
3.3v |
GND |
PA9(TX) |
PA10(RX) |
-
使用 cmd/terminal
进入前面下载解压的 stm32flash 目录,执行以下命令:
.\stm32flash.exe -w generic_boot20_pc13.bin -v -g 0 COM11
# 注意 COM11 改成你的串口设备号,可以在设备管理器查看
# Linux下的串口设备可能是 /dev/ttyAMA0 等
# 注意关闭 Cura 等自动占用串口设备的程序
-
恢复boot0和boot1置低,重新上电即可以看到绿色led灯点亮,通过usb数据线插入电脑,可以识别到新设备,此时代表引导器安装成功,可以通过usb刷写程序了。
3.1.2.1 进入 stm32duino-bootloader 方法
此引导器会在上电时,会自动进入并保持一小段时间,此时对应的板载LED会闪烁,请及时刷入固件。如果想要启动后停留在引导器,请把 boot0
置低,boot1
置高的方式或者也可以通过 USBSerial MagicWord 进入。此方式可能需要安装驱动。
- USB-TTL 串口调试模块设置电平为 3.3v
- 确认接线,正负极一定不要接反
- 确认 STM32 Boot0/Boot1 跳帽设置,如此进入 ISP 烧录模式
- 插入串口模块,使用此命令烧录 bootloader,之后就可以使用USB烧录,不需要再用到杜邦线和串口模块,(也可以在此步同时烧录固件)
- 跳帽双低,然后microUSB上电一次,会短暂进入bootloader然后加载程序,表现是刚插上的时候绿灯会短暂闪烁,此时可以刷写固件
- 断开连接,跳帽设置反过来,用USB数据线接到派上面,此时启动后会停留在 BL,绿灯一直闪,使用以下命令烧录固件,拔掉数据线,跳帽恢复双低,上电即可以进入 Klipper
3.1.2.2 使用USB刷入 Klipper 固件
这一步可以从树莓派上编译并上传或者下载编译好的固件从 Windows 系统烧录。由于 stm32duino bootloader
占用 8KB 空间,编译的时候选择起始地址 8KB,此外最小系统晶振为 8MHz。
# 需要root权限,设备id是固定的
sudo dfu-util -d 1eaf:0003 -a 2 -R -D ~/klipper/out/klipper.bin
3.1.2.3 另一个选择:HID-bootloader
这是支持 USB 的另一个引导器,占用空间更小(2KB),并且不需要安装驱动(识别为HID设备)。可以通过 3.3v Serial 刷入,方法同上。对于某些主板,无法改变 boot0
状态的,则可以尝试 STLinkv2 + STM32CubeProgrammer 烧录;或是使用树莓派 + OpenOCD。
注意,使用上述任意引导器,会导致原来的SD卡更新的功能失效,如有需要,请事先备份或者从官方下载旧引导器。
3.2 STM32F4 烧录方式
- F4出厂默认支持 3.3v Serial 和 USB 烧录模式(DFU),后者需要安装驱动。
- 采用默认方式(Serial/DFU)烧录固件,编译选择
No bootloader
- stm32duino-bootloader 不支持 STM32F4
- 可以使用 HID-bootloader,编译选择
HID Bootloader
,主要针对不支持 DFU 模式的主板。其他操作同上。
- 可以参考 STM32F405 DFU更新固件_嵌入式技术在路上-CSDN博客
# 使用 HID-Bootloader 烧录固件,注意偏移16KB
sudo dfu-util -d 0483:df11 -a 0 -R -s 0x08004000 -D ~/klipper/out/klipper.bin
总结
相信到这里,朋友们已经学会如何在最小系统上连接加速度计了,下文我们会测试加速度计,探讨 UART 通讯会不会是共振频率测试的瓶颈,这也关系到我们这种 2ndMCU 方案是否有实际应用价值。