当前位置: 首页 > news >正文

GD32F103 SPI实战:手把手教你配置主机从机全双工通信(附完整代码)

GD32F103 SPI全双工通信实战:从硬件配置到代码调试全解析

刚拿到GD32开发板时,最让人头疼的就是外设配置。特别是对于从STM32转过来的开发者,虽然两者相似,但细节差异往往让人踩坑。SPI作为高速通信的利器,在传感器、存储芯片等场景中应用广泛。本文将用一块GD32F103C8T6开发板,带你完整实现主机与从机的全双工通信,过程中会特别标注与STM32的差异点。

1. 硬件连接与SPI基础

SPI通信需要四根基础线:NSS(片选)、SCK(时钟)、MOSI(主机输出从机输入)和MISO(主机输入从机输出)。在GD32F103C8T6上,SPI0的默认引脚映射如下:

引脚功能引脚编号
NSSPA4
SCKPA5
MISOPA6
MOSIPA7

关键点注意

  • 硬件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; // 系统时钟108MHz,8分频后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 }

配置要点解析

  1. 时钟分频(prescale):决定了通信速率,GD32F103的SPI时钟源是APB2总线时钟(通常108MHz)
  2. 时钟极性和相位(clock_polarity_phase):
    • SPI_CK_PL_LOW_PH_1EDGE表示空闲时时钟为低,数据在第一个边沿采样
    • 这是最常用的模式,与多数SPI设备兼容
  3. 硬件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 i=0; i<10; 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 i=0; i<10; i++) printf("%02X ", master_rx[i]); printf("\nSlave Received: "); for(int i=0; i<10; i++) printf("%02X ", slave_rx[i]); printf("\n"); }

调试技巧

  1. 先用逻辑分析仪或示波器检查SCK、MOSI、MISO信号
  2. 如果通信失败,首先检查:
    • 时钟极性和相位是否匹配
    • NSS信号是否正确
    • 线序是否正确(MOSI接MOSI,不要交叉)
  3. 降低时钟频率测试,排除信号完整性问题
  4. 检查电源和地线连接,SPI对共地要求严格

5. 常见问题与性能优化

问题1:数据错位或全为0xFF/0x00

  • 检查时钟极性和相位设置
  • 确认MISO/MOSI没有接反
  • 检查从机是否被正确使能

问题2:通信速度不达标

  • 确认APB2时钟配置正确
  • 尝试减小分频系数
  • 检查PCB走线,过长或过近的走线会影响信号质量

性能优化建议

  1. 使用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);
  1. 合理设置FIFO阈值,减少中断频率
  2. 硬件NSS比软件NSS节省CPU资源
  3. 对于大批量传输,考虑使用CRC校验确保数据完整性

在完成基础通信后,可以尝试更复杂的应用场景,如连接SPI Flash、TFT屏幕或多个SPI设备。这时需要注意片选信号的管理,硬件NSS只能控制一个从机,多个从机时需要软件控制额外的GPIO作为片选。

http://www.jsqmd.com/news/680859/

相关文章:

  • Rust vs C++:从‘零成本抽象’看两种语言的设计哲学与实战选择(附性能对比小实验)
  • 分析文与道考研口碑怎么样,呼和浩特地区考研辅导靠谱之选? - 工业推荐榜
  • 马思特切削液区域代理哪家口碑好,全国范围内有推荐吗? - 工业品网
  • IB课程辅导机构哪家更适合冲藤校?师资背景、HL科目覆盖与成绩提升对比 - 品牌排行榜
  • 如何快速掌握yfinance:Python金融数据采集的完整指南
  • 如何在Mac上一键解密QQ音乐加密文件:QMCDecode完整教程
  • 如何轻松管理JetBrains IDE试用期:IDE Eval Resetter完整使用指南
  • 如何导出AI对话 - DS随心转小程序
  • 终极指南:如何使用FakeLocation Xposed模块实现应用级虚拟定位
  • NVIDIA Profile Inspector终极指南:如何深度优化显卡性能的5个核心技巧
  • 拆解一台2015年的华硕A555L:聊聊‘混合硬盘’、‘低压U’和‘入门独显’那些坑
  • 2026年导轨、阀杆、双轴淬火机床厂家盘点,哪家口碑好 - 工业品牌热点
  • 2026年郑州性价比高的钢结构工程设计专业公司,费用多少钱 - mypinpai
  • 如何快速掌握SketchUp STL插件:3D打印模型转换的完整解决方案
  • 2026年3月市面上不锈钢灰厂家,精密铸造砂/碳化硅/不锈钢灰/黑碳化硅/棕刚玉/金刚砂/磨料,不锈钢灰定制哪家好 - 品牌推荐师
  • 2026年美国移民公司推荐及选择要点分析 - 品牌排行榜
  • 4个SVG设计排版技巧:文艺风教程(2026实测) - 小小智慧树~
  • 如何高效解决QQ音乐加密格式问题:macOS平台QMCDecode完整实践指南
  • 有关医疗废水预排前准备工作!
  • 2026年河南淬火机床费用探讨,价格多少合适 - 工业推荐榜
  • 探寻2026年靠谱的白蚁防治服务,志得资质正规更放心 - 工业品网
  • 2026年市面上热门的SolidWorks培训机构推荐,SolidWorks培训学校推荐分析 - 品牌推荐师
  • TinyObjLoader vs. Assimp:C++游戏开发中,轻量级与全能型模型加载库该怎么选?
  • 解锁音乐自由:qmcdump音频解码工具完全指南
  • 探讨卧式淬火机床厂家,怎么选择靠谱的品牌 - myqiye
  • 别再死记硬背了!用HashMap、LinkedHashMap、TreeMap搞懂Java集合的‘性格差异’
  • 保姆级教程:用STM32CubeMX+Keil5快速搞定AHT21B温湿度数据采集(含串口打印和避坑点)
  • 盘点2026年浙江性价比高的白蚁防治中心 靠谱白蚁防治中心推荐 - 工业品牌热点
  • DoL-Lyra构建系统:游戏模组整合的自动化解决方案
  • 第5章 云端部署(阿里云/腾讯云)OpenClaw,实现随时随地“养虾”