spi总线协议及spi时序图详解(掌握SPI和I2C总线协议心得)
首发

spi总线协议及spi时序图详解(掌握SPI和I2C总线协议心得)

优质
请用语音读文章

 SPI(Serial peripheral interface)即串行外围设备接口。是由Motorola首先在其MC68HCxx系列单片机上定义的。基于高速全双工总线的通讯协议。(又是高速。而且全双工。确实强大)被广泛应用于ADC、LCD等设备与MCU之间。

  跟前面学习I2C、USART一样。学习一种协议。还是从两个层面分析:物理层和协议层。

1. SPI物理层

SPI通讯需要使用4条线:3条总线和1条片选

图1

  SPI还是遵循主从模式。3条总线分别是SCK、MOSI和MISO。片选线为nSS(低电平有效)。SPI协议适用于一主多从的工作场景:

图2

(1) nSS(Slave Select):片选信号线。用于选中SPI从设备。每个从设备独立拥有这条nSS信号线。占据主机的一个引脚。设备的其他总线是并联到SPI主机的。即无论多少个从设备。都共同使用这3条总线。当从设备上的nSS引脚被置拉低时表明该从设备被主机选中。

(2) SCK(Serial Clock):时钟信号线。通讯数据同步用。时钟信号由通讯主机产生。它决定了SPI的通讯速率。

(3) MOSI(Master Ouput Slave Input):主机(数据)输出/从设备(数据)输入引脚。即这条信号线上传输从主机到从机的数据。

(4) MISO(Master Input Slave Ouput):主机(数据)输入/从设备(数据)输出引脚。即这条信号线上传输从机从到主机的数据主从机通过两条信号线来传输数据。那么自然是全双工通讯的了。之前的I2C通讯。数据只在一条SDA线上传输。主从机数据交互只能采用半双工。

2. SPI协议层

图3

  如上为SPI通讯时序图。nSS、SCK、MOSI信号均由主机产生。MISO信号由从机产生。在nSS为低电平的前提下。MOSI和MISO信号才有效。在每个时钟周期MOSI和MISO传输一位数据。

跟I2C通讯类似。SPI通讯也需要通讯的起始/结束信号。有效数据和同步时钟。

2.1 通讯的起始/结束信号

  图中的nSS信号由高电平变为低电平即为SPI通讯的起始信号。反过来。nSS信号由低电平变为高电平即为SPI通讯的结束信号。这个可比I2C简单得多吧。当从机检测到自身的nSS引脚被拉低时就知道自己被主机选中。准备和主机进行通讯。

2.2 有效数据的采集

  SPI通讯的数据采集是个相对复杂的环节。先不说其他。以上图为例:

图中红色框框即为有效数据被采集的时间点。”CPOL = 0″所在的脉冲信号表示的是用于进行数据同步的SCK。MOSI和MISO线上的数据在每个SCK时钟周期传输一位数据。注意。数据的输入/输出是可以同时进行的。

  由图可见。在SCK为奇数(更正:这里应该是偶数)边沿(在这里该边沿为下降沿)时。数据得到有效采样。也就是说。在这个时刻。MISO和MOSI的数据有效。高电平表示数据1。低电平表示数据0。在其它时刻数据并无效。可以理解为为下一次MISO和MOSI的数据传输做准备。

  数据在传输中。高位在先还是低位在先。SPI协议并无明确规定。但是数据要在主从机中正确传输。自然双方要先约定好。一般会采用高位在先(MSB)方式传输。

  这里需要再提及的概念是时钟极性(CPOL)和时钟相位(CPHA)。

  时钟极性(CPOL)指通讯设备处于空闲状态(SPI开始通讯前、nSS线无效)时。SCK的状态。

CPOL = 0:SCK在空闲时为低电平

CPOL = 1:SCK在空闲时为高电平

1|CPOL=0:SCK在空闲时为低电平
2|CPOL=1:SCK在空闲时为高电平

  时钟相位(CPHA)指数据的采样时刻位于SCK的偶数边沿采样还是奇数边沿采样。

CPHA = 0:在SCK的奇数边沿采样

CPHA = 1:在SCK的偶数边沿采样

1|CPHA=0:在SCK的奇数边沿采样
2|CPHA=1:在SCK的偶数边沿采样

  那么这样说来。SPI的采样时刻并非由上升沿/下降沿决定的。注意的是。在数据采样时刻。MOSI和MOSI的电平为有效电平。数据不能在这个时刻进行切换,在非采样时刻MOSI和MISO上的信号才能切换。

  完整的时序图如下:

图4

图5

  所以说。SPI有4中工作模式:

图6

更正:工作模式3的CPOL应为1。

注意要让主机和从机需要在相同的工作模式下。这样才可以实现正常通讯。

下面介绍用STM32库函数实SPI通讯代码。

#ifndef__SPI_H
#define__SPI_H


#include"stm32f10x.h"


/*¶¨ÒåÈ«¾Ö±äÁ¿*/
voidSPI2_Config(void);
voidSPI2_SetSpeed(uint8_tSpeed);
uint8_tSPI2_WriteReadData(uint8_tdat);

voidSPI1_Config(void);
voidSPI1_SetSpeed(uint8_tspeed);
uint8_tSPI1_WriteReadData(uint8_tdat);



/****************************************************************************
*FunctionName:SPI1_Config
*Description:³õʼ»¯SPI2
*Input:None
*Output:None
*Return:None
****************************************************************************/

voidSPI1_Config(void)
{
	GPIO_InitTypeDefGPIO_InitStructure;
	SPI_InitTypeDefSPI_InitStructure;

/*SPIµÄIO¿ÚºÍSPIÍâÉè´ò¿ªÊ±ÖÓ*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE);

/*SPIµÄIO¿ÚÉèÖÃ*/
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;

	GPIO_Init(GPIOA,&GPIO_InitStructure);
	GPIO_SetBits(GPIOA,GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7);//PA5.6.7ÉÏÀ­
/***************************************************************************/
/*************************ÉèÖÃSPIµÄ²ÎÊý***********************************/
/***************************************************************************/
	
	SPI_InitStructure.SPI_Direction=SPI_Direction_2Lines_FullDuplex;//Ñ¡ÔñÈ«Ë«¹¤SPIģʽ
	SPI_InitStructure.SPI_Mode=SPI_Mode_Master;//Ö÷»úģʽ
	SPI_InitStructure.SPI_DataSize=SPI_DataSize_8b;//8λSPI
	SPI_InitStructure.SPI_CPOL=SPI_CPOL_High;//ʱÖÓÐü¿Õ¸ßµçƽ
	SPI_InitStructure.SPI_CPHA=SPI_CPHA_2Edge;//ÔÚµÚ¶þ¸öʱÖӲɼ¯Êý¾Ý
	SPI_InitStructure.SPI_NSS=SPI_NSS_Soft;		//NssʹÓÃÈí¼þ¿ØÖÆ
	/*Ñ¡Ôñ²¨ÌØÂÊÔ¤·ÖƵΪ256*/
	SPI_InitStructure.SPI_BaudRatePrescaler=SPI_BaudRatePrescaler_256;
	SPI_InitStructure.SPI_FirstBit=SPI_FirstBit_MSB;//´Ó×î¸ßλ¿ªÊ¼´«Êä
	SPI_InitStructure.SPI_CRCPolynomial=7;
	
	SPI_Cmd(SPI1,ENABLE);
	SPI_Init(SPI1,&SPI_InitStructure);
}

/****************************************************************************
*FunctionName:SPI2_Config
*Description:³õʼ»¯SPI2
*Input:None
*Output:None
*Return:None
****************************************************************************/

voidSPI2_Config(void)
{
	GPIO_InitTypeDefGPIO_InitStructure;
	SPI_InitTypeDefSPI_InitStructure;

/*SPIµÄIO¿ÚºÍSPIÍâÉè´ò¿ªÊ±ÖÓ*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,ENABLE);

/*SPIµÄIO¿ÚÉèÖÃ*/
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;

	GPIO_Init(GPIOB,&GPIO_InitStructure);

/***************************************************************************/
/*************************ÉèÖÃSPIµÄ²ÎÊý***********************************/
/***************************************************************************/
	
	SPI_InitStructure.SPI_Direction=SPI_Direction_2Lines_FullDuplex;//Ñ¡ÔñÈ«Ë«¹¤SPIģʽ
	SPI_InitStructure.SPI_Mode=SPI_Mode_Master;//Ö÷»úģʽ
	SPI_InitStructure.SPI_DataSize=SPI_DataSize_8b;//8λSPI
	SPI_InitStructure.SPI_CPOL=SPI_CPOL_Low;//ʱÖÓÐü¿Õ¸ßµçƽ
	SPI_InitStructure.SPI_CPHA=SPI_CPHA_1Edge;//ÔÚµÚ¶þ¸öʱÖӲɼ¯Êý¾Ý
	SPI_InitStructure.SPI_NSS=SPI_NSS_Soft;		//NssʹÓÃÈí¼þ¿ØÖÆ
	/*Ñ¡Ôñ²¨ÌØÂÊÔ¤·ÖƵΪ256*/
	SPI_InitStructure.SPI_BaudRatePrescaler=SPI_BaudRatePrescaler_256;
	SPI_InitStructure.SPI_FirstBit=SPI_FirstBit_MSB;//´Ó×î¸ßλ¿ªÊ¼´«Êä
	SPI_InitStructure.SPI_CRCPolynomial=7;
	
	SPI_Cmd(SPI2,ENABLE);
	SPI_Init(SPI2,&SPI_InitStructure);
}

/****************************************************************************
*FunctionName:SPI1_SetSpeed
*Description:ÉèÖÃSPI1µÄ´«ÊäËٶȡ£
*Input:ËٶȲ¨ÌØÂÊ·ÖƵ
*Output:None
*Return:None
****************************************************************************/

voidSPI1_SetSpeed(uint8_tspeed)
{
	SPI1->CR1&=0xFFC7;
	SPI1->CR1|=speed;
	SPI_Cmd(SPI1,ENABLE);
	
}

/****************************************************************************
*FunctionName:SPI2_SetSpeed
*Description:ÉèÖÃSPI2µÄ·ÖƵÊý£¬ÒԸıäSPI2µÄËÙ¶È.
*Input:Speed£º·ÖƵÊý
*Output:None
*Return:None
****************************************************************************/

voidSPI2_SetSpeed(uint8_tSpeed)
{
	
	SPI2->CR1&=0xFFC7;
	SPI2->CR1|=Speed;
	SPI_Cmd(SPI2,ENABLE);
			
}

/****************************************************************************
*FunctionName:SPI1_WriteReadData
*Description:ʹÓÃSPI1дÈëÒ»¸ö×Ö½ÚÊý¾Ýͬʱ¶ÁÈ¡Ò»¸ö×Ö½ÚÊý¾Ý¡£
*Input:dat£ºÒªÐ´µÄ8λÊý¾Ý
*Output:None
*Return:¶ÁÈ¡µ½µÄ8λÊý¾Ý
****************************************************************************/

uint8_tSPI1_WriteReadData(uint8_tdat)
{
	uint16_ti=0;

/*µ±·¢ËÍ»º³åÆ÷¿Õ*/	
	while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE)==RESET)
	{
		i++;
		if(i>10000)
		{
			return0xFF;
		}
	}
	
/*·¢ËÍÊý¾Ý*/
	SPI_I2S_SendData(SPI1,dat);
	
	/*µÈ´ý½ÓÊÕ»º³åÆ÷Ϊ·Ç¿Õ*/
	while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_RXNE)==RESET);
	
	/*½«¶ÁÈ¡µ½µÄÊýÖµ·µ»Ø*/
	returnSPI_I2S_ReceiveData(SPI1);		
}

/****************************************************************************
*FunctionName:SPI2_WriteReadData
*Description:ʹÓÃSPI2дÈëÒ»¸ö×Ö½ÚÊý¾Ýͬʱ¶ÁÈ¡Ò»¸ö×Ö½ÚÊý¾Ý¡£
*Input:dat£ºÐ´ÈëµÄÊý¾Ý
*Output:None
*Return:¶ÁÈ¡µ½µÄÊý¾Ý
**¶Áȡʧ°Ü·µ»Ø0xFF
****************************************************************************/

uint8_tSPI2_WriteReadData(uint8_tdat)
{
	uint16_ti=0;

/*µ±·¢ËÍ»º³åÆ÷¿Õ*/	
	while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_TXE)==RESET)
	{
		i++;
		if(i>10000)
		{
			return0xFF;
		}
	}
	
/*·¢ËÍÊý¾Ý*/
	SPI_I2S_SendData(SPI2,dat);
	
	/*µÈ´ý½ÓÊÕ»º³åÆ÷Ϊ·Ç¿Õ*/
	while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_RXNE)==RESET);
	
	/*½«¶ÁÈ¡µ½µÄÊýÖµ·µ»Ø*/
	returnSPI_I2S_ReceiveData(SPI2);		
}

以上就是由优质生活领域创作者 生活常识网 整理编辑的,如果觉得有帮助欢迎收藏转发~

分享到 :
相关推荐

李商隐无题相见时难别亦难赏析(相见时难别亦难原文)

请用语音读文章无题【唐】李商隐相见时难别亦难。东风无力百花残。春蚕到死丝方尽[&he...

哪个电脑管家好用(微软电脑管家曝光)

请用语音读文章首先我要替360说句话:360全家桶真的超级好用!总是观看到的有网[&...

太平间闹鬼事件(免费HD电影完整版)

请用语音读文章2011年7月。江西靖安县人民医院的太平间中。深夜传出一阵阵婴儿哭声[...

有什么好看的灾难片推荐一下(最好看的灾难片排行榜前十名)

请用语音读文章巧了。本人也是灾难片爱好者。几十部灾难片可以张口就来。而且都是经典之[...

发表评论

您的电子邮箱地址不会被公开。