zoukankan      html  css  js  c++  java
  • STM32-串行SPI nor

    源:FLASH 存储学习-串行SPI nor

    1.1 SST25VF080B简介1.1.1 主要特性

    关键点:容量、速度(时钟速度、读写速度)、功耗。

    l 容量:8MBit;

    l 最高SPI时钟频率:50MHz;

    l 低功耗模式下电流消耗:5uA,正常读模式电流:15mA;低功耗!采用不同的制造技术功耗要低很多。

    l 整片擦除:35ms;扇区/块擦除:18ms;字节编程:7us;整片擦除的速度要快很多!

    1.2 系统框图与电路1.2.1 系统框图

        关于内部存储矩阵的访问和存储结构同并行NorFlash的一致,只不过多了个串行接口,用于实现对串行数据的解码。

    f908cbd1-8f4c-427c-a120-10e2b99a36d4.jpg

    1.2.2 存储组织

    扇区大小4KB,块大小:32/64KB;页大小为字节或字。

    3b5ed3be-3709-4650-b240-65232320db6e.jpg

    1.2.3 引脚及封装

    7bfcb528-faec-4e6f-9fa1-b6d50d0d0155.jpg

    引脚说明:

    l SCK、SI、SO、CE – SPI接口控制线

    l WP# -- 用于使能状态寄存器中的BPL位,有效时只允许锁定BPL,而不允许解锁BPL。不是说明使能保护!BPL位用于锁定控制扇区保护的相应位。

    l HOLD# -- 用于暂停与SPI的通信,而不需要复位器件;

    1.2.4 典型电路

    使用STM32F103驱动该器件时,典型的电路如图所示。

    7d4ba3d8-0809-472c-89d5-9a429d05ba2d.jpg

    注意,WP为低,判定寄存器的锁定功能将启用。但不会太影响片内块的保护。

    1.3 保护机制

    b73d03bc-1ede-481c-b8b4-25ec0d21d2c4.jpg

      l 软件写保护:状态寄存器中的BP3—BP0、BPL提供片内块、状态寄存器的写保护。

      l 硬件保护:WP#引脚—低电平,用于锁定状态寄存位7—BPL。由表2,WP#为高时,可以执行状态寄存器写命令,可随时更改状态寄存器。为低时,只能将BPL置为1,而不能从1置为0,即置1后,状态寄存器将锁定不变。

    WP# àBPL、BP3—BP0

    BPLàBP3、BP0

    78dbe8b7-8266-451d-bbc7-ad997db56673.jpg

    1.4 编程接口1.4.1 状态寄存器

    状态寄存器用于用于获取FLASH的当前工作状态。

    ae4f1f2b-ed0a-4e13-87ed-4b4fe15971b5.jpg

    l BUSY位:指示是否正在编程或擦除操作;

    l WEL位:指示器件是否处理可写的状态。RESET状态(0)指示不可写,默认在上电、完成写操作后,器件自行返回到不可写状态,以保护器件不受意外的擦写。因而每次写FLASH前,都必须先清除(写1),以使能器件的写。软件可控写。

    l AAI位:指示器件是否处理地址自动增加模式或者是字节编程模式。

    l BPL位:用于控制BPX是否可写。

    l BPX位:用于控制保护块的范围,属于软件保护,扇区保护如下。

     

                                              916285ed-16ee-46e6-95bc-be7e6de3ca09.jpg

    这里的扇区保护比较简单,更为复杂的扇区保护机制可针对每个扇区进行保护。

    1.4.2 命令接口

    通信过程中,仅仅只有读ID、读数据、读状态寄存器需要在可保护CS不变而继续写数据。其它的则需要写完后接CS线。

    cf703fe9-2f56-4a34-a275-9e02b6f631a9.jpg

    1.5 通信时序1. 单字SPI通信时序

    无论SPI总线空闲时SCK为高或为低,保证在上升沿采样数据,下降沿输出数据。先传送高位,每次传送8位。

    5b5e4aa8-2827-4a9d-8aaa-698246c365b3.jpg

    2. 读命令

    81ce36f2-eaf9-48d8-a32f-4bdfd4e392ed.jpg

    3. 快速读模式

    有可能内部使用了缓冲模式,可在更高的时钟速度(50MHz)下读数据。

    57e6ffbf-2702-4255-ae0c-02e7507637ef.jpg

    4. 写使能

    该命令可设置状态寄存器中的WEL位,使得可执行擦除和编程命令。

    7d98eb1f-db2f-4297-b0a3-3c16de7bb647.jpg

    5. 写禁能

    1463099b-3294-4a4a-a981-432e4a795a3f.jpg

    6. 字节编程

    eea22af1-623c-4633-8ff8-238ae3bb3617.jpg

    7. AAI编程

    即自动地址增量的字编程,每个周期写1个字。

    bdd8b701-619a-4903-aae0-a5aba42b6eb1.jpg

    在最后,通过WRDI返回来正常模式。在每写完两个字后,需查询害怕状态。

    在写字的过程中,有三种方式检测是否完成字编程。其中硬件检测:读SO的状态。可在写AAI命令之前,通过命令配置SO口为RD/BY#状态。或者也可通过读取状态寄存器来检测是否完成写操作。

    ab211f0b-3e3e-443a-9b3c-7d30c8f7d942.jpg

    ee6ce252-01d8-4e12-8961-a163ddebb31a.jpg

    8. 4KB的扇区擦除

    ec41671d-eddc-4cbf-a1e7-f845e0d93f0c.jpg

    9. 32K块擦除

    438305ca-3e36-4a1e-a99b-160c87082fcd.jpg

    10. 64K块擦除

     

    11. 整片擦除

    2c68747f-b720-4391-9abf-fe0a120374f7.jpg

    12. 读状态寄存器

    347abe8c-28c5-46cf-a05d-92037c581eca.jpg

    13. 使能写状态寄存器和写状态寄存器

    两条命令必须连续写,不允许被打断?以避免意外写状态寄存器。

    l 当WP#为低电平时,BPL只能写1,不可写0.;此时若BPL位为高时,写状态寄存器命令将被忽略。即此时,状态寄存器只能被锁定(写1),而不能再解锁(写0)!锁定后将不能再任意改高保护方式,相当于将当前的保护方式给固定下来!

    l 而若WP#为高电平,BPL位失效,状态寄存器不再被锁定,此时BPL、BPX可被更改。BPL位可设置为1,也可设置为0。可任意更改保护方式。

    WP#的作用,锁定BPL为1。一旦BPL锁定为1,则BPX将不可再更改,即软件保护将被锁定。当WP#无效时,BPL可随时、任意更改,同时更改软件保护。

    8c131337-ba12-4b87-93e0-91974822a930.jpg

    14. JEDEC Read-ID

    获取SST制造商的ID和SST FLASH器件的ID。

    af1e3af4-0b89-4e77-a8e5-04c4c678d8c1.jpg

    1.6 驱动设计

    驱动框架如下图:

    93afb61e-a596-454f-aaec-33482e5ee30a.jpg

    算法编写原则:

    (1) 可以为每一种SPI Flash针对性的写一份驱动源码,但是当更换Flash时,需要修改的地方很多;当系统中有多个设备时,显示这不够用,因而最好的方法是实现面向对像的封装,将与Flash设备相关的信息封装在一个结构体内,具体的算法根据结构体中相关的数据来决定如何访问硬件,做到过程可以不依赖于实际的硬件;

    (2) 尽量按标准的初始化、读写、关闭、控制接口设计API,这样可统一抽像出相应的结构,也易于使用和理解。留给最终用户调用的API应该尽量的少和易于理解;

    (3) SPI Flash接口为SPI,操作方法与并行接口一致,但其扇区组织类似,从最大到最小区域分为芯片-块-扇区-页。编程算法则也是通过写命令序列的方式,如发送命令字-发送字节-发送数据-查询状态寄存器。保护方式是通过存储器中的一些非易失性的位置0或置1选择性地以扇区或块为保护单位。

    1.7 驱动代码

    // SST25VF080B驱动接口
    // By:lstzixing At ZLG
    // Date: 2011-1
    #include "STM32Lib\stm32f10x.h"
    #include "hal.h"
    
    typedef unsigned long uint32;
    typedef unsigned short uint16;
    typedef unsigned char uint8;
    
    #define SPI_FLASH_SIZE (1024*1024*2) // SPI Flash容量
    #define SPI_FLASH_OK 0 
    #define SPI_FLASH_ERR_PARA 3 // SPI Flash参数错误
    
    // SPI Flash擦除操作码
    #define SPI_FLASH_ERASE_CHIP 0 // 整片擦除
    #define SPI_FLASH_ERASE_SECTOR 1 // 扇区擦除
    #define SPI_FLASH_ERASE_BLOCK 2 // 块擦除
    
    // SPI状态寄存器和位
    #define SPI_FLASH_REG_BIT_BUSY (1 << 0)
    #define SPI_FLASH_REG_BIT_WEL (1 << 1)
    #define SPI_FLASH_REG_BIT_BPX (0xF << 2)
    #define SPI_FLASH_REG_BIT_AAI (1 << 6)
    #define SPI_FLASH_REG_BIT_BPL (1 << 7)
    
    #define SPIFlashSelect()         GPIO_ResetBits(GPIOC, GPIO_Pin_13) /* SST CS = L */ 
    #define SPIFlashDeSelect()         GPIO_SetBits(GPIOC, GPIO_Pin_13) /* SST CS = H */
    
    /*********************************************************************************************************
    ** Function name: SPIFlashInit
    ** Descriptions: 初始化SPI硬件,设置相关的GPIO口、SPI控制器
    ** Input parameters: none
    ** Output parameters: None
    ** Returned value: none
    *********************************************************************************************************/
    void SPIFlashInit(void)
    {
        // 打开SPI1和GPIO时钟 
        RCC->APB2ENR |= RCC_APB2ENR_SPI1EN |
                        RCC_APB2ENR_IOPAEN |
                        RCC_APB2ENR_IOPBEN |
                        RCC_APB2ENR_IOPCEN |
                        RCC_APB2ENR_IOPDEN |
                        RCC_APB2ENR_IOPEEN |
                        RCC_APB2ENR_IOPFEN;
    
        // PA5/6/7为复用模式, 50MHZ
        GPIOA->CRL &= ~(GPIO_CRL_CNF5 | GPIO_CRL_CNF6 | GPIO_CRL_CNF7 |
                        GPIO_CRL_MODE5 | GPIO_CRL_MODE6 | GPIO_CRL_MODE7);
        GPIOA->CRL |= GPIO_CRL_MODE5 | GPIO_CRL_MODE6 | GPIO_CRL_MODE7 |
                      GPIO_CRL_CNF5_1 | GPIO_CRL_CNF6_1 | GPIO_CRL_CNF7_1;
    
        // 配置PC.13为输出片选线
        GPIOC->CRH &= ~(GPIO_CRH_CNF13 | GPIO_CRH_MODE13);
        GPIOC->CRH |= GPIO_CRH_MODE13;
    
        // 配置SPI1,第一个时钟沿采样、主机、分频最大、使能、软件从机管理 
        SPI1->CR1 = //SPI_CR1_CPHA | 
                    SPI_CR1_MSTR |
                    //SPI_CR1_BR |
                    SPI_CR1_SSI | 
                    SPI_CR1_SPE |
                    SPI_CR1_SSM; 
    
        SPI1->CR2 = 0;
    
        SPIFlashDeSelect();
    }
    
    /*********************************************************************************************************
    ** Function name: SPIWriteReadByte
    ** Descriptions: 向SPI发送并读取一数据
    ** Input parameters: data 要发送的数据
    ** Output parameters: None
    ** Returned value: uint16 读取的数据
    *********************************************************************************************************/
    static uint16 SPIWriteReadByte(uint16 data)
    {
        // 发送一字节
        while((SPI1->SR & SPI_I2S_FLAG_TXE) == RESET);
        SPI1->DR = data;
    
        // 接收一字节
        while((SPI1->SR & SPI_I2S_FLAG_RXNE) == RESET);
        return(SPI1->DR);
    }
    
    /*********************************************************************************************************
    ** Function name: SPIFlashReadstatusReg
    ** Descriptions: 读状态寄存器
    ** Input parameters: none
    ** Output parameters: 当前状态寄存器的值
    ** BIT0 -- BUSY位,写忙标志 
    ** BIT1 -- WEL位,FLASH处于写保护状态
    ** BIT6[5..2]--BP [3..0],扇区保护位
    ** BIT6 -- 指示正在自动自境编程中
    ** BIT7 -- BPL,BPX的保护位
    ** Returned value: none
    *********************************************************************************************************/
    static uint8 SPIFlashReadstatusReg (void)
    {
        uint8 uByte;
    
        // 发送读状态寄存器命令
        SPIFlashSelect();
        {
            SPIWriteReadByte(0x5);
            uByte = SPIWriteReadByte(0xFF);
        }
        SPIFlashDeSelect();
    
        return uByte; 
    }
    
    /*********************************************************************************************************
    ** Function name: SPIFlashWritestatusReg
    ** Descriptions: 写状态寄存器
    ** Input parameters: status 要写入的值
    ** Output parameters: none
    ** Returned value: none
    *********************************************************************************************************/
    static void SPIFlashWritestatusReg (uint8 status)
    {
        // 发送使能状态寄存器写命令
        SPIFlashSelect();
        {
            SPIWriteReadByte(0x50);
        }
        SPIFlashDeSelect();
        
        // 写命令和状态值
        SPIFlashSelect();
        {
            SPIWriteReadByte(0x1);
            SPIWriteReadByte(status);
        }
        SPIFlashDeSelect();
    }
    
    /*********************************************************************************************************
    ** Function name: SPIFlashReadID
    ** Descriptions: 读SPI FLASH的JEDEC ID
    ** Input parameters: none
    ** Output parameters: 读的ID,从最高字节到最低字节:制造商ID(1B)-存储类型(1B)-存储容量(1B)
    ** Returned value: none
    *********************************************************************************************************/
    uint32 SPIFlashReadID (void)
    {
        uint32 spiID;
    
        // 发送低速读命令0x9F,3字节地址,写入的字节
        SPIFlashSelect();
        {
            SPIWriteReadByte(0x9F);
            spiID = SPIWriteReadByte(0xff) << 16;
            spiID |= SPIWriteReadByte(0xff) << 8;
            spiID |= SPIWriteReadByte(0xff);
        }
        SPIFlashDeSelect();
    
        return spiID;
    }
    
    
    /*********************************************************************************************************
    ** Function name: SPIFlashRead
    ** Descriptions: 以低速方式(<=25MHZ)从SPI FLASH读数据
    ** Input parameters: readBuf 读数据存储的缓冲区首址
    ** readCnt 要读取的数据量
    ** Output parameters: uint32 实际读得的数据量。当读地址超出芯片容量时,将只读在芯片地址范围内
    ** 的数据
    ** Returned value: none
    *********************************************************************************************************/
    uint32 SPIFlashRead (uint32 readAddr, uint8 * readBuf, uint32 readCnt)
    {
        uint32 i;
        uint8 addr[3];
    
        // 检查参数
        if (readAddr >= SPI_FLASH_SIZE || 
            readBuf == 0 ||
            readCnt == 0) {
            return SPI_FLASH_ERR_PARA;
        }
    
        // 校正要读的数据总量
        if (readAddr + readCnt >= SPI_FLASH_SIZE) {
            readCnt = SPI_FLASH_SIZE - readAddr;
        }
    
        // 将地址转换为字节数组
        addr[0] = (uint8)(readAddr >> 16);
        addr[1] = (uint8)(readAddr >> 8);
        addr[2] = (uint8)(readAddr & 0xff);
    
        // 等待SPI Flash完成写操作
        while (SPIFlashReadstatusReg() & SPI_FLASH_REG_BIT_BUSY );
    
        SPIFlashSelect();
        {
            // 发送低速读命令0x3,3字节地址
            SPIWriteReadByte(0x3);
            SPIWriteReadByte(addr[0]);
            SPIWriteReadByte(addr[1]);
            SPIWriteReadByte(addr[2]);
        
            // 依次读readCnt个数据
            for (i = 0; i < readCnt; i++) {
                readBuf[i] = SPIWriteReadByte(0xff);
            }
        }
        SPIFlashDeSelect();
    
        return i;
    }
    
    /*********************************************************************************************************
    ** Function name: SPIFlashFastRead
    ** Descriptions: 以高速方式(<=50MHZ)从SPI FLASH读数据
    ** Input parameters: readBuf 读数据存储的缓冲区首址
    ** readCnt 要读取的数据量
    ** Output parameters: uint32 实际读得的数据量。当读地址超出芯片容量时,将只读在芯片地址范围内
    ** 的数据
    ** Returned value: none
    *********************************************************************************************************/
    uint32 SPIFlashFastRead (uint32 readAddr, uint8 * readBuf, uint32 readCnt)
    {
        uint32 i;
        uint8 addr[3];
    
        // 检查参数
        if (readAddr >= SPI_FLASH_SIZE || 
            readBuf == 0 ||
            readCnt == 0) {
            return SPI_FLASH_ERR_PARA;
        }
    
        // 校正要读的数据总量
        if (readAddr + readCnt >= SPI_FLASH_SIZE) {
            readCnt = SPI_FLASH_SIZE - readAddr;
        }
    
        // 将地址转换为字节数组
        addr[0] = (uint8)(readAddr >> 16);
        addr[1] = (uint8)(readAddr >> 8);
        addr[2] = (uint8)(readAddr & 0xff);
    
        // 等待SPI Flash完成写操作
        while (SPIFlashReadstatusReg() & SPI_FLASH_REG_BIT_BUSY );
    
        SPIFlashSelect();
        {
            // 发送低速读命令0x3,3字节地址,dummy字节
            SPIWriteReadByte(0xB);
            SPIWriteReadByte(addr[0]);
            SPIWriteReadByte(addr[1]);
            SPIWriteReadByte(addr[2]);
            SPIWriteReadByte(0xff);
        
            // 依次读readCnt个数据
            for (i = 0; i < readCnt; i++) {
                readBuf[i] = SPIWriteReadByte(0xff);
            }
        }
        SPIFlashDeSelect();
    
        return i;
    }
    
    /*********************************************************************************************************
    ** Function name: SPIFlashWriteByte
    ** Descriptions: 向SPI Flash指定地址处写一字节数据
    ** Input parameters: writeAddr 写入的地址
    ** udata 写入的值
    ** Output parameters: none
    ** Returned value: none
    ** Notes: 写之前必须注意到写保护的存储影响
    *********************************************************************************************************/
    uint32 SPIFlashWriteByte (uint32 writeAddr, uint8 uByte)
    {
        uint8 addr[3];
    
        // 检查写地址是否越界
        if (writeAddr >= SPI_FLASH_SIZE) {
            return SPI_FLASH_ERR_PARA;
        }
    
        // 将地址转换为字节数组
        addr[0] = (uint8)(writeAddr >> 16);
        addr[1] = (uint8)(writeAddr >> 8);
        addr[2] = (uint8)(writeAddr & 0xff);
    
        // 等待SPI Flash完成写操作
        while (SPIFlashReadstatusReg() & SPI_FLASH_REG_BIT_BUSY );
    
        // 发送写使能命令
        SPIFlashSelect();
        {
            SPIWriteReadByte(0x06);
        }
        SPIFlashDeSelect();
    
        // 写入实际要写入的数据
        SPIFlashSelect();
        {
            // 发送低速读命令0x3,3字节地址,写入的字节
            SPIWriteReadByte(0x2);
            SPIWriteReadByte(addr[0]);
            SPIWriteReadByte(addr[1]);
            SPIWriteReadByte(addr[2]);
            SPIWriteReadByte(uByte);
        }
        SPIFlashDeSelect();
    
        return SPI_FLASH_OK;
    }
    
    /*********************************************************************************************************
    ** Function name: SPIFlashFastWrite
    ** Descriptions: 以地址自增方式向指定FLASH地址处连续写字数据
    ** Input parameters: writeAddr 写入的起始地址
    ** writeBuferr 写数据存储的缓冲区
    ** uWord 写入的数据量,以字为单位
    ** Output parameters: none
    ** Returned value: none
    ** Notes: 写之前必须注意到写保护的存储影响
    *********************************************************************************************************/
    uint32 SPIFlashFastWrite (uint32 writeAddr, uint16 * writeBuferr, uint16 uWord)
    {
        uint16 i;
        uint8 addr[3];
    
         // 检查参数
        if (writeAddr >= SPI_FLASH_SIZE || 
            writeBuferr == 0 ||
            uWord == 0) {
            return SPI_FLASH_ERR_PARA;
        }
    
        // 检查写地址是否越界
        if (writeAddr + (uWord << 1) >= SPI_FLASH_SIZE) {
            uWord = (SPI_FLASH_SIZE - writeAddr) >> 1; 
        }
    
        // 将地址转换为字节数组
        addr[0] = (uint8)(writeAddr >> 16);
        addr[1] = (uint8)(writeAddr >> 8);
        addr[2] = (uint8)(writeAddr & 0xff);
    
        // 等待SPI Flash完成写操作
        while (SPIFlashReadstatusReg() & SPI_FLASH_REG_BIT_BUSY );
    
        // 发送写使能命令
        SPIFlashSelect();
        {
            SPIWriteReadByte(0x06);
        }
        SPIFlashDeSelect();
    
        // 发送低速读命令0xAD,3字节地址,写入的字节
        SPIFlashSelect();
        {
            SPIWriteReadByte(0xAD);
            SPIWriteReadByte(addr[0]);
            SPIWriteReadByte(addr[1]);
            SPIWriteReadByte(addr[2]);
            SPIWriteReadByte(writeBuferr[0] >> 0x08);
            SPIWriteReadByte(writeBuferr[0] & 0xFF);
        }
        SPIFlashDeSelect();
    
        // 发送其它字节
        for (i = 1; i < uWord; i++) {
            // 等待SPI Flash完成写操作
            while (SPIFlashReadstatusReg() & SPI_FLASH_REG_BIT_BUSY );
        
            SPIFlashSelect();
            {
               // 发送引导命令和编程字数据
                SPIWriteReadByte(0xAD);
                SPIWriteReadByte(writeBuferr[i] >> 0x08);
                SPIWriteReadByte(writeBuferr[i] & 0xFF); 
            }
            SPIFlashDeSelect();
       } 
    
        // 等待SPI Flash完成写操作
        while (SPIFlashReadstatusReg() & SPI_FLASH_REG_BIT_BUSY );
    
        // 发送写禁能命令,退出该模式
        SPIFlashSelect();
        {
            SPIWriteReadByte(0x04);
        }
        SPIFlashDeSelect();
    
        return SPI_FLASH_OK;
    }
    
    
    /*********************************************************************************************************
    ** Function name: SPIFlashChipErase
    ** Descriptions: 擦除整块芯片
    ** Input parameters: none
    ** Output parameters: none
    ** Returned value: none
    ** Note: 如果sectorAaddr
    *********************************************************************************************************/
    void SPIFlashErase (uint8 eraseType, uint32 eraseAddr)
    {
        uint8 addr[3];
    
        // 等待SPI Flash完成写操作
        while (SPIFlashReadstatusReg() & SPI_FLASH_REG_BIT_BUSY );
    
        // 发送写使能命令
        SPIFlashSelect();
        {
            SPIWriteReadByte(0x06);
        }
        SPIFlashDeSelect();
    
        // 根据类型选择擦除方式
        switch (eraseType) {
            // 选择整片擦除方式
            case SPI_FLASH_ERASE_CHIP:
                SPIFlashSelect();
                {
                    SPIWriteReadByte(0x60);
                }
                SPIFlashDeSelect();
                break;
            
            // 扇区擦除方式
            case SPI_FLASH_ERASE_SECTOR:
                if (eraseAddr < SPI_FLASH_SIZE) {
                    // 将地址转换为字节数组
                    addr[0] = (uint8)(eraseAddr >> 16);
                    addr[1] = (uint8)(eraseAddr >> 8);
                    addr[2] = (uint8)(eraseAddr & 0xff);
                
                    // 发送扇区擦除命令,擦除的扇区地址 
                    SPIFlashSelect();
                    {
                        SPIWriteReadByte(0x20);
                        SPIWriteReadByte(addr[0]);
                        SPIWriteReadByte(addr[1]);
                        SPIWriteReadByte(addr[2]);
                    }
                    SPIFlashDeSelect();
                }
                break;
    
            // 32K块擦除方式
            case SPI_FLASH_ERASE_BLOCK:
                if (eraseAddr < SPI_FLASH_SIZE) {
                    // 将地址转换为字节数组
                    addr[0] = (uint8)(eraseAddr >> 16);
                    addr[1] = (uint8)(eraseAddr >> 8);
                    addr[2] = (uint8)(eraseAddr & 0xff);
                
                    // 发送块擦除命令,擦除的块地址 
                    SPIFlashSelect();
                    {
                        SPIWriteReadByte(0x52);
                        SPIWriteReadByte(addr[0]);
                        SPIWriteReadByte(addr[1]);
                        SPIWriteReadByte(addr[2]);
                    }
                    SPIFlashDeSelect();
                }
                break;
    
            default:
                break;
        }
    
    
    }
    
    /*********************************************************************************************************
    ** Function name: SPIFlashSetProtection
    ** Descriptions: 设定SPI存储器的保护模式,加保护或解保护
    ** Input parameters: isProtect 保护的模式,1--加保护, 0 -- 解保护
    ** startAddr 保护区域的起始地址
    ** stopAddr 保护区域的结束地址
    ** Output parameters: 0 -- 操作成功, 1 -- 操作失败(当为解保护时,硬件保护阻止了解保护)
    ** Returned value: none
    *********************************************************************************************************/
    uint32 SPIFlashSetProtection (uint8 isProtect, uint32 startAddr, uint32 stopAddr)
    {
        uint8 BPXMask;
        uint8 status;
    
        // 等待SPI Flash完成写操作
        while (SPIFlashReadstatusReg() & SPI_FLASH_REG_BIT_BUSY );
    
        if (isProtect) {
            // 清除所有的保护位
            SPIFlashWritestatusReg(0);
    
            // 如果BPL只读,即不可更改BPX位
            if (SPIFlashReadstatusReg() & SPI_FLASH_REG_BIT_BPL) {
    
                // 当该区域已经加保护时,正确返回
                if ((status & SPI_FLASH_REG_BIT_BPX) > BPXMask) {
                    return 0;
                } else {
                    // 当未加保护时,返回错误,不可更改保护模式
                    return 1;
                }
                
            } else { 
                // 计算保护模式位
                if (startAddr > SPI_FLASH_SIZE) {
                    return 0;
                } else if (startAddr >= 0xF0000) {
                    BPXMask = 0x1 << 2; 
                } else if (startAddr >= 0xE0000) {
                    BPXMask = 0x2 << 2; 
                } else if (startAddr >= 0xC0000) {
                    BPXMask = 0x3 << 2; 
                } else if (startAddr >= 0x80000) {
                    BPXMask = 0x4 << 2; 
                } else {
                    BPXMask = 0x5 << 2; 
                }
    
                // BPL写读写,此时加上保护位即可,但不锁定BPL,以妨不可恢复 
                SPIFlashWritestatusReg(BPXMask);
    
                return 0;
            }
        } else {
            // 解除保护,写BPL位为0,解除保护
            SPIFlashWritestatusReg(0);
    
            // 读保护位,如果值不为全0,则可能硬件加保护,解保护失败
            if (SPIFlashReadstatusReg() & SPI_FLASH_REG_BIT_BPX) {
                return 1; 
            } else {
                return 0;
            }
        }
    }
    
    
    /* -------------------------------------------------- 测试代码 ---------------------------------------*/
    uint32 id;
    uint8 writeBuffer[1024];
    uint8 readBuffer[1024];
    uint8 status;
    
    #define SPI_FLASH_SECTOR_SIZE (1024*4)
    #define SPI_FLASH_BLOCK_SIZE (1024*32)
    
    void SPIFlashTest (void)
    {
        uint32 i, j;
        
        // 初始化SPI控制器
        SPIFlashInit();
    
        // 读ID测试,值应为0x00BF258E
        id = SPIFlashReadID();
        if (id != 0x00BF258E) {
            return;
        }
    
        // 先解除保护才可写
        SPIFlashSetProtection(0, 0xF0000, 0xFFFFF);
    
        // 整片擦除测试
        SPIFlashErase(SPI_FLASH_ERASE_CHIP, 0);
        for (i = 0; i < SPI_FLASH_SIZE / sizeof(readBuffer); i++) {
            SPIFlashRead(i * sizeof(readBuffer), readBuffer, sizeof(readBuffer)); 
            for (j = 0; j < sizeof(readBuffer); j++) {
                if (readBuffer[j] != 0xff) {
                    return;
                }
            } 
        }
        
        // 字节写和读测试
        for (i = 0; i < SPI_FLASH_SIZE; i++) {
            SPIFlashWriteByte(i, i); 
            SPIFlashRead (i, readBuffer, 1); 
            if( readBuffer[0] != (i & 0xff)) {
                return;
            } 
        }
        
        // 扇区擦除测试
        for (i = 0; i < SPI_FLASH_SIZE / (4*1024); i++) {
            SPIFlashErase(SPI_FLASH_ERASE_SECTOR, i * (4*1024));
        }
        for (i = 0; i < SPI_FLASH_SIZE / sizeof(readBuffer); i++) {
            SPIFlashRead(i * sizeof(readBuffer), readBuffer, sizeof(readBuffer)); 
            for (j = 0; j < sizeof(readBuffer); j++) {
                if (readBuffer[j] != 0xff) {
                    return;
                }
            } 
        }
    
        // 快速写测试
        for (i = 0; i < SPI_FLASH_SIZE / sizeof(writeBuffer); i++) {
            for (j = 0; j < sizeof(writeBuffer); j++) {
                writeBuffer[j] = j;
            }
            SPIFlashFastWrite( i * sizeof(writeBuffer), (uint16 *)writeBuffer, sizeof(writeBuffer) >> 1);
            SPIFlashFastRead ( i * sizeof(writeBuffer), (uint8 *)readBuffer, sizeof(readBuffer));
    
            // 交换字节顺序再比较
            for (j = 0; j < sizeof(writeBuffer) >> 1; j++) {
                uint16 * ptr = (uint16 *)&readBuffer[j << 1];
                *ptr = ((*ptr & 0xff) << 8) | (*ptr >> 8); 
            }
            if( memcmp( writeBuffer, readBuffer, sizeof(readBuffer)) != 0) {
                return;
            }
        }
    
        // 扇区擦除测试
        for (i = 0; i < SPI_FLASH_SIZE / (32*1024); i++) {
            SPIFlashErase(SPI_FLASH_ERASE_BLOCK, i * (32*1024));
        }
        for (i = 0; i < SPI_FLASH_SIZE / sizeof(readBuffer); i++) {
            SPIFlashRead(i * sizeof(readBuffer), readBuffer, sizeof(readBuffer)); 
            for (j = 0; j < sizeof(readBuffer); j++) {
                if (readBuffer[j] != 0xff) {
                    return;
                }
            } 
        }
        
        // 保护测试
        SPIFlashSetProtection(1, 0xF0000, 0xFFFFF);
        SPIFlashSetProtection(1, 0x80000, 0xFFFFF);
        SPIFlashSetProtection(0, 0xF0000, 0xFFFFF);
    }

    参考:

    STM32之SPI_FLASH(实例)

    W25X SPI Flash中文资料

  • 相关阅读:
    何为大数据
    开启新征程
    碰撞检测系统
    利用卷积自编码器对图片进行降噪
    Tensorflow实现稀疏自动编码(SAE)
    Linux主机定期打补丁修复漏洞
    值得研究的学问
    TensorFlow 安装教程
    早上收到这样一份通知,求一无漏洞框架,无力吐槽
    如何修改WAMP中mysql默认空密码 以及修改时报错的处理方法
  • 原文地址:https://www.cnblogs.com/LittleTiger/p/8301325.html
Copyright © 2011-2022 走看看