历史上的今天
返回首页

历史上的今天

今天是:2025年01月09日(星期四)

正在发生

2019年01月09日 | SPI专题(二)——STM32驱动FLASH(W25Q64)

2019-01-09 来源:eefocus

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 

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


推荐阅读

史海拾趣

Ametek公司的发展小趣事

Ametek公司成立于1930年,总部位于美国宾夕法尼亚州伯温。它是一家跨国公司,涉足电子仪器和电子设备的设计、制造和销售。以下是关于Ametek公司发展的五个相关故事:

  1. 初期成立与增长:Ametek最初成立为一个机械仪器制造商,专注于生产电动车和电动车零部件。随着时间的推移,公司逐渐扩展业务,开始涉足电子仪器和设备的制造,并迅速取得了成长。

  2. 收购与多元化发展:Ametek通过一系列收购和合并扩大了业务范围。这些收购使得公司能够进入新的市场领域,并获得新的技术和产品线。例如,1998年Ametek收购了Programmable Power,进入了电源和电源测试设备市场。这种多元化战略为公司带来了更广泛的客户群和收入来源。

  3. 技术创新和研发投入:Ametek不断投入研发,并推出了一系列技术创新产品。公司在测量、测试和分析技术方面取得了重大突破,为客户提供高质量的解决方案。Ametek的产品包括精密仪器、检测设备、传感器等,广泛应用于航空航天、能源、制造业等领域。

  4. 全球扩张与市场布局:Ametek通过建立全球销售网络和设施,实现了全球市场的扩张和布局。公司在北美、欧洲、亚洲等地设有多个生产基地和销售办事处,为客户提供及时的服务和支持。这种全球化战略使得Ametek能够更好地满足不同地区客户的需求,并加强了在全球市场的竞争力。

  5. 持续增长与业绩表现:Ametek持续实现了稳健的增长和良好的业绩表现。公司不断推出新产品、拓展市场,并通过提高生产效率和降低成本等措施提升盈利能力。Ametek的业绩稳步增长,为股东创造了可观的价值,并使得公司成为电子仪器和设备行业的领先企业之一。

这些故事展示了Ametek公司从成立初期到如今在收购与多元化发展、技术创新与研发投入、全球扩张与市场布局以及持续增长与业绩表现等方面取得的重要进展。

DACHANG公司的发展小趣事

DACHANG公司深知创新是企业发展的根本动力。因此,公司始终将创新作为自己的核心竞争力,不断推出具有创新性的产品和解决方案。无论是产品设计、技术研发还是市场营销,DACHANG公司都积极引入新的理念和模式,为公司的发展注入了源源不断的活力。

APEM公司的发展小趣事

为了进一步拓展北美市场,APEM在1991年收购了位于美国波士顿的American Switch Corp.,并成立了子公司APEM COMPONENTS Inc.。这一收购使APEM得以在北美市场建立稳固的地位,为其全球扩张提供了有力支持。

GE Industrial Solutions公司的发展小趣事
使用高压测试仪测量输入输出之间的隔离电压,确保满足安全要求。
Cyrix Corp公司的发展小趣事

在被VIA收购后的一段时间里,Cyrix继续寻求市场扩张的机会。例如,在1998年,Cyrix与北京大船电子技术公司签订了总代理协议,以扩大其在中国的分销网络。这一策略帮助Cyrix进一步打开了中国市场的大门,为其在全球市场的发展奠定了基础。

以上五个故事描绘了Cyrix Corp公司在电子行业中的发展历程。从初创时期的挑战英特尔到被多次收购后的市场策略调整和市场扩张尝试,Cyrix的发展之路充满了曲折和变革。

Advanced Ceramic X Corporation公司的发展小趣事

在被VIA收购后的一段时间里,Cyrix继续寻求市场扩张的机会。例如,在1998年,Cyrix与北京大船电子技术公司签订了总代理协议,以扩大其在中国的分销网络。这一策略帮助Cyrix进一步打开了中国市场的大门,为其在全球市场的发展奠定了基础。

以上五个故事描绘了Cyrix Corp公司在电子行业中的发展历程。从初创时期的挑战英特尔到被多次收购后的市场策略调整和市场扩张尝试,Cyrix的发展之路充满了曲折和变革。

问答坊 | AI 解惑

程控三相精密线性功率源的设计

  摘要:针对某些领域对三相程控高精度电源的需求,提出了一种三相功率源的设计方法。该方法主要利用单片EPROM实现三相稳定信号源的产生,采用同步跟随电源技术进行线性功率放大,采用功率合成技术实现大功率输出。测试结果表明该功率源波形失 ...…

查看全部问答>

威盛694XB公版主板电路图(INTEL810E)

威盛694XB公版主板电路图,需要的朋友看看吧.... 威胜的那套图被黑客给黑掉了,我也没有了.换一套INTEL810E的吧.再过几天我会陆续在上一些PC主板的图的. [ 本帖最后由 西门 于 2009-5-15 22:32 编辑 ]…

查看全部问答>

【新手指南】论坛帮助帖汇总

最近抽空整理了一些论坛的帮助帖,希望能对大家有帮助,不断完善中........ 大家多提意见,多问问题,我会帮大家找到答案并在帖子中逐步完善!                   一、 &nbs ...…

查看全部问答>

wince5.0怎样才能收到蓝牙耳机按键消息?

修改C:\\WINCE500\\PRIVATE\\WINCEOS\\COMM\\BLUETOOTH里面的代码,进行全编译都编不进去 有什么办法可以截获蓝牙耳机按键消息吗?…

查看全部问答>

高手进来指导下。我用C语言写了两天的日历时钟,有闹钟功能、计时功能。

#include #include #define UCHAR unsigned char #define UINT unsigned int void init(); void delay(UINT); void displayWeek(UCHAR week); void displayTime(UCHAR first, UCHAR second); void writeCmd(UCHAR cmd); void writeData(UCHAR dat) ...…

查看全部问答>

请问AT91RM9200的网络DMA

它的EMC的传输方式是不是就称为DMA?  还是说他另外有一种DMA的传输方式可以选择?…

查看全部问答>

帮个忙!!

本人搞软件的,会几门编程语言。比较熟悉的有vb、vc++,现在想往单片机方面靠靠。以前上学时学过单片机,汇编语言,计算机组成等,对单片机还特意学了一段时间(都是理论的,没实践过),毕业后因为工作的事就在也没接触过。现在想学的话从什么地方 ...…

查看全部问答>

段寄存器左移四位后,前四位不是溢出了吗?

8086微处理器课程中,在介绍存储器20位地址的形成时,说16位段寄存器中数据左移四位后再与偏移值相加,那么左移四位后前四位不是溢出了吗?如原来是ABCDH,左移后为BCD0H,不会为ABCD0H啊?…

查看全部问答>

cyclone2 FPGA 开发板转让

  [ 本帖最后由 wenhuawu 于 2011-6-3 10:57 编辑 ]…

查看全部问答>