参考博客 : https://blog.csdn.net/ivy_reny/article/details/78189058
一、概述
SPI(Serial Peripheral Interface,串行外围设备接口),是Motorola公司提出的一种同步串行接口技术,是一种高速、全双工、同步通信总线,在芯片中只占用四根管脚用来控制及数据传输,节约了芯片pin的数目,同时为PCB在布局上节省了空间。正是由于这种简单易用的特性,现在越来越多的芯片上都集成了SPI技术。
SPI主要用于EEPROM、Flash、RTC(实时时钟)、ADC(数模转换器)、DSP(数字信号处理器)以及数字信号解码器上。目前应用中的数据速率可达几Mbps。
二、特点
1、采用主从模式(Master-Slave)的控制方式,支持单Master多Slave
SPI规定了两个SPI设备之间通信必须由主设备Master来控制从设备Slave。一个Master可以通过提供clock以及对Slave进行片选(Slave Select)来控制多个Slave。SPI协议还规定Slave设备的clock由Master通过SCK管脚提供给Slave,Slave本身不能产生或控制clock,没有clock则Slave不能正常工作。
2、采用同步方式(Synchronous)传输数据
Master会根据将要交换的数据产生相应的时钟脉冲,组成时钟信号,时钟信号通过时钟极性(CPOL)和时钟相位(CPHA)控制两个SPI设备何时交换数据以及何时对接收数据进行采样,保证数据在两个设备之间是同步传输的。
3、数据交换
SPI设备间的数据传输被称为数据交换,因为SPI协议规定一个SPI设备不能在数据通信过程中仅仅充当一个发送者(Transmitter)或者接受者(Receiver)。在每个clock周期内,SPI设备都会发送并接收1 bit数据,相当于有1 bit数据被交换了。数据传输高位在前,低位在后(MSB first)。
在数据传输过程中,每次接收到的数据必须在下一次数据传输之前被采样,如果之前接收到的数据没有被读取,那么这些已经接收完成的数据将有可能被丢弃,导致SPI物理模块最终失效。因此在程序中一般都会在SPI传输完数据后,读取SPI设备里的数据,即使这些数据在程序里是无效的。。
SPI总线四种工作方式
SPI 模块为了和外设进行数据交换,根据外设工作要求,其输出串行同步时钟极性和相位可以进行配置,时钟极性(CPOL)对传输协议没有重大的影响。
时序详解:
CPOL:时钟极性选择,为0时SPI总线空闲为低电平,为1时SPI总线空闲为高电平
CPHA:时钟相位选择,为0时在SCK第一个跳变沿采样,为1时在SCK第二个跳变沿采样
三、数据传输
SPI是一个环形总线结构,由SS (CS)、SCK、SDI、SDO构成,时序很简单,在SCK的控制下,两个双向移位寄存器进行数据交换。寄存器中的内容全部移出时,相当于完成了两个寄存器内容的交换。SSPSR控制数据移入移出SSPBUF,controller确定SPI总线的通信模式。
SSPBUF:Synchronous Serial Port Buffer,泛指SPI设备里面的内部缓冲区,一般在物理上是以FIFO的形式,保存传输过程中的临时数据;
SSPSR:Synchronous Serial Port Shift Register,泛指SPI设备里面的移位寄存器,根据设置好的数据位宽把数据移入或移出SSPBUF;
Controller:泛指SPI设备里面的控制寄存器,通过配置寄存器来设置SPI总线的传输模式。
通常情况下,只需要对四个pin进行编程即可控制SPI设备之间的数据通信:
SCK(Serial Clock):主要作用是Master向Slave传输时钟信号,控制数据交换的时机和速率;
SS/CS(Slave Select/Chip Select):用于Master片选Slave,使被选中的Slave能够被Master访问;
SDO/MOSI(Serial Data Output/Master Out Slave In):在Master上也被称为Tx-channel,作为数据的出口,主要用于SPI设备发送数据;
SDI/MISO(Serial Data Input/Master In Slave Out):在Master上也被称为Rx-channel,作为数据的入口,主要用于SPI设备接收数据。
四、传输时序
SPI接口有四种不同的数据传输时序,取决于时钟极性(CPOL)和时钟相位(CPHA)的组合。时钟相位设置读取数据和发送数据的时钟沿。主机和从机发送数据是同时完成的,接收数据也是同时完成的。
时钟极性CPOL:SPI在空闲时,时钟信号是高电平还是低电平,即SCLK发送8 bit数据之前和之后的状态。CPOL=0,空闲电平为低电平,CPOL=1,空闲电平为高电平。
时钟相位CPHA:数据采样在时钟的第几个边沿。CPHA=0,在每个周期的第一个时钟沿采样,CPHA=1,在每个周期的第二个时钟沿采样。
Bit1为MSB,Bit8为LSB。假设CPOL=0,CPHA=0。在SCK的第一个时钟周期,在时钟的前沿采样数据(上升沿),在时钟的后沿输出数据。先看主器件,主器件的输出口(MOSI)输出数据bit1,在时钟的前沿被从器件采样,那主器件是何时输出bit1的呢?bit1的输出时刻实际上在SCK信号有效以前,比SCK的上升沿还要早半个时钟周期,bit1的输出时刻与SSEL信号没有关系。再来看从器件,主器件的输入口MISO同样是在时钟的前沿采样从器件输出的bit1的,那从器件又是在何时输出bit1的呢?从器件实在SSEL信号有效后,立即输出bit1,尽管此时SCK信号还没有生效。
SSPSR
是SPI设备内部的移位寄存器,根据SPI时钟信号状态,往SSPBUF里移入或移出数据,每次移动的数据大小由Bus-width和Channel-width决定。
Bus-width的作用是指地址总线到Master之间数据传输的单位。例如要往Master的SSPBUF写入16 byte大小的数据。首先,给Master的配置寄存器设置Bus-width为Byte,然后往Master的Tx-Data移位寄存器在地址总线的入口写入数据,每次写入1 byte大小的数据,写完1 byte数据后,Master里面的Tx-data移位寄存器会自动把地址总线传来的1 byte数据移入SSPBUF里,上述动作一共重复执行16次。
Channel-width的作用是指定Master与Slave之间数据传输的单位,与Bus-width类似,Master内部的移位寄存器会根据channel-width自动把数据从Master-SSPBUF里通过Master-SDO管脚搬运到Slave-SDI管脚,Slave-SSPSR再每次把接收的数据移入Slave-SSPBUF里。
通常情况下,Bus-width总是大于或等于channel-width,这样保证不会出现因Master与Slave之间数据交换的频率比地址总线和Master数据交换频率快,导致SSPBUF里面存放数据为无效数据的情况发生。
SSPBUF
在每个时钟周期内,Master与Slave之间交换的数据其实都是SPI内部移位寄存器从SSPBUF里面拷贝的,可以通过往SSPBUF对应的寄存器(Tx-Data/Rx-Data register)里读写数据,间接操控SPI内部的SSPBUF。
在发送数据之前,先往Master的Tx-Data寄存器写入要发送出去的数据,这些数据会被Master-SSPSR移位寄存器根据Bus-width自动移入Master-SSPBUF里,然后这些数据又被Master-SSPSR根据Channel-width从Master-SSPBUF中移出,通过Master-SDO管脚传给Slave-SDI管脚,Slave-SSPSR则把从Slave-SDI接收到的数据移入Slave-SSPBUF里。与此同时Slave-SSPBUF里面的数据根据每次接收数据的大小(Channel-width)通过Slave-SDO发往Master-SDI,Master-SSPSR再把从Master-SDI接收的数据移入Master-SSPBUF。在单次数据传输完成后,用户程序可以通过从Master的Rx-Data寄存器读取Master数据交换得到的数据。
Controller
Master里面的Controller主要通过时钟信号以及片选信号来控制Slave。Slave会一直等待,直到接收到Master发过来的片选信号,然后根据时钟信号来工作。
Master的片选操作必须由程序实现。例如,程序把SS/CS的信号拉低电平,完成SPI设备数据通信的前期工作;当程序想让SPI设备结束数据通信时,再把SS/CS管脚上的时钟信号拉高电平。
总结:
1.主机和从机交换数据是一起的,采样和变换数据也是MOSI和MSIO一起进行的。
2.无论是硬件的SPI协议还是软件的SPI协议,当你要读取从机的数据的时候,都要发送同样大小BIT的字节给原来的Slave来交换数据, 在发送数据之前,先往Master的Tx-Data寄存器写入要发送出去的数据,这些数据会被Master-SSPSR移位寄存器根据Bus-width自动移入Master-SSPBUF里,然后这些数据又被Master-SSPSR根据Channel-width从Master-SSPBUF中移出,通过Master-SDO管脚传给Slave-SDI管脚,Slave-SSPSR则把从Slave-SDI接收到的数据移入Slave-SSPBUF里。与此同时Slave-SSPBUF里面的数据根据每次接收数据的大小(Channel-width)通过Slave-SDO发往Master-SDI,Master-SSPSR再把从Master-SDI接收的数据移入Master-SSPBUF。在单次数据传输完成后,用户程序可以通过从Master的Rx-Data寄存器读取Master数据交换得到的数据。
3.如你需要读取一个地址的数据,发送地址给从机了,从机就准备好数据,在等待信号线的变化,因为协议的原因,主机也会在信号线变化的时候,发送数据给从机,硬件SPI是把主机发送的数据放在发送缓冲区后,时钟线自动变化,主从机开始交换数据,软件模拟SPI的话,当你读取数据的时候,是直接手动改变时钟线,你只是操作MISO,但是MOSI的模拟端口本来就有高低位(1或者0),如果只是读取操作的话,完全不用考虑从机采样MOSI,那不过是辣鸡数据,所以但是MISO的数据要自己动手采集,所以这就是区别,两者实质上是一样。
例子 RFID上升沿保持不变,可以采样,下降沿数据变化,不能采样。
void SPI_RC522_SendByte ( uint8_t byte )
{
uint8_t counter;
for(counter=0;counter<8;counter++)
{
RC522_DELAY();
RC522_SCK_0 ();//下降沿改变数据
if ( byte & 0x80 )
RC522_MOSI_1 ();
else
RC522_MOSI_0 ();
RC522_DELAY();
RC522_SCK_1();//上升沿,数据等待从机采样
RC522_DELAY();
byte <<= 1;
}
}
/**
* @brief 从RC522发送1 Byte 数据
* @param 无
* @retval RC522返回的数据
*/
uint8_t SPI_RC522_ReadByte ( void )
{
uint8_t counter;
uint8_t SPI_Data;
for(counter=0;counter<8;counter++)
{
SPI_Data <<= 1;
RC522_SCK_0 ();下降沿改变数据
RC522_DELAY();
RC522_SCK_1 (); //上升沿,数据等待主机机采样
if ( RC522_MISO_GET() == 1)
SPI_Data |= 0x01;
RC522_DELAY();
}
return SPI_Data;
}
.