通信协议之软件模拟SPI通信(HAL库)

        SPI(Serial Peripheral Interface,串行外围设备接口)通信协议是一种由Motorola公司首先在其MC68HCXX系列处理器上定义的同步通信协议。该协议广泛应用于MCU、存储芯片、AD转换器和LCD等之间的数据交互。

一、SPI通信的基本组成

SPI总线通常由四根线组成,但在单向传输时可能只需要三根线,包括以下四根线:

  1. SCLK(Serial Clock,时钟信号):由主设备产生,用于规定数据的传输时间。
  2. MOSI(Master Output/Slave Input,主设备输出/从设备输入):用于主设备向从设备发送数据。
  3. MISO(Master Input/Slave Output,主设备输入/从设备输出):用于从设备向主设备发送数据。
  4. CS/SS(Chip Select/Slave Select,片选/从设备选择信号):由主设备控制,用于选择特定的从设备进行通信。在多从设备系统中,每个从设备可能需要一个独立的CS/SS线

二、工作原理

        SPI通信协议是一种主从设备架构,其中主设备负责产生时钟信号并控制通信过程,从设备则根据时钟信号进行数据的接收和发送。在通信过程中,主设备和从设备都拥有串行移位寄存器,用于数据的串行传输。

通信过程通常包括以下几个步骤:

  1. 主设备发起通信:主设备通过拉低从设备的CS/SS线来启动通信。
  2. 时钟信号同步:主设备产生时钟信号(SCLK),从设备根据时钟信号的边沿(上升沿或下降沿)进行数据的采样和发送。
  3. 数据交换:主设备通过MOSI线向从设备发送数据,同时从设备通过MISO线向主设备发送数据。数据的交换是同步进行的,即主设备发送一位数据的同时,从设备也发送一位数据。

三、工作模式

SPI协议定义了四种工作模式,这些模式通过CPOL(Clock Polarity,时钟极性)和CPHA(Clock Phase,时钟相位)的组合来区分。

模式CPOLCPHA描述
000空闲时SCLK为低电平,数据在SCK的上升沿被采样,在下降沿被发送。

模式CPOLCPHA描述
101空闲时SCLK为低电平,数据在SCK的下降沿被采样,在上升沿被发送。

模式CPOLCPHA描述
210空闲时SCLK为高电平,数据在SCK的下降沿被采样,在上升沿被发送。

模式CPOLCPHA描述
311空闲时SCLK为高电平,数据在SCK的上升沿被采样,在下降沿被发送。

四、优缺点

优点

  • 数据传输速率高,通常能达到甚至超过10Mbps。
  • 独立的MISO和MOSI线路,支持全双工通信。
  • 硬件连接简单,不需要像I2C那样的从设备寻址系统。

缺点

  • 使用四根信号线,相比I2C和UART等协议较为复杂。
  • 无法直接确认数据是否成功接收(虽然可以通过其他机制实现)。

五、时序单元

        根据SPI通信协议的通信方式,可以将SPI通信协议拆分为三个部分;以下是主机通过SPI通信协议向从机发送0x06的时序图。

        通过观察时许,可以看到该通信模式为模式0。

        时许可以拆分为三个部分:开始通信、结束通信、交换数据。

开始通信:

当SS/CS线置为低电平时,表明SPI通信协议通信开始。

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

结束通信:

当SS/CS从低电平置为高电平,表明通信结束

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

交换数据:

红色框部分为数据交换部分,本次通信在模式0的情况下。

通信开始,当时钟线SCK在低电平时,由于SPI通信是高位先行。于是先将高位(B7)发送到MOSI线上,等到SCK上升沿时。对MOSI进行采样,根据移位机制,将MOSI的高位移动到MISO的低位。即MOSI的最高位变为MISO的最低位。如此循环八次即可完成主机跟从机数据的发送/移位。

uint8_t MySPI_SwapByte(uint8_t ByteSend)
{
	uint8_t ByteReceive = 0x00;
	uint8_t i ;
	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;
}

至此,SPI通信的时序单元已经写好。

MySPI.c

#include "MySpi.h"
#include "main.h"


void MySPI_W_SS(uint8_t BitValue)
{
	HAL_GPIO_WritePin(SPI2_SS_PORT,SPI2_SS_PIN,(GPIO_PinState)BitValue);
}

void MySPI_W_SCK(uint8_t BitValue)
{
	HAL_GPIO_WritePin(SPI2_SCK_PORT,SPI2_SCK_PIN,(GPIO_PinState)BitValue);
}

void MySPI_W_MOSI(uint8_t BitValue)
{
	HAL_GPIO_WritePin(SPI2_MOSI_PORT,SPI2_MOSI_PIN,(GPIO_PinState)BitValue);
}

uint8_t MySPI_R_MISO(void)
{
	uint8_t PIN_STATE;
	PIN_STATE = HAL_GPIO_ReadPin(SPI2_MISO_PORT,SPI2_MISO_PIN);
	return PIN_STATE;
}

void MySPI_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStruct = {0};
	  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOB_CLK_ENABLE();
	
	GPIO_InitStruct.Pin  = SPI2_MISO_PIN;
	GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
	GPIO_InitStruct.Pull = GPIO_PULLUP;
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
	HAL_GPIO_Init(GPIOB,&GPIO_InitStruct);
	
	__HAL_RCC_GPIOB_CLK_ENABLE();
	GPIO_InitStruct.Pin  = SPI2_SS_PIN|SPI2_SCK_PIN|SPI2_MOSI_PIN;
	GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
	GPIO_InitStruct.Pull = GPIO_NOPULL;
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
	HAL_GPIO_Init(GPIOB,&GPIO_InitStruct);
	
	MySPI_W_SS(1);//上电初始化 默认不选中从机
	MySPI_W_SCK(0);// 默认选择模式0
}

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

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



uint8_t MySPI_SwapByte(uint8_t ByteSend)
{
	uint8_t ByteReceive = 0x00;
	uint8_t i ;
	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;
}

数据移位模型
//uint8_t MySPI_SwapByte(uint8_t ByteSend)
//{
//	uint8_t ByteReceive = 0x00;
//	uint8_t i ;
//	for(i = 0; i < 8; i++)
//	{
//		MySPI_W_MOSI(ByteSend & 0x80);//通过掩码进行数据交换
//		ByteSend <<= 1;
//		MySPI_W_SCK(1);
//		if(MySPI_R_MISO() == 1){ByteSend |= 0x01;}
//		MySPI_W_SCK(0);
//	}
//	return ByteReceive;
//}


MySPI.h

#ifndef	__MySpi_H_
#define	__MySpi_H_

#include <stdint.h>

#define SPI2_SS_PORT		GPIOB
#define SPI2_SS_PIN		GPIO_PIN_12

#define SPI2_SCK_PORT		GPIOB
#define SPI2_SCK_PIN		GPIO_PIN_13

#define SPI2_MISO_PORT	GPIOB
#define SPI2_MISO_PIN		GPIO_PIN_14

#define SPI2_MOSI_PORT	GPIOB
#define SPI2_MOSI_PIN		GPIO_PIN_15


void MySPI_W_SS(uint8_t BitValue);
void MySPI_W_SCK(uint8_t BitValue);
void MySPI_W_MOSI(uint8_t BitValue);
uint8_t MySPI_R_MISO(void);
void MySPI_Init(void);
void MySPI_Start(void);
void MySPI_Stop(void);
//数据移位模型
uint8_t MySPI_SwapByte(uint8_t ByteSend);


#endif

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值