3. 坐标系与姿态运动学
硬件接口设计这块,说白了就是让芯片的SPI模块和外面的传感器、存储器能正常“对话”。很多新手上来就写代码,结果发现数据死活不对——大概率是引脚没配好,或者电气特性没搞明白。我刚开始做项目时也踩过这个坑,后来才意识到,硬件接口才是SPI通信的基石。
3.1 STM32的SPI外设框图解读
先看STM32。它的SPI外设框图,我习惯把它拆成三个部分来看:
- 时钟生成单元:由波特率发生器、时钟极性和相位控制组成。说白了就是决定SCK的频率和波形。
- 数据移位单元:包含8/16位移位寄存器和收发缓冲区。数据就是在这里被串行化或并行化的。
- 控制与状态单元:包括主从模式选择、中断/DMA请求、各种标志位(TXE、RXNE、BSY等)。
我记得有一次调试一个AD7793的SPI通信,数据总是错位。后来查了STM32的参考手册才发现,它的SPI数据寄存器是16位的,而AD7793要求8位数据。嗯,这里要注意:STM32的SPI_DR寄存器在16位模式下,读写操作必须对齐。
关键点:STM32的SPI外设支持主模式和从模式,但大多数传感器应用我们都用主模式。从模式一般用于多机通信或特殊场景。
3.2 ESP32的SPI外设框图解读
ESP32的SPI外设和STM32不太一样。它有三个SPI控制器:SPI0、SPI1和SPI2(也叫HSPI和VSPI)。其中SPI0和SPI1是内部使用的,我们能用的是SPI2。
ESP32的SPI框图有个特点:它支持DMA直连。这意味着大数据传输时,CPU可以解放出来干别的事。我在做摄像头图像采集时就用到了这个特性,效果确实不错。
| 特性 | STM32 SPI | ESP32 SPI |
|---|---|---|
| 控制器数量 | 1-6个(视型号) | 3个(2个可用) |
| 数据位宽 | 8/16位 | 8/16/32/64位 |
| DMA支持 | 可选 | 内置 |
| 时钟频率 | 最高fPCLK/2 | 最高80MHz |
| 多片选支持 | 软件控制 | 硬件支持 |
个人经验:ESP32的SPI2默认引脚是GPIO18(SCK)、GPIO19(MISO)、GPIO23(MOSI)、GPIO5(CS)。但你可以通过GPIO矩阵映射到任意引脚——这个灵活性是STM32没有的。
3.3 GPIO复用功能配置
配置SPI引脚,说白了就是告诉芯片:“这几个引脚别当普通IO用,给我当SPI用”。
STM32的配置方式:
// 以STM32F4为例,配置SPI1引脚
GPIO_InitTypeDef GPIO_InitStruct = {0};
// 使能GPIO时钟
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_SPI1_CLK_ENABLE();
// 配置PA5(SCK), PA6(MISO), PA7(MOSI)为复用功能
GPIO_InitStruct.Pin = GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF5_SPI1;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
ESP32的配置方式:
// 使用Arduino框架配置SPI引脚
SPI.begin(18, 19, 23, 5); // SCK, MISO, MOSI, CS
// 或者使用ESP-IDF方式
spi_bus_config_t buscfg = {
.mosi_io_num = 23,
.miso_io_num = 19,
.sclk_io_num = 18,
.quadwp_io_num = -1,
.quadhd_io_num = -1,
.max_transfer_sz = 4096
};
spi_bus_initialize(SPI2_HOST, &buscfg, SPI_DMA_CH_AUTO);
避坑指南:我曾经在STM32上把SPI引脚配成了推挽输出而不是复用推挽,结果SCK波形完全不对。记住:SPI的SCK、MOSI必须配成复用功能,MISO配成复用输入。
3.4 SPI引脚电气特性与上拉电阻选择
电气特性这块,很多人容易忽略。我直接说结论:
- SCK和MOSI:输出引脚,不需要上拉。但如果你担心浮空,可以加10kΩ下拉。
- MISO:输入引脚,建议加10kΩ上拉。特别是从设备不驱动MISO时,防止浮空导致误触发。
- CS:片选信号,必须加10kΩ上拉。防止在设备未选中时浮空,导致误通信。
| 引脚 | 方向 | 推荐上拉 | 说明 |
|---|---|---|---|
| SCK | 输出 | 不需要 | 主设备驱动,电平确定 |
| MOSI | 输出 | 不需要 | 主设备驱动,电平确定 |
| MISO | 输入 | 10kΩ上拉 | 防止浮空,提高抗干扰 |
| CS | 输出 | 10kΩ上拉 | 防止误选中从设备 |
个人习惯:我一般会在CS和MISO上各加一个10kΩ上拉电阻。虽然STM32和ESP32内部都有上拉,但外部电阻更可靠,特别是长线传输时。
3.5 PCB布局注意事项
PCB布局这块,我踩过的坑比代码还多。直接上干货:
- 信号线等长:SCK、MOSI、MISO这三根线尽量等长,特别是高速SPI(>10MHz)。长度差不要超过信号上升时间的1/10。
- 避免直角走线:直角会导致阻抗突变,产生反射。用45度角或圆弧过渡。
- 地平面完整:SPI信号线下面要有完整的地平面。不要跨分割,否则信号质量会变差。
- 去耦电容:每个SPI设备附近放一个0.1μF去耦电容,靠近电源引脚放置。
- 远离干扰源:SPI线不要靠近晶振、DC-DC转换器、大电流走线。
我曾经做过一个项目,SPI通信距离约15cm,用了杜邦线连接。结果10MHz下数据全是错的。后来换成屏蔽线,并在两端加了33Ω串联电阻,问题才解决。长线传输时,串联电阻可以抑制振铃。
3.6 知识结构总览
下面这张图总结了本章的核心内容,你可以把它当作硬件设计的检查清单:
硬件接口设计这块,说白了就是三个字:配、拉、布。配好引脚复用,拉好上拉电阻,布好PCB走线。这三步做好了,SPI通信就成功了一大半。