《STM32 江湖 SPI 双绝:硬件外设与软件模拟的深度解析》

章节 技术类型 核心机制 适用场景 难度评级
第一章 SPI 基础 串行通信协议 外设互联场景 ★★☆☆☆
第二章 硬件 SPI 内置外设实现 高速数据传输 ★★★☆☆
第三章 模拟 SPI GPIO 软件模拟 灵活适配场景 ★★☆☆☆
第四章 技术对比 性能与特性差异 方案选型参考 ★★★★☆
第五章 跨 MCU 移植 主频适配方法 多平台兼容 ★★★☆☆
第六章 实战调试 问题排查技巧 工程实践 ★★★★☆

第一章:SPI 基础・串行通信协议

1.1 SPI 的起源与应用

SPI(Serial Peripheral Interface,串行外设接口)是由摩托罗拉公司在 20 世纪 80 年代提出的一种同步串行通信协议,以高速、全双工、操作简单为特点,广泛应用于微控制器(MCU)与外设的通信场景,如传感器、显示屏、存储芯片(Flash)、AD/DA 转换器等。

在 STM32 系列 MCU 中,SPI 通信有两种实现方式:硬件 SPI(依托芯片内置的 SPI 外设模块)和模拟 SPI(通过 GPIO 引脚手动模拟通信时序)。两种方式各有优劣,适用于不同的工程需求。

1.2 SPI 的核心组成要素

1.2.1 信号线定义

SPI 通信需 4 根信号线完成数据传输,分别是:

  • SCLK(Serial Clock):时钟线,由主机产生,用于同步数据传输节奏,控制数据收发的时序。
  • MOSI(Master Out Slave In):主机输出 / 从机输入线,主机通过此线向从机发送数据。
  • MISO(Master In Slave Out):主机输入 / 从机输出线,从机通过此线向主机返回数据。
  • NSS(Chip Select):片选线(通常低电平有效),主机通过此线选择需要通信的从机(同一总线上可连接多个从机,通过 NSS 单独选中)。
1.2.2 时序参数(CPOL 与 CPHA)

SPI 的通信时序由两个核心参数定义,分别是时钟极性(CPOL)和时钟相位(CPHA),两者组合形成 4 种标准时序模式:

  • CPOL(时钟极性):定义 SCLK 在空闲状态(未通信时)的电平。CPOL=0 时,SCLK 空闲为低电平;CPOL=1 时,SCLK 空闲为高电平。
  • CPHA(时钟相位):定义数据的采样时刻。CPHA=0 时,在 SCLK 的第一个跳变沿(从空闲电平到工作电平的跳变)采样数据;CPHA=1 时,在 SCLK 的第二个跳变沿(从工作电平回到空闲电平的跳变)采样数据。

4 种时序模式的具体参数如下表:

模式 CPOL CPHA 空闲时 SCLK 电平 数据采样沿
0 0 0 低电平 上升沿
1 0 1 低电平 下降沿
2 1 0 高电平 下降沿
3 1 1 高电平 上升沿
1.2.3 通信架构(主从模式)

SPI 采用 “一主多从” 的通信架构:

  • 主机(Master):负责产生 SCLK 时钟信号,控制 NSS 线选择从机,并主动发起数据传输。
  • 从机(Slave):被动响应主机的时钟信号,仅在被 NSS 选中时参与通信,按主机的节奏收发数据。

通信流程可概括为:主机拉低目标从机的 NSS 线(选中从机)→ 主机通过 SCLK 输出时钟信号,同时在 MOSI 线发送数据,从机在 MISO 线返回数据→ 传输完成后,主机拉高 NSS 线(结束通信)。

第二章:硬件 SPI・内置外设实现

硬件 SPI 是通过 STM32 内置的 SPI 外设模块实现通信,依托硬件电路自动生成时序,具有速度快、CPU 占用低的特点,适用于高速数据传输场景。

2.1 硬件 SPI 的内部结构

STM32 的 SPI 外设模块由多个功能单元组成,核心结构包括:

  • 时钟发生器:由 APB 总线时钟分频得到 SCLK 信号,支持的分频比为 2/4/8/16/32/64/128/256,可根据需求调整通信速率。
  • 数据寄存器(DR):8 位或 16 位的双向缓冲寄存器,发送数据时需写入该寄存器,接收数据时从该寄存器读取。
  • 控制寄存器(CR1/CR2):用于配置 SPI 的核心参数,如主从模式、数据长度、CPOL/CPHA、分频比、数据传输方向(全双工 / 半双工)等。
  • 状态寄存器(SR):指示 SPI 的工作状态,如发送缓冲区空(TXE)、接收缓冲区满(RXNE)、忙状态(BSY)等,用于判断数据传输进度。
  • 中断 / DMA 控制器:支持通过中断或 DMA 方式处理数据收发,减少 CPU 的干预,提高效率。

2.2 硬件 SPI 的初始化配置

以 STM32F103C8T6(主频 72MHz)为例,硬件 SPI 的初始化需完成时钟使能、GPIO 配置、SPI 参数配置三个步骤,具体代码如下:

c

运行

// 步骤1:使能时钟(SPI外设和GPIO端口时钟)
void SPI_Hard_Init(void) {
    // 使能SPI1时钟(SPI1挂载在APB2总线,时钟72MHz)
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
    // 使能GPIOA时钟(SPI1默认引脚为PA5~7,NSS使用PA4)
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    
    // 步骤2:配置GPIO引脚
    GPIO_InitTypeDef GPIO_InitStructure;
    // 配置SCLK(PA5)和MOSI(PA7)为复用推挽输出(由SPI外设控制)
    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(PA6)为浮空输入(接收从机数据)
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 浮空输入
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    // 配置NSS(PA4)为推挽输出(软件控制片选)
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 通用推挽输出
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    GPIO_SetBits(GPIOA, GPIO_Pin_4); // 初始拉高NSS(未选中从机)
    
    // 步骤3:配置SPI1参数
    SPI_InitTypeDef SPI_InitStructure;
    SPI_InitStructure.SPI_Direction = SPI_Direction_2Line_FullDuplex; // 全双工模式
    SPI_InitStructure.SPI_Mode = SPI_Mode_Master; // 主机模式
    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; // 8位数据长度
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; // 时钟极性:空闲低电平(模式0)
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; // 时钟相位:第一个跳变沿采样(模式0)
    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; // 软件控制NSS(不使用硬件NSS)
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2; // 分频2,SCLK=36MHz
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; // 高位先传输
    SPI_InitStructure.SPI_CRCPolynomial = 7; // CRC多项式(不使用CRC时可任意设置)
    SPI_Init(SPI1, &SPI_InitStructure);
    
    // 使能SPI1
    SPI_Cmd(SPI1, ENABLE);
}

2.3 硬件 SPI 的数据收发实现

硬件 SPI 支持三种数据传输方式:阻塞式、中断式和 DMA 式,分别适用于不同的实时性和效率需求。

2.3.1 阻塞式收发

阻塞式传输通过轮询状态寄存器的标志位,等待数据发送 / 接收完成,实现简单但会占用 CPU 资源,适用于数据量小、对实时性要求不高的场景。

c

运行

// 发送1字节数据(阻塞式)
void SPI_Hard_SendByte(SPI_TypeDef* SPIx, uint8_t data) {
    // 等待发送缓冲区为空(TXE标志置位)
    while (SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_TXE) == RESET);
    // 写入数据到SPI数据寄存器,启动发送
    SPI_I2S_SendData(SPIx, data);
}

// 接收1字节数据(阻塞式)
uint8_t SPI_Hard_ReceiveByte(SPI_TypeDef* SPIx) {
    // 等待接收缓冲区为满(RXNE标志置位)
    while (SPI_I2S_GetFlagStatus(SPI
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

无人装备硬件开发爱好者

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值