基于STM32读取W25Q64(模拟SPI)

4 篇文章 0 订阅
1 篇文章 0 订阅

如有错误,希望能够提出
关于模拟SPI的介绍详见博主另外一篇博客
在这里插入图片描述

1、读取8位 Device ID

Device ID

/*读取设备ID*/
void Read_Device_ID(void)
{
	u8 ID;
	
	SPI_CS_0();
	Software_SPI_Write_Read(Device_ID);
	Software_SPI_Write_Read(Dummy_Bytes);
	Software_SPI_Write_Read(Dummy_Bytes);
	Software_SPI_Write_Read(Dummy_Bytes);
	ID = Software_SPI_Write_Read(Dummy_Bytes);
	SPI_CS_1();
	printf("Device_ID:%x\r\n",ID);
}

=SPI在读取数据时,为什么我们必须发送虚拟字节Dummy_Bytes才能接收结果?=
SPI必须生成时钟脉冲才能将数据移出。对于大多数(如果不是全部)SPI主机,产生时钟脉冲的唯一方式是发送字节。如果你仔细想想,这是有道理的。
总结:Dummy_Bytes无实际意义,只是为了产生时钟脉冲,这样才能读取数据。

2、读取MANUFACTURER ID和16位Device ID

JEDEC_ID

/*读取制造商ID*/
void Read_JEDEC_ID(void)
{
	u32 Temp;
	u8 Temp0,Temp1,Temp2;
	SPI_CS_0();
	Software_SPI_Write_Read(JEDEC_ID);
	Temp0 = Software_SPI_Write_Read(Dummy_Bytes);
	Temp1 = Software_SPI_Write_Read(Dummy_Bytes);
	Temp2 = Software_SPI_Write_Read(Dummy_Bytes);
	SPI_CS_1();
	 /*把数据组合起来,作为函数的返回值*/
	Temp = (Temp0 << 16) | (Temp1 << 8) | Temp2;
	printf("JEDEC_ID:%x\r\n",Temp);
}

读取成功

ID

串口调试

3、扇区擦除

a、写使能

在这里插入图片描述

在扇区擦除之前要进行写使能

/*
   *写使能
*/
void FLASH_WriteEnable(void)
{
	SPI_CS_0();
	Software_SPI_Write_Read(Write_Enable);
	SPI_CS_1();
}

b、等待BUSY标志位置0

Status Register-1

BUSY是状态寄存器(S0)中的只读位,当器件执行页面编程、扇区擦除、挡路擦除、芯片擦除或写入状态寄存器指令时,该位设置为1状态。在此期间,器件将忽略除读取状态寄存器和擦除挂起指令之外的其他指令(参见交流特性中的TW、TPP、Tse、TBE和Tce)。当编程、擦除或写入状态寄存器指令完成时,BUSY位将被清除为0状态,表示器件已准备好接受进一步的指令。

开始扇区擦除
扇区擦除
为什么要进行数据擦除:因为Flash芯片内的数据只能由1变0,不能由0变1
扇区擦除指令将一个扇区(4K字节)擦除,擦除后扇区位都为1,在执行扇区擦除指令之前需要先执行写使能指令,保证WEL位为1。WEL位是一个只读位,在状态寄存器的S1位。在执行完“写使能”指令后,该位会被硬件自动置1。当芯片掉电后和执行“写禁能”、“页编程”、“扇区擦除”、“块区擦除”以及“芯片擦除”指令都会进入“写保护状态0”。

 /**
  * @brief  擦除FLASH扇区
  * @param  SectorAddr:要擦除的扇区地址
  * @retval 无
  */
void FLASH_SectorErase(u32 SectorAddr)
{
	FLASH_WriteEnable();//写使能
	SPI_FLASH_WaitForWriteEnd();//等待其他操作完成
	SPI_CS_0();
	Software_SPI_Write_Read(Sector_Erase);//发送擦除指令
	Software_SPI_Write_Read((SectorAddr&0XFF0000)>>16);
	Software_SPI_Write_Read((SectorAddr&0X00FF00)>>8);
	Software_SPI_Write_Read((SectorAddr&0X0000FF)>>0);
	SPI_CS_1();//开始擦除
	SPI_FLASH_WaitForWriteEnd();//等待擦除完毕
	printf("擦除完毕\r\n");
}

擦除成功
串口调试

4、写入数据

a、页写入数据

时序图

/*
 * @brief 页写入数据,调用本函数写入数据前必须先擦除扇区 
 * @para  pBuffer 待写入数据的指针
 * @para  WriteAddr 写入地址
 * @para  NumByteToWrite 数据长度,必须小于SPI_FLASH_PerWritePageSize
*/
void SPI_FLASH_PageWrite(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
{
	FLASH_WriteEnable();//在写数据之前一定要进行写使能,不然会写入失败
	SPI_CS_0();
	Software_SPI_Write_Read(Page_Program);//发送页写指令
	Software_SPI_Write_Read((WriteAddr&0XFF0000)>>16);
	Software_SPI_Write_Read((WriteAddr&0x00FF00)>>8);
	Software_SPI_Write_Read((WriteAddr&0x0000FF)>>0);
	if(NumByteToWrite>SPI_FLASH_PerWritePageSize)
	{
		NumByteToWrite = SPI_FLASH_PerWritePageSize;
		printf("超出页写最大范围\r\n");
	}
	while(NumByteToWrite--)
	{
		Software_SPI_Write_Read(*pBuffer);
		pBuffer++;
	}
	SPI_CS_1();
	SPI_FLASH_WaitForWriteEnd();//等待写入完毕
}

页面编程指令允许从1字节到256字节(一页)的数据进行编程
在写数据之前一定要进行写使能,不然会写入失败,博主之前研究半天写入失败结果发现是没有写使能

b、任意位置写入任意大小数据(不超过地址范围)

 /**
  * @brief  对FLASH写入数据,调用本函数写入数据前需要先擦除扇区
  * @param	pBuffer,要写入数据的指针
  * @param  WriteAddr,写入地址
  * @param  NumByteToWrite,写入数据长度
  * @retval 无
  */
void SPI_FLASH_BufferWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite)
{
  u8 NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0, temp = 0;
	
	/*mod运算求余,若writeAddr是SPI_FLASH_PageSize整数倍,运算结果Addr值为0*/
  Addr = WriteAddr % SPI_FLASH_PageSize;
	
	/*差count个数据值,刚好可以对齐到页地址*/
  count = SPI_FLASH_PageSize - Addr;
	/*计算出要写多少整数页*/
  NumOfPage =  NumByteToWrite / SPI_FLASH_PageSize;
	/*mod运算求余,计算出剩余不满一页的字节数*/
  NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;
	
	/* Addr=0,则WriteAddr 刚好按页对齐 aligned  */
  if (Addr == 0)
  {
		/* NumByteToWrite < SPI_FLASH_PageSize */
    if (NumOfPage == 0) 
    {
      SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);
    }
    else /* NumByteToWrite > SPI_FLASH_PageSize */
    { 
			/*先把整数页都写了*/
      while (NumOfPage--)
      {
        SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize);
        WriteAddr +=  SPI_FLASH_PageSize;
        pBuffer += SPI_FLASH_PageSize;
      }
			/*若有多余的不满一页的数据,把它写完*/
      SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);
    }
  }
	/* 若地址与 SPI_FLASH_PageSize 不对齐  */
  else 
  {
		/* NumByteToWrite < SPI_FLASH_PageSize */
    if (NumOfPage == 0)
    {
			/*当前页剩余的count个位置比NumOfSingle小,一页写不完*/
      if (NumOfSingle > count) 
      {
        temp = NumOfSingle - count;
				/*先写满当前页*/
        SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);
				
        WriteAddr +=  count;
        pBuffer += count;
				/*再写剩余的数据*/
        SPI_FLASH_PageWrite(pBuffer, WriteAddr, temp);
      }
      else /*当前页剩余的count个位置能写完NumOfSingle个数据*/
      {
        SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);
      }
    }
    else /* NumByteToWrite > SPI_FLASH_PageSize */
    {
			/*地址不对齐多出的count分开处理,不加入这个运算*/
      NumByteToWrite -= count;
      NumOfPage =  NumByteToWrite / SPI_FLASH_PageSize;
      NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;
			
			/* 先写完count个数据,为的是让下一次要写的地址对齐 */
      SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);
			
			/* 接下来就重复地址对齐的情况 */
      WriteAddr +=  count;
      pBuffer += count;
			/*把整数页都写了*/
      while (NumOfPage--)
      {
        SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize);
        WriteAddr +=  SPI_FLASH_PageSize;
        pBuffer += SPI_FLASH_PageSize;
      }
			/*若有多余的不满一页的数据,把它写完*/
      if (NumOfSingle != 0)
      {
        SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);
      }
    }
  }
}

5、读取数据

读取数据

 /**
  * @brief   读取FLASH数据
  * @param 	 pBuffer,存储读出数据的指针
  * @param   ReadAddr,读取地址
  * @param   NumByteToRead,读取数据长度
  * @retval  无
  */
void Flash_Read(u8* pBuffer, u32 ReadAddr, u16 NumByteToRead)
{
	SPI_CS_0();
	//读指令
	Software_SPI_Write_Read(Read_Data);
	//读地址
	Software_SPI_Write_Read((ReadAddr&0XFF0000)>>16);
	Software_SPI_Write_Read((ReadAddr&0x00FF00)>>8);
	Software_SPI_Write_Read((ReadAddr&0x0000FF)>>0);
	//读取数据
	while(NumByteToRead--)
	{
		*pBuffer = Software_SPI_Write_Read(Dummy_Bytes);
		pBuffer++;
	}
	SPI_CS_1();
}

6、主函数

float data[] = {0.11,1.12,2.23,3.34,4.45,5.55};
float readdata[6]={0};

int   num[] = {1,2,3,4,5,6};
int   rnum[6] = {0};
/**
  * @brief  主函数
  * @param  无  
  * @retval 无
  */
int main(void)
{	
	u8 flag=0xcc,i=0;
	u8 rflag;
	USART_Config();
	Software_SPI_Init();
	Read_Device_ID();
	SysTick_Delay_Us(10);
	Read_JEDEC_ID();
	while(1)
	{
		Flash_Read(&rflag,SPI_FLASH_PageSize*0,1);
		if(rflag==0xcc)//有数据
		{
			printf("\r\n有数据,进行读取\r\n");
			Flash_Read((void*)readdata,SPI_FLASH_PageSize*1,sizeof(data));
			Flash_Read((void*)rnum,SPI_FLASH_PageSize*2,sizeof(num));
			for(i=0;i<6;i++)
			{
				printf("%f\r\n%d\r\n",readdata[i],rnum[i]);
			}
			printf("读取完成,进行擦除,请复位重新写入\r\n");
			FLASH_SectorErase(0);
			while(1);
		}
		else
		{
			printf("无数据 开始写入\r\n");
			SPI_FLASH_BufferWrite(&flag,SPI_FLASH_PageSize*0,1);	
			SPI_FLASH_BufferWrite((void*)data,SPI_FLASH_PageSize*1,sizeof(data));//小数
			SPI_FLASH_BufferWrite((void*)num,SPI_FLASH_PageSize*2,sizeof(num));//整数
			printf("写入完成,请复位读取\r\n");
			while(1);
		}
	}

}

效果演示
效果
效果
本文参考了一下两篇文章,感谢
关于flahs存取小数问题
W25Q64说明手册
工程下载

  • 4
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值