spi通信协议

学单片机必须要掌握的集中串行通信协议中spi就是其中之一。

下面主要是讲解下个人对于spi通信时序图以及通信协议的理解(有说错的请大指出,小弟也是刚学的有很多不懂)。

下面进入正题:

 SPI简介

SPI就是串行外围设备接口,它是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,这四根线的定义分别是:

 MISO:主设备数据输入,从设备数据输出;

 MOSI:主设备数据输出,从设备数据输入;

 SCLK:时钟信号,由主设备产生;

 CS:从设备片选信号,由主设备控制

SPI为了和外设进行数据交换,根据外设工作要求(具体得看外设spi读写时序图,有时序图编写驱动代码),其输出串行同步时钟极性和相位可以进行配置,时钟极性(CPOL)对传输协议没有重大的影响。如果CPOL=0,串行同步时钟的空闲状态为低电平;如果CPOL=1,串行同步时钟的空闲状态为高电平。时钟相位(CPHA)能够配置用于选择两种不同的传输协议之一进行数据传输。如果CPHA=0,在串行同步时钟的第一个跳变沿(上升或下降)数据被采样;如果CPHA=1,在串行同步时钟的第二个跳变沿(上升或下降)数据被采样。

spi内部结构图:

SPI总线数据传输时序图如下图:

总结:spi通信为4线全双工,spi通信由于时钟极性(CPOL)时钟相位(CPHA)的不同可以配置为4种模式。完成一位数据传输是在一个周期两个跳变沿内完成的,一个跳变沿采样(可以理解为:内部移位寄存器读取io值),一个跳变沿数据输出(可以理解为:内部移位寄存器将最高位移动到io口上)。

具体移动过程参考此帖子:http://bbs.elecfans.com/jishu_441914_1_1.html

时钟前沿输出:

时钟后沿采样:

就这样一个周期完成一位数据的交换。

stm32spi主模式下数据的发送与接收:

数据发送过程
当写入数据至发送缓冲器时,发送过程开始。
在发送第一个数据位时,数据字被并行地(通过内部总线)传入移位寄存器,而后串行地移出到MOSI脚上; MSB在先还是LSB在先,取决于SPI_CR1寄存器中的LSBFIRST位的设置。数据从发送缓冲器传输到移位寄存器时TXE标志将被置位,如果设置了SPI_CR1寄存器中的TXEIE位,将产生中断。
数据接收过程
对于接收器来说,当数据传输完成时:
● 传送移位寄存器里的数据到接收缓冲器,并且RXNE标志被置位。
● 如果设置了SPI_CR2寄存器中的RXNEIE位,则产生中断。
在最后采样时钟沿, RXNE位被设置,在移位寄存器中接收到的数据字被传送到接收缓冲器。读SPI_DR寄存器时, SPI设备返回接收缓冲器中的数据。读SPI_DR寄存器将清除RXNE位。
一旦传输开始,如果下一个将发送的数据被放进了发送缓冲器,就可以维持一个连续的传输流。在试图写发送缓冲器之前,需确认TXE标志应该为’1’。

总结;  发送端:数据---DR寄存器---->缓冲区----->移位寄存器------>一位一位的发送到主机输出口

接收端:读取miso口数据(从机发来)到移位寄存器---->读取8位后放入缓冲区-----DR寄存器

程序的读写数据都是对缓冲区操作的。发送缓冲寄存器为空TXE=1,接收缓冲寄存器不为空RXNE=1,所以spi读写数据过程:

发送数据时:判断发送缓冲寄存器是否为空为空则发送,

接收数据时:判断接收缓冲寄存器是否非空,是则读取数据。

例如:

u8 SPI1_ReadWriteByte(u8 TxData)
{
       u8 retry=0;
       while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET) //检查指定的 SPI
                                                                                                                          //标志位设置与否:发送缓存空标志位
{
        retry++;
       if(retry>200)

       return 0;
 }
       SPI_I2S_SendData(SPI1, TxData); //通过外设 SPIx 发送一个数据
       retry=0;
       while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET)//检查指定的
                                                                                                                         SPI 标志位设置与否:接受缓存非空标志位
{
        retry++;
        if(retry>200)return 0;
}
         return SPI_I2S_ReceiveData(SPI1); //返回通过 SPIx 最近接收的数据
}
stm32 spi初始化过程:

配置步骤
1. 通过SPI_CR1寄存器的BR[2:0]位定义串行时钟波特率。
2. 选择CPOL和CPHA位,定义数据传输和串行时钟间的相位关系(见图212)。
3. 设置DFF位来定义8位或16位数据帧格式。
4. 配置SPI_CR1寄存器的LSBFIRST位定义帧格式。
5. 如果需要NSS引脚工作在输入模式,硬件模式下,在整个数据帧传输期间应把NSS脚连接
到高电平;在软件模式下,需设置SPI_CR1寄存器的SSM位和SSI位。如果NSS引脚工作
在输出模式,则只需设置SSOE位。
6. 必须设置MSTR位和SPE位(只当NSS脚被连到高电平,这些位才能保持置位)。
在这个配置中, MOSI引脚是数据输出,而MISO引脚是数据输入。
例如:

SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //设置 SPI 全双工
SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //设置 SPI 工作模式:设置为主 SPI
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; // 8 位帧结构
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;//选择了串行时钟的稳态:时钟悬空高
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; //数据捕获于第二个时钟沿
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS 信号由硬件管理
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; //预分频 256
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //数据传输从 MSB 位开始
SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC 值计算的多项式
SPI_Init(SPI1, &SPI_InitStructure); //②根据指定的参数初始化外设 SPIx 寄存器
SPI_Cmd(SPI1, ENABLE); //③使能 SPI 外设
 

猜你喜欢

转载自blog.csdn.net/zj490044512/article/details/83867594