精华内容
下载资源
问答
  • W25Q64中文手册.pdf

    2020-04-27 19:14:00
    W25Q64中文手册 非英文版,资料全面 W25X16\W25X32\W25X64 中文资料
  • stm32 w25q64

    2017-02-21 11:58:44
    stm32 w25q64
  • W25Q64中文数据手册.pdf

    2021-02-25 14:46:53
    W25Q64中文数据手册
  • w25q64jv详细介绍

    2017-09-06 16:16:00
    w25q64jv spi 详细介绍,W25Q64支持标准的SPI,双输出SPI和四输出SPI操作。标准的SPI指令利用单向的数据输入引脚在串行时钟输入上升沿串行地向器件写入指令、地址或数据。
  • W25Q64中文文档

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

    2016-10-16 17:59:52
    W25Q64官方数据手册
  • W25Q64芯片数据手册PDF

    2018-10-18 16:22:59
    正版w25Q64官方数据手册,PDF文档,有目录,敬请采纳。
  • w25q64fw_revk 07012016 sfdp.pdf
  • W25Q64_SPI.rar

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

    2020-08-29 17:23:16
    1、W25Q64Flash芯片介绍 板上有个带SPI的FLASH产品,W25Q64,大容量64Mb的,也就是8M字节, 大小:8M(Byte) (128块(Block),每块64K字节,每块16个扇区(Sector),每个扇区4K字节,每个扇区16页,每页256个 ...

    1、W25Q64Flash芯片介绍

    板上有个带SPI的FLASH产品,W25Q64,大容量64Mb的,也就是8M字节,

    大小:8M(Byte)   (128块(Block),每块64K字节,每块16个扇区(Sector),每个扇区4K字节,每个扇区16页,每页256个 字节)

    特点:Flash芯片内的数据只能由1变0,不能由0变1。

    W25Q64FV是一款FLASH存储芯片,由32768页组成的存储芯片,其中每一页有256字节。

    每次最多可以一次写入256字节(一页);擦除则可以按扇区进行擦除(一个扇区16页,4KB字节),或者可以按128个页进行擦除(32KB),或者可以按256个页(64KB)进行擦除,以及整个芯片擦除方式进行擦除。

    8M 8*1024k 8*1024*1024字节 0x200000 2097152
    分为128块 128      
    每个块64k 64   0x208f34 2133812
    每个块分为16个扇区 16      
    每个扇区4K字节 4   36660  
    每个扇区16页 16      
    每个页256字节 256      

     

     

     

    展开全文
  • 利用STM32F103读写W25Q64

    2019-03-30 20:20:45
    利用STM32F103读写W25Q64,是野火的例程,不错的,读写FLASH,利用uart打印
  • #include "iospiflash.h" .../*******************************************//// This is a IOSPI (simulater by IO)// // Lib For Driver Flash W25Q64BV//*******************************************///sbit...

    #include "iospiflash.h"

    /*******************************************
    //
    // This is a IOSPI (simulater by IO)
    //
    // Lib For Driver Flash W25Q64BV
    //
    *******************************************/
    //sbit IOFLASHSPI_CS = P1^0;
    //sbit IOFLASHSPI_DIN = P1^3;
    //sbit IOFLASHSPI_DOUT = P1^4;
    //sbit IOFLASHSPI_CLK = P1^5;

    /*******************************************
    //
    // IOSPI base FUNC
    // Data shifting at the --rising edge-- of the clk
    // CLK need a hold time MyDelay(3)
    //
    // Shift
    // Write u8
    // Read u8
    //
    // 1
    Time Series requires:
    The W25Q64B Flash support the stantdard SPI , but the SPI Mode 0 or 3.
    SPI Mode 0 required that:
    when free:
    the CLK need be low - 0 , while the /CS is high( when free );
    when execute an instruction:
    Fall the /CS that Enable the Flash.
    0 - 1 - 0 - 1 the CLK,
    when the rasing edge of CLK, the DOUT(0,1) will be send to the FLASH.
    when a whole instruction completed.
    need to! PULL up the /CS than Over an Instruction.

    SPI Mode 3 required that:
    ..... /CS /CLK the reverse.

    2
    Other IO need!
    /HOLD --- like a enable pin.
    when /hold is Low,the FLASH ,can not excute any instructions.

     


    //
    *******************************************/
    u8 IOSPI_Flash_Shift(u8 Data)
    {
    u8 DataRead = 0xFF;
    u8 i = 0;
    for(i = 0 ; i < 8 ; i++)
    {
    IOFLASHSPI_CLK = 0; //fall edge chage the out data bit
    IOFLASHSPI_DOUT = ((Data & 0x80) == 0x80 )?1 : 0;
    Data <<= 1;
    DataRead <<= 1;

    MyDelay(5);

    IOFLASHSPI_CLK = 1; //rising edge
    // the rising edge out the data bit and in the data bit
    MyDelay(5);
    if(IOFLASHSPI_DIN==1) //get bit
    DataRead += 1;
    }

    return DataRead;

    }
    //rising edge of CLK
    //!!!!!the CLK should be keep 0,while there is not excuting any instructions!
    void IOSPI_Flash_u8_Write(u8 Data)
    {
    u8 i = 0;
    for(i = 0 ; i < 8 ; i++)
    {
    IOFLASHSPI_CLK = 0;
    IOFLASHSPI_DOUT = (Data & 0x80);
    Data <<= 1;
    MyDelay(5);
    IOFLASHSPI_CLK = 1; //rising edge
    MyDelay(5);
    }

    IOFLASHSPI_CLK = 0;
    }

    u8 IOSPI_Flash_u8_Read(void)
    {
    u8 Data = 0xFF;
    u8 i = 0;
    for(i = 0 ; i < 8 ; i++)
    {
    IOFLASHSPI_CLK = 0;
    Data <<= 1;
    MyDelay(5); //hold time
    IOFLASHSPI_CLK = 1; //rising edge
    MyDelay(5);
    if(IOFLASHSPI_DIN==1) //get bits
    Data += 1;
    }
    IOFLASHSPI_CLK = 0;
    return Data;
    }

     

    //-----------------------------------
    //
    // IOSPI FLASH FUNC
    //
    //-----------------------------------
    void IOSPI_Flash_CMD(u8 CMD)
    {
    IOSPI_Flash_u8_Write(CMD);
    }

    u8 IOSPI_Flash_RDSR(void)
    {
    u8 ReadStatus = 0;

    IOSPI_FLASH_CS_ENABLE

    IOSPI_Flash_CMD(CMD_RDSR); //time series need to be learn
    ReadStatus = IOSPI_Flash_u8_Read();

    IOSPI_FLASH_CS_DISABLE //cs over the instruction

    return ReadStatus;
    }

    u32 IOSPI_Flash_RDJCID()
    {
    u8 i = 0;
    u32 JCID = 0x00000000;
    u8 *pData = &JCID;

    IOSPI_FLASH_CS_ENABLE

    IOSPI_Flash_CMD(CMD_RDJCID); //time series need to be learn
    for(i = 0 ; i < 3 ; i++)
    {
    *(pData++) = IOSPI_Flash_u8_Read();
    }

    IOSPI_FLASH_CS_DISABLE //cs over the instruction

    return JCID;
    }

    void IOSPI_Flash_WaitFree(void)
    {
    while((IOSPI_Flash_RDSR() & 0x01)==0x01) //check the busy = 0;
    {
    MyDelay(30);
    }
    }


    //---------------------------------------------------------
    //
    // 4K byte Erase 16 pages
    //
    //---------------------------------------------------------
    void IOSPI_Flash_SectorErase(u32 Addr)
    {
    u8* pAddr = &Addr;
    IOSPI_Flash_WaitFree();

    IOSPI_FLASH_CS_ENABLE
    IOSPI_Flash_CMD(CMD_ENABLEWRITE);
    IOSPI_FLASH_CS_DISABLE

    MyDelay(5);

    IOSPI_FLASH_CS_ENABLE
    IOSPI_Flash_CMD(CMD_SECERASE);
    IOSPI_Flash_u8_Write(*pAddr++);
    IOSPI_Flash_u8_Write(*pAddr++);
    IOSPI_Flash_u8_Write(*pAddr);
    IOSPI_FLASH_CS_DISABLE
    }
    //void IOSPI_Flash_BlockErase(u8* Addr);


    //-----------------------------------------------------------------
    //
    // Page Programe
    // the data to be programe must less than 256bytes,
    // otherwise the page will be wrap by the bytes lengther than the 256 bytes;
    //
    //-----------------------------------------------------------------
    void IOSPI_Flash_Write(u32 Addr , char* Data , u16 length)
    {
    u16 i = 0;
    u8 pData = 0x00;
    u8* pAddr = &Addr;
    if(length > 256)
    return ;

    IOSPI_Flash_WaitFree();

    IOSPI_FLASH_CS_ENABLE
    IOSPI_Flash_CMD(CMD_ENABLEWRITE);
    IOSPI_FLASH_CS_DISABLE

    MyDelay(5);

    IOSPI_FLASH_CS_ENABLE
    IOSPI_Flash_CMD(CMD_PAGEWRITE);
    IOSPI_Flash_u8_Write(*pAddr++);
    IOSPI_Flash_u8_Write(*pAddr++);
    IOSPI_Flash_u8_Write(*pAddr);

    for(i = 0 ; i < length ; i++)
    {
    IOSPI_Flash_u8_Write(*(Data+i));
    }
    IOSPI_FLASH_CS_DISABLE

    }

    //-----------------------------------------------------
    //
    // Standard Flash Read.
    // Can Read 256 bytes once at most.
    //
    //-----------------------------------------------------
    void IOSPI_Flash_Read(u32 Addr , char* Data , u16 length)
    {
    u16 i = 0;
    u8* pAddr = &Addr;
    IOSPI_Flash_WaitFree();

    IOSPI_FLASH_CS_ENABLE
    IOSPI_Flash_CMD(CMD_READ);
    IOSPI_Flash_u8_Write(*pAddr++);
    IOSPI_Flash_u8_Write(*pAddr++);
    IOSPI_Flash_u8_Write(*pAddr);

    for(i = 0 ; i < length ; i++)
    {
    *(Data+i) = IOSPI_Flash_u8_Read();
    }
    IOSPI_FLASH_CS_DISABLE

    }

    void IOSPI_Flash_FastRead(u32 Addr , char* Data , u16 length)
    {
    u16 i = 0;
    u8* pAddr = &Addr;
    if(length > 256)
    return ;
    IOSPI_Flash_WaitFree();

    IOSPI_FLASH_CS_ENABLE
    IOSPI_Flash_CMD(CMD_FASTREAD);
    IOSPI_Flash_u8_Write(*pAddr++);
    IOSPI_Flash_u8_Write(*pAddr++);
    IOSPI_Flash_u8_Write(*pAddr);
    IOSPI_Flash_u8_Write(0x00);

    for(i = 0 ; i < length ; i++)
    {
    *(Data+i) = IOSPI_Flash_u8_Read();
    }
    IOSPI_FLASH_CS_DISABLE
    }

     

     

     

     

     

    ===========================================================================================================

    ===========================================================================================================

    =========================================================================================================== 

    #ifndef __IOSPIFLASH_H
    #define __IOSPIFLASH_H
    #include "Common.h"
    /**************************************************************
    //
    // W25Q64BVSFIG FLASH
    //
    //
    *************************************************************/
    #define CMD_RDSR 0x05
    #define CMD_ENABLEWRITE 0x06
    #define CMD_DISABLEWRITE 0x04
    #define CMD_WDSR 0x01
    #define CMD_READ 0x03
    #define CMD_FASTREAD 0x0B
    #define CMD_PAGEWRITE 0x02
    #define CMD_SECERASE 0x20
    #define CMD_BLOCKERASE 0x52
    #define CMD_RDJCID 0x9F


    #define IOSPI_FLASH_CS_ENABLE {IOFLASHSPI_CS = 0;}
    #define IOSPI_FLASH_CS_DISABLE {IOFLASHSPI_CS = 1;}


    u8 IOSPI_Flash_Shift(u8 Data);
    void IOSPI_Flash_u8_Write(u8 Data);
    u8 IOSPI_Flash_u8_Read(void);

    void IOSPI_Flash_CMD(u8 CMD);
    u8 IOSPI_Flash_RDSR(void);
    void IOSPI_Flash_WaitFree(void);
    void IOSPI_Flash_SectorErase(u32 Addr);
    //void IOSPI_Flash_BlockErase(u2 Addr);
    void IOSPI_Flash_Write(u32 Addr , char* Data , u16 length);
    void IOSPI_Flash_Read(u32 Addr , char* Data , u16 length);
    void IOSPI_Flash_FastRead(u32 Addr , char* Data , u16 length);

    u32 IOSPI_Flash_RDJCID(void);


    #endif

    转载于:https://www.cnblogs.com/hisoka/p/4794649.html

    展开全文
  • W25Q64Flash芯片STM32操作

    2018-08-01 11:32:37
    该资源包含STM32F407的SPI寄存器配置及对W25Q64进行读写及擦除所需要的函数。
  • C51 w25q64驱动及字库烧录程序,包含点阵字库烧录及字定位,使用uincode,keil编译采用uft-8编码。
  • SPI专题(二)——STM32驱动FLASH(W25Q64)

    万次阅读 多人点赞 2017-08-31 22:18:02
    W25Q64 将 8M 的容量分为 128 个块(Block),每个块大小为 64K 字节,每个块又分为 16个扇区(Sector),每个扇区 4K 个字节。 W25Q64 的最少擦除单位为一个扇区,也就是每次必须擦除 4K 个字节。操作需要给 W25Q64...

    前言:

    为了方便查看博客,特意申请了一个公众号,附上二维码,有兴趣的朋友可以关注,和我一起讨论学习,一起享受技术,一起成长。

    在这里插入图片描述



    githubmy github


    注:博客所涉及的关于 stm32 的代码,均在仓库【stm32f013_study】下,包括底层驱动和应用测试代码。
    本文设计的文件包含:
    (1)hardware_spi.c:硬件 SPI 驱动实现
    (2)drvsfspi.c:软件模拟 SPI 实现代码
    (3)drvexflash.c:SPI FLASH 操作部分代码
    (4)hal_spi.c:SPI 软件、硬件方式封装统一接口
    (5)头文件:
    hardware_spi.h :硬件 SPI 相关
    drvsfspi.h :软件模拟 SPI 相关
    drvexflash.hSPI FLASH 相关
    hal_spi.h:软件、硬件 SPI 接口封装


    1. 硬件连接

    W25Q64 将 8M 的容量分为 128 个块(Block),每个块大小为 64K 字节,每个块又分为 16个扇区(Sector),每个扇区 4K 个字节

    W25Q64 的**最少擦除单位为一个扇区,也就是每次必须擦除 4K 个字节。**操作需要给 W25Q64 开辟一个至少 4K 的缓存区,对 SRAM 要求比较高,要求芯片必须有 4K 以上 SRAM 才能很好的操作。

    这里写图片描述

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

    1.1 硬件连接

    与 STM32 的引脚连接如下:这里是使用SPI1配置。

    这里写图片描述

    STM32引脚 对应SPI功能
    PA2 片选CS
    PA5 时钟SCK
    PA6 MISO
    PA7 MOSI

    STM32 的 SPI 功能很强大, SPI 时钟最多可以到 18Mhz,支持 DMA,可以配置为 SPI 协议或者 I2S 协议(仅大容量型号支持)。

    1.2 SPI 通讯的通讯时序

    SPI 协议定义了通讯的起始和停止信号、数据有效性、时钟同步等环节。

    我们以读取 FLASH 的状态寄存器的时序图分析一下,时序图也是书写软件模拟时序的依据。

    在这里插入图片描述
    如上图,我们知道书写 FLASH (来自华邦 W25X 手册)支持的是模式 0 (CPOL = 0 && CPHA == 0) 和 模式 3(CPOL = 1 && CPHA == 1)

    CS、SCK、MOSI 信号都由主机控制产生,而 MISO 的信号由从机产生,主机通过该信号线读取从机的数据。MOSI 与 MISO 的信号只在 CS 为低电平的时候才有效,在 SCK 的每个时钟周期 MOSI 和 MISO 传输一位数据。

    1.2.1. 通讯的起始和停止信号

    在上图,CS 信号线由高变低,为 SPI 通讯的起始信号。CS 是每个从机各自独占的信号线,当从机在自己的 CS 线检测到起始信号后,就知道自己被主机选中了,开始准备与主机通讯。当 CS 信号由低变高,为 SPI 通讯的停止信号,表示本次通讯结束,从机的选中状态被取消。

    1.2.2. 数据有效性

    SPI 使用 MOSI 及 MISO 信号线来传输数据,使用 SCK 信号线进行数据同步。

    在这里插入图片描述
    MOSI 及 MISO 数据线在 SCK 的每个时钟周期传输一位数据。数据传输时,MSB 先行或 LSB 先行并没有作硬性规定,但要保证两个 SPI 通讯设备之间使用同样的协定,一般都会采用图中的 MSB 先行模式。

    观察上图,可知模式 0 和 3 都是在上升沿读取数据。

    示例: FLASH 读取 JEDEC_ID (0x9F),SPI 模式 0,,频率 f = 1MHz。

    在这里插入图片描述
    读取 JEDEC_ID 时,FLASH 回复的一个字节:0xC8。

    在这里插入图片描述

    1.2.3 STM32 SPI外设

    STM32 的 SPI 外设可用作通讯的主机及从机,支持最高的 SCK 时钟频率为 f pclk / 2 (STM32F103 型号的芯片默认 f pclk1 为 72MHz,f pclk2 为 36MHz),完全支持 SPI 协议的 4 种模式,数据帧长度可设置为 8 位或 16 位,可设置数据 MSB 先行或 LSB 先行。它还支持双线全双工、双线单向以及单线模式。

    SPI架构:

    这里写图片描述

    通讯引脚 :

    SPI 的所有硬件架构都从上图中左 MOSI、MISO、SCK及 NSS 线展开的。

    STM32 芯片有多个 SPI 外设,它们的 SPI 通讯信号引出到不同的 GPIO 引脚上,使用时必须配置到这些指定的引脚。

    2. 软件配置

    这里使用 STM32 的 SPI1 的主模式,SPI 相关的库函数和定义分布在文件 stm32f10x_spi.c 以及头文件 stm32f10x_spi.h 中。

    2.1 配置相关引脚的复用功能

    第一步就要使能 SPI1 的时钟, SPI1 的时钟通过 APB2ENR 的第 12 位来设置。其次要设置 SPI1 的相关引脚为复用输出,这样才会连接到 SPI1 上否则这些 IO 口还是默认的状态,也就是标准输入输出口。这里我们使用的是 PA5、 PA6、 PA7 这 3 个(SCK、 MISO、 MOSI、CS 使用软件管理方式),所以设置这三个为复用 IO

    宏定义:

    #define SPIM1_GPIO_PORT	    GPIOA
    
    #define SPIM1_CLK_IO	(GPIO_Pin_5)
    #define SPIM1_MISO_IO	(GPIO_Pin_6)
    #define SPIM1_MOSI_IO	(GPIO_Pin_7)
    
    #define FLASH_CS_IO     		(GPIO_Pin_2)
    #define FLASH_CS_0()			(GPIO_ResetBits(SPIM1_GPIO_PORT, FLASH_CS_IO))		
    #define FLASH_CS_1() 			(GPIO_SetBits(SPIM1_GPIO_PORT, FLASH_CS_IO))
    
    #define RCC_PCLK_SPIM1_GPIO     RCC_APB2Periph_GPIOA
    #define RCC_PCLK_SPIM1_HD       RCC_APB2Periph_SPI1
    

    IO 配置:

    
    	//--------------------------------------------------------------------------------------------------------
    //	函 数 名: spi_gpio_init
    //	功能说明: SPI 硬件IO初始化
    //	形    参: 	spi_chl:SPIM 通道
    //	返 回 值: 无
    //	日    期: 2020-03-12
    //  备    注:采用 Unix like 方式
    //	作    者: by 霁风AI
    //--------------------------------------------------------------------------------------------------------
    void spi_gpio_init(uint8_t spi_chl)
    {
    	GPIO_InitTypeDef gpio_config_init;
    
    	if (spi_chl == 1)
    	{
    		RCC_APB2PeriphClockCmd(RCC_PCLK_SPIM1_GPIO, ENABLE);		//开启SPIM1 GPIO时钟、
    		
    //		gpio_config_init.GPIO_Pin 		= SPIM1_CLK_IO | SPIM1_MISO_IO | SPIM1_MOSI_IO;	//SPIM1_CLK_IO IO初始化
    		gpio_config_init.GPIO_Pin 		= SPIM1_CLK_IO | SPIM1_MOSI_IO;
    		gpio_config_init.GPIO_Mode 		= GPIO_Mode_AF_PP;  //复用推挽输出
    		gpio_config_init.GPIO_Speed 	= GPIO_Speed_50MHz;
    		
    		GPIO_Init(SPIM1_GPIO_PORT, &gpio_config_init);
    		
    		gpio_config_init.GPIO_Pin 		= SPIM1_MISO_IO;	//SPIM1_MISO_IO IO初始化
    		gpio_config_init.GPIO_Mode 		= GPIO_Mode_IN_FLOATING;  //MISO浮空输入
    		gpio_config_init.GPIO_Speed 	= GPIO_Speed_50MHz;
    		GPIO_Init(SPIM1_GPIO_PORT, &gpio_config_init);
    
    		GPIO_SetBits(SPIM1_GPIO_PORT, SPIM1_CLK_IO | SPIM1_MISO_IO | SPIM1_MOSI_IO);	//IO初始状态都设置为高电平
    	}		
    }
    

    2.2 初始化 SPI1,设置 SPI1 工作模式

    接下来初始化 SPI1,设置 SPI1 为主机模式,设置数据格式为 8 位,然设置 SCK 时钟极性及采样方式。并设置 SPI1 的时钟频率(最大 18Mhz),以及数据的格式(MSB 在前还是 LSB 在前)。这在库函数中是通过 SPI_Init 函数来实现。
    函数原型:

    void SPI_Init(SPI_TypeDef* SPIx, SPI_InitTypeDef* SPI_InitStruct)

    第一个参数是 SPI 标号,第二个参数结构体类型 SPI_InitTypeDef 为相关属性设置。

    SPI_InitTypeDef 的定义如下:

    typedef struct
    {
    uint16_t SPI_Direction;
    uint16_t SPI_Mode;
    uint16_t SPI_DataSize;
    uint16_t SPI_CPOL;
    uint16_t SPI_CPHA;
    uint16_t SPI_NSS;
    uint16_t SPI_BaudRatePrescaler;
    uint16_t SPI_FirstBit;
    uint16_t SPI_CRCPolynomial;
    }SPI_InitTypeDef;
    
    
    参数 解释
    SPI_Direction 设置 SPI 的通信方式,可以选择为半双工,全双工,以及串行发和串行收方式
    SPI_Mode 设置 SPI 的主从模式,主机模式 (SPI_Mode_Master),从机模式 (PI_Mode_Slave)。
    SPI_DataSiz 数据为 8 位还是 16 位帧格式选择项。SPI_DataSize_8b(8 位),SPI_DataSize_16b (16位)
    SPI_CPOL 设置时钟极性
    SPI_CPHA 设置时钟相位,也就是选择在串行同步时钟的第几个跳变沿(上升或下降)数据被采样,可以为第一个或者第二个条边沿采集
    SPI_NSS 设置 NSS 信号由硬件(NSS 管脚)还是软件控制
    SPI_BaudRatePrescaler 设置 SPI 波特率预分频值也就是决定 SPI 的时钟的参数 ,从不分频道 256 分频 8 个可选值 ,选择 256 分频值SPI_BaudRatePrescaler_256, 传输速度为 36M/256=140.625KHz。
    SPI_FirstBit 设置数据传输顺序是 MSB 位在前还是 LSB 位在前。SPI_FirstBit_MSB (高位在前)
    SPI_CRCPolynomial 设置 CRC 校验多项式,提高通信可靠性,大于 1 即可

    初始化的范例格式为:

    //--------------------------------------------------------------------------------------------------------
    //	函 数 名: spi_master_init
    //	功能说明: SPI 硬件配置参数初始化
    //	形    参: 	spi_chl:SPIM 通道
    //	返 回 值: 无
    //	日    期: 2020-03-12
    //  备    注:采用 Unix like 方式
    //	作    者: by 霁风AI
    //--------------------------------------------------------------------------------------------------------
    void spi_master_init(uint8_t spi_chl)
    {
    	SPI_InitTypeDef  spi_config_init;
    #if 1  
    	if(spi_chl == 1)
    	{	
    		spi_flash_gpio_init();	//spi flash cs 初始化
    //		sd_gpio_init();	//spi sd cs 初始化
    //		nrf24l01_gpio_init();//spi nrf24l01 cs 初始化
    		
    		spi_gpio_init(1);	//spi gpio 初始化
    
    		RCC_APB2PeriphClockCmd(RCC_PCLK_SPIM1_HD, ENABLE);	//SPI1时钟使能
    
    		spi_config_init.SPI_Direction 			= SPI_Direction_2Lines_FullDuplex;  //设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工
    		spi_config_init.SPI_Mode 				= SPI_Mode_Master;		//设置SPI工作模式:设置为主SPI
    		spi_config_init.SPI_DataSize 			= SPI_DataSize_8b;		//设置SPI的数据大小:SPI发送接收8位帧结构
    		spi_config_init.SPI_CPOL 				= SPI_CPOL_Low;		//选择了串行时钟的稳态:空闲时钟低
    		spi_config_init.SPI_CPHA 				= SPI_CPHA_1Edge;	//数据捕获(采样)于第1个时钟沿
    		spi_config_init.SPI_NSS					= SPI_NSS_Soft;//SPI_NSS_Soft;		//NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制
    		spi_config_init.SPI_BaudRatePrescaler 	= SPI_BaudRatePrescaler_256;		//定义波特率预分频的值:波特率预分频值为256
    		spi_config_init.SPI_FirstBit 			= SPI_FirstBit_MSB;	//指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始
    		spi_config_init.SPI_CRCPolynomial 		= 7;	//CRC值计算的多项式
    		
    		SPI_Init(SPI1, &spi_config_init);  //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器
    	 
    		SPI_Cmd(SPI1, ENABLE); //使能SPI外设
    		
    //		spi_master_send_recv_byte(1, 0xFF);	//启动传输	
    	
    	}
    #endif
    } 
    

    2.3 SPI 传输数据

    通信接口需要有发送数据和接受数据的函数,固件库提供的发送数据函数原型为:

    void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data)

    往 SPIx 数据寄存器写入数据 Data,从而实现发送。

    固件库提供的接受数据函数原型为:

    uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx)

    这从 SPIx 数据寄存器读出接收到的数据。

    收发单个字节数据:

    //--------------------------------------------------------------------------------------------------------
    //	函 数 名: spi_master_send_recv_byte
    //	功能说明: SPI 收发数据
    //	形    参: 	spi_chl:SPIM 通道
    //				send_byte:发送的数据
    //	返 回 值: 无
    //	日    期: 2020-03-14
    //  备    注:采用 Unix like 方式
    //	作    者: by 霁风AI
    //--------------------------------------------------------------------------------------------------------
    uint8_t spi_master_send_recv_byte(uint8_t spi_chl, uint8_t spi_byte)
    {		
    	uint8_t time = 0;
    	
    	if (spi_chl == 1)			    
    	{
    		while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET) //检查指定的SPI标志位设置与否:发送缓存空标志位
    		{
    			time++;
    			if(time>200)
    			{
    				return false;
    			}
    		}			  
    		SPI_I2S_SendData(SPI1, spi_byte); //通过外设SPIx发送一个数据
    	
    		time = 0;
    
    		while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET)//检查指定的SPI标志位设置与否:接受缓存非空标志位
    		{
    			time++;
    			if(time>200)
    			{
    				return false;
    			}
    		}	  						    
    			return SPI_I2S_ReceiveData(SPI1); //返回通过SPIx最近接收的数据	
    	}
    	else 
    	{
    		return false;
    	}
    }
    

    收发多个字节数据:

    //--------------------------------------------------------------------------------------------------------
    //	函 数 名: spi_master_send_some_bytes
    //	功能说明: SPI 发送多个字节数据
    //	形    参: 	spi_chl:SPIM 通道
    //				pbdata:发送的数据首地址
    //				send_length:发送数据长度
    //	返 回 值: 无
    //	日    期: 2020-03-12
    //  备    注:采用 Unix like 方式
    //	作    者: by 霁风AI
    //--------------------------------------------------------------------------------------------------------
    void spi_master_send_some_bytes(uint8_t spi_chl, uint8_t *pbdata, uint16_t send_length)
    {
    	uint16_t i = 0;
    
    	for (i = 0; i < send_length; i++)
    	{
    		spi_master_send_recv_byte(spi_chl, pbdata[i]);
    	}
    	
    //	while (send_length--)
    //	{
    //		spi_master_send_byte(spi_chl, *pbdata++);
    //	}
    	
    }
    
    //--------------------------------------------------------------------------------------------------------
    //	函 数 名: spi_master_recv_some_bytes
    //	功能说明: SPI 接收多个字节数据
    //	形    参: 	spi_chl:SPIM 通道
    //				pbdata:接收的数据首地址
    //				send_length:接收数据长度
    //	返 回 值: 无
    //	日    期: 2020-03-12
    //  备    注:采用 Unix like 方式
    //	作    者: by 霁风AI
    //--------------------------------------------------------------------------------------------------------
    void spi_master_recv_some_bytes(uint8_t spi_chl, uint8_t *pbdata, uint16_t recv_length)
    {
    	uint8_t *temp_data = pbdata;
    
    	while (recv_length--)
    	{
    		*temp_data++ = spi_master_send_recv_byte(spi_chl, 0xFF);	//发送 0xff 为从设备提供时钟
    	}
    	
    }
    

    2.4 查看 SPI 传输状态

    在 SPI 传输过程中,要判断数据是否传输完成,发送区是否为空等等状态,
    通过函数 SPI_I2S_GetFlagStatus 实现的,判断发送是否完成的方法是:

    SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE)

    3. SPI FLASH 操作

    3.1 宏定义部分

    #define  FLASH_WRITE_ENABLE_CMD 		0x06
    #define  FLASH_WRITE_DISABLE_CMD		0x04
    #define  FLASH_READ_SR_CMD				0x05
    #define  FLASH_WRITE_SR_CMD				0x01
    #define  FLASH_READ_DATA				0x03
    #define  FLASH_FASTREAD_DATA			0x0b
    #define  FLASH_WRITE_PAGE				0x02
    #define  FLASH_ERASE_PAGE      			0x81
    #define  FLASH_ERASE_SECTOR       		0x20
    #define	 FLASH_ERASE_BLOCK				0xd8
    #define	 FLASH_ERASE_CHIP				0xc7
    #define  FLASH_POWER_DOWN				0xb9
    #define  FLASH_RELEASE_POWER_DOWN       0xab
    #define  FLASH_READ_DEVICE_ID      		0x90
    #define  FLASH_READ_JEDEC_ID      		0x9f
    
    #define 	FLASH_SIZE	 (1*1024*1024)	// 1M字节
    #define		PAGE_SIZE			8192	// 256 bytes
    #define 	SECTOR_SIZE		512	 // 4-Kbyte
    #define		BLOCK_SIZE		32	// 64-Kbyte	
    
    #define PAGE_LEN		255	 //一页256字节
    

    3.2 中间层函数封装

    注明: 此部分函数的封装是为了统一硬件 SPI 和软件模拟 SPI 接口。

    //--------------------------------------------------------------------------------------------------------
    //	函 数 名: hal_spi_send_bytes
    //	功能说明: SPI 发送数据,包含软件和硬件通信方式
    //	形    参: 	mode:通信方式选择(0:软件SPI;1:硬件SPI)
    //				pbdata:发送数据的首地址
    //				send_length:发送数据长度
    //	返 回 值: 执行状态(true or false)
    //	日    期: 2020-03-12
    //  备    注: 中间层封装底层接口
    //	作    者: by 霁风AI
    //--------------------------------------------------------------------------------------------------------
    uint8_t hal_spi_send_bytes(uint8_t mode, uint8_t *pbdata, uint16_t send_length)
    {
        if (mode == 0)
        {
            for (uint16_t i = 0; i < send_length; i++)
            {
                Spi_WriteByte(pbdata[i]);
            }
    		
    		return true;
        }
        else if (mode == 1)
        {
            spi_master_send_some_bytes(1, pbdata, send_length);
    		
    //		for (uint16_t i = 0; i < send_length; i++)
    //        {
    //            spi_master_send_recv_byte(1, pbdata[i]);
    //        }
    		
    		return true;
        }
    	else 
    	{
    		return false;
    	}
        
    }
    
    //--------------------------------------------------------------------------------------------------------
    //	函 数 名: hal_spi_recv_bytes
    //	功能说明: SPI 接收数据,包含软件和硬件通信方式
    //	形    参: 	mode:通信方式选择(0:软件SPI;1:硬件SPI)
    //				pbdata:发送数据的首地址
    //				send_length:发送数据长度
    //	返 回 值: 执行状态(true or false)
    //	日    期: 2020-03-12
    //  备    注: 中间层封装底层接口
    //	作    者: by 霁风AI
    //--------------------------------------------------------------------------------------------------------
    uint8_t hal_spi_recv_bytes(uint8_t mode, uint8_t *pbdata, uint16_t recv_length)
    {
        if (mode == 0)
        {
            for (uint16_t i = 0; i < recv_length; i++)
            {
                 *pbdata++ = Spi_ReadByte();	//软件模拟SPI
            }   
    		
    		return true;
        }
        else if (mode == 1)
        {
            spi_master_recv_some_bytes(1, pbdata, recv_length);	//硬件SPI
    		
    //		for (uint16_t i = 0; i < recv_length; i++)
    //        {
    //            *pbdata++ = spi_master_send_recv_byte(1, 0xFF);
    //        }
    		
    		return true;
        }
    	else 
    	{
    		return false;
    	}
        
    }
    

    关于软件模拟 SPI 部分代码,参看软件模拟SPI代码 。此处不再贴出。


    3.3 FLASH 部分

    
    __align(4) uint8_t g_DataTmpBuffer[0x1000] = {0};
    
    #define SectorBuf  g_DataTmpBuffer
    
    //--------------------------------------------------------------------------------------------------------
    //	函 数 名: Flash_WriteEnable
    //	功能说明: 写使能,置位 WEL 位 WEL 位(WEL-->1)
    //	形    参: 无
    //	返 回 值: 无
    //	日    期: 2020-03-07
    //  备    注: 
    //	作    者: by 霁风AI
    //--------------------------------------------------------------------------------------------------------
    void Flash_WriteEnable(void)
    {
    	uint8_t command = FLASH_WRITE_ENABLE_CMD;
    
    	FLASH_CS_LOW;
    	hal_spi_send_bytes(SPI_COMM_MODE, &command, 1);//开启写使能
    	FLASH_CS_HIGH;
    }
    
    //--------------------------------------------------------------------------------------------------------
    //	函 数 名: Flash_WriteDisable
    //	功能说明: 写失能,复位 WEL 位(WEL-->0)
    //	形    参: 无
    //	返 回 值: 无
    //	日    期: 2020-03-07
    //  备    注: 
    //	作    者: by 霁风AI
    //--------------------------------------------------------------------------------------------------------
    void Flash_WriteDisable(void)
    {
    	uint8_t command = FLASH_WRITE_DISABLE_CMD;
    	FLASH_CS_LOW;
    	hal_spi_send_bytes(SPI_COMM_MODE, &command, 1);
    	// Spi_WriteByte(FLASH_WRITE_DISABLE_CMD);	//开启写失能 04h
    	FLASH_CS_HIGH;
    }
    
    //--------------------------------------------------------------------------------------------------------
    //	函 数 名: Flash_WriteSR
    //	功能说明: 读状态寄存器
    //	形    参: 无
    //	返 回 值: 无
    //	日    期: 2020-03-07
    //  备    注: 多用于检查 BUSY 位
    //	作    者: by 霁风AI
    //--------------------------------------------------------------------------------------------------------
    uint8_t Flash_ReadSR(void)
    {
    	uint8_t ucTmpVal = 0;
    	uint8_t command = FLASH_READ_SR_CMD;
    
    	FLASH_CS_LOW;
    	
    	hal_spi_send_bytes(SPI_COMM_MODE, &command, 1);	//05h
    	hal_spi_recv_bytes(SPI_COMM_MODE, &ucTmpVal, 1);
    
    	// ucTmpVal = Spi_ReadByte();
    	
    	FLASH_CS_HIGH;
    	
    	return ucTmpVal;
    }
    
    //--------------------------------------------------------------------------------------------------------
    //	函 数 名: Flash_WriteSR
    //	功能说明: 写状态寄存器
    //	形    参: 	_ucByte:写入状态寄存器的数值
    //	返 回 值: no
    //	日    期: 2020-03-07
    //  备    注: 
    //	作    者: by 霁风AI
    //--------------------------------------------------------------------------------------------------------
    void Flash_WriteSR(uint8_t _ucByte)
    {
    	uint8_t command = FLASH_WRITE_SR_CMD;
    
    	Flash_WriteEnable();	
    	Flash_WaitNobusy();
    
    	FLASH_CS_LOW;
    	hal_spi_send_bytes(SPI_COMM_MODE, &command, 1);	//01h
    	hal_spi_send_bytes(SPI_COMM_MODE, &_ucByte, 1);	//写入一个字节
    	FLASH_CS_HIGH;
    }
    
    //--------------------------------------------------------------------------------------------------------
    //	函 数 名: Flash_WaitNobusy
    //	功能说明: 检查 FLASH BUSY 位状态
    //	形    参: no
    //	返 回 值: no
    //	日    期: 2020-03-07
    //  备    注: 调用Flash_ReadSR(),判断状态寄存器的R0位,执行结束操作清零
    //	作    者: by 霁风AI
    //--------------------------------------------------------------------------------------------------------
    void Flash_WaitNobusy(void)
    {
    	//FLASH_READ_SR_CMD 指令的发送,有的FLASH仅需发送一次,FLASH自动回复,有的FLASH无法自动回复,需要循环一直发送等待
    	while(((Flash_ReadSR()) & 0x01)==0x01);	//等待BUSY位清空
    }
    
    //--------------------------------------------------------------------------------------------------------
    //	函 数 名: Flash_FastReadByte
    //	功能说明: flash 都数据(快速读取:Fast read operate at the highest poossible frequency)
    //	形    参: 	ucpBuffer:数据存储区首地址
    //				_ulReadAddr: 要读出Flash的首地址
    //				_usNByte: 要读出的字节数(最大65535B)
    //	返 回 值: no
    //	日    期: 2020-03-07
    //  备    注: 从_ulReadAddr地址,连续读出_usNByte长度的字节
    //	作    者: by 霁风AI
    //--------------------------------------------------------------------------------------------------------
    void Flash_ReadSomeBytes(uint8_t *ucpBuffer, uint32_t _ulReadAddr, uint16_t _usNByte)
    {
    	uint8_t command = FLASH_READ_DATA;
    	uint8_t temp_buff[3] = {0};
    
    	temp_buff[0] = (uint8_t)(_ulReadAddr >> 16);
    	temp_buff[1] = (uint8_t)(_ulReadAddr >> 8);
    	temp_buff[2] = (uint8_t)(_ulReadAddr >> 0);
    
    	FLASH_CS_LOW;
    	
    	hal_spi_send_bytes(SPI_COMM_MODE, &command, 1);
    	hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[0], 1);
    	hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[1], 1);
    	hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[2], 1);
    
    	hal_spi_recv_bytes(SPI_COMM_MODE, ucpBuffer, _usNByte);
    
    	// Spi_WriteByte(FLASH_READ_DATA);	//连续读取数据 03h
    	// Spi_WriteByte((uint8_t)(_ulReadAddr>>16));	//写入24位地址
    	// Spi_WriteByte((uint8_t)(_ulReadAddr>>8));
    	// Spi_WriteByte((uint8_t)(_ulReadAddr>>0));
    
    	// while(_usNByte--)
    	// {
    	// 	*ucpBuffer = Spi_ReadByte();
    	// 	ucpBuffer++;
    	// }
    	
    	FLASH_CS_HIGH;
    }
    
    //--------------------------------------------------------------------------------------------------------
    //	函 数 名: Flash_FastReadByte
    //	功能说明: flash 都数据(快速读取:Fast read operate at the highest poossible frequency)
    //	形    参: 	ucpBuffer:数据存储区首地址
    //				_ulReadAddr: 要读出Flash的首地址
    //				_usNByte: 要读出的字节数(最大65535B)
    //	返 回 值: no
    //	日    期: 2020-03-07
    //  备    注: 从_ulReadAddr地址,连续读出_usNByte长度的字节
    //	作    者: by 霁风AI
    //--------------------------------------------------------------------------------------------------------
    void Flash_FastReadByte(uint8_t *ucpBuffer, uint32_t _ulReadAddr, uint16_t _usNByte)
    {
    	uint8_t command = FLASH_FASTREAD_DATA;
    	uint8_t temp_buff[3] = {0};
    
    	temp_buff[0] = (uint8_t)(_ulReadAddr >> 16);
    	temp_buff[1] = (uint8_t)(_ulReadAddr >> 8);
    	temp_buff[2] = (uint8_t)(_ulReadAddr >> 0);
    
    	FLASH_CS_LOW;
    	
    	hal_spi_send_bytes(SPI_COMM_MODE, &command, 1);
    	hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[0], 1);
    	hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[1], 1);
    	hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[2], 1);
    
    	hal_spi_recv_bytes(SPI_COMM_MODE, ucpBuffer, _usNByte);
    	
    	// Spi_WriteByte(FLASH_FASTREAD_DATA);//快速读取数据 0bh
    	// Spi_WriteByte((uint8_t)(_ulReadAddr>>16));//写入24位地址
    	// Spi_WriteByte((uint8_t)(_ulReadAddr>>8));
    	// Spi_WriteByte((uint8_t)(_ulReadAddr>>0));
    	// Spi_WriteByte(0xFF);//等待8个时钟(dummy byte)
    	// while(_usNByte--)
    	// {
    	// 	*ucpBuffer = Spi_ReadByte();
    	// 	ucpBuffer++;
    	// }
    	
    	FLASH_CS_HIGH;
    }
    
    //--------------------------------------------------------------------------------------------------------
    //	函 数 名: Flash_WritePage
    //	功能说明: flash 写数据(按页写入,一页256字节,写入之前FLASH地址上必须为0xFF)
    //	形    参: 	ucpBuffer:数据存储区首地址
    //				_ulWriteAddr: 要读写入Flash的首地址
    //				_usNByte: 要写入的字节数(最大65535B = 64K 块)
    //	返 回 值: no
    //	日    期: 2020-03-07
    //  备    注: _ulWriteAddr,连续写入_usNByte长度的字节
    //	作    者: by 霁风AI
    //--------------------------------------------------------------------------------------------------------
    void Flash_WritePage(uint8_t *ucpBuffer, uint32_t _ulWriteAddr, uint16_t _usNByte)
    {
    	uint8_t command = FLASH_WRITE_PAGE;
    	uint8_t temp_buff[3] = {0};
    
    	temp_buff[0] = (uint8_t)(_ulWriteAddr >> 16);
    	temp_buff[1] = (uint8_t)(_ulWriteAddr >> 8);
    	temp_buff[2] = (uint8_t)(_ulWriteAddr >> 0);
    	
    	Flash_WriteEnable();	//写使能
    	Flash_WaitNobusy();	//等待写入结束
    	
    	FLASH_CS_LOW;
    	
    	hal_spi_send_bytes(SPI_COMM_MODE, &command, 1);
    	hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[0], 1);
    	hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[1], 1);
    	hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[2], 1);
    
    	hal_spi_send_bytes(SPI_COMM_MODE, ucpBuffer, _usNByte);
    
    	// Spi_WriteByte(FLASH_WRITE_PAGE);	//02h
    	// Spi_WriteByte((uint8_t)(_ulWriteAddr>>16));	//写入24位地址
    	// Spi_WriteByte((uint8_t)(_ulWriteAddr>>8));
    	// Spi_WriteByte((uint8_t)(_ulWriteAddr>>0));
    	// while(_usNByte--)
    	// {
    	// 	Spi_WriteByte(*ucpBuffer);	//SPI 写入单个字节
    	// 	ucpBuffer++;
    	// }
    	
    	FLASH_CS_HIGH;
    	
    	Flash_WaitNobusy();	//等待写入结束
    }
    
    //--------------------------------------------------------------------------------------------------------
    //	函 数 名: Flash_WriteNoCheck
    //	功能说明: flash 写数据(不带擦除,写入之前必须确保写入部分FLASH的数据全为0xFf,否则写入失败)
    //	形    参: 	ucpBuffer:数据存储区首地址
    //				_ulWriteAddr: 要读写入Flash的首地址
    //				_usNByte: 要写入的字节数(最大65535B = 64K 块)
    //	返 回 值: no
    //	日    期: 2020-03-07
    //  备    注: _ulWriteAddr,连续写入_usNByte长度的字节,程序带FLASH数据检查写入
    //	作    者: by 霁风AI
    //--------------------------------------------------------------------------------------------------------
    void Flash_WriteNoCheck(uint8_t *ucpBuffer, uint32_t _ulWriteAddr, uint16_t _usNByte)
    {
    	uint16_t PageByte = 256 - _ulWriteAddr % 256;//单页剩余可写字节数
    
    	if(_usNByte <= PageByte)	//不大于256字节
    	{
    		PageByte = _usNByte;
    	}
    	
    	while(1)
    	{
    		Flash_WritePage(ucpBuffer, _ulWriteAddr, PageByte);
    		if(_usNByte == PageByte)	//写入结束
    			break;
    		else
    		{
    			ucpBuffer += PageByte;	//下一页写入的数据
    			_ulWriteAddr += PageByte;	//下一页写入的地址
    			_usNByte -= PageByte;	//待写入的字节数递减
    			if(_usNByte > 256)
    			{
    				PageByte = 256;
    			}
    			else
    			{
    				PageByte = _usNByte;
    			}
    		}
    	}
    }
    
    //--------------------------------------------------------------------------------------------------------
    //	函 数 名: Flash_WriteSomeBytes
    //	功能说明: flash 写数据
    //	形    参: 	ucpBuffer:数据存储区首地址
    //				_ulWriteAddr: 要读写入Flash的首地址
    //				_usNByte: 要写入的字节数(最大65535B = 64K 块)
    //	返 回 值: no
    //	日    期: 2020-03-07
    //  备    注: _ulWriteAddr,连续写入_usNByte长度的字节,程序带FLASH数据检查写入
    //	作    者: by 霁风AI
    //--------------------------------------------------------------------------------------------------------
    void Flash_WriteSomeBytes(uint8_t *ucpBuffer, uint32_t _ulWriteAddr, uint16_t _usNByte)
    {
    	uint32_t ulSecPos = 0;				//得到扇区位置
    	uint16_t usSecOff = 0;				//扇区偏移
    	uint16_t usSecRemain = 0;		//剩余扇区
    	uint32_t i = 0;
    
    	ulSecPos = _ulWriteAddr / 4096;//地址所在扇区(0--511)
    	usSecOff = _ulWriteAddr % 4096;//扇区内地址偏移
    	usSecRemain = 4096 - usSecOff;//扇区除去偏移,还剩多少字节
    
    	if(_usNByte <= usSecRemain)	//写入数据大小 < 剩余扇区空间大小
    	{
    		usSecRemain = _usNByte;
    	}
    
    	while(1)
    	{
    		Flash_ReadSomeBytes(SectorBuf, ulSecPos*4096, 4096);//读出整个扇区的内容
    		for (i = 0; i < usSecRemain; i++)	//校验数据
    		{
    			if (SectorBuf[usSecOff + i] != 0xFF)//储存数据不为0xFF,需要擦除
    				break;
    		}
    		
    		if(i < usSecRemain)	//需要擦除
    		{
    			Flash_EraseSector(ulSecPos);	//擦除这个扇区
    			for(i = 0; i < usSecRemain; i++)	//保存写入的数据
    			{
    				SectorBuf[usSecOff + i] = ucpBuffer[i];
    			}
    			Flash_WriteNoCheck(SectorBuf, ulSecPos*4096, 4096);	//写入整个扇区(扇区=老数据+新写入数据)
    		}
    		else
    		{
    			Flash_WriteNoCheck(ucpBuffer, _ulWriteAddr, usSecRemain);//不需要擦除,直接写入扇区
    		}
    		if(_usNByte == usSecRemain)	//写入结束
    		{
    			Flash_WriteDisable();
    			break;
    		}
    		else
    		{
    			ulSecPos++;		//扇区地址增加1
    			usSecOff = 0;		//扇区偏移归零
    			ucpBuffer += usSecRemain;	//指针偏移
    			_ulWriteAddr += usSecRemain;	//写地址偏移
    			_usNByte -= usSecRemain;	//待写入的字节递减
    
    			if(_usNByte > 4096)
    			{
    				usSecRemain = 4096;	//待写入一扇区(4096字节大小)
    			}
    			else
    			{
    				usSecRemain = _usNByte;		//待写入少于一扇区的数据
    			}
    		}
    		
    	}
    	
    }
    
    //--------------------------------------------------------------------------------------------------------
    //	函 数 名: Flash_ErasePage
    //	功能说明: flash erase page
    //	形    参: no
    //	返 回 值: no
    //	日    期: 2020-03-07
    //  备    注: 有的 FLASH 支持
    //	作    者: by 霁风AI
    //--------------------------------------------------------------------------------------------------------
    void Flash_ErasePage(uint32_t _ulPageAddr)
    {
    	_ulPageAddr *= 256;
    	
    	Flash_WriteEnable();
    	Flash_WaitNobusy();
    	
    	FLASH_CS_LOW;
    	Spi_WriteByte(FLASH_ERASE_PAGE);	//页擦除指令
    	Spi_WriteByte((uint8_t)(_ulPageAddr>>16));	//写入24位地址
    	Spi_WriteByte((uint8_t)(_ulPageAddr>>8));
    	Spi_WriteByte((uint8_t)(_ulPageAddr>>0));
    	FLASH_CS_HIGH;
    	
    	Flash_WaitNobusy();	//等待写入结束
    }
    
    //--------------------------------------------------------------------------------------------------------
    //	函 数 名: Flash_EraseSector
    //	功能说明: flash erase sector
    //	形    参: no
    //	返 回 值: no
    //	日    期: 2020-03-07
    //  备    注: 1扇区 = 4K Bytes
    //	作    者: by 霁风AI
    //--------------------------------------------------------------------------------------------------------
    void Flash_EraseSector(uint32_t _ulSectorAddr)
    {
    	uint8_t command = FLASH_ERASE_SECTOR;
    	uint8_t temp_buff[3] = {0};
    	
    	temp_buff[0] = (uint8_t)(_ulSectorAddr >> 16);
    	temp_buff[1] = (uint8_t)(_ulSectorAddr >> 8);
    	temp_buff[2] = (uint8_t)(_ulSectorAddr >> 0);
    	
    	_ulSectorAddr *= 4096;	//1个扇区 4 KBytes
    	
    	Flash_WriteEnable();
    	Flash_WaitNobusy();
    	
    	FLASH_CS_LOW;
    	hal_spi_send_bytes(SPI_COMM_MODE, &command, 1);
    	hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[0], 1);
    	hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[1], 1);
    	hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[2], 1);
    
    
    //	Spi_WriteByte(FLASH_ERASE_SECTOR);	//20h
    //	Spi_WriteByte((uint8_t)(_ulSectorAddr>>16));	//写入24位地址
    //	Spi_WriteByte((uint8_t)(_ulSectorAddr>>8));
    //	Spi_WriteByte((uint8_t)(_ulSectorAddr));
    	FLASH_CS_HIGH;
    	
    	Flash_WaitNobusy();	//等待写入结束
    }
    
    //--------------------------------------------------------------------------------------------------------
    //	函 数 名: Flash_EraseBlock
    //	功能说明: flash erase block 
    //	形    参: no
    //	返 回 值: no
    //	日    期: 2020-03-07
    //  备    注: 1块 = 64K Bytes
    //	作    者: by 霁风AI
    //--------------------------------------------------------------------------------------------------------
    void Flash_EraseBlock(uint32_t _ulBlockAddr)
    {
    	uint8_t command = FLASH_ERASE_BLOCK;
    	_ulBlockAddr *= 65536;	//块地址,一块64K
    	
    	Flash_WriteEnable();
    	Flash_WaitNobusy();
    
    	FLASH_CS_LOW;
    	hal_spi_send_bytes(SPI_COMM_MODE, &command, 1);
    	hal_spi_send_bytes(SPI_COMM_MODE, (uint8_t *)(_ulBlockAddr>>16), 1);
    	hal_spi_send_bytes(SPI_COMM_MODE, (uint8_t *)(_ulBlockAddr>>8), 1);
    	hal_spi_send_bytes(SPI_COMM_MODE, (uint8_t *)(_ulBlockAddr>>0), 1);
    
    	// Spi_WriteByte(FLASH_ERASE_BLOCK);	//d8h
    	// Spi_WriteByte((uint8_t)(_ulBlockAddr>>16));	//写入24位地址
    	// Spi_WriteByte((uint8_t)(_ulBlockAddr>>8));
    	// Spi_WriteByte((uint8_t)(_ulBlockAddr));
    	FLASH_CS_HIGH;
    
    	Flash_WaitNobusy();	//等待写入结束
    }
    
    //--------------------------------------------------------------------------------------------------------
    //	函 数 名: Flash_EraseChip
    //	功能说明: flash erase chip , it makes flash  recovery FF
    //	形    参: no
    //	返 回 值: no
    //	日    期: 2020-03-07
    //  备    注: 软件模拟SPI
    //	作    者: by 霁风AI
    //--------------------------------------------------------------------------------------------------------
    void Flash_EraseChip(void)
    {
    	uint8_t command = FLASH_ERASE_CHIP;
    
    	Flash_WriteEnable();	//flash芯片写使能
    	Flash_WaitNobusy();	//等待写操作完成
    	
    	FLASH_CS_LOW;
    	hal_spi_recv_bytes(SPI_COMM_MODE, &command, 1);
    	// Spi_WriteByte(FLASH_ERASE_CHIP);	//c7h
    	FLASH_CS_HIGH;
    	
    	Flash_WaitNobusy();	//等待写入结束
    }
    
    //--------------------------------------------------------------------------------------------------------
    //	函 数 名: Flash_PowerDown
    //	功能说明: flash into power down mode 
    //	形    参: no
    //	返 回 值: no
    //	日    期: 2020-03-07
    //  备    注: 软件模拟SPI
    //	作    者: by 霁风AI
    //--------------------------------------------------------------------------------------------------------
    void Flash_PowerDown(void)
    {
    	uint8_t command = FLASH_POWER_DOWN; 
    
    	FLASH_CS_LOW;
    	hal_spi_send_bytes(SPI_COMM_MODE, &command, 1);
    	// Spi_WriteByte(FLASH_POWER_DOWN);	//b9h
    	FLASH_CS_HIGH;
    	Sys_delay_us(3);	// cs go high , need to delay 3us
    }
    
    //--------------------------------------------------------------------------------------------------------
    //	函 数 名: Flash_WakeUp
    //	功能说明: wake up flash from power down mode or hign performance mode
    //	形    参: no
    //	返 回 值: no
    //	日    期: 2020-03-07
    //  备    注: 软件模拟SPI
    //	作    者: by 霁风AI
    //--------------------------------------------------------------------------------------------------------
    void Flash_WakeUp(void)
    {
    	uint8_t command = FLASH_RELEASE_POWER_DOWN; 
    
    	FLASH_CS_LOW;
    	hal_spi_send_bytes(SPI_COMM_MODE, &command, 1);
    	// Spi_WriteByte(FLASH_RELEASE_POWER_DOWN);//abh
    	FLASH_CS_HIGH;
    	Sys_delay_us(3);	//CS go high , need delay 3us
    }
    
    //--------------------------------------------------------------------------------------------------------
    //	函 数 名: Flash_ReadDeviceID
    //	功能说明: 读取FLASH ID(manufacturer ID-1Byte + Device ID-2Byte:type+density)
    //	形    参: 无
    //	返 回 值: ulJedId:FLASH ID 3字节
    //	日    期: 2020-03-06
    //  备    注: 软件模拟SPI
    //	作    者: by 霁风AI
    //--------------------------------------------------------------------------------------------------------
    uint16_t Flash_ReadDeviceID(void)
    {
    	uint8_t command = FLASH_READ_DEVICE_ID;
    	uint16_t usFlashId = 0;
    	uint8_t temp_buff[3] = {0};
    	
    	FLASH_CS_LOW;
    	
    	hal_spi_send_bytes(SPI_COMM_MODE, &command, 1);	//90h
    	hal_spi_send_bytes(SPI_COMM_MODE, temp_buff, 3);	//写入24位地址;假地址
    	hal_spi_recv_bytes(SPI_COMM_MODE, temp_buff, 2);
    
    	// Spi_WriteByte(FLASH_READ_DEVICE_ID);	//90h
    	// Spi_WriteByte(0x00);//写入24位地址;假地址
    	// Spi_WriteByte(0x00);
    	// Spi_WriteByte(0x00);	//如果0x01,先输出 Device ID
    	// usFlashId |= Spi_ReadByte()<<8;
    	// usFlashId |= Spi_ReadByte();
    	
    	FLASH_CS_HIGH;
    	
    	usFlashId = (uint16_t)(temp_buff[0] << 8) | (temp_buff[1] << 0);
    
    	return usFlashId;
    }
     
    //--------------------------------------------------------------------------------------------------------
    //	函 数 名: Flash_ReadJEDECID
    //	功能说明: 读取FLASH ID(manufacturer ID-1Byte + Device ID-2Byte:type+density)
    //	形    参: 无
    //	返 回 值: ulJedId:FLASH ID 3字节
    //	日    期: 2020-03-06
    //  备    注: 软件模拟SPI
    //	作    者: by 霁风AI
    //--------------------------------------------------------------------------------------------------------
    uint32_t Flash_ReadJEDECID(void)
    {
    	uint8_t command = FLASH_READ_JEDEC_ID;
    	uint32_t flash_jed_id = 0;
    	uint8_t recv_buff[3] = {0};
    	
    	FLASH_CS_LOW;
    
    	hal_spi_send_bytes(SPI_COMM_MODE, &command, 1);	//9fh
    	hal_spi_recv_bytes(SPI_COMM_MODE, recv_buff, 3);
    	
    	FLASH_CS_HIGH;
    
    	flash_jed_id = (recv_buff[0] << 16) | (recv_buff[1] << 8) | (recv_buff[2] << 0);
    	
    	return flash_jed_id;
    }
    

    参考:

    1.原子库函数手册

    2.SPI—读写串行 FLASH

    展开全文
  • QSPI W25Q64 FLASH FLM

    2021-01-15 15:30:03
    QSPI W25Q64 FALSH FLM QSPI 初始化引脚,复用功能 修改QSPI BANK 修改大小 W25Q64 增加擦除块程序,相对擦除扇区速度快了3/4倍 FLASH下载 FLASH下载算法的信息 初始化 擦除函数 编程函数 验证函数...

    QSPI W25Q64 FALSH FLM

    1. QSPI
      初始化引脚,复用功能
      在这里插入图片描述

    修改QSPI BANK
    在这里插入图片描述

    修改大小
    在这里插入图片描述

    1. W25Q64
      增加擦除块程序,相对擦除扇区速度快了3/4倍
      在这里插入图片描述

    2. FLASH下载
      FLASH下载算法的信息
      在这里插入图片描述

    初始化
    在这里插入图片描述

    擦除函数
    在这里插入图片描述
    在这里插入图片描述

    编程函数
    在这里插入图片描述

    验证函数
    在这里插入图片描述

    在这里插入图片描述

    展开全文
  • 剖析STM32F103读写W25Q64

    2020-07-24 16:20:49
    最近使用STM32F103+W25Q64+USB+FATFS做了一个U盘设备。程序已经调试完成了,现在重新梳理一下知识再做一个记录。 STM32F103+USB是根据官方demo修改的,这一部分没啥可说的。关于FATFS的移植下一篇文章介绍。本篇文章...
  • STM8S模拟SPI读写 W25Q64程序,IAR开发环境,程序大部份都有注释,STM8S105测试OK.
  • Lenovo IH110MS W25Q64FV BIOS,很少见的bios,亲测好用。
  • 学习日记——W25Q64 FLASH—QSPI

    千次阅读 2020-02-15 21:55:07
    W25Q64串行FLASH基础知识 大小:8M(Byte)(128块(Block),每块64K字节,每块16个扇区(Sector),每个扇区4K字 节,每个扇区16页,每页256个字节) 特点:Flash芯片内的数据只能由1变0,不能由0变1。 W25Q64...
  • 32读取W25Q64整数和小数 模拟SPI
  • 该程序为用IO口模拟SPI读取W25Q64的程序, 在正点原子战舰开发板,野火霸道开发板上运行通过,可以读写W25Q64 里面的数据,只需要修改.h里的管脚定义,就可以运行率,程序简单明了,备注非常详细
  • STM32F103系列驱动W25Q64FV(测试通过,非最常用的W25Q64BV)SPI FLASH驱动源码
  • 主板程序 华硕笔记本 BIOS x450cc W25Q64 bin x450bios
  • m using this library with W25Q64BV and Arduino MEGA correctly . I upgraded my flash memory with W25Q64JV. But now library doesn't work. What could be the problem?</p><p>该提问来源于开源项目:...
  • SPIFlash W25Q64 新唐单片机程序

    热门讨论 2014-01-21 17:37:15
    SPIFlash W25Q64 新唐单片机程序 可读,可写, 基本的操作

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 400
精华内容 160
关键字:

w25q64