datasheet

SPI专题(二)——STM32驱动FLASH(W25Q64)

2019-01-09来源: eefocus关键字:SPI  STM32  驱动FLASH  W25Q64

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协议定义了通讯的起始和停止信号、数据有效性、时钟同步等环节。


这里写图片描述


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


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


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


2.数据有效性


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


观察图中的2345标号处,MOSI及 MISO的数据在 SCK的上升沿期间变化输出,在SCK 的下降沿时被采样。即在 SCK 的下降沿时刻,MOSI 及 MISO 的数据有效,高电平时表示数据“1”,为低电平时表示数据“0”。在其它时刻,数据无效,MOSI及MISO为下一次表示数据做准备。SPI每次数据传输可以 8 位或 16 位为单位,每次传输的单位数不受限制。


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



    GPIO_InitTypeDef GPIO_InitStructure;


    RCC_APB2PeriphClockCmd( RCC_APB2P%riphGPHOA|RCC_APB2Periph_SPI1%r52C%521ENABLE ); 


    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

    GPIO_Init(GPIOA, &GPIO_InitStructure);


    GPIO_SetBits(GPIOA,GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7);


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_InitTypeDef  SPI_InitStructure;


SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;  //设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工

    SPI_InitStructure.SPI_Mode = SPI_Mode_Master;       //设置SPI工作模式:设置为主SPI

    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;       //设置SPI的数据大小:SPI发送接收8位帧结构

    SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;     //选择了串行时钟的稳态:时钟悬空高

    SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;    //数据捕获(采样)于第二个时钟沿

    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;       //NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制

    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;        //定义波特率预分频的值:波特率预分频值为256

    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;  //指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始

    SPI_InitStructure.SPI_CRCPolynomial = 7;    //CRC值计算的多项式

    SPI_Init(SPI1, &SPI_InitStructure);  //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器


2.3使能 SPI1

初始化完成之后使能 SPI1 通信,在使能 SPI1 之后,就可以开始 SPI 通讯了。使能 SPI1 的方法为:


SPI_Cmd(SPI1, ENABLE); //使能 SPI 外设


2.4 SPI 传输数据

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


void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data);


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


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


uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx) ;


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


2.5查看 SPI 传输状态

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


SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE);


3.软件设计

3.1 SPI实现


SPI_InitTypeDef  SPI_InitStructure;


void SPI1_Init(void)

{

    GPIO_InitTypeDef GPIO_InitStructure;


    RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA|RCC_APB2Periph_SPI1, ENABLE ); 


    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

    GPIO_Init(GPIOA, &GPIO_InitStructure);


    GPIO_SetBits(GPIOA,GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7);


    SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;  //设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工

    SPI_InitStructure.SPI_Mode = SPI_Mode_Master;       //设置SPI工作模式:设置为主SPI

    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;       //设置SPI的数据大小:SPI发送接收8位帧结构

    SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;     //选择了串行时钟的稳态:时钟悬空高

    SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;    //数据捕获(采样)于第二个时钟沿

    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;       //NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制

    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;        //定义波特率预分频的值:波特率预分频值为256

    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;  //指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始

    SPI_InitStructure.SPI_CRCPolynomial = 7;    //CRC值计算的多项式

    SPI_Init(SPI1, &SPI_InitStructure);  //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器


    SPI_Cmd(SPI1, ENABLE); //使能SPI外设


    SPI1_ReadWriteByte(0xff);//启动传输      

}   

/************************************************/

//SPI 速度设置函数

//SpeedSet:

//SPI_BaudRatePrescaler_2   2分频   (SPI 36M@sys 72M)

//SPI_BaudRatePrescaler_8   8分频   (SPI 9M@sys 72M)

//SPI_BaudRatePrescaler_16  16分频  (SPI 4.5M@sys 72M)

//SPI_BaudRatePrescaler_256 256分频 (SPI 281.25K@sys 72M)


void SPI1_SetSpeed(u8 SpeedSet)

{

    SPI_InitStructure.SPI_BaudRatePrescaler = SpeedSet ;

    SPI_Init(SPI1, &SPI_InitStructure);

    SPI_Cmd(SPI1,ENABLE);


/************************************************/

//SPIx 读写一个字节

//TxData:要写入的字节

//返回值:读取到的字节

u8 SPI1_ReadWriteByte(u8 TxData)

{       

    u8 retry=0;                 

    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET) //检查指定的SPI标志位设置与否:发送缓存空标志位

        {

        retry++;

        if(retry>200)return 0;

        }             

    SPI_I2S_SendData(SPI1, TxData); //通过外设SPIx发送一个数据

    retry=0;


    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET)//检查指定的SPI标志位设置与否:接受缓存非空标志位

        {

        retry++;

        if(retry>200)return 0;

        }                               

    return SPI_I2S_ReceiveData(SPI1); //返回通过SPIx最近接收的数据                     

}


3.2 Flash读写


u16 SPI_FLASH_TYPE=W25Q64;//默认就是25Q64

//4Kbytes为一个Sector

//16个扇区为1个Block

//W25X16

//容量为2M字节,共有32个Block,512个Sector 


//初始化SPI FLASH的IO口

void SPI_Flash_Init(void)

{


    GPIO_InitTypeDef GPIO_InitStructure;


  RCC_APB2PeriphClockCmd(   RCC_APB2Periph_GPIOA, ENABLE );


    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4;  //SPI CS

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;  //复用推挽输出

    GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;

    GPIO_Init(GPIOA, &GPIO_InitStructure);

    GPIO_SetBits(GPIOA,GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4);

    SPI1_Init();           //初始化SPI

    SPI1_SetSpeed(SPI_BaudRatePrescaler_4); //设置为18M时钟,高速模式

    SPI_FLASH_TYPE=SPI_Flash_ReadID();//读取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 SPI_Flash_ReadSR(void)   

{  

    u8 byte=0;   

    SPI_FLASH_CS=0;                            //使能器件   

    SPI1_ReadWriteByte(W25X_ReadStatusReg);    //发送读取状态寄存器命令    

    byte=SPI1_ReadWriteByte(0Xff);             //读取一个字节  

    SPI_FLASH_CS=1;                            //取消片选     

    return byte;   


/************************************************/

//写SPI_FLASH状态寄存器

//只有SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2)可以写!!!

void SPI_FLASH_Write_SR(u8 sr)   

{   

    SPI_FLASH_CS=0;                            //使能器件   

    SPI1_ReadWriteByte(W25X_WriteStatusReg);   //发送写取状态寄存器命令    

    SPI1_ReadWriteByte(sr);               //写入一个字节  

    SPI_FLASH_CS=1;                            //取消片选             

}   


/************************************************/

//SPI_FLASH写使能  

//将WEL置位   

void SPI_FLASH_Write_Enable(void)   

{

    SPI_FLASH_CS=0;                            //使能器件   

    SPI1_ReadWriteByte(W25X_WriteEnable);      //发送写使能  

    SPI_FLASH_CS=1;                            //取消片选             


/************************************************/

//SPI_FLASH写禁止  

//将WEL清零  

void SPI_FLASH_Write_Disable(void)   

{  

    SPI_FLASH_CS=0;                            //使能器件   

    SPI1_ReadWriteByte(W25X_WriteDisable);     //发送写禁止指令    

    SPI_FLASH_CS=1;                            //取消片选             

}               


/************************************************/

//读取芯片ID W25X16的ID:0XEF14

u16 SPI_Flash_ReadID(void)

{

    u16 Temp = 0;     

    SPI_FLASH_CS=0;                 

    SPI1_ReadWriteByte(0x90);//发送读取ID命令     

    SPI1_ReadWriteByte(0x00);       

    SPI1_ReadWriteByte(0x00);       

    SPI1_ReadWriteByte(0x00);                  

    Temp|=SPI1_ReadWriteByte(0xFF)<<8;  

    Temp|=SPI1_ReadWriteByte(0xFF);  

    SPI_FLASH_CS=1;                 

    return Temp;

}               


/************************************************/

//读取SPI FLASH  

//在指定地址开始读取指定长度的数据

//pBuffer:数据存储区

//ReadAddr:开始读取的地址(24bit)

//NumByteToRead:要读取的字节数(最大65535)

void SPI_Flash_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead)   

    u16 i;                                                      

    SPI_FLASH_CS=0;                            //使能器件   

    SPI1_ReadWriteByte(W25X_ReadData);         //发送读取命令   

    SPI1_ReadWriteByte((u8)((ReadAddr)>>16));  //发送24bit地址    

    SPI1_ReadWriteByte((u8)((ReadAddr)>>8));   

    SPI1_ReadWriteByte((u8)ReadAddr);   

    for(i=0;i

    { 

        pBuffer[i]=SPI1_ReadWriteByte(0XFF);   //循环读数  

    }

    SPI_FLASH_CS=1;                            //取消片选             

}  


/************************************************/

//SPI在一页(0~65535)内写入少于256个字节的数据

//在指定地址开始写入最大256字节的数据

//pBuffer:数据存储区

//WriteAddr:开始写入的地址(24bit)

//NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!!   

void SPI_Flash_Write_Page(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)

{

    u16 i;  

    SPI_FLASH_Write_Enable();                  //SET WEL 

    SPI_FLASH_CS=0;                            //使能器件   

    SPI1_ReadWriteByte(W25X_PageProgram);      //发送写页命令   

    SPI1_ReadWriteByte((u8)((WriteAddr)>>16)); //发送24bit地址    

    SPI1_ReadWriteByte((u8)((WriteAddr)>>8));   

    SPI1_ReadWriteByte((u8)WriteAddr);   

    for(i=0;i

        {

            SPI1_ReadWriteByte(pBuffer[i]);//循环写数  

        }

    SPI_FLASH_CS=1;                            //取消片选 

    SPI_Flash_Wait_Busy();                     //等待写入结束


/************************************************/

//无检验写SPI FLASH 

//必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!

//具有自动换页功能 

//在指定地址开始写入指定长度的数据,但是要确保地址不越界!

//pBuffer:数据存储区

//WriteAddr:开始写入的地址(24bit)

//NumByteToWrite:要写入的字节数(最大65535)

//CHECK OK

void SPI_Flash_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)   

{                    

    u16 pageremain;    

    pageremain=256-WriteAddr%256; //单页剩余的字节数                

    if(NumByteToWrite<=pageremain)

        pageremain=NumByteToWrite;//不大于256个字节

    while(1)

    {      

        SPI_Flash_Write_Page(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 SPI_Flash_Write(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) 

    {   

        SPI_Flash_Read(SPI_FLASH_BUF,secpos*4096,4096);//读出整个扇区的内容

        for(i=0;i

        {

            if(SPI_FLASH_BUF[secoff+i]!=0XFF)break;//需要擦除     

        }

        if(i

        {

            SPI_Flash_Erase_Sector(secpos);//擦除这个扇区

            for(i=0;i

            {

                SPI_FLASH_BUF[i+secoff]=pBuffer[i];   

            }

            SPI_Flash_Write_NoCheck(SPI_FLASH_BUF,secpos*4096,4096);//写入整个扇区  


        }

        else 

            SPI_Flash_Write_NoCheck(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 SPI_Flash_Erase_Chip(void)   

{                                             

    SPI_FLASH_Write_Enable();                  //SET WEL 

    SPI_Flash_Wait_Busy();   

    SPI_FLASH_CS=0;                            //使能器件   

    SPI1_ReadWriteByte(W25X_ChipErase);        //发送片擦除命令  

    SPI_FLASH_CS=1;                            //取消片选             

    SPI_Flash_Wait_Busy();                     //等待芯片擦除结束

}   


/************************************************/

//擦除一个扇区

//Dst_Addr:扇区地址 0~511 for w25x16

//擦除一个山区的最少时间:150ms

void SPI_Flash_Erase_Sector(u32 Dst_Addr)   

{   

    Dst_Addr*=4096;

    SPI_FLASH_Write_Enable();                  //SET WEL     

    SPI_Flash_Wait_Busy();   

    SPI_FLASH_CS=0;                            //使能器件   

    SPI1_ReadWriteByte(W25X_SectorErase);      //发送扇区擦除指令 

    SPI1_ReadWriteByte((u8)((Dst_Addr)>>16));  //发送24bit地址    

    SPI1_ReadWriteByte((u8)((Dst_Addr)>>8));   

    SPI1_ReadWriteByte((u8)Dst_Addr);  

    SPI_FLASH_CS=1;                            //取消片选             

    SPI_Flash_Wait_Busy();                     //等待擦除完成

}  


/************************************************/

//等待空闲

void SPI_Flash_Wait_Busy(void)   

{   

    while ((SPI_Flash_ReadSR()&0x01)==0x01);   // 等待BUSY位清空

}  


/************************************************/

//进入掉电模式

void SPI_Flash_PowerDown(void)   

    SPI_FLASH_CS=0;                            //使能器件   

    SPI1_ReadWriteByte(W25X_PowerDown);        //发送掉电命令  

    SPI_FLASH_CS=1;                            //取消片选             

    delay_us(3);                               //等待TPD  

}   


/************************************************/

//唤醒

void SPI_Flash_WAKEUP(void)   

{  

    SPI_FLASH_CS=0;                            //使能器件   

    SPI1_ReadWriteByte(W25X_ReleasePowerDown);   //  send W25X_PowerDown command 0xAB    

      SPI_FLASH_CS=1;                            //取消片选               

    delay_us(3);                               //等待TRES1

}   



参考:

1.原子库函数手册


2.SPI—读写串行 FLASH

--------------------- 

作者:wwt18811707971 

来源:CSDN 

原文:https://blog.csdn.net/wwt18811707971/article/details/77756312 

版权声明:本文为博主原创文章,转载请附上博文链接!


关键字:SPI  STM32  驱动FLASH  W25Q64

编辑:什么鱼 引用地址:http://www.eeworld.com.cn/mcu/2019/ic-news010942855.html
本网站转载的所有的文章、图片、音频视频文件等资料的版权归版权所有人所有,本站采用的非本站原创文章及图片等内容无法一一联系确认版权者。如果本网所选内容的文章作者及编辑认为其作品不宜公开自由传播,或不应无偿使用,请及时通过电子邮件或电话通知我们,以迅速采取适当措施,避免给双方造成不必要的经济损失。

上一篇:STM32学习笔记一一输入捕获
下一篇:IIC专题(二)——STM32驱动AT24C02

关注eeworld公众号 快捷获取更多信息
关注eeworld公众号
快捷获取更多信息
关注eeworld服务号 享受更多官方福利
关注eeworld服务号
享受更多官方福利

推荐阅读

STM32F103 SPI3总结

最近用STM32F013RCT6调试NRF24L01模块,总共三路SPI,连接3个2.4G模块。SPI1和SPI2没有问题,check能通过。相同的方法配置SPI3,check不能通过。检查多遍管脚配置和接线线序,都没有问题,由于SPI3管脚会复用JTAG仿真接口,所以把焦点集中在这一块,按照要求将   GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE)    添加到程序中,依旧不起作用。查手册发现当需要重新配置IO口时,需要开启AFIO时钟。所以配置时为如下指令:1、使能AFIO时钟:RCC_APB2PeriphClockCmd
发表于 2019-04-12

STM32之SPI库函数介绍

首先SPI的一些基本介绍参照相关芯片的库函数或者使用手册,以下介绍SPI库函数的基本用法和参数的定义。1.SPI_DeInit函数的功能是将外设SPIx寄存器重设为默认值。输入参数SPIx可以是1和2,是用来选择SPI外设的。2.SPI_Init函数的功能是根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器。SPI_InitDefType的结构体是定义在文件stm32f10x_spi.h里。typedef struct{u16 SPI_Direction;//用于设置SPI单向或者双向数据模式。u16 SPI_Mode;//用于设置SPI的工作模式;u16 SPI_DataSize;//用于设置SPI数据大小
发表于 2019-04-11

spi实验:接收电路与发送电路的自环测试

SPI简介:SPI,是英语Serial Peripheral Interface的缩写,顾名思义就是串行外围设备接口。SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线。SPI 规定了两个 SPI 设备之间通信必须由主设备 (Master) 来控制次设备 (Slave). 一个 Master 设备可以通过提供 Clock 以及对 Slave 设备进行片选 (Slave Select) 来控制多个 Slave 设备, SPI 协议还规定 Slave 设备的 Clock 由 Master 设备通过 SCK 管脚提供给 Slave 设备, Slave 设备本身不能产生或控制 Clock, 没有 Clock
发表于 2019-04-11
spi实验:接收电路与发送电路的自环测试

模拟SPI实现和调试流程

一般来说,所有的spi通信设备都可以使用模拟spi来实现,而且模拟spi的好处就是不需要针对每一款mcu去重新熟悉其spi控制器的配置,只要简单配置一下spi_clk、spi_cs、spi_mosi、spi_miso四个引脚的输入输出即可,具有很好的可移植性。下面我以stm32为例,简单讲解一下模拟spi的实现和调试流程,实例中spi以上升沿来进行收发数据1、首先先贴出代码    //初始化spi_clk、spi_cs、spi_mosi、spi_miso四个io    //spi_cs    GPIO_Initure.Pin=SIMULATE_SPI
发表于 2019-04-11

STM32之SPI通信

之前一直对SPI通信一知半解,所以想抽空把它搞得明白一些。考虑到之前是结合Flash芯片来学的,十分不直观,而且主要把时间和精力都花在Flash芯片的datasheet和驱动上了,SPI通信也没学好。所以这次就考虑用4位数码管显示模块,模块是直接买的现成的,如下图所示,这样可以简化操作,把精力聚焦到学习的核心–SPI通信本身上来。   该模块是用2片74HC595串联驱动的,一片用来控制数码管的位选(U1),一片用来控制数码管的段选(U2)。接口比较简单,总共5个引脚,2个引脚分别接VCC和GND,DIO用来接收串行数据的输入,SCLK用来接收同步时钟,每个SCLK上升沿74HC595内部的移位寄存器
发表于 2019-04-11
STM32之SPI通信

stm32 io模拟spi通信

;    unsigned char SPI_SendByte(unsigned char dt)  {      u8 i;      u8 temp=0;      for(i=8;i>0;i--)      {          if(dt&0x80)MOSI_H;         
发表于 2019-04-11

小广播

何立民专栏

单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号 电信业务审批[2006]字第258号函 京公海网安备110108001534 Copyright © 2005-2019 EEWORLD.com.cn, Inc. All rights reserved