SPI通讯

STM32F1与W25Q128通讯

SPI是英语Serial Peripheral interface的缩写,顾名思义就是串行外围设备接口。SPI接口主要应用在EEPROMFLASH,实时时
钟,
AD 转换器,还有数字信号处理器和数字信号解码器之间。SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管

脚上只占用四根线。


SPI接口一般使用4条线通信:
MISO主设备数据输入,从设备数据输出。
MOSI主设备数据输出,从设备数据输入。
SCLK时钟信号,由主设备产生。
CS从设备片选信号,由主设备控制。

主机和从机都有一个串行移位寄存器,主机通过向它的SPI串行寄存器写入一个字节来发起一次传输。寄存器通过MOSI信号线将字

节传送给从机,从机也将自己的移位寄存器中的内容通过 MISO 信号线返回给主机。这样,两个移位寄存器中的内容就被交换。外

设的写操作和读操作是同步完成的。如果只进行写操作,主机只需忽略接收到的字节;反之,若主机要读取从机的一个字节,就必

须发送一个空字节来引发从机的传输。

SPI主要特点有: 可以同时发出和接收串行数据; 可以当作主机或从机工作; 提供频率可编程时钟; 发送结束中断标志; 写冲突

保护; 总线竞争保护等。

W25Q128是外部flash芯片,具体芯片资料:以下列出一些关键的文件描述及时序图

1. 在对该flash进行写操作时必须先有擦出操作,不然会写入出错,还需写使能,写完必须写失能(很关键)

2. 擦出有扇区擦除,半块擦除,块擦除,全部擦除,全部擦除需要耗时超久

3. 可以对扇区,块上解锁

4. 有3个状态寄存器,可以查看芯片及内存的各种状态

5. 内存地址为24为地址


看图说话

1.flash内存图

SPI通讯_第1张图片

概述:内存物理地址,分为块,扇区等等

2.读操作

SPI通讯_第2张图片

概述:拉低片选,再发送读命令,再发送地址,随后才是数据


3.擦除

SPI通讯_第3张图片

概述:类似读,知识命令不一样,命令详情可于作者的资料里下载


4.写操作

SPI通讯_第4张图片

概述:类似读,写之前进行内存擦除,写使能,写一位数据后将clk拉高一表示将其stable(稳定)


6.操作流程图

SPI通讯_第5张图片

更加具体的SPI操作flash的细节在代码中体现,代码中不为增加鲁棒性而增加代码的复杂度,极度最求简介易懂。

extern u16 W25QXX_TYPE;					//¶¨ÒåW25QXXоƬÐͺÅ		   

#define	W25QXX_CS 		PBout(12)  		//W25QXXµÄƬѡÐźÅ
				 
////////////////////////////////////////////////////////////////////////////
 
//Ö¸Áî±í
#define W25X_WriteEnable		0x06 
#define W25X_WriteDisable		0x04 
#define W25X_ReadStatusReg		0x05 
#define W25X_WriteStatusReg		0x01 
#define W25X_ReadData			0x03 
#define W25X_FastReadData		0x0B 
#define W25X_FastReadDual		0x3B 
#define W25X_PageProgram		0x02 
#define W25X_BlockErase			0xD8 
#define W25X_SectorErase		0x20 
#define W25X_ChipErase			0xC7 
#define W25X_PowerDown			0xB9 
#define W25X_ReleasePowerDown	0xAB 
#define W25X_DeviceID			0xAB 
#define W25X_ManufactDeviceID	0x90 
#define W25X_JedecDeviceID		0x9F 

void W25QXX_Init(void);
u16  W25QXX_ReadID(void);  	    		//¶ÁÈ¡FLASH ID
u8	 W25QXX_ReadSR(void);        		//¶Áȡ״̬¼Ä´æÆ÷ 
void W25QXX_Write_SR(u8 sr);  			//д״̬¼Ä´æÆ÷
void W25QXX_Write_Enable(void);  		//дʹÄÜ 
void W25QXX_Write_Disable(void);		//д±£»¤
void W25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);
void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead);   //¶ÁÈ¡flash
void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);//дÈëflash
void W25QXX_Erase_Chip(void);    	  	//ÕûƬ²Á³ý
void W25QXX_Erase_Sector(u32 Dst_Addr);	//ÉÈÇø²Á³ý
void W25QXX_Wait_Busy(void);           	//µÈ´ý¿ÕÏÐ
void W25QXX_PowerDown(void);        	//½øÈëµôµçģʽ
void W25QXX_WAKEUP(void);
void SPI2_Init(void);			 
void SPI2_SetSpeed(u8 SpeedSet);   
u8 SPI2_ReadWriteByte(u8 TxData);				

u16 W25QXX_TYPE=W25Q128;
//芯片内部硬件SPI初始化	
void SPI2_Init(void)
{
 	GPIO_InitTypeDef GPIO_InitStructure;
  	SPI_InitTypeDef  SPI_InitStructure;

	RCC_APB2PeriphClockCmd(	RCC_APB2Periph_GPIOB, ENABLE );//PORTBʱÖÓʹÄÜ 
	RCC_APB1PeriphClockCmd(	RCC_APB1Periph_SPI2,  ENABLE );//SPI2ʱÖÓʹÄÜ 	
 
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //PB13/14/15¸´ÓÃÍÆÍìÊä³ö 
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);//³õʼ»¯GPIOB

 	GPIO_SetBits(GPIOB,GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15);  //PB13/14/15ÉÏÀ­

	SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;  //ÉèÖÃSPIµ¥Ïò»òÕßË«ÏòµÄÊý¾Ýģʽ:SPIÉèÖÃΪ˫ÏßË«ÏòÈ«Ë«¹¤
	SPI_InitStructure.SPI_Mode = SPI_Mode_Master;		//ÉèÖÃSPI¹¤×÷ģʽ:ÉèÖÃΪÖ÷SPI
	SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;		//ÉèÖÃSPIµÄÊý¾Ý´óС:SPI·¢ËͽÓÊÕ8λ֡½á¹¹
	SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;		//´®ÐÐͬ²½Ê±ÖӵĿÕÏÐ״̬Ϊ¸ßµçƽ
	SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;	//´®ÐÐͬ²½Ê±Öӵĵڶþ¸öÌø±äÑØ£¨ÉÏÉý»òϽµ£©Êý¾Ý±»²ÉÑù
	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;		//NSSÐźÅÓÉÓ²¼þ£¨NSS¹Ü½Å£©»¹ÊÇÈí¼þ£¨Ê¹ÓÃSSI룩¹ÜÀí:ÄÚ²¿NSSÐźÅÓÐSSIλ¿ØÖÆ
	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;		//¶¨Ò岨ÌØÂÊÔ¤·ÖƵµÄÖµ:²¨ÌØÂÊÔ¤·ÖƵֵΪ256
	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;	//Ö¸¶¨Êý¾Ý´«Êä´ÓMSBλ»¹ÊÇLSBλ¿ªÊ¼:Êý¾Ý´«Êä´ÓMSBλ¿ªÊ¼
	SPI_InitStructure.SPI_CRCPolynomial = 7;	//CRCÖµ¼ÆËãµÄ¶àÏîʽ
	SPI_Init(SPI2, &SPI_InitStructure);  //¸ù¾ÝSPI_InitStructÖÐÖ¸¶¨µÄ²ÎÊý³õʼ»¯ÍâÉèSPIx¼Ä´æÆ÷
 
	SPI_Cmd(SPI2, ENABLE); 
	
	SPI2_ReadWriteByte(0xff);		 
 

}   

//SpeedSet:
//SPI_BaudRatePrescaler_2   2分频 
//SPI_BaudRatePrescaler_8   8分频 
//SPI_BaudRatePrescaler_16  16分频   
//SPI_BaudRatePrescaler_256 256分频   
void SPI2_SetSpeed(u8 SPI_BaudRatePrescaler)
{
  assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));
	SPI2->CR1&=0XFFC7;
	SPI2->CR1|=SPI_BaudRatePrescaler;	 
	SPI_Cmd(SPI2,ENABLE); 

} 

//内部使用的是库函数,可以自行查看源码备注
u8 SPI2_ReadWriteByte(u8 TxData)
{		
	u8 retry=0;				 	
	while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET) 
		retry++;
		if(retry>200)return 0;
		}			  
	SPI_I2S_SendData(SPI2, TxData); 
	retry=0;

	while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET) 
		retry++;
		if(retry>200)return 0;
		}	  						    
	return SPI_I2S_ReceiveData(SPI2); 					    
}




//flash片选引脚初始化
void W25QXX_Init(void)
{	
  	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(	RCC_APB2Periph_GPIOB, ENABLE );
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;  
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 	GPIO_Init(GPIOB, &GPIO_InitStructure);
 	GPIO_SetBits(GPIOB,GPIO_Pin_12);
 
        W25QXX_CS=1;				
	SPI2_Init();		   	
	SPI2_SetSpeed(SPI_BaudRatePrescaler_2);
	W25QXX_TYPE=W25QXX_ReadID();//读取flash产品ID 

}  

//读寄存器
u8 W25QXX_ReadSR(void)   
{  
	u8 byte=0;   
	W25QXX_CS=0;                            
	SPI2_ReadWriteByte(W25X_ReadStatusReg);    
	byte=SPI2_ReadWriteByte(0Xff);          
	W25QXX_CS=1;                             
	return byte;   
} 
//写寄存器
//只有SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2)可以写
void W25QXX_Write_SR(u8 sr)   
{   
	W25QXX_CS=0;                             
	SPI2_ReadWriteByte(W25X_WriteStatusReg);    
	SPI2_ReadWriteByte(sr);               	
	W25QXX_CS=1;                            	      
}   
//写使能  
void W25QXX_Write_Enable(void)   
{
	W25QXX_CS=0;                          	   
    	SPI2_ReadWriteByte(W25X_WriteEnable); 	 
	W25QXX_CS=1;                           	    	      
} 
	
//写失能  
void W25QXX_Write_Disable(void)   
{  
	W25QXX_CS=0;                             
    	SPI2_ReadWriteByte(W25X_WriteDisable);      
	W25QXX_CS=1;                               	      
} 		
	  
u16 W25QXX_ReadID(void)
{
	u16 Temp = 0;	  
	W25QXX_CS=0;				    
	SPI2_ReadWriteByte(0x90);	    
	SPI2_ReadWriteByte(0x00); 	    
	SPI2_ReadWriteByte(0x00); 	    
	SPI2_ReadWriteByte(0x00); 	 			   
	Temp|=SPI2_ReadWriteByte(0xFF)<<8;  
	Temp|=SPI2_ReadWriteByte(0xFF);	 
	W25QXX_CS=1;				    
	return Temp;
}   		    

//pBuffer:数据存储区
//ReadAddr:地址
//NumByteToRead:需要读取的长度
void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead)   
{ 
 	u16 i;   										    
	W25QXX_CS=0;                            	   
    	SPI2_ReadWriteByte(W25X_ReadData);         	   
   	SPI2_ReadWriteByte((u8)((ReadAddr)>>16));  	    
    	SPI2_ReadWriteByte((u8)((ReadAddr)>>8));   
    	SPI2_ReadWriteByte((u8)ReadAddr);   
    	for(i=0;i	pBuffer[i]=SPI2_ReadWriteByte(0XFF);   	 
  	}
	W25QXX_CS=1;  				    	      
}  

//页写入,一次最多只能写入256个byte	 
void W25QXX_Write_Page(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
{
 	u16 i;  
    	W25QXX_Write_Enable();                  	 
	W25QXX_CS=0;                            	   
    	SPI2_ReadWriteByte(W25X_PageProgram);      	   
    	SPI2_ReadWriteByte((u8)((WriteAddr)>>16)); 	    
    	SPI2_ReadWriteByte((u8)((WriteAddr)>>8));   
    	SPI2_ReadWriteByte((u8)WriteAddr);   
    	for(i=0;i
void W25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)   
{ 			 		 
	u16 pageremain;	   
	pageremain=256-WriteAddr%256; 		 	    
	if(NumByteToWrite<=pageremain)pageremain=NumByteToWrite;
	while(1)
	{	   
		W25QXX_Write_Page(pBuffer,WriteAddr,pageremain);
		if(NumByteToWrite==pageremain)break;
	 	else //NumByteToWrite>pageremain
		{
			pBuffer+=pageremain;
			WriteAddr+=pageremain;	

			NumByteToWrite-=pageremain;			 
			if(NumByteToWrite>256)pageremain=256; 
			else pageremain=NumByteToWrite; 	  
		}
	};	    
} 
   
u8 W25QXX_BUFFER[4096];		 
void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)   
{ 
	u32 secpos;
	u16 secoff;
	u16 secremain;	   
 	u16 i;    
	u8 * W25QXX_BUF;	  
   	W25QXX_BUF=W25QXX_BUFFER;	     
 	secpos=WriteAddr/4096;  
	secoff=WriteAddr%4096;
	secremain=4096-secoff;   
 	if(NumByteToWrite<=secremain)secremain=NumByteToWrite;
	while(1) 
	{	//检查需要擦除的部分,非0xff为不干净的,需要擦除
		W25QXX_Read(W25QXX_BUF,secpos*4096,4096);
		for(i=0;i
			W25QXX_Write_NoCheck(pBuffer,WriteAddr,secremain); 				   
		if(NumByteToWrite==secremain)
			break;
		else//根据flash的内存特性,写入是做相应的地址偏移
		{
			secpos++;
			secoff=0;	 

		   	pBuffer+=secremain;  				
			WriteAddr+=secremain;				   
		   	NumByteToWrite-=secremain;			
			if(NumByteToWrite>4096)secremain=4096;
			else secremain=NumByteToWrite;		
		}	 
	};	 
}
//清除全部内存
void W25QXX_Erase_Chip(void)   
{                                   
    	W25QXX_Write_Enable();                 	 	 
    	W25QXX_Wait_Busy();   
  	W25QXX_CS=0;                            	  
    	SPI2_ReadWriteByte(W25X_ChipErase);        	  
	W25QXX_CS=1;                               	      
	W25QXX_Wait_Busy();   				   		
}   
//扇区擦除
void W25QXX_Erase_Sector(u32 Dst_Addr)   
{  
 	printf("fe:%x\r\n",Dst_Addr);	  
 	Dst_Addr*=4096;
    	W25QXX_Write_Enable();                  		 
    	W25QXX_Wait_Busy();   
  	W25QXX_CS=0;                            	   
    	SPI2_ReadWriteByte(W25X_SectorErase);      	 
    	SPI2_ReadWriteByte((u8)((Dst_Addr)>>16));  	    
    	SPI2_ReadWriteByte((u8)((Dst_Addr)>>8));   
    	SPI2_ReadWriteByte((u8)Dst_Addr);  
	W25QXX_CS=1;                            	  	      
    	W25QXX_Wait_Busy();   				   		
}
//等待非busy
void W25QXX_Wait_Busy(void)   
{   
	while((W25QXX_ReadSR()&0x01)==0x01);  		// µÈ´ýBUSYλÇå¿Õ
}  

void W25QXX_PowerDown(void)   
{ 
  	W25QXX_CS=0;                           	 	//ʹÄÜÆ÷¼þ   
    	SPI2_ReadWriteByte(W25X_PowerDown);        //·¢Ë͵ôµçÃüÁî  
	W25QXX_CS=1;                            	//È¡ÏûƬѡ     	      
    delay_us(3);                               //µÈ´ýTPD  
}   

void W25QXX_WAKEUP(void)   
{  
  	W25QXX_CS=0;                            	//ʹÄÜÆ÷¼þ   
    	SPI2_ReadWriteByte(W25X_ReleasePowerDown);	//  send W25X_PowerDown command 0xAB    
	W25QXX_CS=1;                            	//È¡ÏûƬѡ     	      
    	delay_us(3);                            	//µÈ´ýTRES1
}   

const u8 TEXT_Buffer[]={"WarShipSTM32 SPI TEST"};
#define SIZE sizeof(TEXT_Buffer)
//
 int main(void)
 {	 
	u8 key;
	u16 i=0;
	u8 datatemp[SIZE];
	u32 FLASH_SIZE; 	 
	delay_init();	    	   
  	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	      SPI2_init();		 	 	
	W25QXX_Init();
 	
	while(W25QXX_ReadID()!=W25Q128)							
	{
		printf("check fail!");
	}    
	FLASH_SIZE=128*1024*1024;	  
	while(1)
	{
		
			W25QXX_Write((u8*)TEXT_Buffer,FLASH_SIZE-100,SIZE);
		
			W25QXX_Read(datatemp,FLASH_SIZE-100,SIZE);					
			printf("the datatemp : %s\n", datatemp);
		}
			   
	}
}















你可能感兴趣的:(ARM通信)