要将两个 MPU(如 STM32F5/L5 系列)通过 SPI 全双工模式连接以实现 RAM 镜像,需结合 SPI 和 DMA 技术。以下是步骤和代码示例:
1. 硬件连接
- 主 MPU (SPI Master) ↔ 从 MPU (SPI Slave):
MOSI (主出从入) ↔ MOSI
MISO (主入从出) ↔ MISO
SCK (时钟) ↔ SCK
CS (片选) ↔ CS (主控手动拉低/拉高)
2. 主 MPU 配置(发送数据)
SPI 初始化
// 启用 SPI 时钟和 DMA 时钟
RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;
RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN;
// SPI 配置为全双工主模式
SPI_HandleTypeDef hspi;
hspi.Instance = SPI1;
hspi.Init.Mode = SPI_MODE_MASTER;
hspi.Init.Direction = SPI_DIRECTION_2LINES;
hspi.Init.DataSize = SPI_DATASIZE_8BIT;
hspi.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi.Init.CLKPhase = SPI_PHASE_1EDGE;
hspi.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8; // 根据需求调整
HAL_SPI_Init(&hspi);
DMA 配置(发送数据块)
// 源地址:待发送的 RAM 区域
// 目标地址:SPI 数据寄存器
uint8_t *source_addr = (uint8_t*)0x20000000; // 示例 RAM 地址
uint32_t data_size = 1024; // 传输字节数
DMA_HandleTypeDef hdma_tx;
hdma_tx.Instance = DMA2_Stream3;
hdma_tx.Init.Channel = DMA_CHANNEL_3;
hdma_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
hdma_tx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_tx.Init.MemInc = DMA_MINC_ENABLE;
hdma_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_tx.Init.Mode = DMA_NORMAL; // 非循环模式
HAL_DMA_Init(&hdma_tx);
__HAL_LINKDMA(&hspi, hdmatx, hdma_tx);
// 启动 SPI DMA 传输
HAL_SPI_Transmit_DMA(&hspi, source_addr, data_size);
3. 从 MPU 配置(接收并写入 RAM)
SPI 初始化
// 从 MPU 配置为 SPI 从模式
SPI_HandleTypeDef hspi_slave;
hspi_slave.Instance = SPI2;
hspi_slave.Init.Mode = SPI_MODE_SLAVE;
hspi_slave.Init.Direction = SPI_DIRECTION_2LINES;
hspi_slave.Init.DataSize = SPI_DATASIZE_8BIT;
hspi_slave.Init.CLKPolarity = SPI_POLARITY_LOW; // 与主设备一致
hspi_slave.Init.CLKPhase = SPI_PHASE_1EDGE;
HAL_SPI_Init(&hspi_slave);
DMA 配置(接收数据到指定 RAM)
// 目标地址:镜像 RAM 区域
uint8_t *mirror_addr = (uint8_t*)0x20000000; // 与主设备相同地址
uint32_t data_size = 1024;
DMA_HandleTypeDef hdma_rx;
hdma_rx.Instance = DMA1_Stream0;
hdma_rx.Init.Channel = DMA_CHANNEL_0;
hdma_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_rx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_rx.Init.MemInc = DMA_MINC_ENABLE;
hdma_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_rx.Init.Mode = DMA_NORMAL;
HAL_DMA_Init(&hdma_rx);
__HAL_LINKDMA(&hspi_slave, hdmarx, hdma_rx);
// 启动 SPI DMA 接收(从设备被动接收)
HAL_SPI_Receive_DMA(&hspi_slave, mirror_addr, data_size);
4. 传输流程
- 启动从设备 DMA:
- 从设备提前调用
HAL_SPI_Receive_DMA() 等待数据。
- 主设备发起传输:
- 拉低 CS 引脚(片选)。
- 调用
HAL_SPI_Transmit_DMA() 发送数据。
- 数据传输:
- DMA 自动将主设备 RAM 数据通过 SPI 发送。
- 从设备 SPI 接收数据,DMA 自动写入其 RAM。
- 传输完成:
- 主/从设备通过 DMA 完成中断(或轮询标志位)通知结束。
- 主设备拉高 CS 引脚。
5. 关键注意事项
- 时钟同步:主从设备 SPI 时钟配置(极性/相位)必须一致。
- 内存地址:确保主/从设备的镜像 RAM 地址在各自系统中有效。
- DMA 通道:根据具体 MPU 型号选择未冲突的 DMA 流(Stream)和通道(Channel)。
- 数据对齐:
MemDataAlignment 需与数据宽度匹配(如 8/16 位)。
- 中断处理:
- 实现
HAL_SPI_TxCpltCallback()(主)和 HAL_SPI_RxCpltCallback()(从)处理完成事件。
- 全双工优化:
- 若需要主从双向同步,使用
HAL_SPI_TransmitReceive_DMA() 同时收发数据。
6. I2C 替代方案(不推荐)
虽然 I2C 支持多主模式,但半双工特性和较低速度不适合 RAM 镜像:
// I2C 示例(主设备写入从设备 RAM)
HAL_I2C_Mem_Write(&hi2c, SLAVE_ADDR, MEM_ADDR, I2C_MEMADD_SIZE_16BIT, data, size, 100);
- 缺点:需指定从设备地址和内存地址,且速度通常低于 SPI DMA。
通过以上步骤,SPI 全双工 + DMA 能高效实现两个 MPU 之间的 RAM 数据镜像。确保硬件连接正确,并参考 MPU 的数据手册调整 SPI/DMA 寄存器配置。