精华内容
下载资源
问答
  • 用于驱动W25Q64,模拟SPI,更改移植方便,标准库
  • W25Q64中文手册.pdf

    2020-04-27 19:14:00
    W25Q64中文手册 非英文版,资料全面 W25X16\W25X32\W25X64 中文资料
  • 该程序为用IO口模拟SPI读取W25Q64的程序, 在正点原子战舰开发板,野火霸道开发板上运行通过,可以读写W25Q64 里面的数据,只需要修改.h里的管脚定义,就可以运行率,程序简单明了,备注非常详细
  • W25Q64中文文档

    2018-09-03 16:26:43
    W25Q64系列FLASH存储器可以为用户提供存储解决方案,具有PCB板占用空间少,引脚数量少,功耗低等特点。与普通串行flash相比,使用更灵活,性能更出色。
  • W25Q64FVSSIG.pdf

    2019-10-10 10:25:08
    一个W25Q64FV的英文手册
  • 华邦flash数据手册W25Q64FV
  • 利用STM32F103读写W25Q64

    2019-03-30 20:20:45
    利用STM32F103读写W25Q64,是野火的例程,不错的,读写FLASH,利用uart打印
  • W25Q64芯片数据手册PDF

    2018-10-18 16:22:59
    正版w25Q64官方数据手册,PDF文档,有目录,敬请采纳。
  • 这是STM32F103读写华邦W25Q64 FLASH存储器的完整驱动代码,资料包里包含了SPI硬件时序和SPI模拟时序两个工程。实现对W25Q64读写。
  • w25q64jv详细介绍

    2017-09-06 16:16:00
    w25q64jv spi 详细介绍,W25Q64支持标准的SPI,双输出SPI和四输出SPI操作。标准的SPI指令利用单向的数据输入引脚在串行时钟输入上升沿串行地向器件写入指令、地址或数据。
  • STM8S模拟SPI读写 W25Q64程序,IAR开发环境,程序大部份都有注释,STM8S105测试OK.
  • C51 w25q64驱动及字库烧录程序,包含点阵字库烧录及字定位,使用uincode,keil编译采用uft-8编码。
  • W25Q64Flash芯片STM32操作

    2018-08-01 11:32:37
    该资源包含STM32F407的SPI寄存器配置及对W25Q64进行读写及擦除所需要的函数。
  • W25Q64_SPI.rar

    2019-07-24 14:33:44
    本程序使用普通IO口模拟SPI,并读写W25Q64存储芯片,编译器为Keil,在STM32F103RCT6上测试没问题。
  • W25Q64官方数据手册

    2016-10-16 17:59:52
    W25Q64官方数据手册
  • stm32f2/stm32f4结合w25q64制作的U盘源码; f2/f4通用代码,可直接用; 注意点: 1、usb的晶振必须配置为48MHz,否则会提示!; 2、必须有系统文件且要初始化引用f_mount,否则每次都会出现上电提示格式化; 3、文件...
  • 主要是spi flash器件,例如w25q32,w25q64,w25q128等。实现了基本的文件管理功能,例如创建,写入,追加,重新读取等,对于删除文件采用标记清除的回收方式,删除文件时仅进行进行标记,在后续的文件写入过程中若...
  • 此资源实现w25q64芯片的镜像提取和导入。 编译执行:gcc spi-w25q64.c -o spi-w25q64 应用使用方法: 1. 读取数据:sudo ./spi-w25q64 -r w25q64.img -n 8388608 2.芯片编程:sudo ./spi-w25q64 -w w25q64.img -n ...
  • W25Q64_128驱动代码.zip

    2019-07-11 11:05:23
    测试通过 spi_flash 常用存储器 STM32 NORDIC
  • w25q64中文数据手册

    热门讨论 2016-01-09 16:03:39
    W25Q80(8M-bit),W25Q16(16M-bit)和W25Q32(32M-bit)是为系统提供一个最小的空间、引脚和功耗的存储器解决方案的串行Flash存储器。25Q系列比普通的串行Flash存储器更灵活,性能更优越。基于双倍/四倍的SPI,它们能够...
  • W25Q64中文手册、W25Q128中文手册 W25Q64 (64M-bit),W25Q16(16M-bit)和 W25Q32(32M-bit)是为系统提供一个最小的空间、引脚 和功耗的存储器解决方案的串行 Flash 存储器。25Q 系列比普通的串行 Flash 存储器更灵活...
  • W25Q64中英文数据手册
  • W25Q64_datasheet

    2014-09-30 09:44:54
    spi flash W25Q64FV官方的datasheet
  • W25Q64中文数据手册.pdf

    2021-02-25 14:46:53
    W25Q64中文数据手册
  • W25Q64数据手册,说明文档,datasheet,超详细,下了就能用,2个积分不亏,交个朋友_^0^_
  • W25Q64.pdf 英文版

    2019-08-18 16:43:17
    64m flash芯片,数据手册,,英文版,页数 61
  • W25Q64_STM32.zip

    2020-04-13 22:23:01
    /** ****************************************************************************** * @file main.c * @author fire * @version V1.0 * @date 2013-xx-xx ... * @brief 华邦 8M串行flash测试,并将测试...
  • W25Q64FV_已解密

    2014-03-14 16:22:54
    W25Q64FV_已解密 这个文档是可以编辑的,可以添加备注等 是经过解密了的,大家赶快下载吧
  • SPI总线:STM32本身支持SPI硬件时序,本文示例代码里同时采用模拟时序和硬件时序两种方式读写W25Q64。 模拟时序更加方便移植到其他单片机,更加方便学习理解SPI时序,通用性更高,不分MCU; 硬件时序效率更高,每个...

    一、环境介绍

    编程软件: keil5

    操作系统: win10

    MCU型号: STM32F103ZET6

    STM32编程方式: 寄存器开发 (方便程序移植到其他单片机)

    SPI总线:  STM32本身支持SPI硬件时序,本文示例代码里同时采用模拟时序和硬件时序两种方式读写W25Q64。

    模拟时序更加方便移植到其他单片机,更加方便学习理解SPI时序,通用性更高,不分MCU;

    硬件时序效率更高,每个MCU配置方法不同,依赖MCU硬件本身支持。

    存储器件: 采用华邦W25Q64  flash存储芯片。 

    W25Q64这类似的Flash存储芯片在单片机里、嵌入式系统里还是比较常见,可以用来存储图片数据、字库数据、音频数据、保存设备运行日志文件等。

    完整工程代码下载:https://download.csdn.net/download/xiaolong1126626497/19425042

    二、华邦W25Q64介绍(FLASH存储类型)

    2.1 W25Q64芯片功能介绍

    W25Q64是为系统提供一个最小空间、最少引脚,最低功耗的串行Flash存储器,25Q系列比普通的串行Flash存储器更灵活,性能更优越。

    W25Q64支持双倍/四倍的SPI,可以储存包括声音、文本、图片和其他数据;芯片支持的工作电压 2.7V 到 3.6V,正常工作时电流小于5mA,掉电时低于1uA,所有芯片提供标准的封装。 

    W25Q64的内存空间结构:  一页256字节,4K(4096 字节)为一个扇区,16个扇区为1块,容量为8M字节,共有128个块,2048 个扇区。  

    W25Q64每页大小由256字节组成,每页的256字节用一次页编程指令即可完成。

    擦除指令分别支持: 16页(1个扇区)、128页、256页、全片擦除。 

    W25Q64支持标准串行外围接口(SPI),和高速的双倍/四倍输出,双倍/四倍用的引脚:串行时钟、片选端、串行数据 I/O0(DI)、I/O1(DO)、I/O2(WP)和 I/O3(HOLD)。
    SPI 最高支持 80MHz,当用快读双倍/四倍指令时,相当于双倍输出时最高速率160MHz,四倍输出时最高速率 320MHz。这个传输速率比得上8位和16位的并行Flash存储器。
     
    W25Q64支持 JEDEC 标准,具有唯一的 64 位识别序列号,方便区别芯片型号。

     2.2 W25Q64芯片特性详细介绍

    ●SPI串行存储器系列    
    -W25Q64:64M 位/8M 字节    
    -W25Q16:16M 位/2M 字节    
    -W25Q32:32M 位/4M 字节    
    -每 256 字节可编程页    

        
    ●灵活的4KB扇区结构     
    -统一的扇区擦除(4K 字节)     
    -块擦除(32K 和 64K 字节) 
    -一次编程 256 字节 
    -至少 100,000 写/擦除周期 
    -数据保存 20 年 
        
    ●标准、双倍和四倍SPI 
    -标准 SPI:CLK、CS、DI、DO、WP、HOLD         
    -双倍 SPI:CLK、CS、IO0、IO1、WP、HOLD         
    -四倍 SPI:CLK、CS、IO0、IO1、IO2、IO3 

    ●高级的安全特点 
    -软件和硬件写保护 
    -选择扇区和块保护 
    -一次性编程保护(1) 
    -每个设备具有唯一的64位ID(1) 

    ●高性能串行Flash存储器     
    -比普通串行Flash性能高6倍          
    -80MHz时钟频率          
    -双倍SPI相当于160MHz         
    -四倍SPI相当于320MHz         
    -40MB/S连续传输数据     
    -30MB/S随机存取(每32字节)     
    -比得上16位并行存储器
             
    ●低功耗、宽温度范围 
    -单电源 2.7V-3.6V 
    -工作电流 4mA,掉电<1μA(典型值)
    -40℃~+85℃工作 

    2.3  引脚介绍

    下面只介绍W25Q64标准SPI接口,因为目前开发板上的封装使用的就是标准SPI接口。

     

    引脚编号

    引脚名称

    I/O

    功能

    1

    /CS

    I

    片选端输入

    2

    DO(IO1)

    I/O

    数据输出(数据输入输出 1)*1 

    3

    /WP(IO2)

    I/O

    写保护输入(数据输入输出 2)*2 

    4

    GND

     

    5

    DI(IO0)

    I/O

    数据输入(数据输入输出 0)*1 

    6

    CLK

    I

    串行时钟输入

    7

    /HOLD(IO3)

    I/O

    保持端输入(数据输入输出 3)*2 

    8

    VCC

     

    电源

      2.2.1 SPI片选(/CS)引脚用于使能和禁止芯片操作

    CS引脚是W25Q64的片选引脚,用于选中芯片;当CS为高电平时,芯片未被选择,串行数据输出(DO、IO0、IO1、IO2 和 IO3)引脚为高阻态。未被选择时,芯片处于待机状态下的低功耗,除非芯片内部在擦除、编程。当/CS 变成低电平,芯片功耗将增长到正常工作,能够从芯片读写数据。上电后, 在接收新的指令前,/CS 必须由高变为低电平。上电后,/CS 必须上升到 VCC,在/CS 接上拉电阻可以完成这个操作。 

    2.2.2 串行数据输入、输出和 IOs(DI、DO 和 IO0、IO1、IO2、IO3)

    W25Q64、W25Q16 和 W25Q32 支持标准 SPI、双倍 SPI 和四倍 SPI。

    标准的 SPI 传输用单向的 DI(输入)引脚连续的写命令、地址或者数据在串行时钟(CLK)的上升沿时写入到芯片内。

    标准的SPI 用单向的 DO(输出)在 CLK 的下降沿从芯片内读出数据或状态。 

    2.2.3 写保护(/WP)

    写保护引脚(/WP)用来保护状态寄存器。和状态寄存器的块保护位(SEC、TB、BP2、BP1 和BP0)和状态寄存器保护位(SRP)对存储器进行一部分或者全部的硬件保护。/WP 引脚低电平有效。当状态寄存器 2 的 QE 位被置位了,/WP 引脚(硬件写保护)的功能不可用。

    2.2.4  保持端(/HOLD)

    当/HOLD 引脚是有效时,允许芯片暂停工作。在/CS 为低电平时,当/HOLD 变为低电平,DO 引脚将变为高阻态,在 DI 和 CLK 引脚上的信号将无效。当/HOLD 变为高电平,芯片恢复工作。/HOLD 功能用在当有多个设备共享同一 SPI 总线时。/HOLD 引脚低电平有效。当状态寄存器 2 的 QE 位被置位了,/ HOLD 引脚的功能不可用。

    2.2.5 串行时钟(CLK)

    串行时钟输入引脚为串行输入和输出操作提供时序。(见 SPI 操作)。

    设备数据传输是从高位开始,数据传输的格式为 8bit,数据采样从第二个时间边沿开始,空闲状态时,时钟线 clk 为高电平。 

    2.3 内部结构框架图

    2.4 W25Q64的标准SPI操作流程

    W25Q64标准SPI总线接口包含四个信号: 串行时钟(CLK)、片选端(/CS)、串行数据输入(DI)和串行数据输出(DO)

    DI输入引脚在CLK的上升沿连续写命令、地址或数据到芯片内。

    DO输出引脚在CLK的下降沿从芯片内读出数据或状态。

    W25Q64分别支持SPI总线工作模式0和工作模式3。模式0和模式3的主要区别在于常态时的CLK信号不同;对于模式0来说,当SPI主机已准备好数据还没传输到串行Flash中时,CLK信号常态为低;

    设备数据传输是从高位开始,数据传输的格式为8bit,数据采样从第二个时间边沿开始,空闲状态时,时钟线clk为高电平

     

    2.5 部分控制和状态寄存器介绍

    2.5.1 W25Q64的指令表

     

    指令名称

    字节 1

    (代码)

     

    字节 2

     

    字节 3

     

    字节 4

     

    字节 5

     

    字节 6

    写使能

    06h

    write_enabled 

    禁止写

    04h

     

    读状态寄存器 1

    05h

    (S7-S0)(2) 

     

    读状态寄存器 2

    35h

    (S15-S8)(2) 

     

    写状态寄存器

    01h

    (S7-S0)

    (S15-S8)

     

    页编程

    02h

    A23-A16

    A15-A8

    A7-A0

    (D7-D0)

     

    四倍页编程

    32h

    A23-A16

    A15-A8

    A7-A0

    (D7-D0,…)(3) 

     

    块擦除(64KB)

    D8h

    A23-A16

    A15-A8

    A7-A0

     

    块擦除(32KB)

    52h

    A23-A16

    A15-A8

    A7-A0

     

    扇区擦除(4KB)

    20h

    A23-A16

    A15-A8

    A7-A0

     

    全片擦除

    C7h/60h

     

    暂停擦除

    75h

     

    恢复擦除

    7Ah

     

    掉电模式

    B9h

     

    高性能模式

    A3h

     

     

     

     

     

    2.5.2 读状态寄存器1

    状态寄存器1的内部结构如下:

    状态寄存器1的S0位是当前W25Q64的忙状态;为1的时候表示设备正在执行程序(可能是在擦除芯片)或写状态寄存器指令,这个时候设备将忽略传来的指令, 除了读状态寄存器和擦除暂停指令外,其他写指令或写状态指令都无效S0 0 状态时指示设备已经执行完毕,可以进行下一步操作。 

    读状态寄存器1的时序如下:

    读取状态寄存器的指令是 8 位的指令。发送指令之前,先将/CS 拉低,再发送指令码“05 h” 或者“35h”。设备收到读取状态寄存器的指令后,将状态信息(高位)依次移位发送出去,读出的状态信息,最低位为 1 代表忙,最低位为 0 代表可以操作,状态信息读取完毕,将片选线拉高。 

    读状态寄存器指令可以使用在任何时候,即使程序在擦除的过程中或者写状态寄存器周期正在进行中。这可以检测忙碌状态来确定周期是否完成,以确定设备是否可以接受另一个指令。
     

    2.5.3 读制造商ID和芯片ID

     时序图如下:

    读取制造商/设备 ID 指令可以读取制造商 ID 和特定的设备 ID。读取之前,拉低 CS 片选信号,接着发送指令代码“90h” ,紧随其后的是一个 24 位地址(A23-A0)000000h。 设备收到指令之后,会发出华邦电子制造商 ID(EFh) 和设备ID(w25q64 为 16h)。如果 24 位地址设置为 000001h ,设备 ID 会先发出,然后跟着制造商 ID。制造商和设备ID可以连续读取。完成指令后,片选信号/ CS 拉高。

    2.5.4 全片擦除(C7h/60h)

    全芯片擦除指令,可以将整个芯片的所有内存数据擦除,恢复到 0XFF 状态。写入全芯片擦除指令之前必须执行设备写使能(发送设备写使能指令 0x06),并判断状态寄存器(状态寄存器位最低位必须等于 0 才能操作)。发送全芯片擦除指令前,先拉低/ CS,接着发送擦除指令码”C7h”或者是”60h”, 指令码发送完毕后,拉高片选线 CS/,,并判断状态位,等待擦除结束。全片擦除指令尽量少用,擦除会缩短设备的寿命。

    2.5.5 读数据(03h)

    读取数据指令允许按顺序读取一个字节的内存数据。当片选 CS/拉低之后,紧随其后是一个 24 位的地址(A23-A0)(需要发送 3 次,每次 8 个字节,先发高位)。芯片收到地址后,将要读的数据按字节大小转移出去,数据是先转移高位,对于单片机,时钟下降沿发送数据,上升沿接收数据。读数据时,地址会自动增加,允许连续的读取数据。这意味着读取整个内存的数据,只要用一个指令就可以读完。数据读取完成之后,片选信号/ CS 拉高。

    读取数据的指令序列,如上图所示。如果一个读数据指令而发出的时候,设备正在擦除扇区,或者(忙= 1),该读指令将被忽略,也不会对当前周期有什么影响。

    三、SPI时序介绍

    SPI是串行外设接口(Serial Peripheral Interface)的缩写,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间。

    SPI是一种高速、高效率的串行接口技术,一共有4根线。通常由一个主模块和一个或多个从模块组成,主模块选择一个从模块进行同步通信,从而完成数据的交换。SPI是一个环形结构,通信时需要至少4根线(在单向传输时3根线也可以)。分别是MISO(主设备数据输入)、MOSI(主设备数据输出)、SCLK(时钟)、CS(片选)。
    (1)MISO– Master Input Slave Output,主设备数据输入,从设备数据输出;
    (2)MOSI– Master Output Slave Input,主设备数据输出,从设备数据输入;
    (3)SCLK – Serial Clock,时钟信号,由主设备产生;
    (4)CS – Chip Select,从设备使能信号,由主设备控制。

    其中,CS是从芯片是否被主芯片选中的控制信号,也就是说只有片选信号为预先规定的使能信号时(高电位或低电位),主芯片对此从芯片的操作才有效。这就使在同一条总线上连接多个SPI设备成为可能。接下来就负责通讯的3根线了。通讯是通过数据交换完成的,这里先要知道SPI是串行通讯协议,也就是说数据是一位一位的传输的。这就是SCLK时钟线存在的原因,由SCLK提供时钟脉冲,SDI,SDO则基于此脉冲完成数据传输。数据输出通过 SDO线,数据在时钟上升沿或下降沿时改变,在紧接着的下降沿或上升沿被读取。完成一位数据传输,输入也使用同样原理。因此,至少需要8次时钟信号的改变(上沿和下沿为一次),才能完成8位数据的传输。
    时钟信号线SCLK只能由主设备控制,从设备不能控制。这样的传输方式有一个优点,在数据位的传输过程中可以暂停,也就是时钟的周期可以为不等宽,因为时钟线由主设备控制,当没有时钟跳变时,从设备不采集或传送数据。SPI还是一个数据交换协议:因为SPI的数据输入和输出线独立,所以允许同时完成数据的输入和输出。芯片集成的SPI串行同步时钟极性和相位可以通过寄存器配置,IO模拟的SPI串行同步时钟需要根据从设备支持的时钟极性和相位来通讯。SPI通信原理比I2C要简单,IIC有应答机制,可以确保数据都全部发送成。SPI接口没有指定的流控制,没有应答机制确认是否接收到数据,速度上更加快。

    SPI总线通过时钟极性和相位可以配置成4种时序:

    STM32F103参考手册,SPI章节介绍的时序图:

    SPI时序比较简单,CPU如果没有硬件支持,可以直接写代码采用IO口模拟,下面是模拟时序的示例的代码:

    SPI的模式1:
    u8 SPI_ReadWriteOneByte(u8 tx_data)
    {
    	u8 i,rx_data=0;
    	SCK=0; //空闲电平(默认初始化情况)
    	for(i=0;i<8;i++)
    	{
    		/*1. 主机发送一位数据*/
    		SCK=0;//告诉从机,主机将要发送数据
    		if(tx_data&0x80)MOSI=1; //发送数据
    		else MOSI=0;
    		SCK=1; //告诉从机,主机数据发送完毕
    		tx_data<<=1; //继续发送下一位
    		
    		/*2. 主机接收一位数据*/
    		rx_data<<=1; //默认认为接收到0
    		if(MISO)rx_data|=0x01;
    	}
    	SCK=0; //恢复空闲电平
    	return rx_data;
    }
    
    SPI的模式2:
    u8 SPI_ReadWriteOneByte(u8 tx_data)
    {
    	u8 i,rx_data=0;
    	SCK=0; //空闲电平(默认初始化情况)
    	for(i=0;i<8;i++)
    	{
    		/*1. 主机发送一位数据*/
    		SCK=1;//告诉从机,主机将要发送数据
    		if(tx_data&0x80)MOSI=1; //发送数据
    		else MOSI=0;
    		SCK=0; //告诉从机,主机数据发送完毕
    		tx_data<<=1; //继续发送下一位
    		
    		/*2. 主机接收一位数据*/
    		rx_data<<=1; //默认认为接收到0
    		if(MISO)rx_data|=0x01;
    	}
    	SCK=0; //恢复空闲电平
    	return rx_data;
    }
    
    
    SPI的模式3:
    u8 SPI_ReadWriteOneByte(u8 tx_data)
    {
    	u8 i,rx_data=0;
    	SCK=1; //空闲电平(默认初始化情况)
    	for(i=0;i<8;i++)
    	{
    		/*1. 主机发送一位数据*/
    		SCK=1;//告诉从机,主机将要发送数据
    		if(tx_data&0x80)MOSI=1; //发送数据
    		else MOSI=0;
    		SCK=0; //告诉从机,主机数据发送完毕
    		tx_data<<=1; //继续发送下一位
    		
    		/*2. 主机接收一位数据*/
    		rx_data<<=1; //默认认为接收到0
    		if(MISO)rx_data|=0x01;
    	}
    	SCK=1; //恢复空闲电平
    	return rx_data;
    }
    
    SPI的模式4:
    u8 SPI_ReadWriteOneByte(u8 tx_data)
    {
    	u8 i,rx_data=0;
    	SCK=1; //空闲电平(默认初始化情况)
    	for(i=0;i<8;i++)
    	{
    		/*1. 主机发送一位数据*/
    		SCK=0;//告诉从机,主机将要发送数据
    		if(tx_data&0x80)MOSI=1; //发送数据
    		else MOSI=0;
    		SCK=1; //告诉从机,主机数据发送完毕
    		tx_data<<=1; //继续发送下一位
    		
    		/*2. 主机接收一位数据*/
    		rx_data<<=1; //默认认为接收到0
    		if(MISO)rx_data|=0x01;
    	}
    	SCK=1; //恢复空闲电平
    	return rx_data;
    }

    四、W25Q64的示例代码

    4.1 STM32采用硬件SPI读写W25Q64示例代码

    /*
    函数功能:SPI初始化(模拟SPI)
    硬件连接:
    MISO--->PB14
    MOSI--->PB15
    SCLK--->PB13
    */
    void SPI_Init(void)
    {
    	/*开启时钟*/
    	RCC->APB1ENR|=1<<14;   //开启SPI2时钟
    	RCC->APB2ENR|=1<<3;    //PB
    	GPIOB->CRH&=0X000FFFFF; //清除寄存器
    	GPIOB->CRH|=0XB8B00000;
    	GPIOB->ODR|=0X7<<13;    	//PB13/14/15上拉--输出高电平
    	/*SPI2基本配置*/
    	SPI2->CR1=0X0; 		//清空寄存器
    	SPI2->CR1|=0<<15; //选择“双线双向”模式
    	SPI2->CR1|=0<<11; //使用8位数据帧格式进行发送/接收;
    	SPI2->CR1|=0<<10; //全双工(发送和接收);
    	SPI2->CR1|=1<<9;  //启用软件从设备管理
    	SPI2->CR1|=1<<8;  //NSS
    	SPI2->CR1|=0<<7;  //帧格式,先发送高位
    	SPI2->CR1|=0x0<<3;//当总线频率为36MHZ时,SPI速度为18MHZ,高速。
    	SPI2->CR1|=1<<2;  //配置为主设备
    	SPI2->CR1|=1<<1;  //空闲状态时, SCK保持高电平。
    	SPI2->CR1|=1<<0;  //数据采样从第二个时钟边沿开始。
    	SPI2->CR1|=1<<6;  //开启SPI设备。
    }
    
    
    /*
    函数功能:SPI读写一个字节
    */
    u8 SPI_ReadWriteOneByte(u8 data_tx)
    {
        u16 cnt=0;				 
        while((SPI2->SR&1<<1)==0)		 //等待发送区空--等待发送缓冲为空	
        {
          cnt++;
          if(cnt>=65530)return 0; 	  //超时退出  u16=2个字节
        }	
        SPI2->DR=data_tx;	 	  		      //发送一个byte 
        cnt=0;
        while((SPI2->SR&1<<0)==0) 		//等待接收完一个byte   
        {
          cnt++;
          if(cnt>=65530)return 0;	   //超时退出
        }	  						    
        return SPI2->DR;          		//返回收到的数据	
    }
    
    
    /*
    函数功能:W25Q64初始化
    硬件连接:
    MOSI--->PB15
    MISO--->PB14
    SCLK--->PB13
    CS----->PB12
    */
    void W25Q64_Init(void)
    {
    	/*1. 开时钟*/
    	RCC->APB2ENR|=1<<3; //PB
    	
    	/*2. 配置GPIO口模式*/
    	GPIOB->CRH&=0xFFF0FFFF;
    	GPIOB->CRH|=0x00030000;
    	
    	W25Q64_CS=1; //未选中芯片
    	SPI_Init();   //SPI初始化
    }
    
    
    /*
    函数功能:读取芯片的ID号
    */
    u16 W25Q64_ReadID(void)
    {
    	u16 id;
    	/*1. 拉低片选*/
    	W25Q64_CS=0;
    	
    	/*2. 发送读取ID的指令*/
    	SPI_ReadWriteOneByte(0x90);
    	
    	/*3. 发送24位的地址-0*/
    	SPI_ReadWriteOneByte(0);
    	SPI_ReadWriteOneByte(0);
    	SPI_ReadWriteOneByte(0);
    	
    	/*4. 读取芯片的ID*/
    	id=SPI_ReadWriteOneByte(0xFF)<<8;
    	id|=SPI_ReadWriteOneByte(0xFF);
    
    	/*5. 拉高片选*/
    	W25Q64_CS=1;
    	return id;
    }
    
    /*
    函数功能:检测W25Q64状态
    */
    void W25Q64_CheckStat(void)
    {
    	u8 stat=1;
    	while(stat&1<<0)
    	{
    		W25Q64_CS=0; //选中芯片
    		SPI_ReadWriteOneByte(0x05);      //发送读状态寄存器1指令
    		stat=SPI_ReadWriteOneByte(0xFF); //读取状态
    		W25Q64_CS=1; //取消选中芯片
    	}
    }
    
    
    /*
    函数功能:页编程
    说    明:一页最多写256个字节。 写数据之前,必须保证空间是0xFF
    函数参数:
    u32 addr:页编程起始地址
    u8 *buff:写入的数据缓冲区
    u16 len :写入的字节长度
    */
    void W25Q64_PageWrite(u32 addr,u8 *buff,u16 len)
    {
    	u16 i;
    	W25Q64_Enabled();  						//写使能
    	W25Q64_CS=0; //选中芯片
    	SPI_ReadWriteOneByte(0x02); //页编程指令
    	SPI_ReadWriteOneByte(addr>>16); //24~16地址
    	SPI_ReadWriteOneByte(addr>>8);  //16~8地址
    	SPI_ReadWriteOneByte(addr);     //8~0地址
    
    	for(i=0;i<len;i++)
    	{
    		SPI_ReadWriteOneByte(buff[i]);     //8~0地址	
    	}
    	W25Q64_CS=1; //取消选中芯片
    	W25Q64_CheckStat();  //检测芯片忙状态
    }
    
    
    /*
    函数功能:连续读数据
    函数参数:
    u32 addr:读取数据的起始地址
    u8 *buff:读取数据存放的缓冲区
    u32 len :读取字节的长度
    */
    void W25Q64_ReadByteData(u32 addr,u8 *buff,u32 len)
    {
    	u32 i;
    	W25Q64_CS=0; //选中芯片
    	SPI_ReadWriteOneByte(0x03);     //读数据指令
    	SPI_ReadWriteOneByte(addr>>16); //24~16地址
    	SPI_ReadWriteOneByte(addr>>8);  //16~8地址
    	SPI_ReadWriteOneByte(addr);     //8~0地址
    	for(i=0;i<len;i++)buff[i]=SPI_ReadWriteOneByte(0xFF);
    	W25Q64_CS=1; //取消选中芯片
    }
    
    
    /*
    函数功能:擦除一个扇区
    函数参数:
    u32 addr:擦除扇区的地址范围
    */
    void W25Q64_ClearSector(u32 addr)
    {
    	W25Q64_Enabled();  						//写使能
    	W25Q64_CS=0; //选中芯片
    	SPI_ReadWriteOneByte(0x20);     //扇区擦除指令
    	SPI_ReadWriteOneByte(addr>>16); //24~16地址
    	SPI_ReadWriteOneByte(addr>>8);  //16~8地址
    	SPI_ReadWriteOneByte(addr);     //8~0地址
    	W25Q64_CS=1; 				//取消选中芯片
    	W25Q64_CheckStat();  //检测芯片忙状态
    }
    
    /*
    函数功能:写使能
    */
    void W25Q64_Enabled(void)
    {
    	W25Q64_CS=0; //选中芯片
    	SPI_ReadWriteOneByte(0x06);     //写使能
    	W25Q64_CS=1; //取消选中芯片
    }
    
    
    /*
    函数功能:指定位置写入指定个数的数据,不考虑擦除问题
    注意事项:W25Q64只能将1写为,不能将0写为1。
    函数参数:
    u32 addr---写入数据的起始地址
    u8 *buff---写入的数据
    u32 len---长度
    */
    void W25Q64_WriteByteDataNoCheck(u32 addr,u8 *buff,u32 len)
    {
    	u32 page_remain=256-addr%256; //计算当前页还可以写下多少数据
    	if(len<=page_remain) //如果当前写入的字节长度小于剩余的长度
    	{
    		page_remain=len;
    	}
    	while(1)
    	{
    		W25Q64_PageWrite(addr,buff,page_remain);
    		if(page_remain==len)break; //表明数据已经写入完毕
    		buff+=page_remain; //buff向后偏移地址
    		addr+=page_remain; //起始地址向后偏移
    		len-=page_remain;  //减去已经写入的字节数
    		if(len>256)page_remain=256;  //如果大于一页,每次就直接写256字节
    		else page_remain=len;
    	}
    }
    
    
    /*
    函数功能:指定位置写入指定个数的数据,考虑擦除问题,完善代码
    函数参数:
    u32 addr---写入数据的起始地址
    u8 *buff---写入的数据
    u32 len---长度
    说明:擦除的最小单位扇区,4096字节
    */
    static u8 W25Q64_READ_WRITE_CHECK_BUFF[4096];
    void W25Q64_WriteByteData(u32 addr,u8 *buff,u32 len)
    {
        u32 i;
        u32 len_w;
        u32 sector_addr; //存放扇区的地址
        u32 sector_move; //扇区向后偏移的地址
        u32 sector_size; //扇区大小。(剩余的空间大小)
        u8 *p=W25Q64_READ_WRITE_CHECK_BUFF;//存放指针
        sector_addr=addr/4096; //传入的地址是处于第几个扇区
        sector_move=addr%4096; //计算传入的地址存于当前的扇区的偏移量位置
        sector_size=4096-sector_move; //得到当前扇区剩余的空间
    
        if(len<=sector_size)
        {
                sector_size=len; //判断第一种可能性、一次可以写完
        }
        
        while(1)
        {
            W25Q64_ReadByteData(addr,p,sector_size);	 //读取剩余扇区里的数据
            for(i=0;i<sector_size;i++)
            {
                if(p[i]!=0xFF)break;
            }
            if(i!=sector_size)  //判断是否需要擦除
            {
                 W25Q64_ClearSector(sector_addr*4096);
            }
    //        for(i=0;i<len;i++)
    //        {
    //             W25Q64_READ_WRITE_CHECK_BUFF[i]=buff[len_w++]; 
            }
    //        W25Q64_WriteByteDataNoCheck(addr,W25Q64_READ_WRITE_CHECK_BUFF,sector_size);
            W25Q64_WriteByteDataNoCheck(addr,buff,sector_size);
            if(sector_size==len)break;
    
            addr+=sector_size; //向后偏移地址
            buff+=sector_size ;//向后偏移
            len-=sector_size;  //减去已经写入的数据
            sector_addr++;     //校验第下个扇区
            if(len>4096)       //表明还可以写一个扇区
            {
                    sector_size=4096;//继续写一个扇区
            }
            else
            {
                    sector_size=len; //剩余的空间可以写完
            }
        }
    }
    

    4.2 STM32采用硬件SPI读写W25Q64示例代码

    #include "spi.h"
    
    
    /*
    函数功能:SPI初始化(模拟SPI)
    硬件连接:
    MISO--->PB14
    MOSI--->PB15
    SCLK--->PB13
    */
    void SPI_Init(void)
    {
    	/*1. 开时钟*/
    	RCC->APB2ENR|=1<<3; //PB
    
    	/*2. 配置GPIO口模式*/
    	GPIOB->CRH&=0x000FFFFF;
    	GPIOB->CRH|=0x38300000;
    
    	/*3. 上拉*/
    	SPI_MOSI=1;
    	SPI_MISO=1;
    	SPI_SCLK=1;
    }
    
    /*
    函数功能:SPI读写一个字节
    */
    u8 SPI_ReadWriteOneByte(u8 data_tx)
    {
    	u8 data_rx=0; //存放读取的数据
    	u8 i;
    	for(i=0;i<8;i++)
    	{
    		SPI_SCLK=0; //准备发送数据
    		if(data_tx&0x80)SPI_MOSI=1;
    		else SPI_MOSI=0;
    		data_tx<<=1; //依次发送最高位
    		SPI_SCLK=1;  //表示主机数据发送完成,表示从机发送完毕
    		
    		data_rx<<=1; //表示默认接收的是0
    		if(SPI_MISO)data_rx|=0x01;
    	}
    	return data_rx;
    }
    
    #include "W25Q64.h"
    
    /*
    函数功能:W25Q64初始化
    硬件连接:
    MOSI--->PB15
    MISO--->PB14
    SCLK--->PB13
    CS----->PB12
    */
    void W25Q64_Init(void)
    {
    	/*1. 开时钟*/
    	RCC->APB2ENR|=1<<3; //PB
    	
    	/*2. 配置GPIO口模式*/
    	GPIOB->CRH&=0xFFF0FFFF;
    	GPIOB->CRH|=0x00030000;
    	
    	W25Q64_CS=1; //未选中芯片
    	SPI_Init();   //SPI初始化
    }
    
    
    /*
    函数功能:读取芯片的ID号
    */
    u16 W25Q64_ReadID(void)
    {
    	u16 id;
    	/*1. 拉低片选*/
    	W25Q64_CS=0;
    
    	/*2. 发送读取ID的指令*/
    	SPI_ReadWriteOneByte(0x90);
    
    	/*3. 发送24位的地址-0*/
    	SPI_ReadWriteOneByte(0);
    	SPI_ReadWriteOneByte(0);
    	SPI_ReadWriteOneByte(0);
    
    	/*4. 读取芯片的ID*/
    	id=SPI_ReadWriteOneByte(0xFF)<<8;
    	id|=SPI_ReadWriteOneByte(0xFF);
    
    	/*5. 拉高片选*/
    	W25Q64_CS=1;
    	return id;
    }
    
    /*
    函数功能:检测W25Q64状态
    */
    void W25Q64_CheckStat(void)
    {
    	u8 stat=1;
    	while(stat&1<<0)
    	{
    		W25Q64_CS=0; //选中芯片
    		SPI_ReadWriteOneByte(0x05);      //发送读状态寄存器1指令
    		stat=SPI_ReadWriteOneByte(0xFF); //读取状态
    		W25Q64_CS=1; //取消选中芯片
    	}
    }
    
    
    /*
    函数功能:页编程
    说    明:一页最多写256个字节。 写数据之前,必须保证空间是0xFF
    函数参数:
    u32 addr:页编程起始地址
    u8 *buff:写入的数据缓冲区
    u16 len :写入的字节长度
    */
    void W25Q64_PageWrite(u32 addr,u8 *buff,u16 len)
    {
    	u16 i;
    	W25Q64_Enabled();  						//写使能
    	W25Q64_CS=0; //选中芯片
    	SPI_ReadWriteOneByte(0x02); //页编程指令
    	SPI_ReadWriteOneByte(addr>>16); //24~16地址
    	SPI_ReadWriteOneByte(addr>>8);  //16~8地址
    	SPI_ReadWriteOneByte(addr);     //8~0地址
    
    	for(i=0;i<len;i++)
    	{
    		SPI_ReadWriteOneByte(buff[i]);     //8~0地址	
    	}
    	W25Q64_CS=1; //取消选中芯片
    	W25Q64_CheckStat();  //检测芯片忙状态
    }
    
    
    /*
    函数功能:连续读数据
    函数参数:
    u32 addr:读取数据的起始地址
    u8 *buff:读取数据存放的缓冲区
    u32 len :读取字节的长度
    */
    void W25Q64_ReadByteData(u32 addr,u8 *buff,u32 len)
    {
    	u32 i;
    	W25Q64_CS=0; //选中芯片
    	SPI_ReadWriteOneByte(0x03);     //读数据指令
    	SPI_ReadWriteOneByte(addr>>16); //24~16地址
    	SPI_ReadWriteOneByte(addr>>8);  //16~8地址
    	SPI_ReadWriteOneByte(addr);     //8~0地址
    	for(i=0;i<len;i++)buff[i]=SPI_ReadWriteOneByte(0xFF);
    	W25Q64_CS=1; //取消选中芯片
    }
    
    
    /*
    函数功能:擦除一个扇区
    函数参数:
    				u32 addr:擦除扇区的地址范围
    */
    void W25Q64_ClearSector(u32 addr)
    {
    	W25Q64_Enabled();  						//写使能
    	W25Q64_CS=0; //选中芯片
    	SPI_ReadWriteOneByte(0x20);     //扇区擦除指令
    	SPI_ReadWriteOneByte(addr>>16); //24~16地址
    	SPI_ReadWriteOneByte(addr>>8);  //16~8地址
    	SPI_ReadWriteOneByte(addr);     //8~0地址
    	W25Q64_CS=1; 				//取消选中芯片
    	W25Q64_CheckStat();  //检测芯片忙状态
    }
    
    /*
    函数功能:写使能
    */
    void W25Q64_Enabled(void)
    {
    	W25Q64_CS=0; //选中芯片
    	SPI_ReadWriteOneByte(0x06);     //写使能
    	W25Q64_CS=1; //取消选中芯片
    }
    
    
    /*
    函数功能:指定位置写入指定个数的数据,不考虑擦除问题
    注意事项:W25Q64只能将1写为,不能将0写为1。
    函数参数:
    u32 addr---写入数据的起始地址
    u8 *buff---写入的数据
    u32 len---长度
    */
    void W25Q64_WriteByteDataNoCheck(u32 addr,u8 *buff,u32 len)
    {
    	u32 page_remain=256-addr%256; //计算当前页还可以写下多少数据
    	if(len<=page_remain) //如果当前写入的字节长度小于剩余的长度
    	{
    		page_remain=len;
    	}
    	while(1)
    	{
    		W25Q64_PageWrite(addr,buff,page_remain);
    		if(page_remain==len)break; //表明数据已经写入完毕
    		buff+=page_remain; //buff向后偏移地址
    		addr+=page_remain; //起始地址向后偏移
    		len-=page_remain;  //减去已经写入的字节数
    		if(len>256)page_remain=256;  //如果大于一页,每次就直接写256字节
    		else page_remain=len;
    	}
    }
    
    
    /*
    函数功能:指定位置写入指定个数的数据,考虑擦除问题,完善代码
    函数参数:
    u32 addr---写入数据的起始地址
    u8 *buff---写入的数据
    u32 len---长度
    说明:擦除的最小单位扇区,4096字节
    */
    static u8 W25Q64_READ_WRITE_CHECK_BUFF[4096];
    void W25Q64_WriteByteData(u32 addr,u8 *buff,u32 len)
    {
    	u32 i;
    	u32 sector_addr; //存放扇区的地址
    	u32 sector_move; //扇区向后偏移的地址
    	u32 sector_size; //扇区大小。(剩余的空间大小)
    	u8 *p=W25Q64_READ_WRITE_CHECK_BUFF;//存放指针
    	sector_addr=addr/4096; //传入的地址是处于第几个扇区
    	sector_move=addr%4096; //计算传入的地址存于当前的扇区的偏移量位置
    	sector_size=4096-sector_move; //得到当前扇区剩余的空间
    
    	if(len<=sector_size)
    	{
    		sector_size=len; //判断第一种可能性、一次可以写完
    	}
    
    	while(1)
    	{
    		W25Q64_ReadByteData(addr,p,sector_size);	 //读取剩余扇区里的数据
    		for(i=0;i<sector_size;i++)
    		{
    			if(p[i]!=0xFF)break;
    		}
    		if(i!=sector_size)  //判断是否需要擦除
    		{
    			W25Q64_ClearSector(sector_addr*4096);
    		}
    		W25Q64_WriteByteDataNoCheck(addr,buff,sector_size);
    		if(sector_size==len)break;
    
    		addr+=sector_size; //向后偏移地址
    		buff+=sector_size ;//向后偏移
    		len-=sector_size;  //减去已经写入的数据
    		sector_addr++;     //校验第下个扇区
    		if(len>4096)       //表明还可以写一个扇区
    		{
    			sector_size=4096;//继续写一个扇区
    		}
    		else
    		{
    			sector_size=len; //剩余的空间可以写完
    		}
    	}
    }
    

     

    展开全文

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 3,105
精华内容 1,242
关键字:

w25q64