SPI概述
一、SPI简介
SPI是一种串行外围设备接口,高速全双工的通信总线,广泛应用于ADC、LCD等设备于MCU间通信场合。
SPI包含4条总线,分别为NSS、SCK、MISO、MOSI,各信号线功能如下:
1. NSS
NSS为片选信号,当NSS信号为低电平时,片选有效,开始SPI主从模式通讯。
SPI主从模式的配置真正起作用的是内部NSS引脚,内部NSS引脚才真正连接到SPI通讯控制器上。软件NSS模式和硬件NSS模式由SSM控制位实现,SSM位控制内部NSS引脚与SSI位(软件模式)相连,还是与NSS外部引脚(STM32引脚,硬件模式)。如图1-1所示。
图1-1内外部NSS
在实际的应用中,STM32的SPI功能通常用作主模式。选择软件模式输入控制内部NSS为高电平,使能外部NSS管脚为普通IO口输出低电平来使能从设备,实现一主多从设备的数据交流。如图1-2所示。
图1-2 一主多从
2. SCK
SCK为时钟信号线,由主通讯设备产生,不同的设备支持的时钟频率不一样,STM32的SPI时钟频率最大为fpclk/2.
3. MISO和MOSI
MISO和MOSI为主设备和从设备通讯数据线。MISO为从主机到从机,MOSI为从从机到主机。
二、SPI寄存器配置
因为在实际的应用中,STM32的SPI功能通常用作主模式,选择软件模式配置NSS管脚,在此以SPI1为例说明该种SPI工作模式配置步骤:
1. 使能SP1时钟,配置GPIOA5、6、7、8;
查找《STM32参考手册》中系统结构图得知SPI和GPIOA均挂载在APB2时钟线上,查找APB2外设时钟使能寄存器(RCC_APB2ENR)(图1-4),使能SPI和GPIOA的时钟:
RCC->APB2ENR |= 1<<2; //使能GPIOA时钟;
RCC->APB2ENR |= 1<<12; //使能SPI1时钟;
参考前文《GPIO概述》配置5,6,7,为复用推挽输出:
GPIOA->CRL &= 0x000FFFFF;
GPIOA->CRL |= 0xBBB00000; //复用推挽输出
GPIOA->ODR |= 0x07<<5; //上拉
图1-3 STM32系统结构图
图1-4 RCC_APB2ENR
2. 开全双工模式并配置软件管理NSS
SPI1->CR1 |= 0<<10; //全双工模式
SPI1->CR1 |= 1<<9; //软件NSS-SSM
SPI1->CR1 |= 1<<8; //软件NSS-SSI
图1-5 SPI_CR1
3. 设置SPI1位主机并设置数据帧格式
SPI1->CR1 |= 1<<2; //SPI主机
SPI1->CR1 |= 0<<11; //8bit数据格式
4. 设置时钟极性和相位极性
SPI1->CR1 =1<<1; //空闲模式下SCK=1,CPOL=1
SPI1->CR1 = 1<<0; //数据采样从第二个时钟边沿开始,CPHA=1
5. 传输速度和LSBfIRST帧格式设置
SPI1->CR1 |=7<<3; //设置SCK波特率为
SPI1->CR1 |=0<<7; //MSBfirst
6. 使能SPI1
SPI1->CR1 |=1<<6; //SPI设备使能
三、SPI库配置
Void SPI1_Configuration(void)
{
SPI_InitTypeDef SPI_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP; //NSS推挽输出
GPIO_Init(GPIOB,&GPIO_InitStructure);
SPI_InitStructure.SPI_Direction=SPI_Direction_2Lines_FullDuplex;//SPI设置为双线双向全双工
SPI_InitStructure.SPI_Mode=SPI_Mode_Master;//设置为主SPI
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位控制,SSI=1,SSM=1
SPI_InitStructure.SPI_BaudRatePrescaler=SPI_BaudRatePrescaler_8;//波特率预分频值为 8
SPI_InitStructure.SPI_FirstBit=SPI_FirstBit_MSB;//数据传输从MSB位开始
SPI_InitStructure.SPI_CRCPolynomial = 7;//SPI_CRCPolynomial定义了用于CRC值计算的多项式
SPI_Init(SPI1,&SPI_InitStructure);//参数传送
SPI_Cmd(SPI1,ENABLE); //使能 SPI
}
四、SPI数据接收/发送
寄存器版
u8 SPI1_ReadWriteByte(u8 TxData)
{
While((SPI1->SR &1<<1) ==0); //发送区非空循环,TXE=1为空
SPI1->DR = TxData;
While((SPI1->SR &1<<0) ==0); //等待接收到一个字节,RXNE=1为空
Return SPI1->DR;
}
库操作版:
u8 SPI_SendWriteByte(u8 byte) //SPI 读写函数
{
while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE)==RESET);//判断发送缓冲区是否为空
SPI_I2S_SendData(SPI1,byte);//发送数据
while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_RXNE)==RESET);//判断接收缓冲区是否是空的
return SPI_I2S_ReceiveData(SPI1);//接收数据,并返回到接收缓冲区
}
五、SPI接收中断
中断开启:SPI_I2S_ITConfig(SPI1,SPI_I2S_IT_RXNE,ENABLE);
中断清除:SPI_I2S_ClearFlag(SPI1,SPI_I2S_IT_RXNE); //实际工作中,读取SPI_DR寄存器时将清除RXNE位,因此可以不用清除中断标志,同USART。