17. STM32——SPI硬件

发布时间:2023-12-02 09:30

STM32——SPI

  • SPI协议
  • SPI接口
  • SPI接口框图
  • SPI工作原理
  • 时钟信号的相位和极性
    • CPHA = 0
    • CPHA = 1
  • SPI中断
    • 状态标志
      • 发送缓存器空闲标志(TXE)
      • 接收缓冲器非空(RXNE)
      • 忙BUSY标志
  • SPI引脚配置
  • 从设备引脚管理(NSS)
    • ①软件模式:
    • ②硬件模式:
  • SPI结构体
  • SPI配置过程
    • ①配置引脚,使能时钟
    • ②初始化SPI,设置工作模式
    • ③使能SPIx
    • ④SPI传输数据
    • ⑤查看SPI传输状态
  • 整合
    • 初始化SPI
  • 读写字节
    • 设置SPI的波特率
    • spi.h
    • spi.c

SPI协议

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

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

SPI接口

SPI总线包括4条逻辑线,定义如下:

MISO:Master input slave output 主机输入,从机输出(数据来自从机);

MOSI:Master output slave input 主机输出,从机输入(数据来自主机);

SCLK:Serial Clock 串行时钟信号,由主机产生发送给从机;SCLK也可以是SCK

SS:Slave Select 片选信号,由主机发送,以控制与哪个从机通信,通常是低电平有效信号。

SPI接口框图

17. STM32——SPI硬件_第1张图片

SPI工作原理

①硬件为四根线。

②主机和从机都有一个串行移位寄存器,主机通过SPI的串行移位寄存器写入一个字节发送一次传输。

③串行移位寄存器通过MOSI信号线将字节发送给从机,从机将自己的串行移位寄存器的内容通过MISO返回给主机,这样两个移位寄存器的内容被交换。

④外设的读写操作同步完成,只进行写操作,则忽略读操作;主机只进行从机的读操作,则主机须发送一个空字节给从机引发传输。

时钟信号的相位和极性

SPI_CR寄存器的CPOL和CPHA位,能够组合成四种可能的时序关系,CPOL(时钟极性)位控制在没有数据传输时时钟的空闲状态电平,此位对主模式和从模式下的设备都有效。如果CPOL被清’0’,SCK引脚在空闲状态下保持低电平;如果CPOL被置‘1’,SCK引脚在空闲状态保持高电平。

CPHA = 0

如果CPHA位被清0,SCK时钟的第一个边沿(0为下降沿,1为上升沿)进行数据位采集,数据在第一个时钟边沿被锁存。

17. STM32——SPI硬件_第2张图片

CPHA = 1

如果CPHA(时钟相位)位被置‘1’,SCK时钟的第二个边沿(0为下降沿,1为上升沿)进行数据位的采样,数据在第二个时钟边沿被锁存.

17. STM32——SPI硬件_第3张图片

SPI中断

状态标志

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

发送缓存器空闲标志(TXE)

此标志为1的时候,表示发送缓冲寄存器为空,可以写入下一个待发送数据进入缓冲器中,当写入SPI_DR(数据寄存器)时,TXE标志被清除。

接收缓冲器非空(RXNE)

此标志为1表明接收缓冲器中包含有效数据,读SPI数据寄存器可以清楚此标志。

忙BUSY标志

BSY标志由硬件设置与清楚,此标志表明SPI通信层的状态。

17. STM32——SPI硬件_第4张图片

SPI引脚配置

17. STM32——SPI硬件_第5张图片

从设备引脚管理(NSS)

①软件模式:

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

②硬件模式:

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

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

SPI结构体

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;

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);

整合

初始化SPI

#include "SPI.h"
#include "stm32f10x.h"

void SPI2_Init(void)//SPI2初始化   主模式
{
	SPI_InitTypeDef  SPI_InitStructure;
	GPIO_InitTypeDef GPIO_InitStructure;
	
	/* Enable SPI1 and GPIOA clocks */
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,ENABLE);

	/* Configure SPI1 pins: NSS, SCK, MISO and MOSI */
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;//SCK MOSI 
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//PB13/14/15复用推挽输出 				 
	GPIO_Init(GPIOB, &GPIO_InitStructure);
  	GPIO_SetBits(GPIOB,GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15);//PB13/14/15上拉
	
	/* SPI2 configuration */ 
	SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //SPI2设置为两线全双工
	SPI_InitStructure.SPI_Mode = SPI_Mode_Master;	                   //设置SPI2为主模式
	SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;                  //SPI发送接收8位帧结构
	SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;	 		               //串行时钟在不操作(空闲)时,时钟为高电平
	SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;		               //第二个时钟沿开始采样数据(此处为上升沿采集数据)
	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;			               //NSS信号由软件(使用SSI位)管理
	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8; //定义波特率预分频的值:波特率预分频值为8,分频后为9MHZ
	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;				   //数据传输从MSB位开始  高位
	SPI_InitStructure.SPI_CRCPolynomial = 7;						   //CRC值计算的多项式
	SPI_Init(SPI2, &SPI_InitStructure);
	
	/* Enable SPI2  */
	SPI_Cmd(SPI2, ENABLE); 											  //使能SPI1外设
}

读写字节

u8 SPI2_ReadWriteByte(u8 Data)
{
	unsigned char t = 0;
	
	while(SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET)
	{
		t++;
		
		if(t>=200)
		{
			return 0;
		}
	}
	
	SPI_I2S_SendData(SPI2, Data);
	
	while(SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET)
	{
		t++;
		
		if(t>=200)
		{
			return 0;
		}
	}
	
	return SPI_I2S_ReceiveData(SPI2);

}

设置SPI的波特率

17. STM32——SPI硬件_第6张图片

void SPI2_SetSpeed(u8 SPI_BaudRatePrescaler)
{
	assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));
	
	SPI2->CR1 &= 0XFFC7; //&1111 1111 1100 0111 把D3~D4清零
	SPI2->CR1 |= SPI_BaudRatePrescaler; //这里是把SPI_BaudRatePrescaler的值与SPI2->CR1中的值按2进制的方式相加
	//如果SPI_BaudRatePrescaler的值为0x038,二进制数为00111000与1111 1111 1100 0111相或后结果是1111 1111 1111 1111->0XFFFF; 
	
	SPI_Cmd(SPI2, ENABLE);

}

spi.h

#ifndef __spi_h__
#define __spi_h__

#include "stm32f10x.h"

void SPI2_Init(void) ;//SPI2初始化   主模式
u8  SPI2_ReadWriteByte(u8 dat);
void SPI2_SetSpeed(u8 SPI_BaudRatePrescaler);

#endif  

spi.c

#include "spi.h"
#include "stm32f10x.h"

void SPI2_Init(void)//SPI2初始化   主模式
{
	SPI_InitTypeDef  SPI_InitStructure;
	GPIO_InitTypeDef GPIO_InitStructure;
	
	/* Enable SPI1 and GPIOA clocks */
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,ENABLE);

	/* Configure SPI1 pins: NSS, SCK, MISO and MOSI */
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;//SCK MOSI 
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽				 
	GPIO_Init(GPIOB, &GPIO_InitStructure);
  	GPIO_SetBits(GPIOB,GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15);
	

	/* SPI2 configuration */ 
	SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //SPI2设置为两线全双工
	SPI_InitStructure.SPI_Mode = SPI_Mode_Master;	                   //设置SPI2为主模式
	SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;                  //SPI发送接收8位帧结构
	SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;	 		               //串行时钟在不操作(空闲)时,时钟为低电平
	SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;		               //第一个时钟沿开始采样数据(此处为上升沿采集数据)
	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;			               //NSS信号由软件(使用SSI位)管理
	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8; //定义波特率预分频的值:波特率预分频值为8,分频后为9MHZ
	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;				   //数据传输从MSB位开始  高位
	SPI_InitStructure.SPI_CRCPolynomial = 7;						   //CRC值计算的多项式
	SPI_Init(SPI2, &SPI_InitStructure);
	/* Enable SPI2  */
	SPI_Cmd(SPI2, ENABLE); 											  //使能SPI1外设
}

//SPIx 读写一个字节
//返回值:读取到的字节
//#define SPI_RW  SPI1_ReadWriteByte //代替函数名,以便适用于后面函数

u8 SPI2_ReadWriteByte(u8 Data)
{
	unsigned char t = 0;
	
	while(SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET)
	{
		t++;
		
		if(t>=200)
		{
			return 0;
		}
	}
	
	SPI_I2S_SendData(SPI2, Data);
	
	while(SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET)
	{
		t++;
		
		if(t>=200)
		{
			return 0;
		}
	}
	
	return SPI_I2S_ReceiveData(SPI2);

}

void SPI2_SetSpeed(u8 SPI_BaudRatePrescaler)
{
	assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));
	
	SPI2->CR1 &= 0XFFC7; //&1111 1111 1100 0111 把D3~D4清零
	SPI2->CR1 |= SPI_BaudRatePrescaler; //这里是把SPI_BaudRatePrescaler的值与SPI2->CR1中的值按2进制的方式相加
	//如果SPI_BaudRatePrescaler的值为0x038,二进制数为00111000与1111 1111 1100 0111相或后结果是1111 1111 1111 1111->0XFFFF; 
	
	SPI_Cmd(SPI2, ENABLE);

}

ItVuer - 免责声明 - 关于我们 - 联系我们

本网站信息来源于互联网,如有侵权请联系:561261067@qq.com

桂ICP备16001015号