总线二:SPI读写串行FLASH

之前在项目中调过的SPI FLASH的型号有M25P64、GD25Q64、以及W25Q64等......

今天就来简单介绍以下SPI读写串行FLASH

 兆易创新的GD25Q64手册在网上还不好找,然后我记得手机里面有M25P64的手册,找了好久,找到是找到了,但是过期了~

害!那咱们还是以最简单的W25Q64来讲解吧

本节呢,还是先看手册~这个手册网上害挺多的,大家可以自行下载哈,或者看我翻译的也行~

一、W25Q64简介

1.1 特点

W25Q64,是一种NOR FLASH。容量为64M bit = 8M Byte(8M 字节),实际上将8M字节分成0~127个block(块),每个块都64字节块,块又细分位0-15Sector(扇区),每个扇区大小为4k。

支持高达80MHz的时钟频率,可以真正的支持XIP。

灵活的架构,具有4KB扇区,

擦除选项:

统一扇区擦除:可以统一擦除每个包含4K字节数据的扇区。

块擦除:芯片支持块擦除,可选择32K和64K字节的块。

编程能力:

该芯片允许一次性编程从1到256字节,提供了数据存储的粒度。

1.2 引脚定义

        1. IO0 和 IO1 用于标准和双 SPI 指令        2. IO0 – IO3 用于四线 SPI 指令

引脚具体定义如下:

8.1 封装类型 W25Q64BV提供8引脚塑料208毫米宽SOIC(封装代码SS)和8x6毫米WSON(封装代码ZE),如图1a和1b所示。另一种封装选择是300毫米宽的8引脚PDIP(图1c)。W25Q64BV还提供16引脚塑料300毫米宽SOIC(封装代码SF),如图1d所示。封装的图表和尺寸在本数据表的末尾进行了说明。

8.2 片选信号(/CS) SPI芯片选择(/CS)引脚用于启用和禁用设备操作。当/CS为高电平时,设备未选择,串行数据输出(DO,或IO0,IO1,IO2,IO3)引脚处于高阻抗状态。未选择时,设备的功耗将保持在待机水平,除非正在进行内部擦除、编程或状态寄存器周期。当/CS拉低时,设备将被选择,功耗将增加到活动水平,可以向设备写入指令并从设备读取数据。上电后,必须在接受新指令之前将/CS从高电平转变为低电平。/CS输入必须在上电时跟踪VCC供电电平(参见“写保护”和图31)。如果需要,可以使用/CS上的上拉电阻来实现这一点。

8.3 串行数据输入、输出和IO(DI、DO和IO0、IO1、IO2、IO3) W25Q64BV支持标准SPI、双SPI和四线SPI操作。标准SPI指令使用单向DI(输入)引脚,在串行时钟(CLK)输入引脚的上升沿将指令、地址或数据串行写入设备。标准SPI还使用单向DO(输出)在CLK下降沿时从设备读取数据或状态。

双SPI和四线SPI指令使用双向IO引脚,在CLK上升沿时串行写入指令、地址或数据到设备,并在CLK下降沿时从设备读取数据或状态。四线SPI指令要求在状态寄存器-2中设置的非易失性四线使能位(QE)为1。当QE=1时,/WP引脚变为IO2,/HOLD引脚变为IO3。

8.4 写保护(/WP) 写保护(/WP)引脚可用于防止写入状态寄存器。与状态寄存器的块保护(SEC、TB、BP2、BP1和BP0)位以及状态寄存器保护(SRP)位一起使用,可以硬件保护部分或整个存储器数组。/WP引脚为低电平有效。当状态寄存器-2的QE位设置为四线I/O时,由于该引脚用于IO2,因此不可用硬件写保护(Hardware Write Protect)功能。请参见图1a、1b、1c和1d以获取四线I/O操作的引脚配置。

8.5 暂停(/HOLD) /HOLD引脚允许在设备被主动选择时暂停设备。当/HOLD拉低时,当/CS为低时,DO引脚将处于高阻抗状态,DI和CLK引脚上的信号将被忽略(不关心)。当/HOLD被拉高时,设备操作可以恢复。当多个设备共享相同的SPI信号时,/HOLD功能可能很有用。/HOLD引脚为低电平有效。当状态寄存器-2的QE位设置为四线I/O时,由于该引脚用于IO3,因此/HOLD引脚功能不可用。请参见图1a-d以获取四线I/O操作的引脚配置。

8.6 串行时钟(CLK) SPI串行时钟输入(CLK)引脚提供串行输入和输出操作的定时。("具体请参阅SPI操作")

1.3 常见电路解法:

 写保护和hold都是低电平有效,我们这里不使用,就直接接高电平,将写保护关闭,芯片的输出一定要接STM32的输入。

1.4 存储器框图

注意:FLASH的存储特性如下:

  1. 在写入数据之前,必须先擦除,
  2. 擦除时会把所有的数据全部重置为1
  3. 写入数据之前必须把1的数据位改为0
  4. 擦除时,必须按照最小单位来擦除(最小的擦除单位一般就是扇区,也就是说4KB,具体得看芯片数据手册)

NOR FLASH可以一个字节一个字节的读写(所以能支持XIP);NAND FLASH必须以块或扇区为单位进行读写;

1.5 状态寄存器

11.1.1 BUSY 是状态寄存器(S0)中的只读位,当设备执行页面编程、扇区擦除、块擦除、芯片擦除写状态寄存器指令时,该位设置为1。在此期间,设备将忽略除了读状态寄存器擦除暂停指令之外的任何其他指令(请参阅AC特性中的tW、tPP、tSE、tBE和tCE)。当程序、擦除或写状态寄存器指令完成时,BUSY位将被清零为0,表示设备已准备好接受进一步的指令。

11.1.2 写使能锁存器(WEL) 是状态寄存器(S1)中的只读位,在执行写使能指令后被设置为1。当设备被禁止写入时,WEL状态位被清零为0。写禁止状态在上电后或执行以下任何指令后发生:写禁止、页面编程、扇区擦除、块擦除、芯片擦除和写状态寄存器。

11.1.3 块保护位(BP2、BP1、BP0)是状态寄存器(S4、S3 和 S2)中的非易失性读/写位,提供写保护的控制和状态。块保护位可以使用写状态寄存器指令进行设置(请参阅AC特性中的tW)。可以使用块保护位保护整个、部分或不保护内存数组免受编程和擦除指令的影响(请参阅状态寄存器内存保护表)。块保护位的出厂默认设置为0,即数组的任何部分都未受保护。

读出的结果如下,这样我们就知道总线忙不忙了(即内部时序是否完成)

 STM32通过SPI接口给flash发送指令集(一些命令),比如想读取状态寄存器,就给flash发命令,这样flash就会给我们回复状态了。

1.6 指令集

11.2 指令 W25Q64BV的指令集包括二十七个基本指令,通过SPI总线完全受控制(请参阅指令集表)。指令由芯片选择(/CS)的下降沿引发。输入到DI的第一个数据字节提供指令代码。DI输入上升沿时取样数据,最高有效位(MSB)首先。

指令的长度从单个字节到多个字节不等,可能后跟地址字节、数据字节、虚拟字节(不关心)以及在某些情况下是这些的组合。指令完成于芯片选择(/CS)的上升沿。每个指令的时钟相对时序图包含在图4到30中。所有读取指令可以在任何时钟位之后完成。然而,所有写入、编程或擦除的指令必须在字节边界上完成(在完整的8位被时钟后,/CS被拉高),否则指令将被终止。这一特性进一步保护设备免受意外写入的影响。此外,在内存被编程或擦除时,或者在写入状态寄存器时,所有指令都将被忽略,直到编程或擦除周期完成。

带括号的数据是从flash传输给STM32控制器的,没带括号的这些字节内容就是由控制器传输给flash的。

第一个字节都是控制器通过SPI接口发送给flash的。比如我们想知道状态寄存器的值,那么就给flash发送05,flash收到这个命令代码之后,就会在flash通过SPI通讯的第二个字节,酒吧状态寄存器的值,通过SPI返回给微控器STM32了。 

下面可以结合时序去详细说明

 本质上来说它都是通过SPI协议向flash发送数据,或者从flash读取数据。但是它规定了一些特殊的时序,比如第一个字节,假如我们通过SPI协议,第一个字节发送0X05,通过DO引脚,就相当于发送了一个读取状态寄存器的命令了,flash接收到这个数据之后,就会认为这是一个读取状态寄存器的命令,对于我们的微控器而言,它就是一个普通的数据而已,那么flash就会根据约定返回这个寄存器的值,通过D0引脚在第二个字节,给微控器返回一个字节(后面是重复发送,而我们只要读一次就可以了)。如果我们没有结束通讯的话,它会一直返回这个状态寄存器的值,那么我们的微控器接收到这个字节之后,直接读取它的最低位,就知道它的BUSY位是否为1了,这样就能确认FLASH是否处于忙的状态了。

对于flash来说,还有很多指令集,比如说Block Erase(64KB)  还有Sector Erase(4KB),这个用的比较多,我们此处可以介绍一下:

在写入数据之前,必须对flash进行擦除的操作,擦除的时候,又以扇区为单位,每一次擦除的时候都会擦除4KB。这个命令的代码是0x20 

比如我们向flash发送0x20,后面三个字节是地址,flash的地址是24位,所以我们要通过三个字节来传输要擦除的地址,flash在接收到这个命令和地址之后,就会对相应地址进行擦除,擦除之后,我们又可以读取状态寄存器来了解是否擦除完成,擦除完成那么我们就可以对其进行写入数据了。

写入数据的代码是0x02

比如我们先给flash 发送0x02命令,发送完命令,接着是地址,后面紧接着是数据。

读数据的命令是0X03,那么我们先发送03,接着是地址,地址发送完成,flash会给微控器返回数据。

 还有几个比较重要的如下:

Dummy就是空白字节(可以是任意数据)

先发送0x90  然后dummy dummy 第四个字节是00  然后第五个字节flash就会返回EF啊 16h这些值。

通过读取这些值,判断flash是否可以连接正确。

 Flash的ID号如下:(每次可以先读取ID号,看是否跟4017的值相等,我们的微控器跟flash是否能正常通讯)

 POWER down,让其进去低功耗模式

 

将其从低功耗模式唤醒,flash也会返回一个ID号 

1.7 读取时序

下面来对时序进行分析,就拿Read Data举例说明吧

命令是0x03,再发送24位地址(需要读取的三个字节的地址) ,然后flash就会一直返回数据,返回的数据是没有限制的,一直返回到数据结束为止。(发送接收是严格分开的,大家注意时序哈)

Fast Read的时序跟这个没区别,就是比我们普通的读取速度要快。

接下来是写入

写入 命令代码为0x02,然后写入命令代码之后,然后发送24位地址,以及要写入的数据(写入都是这些都是通过DI引脚的)

一次最大可以写256字节。

擦除的话,是需要以4096为最小单位进行擦除的(Sector 4 KB),发送地址一定要发送对应Sector的最低地址。(严格要求以4096对齐)

二、硬件设计

        写代码之前注意一定要对应硬件原理图哈,SPI 串行FLASH 硬件连接图如下:

        FLASH 芯片(型号:W25Q64)是一种使用SPI 通讯协议的NOR FLASH存
储器, 它的CS/CLK/DIO/DO 引脚分别连接到了STM32 对应的SPI 引脚
NSS/SCK/MOSI/MISO 上,其中STM32 的NSS 引脚虽然是其片上SPI 外设的硬件引脚,但
实际上后面的程序只是把它当成一个普通的GPIO,使用软件的方式控制NSS 信号,所以
在SPI 的硬件设计中,NSS 可以随便选择普通的GPIO,不必纠结于选择硬件NSS 信号。
FLASH 芯片中还有WP 和HOLD 引脚。WP 引脚可控制写保护功能,当该引脚为低电
平时,禁止写入数据。我们直接接电源,不使用写保护功能。HOLD 引脚可用于暂停通讯,
该引脚为低电平时,通讯暂停,数据输出引脚输出高阻抗状态,时钟和数据输入引脚无效。
我们直接接电源,不使用通讯暂停功能。
        关于FLASH 芯片的更多信息,可参考其数据手册《W25Q64》来了解。若您使用的实
验板FLASH 的型号或控制引脚不一样,只需根据提供的工程修改即可,程序的控制原理相
同。 

三、软件设计

3.1 编程要点

  1. 初始化通讯使用的目标引脚以及端口时钟
  2. 使能SPI外设时钟
  3. 配置SPI的外设模式、地址、速率等参数并使能SPI外设。
  4.  编写基本SPI 按字节收发的函数;
  5. 编写对FLASH 擦除及读写操作的的函数;
  6. 编写测试程序,对读写数据进行校验。

3.2. 代码分析

SPI 硬件相关宏定义,把SPI 硬件相关的配置都以宏的形式定义到 “bsp_spi_ flash.h”文件中

先上传个初版的程序,功能我测了,亲测可用

bsp_spi_ flash.h如下:

 #ifndef __SPI_FLASH_H
#define	__SPI_FLASH_H


#include "stm32f10x.h"


/**********************************SPI参数定义**********************************/
#define             FLASH_SPIx                                SPI1
#define             FLASH_SPI_APBxClock_FUN                   RCC_APB2PeriphClockCmd
#define             FLASH_SPI_CLK                             RCC_APB2Periph_SPI1
#define             FLASH_SPI_GPIO_APBxClock_FUN              RCC_APB2PeriphClockCmd
#define             FLASH_SPI_GPIO_CLK                        RCC_APB2Periph_GPIOA

#define             FLASH_SPI_SCK_PORT                        GPIOA   
#define             FLASH_SPI_SCK_PIN                         GPIO_Pin_5

#define             FLASH_SPI_MISO_PORT                       GPIOA 
#define             FLASH_SPI_MISO_PIN                        GPIO_Pin_6

#define             FLASH_SPI_MOSI_PORT                       GPIOA 
#define             FLASH_SPI_MOSI_PIN                        GPIO_Pin_7

#define             FLASH_SPI_CS_PORT                       	GPIOA 
#define             FLASH_SPI_CS_PIN                        	GPIO_Pin_4

/*CS 引脚配置*/
#define							FLASH_SPI_CS_HIGH													GPIO_SetBits(FLASH_SPI_CS_PORT,FLASH_SPI_CS_PIN);								
#define							FLASH_SPI_CS_LOW													GPIO_ResetBits(FLASH_SPI_CS_PORT,FLASH_SPI_CS_PIN);								


/*等待超时时间*/
#define SPIT_FLAG_TIMEOUT         ((uint32_t)0x1000)
#define SPIT_LONG_TIMEOUT         ((uint32_t)(10 * SPIT_FLAG_TIMEOUT))


/*信息输出*/
#define FLASH_DEBUG_ON         0

#define FLASH_INFO(fmt,arg...)           printf("<<-FLASH-INFO->> "fmt"\n",##arg)
#define FLASH_ERROR(fmt,arg...)          printf("<<-FLASH-ERROR->> "fmt"\n",##arg)
#define FLASH_DEBUG(fmt,arg...)          do{\
                                          if(FLASH_DEBUG_ON)\
                                          printf("<<-FLASH-DEBUG->> [%d]"fmt"\n",__LINE__, ##arg);\
                                          }while(0)

#define DUMMY									0x00
#define READ_JEDEC_ID					0x9f
#define EARSE_SECTOR					0x20																					
#define READ_STATUE 					0x05
#define READ_DATA							0x03
#define WRITE_ENABLE					0x06
#define WRITE_DATA						0x02
																					
																					
void SPI_FLASH_Init(void)	;	
uint32_t SPI_Read_ID(void);	
void SPI_Erase_Sector(uint32_t addr);		
void SPI_WaitForWriteEnd(void );	
void SPI_Read_data(uint32_t addr,uint8_t *readBuff,uint32_t numByteToRead);																					
void SPI_Write_data(uint32_t addr,uint8_t *writeBuff,uint32_t numByteToWrite);																			

#endif /* __SPI_FLASH_H */

bsp_spi_flash.c

/**
  ******************************************************************************
  * @file    bsp_i2c_ee.c
  * @author  STMicroelectronics
  * @version V1.0
  * @date    2013-xx-xx
  * @brief   SPI FLASH(W25Q64)应用函数bsp
  ******************************************************************************
  * @attention
  ******************************************************************************
  */ 

#include "./flash/bsp_spi_flash.h"
#include "./usart/bsp_usart.h"		




uint16_t EEPROM_ADDRESS;

static __IO uint32_t  SPITimeout = SPIT_FLAG_TIMEOUT;   


static uint32_t SPI_TIMEOUT_UserCallback(uint8_t errorCode);


/**
  * @brief  SPI I/O配置
  * @param  无
  * @retval 无
  */
static void SPI_GPIO_Config(void)
{
  GPIO_InitTypeDef  GPIO_InitStructure; 

	/* 使能与 SPI 有关的时钟 */
	FLASH_SPI_APBxClock_FUN ( RCC_APB2Periph_SPI1, ENABLE );
	FLASH_SPI_GPIO_APBxClock_FUN ( RCC_APB2Periph_GPIOA, ENABLE );
	
    
  /* MISO、MOSI、SCK*/
  GPIO_InitStructure.GPIO_Pin = FLASH_SPI_SCK_PIN;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	       // 推挽复用输出
  GPIO_Init(FLASH_SPI_SCK_PORT, &GPIO_InitStructure);
		
	GPIO_InitStructure.GPIO_Pin = FLASH_SPI_MOSI_PIN;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	       // 推挽复用输出
  GPIO_Init(FLASH_SPI_MOSI_PORT, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Pin = FLASH_SPI_MISO_PIN;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;	 // 浮空输入或带上拉输入
  GPIO_Init(FLASH_SPI_MISO_PORT, &GPIO_InitStructure);
	
	/* 初始化CS引脚,使用软件控制,所以直接设置成推挽输出*/
	GPIO_InitStructure.GPIO_Pin = FLASH_SPI_CS_PIN;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;			//推挽输出	       
  GPIO_Init(FLASH_SPI_CS_PORT, &GPIO_InitStructure);
		
	FLASH_SPI_CS_HIGH;
	
}


/**
  * @brief  SPI 工作模式配置
  * @param  无
  * @retval 无
  */
static void SPI_Mode_Configu(void)
{
  SPI_InitTypeDef  SPI_InitStructure; 
	
	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2 ;//波特率最高支持80M  stm2分配也才36M
	SPI_InitStructure.SPI_CPHA = 	SPI_CPHA_2Edge;											 //模式0 和模式3都可以,此处配置成模式3  俩都配置成1
	SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
	SPI_InitStructure.SPI_CRCPolynomial = 0;			//不使用CRC校验,数值随便写
	SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
	SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;//双线全双工
	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;//MSB先行
	SPI_InitStructure.SPI_Mode = SPI_Mode_Master ;	
	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
	
	SPI_Init(SPI1, &SPI_InitStructure);//写入配置寄存器
	SPI_Cmd(SPI1,ENABLE);
	
  
}


/**
  * @brief  SPI 外设(FLASH)初始化
  * @param  无
  * @retval 无
  */
void SPI_FLASH_Init(void)
{

  SPI_GPIO_Config(); 
 
  SPI_Mode_Configu();


}

/*向flash 发送并接收一个字节  发送接收是同步的*/
/* 想要发送或者接收一个字节的时候,都可以直接调用这个函数*/
uint8_t SPI_FLASH_Send_Byte(uint8_t data)
{
	
	SPITimeout = SPIT_FLAG_TIMEOUT;
	
	//检查并等待至TX缓冲区为空
	while(SPI_I2S_GetFlagStatus( SPI1, SPI_I2S_FLAG_TXE) == RESET)
	{
		if( 0 == (SPITimeout--)) return SPI_TIMEOUT_UserCallback(0);
	}
		
	//程序执行到此处,TX缓冲区为空
	SPI_I2S_SendData (SPI1,  data) ;
	
	
	/* 什么时候能确认已经发送完毕呢 检测一个标志8位数据都已经发送完了 */
	//检查并等待RX缓冲区非空 
	SPITimeout = SPIT_FLAG_TIMEOUT;
	while(SPI_I2S_GetFlagStatus( SPI1, SPI_I2S_FLAG_RXNE) == RESET)
	{
		if( 0 == (SPITimeout--)) return SPI_TIMEOUT_UserCallback(1);
	}
	
	//程序执行到此处,说明数据发送完毕,并接收到一字 字节
	return SPI_I2S_ReceiveData( SPI1 ) ;
	
}

//接收数据会忽略发送线上的数据
uint8_t SPI_FLASH_Read_Byte(void)
{
	return SPI_FLASH_Send_Byte(DUMMY);
}


//读取ID号
uint32_t SPI_Read_ID(void)
{
	uint32_t flash_id;
	//片选使能
	FLASH_SPI_CS_LOW;
	SPI_FLASH_Send_Byte(READ_JEDEC_ID);
	
	flash_id = SPI_FLASH_Send_Byte(DUMMY);
	
	flash_id <<= 8;
	
	flash_id |= SPI_FLASH_Send_Byte(DUMMY);
	
	flash_id <<= 8;
	
	flash_id |= SPI_FLASH_Send_Byte(DUMMY);
	
	FLASH_SPI_CS_HIGH;
	
	return flash_id;
}

//FLASH写入使能
void SPI_Write_Enable(void)
{
	FLASH_SPI_CS_LOW;
	SPI_FLASH_Send_Byte(WRITE_ENABLE);
	FLASH_SPI_CS_HIGH;
}


//擦除FLASH指定扇区
void SPI_Erase_Sector(uint32_t addr)
{
	SPI_Write_Enable();
	
	FLASH_SPI_CS_LOW;
	SPI_FLASH_Send_Byte(EARSE_SECTOR);
	
	SPI_FLASH_Send_Byte((addr>>16)&0xff);
	
	SPI_FLASH_Send_Byte((addr>>8)&0xff);
	
	SPI_FLASH_Send_Byte(addr&0xff);
	
	FLASH_SPI_CS_HIGH;
	
	SPI_WaitForWriteEnd();//擦除和写入都是需要等待内部时序的
}

//读取FLASH的指定扇区
void SPI_Read_data(uint32_t addr,uint8_t *readBuff,uint32_t numByteToRead)
{
	FLASH_SPI_CS_LOW;
	SPI_FLASH_Send_Byte(READ_DATA);
	
	SPI_FLASH_Send_Byte((addr>>16)&0xff);
	
	SPI_FLASH_Send_Byte((addr>>8)&0xff);
	
	SPI_FLASH_Send_Byte(addr&0xff);
	
	while(numByteToRead--)
	{
		*readBuff = SPI_FLASH_Send_Byte(DUMMY);
		readBuff++;
	}
	
	FLASH_SPI_CS_HIGH;
}

//向FLASH写入内容 最多之前写256 写之前必须保证写使能
void SPI_Write_data(uint32_t addr,uint8_t *writeBuff,uint32_t numByteToWrite)
{
	SPI_Write_Enable();
	
	FLASH_SPI_CS_LOW;
	SPI_FLASH_Send_Byte(WRITE_DATA);
	
	SPI_FLASH_Send_Byte((addr>>16)&0xff);
	
	SPI_FLASH_Send_Byte((addr>>8)&0xff);
	
	SPI_FLASH_Send_Byte(addr&0xff);
	
	while(numByteToWrite--)
	{
		SPI_FLASH_Send_Byte(*writeBuff);
		writeBuff++;
	}
	
	FLASH_SPI_CS_HIGH;
	
	SPI_WaitForWriteEnd();//擦除和写入都是需要等待内部时序的
}



//等待FLASH 内部时序操作完成
void SPI_WaitForWriteEnd(void )
{
	uint8_t status_reg = 0;
	
	FLASH_SPI_CS_LOW;
	SPI_FLASH_Send_Byte(READ_STATUE);
	
	do{
	
	status_reg = SPI_FLASH_Send_Byte(DUMMY);
	}
	while((status_reg & 0x01) == 1);	
	
	FLASH_SPI_CS_HIGH;
}

/**
  * @brief  Basic management of the timeout situation.
  * @param  errorCode:错误代码,可以用来定位是哪个环节出错.
  * @retval 返回0,表示SPI读取失败.
  */
static  uint32_t SPI_TIMEOUT_UserCallback(uint8_t errorCode)
{
  /* Block communication and all processes */
  FLASH_ERROR("SPI 等待超时!errorCode = %d",errorCode);
  
  return 0;
}
/*********************************************END OF FILE**********************/

main.c

/**
  ******************************************************************************
  * @file    main.c
  * @author  fire
  * @version V1.0
  * @date    2013-xx-xx
  * @brief   SPI FLASH(W25Q64)测试,测试信息通过USART1打印在电脑的超级终端
  ******************************************************************************
  * @attention
  ******************************************************************************
  */
  
#include "stm32f10x.h"
#include "./led/bsp_led.h"
#include "./usart/bsp_usart.h"
#include "./flash/bsp_spi_flash.h"
#include <string.h>


uint8_t readBuff[4096] = {0};//定义成全局变量  就不会存储在栈空间了  而是内存上
uint8_t writeBuff[4096] = {0};


/**
  * @brief  主函数
  * @param  无
  * @retval 无
  */
int main(void)
{ 
	uint32_t id;
	uint16_t i = 0;
	
	
  LED_GPIO_Config();
  
  LED_BLUE;
  /* 串口初始化 */
	USART_Config();
	
	printf("\r\n 这是一个SPI-FLASH读写测试例程 \r\n");
	
	SPI_FLASH_Init();
	
	id = SPI_Read_ID();
	
	printf("\r\n id =0x%x \r\n",id);
	
	//如果你的flash本身就是干净的,那么就没办法通过此方法校验了
	SPI_Erase_Sector(0);
	for(i = 0;i < 4096;i++)
	{
		writeBuff[i] = i;
	}
	//假如不擦除 直接写的话,就会把1保留 0的一些地方会改为1
	SPI_Write_data(0,writeBuff,256);
	
	SPI_Read_data(0,readBuff,4096);
	
	for(i = 0;i < 4096 ;i++)
	{
			printf("0x%x ",readBuff[i]);
			if(i%10 == 0)
				printf("\r\n");
	}
	 
	
	//确认测试完ID之后  再去写后面的擦除/读写FLASH 不然后面调试起来很麻烦的

	
  
  while (1)
  {      
  }
}

/*********************************************END OF FILE**********************/

初版代码就先这样啦,有需要的同学可以留言或者私信我~懒得上传工程,如果哪天上传了,我会到时候把链接附在这的~这个章节就暂时先告一段落啦~

  • 15
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

I am Supreme

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值