STM32单片机初学8-SPI flash(W25Q128)数据读写

这篇具有很好参考价值的文章主要介绍了STM32单片机初学8-SPI flash(W25Q128)数据读写。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

        当使用单片机进行项目开发,涉及大量数据需要储存时(例如使用了屏幕作为显示设备,常常需要存储图片、动画等数据),单靠单片机内部的Flash往往是不够用的。

w25q128写保护功能是控制wp电平实现吗,单片机,stm32,嵌入式硬件,单片机,stm32

        如STM32F103系列,内部Flash最多只能达到512KByte,假设要储存240*240分辨率、64K彩色图片,只够存储4张左右。如果使用外置储存器,将图片等其他数据放置在外置储存器,内部Flash只储存程序,就能减小内部Flash的需求,降低成本。     w25q128写保护功能是控制wp电平实现吗,单片机,stm32,嵌入式硬件,单片机,stm32         Flash种类有很多,按其制程和制作工艺的不同可分为NOR Flash、NAND Flash。NAND的串行结构使得其容量很容易做的很大(SD卡、U盘、硬盘大都采用该类Flash),但是其读取速度却比不上并行结构的NOR Flash,且可靠性要差些,一旦出现数据块坏点,是不可逆、无法修复的。由于其数据存储原理,Flash在写入新的数据之前,都需要将数据地址所在的块擦除,NOR Flash的擦除速度比NAND慢很多。

w25q128写保护功能是控制wp电平实现吗,单片机,stm32,嵌入式硬件,单片机,stm32

        Flash按其数据传输方式的不同可分为并口传输与串口传输。STM32的并口传输需使用FSMC接口,虽然其读写速度很快,但对于100PIN脚以下的封装是不带FSMC功能的。

w25q128写保护功能是控制wp电平实现吗,单片机,stm32,嵌入式硬件,单片机,stm32

         所以使用更多的是串口传输方式。串口方式一般采用的是SPI通讯。

w25q128写保护功能是控制wp电平实现吗,单片机,stm32,嵌入式硬件,单片机,stm32

          W25Q系列Flash是Winbond(台湾华邦科技)生产的SPI Flash系列,是单片机开发中比较常用的外置Flash。其支持标准四线SPI、Dual SPI、Quad SPI、QPI,其时钟频率分别可达到104MHz、208MHz、416MHz。对于STM32F103系列,其主频最高72MHz(SPI通信速率最高18Mbps),所以标准SPI就已经是足够F103系列单片机使用了。这里我使用W25Q128FV来讲解Flash的使用。

w25q128写保护功能是控制wp电平实现吗,单片机,stm32,嵌入式硬件,单片机,stm32

        先来了解其引脚定义,上面展示的是SOP8封装,还有SOP16封装的,功能都是差不多的。

w25q128写保护功能是控制wp电平实现吗,单片机,stm32,嵌入式硬件,单片机,stm32        /CS: Flash的片选引脚。当/CS高时,Flash的串行数据输出(DO或IO0、IO1、IO2、IO3)引脚处于高阻抗,此时设备功耗将处于待机水平(除非正在进行内部擦除、程序或写入状态寄存器周期)。当/CS为低电平,Flash将被选中,功耗将增加到活动水平,并且可以向该设备写入指令和从该设备读取数据。启动后,/CS必须从高电平转换到低电平,才能接受新的指令。

        DO(IO1):DO指数据输出口(Data Out),一般连接到单片机SPI接口的数据输入端,即MISO。IO1是其复用功能,当启用SPI四位传输模式时,该引脚功能为IO1.

        /WP:写保护(WP)引脚。可以用来防止状态寄存器被写入。与状态寄存器的块保护(CMP, SEC, TB, BP2, BP1和BPO)位和状态寄存器保护(SRP)位一起使用,小到4KB扇区或整个内存阵列都可以被硬件保护。/WP引脚低电平有效。当状态寄存器2的QE位设置为Quad I/O时,/WP引脚功能不可用,因为该引脚用于IO2。如果不想使用该功能,可以直接将该引脚接VCC。

        GND:Flash的供电GND

        DI:DI指数据输入口(Data In),一般连接到单片机SPI接口的数据输出端,即MOSI。IO0是其复用功能,当启用SPI四位传输模式时,该引脚为IO0

        CLK:SPI时钟线。连接至单片机的SPI时钟接口。

        /HOLD,/RESET:/HOLD能让设备主动暂停数据传输。当/HOLD低时,而/CS低时,DO引脚将处于高阻抗,Dl和CLK引脚上的信号将被忽略。当/HOLD调高时,设备可以恢复运行。当多个设备共享相同的SPl信号时,/HOLD就能发挥作用。 /RESET引脚用于设备复位。注意,如果在写入数据时复位,可能会造成数据丢失。所以如果不需要给Flash复位,该引脚常常直接与VCC相接。

        VCC:Flash供电电源3.3V。

w25q128写保护功能是控制wp电平实现吗,单片机,stm32,嵌入式硬件,单片机,stm32

         不管何种存储器,在进行数据读写时都需要知道数据的地址。数据存储在寄存器中,所以数据的地址即寄存器地址。我们来看看W25Q128的内部原理图。

w25q128写保护功能是控制wp电平实现吗,单片机,stm32,嵌入式硬件,单片机,stm32

        其内部是由数据存储单元和各种控制器组成。

        存储单元的最小单位为一个寄存器,每个寄存器可存储1个字节的数据。

        每256个寄存器组成一页(Page),也就是一页能存储256Byte数据,

        每16页组成一个扇区(Sector),一个扇区能储存16x256=4096Byte数据(近似4KB)。比如扇区0的数据地址范围为000000 h-000FFF h。

        每16个扇区又组成一个块(Block),一个块能储存4096x16=65536Byte数据(近似64K)。例如块0的数据地址范围为000000 h-00FFFF h 。

         整个存储单元共256个块,所以其总存储容量为256x65536=16777216Byte数据,近似为16MByte。数据地址范围为000000 h-FFFFFF h。


        不管何种外设,都是通过发送命令与数据来控制的。Flash也不例外,所以需要知道如何使用Flash,只需在其技术手册上找到其命令表即可。

w25q128写保护功能是控制wp电平实现吗,单片机,stm32,嵌入式硬件,单片机,stm32

w25q128写保护功能是控制wp电平实现吗,单片机,stm32,嵌入式硬件,单片机,stm32

         可用的命令有很多,但常用的就一部分。

        现在我们来讲解程序里如何实现STM32F103读写SPI Flash的数据。

        这里我使用的是SPI2,硬件连接如下。

w25q128写保护功能是控制wp电平实现吗,单片机,stm32,嵌入式硬件,单片机,stm32

        再来看程序部分:

1.SPI2初始化。为了将读出来的数据显示出来,这里我使用串口将数据传输到电脑上。所以对usart1也初始化。

void SPI2_UserInit(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);	//使能GPIOB的时钟

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13|GPIO_Pin_15;    //PA13为SCK时钟,PA15为MISO
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;			//速度50MHz
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;			//复用推挽输出
  GPIO_Init(GPIOB, &GPIO_InitStructure);
	
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;					//PA14为MISO
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;			    //速度50MHz
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;			//浮空输入
  GPIO_Init(GPIOB, &GPIO_InitStructure);
	
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;					//PA12为片选
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;			    //速度50MHz
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;				//复用推挽输出
  GPIO_Init(GPIOB, &GPIO_InitStructure);
 
  SPI_InitTypeDef SPI_InitStructure;
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);		     //使能SPI时钟
	
  SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;//设置双向双线全双工
  SPI_InitStructure.SPI_Mode = SPI_Mode_Master;						//设置为SPI主站
  SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;					//设置为8位帧结构
  SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;					//串行时钟的稳态为时钟高
  SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;		//位捕获的时钟活动沿为第1个时钟沿
  SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;					//指定NSS信号由软件控制
  SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;    //波特率预分频值
  SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;					//数据位从MSB开始
  SPI_InitStructure.SPI_CRCPolynomial = 7;								//CRC检验
  SPI_Init(SPI2, &SPI_InitStructure);							//按以上设置初始化SPI2
 
  SPI_Cmd(SPI2, ENABLE);							//使能SPI2
  GPIO_SetBits(GPIOB,GPIO_Pin_12);	            	//CS置高
}

void USART1_Userinit(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;    //PA9为USART1_TX将这个GPIO初始化
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;			//速度50MHz
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;				//复用推挽输出
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;	//PA10为USART_RX,将这个GPIO初始化
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;			//速度50MHz
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;		//浮空输入
	GPIO_Init(GPIOA, &GPIO_InitStructure);
		
	USART_InitTypeDef USART_InitStructure;						//定义USART配置结构体
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);		//打开USART1时钟
	
	USART_InitStructure.USART_BaudRate = 115200; 					//波特率
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;		//数据帧位数 
	USART_InitStructure.USART_StopBits = USART_StopBits_1; 			//停止位数目
	USART_InitStructure.USART_Parity = USART_Parity_No; 			//奇偶模式(USART_Parity_No 无,USART_Parity_Even 偶SART_Parity_Odd奇)
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; 	//硬件流控制模式
	USART_InitStructure.USART_Mode = USART_Mode_Tx| USART_Mode_Rx; 									//发送、接收使能
	USART_Init(USART1, &USART_InitStructure);											//初始化
	USART_Cmd(USART1,ENABLE);																			//使能USART1串口	
	
	USART_ITConfig(USART1,USART_IT_RXNE, ENABLE);		//使能USART1接收中断
	
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;				//中断通道
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;	//抢占优先级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority =3;				//子优先级
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;					//使能中断
	NVIC_Init(&NVIC_InitStructure);									//初始化中断
	
}

2.发送命令或者读写数据都是通过最基本的发送数据、接受数据函数来实现。为了不出现数据丢失,每次发送数据前都需要判断上次发送的数据是否已经发送玩,这可以通过相关标志位来判断;同样,为了不出现数据重复,每次接收数据前都要判断接收缓存区是否为空。

w25q128写保护功能是控制wp电平实现吗,单片机,stm32,嵌入式硬件,单片机,stm32

void Flash_WriteData8(u8 Data)	//写8位数据(1个字节)
{
	u8 Wait=0;	
	while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET&&Wait<20) //检查指定的SPI标志位设置与否:发送缓存空标志位,RESET表示正在发送数据
	{
		Wait++;//循环计数200,计数200此(大概20us),不管是否标志位为空,都退出等待
	}
	SPI_I2S_ClearFlag(SPI2, SPI_I2S_FLAG_TXE);    //清除发送完成标志位
	SPI_I2S_SendData(SPI2,Data);				//发送Data
	
}

u8 Flash_ReadData( )														//读一个字节
{		
	u8 Wait=0;
	SPI_I2S_SendData(SPI2,0xff);									//发送0x00,产生时钟信号,用来接收数据,也可以发送其他无响应的命令
	while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET&&Wait<200) //检查指定的SPI标志位设置与否:接受缓存非空标志位
	{
		Wait++;	
	}	
	SPI_I2S_ClearFlag(SPI2, SPI_I2S_FLAG_RXNE);	//清除标志位
	return SPI_I2S_ReceiveData(SPI2);						 //返回通过SPI2最近接收的数据			
}

u8 Flash_WriteReadByte(u8 Data)		//读写函数
{
	while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET) //检查指定的SPI标志位设置与否:发送缓存空标志位,RESET表示正在发送数据
	{

	}
	SPI_I2S_SendData(SPI2,Data);				//发送Data
	
	while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET) //检查指定的SPI标志位设置与否:接受缓存非空标志位
	{

	}	
	return SPI_I2S_ReceiveData(SPI2);						 //返回通过SPIx最近接收的数据	
}

        需要注意:在接受数据的函数中,之所以在SPI_I2S_ReceiveData(SPI2)函数之前要使用SPI_I2S_SendData(SPI2,0xff)函数,是为了产生时钟信号。

        SPI采用的是主从通信结构,时钟信号只能由主设备产生,主设备发送数据的过程中会产生时钟信号,但是从设备发送数据时并不能自己产生时钟信号,所以就无法将数据一位一位发送出去(同步通信必须依靠时钟信号保持时序一致),那就只能依靠主设备产生时钟信号。主设备发送的0xFF,对从设备来说,是无效的数据,不会对该数据做出响应,但是主设备发送0xFF这个数据的时候,产生了时钟信号,所以从设备就依靠这段时钟信号,将数据发送给了主设备,主设备接受会暂存在接收缓存寄存器中,等接受到新的数据自动更新缓存器。

w25q128写保护功能是控制wp电平实现吗,单片机,stm32,嵌入式硬件,单片机,stm32

         具体的通信时序可查阅W25Q128的技术手册(W25Q128FV_PDF_数据手册_Datasheet),这里就不一一列举了。

3.把W25Q128常用的命令封装成函数,只要调用对应的函数,就能实现命令的发送与数据的读写

#define Flash_CS_H() GPIO_SetBits(GPIOB,GPIO_Pin_12)		//Flash 片选信号
#define Flash_CS_L() GPIO_ResetBits(GPIOB,GPIO_Pin_12)      //低电平选中,高电平取消选中

/*****W25Q128常用命令定义*****/
#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_BlockErase32		0x52 		//32K块擦除
#define W25X_BlockErase64		0xD8 		//64K块擦除
#define W25X_SectorErase		0x20 		//4k扇区擦除
#define W25X_ChipErase			0xC7 		//整片擦除
#define W25X_PowerDown			0xB9 		//
#define W25X_ReleasePowerDown	0xAB 	
#define W25X_DeviceID			0xAB 		//读设备ID
#define W25X_ManufactDeviceID	0x90 
#define W25X_JedecDeviceID		0x9F		//读取JEDECID

void Flash_WriteEnable(void)   				//使能写入
{ 
	Flash_CS_L();
	Flash_WriteReadByte(W25X_WriteEnable); 		//发送写使能
	Flash_CS_H();
} 
 

void Flash_WriteDisable(void)  		 		//禁止写入
{  	
	Flash_CS_L();
	Flash_WriteReadByte(W25X_WriteDisable); 	 //发送写禁止指令  
	Flash_CS_H();	
}


 /**
  * @brief  等待WIP(BUSY)标志被置0,即等待到FLASH内部数据写入完毕
  * @param  none
  * @retval none
  */
void SPI_FLASH_WaitBusy(void)
{
	u8 FLASH_Status = 0;
  
	Flash_CS_L();		
	Flash_WriteReadByte(W25X_ReadStatusReg1); 	//发送读取状态寄存器命令  
	do
	{
		FLASH_Status=Flash_WriteReadByte(0xff); 		
	}
	while ((FLASH_Status & 0x01) == 1);  		//判断是否正忙
	Flash_CS_H();		
}


/******************基本功能*****************/
/************读Flash状态寄存器1************/

u8 Flash_ReadSR1(void)   					//读状态寄存器1(busy时也可读)
{  
	u8 Byte;
	Flash_CS_L();								//CS选中
	Flash_WriteReadByte(W25X_ReadStatusReg1); 	//发送读取状态寄存器命令    
	Byte=Flash_WriteReadByte(0xff); 			//读取一个字节 
	Flash_CS_H();								//CS取消选中
	return Byte;   
} 

u8 Flash_ReadSR2(void)   					//读状态寄存器2(busy时也可读)
{  
	u8 Byte;
	Flash_CS_L();							//CS选中
	Flash_WriteReadByte(W25X_ReadStatusReg2); 	//发送读取状态寄存器命令    
	Byte=Flash_WriteReadByte(0xff); 					//读取一个字节 
	Flash_CS_H();
	return Byte;   
}

u8 Flash_ReadSR3(void)   					//读状态寄存器3(busy时也可读)
{  
	u8 Byte;
	Flash_CS_L();							//CS选中
	Flash_WriteReadByte(W25X_ReadStatusReg3); 	//发送读取状态寄存器命令    
	Byte=Flash_WriteReadByte(0xff); 					//读取一个字节 
	Flash_CS_H();
	return Byte;   
}

void Flash_WriteSR1(u8 Sr)   	//写入
{   
	Flash_WriteEnable();
	Flash_CS_L();
	Flash_WriteReadByte(W25X_WriteStatusReg);	//发送写状态寄存器命令    
	Flash_WriteReadByte(Sr);               		//写入一个字节 
	Flash_CS_H();
}   

void Flash_WriteSR2(u8 Sr)   
{   
	Flash_WriteEnable();
	Flash_CS_L();	
	Flash_WriteReadByte(W25X_WriteStatusReg2);	//发送写状态寄存器命令    
	Flash_WriteReadByte(Sr);               		//写入一个字节 	
	Flash_CS_H();
}   

void Flash_WriteSR3(u8 Sr)   
{   
	Flash_WriteEnable();
	Flash_CS_L();
	Flash_WriteReadByte(W25X_WriteStatusReg3);	//发送写状态寄存器命令    
	Flash_WriteReadByte(Sr);               		//写入一个字节 
	Flash_CS_H();	
}   



u16 Flash_ReadID(void)				//读取设备ID(16位JEDEC assigned Manufacturer ID )
{
	u16 Temp ;	 
	u8 TempL,TempH;	
	Flash_CS_L();
	Flash_WriteReadByte(W25X_ManufactDeviceID);	//发送读取ID命令
	Flash_WriteReadByte(0x00);					//指令通过将/CS引脚压低并移动指令代码“90h”和24位地址000000h来启动。			
	Flash_WriteReadByte(0x00);							
	Flash_WriteReadByte(0x00);								
	TempH=Flash_WriteReadByte(0xff);			//接收高8位
	TempL=Flash_WriteReadByte(0xff);			//接收高8位
	Flash_CS_H();
	Temp=TempH;
	Temp<<=8;									//左移8位
	Temp|=TempL;								//高8位与低8位合并成16位(与运算后赋值)
	return Temp;
}


void Flash_ReadSector(u32 ReadAddr)  //读一个扇区
{
	u16 i; 
	Flash_CS_L();	
	Flash_WriteReadByte(W25X_ReadData);         //发送读取命令 
	while(SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE)==RESET)//判断发送缓存区是否为空
	{
		
	}
	Flash_WriteReadByte((u8)((ReadAddr)>>16));  	//发送24bit地址  
	while(SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE)==RESET)//判断发送缓存区是否为空
	{
		
	}	
	Flash_WriteReadByte((u8)((ReadAddr)>>8));
	while(SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE)==RESET)//判断发送缓存区是否为空
	{
		
	}	
	Flash_WriteReadByte((u8)ReadAddr);   
	for(i=0;i<4096;i++)						//一个扇区4096个Byte
	{ 
		FlashSector_Buffer[i]=Flash_WriteReadByte(0xff);   	//循环读取每个字节存在 扇区缓存中
	}	
	Flash_CS_H();
}

void Flash_ReadPage(u32 ReadAddr)  //读一页
{
	u16 i; 
	while((Flash_ReadSR1()&0x00)==0x01);  	// 等待BUSY位清空(等待擦除完成)
	{
		
	}
	Flash_CS_L();	
	Flash_WriteReadByte(W25X_ReadData);         	//发送读取命令   
	Flash_WriteReadByte((u8)((ReadAddr)>>16));  	//发送24bit地址    
	Flash_WriteReadByte((u8)((ReadAddr)>>8));   
	Flash_WriteReadByte((u8)ReadAddr);   
	for(i=0;i<256;i++)				//一个页256个Byte
	{ 
     FlashPage_Buffer[i]=Flash_WriteReadByte(0xff);   	//循环读取每个字节存在 扇区缓存中
	}	
	Flash_CS_H();
}


void Flash_EraseSector(u32 Dst_Addr)   		//擦除扇区(150ms左右)
{  
	Flash_WriteEnable();          //在设备接受扇区擦除指令(状态寄存器位WEL必须等于1)之前,必须执行Write Enable指令。	
	while((Flash_ReadSR1()&0x02)==0x00);  	// 等待BUSY位清空(等待擦除完成)
	{
		
	}
	Flash_CS_L();
	Flash_WriteReadByte(W25X_SectorErase);      	//发送扇区擦除指令 
	Flash_WriteReadByte((u8)((Dst_Addr)>>16));  	//发送24bit地址    
	Flash_WriteReadByte((u8)((Dst_Addr)>>8));   
	Flash_WriteReadByte((u8)Dst_Addr);
	Delay_us(1);
	Flash_CS_H();							//在最后一个字节的第8位被锁存之后,/CS引脚必须置高。否则扇区擦除指令不执行
	while((Flash_ReadSR1()&0x01)==0x01);  	// 等待BUSY位清空(等待擦除完成)
	{
		Delay_us(1);
	}
} 

void Flash_EraseChip(void)   		//整片擦除(4s左右)
{  
	Flash_WriteEnable();          //在设备接受扇区擦除指令(状态寄存器位WEL必须等于1)之前,必须执行Write Enable指令。	
	Delay_us(10);
	Flash_CS_L();
	Flash_WriteReadByte(W25X_ChipErase);      	//发送整片擦除指令 
	Flash_CS_H();							//在最后一个字节的第8位被锁存之后,/CS引脚必须置高。否则擦除指令不执行
	while((Flash_ReadSR1()&0x01)==0x01);  	// 等待BUSY位清空(等待擦除完成)
	{
		//Sr=Flash_ReadSR1();
		Delay_us(1);
	}
}

void Flash_WritePage(u32 WriteAddr)					//写一页(每次写入的最大数量为1页)
{
	u16 i; 
	Flash_WriteEnable(); 
	while((Flash_ReadSR1()&0x01)==0x01);  	// 等待BUSY位清空(等待擦除完成)
	{
	
	}
	Flash_CS_L();
	Flash_WriteReadByte(W25X_PageProgram);      	//发送写页命令   
	Flash_WriteReadByte((u8)((WriteAddr)>>16)); 	//发送24bit地址    
	Flash_WriteReadByte((u8)((WriteAddr)>>8));   
	Flash_WriteReadByte((u8)WriteAddr);   
	for(i=0;i<256;i++)
	{
		Flash_WriteReadByte(FlashPage_Buffer[i]);
	} 	
	Flash_CS_H();
	while((Flash_ReadSR1()&0x01)==0x01);  	// 等待BUSY位清空(等待擦除完成)
	{
	
	}
}


void Flash_WriteReadBytes(u32 WriteAddr,u8 NumByte)		//写入多个字节(不大于256)
{
	u8 i;
	Flash_CS_L();
	Flash_WriteReadByte(W25X_PageProgram);      	//发送写页命令   
	Flash_WriteReadByte((u8)((WriteAddr)>>16)); 	//发送24bit地址    
	Flash_WriteReadByte((u8)((WriteAddr)>>8));   
	Flash_WriteReadByte((u8)WriteAddr);   
	for(i=0;i<NumByte;i++)
	{
		Flash_WriteReadByte(FlashPage_Buffer[i]);
	} 
	Flash_CS_H();
}


u16 Flash_Init(void)			//SPI Flash初始化
{
	u16 ID;
	Flash_CS_L();
	Flash_WriteReadByte(W25X_ReleasePowerDown);	//退出休眠
	Flash_CS_H();
	ID=Flash_ReadID();
	return ID;
}

4.主函数。先读取设备ID,然后将数组ARR1的数据写入Flash的扇区0,再将扇区0的数据读取出来放在数组ARR2中,通过串口将ARR2的数据显示到电脑(串口调试助手)。

#include<stm32f10x.h>

u8 ARR1[10]={0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A};//随意填入几个元素,后面将其元素写入Flash中
u8 ARR2[10];    //后面将Flash的数据读出来,复制到该数组中

void USART_SendDatatoUSB( char ASCII[])    //串口发送字符串函数
{
	u8 i,j,Wait;
	for(i=0;i<12;i++)
	{
		u8 Wait=0;				 	
		while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET&&Wait<200) 
		{
			Wait++;//循环计数200,计数200此(大概20us),不管是否标志位为空,都退出等待
		}
		USART_ClearFlag(USART1,USART_FLAG_TC);
		j=ASCII[i];
		USART_SendData(USART1,j);
	}
	Wait=0;
	while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET&&Wait<200) 
	{
		Wait++;//循环计数200,计数200此(大概20us),不管是否标志位为空,都退出等待
	}
	USART_ClearFlag(USART1,USART_FLAG_TC);
	USART_SendData(USART1,10);	//换行	
}

int main()
{
    SPI2_UserInit();			//SPI2初始化----控制SPI Flash
    USART1_Userinit();			//USART1初始化--控制串口CH340
    
    Flash_Write_Enable();    //Flash写使能
    USART_SendDatatoUSB( "Flash:ID")
	
	Data=Flash_ReadID();    //读取ID(16位)
	while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET)
    {

     }
	USART_SendData(USART1,(Data>>16)&0xFF);
	while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET)
    {

     }
	USART_SendData(USART1,(Data>>8)&0xFF);

	Flash_EraseSector(0x000000);        //擦除扇区
    USART_SendDatatoUSB( "扇区擦除完成");
    Flash_WritePage(0x000000,10);        //写入数据
    USART_SendDatatoUSB( "数据写入成功");
    
    Flash_ReadSector(0x000000)  //读数据
    USART_SendDatatoUSB( "数据读取成功");
    

	for(i=0;i<10;i++)
	{
		u8 Wait=0;				 	
		while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET&&Wait<200) //检查指定的SPI标志位设置与否:发送缓存空标志位,RESET表示正在发送数据
		{
			Wait++;//循环计数200,计数200此(大概20us),不管是否标志位为空,都退出等待
		}
		USART_ClearFlag(USART1,USART_FLAG_TC);
		j=ARR2[i];
		USART_SendData(USART1,j);
	}
}

最后接收到数据如下。

w25q128写保护功能是控制wp电平实现吗,单片机,stm32,嵌入式硬件,单片机,stm32文章来源地址https://www.toymoban.com/news/detail-780357.html

到了这里,关于STM32单片机初学8-SPI flash(W25Q128)数据读写的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • STM32—SPI详解入门(使用SPI通讯读写W25Q128模块)

    目录 一、SPI是什么 二、SPI物理架构 三、SPI工作原理 四、SPI工作模式 五、SPI相关寄存器介绍 六、SPI用到的结构体与函数 1.结构体 2.函数 七、W25Q128芯片 1.W25Q128介绍 2.W25Q128存储架构 3.W25Q128常用指令 4.W25Q128状态寄存器 5.W25Q128常见操作流程 八、实验(使用SPI通讯读写W25Q128模块

    2024年02月14日
    浏览(53)
  • 初学者入门:认识STM32单片机

    本教程含有较多专业词汇,大部分时候,不完全理解并不影响继续往下阅读,大家只需要了解大致的概念即可。当然,也鼓励大家多查百度和多问chatgpt,让自己学会的更多。 什么是单片机? 单片机,就是把中央处理器CPU、存储器、等计算机的功能部件,和定时器、I/0(输入

    2024年02月07日
    浏览(57)
  • STM32F4单片机内部FLASH编程时间

    单片机内部的flash除了存储固件以外,经常将其分为多个区域,用来存储一些参数或存储OTA升级等待更新的固件,这时就会涉及单片机内部flash的编程和擦除操作。STM32同系列的单片机内部flash特性和扇区大小都不太一样,以下基于STM32F407VET6此型号进行简单介绍。 STM32F4xx中文参

    2024年02月03日
    浏览(59)
  • 使用stm32的模拟spi读取w25q128读取设备ID时一直出现0xFF

    由于公司的电路是前辈画的,只能使用模拟spi中如图所示   上图是stm32所对应的引脚  上图是w25q128的引脚 当读取的时候ID号一直是0xffff,在网上查了各种方法都试过了都不行,我这个情况稍微特殊,就是使用了PB3、PB4这两个引脚上电复位默认是作为调试端口使用的。所以得先

    2024年02月04日
    浏览(49)
  • STM32单片机初学4-IIC通信(软件模拟)

    IIC ( Inter-Integrated Circuit )又称I2C(习惯读“I方C”),是 IIC Bus简称,中文名为 集成电路总线 ,它是一种串行通信总线,使用多主从架构,由飞利浦公司在1980年代为了让主板、嵌入式系统或手机用以连接低速周边设备而发展。适用于IC间的短距离数据传输。 最初的IIC通信速

    2024年02月05日
    浏览(75)
  • 使用STM32CubeProgrammer工具读取单片机Flash数据读取

    本文主要介绍,如何使用STM32CubeProgrammer工具读取和写入单片机Flash内部的数据,方便调试使用。 2.1 连接Stlink和单片机,点击“connect”进行连接 2.2 读取固定长度的数据 根据程序的大小,设置需要读取的字节数,如下图所示。点击“read”将单片机Flash中的数据读取到STM32Cube

    2024年02月12日
    浏览(44)
  • STM32单片机Flash不擦除直写案例分析

    产品跳闸前需保存致使产品动作的故障类型和具体的故障分析数据,并在产品二次上电后读取故障类型,进行相应指示;之后清除故障类型的相关Flash,但故障分析数据仍保存,以便后续读出分析。然而,Flash扇区擦除时间较长,会影响程序正常运行、判断。 Flash的编程原理都

    2024年02月16日
    浏览(52)
  • 初学stm32单片机可以做什么简单的小作品?

    已从事单片机开发十几年,刚开始接触单片机时,感觉挺有意思。 可以用自己的思维写程序,控制硬件去实现一些智能化的操作。 刚点亮第一个LED,就开始幻想以后能做任何自己想要的产品,那感觉多爽! 但是你会发现学完51单片机、或者STM32单片机以后,还是啥也做不出来

    2024年02月02日
    浏览(53)
  • STM32单片机初学5-IIC通信驱动OLED屏幕

    在我上篇文章(STM32-软件模拟IIC通信)讲解了软件模拟IIC通信。这篇文章详将细讲解利用软件模拟IIC来控制0.96寸的OLED屏幕(如下图),使其显示字符串。本文将不再对IIC通信原理做详细讲解,所以对IIC通信原理不熟悉的话可以参考我上篇文章(点击上面的链接直接跳转)。

    2023年04月10日
    浏览(56)
  • 【STM32】SPI初步使用 读写FLASH W25Q64

    (1) SS( Slave Select):从设备选择信号线,常称为片选信号线,每个从设备都有独立的这一条 NSS 信号线,当主机要选择从设备时,把该从设备的 NSS 信号线设置为低电平,该从设备即被选中,即片选有效,接着主机开始与被选中的从设备进行 SPI通讯。所以 SPI通讯以 NSS 线置低电

    2024年02月10日
    浏览(56)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包