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 Output, Slave 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 Output, Slave 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中断,TXE或RXNE置1时会产生SPI中断信号, 进入同一个中断服务函数,到SPI中断服务程序后,可通过检查寄存器 位来了解是哪一个事件,再分别进行处理。也可以使用DMA方式来收 发“数据寄存器DR”中的数据。

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

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

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

    初始化SPI

    /**
      * @brief  SPI_FLASH初始化
      * @param  无
      * @retval 无
      */
    void SPI_FLASH_Init(void)
    {
      SPI_InitTypeDef  SPI_InitStructure;
      GPIO_InitTypeDef GPIO_InitStructure;
    	
    	/* 使能SPI时钟 */
    	FLASH_SPI_APBxClock_FUN ( FLASH_SPI_CLK, ENABLE );
    	
    	/* 使能SPI引脚相关的时钟 */
     	FLASH_SPI_CS_APBxClock_FUN ( FLASH_SPI_CS_CLK|FLASH_SPI_SCK_CLK|
    																	FLASH_SPI_MISO_PIN|FLASH_SPI_MOSI_PIN, ENABLE );
    	
      /* 配置SPI的 CS引脚,普通IO即可 */
      GPIO_InitStructure.GPIO_Pin = FLASH_SPI_CS_PIN;
    	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
      GPIO_Init(FLASH_SPI_CS_PORT, &GPIO_InitStructure);
    	
      /* 配置SPI的 SCK引脚*/
      GPIO_InitStructure.GPIO_Pin = FLASH_SPI_SCK_PIN;
      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
      GPIO_Init(FLASH_SPI_SCK_PORT, &GPIO_InitStructure);
    
      /* 配置SPI的 MISO引脚*/
      GPIO_InitStructure.GPIO_Pin = FLASH_SPI_MISO_PIN;
      GPIO_Init(FLASH_SPI_MISO_PORT, &GPIO_InitStructure);
    
      /* 配置SPI的 MOSI引脚*/
      GPIO_InitStructure.GPIO_Pin = FLASH_SPI_MOSI_PIN;
      GPIO_Init(FLASH_SPI_MOSI_PORT, &GPIO_InitStructure);
    
      /* 停止信号 FLASH: CS引脚高电平*/
      SPI_FLASH_CS_HIGH();
    
      /* SPI 模式配置 */
      // FLASH芯片 支持SPI模式0及模式3,据此设置CPOL CPHA
      SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
      SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
      SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
      SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
      SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
      SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
      SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;
      SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
      SPI_InitStructure.SPI_CRCPolynomial = 7;
      SPI_Init(FLASH_SPIx , &SPI_InitStructure);
    
      /* 使能 SPI  */
      SPI_Cmd(FLASH_SPIx , ENABLE);
    	
    }

    Flash扇区擦除

    /**
      * @brief  擦除FLASH扇区
      * @param  SectorAddr:要擦除的扇区地址
      * @retval 无
      */
    void SPI_FLASH_SectorErase(u32 SectorAddr)
    {
      /* 发送FLASH写使能命令 */
      SPI_FLASH_WriteEnable();
      SPI_FLASH_WaitForWriteEnd();
      /* 擦除扇区 */
      /* 选择FLASH: CS低电平 */
      SPI_FLASH_CS_LOW();
      /* 发送扇区擦除指令*/
      SPI_FLASH_SendByte(W25X_SectorErase);
      /*发送擦除扇区地址的高位*/
      SPI_FLASH_SendByte((SectorAddr & 0xFF0000) >> 16);
      /* 发送擦除扇区地址的中位 */
      SPI_FLASH_SendByte((SectorAddr & 0xFF00) >> 8);
      /* 发送擦除扇区地址的低位 */
      SPI_FLASH_SendByte(SectorAddr & 0xFF);
      /* 停止信号 FLASH: CS 高电平 */
      SPI_FLASH_CS_HIGH();
      /* 等待擦除完毕*/
      SPI_FLASH_WaitForWriteEnd();
    }
    
     /**
      * @brief  擦除FLASH扇区,整片擦除
      * @param  无
      * @retval 无
      */
    void SPI_FLASH_BulkErase(void)
    {
      /* 发送FLASH写使能命令 */
      SPI_FLASH_WriteEnable();
    
      /* 整块 Erase */
      /* 选择FLASH: CS低电平 */
      SPI_FLASH_CS_LOW();
      /* 发送整块擦除指令*/
      SPI_FLASH_SendByte(W25X_ChipErase);
      /* 停止信号 FLASH: CS 高电平 */
      SPI_FLASH_CS_HIGH();
    
      /* 等待擦除完毕*/
      SPI_FLASH_WaitForWriteEnd();
    }

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

    /**
      * @brief  对FLASH按页写入数据,调用本函数写入数据前需要先擦除扇区
      * @param	pBuffer,要写入数据的指针
      * @param WriteAddr,写入地址
      * @param  NumByteToWrite,写入数据长度,必须小于等于SPI_FLASH_PerWritePageSize
      * @retval 无
      */
    void SPI_FLASH_PageWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite)
    {
      /* 发送FLASH写使能命令 */
      SPI_FLASH_WriteEnable();
    
      /* 选择FLASH: CS低电平 */
      SPI_FLASH_CS_LOW();
      /* 写页写指令*/
      SPI_FLASH_SendByte(W25X_PageProgram);
      /*发送写地址的高位*/
      SPI_FLASH_SendByte((WriteAddr & 0xFF0000) >> 16);
      /*发送写地址的中位*/
      SPI_FLASH_SendByte((WriteAddr & 0xFF00) >> 8);
      /*发送写地址的低位*/
      SPI_FLASH_SendByte(WriteAddr & 0xFF);
    
      if(NumByteToWrite > SPI_FLASH_PerWritePageSize)
      {
         NumByteToWrite = SPI_FLASH_PerWritePageSize;
         FLASH_ERROR("SPI_FLASH_PageWrite too large!"); 
      }
    
      /* 写入数据*/
      while (NumByteToWrite--)
      {
        /* 发送当前要写入的字节数据 */
        SPI_FLASH_SendByte(*pBuffer);
        /* 指向下一字节数据 */
        pBuffer++;
      }
    
      /* 停止信号 FLASH: CS 高电平 */
      SPI_FLASH_CS_HIGH();
    
      /* 等待写入完毕*/
      SPI_FLASH_WaitForWriteEnd();
    }
    
     /**
      * @brief  对FLASH写入数据,调用本函数写入数据前需要先擦除扇区
      * @param	pBuffer,要写入数据的指针
      * @param  WriteAddr,写入地址
      * @param  NumByteToWrite,写入数据长度
      * @retval 无
      */
    void SPI_FLASH_BufferWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite)
    {
      u8 NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0, temp = 0;
    	
    	/*mod运算求余,若writeAddr是SPI_FLASH_PageSize整数倍,运算结果Addr值为0*/
      Addr = WriteAddr % SPI_FLASH_PageSize;
    	
    	/*差count个数据值,刚好可以对齐到页地址*/
      count = SPI_FLASH_PageSize - Addr;
    	/*计算出要写多少整数页*/
      NumOfPage =  NumByteToWrite / SPI_FLASH_PageSize;
    	/*mod运算求余,计算出剩余不满一页的字节数*/
      NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;
    	
    	/* Addr=0,则WriteAddr 刚好按页对齐 aligned  */
      if (Addr == 0)
      {
    		/* NumByteToWrite < SPI_FLASH_PageSize */
        if (NumOfPage == 0) 
        {
          SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);
        }
        else /* NumByteToWrite > SPI_FLASH_PageSize */
        { 
    			/*先把整数页都写了*/
          while (NumOfPage--)
          {
            SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize);
            WriteAddr +=  SPI_FLASH_PageSize;
            pBuffer += SPI_FLASH_PageSize;
          }
    			/*若有多余的不满一页的数据,把它写完*/
          SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);
        }
      }
    	/* 若地址与 SPI_FLASH_PageSize 不对齐  */
      else 
      {
    		/* NumByteToWrite < SPI_FLASH_PageSize */
        if (NumOfPage == 0)
        {
    			/*当前页剩余的count个位置比NumOfSingle小,一页写不完*/
          if (NumOfSingle > count) 
          {
            temp = NumOfSingle - count;
    				/*先写满当前页*/
            SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);
    				
            WriteAddr +=  count;
            pBuffer += count;
    				/*再写剩余的数据*/
            SPI_FLASH_PageWrite(pBuffer, WriteAddr, temp);
          }
          else /*当前页剩余的count个位置能写完NumOfSingle个数据*/
          {
            SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);
          }
        }
        else /* NumByteToWrite > SPI_FLASH_PageSize */
        {
    			/*地址不对齐多出的count分开处理,不加入这个运算*/
          NumByteToWrite -= count;
          NumOfPage =  NumByteToWrite / SPI_FLASH_PageSize;
          NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;
    			
    			/* 先写完count个数据,为的是让下一次要写的地址对齐 */
          SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);
    			
    			/* 接下来就重复地址对齐的情况 */
          WriteAddr +=  count;
          pBuffer += count;
    			/*把整数页都写了*/
          while (NumOfPage--)
          {
            SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize);
            WriteAddr +=  SPI_FLASH_PageSize;
            pBuffer += SPI_FLASH_PageSize;
          }
    			/*若有多余的不满一页的数据,把它写完*/
          if (NumOfSingle != 0)
          {
            SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);
          }
        }
      }
    }

    读Flash

     /**
      * @brief  读取FLASH数据
      * @param 	pBuffer,存储读出数据的指针
      * @param   ReadAddr,读取地址
      * @param   NumByteToRead,读取数据长度
      * @retval 无
      */
    void SPI_FLASH_BufferRead(u8* pBuffer, u32 ReadAddr, u16 NumByteToRead)
    {
      /* 选择FLASH: CS低电平 */
      SPI_FLASH_CS_LOW();
    
      /* 发送 读 指令 */
      SPI_FLASH_SendByte(W25X_ReadData);
    
      /* 发送 读 地址高位 */
      SPI_FLASH_SendByte((ReadAddr & 0xFF0000) >> 16);
      /* 发送 读 地址中位 */
      SPI_FLASH_SendByte((ReadAddr& 0xFF00) >> 8);
      /* 发送 读 地址低位 */
      SPI_FLASH_SendByte(ReadAddr & 0xFF);
    	
    	/* 读取数据 */
      while (NumByteToRead--) /* while there is data to be read */
      {
        /* 读取一个字节*/
        *pBuffer = SPI_FLASH_SendByte(Dummy_Byte);
        /* 指向下一个字节缓冲区 */
        pBuffer++;
      }
    
      /* 停止信号 FLASH: CS 高电平 */
      SPI_FLASH_CS_HIGH();
    }

    读取Flash ID以及FLASH Device ID

     /**
      * @brief  读取FLASH ID
      * @param 	无
      * @retval FLASH ID
      */
    u32 SPI_FLASH_ReadID(void)
    {
      u32 Temp = 0, Temp0 = 0, Temp1 = 0, Temp2 = 0;
    
      /* 开始通讯:CS低电平 */
      SPI_FLASH_CS_LOW();
    
      /* 发送JEDEC指令,读取ID */
      SPI_FLASH_SendByte(W25X_JedecDeviceID);
    
      /* 读取一个字节数据 */
      Temp0 = SPI_FLASH_SendByte(Dummy_Byte);
    
      /* 读取一个字节数据 */
      Temp1 = SPI_FLASH_SendByte(Dummy_Byte);
    
      /* 读取一个字节数据 */
      Temp2 = SPI_FLASH_SendByte(Dummy_Byte);
    
     /* 停止通讯:CS高电平 */
      SPI_FLASH_CS_HIGH();
    
      /*把数据组合起来,作为函数的返回值*/
    	Temp = (Temp0 << 16) | (Temp1 << 8) | Temp2;
    
      return Temp;
    }
     /**
      * @brief  读取FLASH Device ID
      * @param 	无
      * @retval FLASH Device ID
      */
    u32 SPI_FLASH_ReadDeviceID(void)
    {
      u32 Temp = 0;
    
      /* Select the FLASH: Chip Select low */
      SPI_FLASH_CS_LOW();
    
      /* Send "RDID " instruction */
      SPI_FLASH_SendByte(W25X_DeviceID);
      SPI_FLASH_SendByte(Dummy_Byte);
      SPI_FLASH_SendByte(Dummy_Byte);
      SPI_FLASH_SendByte(Dummy_Byte);
      
      /* Read a byte from the FLASH */
      Temp = SPI_FLASH_SendByte(Dummy_Byte);
    
      /* Deselect the FLASH: Chip Select high */
      SPI_FLASH_CS_HIGH();
    
      return Temp;
    }

    主函数MAIN

    typedef enum { FAILED = 0, PASSED = !FAILED} TestStatus;
    
    /* 获取缓冲区的长度 */
    #define TxBufferSize1   (countof(TxBuffer1) - 1)
    #define RxBufferSize1   (countof(TxBuffer1) - 1)
    #define countof(a)      (sizeof(a) / sizeof(*(a)))
    #define  BufferSize (countof(Tx_Buffer)-1)
    
    #define  FLASH_WriteAddress     0x00000
    #define  FLASH_ReadAddress      FLASH_WriteAddress
    #define  FLASH_SectorToErase    FLASH_WriteAddress
    
         
    
    /* 发送缓冲区初始化 */
    uint8_t Tx_Buffer[] = "感谢\r\n";
    uint8_t Rx_Buffer[BufferSize];
    
    __IO uint32_t DeviceID = 0;
    __IO uint32_t FlashID = 0;
    __IO TestStatus TransferStatus1 = FAILED;
    
    // 函数原型声明
    void Delay(__IO uint32_t nCount);
    TestStatus Buffercmp(uint8_t* pBuffer1,uint8_t* pBuffer2, uint16_t BufferLength);
    
    /*
     * 函数名:main
     * 描述  :主函数
     * 输入  :无
     * 输出  :无
     */
    int main(void)
    { 	
    	LED_GPIO_Config();
    	LED_BLUE;
    	
    	/* 配置串口为:115200 8-N-1 */
    	USART_Config();
    	printf("\r\n 这是一个8Mbyte串行flash(W25Q64)实验 \r\n");
    	
    	/* 8M串行flash W25Q64初始化 */
    	SPI_FLASH_Init();
    	
    	/* 获取 Flash Device ID */
    	DeviceID = SPI_FLASH_ReadDeviceID();	
    	Delay( 200 );
    	
    	/* 获取 SPI Flash ID */
    	FlashID = SPI_FLASH_ReadID();	
    	printf("\r\n FlashID is 0x%X,\
    	Manufacturer Device ID is 0x%X\r\n", FlashID, DeviceID);
    	
    	/* 检验 SPI Flash ID */
    	if (FlashID == sFLASH_ID)
    	{	
    		printf("\r\n 检测到串行flash W25Q64 !\r\n");
    		
    		/* 擦除将要写入的 SPI FLASH 扇区,FLASH写入前要先擦除 */
    		// 这里擦除4K,即一个扇区,擦除的最小单位是扇区
    		SPI_FLASH_SectorErase(FLASH_SectorToErase);	 	 
    		
    		/* 将发送缓冲区的数据写到flash中 */
    		// 这里写一页,一页的大小为256个字节
    		SPI_FLASH_BufferWrite(Tx_Buffer, FLASH_WriteAddress, BufferSize);		
    		printf("\r\n 写入的数据为:%s \r\t", Tx_Buffer);
    		
    		/* 将刚刚写入的数据读出来放到接收缓冲区中 */
    		SPI_FLASH_BufferRead(Rx_Buffer, FLASH_ReadAddress, BufferSize);
    		printf("\r\n 读出的数据为:%s \r\n", Rx_Buffer);
    		
    		/* 检查写入的数据与读出的数据是否相等 */
    		TransferStatus1 = Buffercmp(Tx_Buffer, Rx_Buffer, BufferSize);
    		
    		if( PASSED == TransferStatus1 )
    		{ 
    			LED_GREEN;
    			printf("\r\n 8M串行flash(W25Q64)测试成功!\n\r");
    		}
    		else
    		{        
    			LED_RED;
    			printf("\r\n 8M串行flash(W25Q64)测试失败!\n\r");
    		}
    	}// if (FlashID == sFLASH_ID)
    	else// if (FlashID == sFLASH_ID)
    	{ 
    		LED_RED;
    		printf("\r\n 获取不到 W25Q64 ID!\n\r");
    	}
    	
    	while(1);  
    }
    
    /*
     * 函数名:Buffercmp
     * 描述  :比较两个缓冲区中的数据是否相等
     * 输入  :-pBuffer1     src缓冲区指针
     *         -pBuffer2     dst缓冲区指针
     *         -BufferLength 缓冲区长度
     * 输出  :无
     * 返回  :-PASSED pBuffer1 等于   pBuffer2
     *         -FAILED pBuffer1 不同于 pBuffer2
     */
    TestStatus Buffercmp(uint8_t* pBuffer1, uint8_t* pBuffer2, uint16_t BufferLength)
    {
      while(BufferLength--)
      {
        if(*pBuffer1 != *pBuffer2)
        {
          return FAILED;
        }
    
        pBuffer1++;
        pBuffer2++;
      }
      return PASSED;
    }
    
    void Delay(__IO uint32_t nCount)
    {
      for(; nCount != 0; nCount--);
    }
    

    总结

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

    物联沃分享整理
    物联沃-IOTWORD物联网 » SPI协议—读写串行FLASH(详细讲解+代码)

    发表评论