STM32——SPI通信

前言

SPI 是嵌入式开发中最常用、最高速的同步串行通信协议之一,广泛用于驱动 OLED 屏、TFTLCD、W25QXX Flash、传感器等外设。

一、SPI 通信

1. 什么是 SPI?

SPI(Serial Peripheral Interface)串行外设接口 是一种高速、全双工、同步、主从式的通信总线。

  • 串行:数据一位一位传输
  • 全双工 :发送和接收可以同时进行
  • 同步:主机提供统一时钟,收发双方严格同步
  • 主从:STM32 一般作为主机,外设作为从机

2. SPI 4 根核心信号线

引脚 名称 功能
SCLK 串行时钟 主机产生,控制通信速度
MOSI 主机发→从机收 主机输出数据
MISO 从机发→主机收 主机接收数据
CS/NSS 片选信号 选中要通信的从设备

3. SPI 4 种工作模式(关键)

CPOL(时钟空闲电平)CPHA(数据采样沿) 决定:

  • CPOL:时钟空闲时的电平

  • CPHA:数据在第几个时钟边沿采样

    CPOL = High(空闲高)
    CPHA = 2Edge(第二个边沿采样)

对应 SPI 模式 3,是屏幕、Flash 最常用模式。

4. SPI 通信特点

✅ 速度极快(最高可达系统时钟)✅ 全双工,收发同步✅ 协议简单,硬件自动完成收发✅ 适合短距离高速通信

二、工程文件说明

驱动代码分为两个文件:

  1. spi.c ------ SPI 驱动实现
  2. spi.h ------ 函数声明

包含 3 个核心函数:

  • SPI2_Init():SPI2 初始化
  • SPI2_ReadWriteByte():SPI 读写一个字节
  • SPI2_SetSpeed():动态修改 SPI 通信速度

三、代码

1. spi.h 头文件

复制代码
#ifndef _SPI_H_   // 防止头文件被重复包含
#define _SPI_H_

#include "stm32f10x.h"  // 包含STM32标准库

// 函数声明
void SPI2_Init(void);                        // SPI2初始化
u8 SPI2_ReadWriteByte(u8 dat);               // SPI2读写一个字节
void SPI2_SetSpeed(u8 SPI_BaudRatePrescaler); // 设置SPI2速度

#endif

2. spi.c 驱动文件

复制代码
#include "stm32f10x.h"
#include "spi.h"

/**********************************************************
 * 函数名:SPI2_Init
 * 功能 :SPI2初始化(GPIO + 通信模式 + 时序 + 速率)
 * 备注 :PB13=SCLK  PB14=MISO  PB15=MOSI
 **********************************************************/
void SPI2_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;  // GPIO初始化结构体
	SPI_InitTypeDef SPI_InitStructure;    // SPI初始化结构体
	
	//====================== 1. GPIO初始化 ======================
	// SPI属于硬件外设,必须配置为 复用推挽输出(AF_PP)
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	// SPI2的三个引脚:PB13、PB14、PB15
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
	// GPIO速度设置为50MHz
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	// 初始化GPIOB
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	// 默认拉高三个SPI引脚(空闲状态)
	GPIO_SetBits(GPIOB, GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15);
	
	//====================== 2. SPI参数配置 ======================
	// 设置为双线全双工模式(发送和接收同时进行)
	SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
	// STM32作为主机(Master)
	SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
	// 数据宽度:8位(最常用)
	SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
	// 时钟空闲电平:高电平(模式3)
	SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
	// 数据采样沿:第二个时钟沿(模式3)
	SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
	// 片选信号:软件管理(自己用GPIO控制CS)
	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
	// 初始化波特率:256分频(最慢,最稳定)
	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;
	// 数据传输格式:高位先行(MSB)
	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
	// CRC校验多项式(一般不用,默认7)
	SPI_InitStructure.SPI_CRCPolynomial = 7;
	// 根据配置初始化SPI2
	SPI_Init(SPI2, &SPI_InitStructure);
    
    // 最后使能SPI外设
    SPI_Cmd(SPI2, ENABLE);
}

/**********************************************************
 * 函数名:SPI2_ReadWriteByte
 * 功能 :SPI2主机读写一个字节(全双工)
 * 输入 :dat 要发送的数据
 * 返回 :接收到的数据
 * 备注 :SPI特性:发1字节 = 同时收1字节
 **********************************************************/
u8 SPI2_ReadWriteByte(u8 dat)
{
	u8 t = 0;  // 超时计数
	
	// 1. 等待发送缓冲区为空(TXE=1表示空)
	while(SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET)
	{
		t++;
		if(t >= 200) return 0;  // 超时防止程序卡死
	}
	
	// 2. 通过SPI2发送一个字节
	SPI_I2S_SendData(SPI2, dat);
	
	t = 0;
	// 3. 等待接收缓冲区非空(RXNE=1表示收到数据)
	while(SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET)
	{
		t++;
		if(t >= 200) return 0;  // 超时判断
	}
	
	// 4. 读取并返回SPI2接收到的数据
	return SPI_I2S_ReceiveData(SPI2);
}

/**********************************************************
 * 函数名:SPI2_SetSpeed
 * 功能 :动态修改SPI2通信速度
 * 输入 :分频系数(2、4、8、16...256)
 * 注意 :修改SPI配置必须先关闭SPI!
 **********************************************************/
void SPI2_SetSpeed(u8 SPI_BaudRatePrescaler)
{
	// 检查输入参数是否合法
	assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));
	
	// 【重要】修改配置前必须先关闭SPI
	SPI_Cmd(SPI2, DISABLE);
	
	// 清空CR1寄存器的BR[2:0]位(这三位是设置速度的)
	SPI2->CR1 &= 0xFFC7;
	
	// 设置新的分频值
	SPI2->CR1 |= SPI_BaudRatePrescaler;
	
	// 重新使能SPI
	SPI_Cmd(SPI2, ENABLE);
}

四、代码核心重点讲解

1. 初始化函数 SPI2_Init

  • GPIO 必须设为复用推挽输出
  • 模式 3(CPOL=1,CPHA=1) 适合绝大多数屏幕和 Flash
  • 初始化用低速 256 分频,保证稳定
  • 主机模式、8 位数据、高位先行

2. 读写函数 SPI2_ReadWriteByte(SPI 灵魂)

SPI 是全双工:发送 = 接收流程:

  1. 等发送区空
  2. 发送数据
  3. 等接收完成
  4. 返回读到的数据

超时处理,防止总线卡死。

3. 速度设置函数 SPI2_SetSpeed

  • 修改 SPI 速度必须先关闭 SPI
  • 先清零旧速度,再写入新速度
  • 可随时切换速度:初始化慢,运行后改快

五、使用示例

复制代码
// 初始化
SPI2_Init();

// 读写数据
u8 recv_data = SPI2_ReadWriteByte(0x80);

// 改高速(2分频)
SPI2_SetSpeed(SPI_BaudRatePrescaler_2);

六、总结

这套 SPI 代码是STM32 最标准、最通用、最稳定的硬件 SPI 主机驱动:✅ 带完整注释,新手能看懂✅ 带超时保护,不卡死✅ 可动态修改速度✅ 支持所有 SPI 外设(OLED、LCD、Flash、传感器)✅ 模式 3 配置,工业最常用

相关推荐
2035去旅行6 小时前
WIFI传输带宽
arm开发·嵌入式硬件
linbaiwan6666 小时前
内置VDD稳压管减少外围元件的三款LED驱动芯片集成度
单片机·嵌入式硬件
三品吉他手会点灯7 小时前
STM32F103 学习笔记-21-串口通信(第6节)-串口发送命令控制RGB灯
笔记·stm32·单片机·嵌入式硬件·学习
yongui478348 小时前
水表集中抄表器单片机实现方案
单片机·嵌入式硬件
iCxhust11 小时前
8086 Proteus 8253制作跑表
单片机·嵌入式硬件·proteus·微机原理·8088单板机
xiangw@GZ11 小时前
HDI 高密度互连板阶数的深度理解
服务器·单片机·嵌入式硬件
@残梦12 小时前
200、stm32定义缓冲区用在DMA上时,需要谨记4字节地址对齐规则
stm32·单片机·嵌入式硬件
深圳市尚想信息技术有限公司13 小时前
HTW1000 烧录器/仿真器 TENX(十速)/海速芯 MCU在线/串联烧录器 单片机开发 嵌入式系统应用
单片机·烧录器·单片机开发·tenx·十速·海速芯
嵌入式-老费14 小时前
esp开发与应用(驱动步进电机)
单片机·嵌入式硬件