GD32F103 SPI全双工通信实战从硬件配置到代码调试全解析刚拿到GD32开发板时最让人头疼的就是外设配置。特别是对于从STM32转过来的开发者虽然两者相似但细节差异往往让人踩坑。SPI作为高速通信的利器在传感器、存储芯片等场景中应用广泛。本文将用一块GD32F103C8T6开发板带你完整实现主机与从机的全双工通信过程中会特别标注与STM32的差异点。1. 硬件连接与SPI基础SPI通信需要四根基础线NSS片选、SCK时钟、MOSI主机输出从机输入和MISO主机输入从机输出。在GD32F103C8T6上SPI0的默认引脚映射如下引脚功能引脚编号NSSPA4SCKPA5MISOPA6MOSIPA7关键点注意硬件NSS和软件NSS的选择硬件NSS可以自动控制片选信号减少CPU干预时钟极性CKP和相位CPHA必须主机从机一致GD32的SPI最高时钟可达18MHz但实际使用需考虑线路质量和传输距离与STM32的主要差异库函数前缀从SPI_变为spi_部分寄存器位定义不同例如GD32的SPI_CTL0寄存器中的位排列2. 主机端配置详解主机配置是通信的发起方需要正确设置时钟和通信模式。以下是完整的初始化代码void SPI_Master_Init(void) { // 开启GPIO和SPI时钟 rcu_periph_clock_enable(RCU_GPIOA); rcu_periph_clock_enable(RCU_SPI0); // 配置GPIO为复用功能 gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_7); // NSS,SCK,MOSI gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_6); // MISO // SPI参数配置 spi_parameter_struct spi_init_struct {0}; spi_struct_para_init(spi_init_struct); spi_init_struct.trans_mode SPI_TRANSMODE_FULLDUPLEX; spi_init_struct.device_mode SPI_MASTER; spi_init_struct.frame_size SPI_FRAMESIZE_8BIT; spi_init_struct.clock_polarity_phase SPI_CK_PL_LOW_PH_1EDGE; spi_init_struct.nss SPI_NSS_HARD; spi_init_struct.prescale SPI_PSC_8; // 系统时钟108MHz8分频后13.5MHz spi_init_struct.endian SPI_ENDIAN_MSB; spi_init(SPI0, spi_init_struct); spi_nss_output_enable(SPI0); // 使能硬件NSS输出 spi_enable(SPI0); // 使能SPI }配置要点解析时钟分频prescale决定了通信速率GD32F103的SPI时钟源是APB2总线时钟通常108MHz时钟极性和相位clock_polarity_phaseSPI_CK_PL_LOW_PH_1EDGE表示空闲时时钟为低数据在第一个边沿采样这是最常用的模式与多数SPI设备兼容硬件NSS使能后发送时会自动拉低NSS发送完成自动拉高3. 从机端配置关键点从机配置与主机类似但有几点特别注意void SPI_Slave_Init(void) { // 开启GPIO和SPI时钟 rcu_periph_clock_enable(RCU_GPIOB); rcu_periph_clock_enable(RCU_SPI1); // 配置GPIO - 注意从机的NSS是输入 gpio_init(GPIOB, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_14); // MISO gpio_init(GPIOB, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_15); // NSS,SCK,MOSI // SPI参数配置 spi_parameter_struct spi_init_struct {0}; spi_struct_para_init(spi_init_struct); spi_init_struct.trans_mode SPI_TRANSMODE_FULLDUPLEX; spi_init_struct.device_mode SPI_SLAVE; // 从机模式 spi_init_struct.frame_size SPI_FRAMESIZE_8BIT; spi_init_struct.clock_polarity_phase SPI_CK_PL_LOW_PH_1EDGE; spi_init_struct.nss SPI_NSS_HARD; // 硬件NSS spi_init_struct.prescale SPI_PSC_8; // 从机模式下分频设置不影响实际时钟 spi_init_struct.endian SPI_ENDIAN_MSB; spi_init(SPI1, spi_init_struct); spi_enable(SPI1); // 使能SPI }从机特殊注意事项从机的SCK由主机提供所以分频系数设置不影响实际通信速率从机的NSS必须配置为输入由主机控制从机的MISO需要配置为输出其他引脚为输入4. 全双工通信实现与调试全双工通信允许主机和从机同时收发数据。下面是一个完整的通信示例void SPI_FullDuplex_Exchange(void) { uint8_t master_tx[10] {0xA1,0xB2,0xC3,0xD4,0xE5,0xF6,0x07,0x18,0x29,0x3A}; uint8_t master_rx[10] {0}; uint8_t slave_tx[10] {0x1A,0x2B,0x3C,0x4D,0x5E,0x6F,0x70,0x81,0x92,0xA3}; uint8_t slave_rx[10] {0}; // 主从机同时收发 for(int i0; i10; i){ // 从机准备发送数据 while(spi_i2s_flag_get(SPI1, SPI_FLAG_TBE) RESET); spi_i2s_data_transmit(SPI1, slave_tx[i]); // 主机发送并接收 while(spi_i2s_flag_get(SPI0, SPI_FLAG_TBE) RESET); spi_i2s_data_transmit(SPI0, master_tx[i]); // 从机接收 while(spi_i2s_flag_get(SPI1, SPI_FLAG_RBNE) RESET); slave_rx[i] spi_i2s_data_receive(SPI1); // 主机接收 while(spi_i2s_flag_get(SPI0, SPI_FLAG_RBNE) RESET); master_rx[i] spi_i2s_data_receive(SPI0); } // 打印收发结果 printf(Master Received: ); for(int i0; i10; i) printf(%02X , master_rx[i]); printf(\nSlave Received: ); for(int i0; i10; i) printf(%02X , slave_rx[i]); printf(\n); }调试技巧先用逻辑分析仪或示波器检查SCK、MOSI、MISO信号如果通信失败首先检查时钟极性和相位是否匹配NSS信号是否正确线序是否正确MOSI接MOSI不要交叉降低时钟频率测试排除信号完整性问题检查电源和地线连接SPI对共地要求严格5. 常见问题与性能优化问题1数据错位或全为0xFF/0x00检查时钟极性和相位设置确认MISO/MOSI没有接反检查从机是否被正确使能问题2通信速度不达标确认APB2时钟配置正确尝试减小分频系数检查PCB走线过长或过近的走线会影响信号质量性能优化建议使用DMA传输减少CPU开销// 配置SPI DMA dma_parameter_struct dma_init_struct; dma_struct_para_init(dma_init_struct); dma_init_struct.direction DMA_MEMORY_TO_PERIPHERAL; dma_init_struct.memory_addr (uint32_t)tx_buffer; dma_init_struct.memory_inc DMA_MEMORY_INCREASE_ENABLE; dma_init_struct.memory_width DMA_MEMORY_WIDTH_8BIT; dma_init_struct.number BUFFER_SIZE; dma_init_struct.periph_addr (uint32_t)SPI_DATA(SPI0); dma_init_struct.periph_inc DMA_PERIPH_INCREASE_DISABLE; dma_init_struct.periph_width DMA_PERIPH_WIDTH_8BIT; dma_init_struct.priority DMA_PRIORITY_HIGH; dma_init(DMA0, DMA_CH0, dma_init_struct);合理设置FIFO阈值减少中断频率硬件NSS比软件NSS节省CPU资源对于大批量传输考虑使用CRC校验确保数据完整性在完成基础通信后可以尝试更复杂的应用场景如连接SPI Flash、TFT屏幕或多个SPI设备。这时需要注意片选信号的管理硬件NSS只能控制一个从机多个从机时需要软件控制额外的GPIO作为片选。