568 字
3 分钟
[onstep] OnStep项目SPI通信实现总结

引言#

OnStep 系统在 ESP32 平台上与 TMC 驱动器交互时,未直接使用硬件 SPI,而是采用了软件模拟(Bit-Banging)方案。本文通过分析源码记录其底层实现细节及设计原因。

1. 核心机制:软件模拟 SPI#

TIP

OnStep 通过 bbspi 类手动控制 GPIO 电平翻转,模拟标准的串行移位过程。

1.1 实现逻辑#

核心逻辑位于 src/lib/SoftSPI.h 中。除了单字节传输,它还提供了高效的 32 位传输函数:

// 摘自 src/lib/SoftSPI.h
uint32_t bbspi::transfer32(uint32_t data_out) {
uint32_t data_in = 0;
for(int i=31; i >= 0; i--) {
digitalWrite(_sck, LOW);
digitalWrite(_mosi, bitRead(data_out, i));
delaySPI;
digitalWrite(_sck, HIGH);
if (_miso >= 0) bitWrite(data_in, i, digitalRead(_miso));
delaySPI;
}
return data_in;
}

2. 协议参数与时序#

  • SPI 模式: 源码注释明确为 Mode 3 (CPOL=1, CPHA=1)。
  • 时钟空闲: digitalWrite(_sck, HIGH)
  • 通信频率: 由 delaySPI 宏控制。在 src/HAL/ESP32/ESP32.h 中:
#define delaySPI delayNanoseconds(500)
NOTE

单次位翻转包含两次 delaySPI,实际 SPI 频率约为 1MHz。这在保障长线缆抗干扰性的同时,满足了驱动器配置的需求。

3. 数据封包:TMC 40-bit 结构#

针对 TMC 驱动器,单次读写由 1 字节地址和 4 字节数据组成。src/lib/TMC_SPI.h 实现了具体的封包逻辑:

3.1 写操作实现#

// 摘自 src/lib/TMC_SPI.h
uint8_t tmcSpiDriver::write(byte Address, uint32_t data_out) {
Address = Address | 0x80; // 第 1 位设为 1 表示写操作
uint8_t status_byte = BBSpi.transfer(Address); // 发送地址,获取状态字节
BBSpi.transfer32(data_out); // 发送 32 位配置数据
return status_byte;
}

3.2 读操作实现#

uint8_t tmcSpiDriver::read(byte Address, uint32_t* data_out) {
Address = Address & ~0x80; // 第 1 位设为 0 表示读操作
uint8_t status_byte = BBSpi.transfer(Address);
*data_out = BBSpi.transfer32(*data_out);
return status_byte;
}

4. 工程设计考量#

IMPORTANT
  1. 确定性定时: 软件模拟 SPI 避免了硬件 SPI 中断或 DMA 可能对电机脉冲生成(ISR)造成的抖动干扰。
  2. 引脚灵活性: 允许在 Config.h 中映射至任意可用 GPIO,不依赖硬件 SPI 专用引脚。
  3. 一致性: bbspi 类可无缝跨平台(AVR, STM32, ESP32)运行,降低了维护成本。

总结#

OnStep 的 SPI 实现体现了工程上的资源取舍:通过放弃极高的传输速度,换取了系统的实时确定性与极强的硬件适配性。这种设计对于需要精确定时和支持多样化 DIY 硬件的天文跟踪系统至关重要。

[onstep] OnStep项目SPI通信实现总结
https://www.eustia-astraea.top/posts/onstep/onstep-spi-analysis/
作者
mcsl
发布于
2025-07-12
许可协议
CC BY-NC-SA 4.0