STM32_10:SPI

1.SPI接口原理

SPI是英语Serial Peripheral interface的缩写,顾名思义就是串行外围设备接口。是Motorola首先在其MC68HCXX系列处理器上定义的。

SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省了空间,提供方便,主要应用在EEPROM,FLASH,实时时钟,AD转换器还有数字信号处理直接。

SPI接口一般使用四根线:

  • MISO,主设备数据输入,从设备数据输出(Master In Slave Out)
  • MOSI,主设备数据输出,从设备数据输入
  • SCLK,时钟信号,由主设备产生
  • CS,从设备片选信号,由主设备控制(Chip Select)

2.SPI工作原理

  1. 硬件为四根线
  2. 主机和从机都有一个串行移位寄存器,主机通过SPI的串行移位寄存器写入一个字节发送一次传输。
  3. 串行移位寄存器通过MOSI信号线将字节发送给从机,从机将自己的串行移位寄存器的内容通过MISO返回给主机,这样两个移位寄存器的内容被交换。
  4. 外设的读写操作同步完成,只进行写操作,则忽略读操作;主机只进行从机的读操作,则主机须发送一个空字节给从机引发传输。

3.SPI特征

  • STM32 的SPI接口可以配置为支持SPI协议或者支持IIS
  • 音频协议,默认是SPI协议,也可以通过软件方式切换称IIS方式

4.从设备引脚管理(NSS)

Negated Slave Select

1.软件模式

可以设置SPI_CR1寄存器的SSM位来使能这种模式,在这种模式下NSS引脚可以用作它用,而内部NSS信号电平可以通过写SPI_CR1的SSI位来驱动。

2.硬件模式

第一种情况:NSS输出使能,当STM32工作为SPI模式的时,NSS输出已经通过SPI_CR2寄存器的SSOE位使能,这时NSS引脚被拉低,所有NSS引脚与这个主SPI的NSS引脚相连并配置为硬件NSS的SPI设备,将自动变成从的SPI设备。

第二种情况:NSS输出被关闭:允许操作于多主环境。

5.时钟信号的相位和极性

SPI_CR寄存器的CPOL(Clock Polarity)和CPHA(Clock Phase)位,能够组合成四种可能的时序关系,CPOL(时钟极性)位控制在没有数据传输时时钟的空闲状态电平,此位对主模式和从模式下的设备都有效。

  • 如果CPOL被清'0',SCK引脚在空闲状态下保持低电平;如果CPOL被置'1',SCK引脚在空闲状态保持高电平。
  • 如果CPHA(时钟相位)位被置'1',SCK时钟的第二个边沿(0为下降沿,1为上升沿)进行数据位的采样,数据在第二个时钟边沿被锁存。
  • 如果CPHA位被清0,SCK时钟的第一个边沿(0为下降沿,1为上升沿)进行数据位采集,数据在第一个时钟边沿被锁存。

CPOL时钟极性和CPHA时钟相位的组合选择数据捕捉的时钟边沿。

6.数据帧和状态

1.数据帧格式

  • 根据SPI_CR1寄存器的LSBFIRST位,输出数据可以选择MSB(高位)先或者LSB(低位)先。
  • 根据SPI_CR1寄存器的DFF位,可以选择数据帧是8位或者16位,对发送和接收都有效。

2.状态标志

通过三个标志可以完全监控SPI总线的状态

  • 发送缓存器空闲标志(TXE)
    • 此标志为1的时候,表示发送缓冲寄存器为空,可以写入下一个待发送数据进入缓冲器中,当写入SPI_DR(数据寄存器)时,TXE标志被清除。
  • 接收缓冲器非空(RXNE)
    • 此标志为1表明接收缓冲器中包含有效数据,读SPI数据寄存器可以清楚此标志
  • 忙BUSY标志
    • BSY标志由硬件设置与清除,此标志表明SPI通信层的状态

7.SPI中断

8.SPI引脚配置

9.结构体

typedef struct

{

uint16_t SPI_Direction; //方向

uint16_t SPI_Mode; //模式

uint16_t SPI_DataSize; //数据大小

uint16_t SPI_CPOL; //时钟极性

uint16_t SPI_CPHA; //时钟相位

uint16_t SPI_NSS; //NSS位

uint16_t SPI_BaudRatePrescaler; //波特率

uint16_t SPI_FirstBit; //位优先

uint16_t SPI_CRCPolynomial; //CRC校验位

}SPI_InitTypeDef;

10.SPI配置过程

①配置引脚,使能时钟

void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);

②初始化SPI,设置工作模式

void SPI_Init(SPI_TypeDef* SPIx, SPI_InitTypeDef* SPI_InitStruct);

③使能SPIx

void SPI_Cmd(SPI_TypeDef* SPIx, FunctionalState NewState);

④SPI传输数据

void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data);

uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx);

⑤查看SPI传输状态

FlagStatus SPI_I2S_GetFlagStatus(SPI_TypeDef* SPIx, uint16_t SPI_I2S_FLAG);

11.代码

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

void SPI2_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	SPI_InitTypeDef SPI_InitStructure;
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB,&GPIO_InitStructure);
	GPIO_SetBits(GPIOB,GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15);
	
	SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
	SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
	SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
	SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
	SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;
	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
	SPI_InitStructure.SPI_CRCPolynomial = 7;
	SPI_Init(SPI2,&SPI_InitStructure);
}


u8 SPI2_ReadWriteByte(u8 data)
{
	u8 i;
	
    // 等待发送缓冲区为空(表示可以发送新数据)
	i = 0;
	while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_TXE) == RESET)
	{
		i++;
		if(i >= 200)
		{
			return 0;
		}
	}
	SPI_I2S_SendData(SPI2,  data);
	
    // 等待接收缓冲区非空(表示有数据可读)
	i = 0;
	while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_RXNE) == RESET)
	{
		i++;
		if(i >= 200)
		{
			return 0;
		}
	}
	return SPI_I2S_ReceiveData(SPI2);
}

void SPI2_SetSpeed(uint16_t SPI_BaudRatePrescaler)
{
    //检查传入的分频值是否合法
	assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));
    //清除旧的分频设置
	SPI2->CR1 &= 0Xffc7;
    //设置新的分频值
	SPI2->CR1 |= SPI_BaudRatePrescaler;
    //重新使能SPI
	SPI_Cmd(SPI2,ENABLE);
	
}

注意:

1.SPI通信的本质

SPI是全双工同步串行通信协议,这意味着:

  1. 主从设备同时收发数据 - 发送和接收同时进行
  2. 时钟由主机控制 - 主机提供时钟信号SCK
  3. 数据传输由时钟边沿触发

关键原理:时钟驱动数据传输

在SPI中,数据在时钟边沿被采样。要实现这个采样过程,必须要有:

  1. 时钟信号(SCK) - 主机产生

  2. 数据传输 - 有数据在MOSI/MISO线上传输

当主机想要读取从机数据时:

  • 主机必须产生时钟脉冲

  • 要产生时钟脉冲,主机必须发送数据(即使是无效数据)

  • 每个时钟脉冲同时完成:

    • 主机发送1位数据到从机(通过MOSI)

    • 从机发送1位数据到主机(通过MISO)

注:

  1. 发送命令和地址时确实产生了时钟

  2. 但从机在这些时钟周期内发送的是无效数据

  3. 必须继续产生时钟,才能让从机输出有效数据

  4. 每个SPI数据交换操作(一个字节)同时完成发送和接收

cs 复制代码
例:
// 读取SPI Flash的完整函数
void SPI_Flash_Read(uint32_t addr, uint8_t *buffer, uint32_t len)
{
    CS_LOW();
    
    // 1. 发送命令和地址(这些读取返回值是垃圾数据,直接丢弃)
    SPI2_ReadWriteByte(FLASH_CMD_READ);  // 命令字节
    SPI2_ReadWriteByte((addr >> 16) & 0xFF);  // 地址字节1
    SPI2_ReadWriteByte((addr >> 8) & 0xFF);   // 地址字节2  
    SPI2_ReadWriteByte(addr & 0xFF);          // 地址字节3
    
    // 2. 现在开始读取实际数据
    // 每次发送0xFF来产生时钟,同时读取一个字节数据
    for(uint32_t i = 0; i < len; i++)
    {
        buffer[i] = SPI2_ReadWriteByte(0xFF);  // 发送伪数据,读取真实数据
    }
    
    CS_HIGH();
}

2.SPI2 的引脚

SPI模块 默认引脚(不复用/重映射前) 所在章节
SPI1 PA4 (NSS), PA5 (SCK), PA6 (MISO), PA7 (MOSI) 8.3.10
SPI2 PB12 (NSS), PB13 (SCK), PB14 (MISO), PB15 (MOSI) 无重映射,固定
SPI3 PA15 (NSS), PB3 (SCK), PB4 (MISO), PB5 (MOSI) 8.3.11

3.SPI2_ReadWriteByte(u8 dat)

作用: 通过SPI2发送一个字节数据,同时接收从机返回的一个字节数据

cs 复制代码
示例1:读取传感器数据

// 发送读取命令,同时接收数据
u8 cmd = 0xA5;  // 读取命令
u8 response = SPI2_ReadWriteByte(cmd);
// response = 从传感器返回的数据

示例2:写入配置寄存器

// 发送配置值,可能返回状态
u8 config_value = 0x3C;
u8 status = SPI2_ReadWriteByte(config_value);
// status = 从机返回的操作状态

示例3:连续读写操作

// 通常需要先拉低片选
SPI2_NSS_LOW();  // PB12 = 0

// 发送多个字节
SPI2_ReadWriteByte(0x01);  // 发送命令
SPI2_ReadWriteByte(0x02);  // 发送地址高字节
SPI2_ReadWriteByte(0x03);  // 发送地址低字节
u8 data = SPI2_ReadWriteByte(0xFF);  // 发送dummy数据,接收实际数据

SPI2_NSS_HIGH();  // PB12 = 1

4.void SPI2_SetSpeed(u8 SPI_BaudRatePrescaler)

作用:修改SPI2的时钟分频系数,从而改变通信速度

参数:预分频值,对应不同的分频系数(2, 4, 8, 16, 32, 64, 128, 256)

5.bps /Bps的基本定义

bps

  • 全称:Bits Per Second(比特/秒)
  • bit:二进制的一位(0 或 1)
  • per second:每秒钟
  • 合起来:每秒钟传输的二进制位数

Bps

  • Bytes per second
  • 字节/秒
  • 1 Bps = 8 bps
相关推荐
polarislove02142 小时前
10.3[ADC]采样时间和转换时间-嵌入式铁头山羊STM32笔记
笔记·stm32·嵌入式硬件
创思通信2 小时前
STM32L151RCT6 BC20 采集温湿度DHT11 采集GPS定位 和ADC发送到最新版本ONENET物联网开放平台
stm32·嵌入式硬件·物联网
__万波__2 小时前
STM32建立完全空白的工程
stm32·单片机·嵌入式硬件
松涛和鸣2 小时前
51、51单片机
c语言·网络·单片机·嵌入式硬件·tcp/ip·51单片机
张海森-1688202 小时前
608_demo例子开红外及ir_cut是怎么做的呢
单片机
LongRunning2 小时前
【IDE】KEIL IAR GCC 编译信息
单片机
麒qiqi3 小时前
51 单片机入门详解:从基础概念到实战开发
单片机·嵌入式硬件
兆龙电子单片机设计3 小时前
【STM32项目开源】STM32单片机充电桩安全监测系统
stm32·单片机·物联网·开源·毕业设计
@good_good_study3 小时前
STM32 C语言函数
stm32