当前位置:   article > 正文

SPI协议—读写串行FLASH(详细讲解+代码)_spi flash

spi flash

目录

前言

SPI总线协议

什么是SPI

SPI的特点

物理层

协议层

SPI通讯过程

总结


前言

         本章所运用的知识点都是博主从各个网站搜集来的(侵删@小麦大叔@野火),也附带一点自己的看法。本章所用到的开发板是野火的霸道F103系列开发板,需要完整可运行代码的同学也可以找@我拿

        总所周知,学习单片机离不开协议,上章我们讲述了I2C的作用、时序、以及基本代码。相信大家或多或少也了解完了,那么现在跟着我一起来学习同样重要且应用广泛的协议——SPI。

SPI总线协议

什么是SPI

        SPI,是英语 Serial Peripheral Interface 的缩写,顾名思义就是串行外围设备接口SPI,是一种高速的,全双工,同步的通信总线,

        并且在芯片的管脚上只占用四根线根据实际情况,也有公司用到无根线),节约了芯片的
管脚,同时为 PCB 的布局上节省空间,提供方便,正是出于这种简单易用的特性,现 在越来越多的芯片集成了这种通信协议。

SPI的特点

物理层

 如上图所示:

SPI协议传输是主机MCU与从机(1、2、3......)进行数据传输,一个主机可以与多个从机通信(但不能同时通信,会出现数据报错),通信要求则是SS线的电平拉低。下面我们来介绍一下主机上的四根线

SS: 从设备选择信号线,常称为 片选信号线 ,也称为NSS CS
SCK (Serial Clock) 时钟信号线 ,用于通讯数据同步。
MOSI (Master OutputSlave Input) 主设备输出/从设备输入引脚
MISO(Master Input,Slave Output) 主设备输入/从设备输出引脚
SS:
        每个从设备都有独立的这一条SS信号线, 本信号线独占主机的一个引脚,即有多少
个从设备,就有多少条片选信号线。 I2C 协议中通过设备地址来寻址、选中总线上
的某个设备并与其进行通讯;而 SPI 协议 中没有设备地址,它使用SS 信号线来寻址,
当主机要选择从设备时,把该从设备的 SS 信号线设置为低电平,该从设备即被选中,
即片选有效,接着主机开始与被选中的从 设备进行SPI 通讯。所以 SPI 通讯以 SS 线
置低电平为开始信号,以 SS 线被拉高作为 结束信号。
SCK (Serial Clock)
        它由通讯主机产生,决定了通讯的速率,不同的设备支持的最高时钟频率不一样,如STM32的SPI时钟频率最大为fpclk/2,两个设备之间通讯时,通讯速率受限于低速设备。
MOSI (Master OutputSlave Input)
        主机的数据从这条信号线输出,从机由这条信号线读入主机发送的数据,即这条线上数据的方向为主机到从机。
MISO(Master Input,Slave Output)
        主机从这条信号线读入数据,从机的数 据由这条信号线输出到主机,即在这条线上数据的方向为从机到主机。  

协议层

SPI协议定义了通讯的起始和停止信号、数据有效性、时钟同 步等环节。
1.SPI 基本通讯过程

2. 通讯的起始和停止信号

  • 标号① 处, NSS 信号线由高变低,是 SPI 通讯的起始信号。 NSS 是每个从机各 自独占的信号线,当从机检在自己的NSS 线检测到起始信号后,就知道自己 被主机选中了,开始准备与主机通讯。
  • 在图中的标号 处, NSS 信号由低变高,是 SPI 通讯的停止信号,表示本次通 讯结束,从机的选中状态被取消 。
3. 数据有效性
  • SPI 使用 MOSI MISO 信号线来传输数据,使用 SCK 信号线进行数据同步。 MOSI及 MISO 数据线在 SCK 的每个时钟周期传输一位数据,且数据输入输出 是同时进行的。

 4.CPOL/CPHA及通讯模式

  • 时钟极性CPOL: 是指 SPI 通讯设备处于空闲状态时, SCK 信号线的电平信号 (即 SPI 通讯开始前、 NSS 线为高电平时 SCK 的状态 ) CPOL=0 时, SCK 在空闲状态时为低电平,CPOL=1 时,则相反。
  • 时钟相位CPHA: 是指数据的采样的时刻,当 CPHA=0 时, MOSI MISO 数 据线上的信号将会在SCK 时钟线的“奇数边沿”被采样。当 CPHA=1 时, 数据线在SCK 的“偶数边沿”采样。

 5.CPOL/CPHA及通讯模式

  • CK 信号线在空闲状态为低电平时, CPOL=0 ;空闲状态为高电平时, CPOL=1
  • CPHA=0 MOSI MISO 数据线的有效信号在 SCK 奇数边沿保持不变,数据信 号将在SCK 奇数边沿时被采样,在非采样时刻, MOSI MISO 的有效信号才发生 切换。 

6.CPOL/CPHA及通讯模式  

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

SPI通讯过程

  • 控制NSS信号线,产生起始信号(图中没有画出)
  • 把要发送的数据写入到“数据寄存器DR”中,该数据会被存储到发 送缓冲区;
  • 通讯开始,SCK时钟开始运行。MOSI把发送缓冲区中的数据一位 一位地传输出去;MISO则把数据一位一位地存储进接收缓冲区中;
  • 当发送完一帧数据的时候,“状态寄存器SR”中的“TXE标志位” 会被置1,表示传输完一帧,发送缓冲区已空;类似地,当接收完 一帧数据的时候,“RXNE标志位”会被置1,表示传输完一帧,接 收缓冲区非空;
  • 等待到“TXE标志位”为1时,若还要继续发送数据,则再次往 “数据寄存器DR”写入数据即可;等待到“RXNE标志位”为1时, 通过读取“数据寄存器DR”可以获取接收缓冲区中的内容。
        假如使能了TXE RXNE中断,TXERXNE1时会产生SPI中断信号, 进入同一个中断服务函数,到SPI中断服务程序后,可通过检查寄存器 位来了解是哪一个事件,再分别进行处理。也可以使用DMA方式来收 发“数据寄存器DR”中的数据。

SPI通讯的优势
使SPI作为串行通信接口脱颖而出的原因很多;

  • 全双工串行通信;
  • 高速数据传输速率。
  • 简单的软件配置;
  • 极其灵活的数据传输,不限于8位,它可以是任意大小的字;
  • 非常简单的硬件结构。从站不需要唯一地址(与I2C不同)。从机使用主机时钟,不需要精密时钟振荡器/晶振(与UART不同)。不需要收发器(与CAN不同)。

SPI的缺点

  • 没有硬件从机应答信号(主机可能在不知情的情况下无处发送);
  • 通常仅支持一个主设备;
  • 需要更多的引脚(与I2C不同);
  • 没有定义硬件级别的错误检查协议;
  • 与RS-232和CAN总线相比,只能支持非常短的距离;

 部分关键代码:

初始化SPI

  1. /**
  2. * @brief SPI_FLASH初始化
  3. * @param 无
  4. * @retval 无
  5. */
  6. void SPI_FLASH_Init(void)
  7. {
  8. SPI_InitTypeDef SPI_InitStructure;
  9. GPIO_InitTypeDef GPIO_InitStructure;
  10. /* 使能SPI时钟 */
  11. FLASH_SPI_APBxClock_FUN ( FLASH_SPI_CLK, ENABLE );
  12. /* 使能SPI引脚相关的时钟 */
  13. FLASH_SPI_CS_APBxClock_FUN ( FLASH_SPI_CS_CLK|FLASH_SPI_SCK_CLK|
  14. FLASH_SPI_MISO_PIN|FLASH_SPI_MOSI_PIN, ENABLE );
  15. /* 配置SPI的 CS引脚,普通IO即可 */
  16. GPIO_InitStructure.GPIO_Pin = FLASH_SPI_CS_PIN;
  17. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  18. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  19. GPIO_Init(FLASH_SPI_CS_PORT, &GPIO_InitStructure);
  20. /* 配置SPI的 SCK引脚*/
  21. GPIO_InitStructure.GPIO_Pin = FLASH_SPI_SCK_PIN;
  22. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  23. GPIO_Init(FLASH_SPI_SCK_PORT, &GPIO_InitStructure);
  24. /* 配置SPI的 MISO引脚*/
  25. GPIO_InitStructure.GPIO_Pin = FLASH_SPI_MISO_PIN;
  26. GPIO_Init(FLASH_SPI_MISO_PORT, &GPIO_InitStructure);
  27. /* 配置SPI的 MOSI引脚*/
  28. GPIO_InitStructure.GPIO_Pin = FLASH_SPI_MOSI_PIN;
  29. GPIO_Init(FLASH_SPI_MOSI_PORT, &GPIO_InitStructure);
  30. /* 停止信号 FLASH: CS引脚高电平*/
  31. SPI_FLASH_CS_HIGH();
  32. /* SPI 模式配置 */
  33. // FLASH芯片 支持SPI模式0及模式3,据此设置CPOL CPHA
  34. SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
  35. SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
  36. SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
  37. SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
  38. SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
  39. SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
  40. SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;
  41. SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
  42. SPI_InitStructure.SPI_CRCPolynomial = 7;
  43. SPI_Init(FLASH_SPIx , &SPI_InitStructure);
  44. /* 使能 SPI */
  45. SPI_Cmd(FLASH_SPIx , ENABLE);
  46. }

Flash扇区擦除

  1. /**
  2. * @brief 擦除FLASH扇区
  3. * @param SectorAddr:要擦除的扇区地址
  4. * @retval 无
  5. */
  6. void SPI_FLASH_SectorErase(u32 SectorAddr)
  7. {
  8. /* 发送FLASH写使能命令 */
  9. SPI_FLASH_WriteEnable();
  10. SPI_FLASH_WaitForWriteEnd();
  11. /* 擦除扇区 */
  12. /* 选择FLASH: CS低电平 */
  13. SPI_FLASH_CS_LOW();
  14. /* 发送扇区擦除指令*/
  15. SPI_FLASH_SendByte(W25X_SectorErase);
  16. /*发送擦除扇区地址的高位*/
  17. SPI_FLASH_SendByte((SectorAddr & 0xFF0000) >> 16);
  18. /* 发送擦除扇区地址的中位 */
  19. SPI_FLASH_SendByte((SectorAddr & 0xFF00) >> 8);
  20. /* 发送擦除扇区地址的低位 */
  21. SPI_FLASH_SendByte(SectorAddr & 0xFF);
  22. /* 停止信号 FLASH: CS 高电平 */
  23. SPI_FLASH_CS_HIGH();
  24. /* 等待擦除完毕*/
  25. SPI_FLASH_WaitForWriteEnd();
  26. }
  27. /**
  28. * @brief 擦除FLASH扇区,整片擦除
  29. * @param 无
  30. * @retval 无
  31. */
  32. void SPI_FLASH_BulkErase(void)
  33. {
  34. /* 发送FLASH写使能命令 */
  35. SPI_FLASH_WriteEnable();
  36. /* 整块 Erase */
  37. /* 选择FLASH: CS低电平 */
  38. SPI_FLASH_CS_LOW();
  39. /* 发送整块擦除指令*/
  40. SPI_FLASH_SendByte(W25X_ChipErase);
  41. /* 停止信号 FLASH: CS 高电平 */
  42. SPI_FLASH_CS_HIGH();
  43. /* 等待擦除完毕*/
  44. SPI_FLASH_WaitForWriteEnd();
  45. }

对Flash进行写入(包括页写入)

  1. /**
  2. * @brief 对FLASH按页写入数据,调用本函数写入数据前需要先擦除扇区
  3. * @param pBuffer,要写入数据的指针
  4. * @param WriteAddr,写入地址
  5. * @param NumByteToWrite,写入数据长度,必须小于等于SPI_FLASH_PerWritePageSize
  6. * @retval 无
  7. */
  8. void SPI_FLASH_PageWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite)
  9. {
  10. /* 发送FLASH写使能命令 */
  11. SPI_FLASH_WriteEnable();
  12. /* 选择FLASH: CS低电平 */
  13. SPI_FLASH_CS_LOW();
  14. /* 写页写指令*/
  15. SPI_FLASH_SendByte(W25X_PageProgram);
  16. /*发送写地址的高位*/
  17. SPI_FLASH_SendByte((WriteAddr & 0xFF0000) >> 16);
  18. /*发送写地址的中位*/
  19. SPI_FLASH_SendByte((WriteAddr & 0xFF00) >> 8);
  20. /*发送写地址的低位*/
  21. SPI_FLASH_SendByte(WriteAddr & 0xFF);
  22. if(NumByteToWrite > SPI_FLASH_PerWritePageSize)
  23. {
  24. NumByteToWrite = SPI_FLASH_PerWritePageSize;
  25. FLASH_ERROR("SPI_FLASH_PageWrite too large!");
  26. }
  27. /* 写入数据*/
  28. while (NumByteToWrite--)
  29. {
  30. /* 发送当前要写入的字节数据 */
  31. SPI_FLASH_SendByte(*pBuffer);
  32. /* 指向下一字节数据 */
  33. pBuffer++;
  34. }
  35. /* 停止信号 FLASH: CS 高电平 */
  36. SPI_FLASH_CS_HIGH();
  37. /* 等待写入完毕*/
  38. SPI_FLASH_WaitForWriteEnd();
  39. }
  40. /**
  41. * @brief 对FLASH写入数据,调用本函数写入数据前需要先擦除扇区
  42. * @param pBuffer,要写入数据的指针
  43. * @param WriteAddr,写入地址
  44. * @param NumByteToWrite,写入数据长度
  45. * @retval 无
  46. */
  47. void SPI_FLASH_BufferWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite)
  48. {
  49. u8 NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0, temp = 0;
  50. /*mod运算求余,若writeAddr是SPI_FLASH_PageSize整数倍,运算结果Addr值为0*/
  51. Addr = WriteAddr % SPI_FLASH_PageSize;
  52. /*差count个数据值,刚好可以对齐到页地址*/
  53. count = SPI_FLASH_PageSize - Addr;
  54. /*计算出要写多少整数页*/
  55. NumOfPage = NumByteToWrite / SPI_FLASH_PageSize;
  56. /*mod运算求余,计算出剩余不满一页的字节数*/
  57. NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;
  58. /* Addr=0,则WriteAddr 刚好按页对齐 aligned */
  59. if (Addr == 0)
  60. {
  61. /* NumByteToWrite < SPI_FLASH_PageSize */
  62. if (NumOfPage == 0)
  63. {
  64. SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);
  65. }
  66. else /* NumByteToWrite > SPI_FLASH_PageSize */
  67. {
  68. /*先把整数页都写了*/
  69. while (NumOfPage--)
  70. {
  71. SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize);
  72. WriteAddr += SPI_FLASH_PageSize;
  73. pBuffer += SPI_FLASH_PageSize;
  74. }
  75. /*若有多余的不满一页的数据,把它写完*/
  76. SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);
  77. }
  78. }
  79. /* 若地址与 SPI_FLASH_PageSize 不对齐 */
  80. else
  81. {
  82. /* NumByteToWrite < SPI_FLASH_PageSize */
  83. if (NumOfPage == 0)
  84. {
  85. /*当前页剩余的count个位置比NumOfSingle小,一页写不完*/
  86. if (NumOfSingle > count)
  87. {
  88. temp = NumOfSingle - count;
  89. /*先写满当前页*/
  90. SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);
  91. WriteAddr += count;
  92. pBuffer += count;
  93. /*再写剩余的数据*/
  94. SPI_FLASH_PageWrite(pBuffer, WriteAddr, temp);
  95. }
  96. else /*当前页剩余的count个位置能写完NumOfSingle个数据*/
  97. {
  98. SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);
  99. }
  100. }
  101. else /* NumByteToWrite > SPI_FLASH_PageSize */
  102. {
  103. /*地址不对齐多出的count分开处理,不加入这个运算*/
  104. NumByteToWrite -= count;
  105. NumOfPage = NumByteToWrite / SPI_FLASH_PageSize;
  106. NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;
  107. /* 先写完count个数据,为的是让下一次要写的地址对齐 */
  108. SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);
  109. /* 接下来就重复地址对齐的情况 */
  110. WriteAddr += count;
  111. pBuffer += count;
  112. /*把整数页都写了*/
  113. while (NumOfPage--)
  114. {
  115. SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize);
  116. WriteAddr += SPI_FLASH_PageSize;
  117. pBuffer += SPI_FLASH_PageSize;
  118. }
  119. /*若有多余的不满一页的数据,把它写完*/
  120. if (NumOfSingle != 0)
  121. {
  122. SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);
  123. }
  124. }
  125. }
  126. }

读Flash

  1. /**
  2. * @brief 读取FLASH数据
  3. * @param pBuffer,存储读出数据的指针
  4. * @param ReadAddr,读取地址
  5. * @param NumByteToRead,读取数据长度
  6. * @retval 无
  7. */
  8. void SPI_FLASH_BufferRead(u8* pBuffer, u32 ReadAddr, u16 NumByteToRead)
  9. {
  10. /* 选择FLASH: CS低电平 */
  11. SPI_FLASH_CS_LOW();
  12. /* 发送 读 指令 */
  13. SPI_FLASH_SendByte(W25X_ReadData);
  14. /* 发送 读 地址高位 */
  15. SPI_FLASH_SendByte((ReadAddr & 0xFF0000) >> 16);
  16. /* 发送 读 地址中位 */
  17. SPI_FLASH_SendByte((ReadAddr& 0xFF00) >> 8);
  18. /* 发送 读 地址低位 */
  19. SPI_FLASH_SendByte(ReadAddr & 0xFF);
  20. /* 读取数据 */
  21. while (NumByteToRead--) /* while there is data to be read */
  22. {
  23. /* 读取一个字节*/
  24. *pBuffer = SPI_FLASH_SendByte(Dummy_Byte);
  25. /* 指向下一个字节缓冲区 */
  26. pBuffer++;
  27. }
  28. /* 停止信号 FLASH: CS 高电平 */
  29. SPI_FLASH_CS_HIGH();
  30. }

读取Flash ID以及FLASH Device ID

  1. /**
  2. * @brief 读取FLASH ID
  3. * @param 无
  4. * @retval FLASH ID
  5. */
  6. u32 SPI_FLASH_ReadID(void)
  7. {
  8. u32 Temp = 0, Temp0 = 0, Temp1 = 0, Temp2 = 0;
  9. /* 开始通讯:CS低电平 */
  10. SPI_FLASH_CS_LOW();
  11. /* 发送JEDEC指令,读取ID */
  12. SPI_FLASH_SendByte(W25X_JedecDeviceID);
  13. /* 读取一个字节数据 */
  14. Temp0 = SPI_FLASH_SendByte(Dummy_Byte);
  15. /* 读取一个字节数据 */
  16. Temp1 = SPI_FLASH_SendByte(Dummy_Byte);
  17. /* 读取一个字节数据 */
  18. Temp2 = SPI_FLASH_SendByte(Dummy_Byte);
  19. /* 停止通讯:CS高电平 */
  20. SPI_FLASH_CS_HIGH();
  21. /*把数据组合起来,作为函数的返回值*/
  22. Temp = (Temp0 << 16) | (Temp1 << 8) | Temp2;
  23. return Temp;
  24. }
  25. /**
  26. * @brief 读取FLASH Device ID
  27. * @param 无
  28. * @retval FLASH Device ID
  29. */
  30. u32 SPI_FLASH_ReadDeviceID(void)
  31. {
  32. u32 Temp = 0;
  33. /* Select the FLASH: Chip Select low */
  34. SPI_FLASH_CS_LOW();
  35. /* Send "RDID " instruction */
  36. SPI_FLASH_SendByte(W25X_DeviceID);
  37. SPI_FLASH_SendByte(Dummy_Byte);
  38. SPI_FLASH_SendByte(Dummy_Byte);
  39. SPI_FLASH_SendByte(Dummy_Byte);
  40. /* Read a byte from the FLASH */
  41. Temp = SPI_FLASH_SendByte(Dummy_Byte);
  42. /* Deselect the FLASH: Chip Select high */
  43. SPI_FLASH_CS_HIGH();
  44. return Temp;
  45. }

主函数MAIN

  1. typedef enum { FAILED = 0, PASSED = !FAILED} TestStatus;
  2. /* 获取缓冲区的长度 */
  3. #define TxBufferSize1 (countof(TxBuffer1) - 1)
  4. #define RxBufferSize1 (countof(TxBuffer1) - 1)
  5. #define countof(a) (sizeof(a) / sizeof(*(a)))
  6. #define BufferSize (countof(Tx_Buffer)-1)
  7. #define FLASH_WriteAddress 0x00000
  8. #define FLASH_ReadAddress FLASH_WriteAddress
  9. #define FLASH_SectorToErase FLASH_WriteAddress
  10. /* 发送缓冲区初始化 */
  11. uint8_t Tx_Buffer[] = "感谢\r\n";
  12. uint8_t Rx_Buffer[BufferSize];
  13. __IO uint32_t DeviceID = 0;
  14. __IO uint32_t FlashID = 0;
  15. __IO TestStatus TransferStatus1 = FAILED;
  16. // 函数原型声明
  17. void Delay(__IO uint32_t nCount);
  18. TestStatus Buffercmp(uint8_t* pBuffer1,uint8_t* pBuffer2, uint16_t BufferLength);
  19. /*
  20. * 函数名:main
  21. * 描述 :主函数
  22. * 输入 :无
  23. * 输出 :无
  24. */
  25. int main(void)
  26. {
  27. LED_GPIO_Config();
  28. LED_BLUE;
  29. /* 配置串口为:115200 8-N-1 */
  30. USART_Config();
  31. printf("\r\n 这是一个8Mbyte串行flash(W25Q64)实验 \r\n");
  32. /* 8M串行flash W25Q64初始化 */
  33. SPI_FLASH_Init();
  34. /* 获取 Flash Device ID */
  35. DeviceID = SPI_FLASH_ReadDeviceID();
  36. Delay( 200 );
  37. /* 获取 SPI Flash ID */
  38. FlashID = SPI_FLASH_ReadID();
  39. printf("\r\n FlashID is 0x%X,\
  40. Manufacturer Device ID is 0x%X\r\n", FlashID, DeviceID);
  41. /* 检验 SPI Flash ID */
  42. if (FlashID == sFLASH_ID)
  43. {
  44. printf("\r\n 检测到串行flash W25Q64 !\r\n");
  45. /* 擦除将要写入的 SPI FLASH 扇区,FLASH写入前要先擦除 */
  46. // 这里擦除4K,即一个扇区,擦除的最小单位是扇区
  47. SPI_FLASH_SectorErase(FLASH_SectorToErase);
  48. /* 将发送缓冲区的数据写到flash中 */
  49. // 这里写一页,一页的大小为256个字节
  50. SPI_FLASH_BufferWrite(Tx_Buffer, FLASH_WriteAddress, BufferSize);
  51. printf("\r\n 写入的数据为:%s \r\t", Tx_Buffer);
  52. /* 将刚刚写入的数据读出来放到接收缓冲区中 */
  53. SPI_FLASH_BufferRead(Rx_Buffer, FLASH_ReadAddress, BufferSize);
  54. printf("\r\n 读出的数据为:%s \r\n", Rx_Buffer);
  55. /* 检查写入的数据与读出的数据是否相等 */
  56. TransferStatus1 = Buffercmp(Tx_Buffer, Rx_Buffer, BufferSize);
  57. if( PASSED == TransferStatus1 )
  58. {
  59. LED_GREEN;
  60. printf("\r\n 8M串行flash(W25Q64)测试成功!\n\r");
  61. }
  62. else
  63. {
  64. LED_RED;
  65. printf("\r\n 8M串行flash(W25Q64)测试失败!\n\r");
  66. }
  67. }// if (FlashID == sFLASH_ID)
  68. else// if (FlashID == sFLASH_ID)
  69. {
  70. LED_RED;
  71. printf("\r\n 获取不到 W25Q64 ID!\n\r");
  72. }
  73. while(1);
  74. }
  75. /*
  76. * 函数名:Buffercmp
  77. * 描述 :比较两个缓冲区中的数据是否相等
  78. * 输入 :-pBuffer1 src缓冲区指针
  79. * -pBuffer2 dst缓冲区指针
  80. * -BufferLength 缓冲区长度
  81. * 输出 :无
  82. * 返回 :-PASSED pBuffer1 等于 pBuffer2
  83. * -FAILED pBuffer1 不同于 pBuffer2
  84. */
  85. TestStatus Buffercmp(uint8_t* pBuffer1, uint8_t* pBuffer2, uint16_t BufferLength)
  86. {
  87. while(BufferLength--)
  88. {
  89. if(*pBuffer1 != *pBuffer2)
  90. {
  91. return FAILED;
  92. }
  93. pBuffer1++;
  94. pBuffer2++;
  95. }
  96. return PASSED;
  97. }
  98. void Delay(__IO uint32_t nCount)
  99. {
  100. for(; nCount != 0; nCount--);
  101. }

总结

以上就是SPI的全部内容,本文简单介绍了SPI的使用,希望对大家有帮助!

声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号