zoukankan      html  css  js  c++  java
  • STM32驱动W25X64存储器

    W25X64 是华邦公司推出的大容量

    SPI  FLASH 产品,W25X64 的容量为 64Mbit(8M),该系列还有 W25Q80/16/32 等。W25X16,W25X32,W25X64分别有8192,16384,32768个可编程页,每页256字节,用扇区擦除指令每次可以擦除16页,用块擦除指令每次可以擦除256页,用整片擦除指令既可以擦除整个芯片,W25X16,W25X32,W25X64分别有512,1024,2048个可擦除扇区,或者32,64,128个可擦除的块

    W25Q64 的擦写周期多达 10W 次,具有 20 年的数据保存期限,支持电压为 2.7~3.6V,
    W25Q64 支持标准的 SPI,还支持双输出/四输出的 SPI,最大 SPI 时钟可以到 80Mhz(双输出
    时相当于 160Mhz,四输出时相当于 320M)


    引脚一般如下


    其中CS DO SIO VCC GND都是SPI通讯引脚

    HOLD是防误操作引脚,该脚为低时,忽略一切外部指令

    wp为保护引脚,该脚为低,数据无法擦除修改

    对W25X64的写入读出都伴随着指令,指令集如下



    有一个很重要的寄存器是状态寄存器,在对flash写入的时候一定要对状态寄存器查看一下


    busy:只读,当flash内部正在进行操作的时候,这一位自动变为1,当该位为1的时候,除了读状态指令,不响应任何指令

    wel:写保护位,只读,当芯片处于写保护状态的时候,该位为0,所以当要对芯片进行操作的时候一定要查看这一位,否则无法写入,该位在掉电后,写禁能,页编程,扇区擦除,芯片擦除以及写状态寄存器特定值之后会变为0,执行写使能命令之后会变成1

    其他状态寄存器


    另外,芯片初始化自检的时候需要读取ID,用于设备识别,id寄存器如下


    代码如下

    #ifndef __FLASH_H
    #define __FLASH_H
    #include "spi.h"
    #include "delay.h" 
    #include "ioremap.h"
    
    
    //W25X系列/Q系列芯片列表	   
    //W25Q80 ID  0XEF13
    //W25Q16 ID  0XEF14
    //W25Q32 ID  0XEF15
    //W25Q32 ID  0XEF16	
    #define W25Q80 	0XEF13 	
    #define W25Q16 	0XEF14
    #define W25Q32 	0XEF15
    #define W25Q64 	0XEF16
    
    extern u16 SPI_FLASH_TYPE;//定义我们使用的flash芯片型号
    
    #define	SPI_FLASH_CS PBout(12)  //选中FLASH	
    
    ////////////////////////////////////////////////////////////////////////////
    
    //指令表
    #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    //读取设备ID
    #define W25X_ManufactDeviceID	        0x90    //
    #define W25X_JedecDeviceID		0x9F    //
    
    void SpiFlashInit(void);
    
    u16  SpiFlashReadID(void);  	    //读取FLASH ID
    
    u8   SpiFlashReadSR(void);        //读取状态寄存器 
    
    void SpiFlashWriteSR(u8 sr);  	//写状态寄存器
    
    void SpiFlashWriteEnable(void);  //写使能 
    
    void SpiFlashWriteDisable(void);	//写保护
    
    void SpiFlashWriteNoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);
    
    void SpiFlashRead(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead);   //读取flash
    
    void SpiFlashWrite(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);//写入flash
    
    void SpiFlashEraseChip(void);    	  //整片擦除
    
    void SpiFlashEraseSector(u32 Dst_Addr);//扇区擦除
    
    void SpiFlashWaitBusy(void);           //等待空闲
    
    void SpiFlashPowerDown(void);           //进入掉电模式
    
    void SpiFlashWakeUp(void);			  //唤醒
    
    u8 SpiFlashCheck(void);              //flash检测
    
    
    
    
    
    
    
    
    
    
    
    #endif
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    #include "flash.h" 
    
    u16 SPI_FLASH_TYPE=W25Q64;//默认就是25Q64
    
    //4Kbytes为一个Sector
    //16个扇区为1个Block
    //W25X16
    //容量为2M字节,共有32个Block,512个Sector 
    
    //初始化SPI FLASH的IO口
    void SpiFlashInit(void)
    {	
        GPIO_InitTypeDef GPIO_InitStructure;
        RCC_APB2PeriphClockCmd(	RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOD|RCC_APB2Periph_GPIOG, ENABLE );//PORTB时钟使能 
        
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;  // PB12 推挽 
        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);
        
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;  // PD2 推挽 
        GPIO_Init(GPIOD, &GPIO_InitStructure);
        GPIO_SetBits(GPIOD,GPIO_Pin_2);
        
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;  // PG7 推挽 
        GPIO_Init(GPIOG, &GPIO_InitStructure);
        GPIO_SetBits(GPIOG,GPIO_Pin_7);
        
        Spi2Init();		   	//初始化SPI
        Spi2SetSpeed(SPI_BaudRatePrescaler_2);//设置为18M时钟,高速模式
        SPI_FLASH_TYPE=SpiFlashReadID();//读取FLASH ID.  
        
    }  
    
    //读取SPI_FLASH的状态寄存器
    //BIT7  6   5   4   3   2   1   0
    //SPR   RV  TB BP2 BP1 BP0 WEL BUSY
    //SPR:默认0,状态寄存器保护位,配合WP使用
    //TB,BP2,BP1,BP0:FLASH区域写保护设置
    //WEL:写使能锁定
    //BUSY:忙标记位(1,忙;0,空闲)
    //默认:0x00
    u8 SpiFlashReadSR(void)   
    {  
        u8 byte=0;   
        SPI_FLASH_CS=0;                            //使能器件   
        Spi2ReadWriteByte(W25X_ReadStatusReg);     //发送读取状态寄存器命令    
        byte=Spi2ReadWriteByte(0Xff);              //读取一个字节  
        SPI_FLASH_CS=1;                            //取消片选     
        return byte;   							
    } 
    
    //写SPI_FLASH状态寄存器
    //只有SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2)可以写!!!
    void SpiFlashWriteSR(u8 sr)   
    {   
        SPI_FLASH_CS=0;                            //使能器件   
        Spi2ReadWriteByte(W25X_WriteStatusReg);   //发送写取状态寄存器命令    
        Spi2ReadWriteByte(sr);               //写入一个字节  
        SPI_FLASH_CS=1;                            //取消片选     	      
    } 
      
    //SPI_FLASH写使能	
    //将WEL置位   
    void SpiFlashWriteEnable(void)   
    {
        SPI_FLASH_CS=0;                            //使能器件   
        Spi2ReadWriteByte(W25X_WriteEnable);      //发送写使能  
        SPI_FLASH_CS=1;                            //取消片选     	      
    } 
    
    //SPI_FLASH写禁止	
    //将WEL清零  
    void SpiFlashWriteDisable(void)   
    {  
        SPI_FLASH_CS=0;                            //使能器件   
        Spi2ReadWriteByte(W25X_WriteDisable);     //发送写禁止指令    
        SPI_FLASH_CS=1;                            //取消片选     	      
    } 		
    	    
    //读取芯片ID W25X16的ID:0XEF14
    u16 SpiFlashReadID(void)
    {
        u16 Temp = 0;	  
        SPI_FLASH_CS=0;				    
        Spi2ReadWriteByte(0x90);//发送读取ID命令	    
        Spi2ReadWriteByte(0x00); 	    
        Spi2ReadWriteByte(0x00); 	    
        Spi2ReadWriteByte(0x00); 	 			   
        Temp|=Spi2ReadWriteByte(0xFF)<<8;  
        Temp|=Spi2ReadWriteByte(0xFF);	 
        SPI_FLASH_CS=1;				    
        return Temp;
    }  
     		 
    //flash检测 检测到返回0 失败返回1
    u8 SpiFlashCheck(void)              
    {
        u16 flashId = 0;
        flashId = SpiFlashReadID();
        if(flashId == W25Q64)
            return 0;
        else
            return 1;
    }
    
    //读取SPI FLASH  
    //在指定地址开始读取指定长度的数据
    //pBuffer:数据存储区
    //ReadAddr:开始读取的地址(24bit)
    //NumByteToRead:要读取的字节数(最大65535)
    void SpiFlashRead(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead)   
    { 
        u16 i;    												    
        SPI_FLASH_CS=0;                            //使能器件   
        Spi2ReadWriteByte(W25X_ReadData);         //发送读取命令   
        Spi2ReadWriteByte((u8)((ReadAddr)>>16));  //发送24bit地址    
        Spi2ReadWriteByte((u8)((ReadAddr)>>8));   
        Spi2ReadWriteByte((u8)ReadAddr);   
        for(i=0;i<NumByteToRead;i++)
        { 
            pBuffer[i]=Spi2ReadWriteByte(0XFF);   //循环读数  
        }
        SPI_FLASH_CS=1;                            //取消片选     	      
    }  
    
    //SPI在一页(0~65535)内写入少于256个字节的数据
    //在指定地址开始写入最大256字节的数据
    //pBuffer:数据存储区
    //WriteAddr:开始写入的地址(24bit)
    //NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!!	 
    void SPIFlashWritePage(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
    {
        u16 i;  
        SpiFlashWriteEnable();                  //SET WEL 
        SPI_FLASH_CS=0;                            //使能器件   
        Spi2ReadWriteByte(W25X_PageProgram);      //发送写页命令   
        Spi2ReadWriteByte((u8)((WriteAddr)>>16)); //发送24bit地址    
        Spi2ReadWriteByte((u8)((WriteAddr)>>8));   
        Spi2ReadWriteByte((u8)WriteAddr);   
        for(i=0;i<NumByteToWrite;i++)Spi2ReadWriteByte(pBuffer[i]);//循环写数  
        SPI_FLASH_CS=1;                            //取消片选 
        SpiFlashWaitBusy();					   //等待写入结束
    } 
    
    //无检验写SPI FLASH 
    //必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!
    //具有自动换页功能 
    //在指定地址开始写入指定长度的数据,但是要确保地址不越界!
    //pBuffer:数据存储区
    //WriteAddr:开始写入的地址(24bit)
    //NumByteToWrite:要写入的字节数(最大65535)
    //CHECK OK
    void SpiFlashWriteNoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)   
    { 			 		 
        u16 pageremain;	   
        pageremain=256-WriteAddr%256; //单页剩余的字节数		 	    
        if(NumByteToWrite<=pageremain)pageremain=NumByteToWrite;//不大于256个字节
        while(1)
        {	   
            SPIFlashWritePage(pBuffer,WriteAddr,pageremain);
            if(NumByteToWrite==pageremain)break;//写入结束了
            else //NumByteToWrite>pageremain
            {
                pBuffer+=pageremain;
                WriteAddr+=pageremain;	
                
                NumByteToWrite-=pageremain;			  //减去已经写入了的字节数
                if(NumByteToWrite>256)pageremain=256; //一次可以写入256个字节
                else pageremain=NumByteToWrite; 	  //不够256个字节了
            }
        };	    
    } 
    
    //写SPI FLASH  
    //在指定地址开始写入指定长度的数据
    //该函数带擦除操作!
    //pBuffer:数据存储区
    //WriteAddr:开始写入的地址(24bit)
    //NumByteToWrite:要写入的字节数(最大65535)  		   
    u8 SPI_FLASH_BUF[4096];
    void SpiFlashWrite(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)   
    { 
        u32 secpos;
        u16 secoff;
        u16 secremain;	   
        u16 i;    
        
        secpos=WriteAddr/4096;//扇区地址 0~511 for w25x16
        secoff=WriteAddr%4096;//在扇区内的偏移
        secremain=4096-secoff;//扇区剩余空间大小   
        
        if(NumByteToWrite<=secremain)secremain=NumByteToWrite;//不大于4096个字节
        while(1) 
        {	
            SpiFlashRead(SPI_FLASH_BUF,secpos*4096,4096);//读出整个扇区的内容
            for(i=0;i<secremain;i++)//校验数据
            {
                if(SPI_FLASH_BUF[secoff+i]!=0XFF)break;//需要擦除  	  
            }
            if(i<secremain)//需要擦除
            {
                SpiFlashEraseSector(secpos);//擦除这个扇区
                for(i=0;i<secremain;i++)	   //复制
                {
                    SPI_FLASH_BUF[i+secoff]=pBuffer[i];	  
                }
                SpiFlashWriteNoCheck(SPI_FLASH_BUF,secpos*4096,4096);//写入整个扇区  
                
            }else SpiFlashWriteNoCheck(pBuffer,WriteAddr,secremain);//写已经擦除了的,直接写入扇区剩余区间. 				   
            if(NumByteToWrite==secremain)break;//写入结束了
            else//写入未结束
            {
                secpos++;//扇区地址增1
                secoff=0;//偏移位置为0 	 
                
                pBuffer+=secremain;  //指针偏移
                WriteAddr+=secremain;//写地址偏移	   
                NumByteToWrite-=secremain;				//字节数递减
                if(NumByteToWrite>4096)secremain=4096;	//下一个扇区还是写不完
                else secremain=NumByteToWrite;			//下一个扇区可以写完了
            }	 
        };	 	 
    }
    
    //擦除整个芯片
    //整片擦除时间:
    //W25X16:25s 
    //W25X32:40s 
    //W25X64:40s 
    //等待时间超长...
    void SpiFlashEraseChip(void)   
    {                                             
        SpiFlashWriteEnable();                  //SET WEL 
        SpiFlashWaitBusy();   
        SPI_FLASH_CS=0;                            //使能器件   
        Spi2ReadWriteByte(W25X_ChipErase);        //发送片擦除命令  
        SPI_FLASH_CS=1;                            //取消片选     	      
        SpiFlashWaitBusy();   				   //等待芯片擦除结束
    }   
    
    //擦除一个扇区
    //Dst_Addr:扇区地址 0~511 for w25x16
    //擦除一个山区的最少时间:150ms
    void SpiFlashEraseSector(u32 Dst_Addr)   
    {   
        Dst_Addr*=4096;
        SpiFlashWriteEnable();                  //SET WEL 	 
        SpiFlashWaitBusy();   
        SPI_FLASH_CS=0;                            //使能器件   
        Spi2ReadWriteByte(W25X_SectorErase);      //发送扇区擦除指令 
        Spi2ReadWriteByte((u8)((Dst_Addr)>>16));  //发送24bit地址    
        Spi2ReadWriteByte((u8)((Dst_Addr)>>8));   
        Spi2ReadWriteByte((u8)Dst_Addr);  
        SPI_FLASH_CS=1;                            //取消片选     	      
        SpiFlashWaitBusy();   				   //等待擦除完成
    }  
    
    //等待空闲
    void SpiFlashWaitBusy(void)   
    {   
        while ((SpiFlashReadSR()&0x01)==0x01);   // 等待BUSY位清空
    }  
    
    //进入掉电模式
    void SpiFlashPowerDown(void)   
    { 
        SPI_FLASH_CS=0;                            //使能器件   
        Spi2ReadWriteByte(W25X_PowerDown);        //发送掉电命令  
        SPI_FLASH_CS=1;                            //取消片选     	      
        DelayMs(3);                               //等待TPD  
    }   
    
    //唤醒
    void SpiFlashWakeUp(void)   
    {  
        SPI_FLASH_CS=0;                            //使能器件   
        Spi2ReadWriteByte(W25X_ReleasePowerDown);   //  send W25X_PowerDown command 0xAB    
        SPI_FLASH_CS=1;                            //取消片选     	      
        DelayMs(3);                               //等待TRES1
    }   
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    


    SPI偽字节在一般意义上就是指发送0XFF,是为了给设备提供处理时钟.


  • 相关阅读:
    通用权限管理设计 之 数据库结构设计
    jQuery LigerUI 插件介绍及使用之ligerDateEditor
    jQuery LigerUI 插件介绍及使用之ligerTree
    jQuery LigerUI V1.01(包括API和全部源码) 发布
    jQuery liger ui ligerGrid 打造通用的分页排序查询表格(提供下载)
    jQuery LigerUI V1.1.5 (包括API和全部源码) 发布
    jQuery LigerUI 使用教程表格篇(1)
    jQuery LigerUI V1.0(包括API和全部源码) 发布
    jQuery LigerUI V1.1.0 (包括API和全部源码) 发布
    nginx keepalived
  • 原文地址:https://www.cnblogs.com/dengxiaojun/p/4279445.html
Copyright © 2011-2022 走看看