STM32基础之SPI

发布者:RainbowDreamer最新更新时间:2022-04-20 来源: eefocus关键字:STM32  基础  SPI 手机看文章 扫描二维码
随时随地手机看文章

SPI简介

SPI协议是由摩托罗拉公司提出的通讯协议(Serial Peripheral Interface),即串行外围设备接口,是一种高速全双工的通信总线。


SPI接口主要应用在EEPROM、FLASH、实时时钟、AD转换器,还有数字信号处理器和数字信号解码器之间。SPI是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,正是出于这种简单易用的特性,现在越来越多的芯片集成了这种通信协议,比如AT91RM9200。


SPI分为主、从两种模式,一个SPI通讯系统需要包含一个(且只能是一个)主设备,一个或多个从设备。SPI接口的读写操作,都是由主设备发起。当存在多个从设备时,通过各自的片选信号进行管理。


优点:支持全双工通信、通信简单、数据传输速率快;

缺点:没有指定的流控制,没有应答机制确认是否接收到数据,所以跟IIC总线协议比较在数据的可靠性上有一定的缺陷。


SPI引脚说明

在这里插入图片描述

SS:从设备选择信号线,常称为片选信号线,也称为NSS、CS。

每个从设备都有独立的这一条SS信号线,本信号线独占主机的一个引脚,即有多少个从设备,就有多少条片选信号线。当主机要选择从设备时,把该从设备的SS信号线设置为低电平,该从设备即被选中,即片选有效,接着主机开始与被选中的从设备进行SPI通讯。所以SPI通讯以SS线置低电平为开始信号,以SS线被拉高作为结束信号。


SCK (Serial Clock):时钟信号线,用于通讯数据同步

每它由通讯主机产生,决定了通讯的速率,不同的设备支持的最高时钟频率不一样,如STM32的SPI时钟频率最大为fpclk/2,两个设备之间通讯时,通讯速率受限于低速设备。


MOSI (Master Output, Slave Input):主设备输出/从设备输入引脚。

MISO(Master Input,,Slave Output):主设备输入/从设备输出引脚。

SPI协议层

SPI基本通讯过程

在这里插入图片描述

标号①处,NSS信号线由高变低,是SPI通讯的起始信号。NSS是每个从机各自独占的信号线,当从机检在自己的NSS线检测到起始信号后,就知道自己被主机选中了,开始准备与主机通讯。


在图中的标号⑥处,NSS信号由低变高,是SPI通讯的停止信号,表示本次通讯结束,从机的选中状态被取消。


SPI使用MOSI及MISO信号线来传输数据,使用SCK信号线进行数据同步。MOSI及MISO数据线在SCK的每个时钟周期传输一位数据,且数据输入输出是同时进行的。


SPI通讯模式

在这里插入图片描述

时钟极性CPOL是指SPI通讯设备处于空闲状态时,SCK信号线的电平信号(即SPI通讯开始前、 NSS线为高电平时SCK的状态)。CPOL=0时, SCK在空闲状态时为低电平,CPOL=1时,则相反。


时钟相位CPHA是指数据的采样的时刻,当CPHA=0时,MOSI或MISO数据线上的信号将会在SCK时钟线的“奇数边沿”被采样。当CPHA=1时,数据线在SCK的“偶数边沿”采样。


SPI通信有4种不同的模式,不同的从设备可能在出厂是就是配置为某种模式,这是不能改变的;但我们的通信双方必须是工作在同一模式下,所以我们可以对我们的主设备的SPI模式进行配置,通过CPOL(时钟极性)和CPHA(时钟相位)来控制我们主设备的通信模式,具体如下:

在这里插入图片描述

STM32的SPI架构剖析

在这里插入图片描述

通讯引脚

STM32芯片有多个SPI外设,它们的SPI通讯信号引出到不同的GPIO引脚上,使用时必须配置到这些指定的引脚,以《STM32F10x规格书》为准。

其中SPI1是APB2上的设备,最高通信速率达36Mbtis/s,SPI2、SPI3是APB1上的设备,最高通信速率为18Mbits/s。除了通讯速率,在其它功能上没有差异。


时钟控制逻辑

SCK线的时钟信号,由波特率发生器根据“控制寄存器CR1”中的BR[0:2]位控制,该位是对fpclk时钟的分频因子,对fpclk的分频结果就是SCK引脚的输出时钟频率

数据控制逻辑

SPI的MOSI及MISO都连接到数据移位寄存器上,数据移位寄存器的数据来源来源于接收缓冲区及发送缓冲区。


通过写SPI的“数据寄存器DR”把数据填充到发送缓冲区中。


通过读“数据寄存器DR”,可以获取接收缓冲区中的内容。


其中数据帧长度可以通过“控制寄存器CR1”的“DFF位”配置成8位及16位模式;配置“LSBFIRST位”可选择MSB先行还是LSB先行。


整体控制逻辑

整体控制逻辑负责协调整个SPI外设,控制逻辑的工作模式根据“控制寄存器(CR1/CR2)”的参数而改变,基本的控制参数包括前面提到的SPI模式、波特率、LSB先行、主从模式、单双向模式等等。


在外设工作时,控制逻辑会根据外设的工作状态修改“状态寄存器(SR)”,只要读取状态寄存器相关的寄存器位,就可以了解SPI的工作状态了。除此之外,控制逻辑还根据要求,负责控制产生SPI中断信号、DMA请求及控制NSS信号线。


实际应用中,一般不使用STM32 SPI外设的标准NSS信号线,而是更简单地使用普通的GPIO,软件控制它的电平输出,从而产生通讯起始和停止信号。


通讯过程

控制NSS信号线,产生起始信号;

把要发送的数据写入到“数据寄存器DR”中,该数据会被存储到发送缓冲区;

通讯开始,SCK时钟开始运行。MOSI把发送缓冲区中的 数据一位一位地传输出去;MISO则把数据一位一位地存储进接收缓冲区中;

当发送完一帧数据的时候,“状态寄存器SR”中的“TXE标志位”会被置1,表示传输完一帧,发送缓冲区已空;类似地,当接收完一帧数据的时候,“RXNE标志位”会被置1,表示传输完一帧,接收缓冲区非空;

等待到“TXE标志位”为1时,若还要继续发送数据,则再次往“数据寄存器DR”写入数据即可;等待到“RXNE标志位”为1时,通过读取“数据寄存器DR”可以获取接收缓冲区中的内容。


假如使能了TXE或RXNE中断,TXE或RXNE置1时会产生SPI中断信号,进入同一个中断服务函数,到SPI中断服务程序后,可通过检查寄存器位来了解是哪一个事件,再分别进行处理。也可以使用DMA方式来收发“数据寄存器DR”中的数据。


SPI编程

SPI相关配置库函数

初始化函数

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


作用:初始化SPI的相关参数,比如方向(全双工)、主从模式、数据大小、CPOL、CPHA、片选软件模式、预分频系数等。


3个使能函数

void SPI_Cmd(SPI_TypeDef* SPIx, FunctionalState NewState);

void SPI_I2S_ITConfig(SPI_TypeDef* SPIx, uint8_t SPI_I2S_IT, FunctionalState NewState);

void SPI_I2S_DMACmd(SPI_TypeDef* SPIx, uint16_t SPI_I2S_DMAReq, FunctionalState NewState);


作用:使能SPI接口;使能SPI中断;使能SPI的DMA功能。


2个数据传输函数

void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data);

uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx);


4个状态位函数

FlagStatus SPI_I2S_GetFlagStatus(SPI_TypeDef* SPIx, uint16_t SPI_I2S_FLAG);

void SPI_I2S_ClearFlag(SPI_TypeDef* SPIx, uint16_t SPI_I2S_FLAG);

ITStatus SPI_I2S_GetITStatus(SPI_TypeDef* SPIx, uint8_t SPI_I2S_IT);

void SPI_I2S_ClearITPendingBit(SPI_TypeDef* SPIx, uint8_t SPI_I2S_IT);


作用:前两者用于获得和清除SPI的各种状态位;后两者则针对SPI的中断标志位。


SPI一般步骤

实验目标:利用SPI2进行初始化等操作。


配置相关引脚的复用功能,使能SPIx时钟。调用函数:void GPIO_Init();

初始化SPIx,设置SPIx工作模式。调用函数:void SPI_Init();

使能SPIx。调用函数:void SPI_Cmd();

SPI传输数据。调用函数:void SPI_I2S_SendData()uint16_t SPI_I2S_ReceiveData();

查看SPI传输状态。调用函数:SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE)。

void SPI2_Init(void)

{

  GPIO_InitTypeDef GPIO_InitStructure;

  SPI_InitTypeDef  SPI_InitStructure;

 

RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE );//PORTB时钟使能 

RCC_APB1PeriphClockCmd( RCC_APB1Periph_SPI2,  ENABLE );//SPI2时钟使能

 

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //PB13/14/15复用推挽输出 

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIOB

 

  GPIO_SetBits(GPIOB,GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15);  //PB13/14/15上拉

 

SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;  //设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工

SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //设置SPI工作模式:设置为主SPI

SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //设置SPI的数据大小:SPI发送接收8位帧结构

SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; //串行同步时钟的空闲状态为高电平

SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; //串行同步时钟的第二个跳变沿(上升或下降)数据被采样

SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制

SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; //定义波特率预分频的值:波特率预分频值为256

SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始

SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC值计算的多项式

SPI_Init(SPI2, &SPI_InitStructure);  //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器

 

SPI_Cmd(SPI2, ENABLE); //使能SPI外设

SPI2_ReadWriteByte(0xff);//启动传输  

 

 

}   

//SPI 速度设置函数

//SpeedSet:

//SPI_BaudRatePrescaler_2   2分频   

//SPI_BaudRatePrescaler_8   8分频   

//SPI_BaudRatePrescaler_16  16分频  

//SPI_BaudRatePrescaler_256 256分频 

  

void SPI2_SetSpeed(u8 SPI_BaudRatePrescaler)

{

  assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));

SPI2->CR1&=0XFFC7;

SPI2->CR1|=SPI_BaudRatePrescaler; //设置SPI2速度 

SPI_Cmd(SPI2,ENABLE); 

 

 

//SPIx 读写一个字节

//TxData:要写入的字节

//返回值:读取到的字节

u8 SPI2_ReadWriteByte(u8 TxData)

{

u8 retry=0;

while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET) //检查指定的SPI标志位设置与否:发送缓存空标志位

{

retry++;

if(retry>200)return 0;

}   

SPI_I2S_SendData(SPI2, TxData); //通过外设SPIx发送一个数据

retry=0;

 

while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET) //检查指定的SPI标志位设置与否:接受缓存非空标志位

{

retry++;

if(retry>200)return 0;

}       

return SPI_I2S_ReceiveData(SPI2); //返回通过SPIx最近接收的数据     

}

关键字:STM32  基础  SPI 引用地址:STM32基础之SPI

上一篇:STM32基础之中断--外部中断
下一篇:STM32基础之IIC

推荐阅读最新更新时间:2024-11-17 05:09

“国十条”将使医疗器械子行业率先受益
  11月5日,国务院总理温家宝主持召开国务院常务会议,提出了当前进一步扩大内需、促进经济增长的十项措施,其中第四条就要求“加强基层医疗卫生服务体系建设”。   措施与医改要求保持一致性   此次“国十条”中关于“加强基层医疗卫生服务体系建设”的政策与10月14日出台的医改方案征求意见稿中的相关要求是一致的。在医改方案中要求了进一步完善医疗服务体系,大力发展农村医疗卫生服务体系和完善以社区卫生服务为基础的新型城市医疗卫生服务体系,同时将健全基层医疗卫生服务体系作为近期工作的重点。   体现中央农村改革发展精神,加快和推动医改进程   此次国务院将“加强基层医疗卫生服务体系建设”政策单独提出来,也体现了中共十七届三中全会提
[医疗电子]
stm32之DMA中断
AD转换之DMA 1、DMA的配置 //DMA的配置 void DMA_Configuration(void) { /* 允许 DMA1 */ RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); /* DMA通道1*/ DMA_DeInit(DMA1_Channel1); DMA_InitStructure.DMA_PeripheralBaseAddr =(u32)( &(ADC1- DR)); //ADC1数据寄存器 DMA_InitStructure.DMA_MemoryBaseAddr = (u32)ADCCov; //获取ADC的数组 DMA_InitStructur
[单片机]
STM32 外设使用的基本配置顺序
引脚重映射配置过程(串口1为例): 1.使能GPIO时钟(重映射后的IO); 使能功能外设时钟(例如串口1); 2.使能AFIO时钟。重映射必须使能AFIO时钟: RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); 3.开启重映射。 GPIO_PinRemapConfig(GPIO_Remap_USART1, ENABLE); 根据第一个参数,来确定是部分重映射还是全部重映射 中断优先级设置步骤 1.系统运行后先设置中断优先级分组。 调用函数:
[单片机]
关于印发《江门市加快推进新能源电动汽车充电基础设施建设三年行动计划(2022-2024年)》的通知
各县(市、区)人民政府、市各相关部门:   《江门市加快推进新能源电动汽车充电基础设施建设三年行动计划(2022-2024年)》已经市人民政府同意,现印发给你们,请结合实际认真组织实施。   特此通知。 江门市发展和改革局 2022年12月15日 江门市加快推进新能源电动汽车充电基础设施建设三年行动计划(2022-2024年)   为贯彻落实国务院和省委省政府、市委市政府关于加快新能源电动汽车推广应用,完善新能源电动汽车充电基础设施配套的工作部署(以下简称“充电基础设施”),切实加快江门市充电基础设施建设,促进电动汽车推广普及。根据《国家发展改革委等部门关于进一步提升电动汽车充电基
[新能源]
STM32】Keil中Debug模式下全局变量的实时查看
在使用Keil开发环境编写stm32程序时我们经常会用到硬件在线仿真的功能,能在watch窗口中看到我们在程序中定义的全局变量。我们希望在程序实时运行中看到变量的变化情况,但有时不能实时查看,原因是软件的设置不对。 在Debug模式下,点击菜单栏下的View,在下面有一个Periodic Window update 点一下,选定一下就行了。
[单片机]
单片机STM32的时钟图文理解
其中,高速时钟(HSE和HSI)提供给芯片主体的主时钟.低速时钟(LSE和LSI)只是提供给芯片中的RTC(实时时钟)及独立看门狗使用,图中可以看出高速时钟也可以提供给RTC。内部时钟是在芯片内部RC振荡器产生的,起振较快,所以时钟在芯片刚上电的时候,默认使用内部高速时钟。而外部时钟信号是由外部的晶振输入的,在精度和稳定性上都有很大优势,所以上电之后我们再通过软件配置,转而采用外部时钟信号. 高速外部时钟(HSE):以外部晶振作时钟源,晶振频率可取范围为4~16MHz,我们一般采用8MHz的晶振。 高速内部时钟(HSI): 由内部RC振荡器产生,频率为8MHz,但不稳定。 低速外部时钟(LSE):以外部晶振作时钟源,主要提
[单片机]
单片机<font color='red'>STM32</font>的时钟图文理解
STM32 的内部温度传感器
STM32 有一个内部的温度传感器,可以用来测量 CPU 及周围的温度(TA)。该温度传感器在内部和 ADCx_IN16 输入通道相连接,此通道把传感器输出的电压转换成数字值。温度传感器模拟输入推荐采样时间是 17.1 s。 STM32 的内部温度传感器支持的温度范围为:-40~125度。精度比较差,为 1.5℃左右。 STM32 内部温度传感器的使用很简单,只要设置一下内部 ADC,并激活其内部通道就差不多了。 STM32 内部温度传感器使用的步骤了,如下: 1)设置 ADC,开启内部温度传感器。 ADC_TempSensorVrefintCmd(ENABLE); //开启内部温度传感器功能: 2)读取通道 16 的 AD
[单片机]
GD32开发实战指南(基础篇) 第14章 内部温度传感器
开发环境: MDK:Keil 5.30 开发板:GD32F207I-EVAL MCU:GD32F207IK 1 内部温度传感器工作原理 GD32 有一个内部的温度传感器,可以用来测量 CPU 及周围的温度(TA)。该温度传感器在内部和 ADCx_IN16 输入通道相连接,此通道把传感器输出的电压转换成数字值。温度传感器模拟输入推荐采样时间是 17.1μs。GD32 的内部温度传感器支持的温度范围为: -40~125度。精度比较差,为±1.5℃左右。 GD32 内部温度传感器的使用很简单,只要设置一下内部 ADC,并激活其内部通道就差不多了。关于 ADC 的设置,我们在前面的章节已经进行了详细的介绍,这里就不再多说。接下来我们介
[单片机]
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved