ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

STM32开发项目:硬件SPI的配置与使用

2020-05-08 10:02:42  阅读:774  来源: 互联网

标签:信号线 硬件 SCK STM32 SPI InitStructure GPIO NSS


目录

项目背景

笔者在一个高速数据采集项目中进行开发时,遇到了快速频繁读取ADC数据的需求,此时的软件模拟SPI已经无法满足,只能尝试采用硬件SPI对ADC进行读写。

关于SPI外设

SPI 协议是由摩托罗拉公司提出的通讯协议(Serial Peripheral Interface),即串行外围设备接口,是一种高速全双工的通信总线。它被广泛地使用在 ADC、LCD 等设备与 MCU 间,要求通讯速率较高的场合。

与I2C通讯不同,应用SPI接口的器件很多都对通讯速率有一定的要求,使用软件模拟SPI通讯有两个主要的缺点:1.增大MCU的负载,尤其是在大量数据高频收发时;2.数据传输速度无法得到控制与保证。

硬件连接

SPI通讯使用3条总线及片选线,3条总线分别为SCK、MOSI、MISO以及片选线为SS,它们的作用介绍如下:

  • SS( Slave Select):从设备选择信号线,常称为片选信号线,也称为 NSS、CS,以下用 NSS 表示。当有多个 SPI从设备与 SPI主机相连时,设备的其它信号线 SCK、MOSI及 MISO同时并联到相同的 SPI总线上,即无论有多少个从设备,都共同只使用这 3条总线;而每个从设备都有独立的这一条 NSS 信号线,本信号线独占主机的一个引脚,即有多少个从设备,就有多少条片选信号线。I2C 协议中通过设备地址来寻址、选中总线上的某个设备并与其进行通讯;而 SPI 协议中没有设备地址,它使用 NSS 信号线来寻址,当主机要选择从设备时,把该从设备的 NSS 信号线设置为低电平,该从设备即被选中,即片选有效,接着主机开始与被选中的从设备进行 SPI通讯。所以 SPI通讯以 NSS 线置低电平为开始信号,以 NSS 线被拉高作为结束信号。
  • SCK (Serial Clock):时钟信号线,用于通讯数据同步。它由通讯主机产生,决定了通讯的速率,不同的设备支持的最高时钟频率不一样,如 STM32 的 SPI 时钟频率最大为fpclk/2,两个设备之间通讯时,通讯速率受限于低速设备。
  • MOSI (Master Output, Slave Input):主设备输出/从设备输入引脚。主机的数据从这条信号线输出,从机由这条信号线读入主机发送的数据,即这条线上数据的方向为主机到从机。
  • MISO(Master Input,,Slave Output):主设备输入/从设备输出引脚。主机从这条信号线读入数据,从机的数据由这条信号线输出到主机,即在这条线上数据的方向为从机到主机。
    SPI通讯的硬件连接示意图

基本通讯过程

下图所示是一个SPI主机的通讯时序。NSS、SCK、MOSI 信号都由主机控制产生,而 MISO 的信号由从机产生,主机通过该信号线读取从机的数据。MOSI 与 MISO 的信号只在 NSS 为低电平的时候才有效,在 SCK 的每个时钟周期 MOSI 和 MISO 传输一位数据。
在这里插入图片描述在标号(1)处,NSS 信号线由高变低,是 SPI 通讯的起始信号。NSS 是每个从机各自独占的信号线,当从机在自己的 NSS 线检测到起始信号后,就知道自己被主机选中了,开始准备与主机通讯。在图中的标号(6)处,NSS 信号由低变高,是 SPI 通讯的停止信号,表示本次通讯结束,从机的选中状态被取消。

SPI 使用 MOSI 及 MISO 信号线来传输数据,使用 SCK 信号线进行数据同步。MOSI 及MISO 数据线在 SCK 的每个时钟周期传输一位数据,且数据输入输出是同时进行的。数据传输时,MSB 先行或 LSB 先行并没有作硬性规定,但要保证两个 SPI 通讯设备之间使用同样的协定,一般都会采用上图所示的 MSB 先行模式

观察图中的(2)~(5)标号处,MOSI 及 MISO 的数据在 SCK 的上升沿期间变化输出,在SCK 的下降沿时被采样。即在 SCK 的下降沿时刻,MOSI 及 MISO 的数据有效,高电平时表示数据“1”,为低电平时表示数据“0”。在其它时刻,数据无效,MOSI及 MISO 为下一次表示数据做准备。SPI 每次数据传输可以 8 位或 16 位为单位,每次传输的单位数不受限制。

工作模式

SPI 一共有四种通讯模式,它们的主要区别是总线空闲时 SCK 的时钟状态以及数据采样时刻。为方便说明,在此引入“时钟极性 CPOL”和“时钟相位 CPHA”的概念。时钟极性 CPOL 是指 SPI 通讯设备处于空闲状态时,SCK 信号线的电平信号(即 SPI 通讯开始前、 NSS 线为高电平时 SCK 的状态)。CPOL=0 时, SCK 在空闲状态时为低电平,CPOL=1 时,则相反。时钟相位 CPHA 是指数据的采样的时刻,当 CPHA=0 时,MOSI 或 MISO 数据线上的信号将会在 SCK 时钟线的“奇数边沿”被采样。当 CPHA=1 时,数据线在 SCK 的“偶数边沿”采样。

CPHA=0 时的 SPI 通讯模式:
CPHA=0 时的 SPI 通讯模式CPHA=1 时的 SPI 通讯模式:
在这里插入图片描述

由 CPOL 及 CPHA 的不同状态,SPI 分成了四种模式,如下表所示,主机与从机需要工作在相同的模式下才可以正常通讯,实际中采用较多的是“模式 0”与“模式 3”。

SPI模式 CPOL CPHA 时钟空闲 采样时刻
0 0 0 low 奇边沿
1 0 1 low 偶边沿
2 1 0 high 奇边沿
3 1 1 high 偶边沿

配置流程

GPIO端口功能配置:

void GPIO_Config()
{
	GPIO_InitTypeDef GPIO_InitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
	GPIO_AFIODeInit();

	//SPI-Clock:PA5 SPI-MISO:PA6 SPI-MOSI:PA7
	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);
}

SPI外设配置:

void SPI_Config()
{
	SPI_InitTypeDef SPI_InitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
	
	SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
	SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
	SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
	SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
	SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_32;
	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
	SPI_InitStructure.SPI_CRCPolynomial = 7;
	
	SPI_Init(SPI1, &SPI_InitStructure);

//	SPI_I2S_ITConfig(SPI1, SPI_I2S_IT_RXNE, ENABLE);
//	SPI_I2S_ClearFlag(SPI1, SPI_I2S_IT_RXNE | SPI_I2S_IT_TXE);
}

在某处使能SPI:

	SPI_Cmd(SPI1, ENABLE);

如果使能了SPI中断,需要在NVIC中配置其中断的主从优先级。

特别注意

硬件SPI写入数据的时候记得读取,不然会一直读出0xFF。因为SPI为双线结构,只要时钟线有信号,两条线上都会有信号,如果只发送数据而不同步读取数据,会造成溢出标志OVR被置1。

Overrun flag (OVR)
This flag is set when data are received and the previous data have not yet been read from SPI_DR. As a result, the incoming data are lost. An interrupt may be generated if the ERRIE bit is set in SPI_CR2.
In this case, the receive buffer contents are not updated with the newly received data from the transmitter device. A read operation to the SPI_DR register returns the previous correctly received data. All other subsequently transmitted half-words are lost.
Clearing the OVR bit is done by a read operation on the SPI_DR register followed by a read access to the SPI_SR register.

使用下面的函数可以通过硬件SPI安全地读写数据:

uint8_t SPI_I2S_ReadWriteData(uint8_t Tdata)
{
	while ((SPI1->SR & SPI_I2S_FLAG_TXE) == (uint16_t) RESET)
		;
	SPI1->DR = Tdata;
	while ((SPI1->SR & SPI_I2S_FLAG_RXNE) == (uint16_t) RESET)
		;
	///注意:接收区满了要及时读出来,否则将不能接收下一个字节数据
	return SPI1->DR;
}

参考文献

标签:信号线,硬件,SCK,STM32,SPI,InitStructure,GPIO,NSS
来源: https://blog.csdn.net/u013441358/article/details/105955058

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有