1. GD32F103 SPIDMA配置基础GD32F103作为国产32位微控制器的代表其外设设计与STM32高度兼容但在库函数命名和细节处理上存在一些差异。初次接触GD32的开发者可能会被其外设编号混乱的问题困扰——比如手册中的SPI0对应代码中的SPI1这种从0开始还是从1开始的混乱命名需要特别注意。SPISerial Peripheral Interface是一种全双工同步串行通信接口在嵌入式系统中广泛用于连接显示屏、存储芯片、传感器等外设。而DMADirect Memory Access则可以在不占用CPU资源的情况下完成内存与外设之间的数据传输大幅提升系统效率。两者结合使用时SPI负责物理层通信DMA负责数据搬运CPU只需初始化配置即可处理其他任务。在实际项目中我遇到过SPIDMA配置的几个典型应用场景驱动高速SPI TFT显示屏需要持续刷新显示内容与高精度ADC芯片通信采集大量传感器数据实现高速数据记录将采集的数据通过SPI接口写入存储设备2. SPI外设初始化详解2.1 GPIO引脚配置SPI通信需要正确配置相关GPIO引脚的工作模式。以SPI1为例通常使用PA4作为片选(CS)、PA5作为时钟(SCK)、PA6作为主输入从输出(MISO)、PA7作为主输出从输入(MOSI)。这里有个细节需要注意GD32的GPIO初始化结构与STM32略有不同但功能相似。// SPI引脚配置示例 GPIO_InitPara GPIO_InitStructure; rcu_periph_clock_enable(RCU_GPIOA); rcu_periph_clock_enable(RCU_SPI0); // 注意这里是SPI0 // SCK和MOSI配置为复用推挽输出 GPIO_InitStructure.GPIO_Pin GPIO_PIN_5 | GPIO_PIN_7; GPIO_InitStructure.GPIO_Mode GPIO_MODE_AF_PP; GPIO_InitStructure.GPIO_Speed GPIO_SPEED_50MHZ; GPIO_Init(GPIOA, GPIO_InitStructure); // MISO配置为浮空输入 GPIO_InitStructure.GPIO_Pin GPIO_PIN_6; GPIO_InitStructure.GPIO_Mode GPIO_MODE_IN_FLOATING; GPIO_Init(GPIOA, GPIO_InitStructure); // CS引脚配置为普通推挽输出 GPIO_InitStructure.GPIO_Pin GPIO_PIN_4; GPIO_InitStructure.GPIO_Mode GPIO_MODE_OUT_PP; GPIO_Init(GPIOA, GPIO_InitStructure);2.2 SPI参数设置SPI初始化时需要关注几个关键参数传输类型全双工、半双工或只收/只发模式主从模式选择数据帧格式8位或16位时钟极性和相位波特率预分频系数数据位顺序MSB/LSB优先SPI_InitPara SPI_InitStructure; SPI_InitStructure.SPI_TransType SPI_TRANSTYPE_FULLDUPLEX; SPI_InitStructure.SPI_Mode SPI_MODE_MASTER; SPI_InitStructure.SPI_FrameFormat SPI_FRAMEFORMAT_8BIT; SPI_InitStructure.SPI_SCKPL SPI_SCKPL_LOW; // 时钟极性 SPI_InitStructure.SPI_SCKPH SPI_SCKPH_1EDGE; // 时钟相位 SPI_InitStructure.SPI_SWNSSEN SPI_SWNSS_SOFT; // 软件控制片选 SPI_InitStructure.SPI_PSC SPI_PSC_4; // 波特率预分频 SPI_InitStructure.SPI_FirstBit SPI_FIRSTBIT_MSB; SPI_Init(SPI1, SPI_InitStructure); // 注意这里变成了SPI1 SPI_Enable(SPI1, ENABLE);3. DMA控制器配置实战3.1 DMA通道映射关系GD32F103的DMA控制器与SPI外设的映射关系需要特别注意。根据我的实测经验外设发送通道接收通道SPI0DMA0-CH3DMA0-CH2SPI1DMA0-CH5DMA0-CH4配置DMA时发送和接收需要分别初始化两个独立的通道。这里有个坑点GD32的DMA通道编号在代码中是从1开始计数的而手册上是从0开始这点特别容易混淆。3.2 DMA初始化步骤DMA初始化主要包含以下步骤使能DMA时钟复位DMA通道配置外设和内存地址设置数据传输方向配置数据宽度和地址增量模式设置传输模式普通/循环配置优先级使能DMA通道void SPI1DMAInit(void) { DMA_InitPara DMA_InitStructure; rcu_periph_clock_enable(RCU_DMA0); // 发送DMA配置 DMA_DeInit(DMA1_CHANNEL3); // 注意通道编号差异 DMA_InitStructure.DMA_PeripheralBaseAddr (uint32_t)(SPI1-DTR); DMA_InitStructure.DMA_MemoryBaseAddr 0; // 实际地址在传输时设置 DMA_InitStructure.DMA_DIR DMA_DIR_PERIPHERALDST; DMA_InitStructure.DMA_BufferSize 0; DMA_InitStructure.DMA_PeripheralInc DMA_PERIPHERALINC_DISABLE; DMA_InitStructure.DMA_MemoryInc DMA_MEMORYINC_ENABLE; DMA_InitStructure.DMA_PeripheralDataSize DMA_PERIPHERALDATASIZE_BYTE; DMA_InitStructure.DMA_MemoryDataSize DMA_MEMORYDATASIZE_BYTE; DMA_InitStructure.DMA_Mode DMA_MODE_NORMAL; DMA_InitStructure.DMA_Priority DMA_PRIORITY_MEDIUM; DMA_Init(DMA1_CHANNEL3, DMA_InitStructure); // 接收DMA配置 DMA_DeInit(DMA1_CHANNEL2); DMA_InitStructure.DMA_DIR DMA_DIR_PERIPHERALSRC; DMA_Init(DMA1_CHANNEL2, DMA_InitStructure); }4. 数据收发实战与优化4.1 启动DMA传输启动DMA传输时需要特别注意顺序问题。我的经验是先配置接收DMA再配置发送DMA这样可以确保接收缓冲区准备就绪后再开始发送数据。此外传输完成后需要检查DMA标志位并清除中断标志。void SPI1TxDMAEnable(uint32_t mem_addr, uint32_t buf_size) { // 先禁用DMA通道 DMA_Enable(DMA1_CHANNEL3, DISABLE); DMA_Enable(DMA1_CHANNEL2, DISABLE); // 等待DMA禁用完成 while(DMA_GetCmdStatus(DMA1_CHANNEL3)); while(DMA_GetCmdStatus(DMA1_CHANNEL2)); // 配置接收DMA DMA_SetCurrDataCounter(DMA1_CHANNEL2, buf_size); DMA_MemoryTargetConfig(DMA1_CHANNEL2, mem_addr); // 配置发送DMA DMA_SetCurrDataCounter(DMA1_CHANNEL3, buf_size); DMA_MemoryTargetConfig(DMA1_CHANNEL3, mem_addr); // 使能SPI DMA请求 SPI_I2S_DMA_Enable(SPI1, SPI_I2S_DMA_RX, ENABLE); SPI_I2S_DMA_Enable(SPI1, SPI_I2S_DMA_TX, ENABLE); // 使能DMA通道 DMA_Enable(DMA1_CHANNEL2, ENABLE); DMA_Enable(DMA1_CHANNEL3, ENABLE); // 等待传输完成 while(!DMA_GetIntBitState(DMA1_INT_TC2)); DMA_ClearIntBitState(DMA1_INT_TC2); while(!DMA_GetIntBitState(DMA1_INT_TC3)); DMA_ClearIntBitState(DMA1_INT_TC3); }4.2 常见问题排查在实际项目中SPIDMA配置容易遇到以下几个典型问题数据错位问题通常是由于时钟极性和相位配置不正确导致的。建议先用示波器观察SCK和MOSI信号确保时序符合外设要求。DMA传输不启动检查DMA通道是否使能、SPI的DMA请求是否使能、内存地址是否对齐。GD32对内存地址对齐有严格要求特别是使用16位数据宽度时。数据覆盖问题全双工模式下发送和接收是同步进行的。虽然代码中使用了相同的内存地址但在数据被覆盖前已经发送出去了这是正常现象。传输速度不达标检查SPI时钟分频设置确保没有配置过大的分频系数。同时确认GPIO速度设置为最高速50MHz。