关于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)
{
}
}