FLASH 存储学习-串行SPI nor

分类: 嵌入式

1.1  SST25VF080B 简介 1.1.1  主要特性

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

容量:8MBit

最高SPI时钟频率:50MHz

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

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

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

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


1.2.2  存储组织

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

FLASH 存储学习-串行SPI nor_第1张图片

1.2.3  引脚及封装

FLASH 存储学习-串行SPI nor_第2张图片

引脚说明:

SCKSISOCE – SPI接口控制线

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

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

1.2.4  典型电路

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

FLASH 存储学习-串行SPI nor_第3张图片

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

1.3  保护机制

FLASH 存储学习-串行SPI nor_第4张图片

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

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

WP# àBPLBP3—BP0

BPLàBP3BP0

FLASH 存储学习-串行SPI nor_第5张图片

1.4  编程接口 1.4.1  状态寄存器

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


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

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

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

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

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

                                          FLASH 存储学习-串行SPI nor_第6张图片

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

1.4.2  命令接口

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

FLASH 存储学习-串行SPI nor_第7张图片

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

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

FLASH 存储学习-串行SPI nor_第8张图片

2.  读命令

FLASH 存储学习-串行SPI nor_第9张图片

3.  快速读模式

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

FLASH 存储学习-串行SPI nor_第10张图片

4.  写使能

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


5.  写禁能

FLASH 存储学习-串行SPI nor_第11张图片

6.  字节编程

FLASH 存储学习-串行SPI nor_第12张图片

7.  AAI 编程

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


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

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

FLASH 存储学习-串行SPI nor_第13张图片

FLASH 存储学习-串行SPI nor_第14张图片

8.  4KB 的扇区擦除

FLASH 存储学习-串行SPI nor_第15张图片

9.  32K 块擦除

FLASH 存储学习-串行SPI nor_第16张图片

10.  64K 块擦除


11.  整片擦除

FLASH 存储学习-串行SPI nor_第17张图片

12.  读状态寄存器

FLASH 存储学习-串行SPI nor_第18张图片

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

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

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

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

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

FLASH 存储学习-串行SPI nor_第19张图片

14.  JEDEC  Read-ID

获取SST制造商的IDSST FLASH器件的ID


1.6  驱动设计

驱动框架如下图:


算法编写原则:

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

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

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

1.7  驱动代码

点击(此处)折叠或打开

  1. // SST25VF080B驱动接口
  2. // By:lstzixing At ZLG
  3. // Date: 2011-1
  4. #include "STM32Lib\\stm32f10x.h"
  5. #include "hal.h"

  6. typedef unsigned long uint32;
  7. typedef unsigned short uint16;
  8. typedef unsigned char uint8;

  9. #define SPI_FLASH_SIZE (1024*1024*2) // SPI Flash容量
  10. #define SPI_FLASH_OK 0 
  11. #define SPI_FLASH_ERR_PARA 3 // SPI Flash参数错误

  12. // SPI Flash擦除操作码
  13. #define SPI_FLASH_ERASE_CHIP 0 // 整片擦除
  14. #define SPI_FLASH_ERASE_SECTOR 1 // 扇区擦除
  15. #define SPI_FLASH_ERASE_BLOCK 2 // 块擦除

  16. // SPI状态寄存器和位
  17. #define SPI_FLASH_REG_BIT_BUSY (<< 0)
  18. #define SPI_FLASH_REG_BIT_WEL (<< 1)
  19. #define SPI_FLASH_REG_BIT_BPX (0xF << 2)
  20. #define SPI_FLASH_REG_BIT_AAI (<< 6)
  21. #define SPI_FLASH_REG_BIT_BPL (<< 7)

  22. #define SPIFlashSelect()         GPIO_ResetBits(GPIOC, GPIO_Pin_13) /* SST CS = L */ 
  23. #define SPIFlashDeSelect()         GPIO_SetBits(GPIOC, GPIO_Pin_13) /* SST CS = H */

  24. /*********************************************************************************************************
  25. ** Function name: SPIFlashInit
  26. ** Descriptions: 初始化SPI硬件,设置相关的GPIO口、SPI控制器
  27. ** Input parameters: none
  28. ** Output parameters: None
  29. ** Returned value: none
  30. *********************************************************************************************************/
  31. void SPIFlashInit(void)
  32. {
  33.     // 打开SPI1和GPIO时钟 
  34.     RCC->APB2ENR |= RCC_APB2ENR_SPI1EN |
  35.                     RCC_APB2ENR_IOPAEN |
  36.                     RCC_APB2ENR_IOPBEN |
  37.                     RCC_APB2ENR_IOPCEN |
  38.                     RCC_APB2ENR_IOPDEN |
  39.                     RCC_APB2ENR_IOPEEN |
  40.                     RCC_APB2ENR_IOPFEN;

  41.     // PA5/6/7为复用模式, 50MHZ
  42.     GPIOA->CRL &= ~(GPIO_CRL_CNF5 | GPIO_CRL_CNF6 | GPIO_CRL_CNF7 |
  43.                     GPIO_CRL_MODE5 | GPIO_CRL_MODE6 | GPIO_CRL_MODE7);
  44.     GPIOA->CRL |= GPIO_CRL_MODE5 | GPIO_CRL_MODE6 | GPIO_CRL_MODE7 |
  45.                   GPIO_CRL_CNF5_1 | GPIO_CRL_CNF6_1 | GPIO_CRL_CNF7_1;

  46.     // 配置PC.13为输出片选线
  47.     GPIOC->CRH &= ~(GPIO_CRH_CNF13 | GPIO_CRH_MODE13);
  48.     GPIOC->CRH |= GPIO_CRH_MODE13;

  49.     // 配置SPI1,第一个时钟沿采样、主机、分频最大、使能、软件从机管理 
  50.     SPI1->CR1 = //SPI_CR1_CPHA | 
  51.                 SPI_CR1_MSTR |
  52.                 //SPI_CR1_BR |
  53.                 SPI_CR1_SSI | 
  54.                 SPI_CR1_SPE |
  55.                 SPI_CR1_SSM; 

  56.     SPI1->CR2 = 0;

  57.     SPIFlashDeSelect();
  58. }

  59. /*********************************************************************************************************
  60. ** Function name: SPIWriteReadByte
  61. ** Descriptions: 向SPI发送并读取一数据
  62. ** Input parameters: data 要发送的数据
  63. ** Output parameters: None
  64. ** Returned value: uint16 读取的数据
  65. *********************************************************************************************************/
  66. static uint16 SPIWriteReadByte(uint16 data)
  67. {
  68.     // 发送一字节
  69.     while((SPI1->SR & SPI_I2S_FLAG_TXE) == RESET);
  70.     SPI1->DR = data;

  71.     // 接收一字节
  72.     while((SPI1->SR & SPI_I2S_FLAG_RXNE) == RESET);
  73.     return(SPI1->DR);
  74. }

  75. /*********************************************************************************************************
  76. ** Function name: SPIFlashReadstatusReg
  77. ** Descriptions: 读状态寄存器
  78. ** Input parameters: none
  79. ** Output parameters: 当前状态寄存器的值
  80. ** BIT0 -- BUSY位,写忙标志 
  81. ** BIT1 -- WEL位,FLASH处于写保护状态
  82. ** BIT6[5..2]--BP [3..0],扇区保护位
  83. ** BIT6 -- 指示正在自动自境编程中
  84. ** BIT7 -- BPL,BPX的保护位
  85. ** Returned value: none
  86. *********************************************************************************************************/
  87. static uint8 SPIFlashReadstatusReg (void)
  88. {
  89.     uint8 uByte;

  90.     // 发送读状态寄存器命令
  91.     SPIFlashSelect();
  92.     {
  93.         SPIWriteReadByte(0x5);
  94.         uByte = SPIWriteReadByte(0xFF);
  95.     }
  96.     SPIFlashDeSelect();

  97.     return uByte; 
  98. }

  99. /*********************************************************************************************************
  100. ** Function name: SPIFlashWritestatusReg
  101. ** Descriptions: 写状态寄存器
  102. ** Input parameters: status 要写入的值
  103. ** Output parameters: none
  104. ** Returned value: none
  105. *********************************************************************************************************/
  106. static void SPIFlashWritestatusReg (uint8 status)
  107. {
  108.     // 发送使能状态寄存器写命令
  109.     SPIFlashSelect();
  110.     {
  111.         SPIWriteReadByte(0x50);
  112.     }
  113.     SPIFlashDeSelect();
  114.     
  115.     // 写命令和状态值
  116.     SPIFlashSelect();
  117.     {
  118.         SPIWriteReadByte(0x1);
  119.         SPIWriteReadByte(status);
  120.     }
  121.     SPIFlashDeSelect();
  122. }

  123. /*********************************************************************************************************
  124. ** Function name: SPIFlashReadID
  125. ** Descriptions: 读SPI FLASH的JEDEC ID
  126. ** Input parameters: none
  127. ** Output parameters: 读的ID,从最高字节到最低字节:制造商ID(1B)-存储类型(1B)-存储容量(1B)
  128. ** Returned value: none
  129. *********************************************************************************************************/
  130. uint32 SPIFlashReadID (void)
  131. {
  132.     uint32 spiID;

  133.     // 发送低速读命令0x9F,3字节地址,写入的字节
  134.     SPIFlashSelect();
  135.     {
  136.         SPIWriteReadByte(0x9F);
  137.         spiID = SPIWriteReadByte(0xff) << 16;
  138.         spiID |= SPIWriteReadByte(0xff) << 8;
  139.         spiID |= SPIWriteReadByte(0xff);
  140.     }
  141.     SPIFlashDeSelect();

  142.     return spiID;
  143. }


  144. /*********************************************************************************************************
  145. ** Function name: SPIFlashRead
  146. ** Descriptions: 以低速方式(<=25MHZ)从SPI FLASH读数据
  147. ** Input parameters: readBuf 读数据存储的缓冲区首址
  148. ** readCnt 要读取的数据量
  149. ** Output parameters: uint32 实际读得的数据量。当读地址超出芯片容量时,将只读在芯片地址范围内
  150. ** 的数据
  151. ** Returned value: none
  152. *********************************************************************************************************/
  153. uint32 SPIFlashRead (uint32 readAddr, uint8 * readBuf, uint32 readCnt)
  154. {
  155.     uint32 i;
  156.     uint8 addr[3];

  157.     // 检查参数
  158.     if (readAddr >= SPI_FLASH_SIZE || 
  159.         readBuf == 0 ||
  160.         readCnt == 0) {
  161.         return SPI_FLASH_ERR_PARA;
  162.     }

  163.     // 校正要读的数据总量
  164.     if (readAddr + readCnt >= SPI_FLASH_SIZE) {
  165.         readCnt = SPI_FLASH_SIZE - readAddr;
  166.     }

  167.     // 将地址转换为字节数组
  168.     addr[0] = (uint8)(readAddr >> 16);
  169.     addr[1] = (uint8)(readAddr >> 8);
  170.     addr[2] = (uint8)(readAddr & 0xff);

  171.     // 等待SPI Flash完成写操作
  172.     while (SPIFlashReadstatusReg() & SPI_FLASH_REG_BIT_BUSY );

  173.     SPIFlashSelect();
  174.     {
  175.         // 发送低速读命令0x3,3字节地址
  176.         SPIWriteReadByte(0x3);
  177.         SPIWriteReadByte(addr[0]);
  178.         SPIWriteReadByte(addr[1]);
  179.         SPIWriteReadByte(addr[2]);
  180.     
  181.         // 依次读readCnt个数据
  182.         for (= 0; i < readCnt; i++) {
  183.             readBuf[i] = SPIWriteReadByte(0xff);
  184.         }
  185.     }
  186.     SPIFlashDeSelect();

  187.     return i;
  188. }

  189. /*********************************************************************************************************
  190. ** Function name: SPIFlashFastRead
  191. ** Descriptions: 以高速方式(<=50MHZ)从SPI FLASH读数据
  192. ** Input parameters: readBuf 读数据存储的缓冲区首址
  193. ** readCnt 要读取的数据量
  194. ** Output parameters: uint32 实际读得的数据量。当读地址超出芯片容量时,将只读在芯片地址范围内
  195. ** 的数据
  196. ** Returned value: none
  197. *********************************************************************************************************/
  198. uint32 SPIFlashFastRead (uint32 readAddr, uint8 * readBuf, uint32 readCnt)
  199. {
  200.     uint32 i;
  201.     uint8 addr[3];

  202.     // 检查参数
  203.     if (readAddr >= SPI_FLASH_SIZE || 
  204.         readBuf == 0 ||
  205.         readCnt == 0) {
  206.         return SPI_FLASH_ERR_PARA;
  207.     }

  208.     // 校正要读的数据总量
  209.     if (readAddr + readCnt >= SPI_FLASH_SIZE) {
  210.         readCnt = SPI_FLASH_SIZE - readAddr;
  211.     }

  212.     // 将地址转换为字节数组
  213.     addr[0] = (uint8)(readAddr >> 16);
  214.     addr[1] = (uint8)(readAddr >> 8);
  215.     addr[2] = (uint8)(readAddr & 0xff);

  216.     // 等待SPI Flash完成写操作
  217.     while (SPIFlashReadstatusReg() & SPI_FLASH_REG_BIT_BUSY );

  218.     SPIFlashSelect();
  219.     {
  220.         // 发送低速读命令0x3,3字节地址,dummy字节
  221.         SPIWriteReadByte(0xB);
  222.         SPIWriteReadByte(addr[0]);
  223.         SPIWriteReadByte(addr[1]);
  224.         SPIWriteReadByte(addr[2]);
  225.         SPIWriteReadByte(0xff);
  226.     
  227.         // 依次读readCnt个数据
  228.         for (= 0; i < readCnt; i++) {
  229.             readBuf[i] = SPIWriteReadByte(0xff);
  230.         }
  231.     }
  232.     SPIFlashDeSelect();

  233.     return i;
  234. }

  235. /*********************************************************************************************************
  236. ** Function name: SPIFlashWriteByte
  237. ** Descriptions: 向SPI Flash指定地址处写一字节数据
  238. ** Input parameters: writeAddr 写入的地址
  239. ** udata 写入的值
  240. ** Output parameters: none
  241. ** Returned value: none
  242. ** Notes: 写之前必须注意到写保护的存储影响
  243. *********************************************************************************************************/
  244. uint32 SPIFlashWriteByte (uint32 writeAddr, uint8 uByte)
  245. {
  246.     uint8 addr[3];

  247.     // 检查写地址是否越界
  248.     if (writeAddr >= SPI_FLASH_SIZE) {
  249.         return SPI_FLASH_ERR_PARA;
  250.     }

  251.     // 将地址转换为字节数组
  252.     addr[0] = (uint8)(writeAddr >> 16);
  253.     addr[1] = (uint8)(writeAddr >> 8);
  254.     addr[2] = (uint8)(writeAddr & 0xff);

  255.     // 等待SPI Flash完成写操作
  256.     while (SPIFlashReadstatusReg() & SPI_FLASH_REG_BIT_BUSY );

  257.     // 发送写使能命令
  258.     SPIFlashSelect();
  259.     {
  260.         SPIWriteReadByte(0x06);
  261.     }
  262.     SPIFlashDeSelect();

  263.     // 写入实际要写入的数据
  264.     SPIFlashSelect();
  265.     {
  266.         // 发送低速读命令0x3,3字节地址,写入的字节
  267.         SPIWriteReadByte(0x2);
  268.         SPIWriteReadByte(addr[0]);
  269.         SPIWriteReadByte(addr[1]);
  270.         SPIWriteReadByte(addr[2]);
  271.         SPIWriteReadByte(uByte);
  272.     }
  273.     SPIFlashDeSelect();

  274.     return SPI_FLASH_OK;
  275. }

  276. /*********************************************************************************************************
  277. ** Function name: SPIFlashFastWrite
  278. ** Descriptions: 以地址自增方式向指定FLASH地址处连续写字数据
  279. ** Input parameters: writeAddr 写入的起始地址
  280. ** writeBuferr 写数据存储的缓冲区
  281. ** uWord 写入的数据量,以字为单位
  282. ** Output parameters: none
  283. ** Returned value: none
  284. ** Notes: 写之前必须注意到写保护的存储影响
  285. *********************************************************************************************************/
  286. uint32 SPIFlashFastWrite (uint32 writeAddr, uint16 * writeBuferr, uint16 uWord)
  287. {
  288.     uint16 i;
  289.     uint8 addr[3];

  290.      // 检查参数
  291.     if (writeAddr >= SPI_FLASH_SIZE || 
  292.         writeBuferr == 0 ||
  293.         uWord == 0) {
  294.         return SPI_FLASH_ERR_PARA;
  295.     }

  296.     // 检查写地址是否越界
  297.     if (writeAddr + (uWord << 1) >= SPI_FLASH_SIZE) {
  298.         uWord = (SPI_FLASH_SIZE - writeAddr) >> 1; 
  299.     }

  300.     // 将地址转换为字节数组
  301.     addr[0] = (uint8)(writeAddr >> 16);
  302.     addr[1] = (uint8)(writeAddr >> 8);
  303.     addr[2] = (uint8)(writeAddr & 0xff);

  304.     // 等待SPI Flash完成写操作
  305.     while (SPIFlashReadstatusReg() & SPI_FLASH_REG_BIT_BUSY );

  306.     // 发送写使能命令
  307.     SPIFlashSelect();
  308.     {
  309.         SPIWriteReadByte(0x06);
  310.     }
  311.     SPIFlashDeSelect();

  312.     // 发送低速读命令0xAD,3字节地址,写入的字节
  313.     SPIFlashSelect();
  314.     {
  315.         SPIWriteReadByte(0xAD);
  316.         SPIWriteReadByte(addr[0]);
  317.         SPIWriteReadByte(addr[1]);
  318.         SPIWriteReadByte(addr[2]);
  319.         SPIWriteReadByte(writeBuferr[0] >> 0x08);
  320.         SPIWriteReadByte(writeBuferr[0] & 0xFF);
  321.     }
  322.     SPIFlashDeSelect();

  323.     // 发送其它字节
  324.     for (= 1; i < uWord; i++) {
  325.         // 等待SPI Flash完成写操作
  326.         while (SPIFlashReadstatusReg() & SPI_FLASH_REG_BIT_BUSY );
  327.     
  328.         SPIFlashSelect();
  329.         {
  330.            // 发送引导命令和编程字数据
  331.             SPIWriteReadByte(0xAD);
  332.             SPIWriteReadByte(writeBuferr[i] >> 0x08);
  333.             SPIWriteReadByte(writeBuferr[i] & 0xFF); 
  334.         }
  335.         SPIFlashDeSelect();
  336.    } 

  337.     // 等待SPI Flash完成写操作
  338.     while (SPIFlashReadstatusReg() & SPI_FLASH_REG_BIT_BUSY );

  339.     // 发送写禁能命令,退出该模式
  340.     SPIFlashSelect();
  341.     {
  342.         SPIWriteReadByte(0x04);
  343.     }
  344.     SPIFlashDeSelect();

  345.     return SPI_FLASH_OK;
  346. }


  347. /*********************************************************************************************************
  348. ** Function name: SPIFlashChipErase
  349. ** Descriptions: 擦除整块芯片
  350. ** Input parameters: none
  351. ** Output parameters: none
  352. ** Returned value: none
  353. ** Note: 如果sectorAaddr
  354. *********************************************************************************************************/
  355. void SPIFlashErase (uint8 eraseType, uint32 eraseAddr)
  356. {
  357.     uint8 addr[3];

  358.     // 等待SPI Flash完成写操作
  359.     while (SPIFlashReadstatusReg() & SPI_FLASH_REG_BIT_BUSY );

  360.     // 发送写使能命令
  361.     SPIFlashSelect();
  362.     {
  363.         SPIWriteReadByte(0x06);
  364.     }
  365.     SPIFlashDeSelect();

  366.     // 根据类型选择擦除方式
  367.     switch (eraseType) {
  368.         // 选择整片擦除方式
  369.         case SPI_FLASH_ERASE_CHIP:
  370.             SPIFlashSelect();
  371.             {
  372.                 SPIWriteReadByte(0x60);
  373.             }
  374.             SPIFlashDeSelect();
  375.             break;
  376.         
  377.         // 扇区擦除方式
  378.         case SPI_FLASH_ERASE_SECTOR:
  379.             if (eraseAddr < SPI_FLASH_SIZE) {
  380.                 // 将地址转换为字节数组
  381.                 addr[0] = (uint8)(eraseAddr >> 16);
  382.                 addr[1] = (uint8)(eraseAddr >> 8);
  383.                 addr[2] = (uint8)(eraseAddr & 0xff);
  384.             
  385.                 // 发送扇区擦除命令,擦除的扇区地址 
  386.                 SPIFlashSelect();
  387.                 {
  388.                     SPIWriteReadByte(0x20);
  389.                     SPIWriteReadByte(addr[0]);
  390.                     SPIWriteReadByte(addr[1]);
  391.                     SPIWriteReadByte(addr[2]);
  392.                 }
  393.                 SPIFlashDeSelect();
  394.             }
  395.             break;

  396.         // 32K块擦除方式
  397.         case SPI_FLASH_ERASE_BLOCK:
  398.             if (eraseAddr < SPI_FLASH_SIZE) {
  399.                 // 将地址转换为字节数组
  400.                 addr[0] = (uint8)(eraseAddr >> 16);
  401.                 addr[1] = (uint8)(eraseAddr >> 8);
  402.                 addr[2] = (uint8)(eraseAddr & 0xff);
  403.             
  404.                 // 发送块擦除命令,擦除的块地址 
  405.                 SPIFlashSelect();
  406.                 {
  407.                     SPIWriteReadByte(0x52);
  408.                     SPIWriteReadByte(addr[0]);
  409.                     SPIWriteReadByte(addr[1]);
  410.                     SPIWriteReadByte(addr[2]);
  411.                 }
  412.                 SPIFlashDeSelect();
  413.             }
  414.             break;

  415.         default:
  416.             break;
  417.     }


  418. }

  419. /*********************************************************************************************************
  420. ** Function name: SPIFlashSetProtection
  421. ** Descriptions: 设定SPI存储器的保护模式,加保护或解保护
  422. ** Input parameters: isProtect 保护的模式,1--加保护, 0 -- 解保护
  423. ** startAddr 保护区域的起始地址
  424. ** stopAddr 保护区域的结束地址
  425. ** Output parameters: 0 -- 操作成功, 1 -- 操作失败(当为解保护时,硬件保护阻止了解保护)
  426. ** Returned value: none
  427. *********************************************************************************************************/
  428. uint32 SPIFlashSetProtection (uint8 isProtect, uint32 startAddr, uint32 stopAddr)
  429. {
  430.     uint8 BPXMask;
  431.     uint8 status;

  432.     // 等待SPI Flash完成写操作
  433.     while (SPIFlashReadstatusReg() & SPI_FLASH_REG_BIT_BUSY );

  434.     if (isProtect) {
  435.         // 清除所有的保护位
  436.         SPIFlashWritestatusReg(0);

  437.         // 如果BPL只读,即不可更改BPX位
  438.         if (SPIFlashReadstatusReg() & SPI_FLASH_REG_BIT_BPL) {

  439.             // 当该区域已经加保护时,正确返回
  440.             if ((status & SPI_FLASH_REG_BIT_BPX) > BPXMask) {
  441.                 return 0;
  442.             } else {
  443.                 // 当未加保护时,返回错误,不可更改保护模式
  444.                 return 1;
  445.             }
  446.             
  447.         } else { 
  448.             // 计算保护模式位
  449.             if (startAddr > SPI_FLASH_SIZE) {
  450.                 return 0;
  451.             } else if (startAddr >= 0xF0000) {
  452.                 BPXMask = 0x1 << 2; 
  453.             } else if (startAddr >= 0xE0000) {
  454.                 BPXMask = 0x2 << 2; 
  455.             } else if (startAddr >= 0xC0000) {
  456.                 BPXMask = 0x3 << 2; 
  457.             } else if (startAddr >= 0x80000) {
  458.                 BPXMask = 0x4 << 2; 
  459.             } else {
  460.                 BPXMask = 0x5 << 2; 
  461.             }

  462.             // BPL写读写,此时加上保护位即可,但不锁定BPL,以妨不可恢复 
  463.             SPIFlashWritestatusReg(BPXMask);

  464.             return 0;
  465.         }
  466.     } else {
  467.         // 解除保护,写BPL位为0,解除保护
  468.         SPIFlashWritestatusReg(0);

  469.         // 读保护位,如果值不为全0,则可能硬件加保护,解保护失败
  470.         if (SPIFlashReadstatusReg() & SPI_FLASH_REG_BIT_BPX) {
  471.             return 1; 
  472.         } else {
  473.             return 0;
  474.         }
  475.     }
  476. }


  477. /* -------------------------------------------------- 测试代码 ---------------------------------------*/
  478. uint32 id;
  479. uint8 writeBuffer[1024];
  480. uint8 readBuffer[1024];
  481. uint8 status;

  482. #define SPI_FLASH_SECTOR_SIZE (1024*4)
  483. #define SPI_FLASH_BLOCK_SIZE (1024*32)

  484. void SPIFlashTest (void)
  485. {
  486.     uint32 i, j;
  487.     
  488.     // 初始化SPI控制器
  489.     SPIFlashInit();

  490.     // 读ID测试,值应为0x00BF258E
  491.     id = SPIFlashReadID();
  492.     if (id != 0x00BF258E) {
  493.         return;
  494.     }

  495.     // 先解除保护才可写
  496.     SPIFlashSetProtection(0, 0xF0000, 0xFFFFF);

  497.     // 整片擦除测试
  498.     SPIFlashErase(SPI_FLASH_ERASE_CHIP, 0);
  499.     for (= 0; i < SPI_FLASH_SIZE / sizeof(readBuffer); i++) {
  500.         SPIFlashRead(* sizeof(readBuffer), readBuffer, sizeof(readBuffer)); 
  501.         for (= 0; j < sizeof(readBuffer); j++) {
  502.             if (readBuffer[j] != 0xff) {
  503.                 return;
  504.             }
  505.         } 
  506.     }
  507.     
  508.     // 字节写和读测试
  509.     for (= 0; i < SPI_FLASH_SIZE; i++) {
  510.         SPIFlashWriteByte(i, i); 
  511.         SPIFlashRead (i, readBuffer, 1); 
  512.         if( readBuffer[0] != (& 0xff)) {
  513.             return;
  514.         } 
  515.     }
  516.     
  517.     // 扇区擦除测试
  518.     for (= 0; i < SPI_FLASH_SIZE / (4*1024); i++) {
  519.         SPIFlashErase(SPI_FLASH_ERASE_SECTOR, i * (4*1024));
  520.     }
  521.     for (= 0; i < SPI_FLASH_SIZE / sizeof(readBuffer); i++) {
  522.         SPIFlashRead(* sizeof(readBuffer), readBuffer, sizeof(readBuffer)); 
  523.         for (= 0; j < sizeof(readBuffer); j++) {
  524.             if (readBuffer[j] != 0xff) {
  525.                 return;
  526.             }
  527.         } 
  528.     }

  529.     // 快速写测试
  530.     for (= 0; i < SPI_FLASH_SIZE / sizeof(writeBuffer); i++) {
  531.         for (= 0; j < sizeof(writeBuffer); j++) {
  532.             writeBuffer[j] = j;
  533.         }
  534.         SPIFlashFastWrite( i * sizeof(writeBuffer), (uint16 *)writeBuffer, sizeof(writeBuffer) >> 1);
  535.         SPIFlashFastRead ( i * sizeof(writeBuffer), (uint8 *)readBuffer, sizeof(readBuffer));

  536.         // 交换字节顺序再比较
  537.         for (= 0; j < sizeof(writeBuffer) >> 1; j++) {
  538.             uint16 * ptr = (uint16 *)&readBuffer[<< 1];
  539.             *ptr = ((*ptr & 0xff) << 8) | (*ptr >> 8); 
  540.         }
  541.         if( memcmp( writeBuffer, readBuffer, sizeof(readBuffer)) != 0) {
  542.             return;
  543.         }
  544.     }

  545.     // 扇区擦除测试
  546.     for (= 0; i < SPI_FLASH_SIZE / (32*1024); i++) {
  547.         SPIFlashErase(SPI_FLASH_ERASE_BLOCK, i * (32*1024));
  548.     }
  549.     for (= 0; i < SPI_FLASH_SIZE / sizeof(readBuffer); i++) {
  550.         SPIFlashRead(* sizeof(readBuffer), readBuffer, sizeof(readBuffer)); 
  551.         for (= 0; j < sizeof(readBuffer); j++) {
  552.             if (readBuffer[j] != 0xff) {
  553.                 return;
  554.             }
  555.         } 
  556.     }
  557.     
  558.     // 保护测试
  559.     SPIFlashSetProtection(1, 0xF0000, 0xFFFFF);
  560.     SPIFlashSetProtection(1, 0x80000, 0xFFFFF);
  561.     SPIFlashSetProtection(0, 0xF0000, 0xFFFFF);
  562. }

阅读(1141) | 评论(0) | 转发(3) |

你可能感兴趣的:(FLASH 存储学习-串行SPI nor)