当前位置:   article > 正文

STM32实战总结:HAL之SPI-Flash_stm32 hal spi

stm32 hal spi

SPI基础内容参考:

嵌入式常见接口协议总结_路溪非溪的博客-CSDN博客

SPI的补充:

通信的起始和停止信号

NSS又由高变低,是SPI通信的起始信号。NSS由低变高,是SPI通信的停止信号,表示本次通信结束。

IIC的任何一个设备既可以作为主设备,也可以作为从设备,但是SPI主从是事先确定的。

关于SPI时序图有一些疑问,有的地方说,发送和接收是同时进行的,但是有的老师又说是在一个边沿发送,但是在另一个边沿接收?我自己看时序图,都只是说在某个边沿采集,并没有提到一个边沿发送一个边沿接收的问题。
其实这里面的接收有两个意思,一个是主机的发送和接收,一个是主机发送,从机的接收。
数据在边沿被采集,发送数据应该不用采集,只有接收数据才叫采集吧。

读时序图,关键的是考虑怎么读数据,因为发送端发数据,就是把数据放到引脚上即可,接收方怎么读,就成了重点关注的问题。主机发送其实就是完成了从机的接收。

任何时刻,只要是工作的,数据线上都是有数据的,只是这个数据是不是我们想要的,SPI在某个时钟边沿被采样,所以需要在采样之前,把我们要传送的数据准备好。

SPI通常在采集边沿的另一个边沿将数据准备好:

STM32的SPI可自行查阅数据手册。本文不赘述。 

NSS是片选信号

QSPI是Quad SPI的简写,表示6线spi,是Motorola公司推出的SPI接口的扩展,比SPI应用更加广泛。在SPI协议的基础上,Motorola公司对其功能进行了增强,增加了队列传输机制,推出了队列串行外围接口协议(即QSPI协议)。使用该接口,用户可以一次性传输包含多达16个8位或16位数据的传输队列。一旦传输启动,直到传输结束,都不需要CPU干预,极大的提高了传输效率。该协议在ColdFire系列MCU得到广泛应用。与SPI相比,QSPI的最大结构特点是以80字节的RAM代替了SPI的发送和接收数据寄存器。

可参考这篇文章: 

QSPI接口新增了哪些功能 - 接口/总线/驱动 - 电子发烧友网

STM32内置Flash

注意:不要把STM32的内置Flash和外置Flash搞混了,STM32内部自带不同大小的Flash,其主要功能是保存已经编译完成的代码。

内部flash和外部flash的主要区别是在读取速度上,内部flash读取速度更快,而且不需要通过接口函数来读取,外部flash的读取速度较慢。

内部flash是用来储存程序的,外部flash一般是用来储存用户数据的。

内部Flash可由ICode直接访问

说到STM32的内置FLSAH,我们的第一反应是用来装程序的,实际上,STM32的片内FLASH不仅用来装程序,还用来装芯片配置、芯片ID、自举程序等等。
根据用途,STM32片内的FLASH分成两部分:主存储块、信息块。

主存储块用于存储程序,我们写的程序一般存储在这里。

信息块又分成两部分:系统存储器、选项字节。  系统存储器存储用于存放在系统存储器自举模式下的启动程序(BootLoader),当使用ISP方式加载程序时,就是由这个程序执行。这个区域由芯片厂写入BootLoader,然后锁死,用户是无法改变这个区域的。 选项字节存储芯片的配置信息及对主存储块的保护信息。   
STM32的FLASH主存储块按页组织,有的产品每页1KB,有的产品每页2KB。页面典型的用途就是用于按页擦除FLASH。从这点来看,页面有点像通用FLASH的扇区。

Flash基础知识 

一般来说,Flash属于典型的块设备。

什么是块设备?通常是指不能像RAM一样以字节为基本读写单位的存储设备。块设备不能完全按字节访问而必须块访问是物理上的限制,而不是人为设置的障碍。

Flash通常分为多个存储块,每个块又分为多个扇区,每个扇区又分为多个页。不过有些块设备只分为块和页,具体芯片具体对待。

其中,页(Page)是Flash可以被单次访问的最小单位。如果某FLASH一页是2KB,那么我们要读写FLASH,每次至少要读写2KB或者n*2KB,即使我们只是想要其中的一个字节。这就是我们说的典型的块设备(PS:现在有些块设备为了方便,提供了一种random read模式,可以只读取1个字节)。

FLASH芯片中主要包含2部分:FLASH存储颗粒+FLASH接口电路。存储颗粒就是纯粹的FLASH的存储单元,类似于仓库;FLASH接口电路是用来管理存储颗粒,并且给外界提供一个统一的FLASH接口规格的访问接口的。


FLASH中有多个存储单元,每个单元都有自己的地址(地址是精确到字节的)。所以FLASH是地址编排精确到字节,但是实际读写却只能精确到页(所以FLASH的很多操作都要求给的地址是页对齐的,譬如2K、4K、512K等这样的地址,不能给3000B这样的地址)。

坏块标志:FLASH芯片用一段时间后,可能某些块会坏掉(这些块无法擦除了,或者无法读写了),FLASH的坏块非常类似于硬盘的坏道。坏块是不可避免的,而且随着FLASH的使用坏块会越来越多。当坏块还不算太多时这个FLASH都是可以用的,除非坏块太多了不能用了才会换新的。所以我们为了管理FLASH发明了一种坏块标志机制。有的FLASH每个页中的一部分会定义一个固定位置(譬如定位第24字节)来标记这个块是好的还是坏的。文件系统在发现这个块已经坏了没法用了时会将这个块标记为坏块,以后访问FLASH时直接跳过这个块即可。

以下以某系列Flash芯片W25QXX为例来说明。

SPIFlash:分为SpiNorFlash和SpiNandFlash,用Spi接口把nand和nor进行了封装,封装成SPI的时序。

常用FLASH型号大小

FLASH的块/扇区/页关系

常用FLASH擦写规则

注意对Flash的三大操作,读、写、擦除。

  • 最小擦除单位:扇区
  • 可选择擦除单位:扇区、块、全片
  • 最大编程(写入)单位:页( 256 Byte),大于256 Byte则需分多次写入。
  • 最小编程(写入)单位:1 Byte,即一次可写入 1~256 Byte的任意长度字节。
  • 未写入时FLASH里面的数据为全1,即0xFF。
  • 写入时,只支持把1写成0,不能把0写成1,如果要把0变成1,只能擦除后再整体写入。对这一点,暂时并不是很理解。我的想法是,如果原来有个数据11110000,现在我想把这个数据变成00001111,高4字节是可以写入的,但是低4位因为无法把0写成1,所以该操作无法直接完成,需要先把原来的数据擦除,变成11111111,然后再写入00001111,这样就能完成“修改”。如果是这样的话,那岂不是每次写入数据之前都要将原来的目标区域给擦除?感觉该操作有风险。

我的一点理解:flash应用场景是,一般都会将特定的内容存放在特定的位置,是提前指定好的,所以擦除再写,就相当于覆盖操作,跟windows里文件替换是一样的道理,默认旧的已经没用了。

常用FLASH封装引脚定义

如图的 8-pin 是较常用的一种封装. 各 pin 的意义:

CS:chip select, 芯片使能引脚。

DO:输出数据。

WP:低电平有效,写保护。

GND:接地。

DI:(在CLK上升沿)向 Flash 输入指令,地址或数据。

CLK:提供输入输出操作的同步时钟。

HOLD:当多个芯片共用 SPI 总线时非常有用。HOLD 为低电平时,DO 引脚变为高阻态,且此时DI/CLK上的信号被忽略。相当于芯片此时不工作。假设对一个 SPI FLASH 的页写操作只进行到一半,此时一个中断来了,另一个更高优先级的任务要占用 SPI 总线,此时就可以使用HOLD拉低来暂停SPI FLASH内部的工作,等到任务切换回来再让操作继续下去。

VCC:2.7 ~ 3.6V

SPIFlash-W25QXX以及STM32内部Flash使用总结

操作外置Flash

本文我们通过使用SPI接口来操作外部Flash,Flash型号为W25Q64。

具体查阅芯片手册,本文不赘述。

硬件原理图

MX配置

1、根据需要选择模式

一般选择全双工主模式不会有啥问题;

2、硬件NSS,如果只有一个从设备,则不需要;

3、STM32自带的SPI的通信速率不应超过18Mbps,

在数据手册上有描述。这是芯片设计时决定的。为了不超过最高18MHz的设计要求,配置SPI2和SPI3时可以使用fPCLK/2的选项,而配置SPI1时则不能使用fPCLK/2的选项

4、极性和相位选择;

5、NSS是软件还是硬件。

相关函数

  1. /* Exported functions --------------------------------------------------------*/
  2. /** @addtogroup SPI_Exported_Functions
  3. * @{
  4. */
  5. /** @addtogroup SPI_Exported_Functions_Group1
  6. * @{
  7. */
  8. /* Initialization/de-initialization functions ********************************/
  9. HAL_StatusTypeDef HAL_SPI_Init(SPI_HandleTypeDef *hspi);
  10. HAL_StatusTypeDef HAL_SPI_DeInit(SPI_HandleTypeDef *hspi);
  11. void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi);
  12. void HAL_SPI_MspDeInit(SPI_HandleTypeDef *hspi);
  13. /* Callbacks Register/UnRegister functions ***********************************/
  14. #if (USE_HAL_SPI_REGISTER_CALLBACKS == 1U)
  15. HAL_StatusTypeDef HAL_SPI_RegisterCallback(SPI_HandleTypeDef *hspi, HAL_SPI_CallbackIDTypeDef CallbackID, pSPI_CallbackTypeDef pCallback);
  16. HAL_StatusTypeDef HAL_SPI_UnRegisterCallback(SPI_HandleTypeDef *hspi, HAL_SPI_CallbackIDTypeDef CallbackID);
  17. #endif /* USE_HAL_SPI_REGISTER_CALLBACKS */
  18. /**
  19. * @}
  20. */
  21. /** @addtogroup SPI_Exported_Functions_Group2
  22. * @{
  23. */
  24. /* I/O operation functions ***************************************************/
  25. HAL_StatusTypeDef HAL_SPI_Transmit(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout);
  26. HAL_StatusTypeDef HAL_SPI_Receive(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout);
  27. HAL_StatusTypeDef HAL_SPI_TransmitReceive(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData, uint16_t Size,
  28. uint32_t Timeout);
  29. HAL_StatusTypeDef HAL_SPI_Transmit_IT(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size);
  30. HAL_StatusTypeDef HAL_SPI_Receive_IT(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size);
  31. HAL_StatusTypeDef HAL_SPI_TransmitReceive_IT(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData,
  32. uint16_t Size);
  33. HAL_StatusTypeDef HAL_SPI_Transmit_DMA(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size);
  34. HAL_StatusTypeDef HAL_SPI_Receive_DMA(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size);
  35. HAL_StatusTypeDef HAL_SPI_TransmitReceive_DMA(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData,
  36. uint16_t Size);
  37. HAL_StatusTypeDef HAL_SPI_DMAPause(SPI_HandleTypeDef *hspi);
  38. HAL_StatusTypeDef HAL_SPI_DMAResume(SPI_HandleTypeDef *hspi);
  39. HAL_StatusTypeDef HAL_SPI_DMAStop(SPI_HandleTypeDef *hspi);
  40. /* Transfer Abort functions */
  41. HAL_StatusTypeDef HAL_SPI_Abort(SPI_HandleTypeDef *hspi);
  42. HAL_StatusTypeDef HAL_SPI_Abort_IT(SPI_HandleTypeDef *hspi);
  43. void HAL_SPI_IRQHandler(SPI_HandleTypeDef *hspi);
  44. void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi);
  45. void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi);
  46. void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi);
  47. void HAL_SPI_TxHalfCpltCallback(SPI_HandleTypeDef *hspi);
  48. void HAL_SPI_RxHalfCpltCallback(SPI_HandleTypeDef *hspi);
  49. void HAL_SPI_TxRxHalfCpltCallback(SPI_HandleTypeDef *hspi);
  50. void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi);
  51. void HAL_SPI_AbortCpltCallback(SPI_HandleTypeDef *hspi);
  52. /**
  53. * @}
  54. */
  55. /** @addtogroup SPI_Exported_Functions_Group3
  56. * @{
  57. */
  58. /* Peripheral State and Error functions ***************************************/
  59. HAL_SPI_StateTypeDef HAL_SPI_GetState(SPI_HandleTypeDef *hspi);
  60. uint32_t HAL_SPI_GetError(SPI_HandleTypeDef *hspi);
  61. /**
  62. * @}
  63. */

关键代码

  1. /* Includes ------------------------------------------------------------------*/
  2. #include "MyApplication.h"
  3. /* Private define-------------------------------------------------------------*/
  4. /* Private variables----------------------------------------------------------*/
  5. /* Private function prototypes------------------------------------------------*/
  6. static void SPI_Flash_ReadFlashID(void); //读取FlashID
  7. static void SPI_Flash_EraseSector(uint32_t); //擦除扇区(4kB)
  8. static void SPI_Flash_EraseTotal(void); //擦除全部
  9. static void SPI_Flash_WriteUnfixed(uint8_t*,uint32_t,uint32_t); //写入不固定长度数据
  10. static void SPI_Flash_ReadUnfixed(uint8_t*,uint32_t,uint32_t); //读取不固定长度数据
  11. /* Public variables-----------------------------------------------------------*/
  12. SPI_Flash_t SPI_Flash =
  13. {
  14. 0,
  15. SPI_Flash_ReadFlashID,
  16. SPI_Flash_EraseSector,
  17. SPI_Flash_EraseTotal,
  18. SPI_Flash_WriteUnfixed,
  19. SPI_Flash_ReadUnfixed
  20. };
  21. /* Private function prototypes------------------------------------------------*/
  22. static uint8_t SPI_Flash_ReadByte(void); //从Flash读1个字节
  23. static void SPI_Flash_WriteByte(uint8_t); //给Flash写1个字节
  24. static void SPI_Flash_WriteEnable(void); //Flash写使能
  25. static void SPI_Flash_WaitForWriteEnd(void); //等待Flash写入完成
  26. static void SPI_Flash_WritePage(uint8_t*,uint32_t,uint16_t); //写入页(256Bytes),写入长度不超过256字节
  27. /*
  28. * @name SPI_Flash_ReadByte
  29. * @brief 从Flash读1个字节
  30. * @param None
  31. * @retval 读取到的数据
  32. */
  33. static uint8_t SPI_Flash_ReadByte()
  34. {
  35. uint8_t ReceiveByte;
  36. //等待模式读出1个字节
  37. if(HAL_SPI_Receive(&hspi3,&ReceiveByte,1,0x0A) != HAL_OK)
  38. ReceiveByte = Dummy_Byte;
  39. //返回字节
  40. return ReceiveByte;
  41. }
  42. /*
  43. * @name SPI_Flash_ReadByte
  44. * @brief 给Flash写1个字节
  45. * @param Byte -> 待写入的字节
  46. * @retval 读取到的数据
  47. */
  48. static void SPI_Flash_WriteByte(uint8_t Byte)
  49. {
  50. uint8_t SendByte = Byte;
  51. //等待模式写入1个字节
  52. HAL_SPI_Transmit(&hspi3,&SendByte,1,0x0A);
  53. }
  54. /*
  55. * @name SPI_Flash_WriteEnable
  56. * @brief Flash写使能
  57. * @param None
  58. * @retval None
  59. */
  60. static void SPI_Flash_WriteEnable()
  61. {
  62. //选择Flash芯片: CS输出低电平
  63. CLR_SPI_Flash_CS;
  64. //发送命令:写使能
  65. SPI_Flash_WriteByte(W25X_WriteEnable);
  66. //禁用Flash芯片: CS输出高电平
  67. SET_SPI_Flash_CS;
  68. }
  69. /*
  70. * @name SPI_Flash_WaitForWriteEnd
  71. * @brief 等待Flash写入完成
  72. * @param None
  73. * @retval None
  74. */
  75. void SPI_Flash_WaitForWriteEnd()
  76. {
  77. uint8_t Flash_Status1 = 0;
  78. //选择Flash芯片: CS输出低电平
  79. CLR_SPI_Flash_CS;
  80. //发送命令:读状态寄存器1
  81. SPI_Flash_WriteByte(W25X_ReadStatusReg1);
  82. //连续读取状态寄存器1,直到WEL为低电?
  83. Timer6.usDelay_Timer = 0;
  84. do
  85. {
  86. Flash_Status1 = SPI_Flash_ReadByte();
  87. //防止意外,概率极低
  88. if(Timer6.usDelay_Timer >= TIMER0_10S)
  89. break;
  90. }
  91. while((Flash_Status1 & Flash_Status1_BUSY) == Flash_Status1_BUSY);
  92. //禁用Flash芯片: CS输出高电平
  93. SET_SPI_Flash_CS;
  94. }
  95. /*
  96. * @name SPI_Flash_ReadFlashID
  97. * @brief 读取FlashID
  98. * @param None
  99. * @retval None
  100. */
  101. static void SPI_Flash_ReadFlashID()
  102. {
  103. uint8_t buf[3];
  104. //检测flash是否处于忙碌状态
  105. SPI_Flash_WaitForWriteEnd();
  106. //选择Flash芯片: CS输出低电平
  107. CLR_SPI_Flash_CS;
  108. //发送命令:读取JEDEC ID(设备标识符 -> 制造商+内存类型+容量)
  109. SPI_Flash_WriteByte(W25X_ReadJedecID);
  110. buf[0] = SPI_Flash_ReadByte();
  111. buf[1] = SPI_Flash_ReadByte();
  112. buf[2] = SPI_Flash_ReadByte();
  113. //禁用Flash芯片: CS输出高电平
  114. SET_SPI_Flash_CS;
  115. SPI_Flash.JedecID = (buf[0] << 16) + (buf[1] << 8) + buf[2];
  116. printf("The JEDEC ID of SPI flash is 0x%.6X\r\n",SPI_Flash.JedecID);
  117. /***
  118. JEDEC ID IC型号 存储空间大小
  119. 0xEF4015 W25Q16JV-IQ/JQ 16M-bit /2M-byte
  120. 0XEF4017 W25Q64JV-IQ/JQ 64M-bit /8M-byte
  121. 0XEF4018 W25Q128JV-IQ/JQ 128M-bit/16M-byte
  122. ***/
  123. switch(SPI_Flash.JedecID)
  124. {
  125. case 0xEF4015: printf("Flash芯片型号为W25Q16JV-IQ/JQ, 16M-bit /2M-byte\r\n"); break;
  126. case 0xEF4017: printf("Flash芯片型号为W25Q64JV-IQ/JQ, 64M-bit /8M-byte\r\n"); break;
  127. case 0xEF4018: printf("Flash芯片型号为W25Q128JV-IQ/JQ,128M-bit/16M-byte\r\n"); break;
  128. default: printf("Flash芯片型号未知\r\n");
  129. }
  130. }
  131. /*
  132. * @name SPI_Flash_EraseSector
  133. * @brief 擦除扇区(4kB)
  134. * @param SectorAddr:待擦除扇区地址,要求为4k倍数
  135. * @retval None
  136. */
  137. static void SPI_Flash_EraseSector(uint32_t SectorAddr)
  138. {
  139. //检测flash是否处于忙碌状态
  140. SPI_Flash_WaitForWriteEnd();
  141. //Flash写使能,允许擦除
  142. SPI_Flash_WriteEnable();
  143. /* 擦除扇区 */
  144. //选择Flash芯片: CS输出低电平
  145. CLR_SPI_Flash_CS;
  146. //发送命令:扇区擦除
  147. SPI_Flash_WriteByte(W25X_SectorErase);
  148. //发送擦除扇区地址的高字节
  149. SPI_Flash_WriteByte((SectorAddr & 0xFF0000) >> 16);
  150. //发送擦除扇区地址的中字节
  151. SPI_Flash_WriteByte((SectorAddr & 0xFF00) >> 8);
  152. //发送擦除扇区地址的低字节
  153. SPI_Flash_WriteByte(SectorAddr & 0xFF);
  154. //禁用Flash芯片: CS输出高电平
  155. SET_SPI_Flash_CS;
  156. //等待擦除完毕
  157. SPI_Flash_WaitForWriteEnd();
  158. }
  159. /*
  160. * @name SPI_Flash_EraseTotal
  161. * @brief 擦除全部
  162. * @param None
  163. * @retval None
  164. */
  165. static void SPI_Flash_EraseTotal()
  166. {
  167. //检测flash是否处于忙碌状态
  168. SPI_Flash_WaitForWriteEnd();
  169. //Flash写使能,允许擦除
  170. SPI_Flash_WriteEnable();
  171. /* 擦除整片 */
  172. //选择Flash芯片: CS输出低电平
  173. CLR_SPI_Flash_CS;
  174. //发送命令:整片擦除
  175. SPI_Flash_WriteByte(W25X_ChipErase);
  176. //禁用Flash芯片: CS输出高电平
  177. SET_SPI_Flash_CS;
  178. //等待擦除完毕
  179. SPI_Flash_WaitForWriteEnd();
  180. }
  181. /*
  182. * @name SPI_Flash_WritePage
  183. * @brief 写入页(256Bytes),写入长度不超过256字节
  184. * @param pWriteBuffer:待写入数据的指针
  185. * WriteAddr :写入地址
  186. * WriteLength :写入数据长度,必须小于等于SPI_FLASH_PerWritePageSize(256Bytes)
  187. * @retval None
  188. */
  189. static void SPI_Flash_WritePage(uint8_t* pWriteBuffer, uint32_t WriteAddr, uint16_t WriteLength)
  190. {
  191. //检测flash是否处于忙碌状态
  192. SPI_Flash_WaitForWriteEnd();
  193. //Flash写使能,允许写入
  194. SPI_Flash_WriteEnable();
  195. //选择Flash芯片: CS输出低电平
  196. CLR_SPI_Flash_CS;
  197. //发送命令:页面编程
  198. SPI_Flash_WriteByte(W25X_PageProgram);
  199. //发送地址高字节
  200. SPI_Flash_WriteByte((WriteAddr & 0xFF0000) >> 16);
  201. //发送地址中字节
  202. SPI_Flash_WriteByte((WriteAddr & 0xFF00) >> 8);
  203. //发送地址低字节
  204. SPI_Flash_WriteByte(WriteAddr & 0xFF);
  205. if(WriteLength > SPI_FLASH_PageSize)
  206. {
  207. WriteLength = SPI_FLASH_PageSize;
  208. printf("Error: Flash每次写入数据不能超过256字节!\n");
  209. }
  210. //开始写入数据
  211. while (WriteLength--)
  212. {
  213. /* 读取一个字节*/
  214. SPI_Flash_WriteByte(*pWriteBuffer);
  215. /* 指向下一个字节缓冲区 */
  216. pWriteBuffer++;
  217. }
  218. //禁用Flash芯片: CS输出高电平
  219. SET_SPI_Flash_CS;
  220. //等待写入完毕
  221. SPI_Flash_WaitForWriteEnd();
  222. }
  223. /*
  224. * @name SPI_Flash_WriteUnfixed
  225. * @brief 写入不固定长度数据
  226. * @param pWriteBuffer:待写入数据的缓存指针
  227. * WriteAddr :写入地址
  228. * WriteLength :写入数据长度
  229. * @retval None
  230. */
  231. static void SPI_Flash_WriteUnfixed(uint8_t* pWriteBuffer, uint32_t WriteAddr, uint32_t WriteLength)
  232. {
  233. uint32_t PageNumofWirteLength = WriteLength / SPI_FLASH_PageSize; //待写入页数
  234. uint8_t NotEnoughNumofPage = WriteLength % SPI_FLASH_PageSize; //不足一页的数量
  235. uint8_t WriteAddrPageAlignment = WriteAddr % SPI_FLASH_PageSize; //如果取余为0,则地址页对齐,可以写连续写入256字节
  236. uint8_t NotAlignmentNumofPage = SPI_FLASH_PageSize - WriteAddrPageAlignment; //地址不对齐部分,最多可以写入的字节数
  237. //写入地址页对齐
  238. if(WriteAddrPageAlignment == 0)
  239. {
  240. //待写入数据不足一页
  241. if(PageNumofWirteLength == 0)
  242. {
  243. SPI_Flash_WritePage(pWriteBuffer,WriteAddr,WriteLength);
  244. }
  245. //待写入数据超过一页
  246. else
  247. {
  248. //先写入整页
  249. while(PageNumofWirteLength--)
  250. {
  251. SPI_Flash_WritePage(pWriteBuffer,WriteAddr,SPI_FLASH_PageSize);
  252. pWriteBuffer += SPI_FLASH_PageSize;
  253. WriteAddr += SPI_FLASH_PageSize;
  254. }
  255. //再写入不足一页的数据
  256. if(NotEnoughNumofPage > 0)
  257. {
  258. SPI_Flash_WritePage(pWriteBuffer,WriteAddr,NotEnoughNumofPage);
  259. }
  260. }
  261. }
  262. //写入地址与页不对齐
  263. else
  264. {
  265. //待写入数据不足一页
  266. if(PageNumofWirteLength == 0)
  267. {
  268. //不足一页的数据 <= 地址不对齐部分
  269. if(NotEnoughNumofPage <= NotAlignmentNumofPage)
  270. {
  271. SPI_Flash_WritePage(pWriteBuffer,WriteAddr,WriteLength);
  272. }
  273. //不足一页的数据 > 地址不对齐部分
  274. else
  275. {
  276. //先写地址不对齐部分允许写入的最大长度
  277. SPI_Flash_WritePage(pWriteBuffer,WriteAddr,NotAlignmentNumofPage);
  278. pWriteBuffer += NotAlignmentNumofPage;
  279. WriteAddr += NotAlignmentNumofPage;
  280. //再写没写完的数据
  281. SPI_Flash_WritePage(pWriteBuffer,WriteAddr,NotEnoughNumofPage-NotAlignmentNumofPage);
  282. }
  283. }
  284. //待写入数据超过一页
  285. else
  286. {
  287. //先写地址不对齐部分允许写入的最大长度,地址此时对齐了
  288. SPI_Flash_WritePage(pWriteBuffer,WriteAddr,NotAlignmentNumofPage);
  289. pWriteBuffer += NotAlignmentNumofPage;
  290. WriteAddr += NotAlignmentNumofPage;
  291. //地址对其后,重新计算写入页数与不足一页的数量
  292. WriteLength -= NotAlignmentNumofPage;
  293. PageNumofWirteLength = WriteLength / SPI_FLASH_PageSize; //待写入页数
  294. NotEnoughNumofPage = WriteLength % SPI_FLASH_PageSize;
  295. //先写入整页
  296. while(PageNumofWirteLength--)
  297. {
  298. SPI_Flash_WritePage(pWriteBuffer,WriteAddr,SPI_FLASH_PageSize);
  299. pWriteBuffer += SPI_FLASH_PageSize;
  300. WriteAddr += SPI_FLASH_PageSize;
  301. }
  302. //再写入不足一页的数据
  303. if(NotEnoughNumofPage > 0)
  304. {
  305. SPI_Flash_WritePage(pWriteBuffer,WriteAddr,NotEnoughNumofPage);
  306. }
  307. }
  308. }
  309. }
  310. /*
  311. * @name SPI_Flash_ReadUnfixed
  312. * @brief 读取不固定长度数据
  313. * @param pReadBuffer:存放读取数据的缓存指针
  314. * ReadAddr :读取地址
  315. * ReadLength :读取数据长度
  316. * @retval None
  317. */
  318. static void SPI_Flash_ReadUnfixed(uint8_t* pReadBuffer, uint32_t ReadAddr, uint32_t ReadLength)
  319. {
  320. //检测flash是否处于忙碌状态
  321. SPI_Flash_WaitForWriteEnd();
  322. //选择Flash芯片: CS输出低电平
  323. CLR_SPI_Flash_CS;
  324. //发送命令:读取数据
  325. SPI_Flash_WriteByte(W25X_ReadData);
  326. //发送地址高字节
  327. SPI_Flash_WriteByte((ReadAddr & 0xFF0000) >> 16);
  328. //发送地址中字节
  329. SPI_Flash_WriteByte((ReadAddr & 0xFF00) >> 8);
  330. //发送地址低字节
  331. SPI_Flash_WriteByte(ReadAddr & 0xFF);
  332. //开始读取数据
  333. while (ReadLength--)
  334. {
  335. /* 读取一个字节*/
  336. *pReadBuffer = SPI_Flash_ReadByte();
  337. /* 指向下一个字节缓冲区 */
  338. pReadBuffer++;
  339. }
  340. //禁用Flash芯片: CS输出高电平
  341. SET_SPI_Flash_CS;
  342. }
  343. /********************************************************
  344. End Of File
  345. ********************************************************/

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/704051
推荐阅读
相关标签
  

闽ICP备14008679号