一文搞懂——软件模拟SPI

关于stm32通信协议:软件模拟SPI、软件模拟I2C的总结(fishing_8)_To_be_a_fisher的博客-CSDN博客_stm32 软件spi

发现一篇写的软件模拟SPI的比较容易理解的博客!感兴趣的可以看看

SPI协议

SPI协议,多用于ADC、DA、LCD等设备与MCU之间,要求通信速率要求较高的场合,它相比于I2C来说速度快的多。一般只需要4根线,分别是MISO、MOSI、SCK、CS线等,但是,有可能只用3根。对于SPI通信协议的详细描述我之前有博客已经有说明了,有需要可以了解一下。这里重点是使用软件模拟SPI协议,实验基于野火开发板指南者F103VET6、Flash芯片W25Q64、这里模拟的时序是SPI的模式3(CPOL=1,CPHA=1),原因有两点原因:1、模式3的SCK空闲电平为高,有高电平向低电平翻转较为容易和快,2、模式3在偶数边沿采样,防止第一个信号没采到。

首先,对于软件模拟SPI的GPIO初始化,可以参考SPI的GPIO初始化的配置,我们不用使用复用功能就行,使用普通推挽输出和浮空输入。

void SPI_GPIO_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStruct;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOC,ENABLE);
	/*SPI CS GPIO Confgi*/
	GPIO_InitStruct.GPIO_Mode	=	GPIO_Mode_Out_PP;
	GPIO_InitStruct.GPIO_Pin	=	EEPROM_SPI_CS_PIN;
	GPIO_InitStruct.GPIO_Speed	=	GPIO_Speed_50MHz;
	GPIO_Init(EEPROM_SPI_CS_PORT,&GPIO_InitStruct);
	
	/*SPI SCK GPIO Config*/
	GPIO_InitStruct.GPIO_Mode	=	GPIO_Mode_Out_PP;
	GPIO_InitStruct.GPIO_Pin	=	EEPROM_SPI_CLK_PIN;
	GPIO_InitStruct.GPIO_Speed	=	GPIO_Speed_50MHz;
	GPIO_Init(EEPROM_SPI_CLK_PORT,&GPIO_InitStruct);
	
	/*MISO GPIO Config*/
	GPIO_InitStruct.GPIO_Mode	=	GPIO_Mode_IN_FLOATING;
	GPIO_InitStruct.GPIO_Pin	=	EEPROM_SPI_MISO_PIN;
	GPIO_Init(EEPROM_SPI_MISO_PORT,&GPIO_InitStruct);
	
	/*MOSI GPIO Config*/
  GPIO_InitStruct.GPIO_Mode	=	GPIO_Mode_Out_PP;
	GPIO_InitStruct.GPIO_Pin	=	EEPROM_SPI_MOSI_PIN;
	GPIO_InitStruct.GPIO_Speed	=	GPIO_Speed_50MHz;
	GPIO_Init(EEPROM_SPI_MOSI_PORT,&GPIO_InitStruct);

	EEPROM_SPI_CS_HIGH();
	EEPROM_SPI_CLK_HIGH();
}

接下来就是最为关键的发送和接收数据函数了,其中注意,使用SPI时,发送和接收其实是同一个函数,通过判断RXNE来确认发送结束,此时也接收完数据,接收数据同样要主机产生时序,时序通过主机发送数据产生,所以会发送无用的Dummy数据。软件模拟不需要,直接接收自己本身会产生时序,不用发送DUMMY数据。Delay函数随便设置,不要小于手册的时间即可。 

软件模拟发送数据

void SPI_SendData(uint8_t data)
{
	uint8_t cnt;
	
	for(cnt=0;cnt<8;cnt++)
	{
		EEPROM_SPI_CLK_LOW();//拉低CLK
		SPI_Delay(10);//这个延时时间任意,但要大于芯片数据手册上的(纳秒级的)
		if(data &0x80)
		{
			EEPROM_SPI_MOSI_HIGH();
		}
		else
		{
			EEPROM_SPI_MOSI_LOW();
		}
		data <<= 1;
		//SPI_Delay(10);
		EEPROM_SPI_CLK_HIGH();//拉高CLK
		SPI_Delay(10);
	}

}

 延时函数

static void SPI_Delay(__IO uint32_t count)
{
	uint32_t i;
	for(i=0;i<count;i++)
	{
		uint8_t uc =12;
		while(uc--);
	}

}

软件模拟接收数据

uint8_t SPI_ReadData(void)
{
	uint8_t i = 0;
	uint8_t value=0;
	for(i=0;i<8;i++)
	{
		EEPROM_SPI_CLK_LOW();
		SPI_Delay(10);
		value<<=1;
		if(EEPROM_SPI_MISO())
		{
			value |= 0x01;
		}
		EEPROM_SPI_CLK_HIGH();
		SPI_Delay(10);
	}
	return value;
}

等待擦除完成

void SPI_WaitErase(void)
{
	uint8_t status = 0x01;
	EEPROM_SPI_CS_LOW();
	SPI_SendData(0x05);
	do
	{
		status = SPI_ReadData();
	}
	while((status&0x01) == 1);
	EEPROM_SPI_CS_HIGH();
}

擦除扇区(Flash存储器的一个特征就是写入之前需要擦除,只能把1改为0,不能把0改写为1)

void SPI_EraseSector(uint32_t addr)
{
		EEPROM_SPI_CS_LOW();
		SPI_SendData(0x06);
		EEPROM_SPI_CS_HIGH();
		EEPROM_SPI_CS_LOW();
		SPI_SendData(0x20);
		SPI_SendData((uint8_t)(addr&0xFF0000)>>16);
		SPI_SendData((addr&0xFF00)>>8);
		SPI_SendData((addr&0xFF));
		EEPROM_SPI_CS_HIGH();
		SPI_WaitErase();
}

检验设备,和前面的程序是否正常,正常才继续后面的页写入和读取数据 ID=0XEF4017

uint32_t SPI_CheckDevice(void)
{
	uint8_t temp0,temp1,temp2;
	uint32_t Device_ID=0;
	EEPROM_SPI_CS_LOW();
	SPI_SendData(0x9F);
	temp0 = SPI_ReadData();
	temp1 = SPI_ReadData();
	temp2 = SPI_ReadData(); 
	EEPROM_SPI_CS_HIGH();
	Device_ID = (temp0<<16) | (temp1<<8) | (temp2);
	return Device_ID;

}

读取数据

void SPI_BufferRead(uint8_t *pBuffer,uint32_t addr,uint32_t numToRead)
{
	EEPROM_SPI_CS_LOW();
	SPI_SendData(0x03);
	SPI_SendData((uint8_t)(addr&0xFF0000)>>16);
	SPI_SendData((addr&0xFF00)>>8);
	SPI_SendData((addr&0xFF));
	while(numToRead--)
	{
		*pBuffer = SPI_ReadData();
		pBuffer++;
	}
	
	EEPROM_SPI_CS_HIGH();

}

页写入

void SPI_PageWrite(uint8_t *pBuffer,uint32_t addr,uint32_t numToWrite)
{
	EEPROM_SPI_CS_LOW();
	SPI_SendData(0x06);
	EEPROM_SPI_CS_HIGH();
	EEPROM_SPI_CS_LOW();
	SPI_SendData(0x02);
	SPI_SendData((addr&0xFF0000)>>16);
	SPI_SendData((addr&0xFF00)>>8);
	SPI_SendData((addr&0xFF));
	
	if(numToWrite>256)
	{
			printf("\r\n页写入最大为256字节\r\n");
			numToWrite = 256 ;
	}
	while(numToWrite--)
	{
		
		SPI_SendData(*pBuffer);
		pBuffer++;
	}
	EEPROM_SPI_CS_HIGH();
	
	SPI_WaitErase();

}


任意大小写入

void SPI_BufferWrite(uint8_t *pBuffer,uint32_t addr,uint32_t numToWrite)
{
	uint32_t count=0,numPage=0,numSingle=0,Addr=0;
	
	Addr = addr%Page_Size;
	count = Page_Size - Addr;
	numPage = numToWrite/Page_Size;
	numSingle = numToWrite%Page_Size;
	
	if(Addr == 0)
	{
		if(numPage==0)
		{
			SPI_PageWrite(pBuffer,addr,numToWrite);
			
		}
		else
		{
			while(numPage--)
			{
				SPI_PageWrite(pBuffer,addr,Page_Size);
				pBuffer+=Page_Size;
				addr+=Page_Size;
			}
			SPI_PageWrite(pBuffer,addr,numSingle);
			
		
		}
	}
	
	else
	{
		if(numPage==0)
		{
			if(numSingle>count)
			{
				SPI_PageWrite(pBuffer,addr,count);
				pBuffer+=count;
				addr+=count;
				SPI_PageWrite(pBuffer,addr,(numSingle-count));
			}
			else
			{
				SPI_PageWrite(pBuffer,addr,numToWrite);

			}
		}
		else
		{
			numToWrite -= count;
			numPage = numToWrite/Page_Size;
			numSingle	=	numToWrite%Page_Size;
			SPI_PageWrite(pBuffer,addr,count);
			addr+=count;
			pBuffer+=count;
			while(numPage--)
			{
				SPI_PageWrite(pBuffer,addr,Page_Size);
				pBuffer+=Page_Size;
				addr+=Page_Size;
				
			}
			if(numSingle!=0)
			{
				SPI_PageWrite(pBuffer,addr,numSingle);
				
			}
			
		
		}
		
	}
	SPI_WaitErase();
}

主函数和读写测试

#include "stm32f10x.h"
#include "bsp_usart.h"
#include "bsp_soft_spi.h"

uint8_t Write_pBuffer[]={"使用野火STM32开发板的软件模拟SPI总算成功了!"};
uint8_t Read_pBuffer[30]={0};
uint8_t pBuffer_Cmp(uint8_t *buffer1,uint8_t *buffer2,uint32_t num)
{
		while(num--)
		{
			if(*buffer1!=*buffer2)
			{
				return 0;
			}
			buffer1++;
			buffer2++;
		}
		return 1;
	
}

void SPI_Test(void)
{
	uint32_t Device_ID = 0;
	Device_ID = SPI_CheckDevice();
	printf("ID=0x%x\r\n",Device_ID);
	if(Device_ID == 0xEF4017)
	{
		printf("SPI FLASH设备正常!\r\n");
		SPI_EraseSector(0x001000);
		SPI_BufferWrite(Write_pBuffer,0x001000,(sizeof(Write_pBuffer)/sizeof(*(Write_pBuffer))-1));
		SPI_BufferRead(Read_pBuffer,0x001000,(sizeof(Write_pBuffer)/sizeof(*(Write_pBuffer))-1));
		if(pBuffer_Cmp(Write_pBuffer,Read_pBuffer,(sizeof(Write_pBuffer)/sizeof(*(Write_pBuffer))-1)) == 1)
		{	
			
			printf("软件模拟SPI的读写数据一致!\r\n");
			printf("\r\n 读出的数据为:%s \r\n", Read_pBuffer);
			
		}
		else
		{
			printf("软件模拟SPI的读写数据不一致!\r\n");
		}
		
	}
	else
	{
		printf("SPI FLASH设备异常!\r\n");
	}
}

int main(void)
{
	
	USART_Config();
	SPI_GPIO_Config();
  /* Output a message on Hyperterminal using printf function */
  printf("\n\r软件模拟SPI实验测试!\n\r");
	SPI_Test();
  while (1)
  {
  }
}




  • 18
    点赞
  • 141
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值