当前位置:   article > 正文

ARM学习 SPI_arm spi

arm spi

SPI协议介绍

SPI协议是由摩托罗拉公司提出的通讯协议(Serial Peripheral Interface),即串行外围设备接口, 是一种高速全双工的通信总线。它被广泛地使用在ADC、LCD等设备与MCU间,要求通讯速率较高的场合。

学习本章时,可与I2C章节对比阅读,体会两种通讯总线的差异以及EEPROM存储器与FLASH存储器的区别。下面我们分别对SPI协议的物理层及协议层进行讲解。

SPI通讯设备之间的常用连接方式见图

SPI通讯使用3条总线及片选线,3条总线分别为SCK、MOSI、MISO,片选线为SS,它们的作用介绍如下:

(1) SS ( Slave Select):从设备选择信号线,常称为片选信号线,也称为NSS、CS,以下用NSS表示。当有多个SPI从设备与SPI主机相连时, 设备的其它信号线SCK、MOSI及MISO同时并联到相同的SPI总线上,即无论有多少个从设备,都共同只使用这3条总线; 而每个从设备都有独立的这一条NSS信号线,本信号线独占主机的一个引脚,即有多少个从设备,就有多少条片选信号线。 I2C协议中通过设备地址来寻址、选中总线上的某个设备并与其进行通讯;而SPI协议中没有设备地址,它使用NSS信号线来寻址, 当主机要选择从设备时,把该从设备的NSS信号线设置为低电平,该从设备即被选中,即片选有效, 接着主机开始与被选中的从设备进行SPI通讯。所以SPI通讯以NSS线置低电平为开始信号,以NSS线被拉高作为结束信号。

(2) SCK (Serial Clock):时钟信号线,用于通讯数据同步。它由通讯主机产生,决定了通讯的速率,不同的设备支持的最高时钟频率不一样, 如STM32的SPI时钟频率最大为fpclk/2,两个设备之间通讯时,通讯速率受限于低速设备。

(3) MOSI (Master Output, Slave Input):主设备输出/从设备输入引脚。主机的数据从这条信号线输出, 从机由这条信号线读入主机发送的数据,即这条线上数据的方向为主机到从机。

(4) MISO (Master Input,,Slave Output):主设备输入/从设备输出引脚。主机从这条信号线读入数据, 从机的数据由这条信号线输出到主机,即在这条线上数据的方向为从机到主机。

SPI通讯时序

由CPOL及CPHA的不同状态,SPI分成了四种模式,如图, 主机与从机需要工作在相同的模式下才可以正常通讯,实际中采用较多的是“模式0”与“模式3”。

初始化SPI的 GPIO

  1. void SPI_FLASH_Init(void)
  2. {
  3. SPI_InitTypeDef SPI_InitStructure;
  4. GPIO_InitTypeDef GPIO_InitStructure;
  5. /* 使能GPIO和SPI时钟 */
  6. FLASH_SPI_APBxClock_FUN ( FLASH_SPI_CLK, ENABLE );
  7. FLASH_SPI_SCK_APBxClock_FUN ( FLASH_SPI_SCK_CLK, ENABLE );
  8. FLASH_SPI_MISO_APBxClock_FUN ( FLASH_SPI_MISO_CLK, ENABLE );
  9. FLASH_SPI_MOSI_APBxClock_FUN ( FLASH_SPI_MOSI_CLK, ENABLE );
  10. FLASH_SPI_CS_APBxClock_FUN ( FLASH_SPI_CS_CLK, ENABLE );
  11. /* 配置SPI功能引脚:SCK 时钟引脚 */
  12. GPIO_InitStructure.GPIO_Pin = FLASH_SPI_SCK_PIN;
  13. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  14. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  15. GPIO_Init(FLASH_SPI_SCK_PORT, &GPIO_InitStructure);
  16. /* 配置SPI功能引脚:MISO 主机输入从机输出引脚 */
  17. GPIO_InitStructure.GPIO_Pin = FLASH_SPI_MISO_PIN;
  18. GPIO_Init(FLASH_SPI_MISO_PORT, &GPIO_InitStructure);
  19. /* 配置SPI功能引脚:MISO 主机输出从机输入引脚 */
  20. GPIO_InitStructure.GPIO_Pin = FLASH_SPI_MOSI_PIN;
  21. GPIO_Init(FLASH_SPI_MOSI_PORT, &GPIO_InitStructure);
  22. /* 配置SPI功能引脚:CS 串行Flash片选引脚 */
  23. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  24. GPIO_InitStructure.GPIO_Pin = FLASH_SPI_CS_PIN;
  25. GPIO_Init(FLASH_SPI_CS_PORT, &GPIO_InitStructure);
  26. /* 首先禁用串行Flash,等需要操作串行Flash时再使能即可 */
  27. FLASH_SPI_CS_DISABLE();
  28. /* SPI外设配置 */
  29. /*
  30. * FLASH芯片:
  31. * 在CLK上升沿时到DIO数据采样输入.
  32. * 在CLK下降沿时在DIO进行数据输出。
  33. * 据此设置CPOL CPHA
  34. */
  35. SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
  36. SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
  37. SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
  38. SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
  39. SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
  40. SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
  41. SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;
  42. SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
  43. SPI_InitStructure.SPI_CRCPolynomial = 7;
  44. SPI_Init(FLASH_SPIx , &SPI_InitStructure);
  45. /* 使能SPI外设 */
  46. SPI_Cmd(FLASH_SPIx , ENABLE);
  47. }

2.擦除串行Flash整片空间

  1. void SPI_FLASH_BulkErase(void)
  2. {
  3. /* 发送FLASH写使能命令 */
  4. SPI_FLASH_WriteEnable();
  5. /* 整片擦除 Erase */
  6. /* 选择串行FLASH: CS低电平 */
  7. FLASH_SPI_CS_ENABLE();
  8. /* 发送整片擦除指令*/
  9. SPI_FLASH_SendByte(W25X_ChipErase);
  10. /* 禁用串行FLASH: CS高电平 */
  11. FLASH_SPI_CS_DISABLE();
  12. /* 等待擦除完毕*/
  13. SPI_FLASH_WaitForWriteEnd();
  14. }

3.写入数据

  1. void SPI_FLASH_BufferWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite)
  2. {
  3. u8 NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0, temp = 0;
  4. Addr = WriteAddr % SPI_FLASH_PageSize;
  5. count = SPI_FLASH_PageSize - Addr;
  6. NumOfPage = NumByteToWrite / SPI_FLASH_PageSize;
  7. NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;
  8. if (Addr == 0) /* 若地址与 SPI_FLASH_PageSize 对齐 */
  9. {
  10. if (NumOfPage == 0) /* NumByteToWrite < SPI_FLASH_PageSize */
  11. {
  12. SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);
  13. }
  14. else /* NumByteToWrite > SPI_FLASH_PageSize */
  15. {
  16. while (NumOfPage--)
  17. {
  18. SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize);
  19. WriteAddr += SPI_FLASH_PageSize;
  20. pBuffer += SPI_FLASH_PageSize;
  21. }
  22. SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);
  23. }
  24. }
  25. else /* 若地址与 SPI_FLASH_PageSize 不对齐 */
  26. {
  27. if (NumOfPage == 0) /* NumByteToWrite < SPI_FLASH_PageSize */
  28. {
  29. if (NumOfSingle > count) /* (NumByteToWrite + WriteAddr) > SPI_FLASH_PageSize */
  30. {
  31. temp = NumOfSingle - count;
  32. SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);
  33. WriteAddr += count;
  34. pBuffer += count;
  35. SPI_FLASH_PageWrite(pBuffer, WriteAddr, temp);
  36. }
  37. else
  38. {
  39. SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);
  40. }
  41. }
  42. else /* NumByteToWrite > SPI_FLASH_PageSize */
  43. {
  44. NumByteToWrite -= count;
  45. NumOfPage = NumByteToWrite / SPI_FLASH_PageSize;
  46. NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;
  47. SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);
  48. WriteAddr += count;
  49. pBuffer += count;
  50. while (NumOfPage--)
  51. {
  52. SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize);
  53. WriteAddr += SPI_FLASH_PageSize;
  54. pBuffer += SPI_FLASH_PageSize;
  55. }
  56. if (NumOfSingle != 0)
  57. {
  58. SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);
  59. }
  60. }
  61. }
  62. }

4.读取数据

  1. void SPI_FLASH_BufferRead(u8* pBuffer, u32 ReadAddr, u16 NumByteToRead)
  2. {
  3. /* 选择串行FLASH: CS低电平 */
  4. FLASH_SPI_CS_ENABLE();
  5. /* 发送 读 指令 */
  6. SPI_FLASH_SendByte(W25X_ReadData);
  7. /* 发送 读 地址高位 */
  8. SPI_FLASH_SendByte((ReadAddr & 0xFF0000) >> 16);
  9. /* 发送 读 地址中位 */
  10. SPI_FLASH_SendByte((ReadAddr& 0xFF00) >> 8);
  11. /* 发送 读 地址低位 */
  12. SPI_FLASH_SendByte(ReadAddr & 0xFF);
  13. while (NumByteToRead--) /* 读取数据 */
  14. {
  15. /* 读取一个字节*/
  16. *pBuffer = SPI_FLASH_SendByte(Dummy_Byte);
  17. /* 指向下一个字节缓冲区 */
  18. pBuffer++;
  19. }
  20. /* 禁用串行FLASH: CS 高电平 */
  21. FLASH_SPI_CS_DISABLE();
  22. }

函数介绍

1.bsp_spi_flash.c 

  1. /* 包含头文件 ----------------------------------------------------------------*/
  2. #include "bsp/spi_flash/bsp_spi_flash.h"
  3. /* 私有类型定义 --------------------------------------------------------------*/
  4. /* 私有宏定义 ----------------------------------------------------------------*/
  5. #define SPI_FLASH_PageSize 256
  6. #define SPI_FLASH_PerWritePageSize 256
  7. #define W25X_WriteEnable 0x06
  8. #define W25X_WriteDisable 0x04
  9. #define W25X_ReadStatusReg 0x05
  10. #define W25X_WriteStatusReg 0x01
  11. #define W25X_ReadData 0x03
  12. #define W25X_FastReadData 0x0B
  13. #define W25X_FastReadDual 0x3B
  14. #define W25X_PageProgram 0x02
  15. #define W25X_BlockErase 0xD8
  16. #define W25X_SectorErase 0x20
  17. #define W25X_ChipErase 0xC7
  18. #define W25X_PowerDown 0xB9
  19. #define W25X_ReleasePowerDown 0xAB
  20. #define W25X_DeviceID 0xAB
  21. #define W25X_ManufactDeviceID 0x90
  22. #define W25X_JedecDeviceID 0x9F
  23. #define WIP_Flag 0x01 /* Write In Progress (WIP) flag */
  24. #define Dummy_Byte 0xFF
  25. /* 私有变量 ------------------------------------------------------------------*/
  26. /* 扩展变量 ------------------------------------------------------------------*/
  27. /* 私有函数原形 --------------------------------------------------------------*/
  28. /* 函数体 --------------------------------------------------------------------*/
  29. /**
  30. * 函数功能: 串行FLASH初始化
  31. * 输入参数: 无
  32. * 返 回 值: uint32_t:返回串行Flash型号ID
  33. * 说 明:初始化串行Flash底层驱动GPIO和SPI外设
  34. */
  35. void SPI_FLASH_Init(void)
  36. {
  37. SPI_InitTypeDef SPI_InitStructure;
  38. GPIO_InitTypeDef GPIO_InitStructure;
  39. /* 使能GPIO和SPI时钟 */
  40. FLASH_SPI_APBxClock_FUN ( FLASH_SPI_CLK, ENABLE );
  41. FLASH_SPI_SCK_APBxClock_FUN ( FLASH_SPI_SCK_CLK, ENABLE );
  42. FLASH_SPI_MISO_APBxClock_FUN ( FLASH_SPI_MISO_CLK, ENABLE );
  43. FLASH_SPI_MOSI_APBxClock_FUN ( FLASH_SPI_MOSI_CLK, ENABLE );
  44. FLASH_SPI_CS_APBxClock_FUN ( FLASH_SPI_CS_CLK, ENABLE );
  45. /* 配置SPI功能引脚:SCK 时钟引脚 */
  46. GPIO_InitStructure.GPIO_Pin = FLASH_SPI_SCK_PIN;
  47. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  48. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  49. GPIO_Init(FLASH_SPI_SCK_PORT, &GPIO_InitStructure);
  50. /* 配置SPI功能引脚:MISO 主机输入从机输出引脚 */
  51. GPIO_InitStructure.GPIO_Pin = FLASH_SPI_MISO_PIN;
  52. GPIO_Init(FLASH_SPI_MISO_PORT, &GPIO_InitStructure);
  53. /* 配置SPI功能引脚:MISO 主机输出从机输入引脚 */
  54. GPIO_InitStructure.GPIO_Pin = FLASH_SPI_MOSI_PIN;
  55. GPIO_Init(FLASH_SPI_MOSI_PORT, &GPIO_InitStructure);
  56. /* 配置SPI功能引脚:CS 串行Flash片选引脚 */
  57. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  58. GPIO_InitStructure.GPIO_Pin = FLASH_SPI_CS_PIN;
  59. GPIO_Init(FLASH_SPI_CS_PORT, &GPIO_InitStructure);
  60. /* 首先禁用串行Flash,等需要操作串行Flash时再使能即可 */
  61. FLASH_SPI_CS_DISABLE();
  62. /* SPI外设配置 */
  63. /*
  64. * FLASH芯片:
  65. * 在CLK上升沿时到DIO数据采样输入.
  66. * 在CLK下降沿时在DIO进行数据输出。
  67. * 据此设置CPOL CPHA
  68. */
  69. SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
  70. SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
  71. SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
  72. SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
  73. SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
  74. SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
  75. SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;
  76. SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
  77. SPI_InitStructure.SPI_CRCPolynomial = 7;
  78. SPI_Init(FLASH_SPIx , &SPI_InitStructure);
  79. /* 使能SPI外设 */
  80. SPI_Cmd(FLASH_SPIx , ENABLE);
  81. }
  82. /**
  83. * 函数功能: 擦除扇区
  84. * 输入参数: SectorAddr:待擦除扇区地址,要求为4096倍数
  85. * 返 回 值: 无
  86. * 说 明:串行Flash最小擦除块大小为4KB(4096字节),即一个扇区大小,要求输入参数
  87. * 为4096倍数。在往串行Flash芯片写入数据之前要求先擦除空间。
  88. */
  89. void SPI_FLASH_SectorErase(u32 SectorAddr)
  90. {
  91. /* 发送FLASH写使能命令 */
  92. SPI_FLASH_WriteEnable();
  93. SPI_FLASH_WaitForWriteEnd();
  94. /* 擦除扇区 */
  95. /* 选择串行FLASH: CS低电平 */
  96. FLASH_SPI_CS_ENABLE();
  97. /* 发送扇区擦除指令*/
  98. SPI_FLASH_SendByte(W25X_SectorErase);
  99. /*发送擦除扇区地址的高位*/
  100. SPI_FLASH_SendByte((SectorAddr & 0xFF0000) >> 16);
  101. /* 发送擦除扇区地址的中位 */
  102. SPI_FLASH_SendByte((SectorAddr & 0xFF00) >> 8);
  103. /* 发送擦除扇区地址的低位 */
  104. SPI_FLASH_SendByte(SectorAddr & 0xFF);
  105. /* 禁用串行FLASH: CS 高电平 */
  106. FLASH_SPI_CS_DISABLE();
  107. /* 等待擦除完毕*/
  108. SPI_FLASH_WaitForWriteEnd();
  109. }
  110. /**
  111. * 函数功能: 擦除整片
  112. * 输入参数: 无
  113. * 返 回 值: 无
  114. * 说 明:擦除串行Flash整片空间
  115. */
  116. void SPI_FLASH_BulkErase(void)
  117. {
  118. /* 发送FLASH写使能命令 */
  119. SPI_FLASH_WriteEnable();
  120. /* 整片擦除 Erase */
  121. /* 选择串行FLASH: CS低电平 */
  122. FLASH_SPI_CS_ENABLE();
  123. /* 发送整片擦除指令*/
  124. SPI_FLASH_SendByte(W25X_ChipErase);
  125. /* 禁用串行FLASH: CS高电平 */
  126. FLASH_SPI_CS_DISABLE();
  127. /* 等待擦除完毕*/
  128. SPI_FLASH_WaitForWriteEnd();
  129. }
  130. /**
  131. * 函数功能: 往串行FLASH按页写入数据,调用本函数写入数据前需要先擦除扇区
  132. * 输入参数: pBuffer:待写入数据的指针
  133. * WriteAddr:写入地址
  134. * NumByteToWrite:写入数据长度,必须小于等于SPI_FLASH_PerWritePageSize
  135. * 返 回 值: 无
  136. * 说 明:串行Flash每页大小为256个字节
  137. */
  138. void SPI_FLASH_PageWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite)
  139. {
  140. /* 发送FLASH写使能命令 */
  141. SPI_FLASH_WriteEnable();
  142. /* 寻找串行FLASH: CS低电平 */
  143. FLASH_SPI_CS_ENABLE();
  144. /* 写送写指令*/
  145. SPI_FLASH_SendByte(W25X_PageProgram);
  146. /*发送写地址的高位*/
  147. SPI_FLASH_SendByte((WriteAddr & 0xFF0000) >> 16);
  148. /*发送写地址的中位*/
  149. SPI_FLASH_SendByte((WriteAddr & 0xFF00) >> 8);
  150. /*发送写地址的低位*/
  151. SPI_FLASH_SendByte(WriteAddr & 0xFF);
  152. if(NumByteToWrite > SPI_FLASH_PerWritePageSize)
  153. {
  154. NumByteToWrite = SPI_FLASH_PerWritePageSize;
  155. //printf("Err: SPI_FLASH_PageWrite too large!\n");
  156. }
  157. /* 写入数据*/
  158. while (NumByteToWrite--)
  159. {
  160. /* 发送当前要写入的字节数据 */
  161. SPI_FLASH_SendByte(*pBuffer);
  162. /* 指向下一字节数据 */
  163. pBuffer++;
  164. }
  165. /* 禁用串行FLASH: CS 高电平 */
  166. FLASH_SPI_CS_DISABLE();
  167. /* 等待写入完毕*/
  168. SPI_FLASH_WaitForWriteEnd();
  169. }
  170. /**
  171. * 函数功能: 往串行FLASH写入数据,调用本函数写入数据前需要先擦除扇区
  172. * 输入参数: pBuffer:待写入数据的指针
  173. * WriteAddr:写入地址
  174. * NumByteToWrite:写入数据长度
  175. * 返 回 值: 无
  176. * 说 明:该函数可以设置任意写入数据长度
  177. */
  178. void SPI_FLASH_BufferWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite)
  179. {
  180. u8 NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0, temp = 0;
  181. Addr = WriteAddr % SPI_FLASH_PageSize;
  182. count = SPI_FLASH_PageSize - Addr;
  183. NumOfPage = NumByteToWrite / SPI_FLASH_PageSize;
  184. NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;
  185. if (Addr == 0) /* 若地址与 SPI_FLASH_PageSize 对齐 */
  186. {
  187. if (NumOfPage == 0) /* NumByteToWrite < SPI_FLASH_PageSize */
  188. {
  189. SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);
  190. }
  191. else /* NumByteToWrite > SPI_FLASH_PageSize */
  192. {
  193. while (NumOfPage--)
  194. {
  195. SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize);
  196. WriteAddr += SPI_FLASH_PageSize;
  197. pBuffer += SPI_FLASH_PageSize;
  198. }
  199. SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);
  200. }
  201. }
  202. else /* 若地址与 SPI_FLASH_PageSize 不对齐 */
  203. {
  204. if (NumOfPage == 0) /* NumByteToWrite < SPI_FLASH_PageSize */
  205. {
  206. if (NumOfSingle > count) /* (NumByteToWrite + WriteAddr) > SPI_FLASH_PageSize */
  207. {
  208. temp = NumOfSingle - count;
  209. SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);
  210. WriteAddr += count;
  211. pBuffer += count;
  212. SPI_FLASH_PageWrite(pBuffer, WriteAddr, temp);
  213. }
  214. else
  215. {
  216. SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);
  217. }
  218. }
  219. else /* NumByteToWrite > SPI_FLASH_PageSize */
  220. {
  221. NumByteToWrite -= count;
  222. NumOfPage = NumByteToWrite / SPI_FLASH_PageSize;
  223. NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;
  224. SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);
  225. WriteAddr += count;
  226. pBuffer += count;
  227. while (NumOfPage--)
  228. {
  229. SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize);
  230. WriteAddr += SPI_FLASH_PageSize;
  231. pBuffer += SPI_FLASH_PageSize;
  232. }
  233. if (NumOfSingle != 0)
  234. {
  235. SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);
  236. }
  237. }
  238. }
  239. }
  240. /**
  241. * 函数功能: 从串行Flash读取数据
  242. * 输入参数: pBuffer:存放读取到数据的指针
  243. * ReadAddr:读取数据目标地址
  244. * NumByteToRead:读取数据长度
  245. * 返 回 值: 无
  246. * 说 明:该函数可以设置任意读取数据长度
  247. */
  248. void SPI_FLASH_BufferRead(u8* pBuffer, u32 ReadAddr, u16 NumByteToRead)
  249. {
  250. /* 选择串行FLASH: CS低电平 */
  251. FLASH_SPI_CS_ENABLE();
  252. /* 发送 读 指令 */
  253. SPI_FLASH_SendByte(W25X_ReadData);
  254. /* 发送 读 地址高位 */
  255. SPI_FLASH_SendByte((ReadAddr & 0xFF0000) >> 16);
  256. /* 发送 读 地址中位 */
  257. SPI_FLASH_SendByte((ReadAddr& 0xFF00) >> 8);
  258. /* 发送 读 地址低位 */
  259. SPI_FLASH_SendByte(ReadAddr & 0xFF);
  260. while (NumByteToRead--) /* 读取数据 */
  261. {
  262. /* 读取一个字节*/
  263. *pBuffer = SPI_FLASH_SendByte(Dummy_Byte);
  264. /* 指向下一个字节缓冲区 */
  265. pBuffer++;
  266. }
  267. /* 禁用串行FLASH: CS 高电平 */
  268. FLASH_SPI_CS_DISABLE();
  269. }
  270. /**
  271. * 函数功能: 读取串行Flash型号的ID
  272. * 输入参数: 无
  273. * 返 回 值: u32:串行Flash的型号ID
  274. * 说 明: FLASH_ID IC型号 存储空间大小
  275. 0xEF3015 W25X16 2M byte
  276. 0xEF4015 W25Q16 4M byte
  277. 0XEF4017 W25Q64 8M byte
  278. 0XEF4018 W25Q128 16M byte (YS-F1Pro开发板默认配置)
  279. */
  280. u32 SPI_FLASH_ReadID(void)
  281. {
  282. u32 Temp = 0, Temp0 = 0, Temp1 = 0, Temp2 = 0;
  283. /* 选择串行FLASH: CS低电平 */
  284. FLASH_SPI_CS_ENABLE();
  285. /* 发送命令:读取芯片型号ID */
  286. SPI_FLASH_SendByte(W25X_JedecDeviceID);
  287. /* 从串行Flash读取一个字节数据 */
  288. Temp0 = SPI_FLASH_SendByte(Dummy_Byte);
  289. /* 从串行Flash读取一个字节数据 */
  290. Temp1 = SPI_FLASH_SendByte(Dummy_Byte);
  291. /* 从串行Flash读取一个字节数据 */
  292. Temp2 = SPI_FLASH_SendByte(Dummy_Byte);
  293. /* 禁用串行Flash:CS高电平 */
  294. FLASH_SPI_CS_DISABLE();
  295. Temp = (Temp0 << 16) | (Temp1 << 8) | Temp2;
  296. return Temp;
  297. }
  298. /**
  299. * 函数功能: 读取串行Flash设备ID
  300. * 输入参数: 无
  301. * 返 回 值: u32:串行Flash的设备ID
  302. * 说 明:
  303. */
  304. u32 SPI_FLASH_ReadDeviceID(void)
  305. {
  306. u32 Temp = 0;
  307. /* 选择串行FLASH: CS低电平 */
  308. FLASH_SPI_CS_ENABLE();
  309. /* 发送命令:读取芯片设备ID * */
  310. SPI_FLASH_SendByte(W25X_DeviceID);
  311. SPI_FLASH_SendByte(Dummy_Byte);
  312. SPI_FLASH_SendByte(Dummy_Byte);
  313. SPI_FLASH_SendByte(Dummy_Byte);
  314. /* 从串行Flash读取一个字节数据 */
  315. Temp = SPI_FLASH_SendByte(Dummy_Byte);
  316. /* 禁用串行Flash:CS高电平 */
  317. FLASH_SPI_CS_DISABLE();
  318. return Temp;
  319. }
  320. /**
  321. * 函数功能: 启动连续读取数据串
  322. * 输入参数: ReadAddr:读取地址
  323. * 返 回 值: 无
  324. * 说 明:Initiates a read data byte (READ) sequence from the Flash.
  325. * This is done by driving the /CS line low to select the device,
  326. * then the READ instruction is transmitted followed by 3 bytes
  327. * address. This function exit and keep the /CS line low, so the
  328. * Flash still being selected. With this technique the whole
  329. * content of the Flash is read with a single READ instruction.
  330. */
  331. void SPI_FLASH_StartReadSequence(u32 ReadAddr)
  332. {
  333. /* Select the FLASH: Chip Select low */
  334. FLASH_SPI_CS_ENABLE();
  335. /* Send "Read from Memory " instruction */
  336. SPI_FLASH_SendByte(W25X_ReadData);
  337. /* Send the 24-bit address of the address to read from -----------------------*/
  338. /* Send ReadAddr high nibble address byte */
  339. SPI_FLASH_SendByte((ReadAddr & 0xFF0000) >> 16);
  340. /* Send ReadAddr medium nibble address byte */
  341. SPI_FLASH_SendByte((ReadAddr& 0xFF00) >> 8);
  342. /* Send ReadAddr low nibble address byte */
  343. SPI_FLASH_SendByte(ReadAddr & 0xFF);
  344. }
  345. /**
  346. * 函数功能: 从串行Flash读取一个字节数据
  347. * 输入参数: 无
  348. * 返 回 值: u8:读取到的数据
  349. * 说 明:This function must be used only if the Start_Read_Sequence
  350. * function has been previously called.
  351. */
  352. u8 SPI_FLASH_ReadByte(void)
  353. {
  354. return (SPI_FLASH_SendByte(Dummy_Byte));
  355. }
  356. /**
  357. * 函数功能: 往串行Flash读取写入一个字节数据并接收一个字节数据
  358. * 输入参数: byte:待发送数据
  359. * 返 回 值: u8:接收到的数据
  360. * 说 明:无
  361. */
  362. u8 SPI_FLASH_SendByte(u8 byte)
  363. {
  364. /* 循环等待直到SPI 数据寄存器DR为空,即当DR寄存器不为空时持续等待 */
  365. while (SPI_I2S_GetFlagStatus(FLASH_SPIx , SPI_I2S_FLAG_TXE) == RESET);
  366. /* 通过SPI外设发送一个字节数据 */
  367. SPI_I2S_SendData(FLASH_SPIx , byte);
  368. /* 等待接收到数据 */
  369. while (SPI_I2S_GetFlagStatus(FLASH_SPIx , SPI_I2S_FLAG_RXNE) == RESET);
  370. /* 读取SPI总线接收到一个字节数据并返回 */
  371. return SPI_I2S_ReceiveData(FLASH_SPIx );
  372. }
  373. /**
  374. * 函数功能: 往串行Flash读取写入半字(16bit)数据并接收半字数据
  375. * 输入参数: byte:待发送数据
  376. * 返 回 值: u16:接收到的数据
  377. * 说 明:无
  378. */
  379. u16 SPI_FLASH_SendHalfWord(u16 HalfWord)
  380. {
  381. /* 循环等待直到SPI 数据寄存器DR为空,即当DR寄存器不为空时持续等待 */
  382. while (SPI_I2S_GetFlagStatus(FLASH_SPIx , SPI_I2S_FLAG_TXE) == RESET);
  383. /* 通过SPI外设发送半字数据 */
  384. SPI_I2S_SendData(FLASH_SPIx , HalfWord);
  385. /* 等待接收到数据 */
  386. while (SPI_I2S_GetFlagStatus(FLASH_SPIx , SPI_I2S_FLAG_RXNE) == RESET);
  387. /* 读取SPI总线接收到半字数据并返回 */
  388. return SPI_I2S_ReceiveData(FLASH_SPIx );
  389. }
  390. /**
  391. * 函数功能: 使能串行Flash写操作
  392. * 输入参数: 无
  393. * 返 回 值: 无
  394. * 说 明:无
  395. */
  396. void SPI_FLASH_WriteEnable(void)
  397. {
  398. /* 选择串行FLASH: CS低电平 */
  399. FLASH_SPI_CS_ENABLE();
  400. /* 发送命令:写使能 */
  401. SPI_FLASH_SendByte(W25X_WriteEnable);
  402. /* 禁用串行Flash:CS高电平 */
  403. FLASH_SPI_CS_DISABLE();
  404. }
  405. /**
  406. * 函数功能: 等待数据写入完成
  407. * 输入参数: 无
  408. * 返 回 值: 无
  409. * 说 明:Polls the status of the Write In Progress (WIP) flag in the
  410. * FLASH's status register and loop until write opertaion
  411. * has completed.
  412. */
  413. void SPI_FLASH_WaitForWriteEnd(void)
  414. {
  415. u8 FLASH_Status = 0;
  416. /* Select the FLASH: Chip Select low */
  417. FLASH_SPI_CS_ENABLE();
  418. /* Send "Read Status Register" instruction */
  419. SPI_FLASH_SendByte(W25X_ReadStatusReg);
  420. /* Loop as long as the memory is busy with a write cycle */
  421. do
  422. {
  423. /* Send a dummy byte to generate the clock needed by the FLASH
  424. and put the value of the status register in FLASH_Status variable */
  425. FLASH_Status = SPI_FLASH_SendByte(Dummy_Byte);
  426. }
  427. while ((FLASH_Status & WIP_Flag) == SET); /* Write in progress */
  428. /* Deselect the FLASH: Chip Select high */
  429. FLASH_SPI_CS_DISABLE();
  430. }
  431. /**
  432. * 函数功能: 进入掉电模式
  433. * 输入参数: 无
  434. * 返 回 值: 无
  435. * 说 明:无
  436. */
  437. void SPI_Flash_PowerDown(void)
  438. {
  439. /* Select the FLASH: Chip Select low */
  440. FLASH_SPI_CS_ENABLE();
  441. /* Send "Power Down" instruction */
  442. SPI_FLASH_SendByte(W25X_PowerDown);
  443. /* Deselect the FLASH: Chip Select high */
  444. FLASH_SPI_CS_DISABLE();
  445. }
  446. /**
  447. * 函数功能: 唤醒串行Flash
  448. * 输入参数: 无
  449. * 返 回 值: 无
  450. * 说 明:无
  451. */
  452. void SPI_Flash_WAKEUP(void)
  453. {
  454. /* Select the FLASH: Chip Select low */
  455. FLASH_SPI_CS_ENABLE();
  456. /* Send "Power Down" instruction */
  457. SPI_FLASH_SendByte(W25X_ReleasePowerDown);
  458. /* Deselect the FLASH: Chip Select high */
  459. FLASH_SPI_CS_DISABLE();
  460. }

2.bsp_spi_flash.h

  1. #ifndef __SPI_FLASH_H__
  2. #define __SPI_FLASH_H__
  3. /* 包含头文件 ----------------------------------------------------------------*/
  4. #include <stm32f10x.h>
  5. /* 类型定义 ------------------------------------------------------------------*/
  6. /* 宏定义 --------------------------------------------------------------------*/
  7. //#define SPI_FLASH_ID 0xEF3015 //W25X16
  8. //#define SPI_FLASH_ID 0xEF4015 //W25Q16
  9. //#define SPI_FLASH_ID 0XEF4017 //W25Q64
  10. #define SPI_FLASH_ID 0XEF4018 //W25Q128 YS-F1Pro开发默认使用
  11. /************************** SPI Flash 连接引脚定义********************************/
  12. #define FLASH_SPIx SPI1
  13. #define FLASH_SPI_APBxClock_FUN RCC_APB2PeriphClockCmd
  14. #define FLASH_SPI_CLK RCC_APB2Periph_SPI1
  15. #define FLASH_SPI_SCK_APBxClock_FUN RCC_APB2PeriphClockCmd
  16. #define FLASH_SPI_SCK_CLK RCC_APB2Periph_GPIOA
  17. #define FLASH_SPI_SCK_PORT GPIOA
  18. #define FLASH_SPI_SCK_PIN GPIO_Pin_5
  19. #define FLASH_SPI_MISO_APBxClock_FUN RCC_APB2PeriphClockCmd
  20. #define FLASH_SPI_MISO_CLK RCC_APB2Periph_GPIOA
  21. #define FLASH_SPI_MISO_PORT GPIOA
  22. #define FLASH_SPI_MISO_PIN GPIO_Pin_6
  23. #define FLASH_SPI_MOSI_APBxClock_FUN RCC_APB2PeriphClockCmd
  24. #define FLASH_SPI_MOSI_CLK RCC_APB2Periph_GPIOA
  25. #define FLASH_SPI_MOSI_PORT GPIOA
  26. #define FLASH_SPI_MOSI_PIN GPIO_Pin_7
  27. #define FLASH_SPI_CS_APBxClock_FUN RCC_APB2PeriphClockCmd
  28. #define FLASH_SPI_CS_CLK RCC_APB2Periph_GPIOA
  29. #define FLASH_SPI_CS_PORT GPIOA
  30. #define FLASH_SPI_CS_PIN GPIO_Pin_4
  31. #define FLASH_SPI_CS_ENABLE() GPIO_ResetBits(FLASH_SPI_CS_PORT, FLASH_SPI_CS_PIN)
  32. #define FLASH_SPI_CS_DISABLE() GPIO_SetBits(FLASH_SPI_CS_PORT, FLASH_SPI_CS_PIN)
  33. #define CALIBRATE_DATA_ADDR 2*4096
  34. /* 扩展变量 ------------------------------------------------------------------*/
  35. /* 函数声明 ------------------------------------------------------------------*/
  36. void SPI_FLASH_Init(void);
  37. void SPI_FLASH_SectorErase(uint32_t SectorAddr);
  38. void SPI_FLASH_BulkErase(void);
  39. void SPI_FLASH_PageWrite(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite);
  40. void SPI_FLASH_BufferWrite(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite);
  41. void SPI_FLASH_BufferRead(uint8_t* pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead);
  42. uint32_t SPI_FLASH_ReadID(void);
  43. uint32_t SPI_FLASH_ReadDeviceID(void);
  44. void SPI_FLASH_StartReadSequence(uint32_t ReadAddr);
  45. void SPI_Flash_PowerDown(void);
  46. void SPI_Flash_WAKEUP(void);
  47. uint8_t SPI_FLASH_ReadByte(void);
  48. uint8_t SPI_FLASH_SendByte(uint8_t byte);
  49. uint16_t SPI_FLASH_SendHalfWord(uint16_t HalfWord);
  50. void SPI_FLASH_WriteEnable(void);
  51. void SPI_FLASH_WaitForWriteEnd(void);
  52. #endif /* __SPI_FLASH_H__ */

3.main.c

  1. /* 包含头文件 ----------------------------------------------------------------*/
  2. #include "stm32f10x.h"
  3. #include "bsp/led/bsp_led.h"
  4. #include "bsp/usart/bsp_debug_usart.h"
  5. #include "bsp/spi_flash/bsp_spi_flash.h"
  6. /* 私有类型定义 --------------------------------------------------------------*/
  7. typedef enum { FAILED = 0, PASSED = !FAILED} TestStatus;
  8. /* 私有宏定义 ----------------------------------------------------------------*/
  9. /* 获取缓冲区的长度 */
  10. #define countof(a) (sizeof(a) / sizeof(*(a)))
  11. #define TxBufferSize1 (countof(TxBuffer1) - 1)
  12. #define RxBufferSize1 (countof(TxBuffer1) - 1)
  13. #define BufferSize (countof(Tx_Buffer)-1)
  14. #define FLASH_WriteAddress 0x00000
  15. #define FLASH_ReadAddress FLASH_WriteAddress
  16. #define FLASH_SectorToErase FLASH_WriteAddress
  17. /* 私有变量 ------------------------------------------------------------------*/
  18. /* 发送缓冲区初始化 */
  19. uint8_t Tx_Buffer[] = " 感谢您选用硬石stm32开发板\n今天是个好日子";
  20. uint8_t Rx_Buffer[BufferSize];
  21. __IO uint32_t DeviceID = 0;
  22. __IO uint32_t FlashID = 0;
  23. __IO TestStatus TransferStatus1 = FAILED;
  24. /* 扩展变量 ------------------------------------------------------------------*/
  25. /* 私有函数原形 --------------------------------------------------------------*/
  26. static void Delay(uint32_t time);
  27. static TestStatus Buffercmp(uint8_t* pBuffer1, uint8_t* pBuffer2, uint16_t BufferLength);
  28. /* 函数体 --------------------------------------------------------------------*/
  29. /**
  30. * 函数功能: 主函数.
  31. * 输入参数: 无
  32. * 返 回 值: 无
  33. * 说 明: 无
  34. */
  35. int main(void)
  36. {
  37. /* 调试串口初始化配置,115200-N-8-1.使能串口发送和接受 */
  38. DEBUG_USART_Init();
  39. /*初始化LED*/
  40. LED_GPIO_Init();
  41. /* 调用格式化输出函数打印输出数据 */
  42. printf("这是一个16M byte串行flash(W25Q128)读写测试实验\n");
  43. /* 16M串行flash W25Q128初始化 */
  44. SPI_FLASH_Init();
  45. /* Get SPI Flash Device ID */
  46. DeviceID = SPI_FLASH_ReadDeviceID();
  47. Delay( 1 );
  48. /* Get SPI Flash ID */
  49. FlashID = SPI_FLASH_ReadID();
  50. printf("FlashID is 0x%X, Manufacturer Device ID is 0x%X\n", FlashID, DeviceID);
  51. /* Check the SPI Flash ID */
  52. if (FlashID == SPI_FLASH_ID) /* #define sFLASH_ID 0XEF4018 */
  53. {
  54. printf("检测到华邦串行flash W25Q128 !\n");
  55. /* 擦除SPI的扇区以写入 */
  56. SPI_FLASH_SectorErase(FLASH_SectorToErase);
  57. /* 将发送缓冲区的数据写到flash中 */
  58. SPI_FLASH_BufferWrite(Tx_Buffer, FLASH_WriteAddress, BufferSize);
  59. SPI_FLASH_BufferWrite(Tx_Buffer, 252, BufferSize);
  60. printf("写入的数据为:\n%s \n", Tx_Buffer);
  61. /* 将刚刚写入的数据读出来放到接收缓冲区中 */
  62. SPI_FLASH_BufferRead(Rx_Buffer, FLASH_ReadAddress, BufferSize);
  63. printf("读出的数据为:\n %s\n", Rx_Buffer);
  64. /* 检查写入的数据与读出的数据是否相等 */
  65. TransferStatus1 = Buffercmp(Tx_Buffer, Rx_Buffer, BufferSize);
  66. if( PASSED == TransferStatus1 )
  67. {
  68. printf("16M串行flash(W25Q128)测试成功!\r");
  69. LED1_ON;
  70. }
  71. else
  72. {
  73. printf("16M串行flash(W25Q128)测试失败!\r");
  74. LED2_ON;
  75. }
  76. }
  77. else
  78. {
  79. printf("获取不到 W25Q128 ID!\n");
  80. LED3_ON;
  81. }
  82. /* 无限循环 */
  83. while (1)
  84. {
  85. }
  86. }
  87. /*
  88. * 函数名:Buffercmp
  89. * 描述 :比较两个缓冲区中的数据是否相等
  90. * 输入 :-pBuffer1 src缓冲区指针
  91. * -pBuffer2 dst缓冲区指针
  92. * -BufferLength 缓冲区长度
  93. * 输出 :无
  94. * 返回 :-PASSED pBuffer1 等于 pBuffer2
  95. * -FAILED pBuffer1 不同于 pBuffer2
  96. */
  97. static TestStatus Buffercmp(uint8_t* pBuffer1, uint8_t* pBuffer2, uint16_t BufferLength)
  98. {
  99. while(BufferLength--)
  100. {
  101. if(*pBuffer1 != *pBuffer2)
  102. {
  103. return FAILED;
  104. }
  105. pBuffer1++;
  106. pBuffer2++;
  107. }
  108. return PASSED;
  109. }

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

闽ICP备14008679号