目录

一、SPI简介

二、移位示意图(重点)

三、SPI时序(重点)

1.模式0

2.模式1

3.模式2

4.模式3

四、软件代码模拟(模式0)


一、SPI简介

       SPI 协议是由摩托罗拉公司提出的通讯协议(Serial Peripheral Interface),串行外围设备接口,允许芯片与外部设备以半/全双工、同步、串行方式通信。此接口可以被配置成主模式,并为外部从设备提供通信时钟(SCK)。它被广泛地使用在 ADC、LCD 等设备与MCU间,要求通讯速率较高的场合。

1.四根通信线:SCK(时钟)、MOSI(主设备输出从设备输入)、MISO(主设备输入从设备输出)、SS(片选)

2.同步、全双工 接收数据线和发送数据线可同时工作

3.支持总线挂载多个设备,实现一主多从模式

4.所有SPI设备的SCK、MOSI、MISO、SS分别连在一起,主机另外引出多条SS控制线,分别接到各从机SS引脚

5.输出引脚配置为推挽输出,输入引脚配置为浮空输入

      主机挂载多个从机设备接线图,时钟线都是连接在一起,片选线SS分别接在不同设备上MOSI和MISO交叉连接,电源的VCC和GND和主设备连接在一起,VCC也可以单独供电,但是GND一定要接在一起,这样才有个参照

主机挂载多个从机示意图 

二、移位示意图(重点)

       主机和从机都有一个8位的移位寄存器,移位寄存器下面有一个时钟输入端,SPI一般都是高位先行,所以每来一个时钟,主机的移位寄存器都会向左移动一位,同理从机也是一样。主机移位寄存器左移出去的数据通过MOSI引脚输入到从机移位寄存器的右边,从机移位寄存器左边移出去的数据通过MISO引脚输入到主机移位寄存器的右边,这个就是SPI的内部基本工作原理。也就是,主机发送一个字节数据的同时也接收了一个字节数据。

 移位示意图

三、SPI时序(重点)

起始条件:SS从高电平切换到低电平

终止条件:SS从低电平切换到高电平

       一般默认SS低电平有效(具体情况根据芯片手册来),从高电平切换到低电平就是代表选中某个从机,这就是通信的开始。当从低电平切换到高电平就是代表终止这次通信。

 起始和终止时序图

SPI有两个可配置的位分别是CPOL时钟极性,CPHA时钟相位

CPOL=0:时钟空闲状态时,SCK为低电平

CPOL=1:时钟空闲状态时,SCK为高电平

CPHA=0:  SCK第一个边沿移入数据,第二个边沿移出数据

CPHA=1:  SCK第一个边沿移出数据,第二个边沿移入数据(SCK的第二个边沿进行数据采样或者是SCK的偶数边沿进行数据采样)

1.模式0

CPOL=0:时钟空闲状态时,SCK为低电平

CPHA=0:  SCK第一个边沿移入数据,第二个边沿移出数据

       数据移入移出的时机会提前半个时钟,也就是相位提前了。在时钟上升沿,主机和从机同时移入数据的,主机通过MISO移入最高位,从机通过MOSI移入最高位,然后时钟运行产生下降沿,此时主机和从机移出数据,这里主机移出的最高位进入从机移位寄存器的最低位,从机移出的最高位进入主机移位寄存器的最低位。

 模式0时序图

2.模式1

CPOL=0:时钟空闲状态时,SCK为低电平

CPHA=1:  SCK第一个边沿移出数据,第二个边沿移入数据

       在时钟上升沿,主机和从机同时移出数据的,主机通过MOSI移出最高位,此时MOSI的电平就表示主机要发送的数据B7,从机通过MISO移出最高位,此时MISO的电平就表示从机要发送的数据B7,然后时钟运行产生下降沿,此时主机和从机移入数据,也就是进行数据采样,这里主机移出的B7进入从机移位寄存器的最低位,从机移出的B7进入主机移位寄存器的最低位。这样,一个时钟脉冲产生完毕,一个数据位传输完毕。

  模式1时序图

3.模式2

CPOL=1:空闲状态时,SCK为高电平

CPHA=0:SCK第一个边沿移入数据,第二个边沿移出数据

  模式2时序图

4.模式3

CPOL=1:空闲状态时,SCK为高电平

CPHA=1:SCK第一个边沿移出数据,第二个边沿移入数据

四、软件代码模拟(模式0)

1.先配置4根通信线

先初始化4根通信线,给它们配置成相应的状态函数

void MySPI_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	MySPI_W_SS(1);  //初始化好给片选引脚拉高
	MySPI_W_SCK(0);  //时钟引脚拉低
}

void MySPI_W_SS(uint8_t BitValue)  //片选高低电平配置函数
{
	GPIO_WriteBit(GPIOA, GPIO_Pin_4, (BitAction)BitValue);
}

void MySPI_W_SCK(uint8_t BitValue)   //时钟高低电平配置函数
{
	GPIO_WriteBit(GPIOA, GPIO_Pin_5, (BitAction)BitValue);
}

void MySPI_W_MOSI(uint8_t BitValue)  //主机输出引脚高低电平配置函数
{
	GPIO_WriteBit(GPIOA, GPIO_Pin_7, (BitAction)BitValue);
}

uint8_t MySPI_R_MISO(void)   主机输入引脚读取高低电平
{
	return GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6);
}

 2.起始和终止信号

按照起始和终止时序图写出模拟的高低电平

void MySPI_Start(void)
{
	MySPI_W_SS(0);
}

void MySPI_Stop(void)
{
	MySPI_W_SS(1);
}

3.交换一个字节

主机发送一个字节,并且从从机得到一个字节

uint8_t MySPI_SwapByte(uint8_t ByteSend)
{
	uint8_t i, ByteReceive = 0x00;
	
	for (i = 0; i < 8; i ++)
	{
		MySPI_W_MOSI(ByteSend & (0x80 >> i));
		MySPI_W_SCK(1);
		if (MySPI_R_MISO() == 1){ByteReceive |= (0x80 >> i);}
		MySPI_W_SCK(0);
	}
	
	return ByteReceive;
}

       上面就是用GPIO口模拟一个SPI通信的时序用于主机发送或接收一个字节,具体的某款芯片对应的SPI通信要根据芯片手册来看给芯片发送什么字节或者我们需要接收什么字节,但是上面是SPI通信的基础有了这个就可以进行我们想要的接收和发送数据了,这样才能进行接下来的通信,

物联沃分享整理
物联沃-IOTWORD物联网 » 软件模拟SPI实现详解

发表评论