STM32(十):SPI (尺度库函数)

打印 上一主题 下一主题

主题 550|帖子 550|积分 1650

前言

上一篇文章已经先容了如何用STM32单片机中USART通讯协议来串口通讯,并向XCOM串口助手发送信息。这篇文章我们来先容一下如何用STM32单片机中SPI接口来实现LED的闪亮并玩转WS2812B灯带。
一、实验原理

串行通讯之前的博客里有所先容,可以查看以下链接
STM32(九):USART串口通讯 (尺度库函数)-CSDN博客
1.SPI的先容

SPI(Serial Peripheral interface),顾名思义就是串行外围装备接口。SPI接口重要应用在 EEPROM,FLASH,实时时钟,AD转换器,还有数字信号处理器和数字信号解码器之间。SPI,是一种高速的,全双工,同步的通讯总线,而且在芯片的管脚上只占用四根线,节省了芯片的管脚,同时为PCB的布局上节省空间,提供方便,正是出于这种简单易用的特性,现在越来越多的芯片集成了这种通讯协议。
2.SPI的通讯原理

   通常SPI通过4个引脚与外部器件相连:
  ● MISO:主装备输入/从装备输出引脚。该引脚在从模式下发送数据,在主模式下接收数据。
  ● MOSI:主装备输出/从装备输入引脚。该引脚在主模式下发送数据,在从模式下接收数据。
  ● SCK: 串口时钟,作为主装备的输出,从装备的输入。
  ● NSS: 从装备选择。这是一个可选的引脚,用来选择主/从装备。它的功能是用来作为“片选引脚”,让主装备可以单独地与特定从装备通讯,避免数据线上的冲突。从装备的NSS引脚可以由主装备的一个尺度I/O引脚来驱动。
    SPI有主、从两种模式
  

  • 作为主机时,利用一个IO引脚拉低相应从机的选择引脚(NSS),传输的起始由主机发送数据来启动,时钟(SCK)信号由主机产生。通过MOSI发送数据,同时通过MISO引脚接收从机发出的数据。    
  • 作为从机时,传输在从机选择引脚(NSS)被主机拉低后开始,接收主机输出的时钟信号,在读取主机数据的同时通过MISO引脚输出数据。
  下图中简单模拟SPI通讯流程,主机拉低NSS片选信号,启动通讯,而且产生时钟信号,上升沿触发边沿信号,主机在MOSI线路一位一位发送数据0X53,在MISO线路一位一位接收数据0X46。

3.SPI的时序

   数据时钟时序图
  
  通过组合CPOL和CPHA的设置,可以产生四种大概的时序关系,这影响数据的采样和锁存时机。这些设置对SPI主模式和从模式下的装备都实用。
  

  • CPOL(时钟极性):控制在没有数据传输时SCK(时钟)引脚的空闲状态电平。
  

  • CPOL=0:SCK引脚在空闲状态保持低电平。
  • CPOL=1:SCK引脚在空闲状态保持高电平。
  

  • CPHA(时钟相位):确定命据位的采样和锁存时机。
  

  • CPHA=0:数据在第一个时钟边沿被采样和锁存。第一个边沿是降落沿假如CPOL=0,是上升沿假如CPOL=1。
  • CPHA=1:数据在第二个时钟边沿被采样和锁存。第二个边沿是降落沿假如CPOL=0,是上升沿假如CPOL=1。
  

  

  4.SPI制止

SPI共有5种制止事件,如下图所示:

二、实验步骤

1.串行FLASH初始化

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

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. }
复制代码
5.使能串行Flash写操作

  
  1. void SPI_FLASH_WriteEnable(void)
  2. {
  3.   /* 选择串行FLASH: CS低电平 */
  4.   FLASH_SPI_CS_ENABLE();
  5.   /* 发送命令:写使能 */
  6.   SPI_FLASH_SendByte(W25X_WriteEnable);
  7.   /* 禁用串行Flash:CS高电平 */
  8.   FLASH_SPI_CS_DISABLE();
  9. }
复制代码

  三、实操代码

程序分为3个文件:bsp_spi_flash.c、bsp_spi_flash.h、main.c
1.bsp_spi_flash.c 
  1. /* 包含头文件 ----------------------------------------------------------------*/#include "bsp/spi_flash/bsp_spi_flash.h"/* 私有范例定义 --------------------------------------------------------------*//* 私有宏定义 ----------------------------------------------------------------*/#define SPI_FLASH_PageSize              256#define SPI_FLASH_PerWritePageSize      256#define W25X_WriteEnable                            0x06 #define W25X_WriteDisable                            0x04 #define W25X_ReadStatusReg                          0x05 #define W25X_WriteStatusReg                          0x01 #define W25X_ReadData                                      0x03 #define W25X_FastReadData                            0x0B #define W25X_FastReadDual                            0x3B #define W25X_PageProgram                            0x02 #define W25X_BlockErase                                    0xD8 #define W25X_SectorErase                            0x20 #define W25X_ChipErase                                    0xC7 #define W25X_PowerDown                                    0xB9 #define W25X_ReleasePowerDown                  0xAB #define W25X_DeviceID                                      0xAB #define W25X_ManufactDeviceID                 0x90 #define W25X_JedecDeviceID                          0x9F #define WIP_Flag                        0x01  /* Write In Progress (WIP) flag */#define Dummy_Byte                      0xFF/* 私有变量 ------------------------------------------------------------------*//* 扩展变量 ------------------------------------------------------------------*//* 私有函数本相 --------------------------------------------------------------*//* 函数体 --------------------------------------------------------------------*/ /**  * 函数功能: 串行FLASH初始化  * 输入参数: 无  * 返 回 值: uint32_t:返回串行Flash型号ID  * 说    明:初始化串行Flash底层驱动GPIO和SPI外设  */void SPI_FLASH_Init(void)
  2. {
  3.   SPI_InitTypeDef  SPI_InitStructure;
  4.   GPIO_InitTypeDef GPIO_InitStructure;       
  5.   
  6.   /* 使能GPIO和SPI时钟 */
  7.         FLASH_SPI_APBxClock_FUN ( FLASH_SPI_CLK, ENABLE );
  8.   FLASH_SPI_SCK_APBxClock_FUN ( FLASH_SPI_SCK_CLK, ENABLE );
  9.         FLASH_SPI_MISO_APBxClock_FUN ( FLASH_SPI_MISO_CLK, ENABLE );
  10.   FLASH_SPI_MOSI_APBxClock_FUN ( FLASH_SPI_MOSI_CLK, ENABLE );
  11.   FLASH_SPI_CS_APBxClock_FUN ( FLASH_SPI_CS_CLK, ENABLE );
  12.   
  13.   /* 配置SPI功能引脚:SCK 时钟引脚 */       
  14.   GPIO_InitStructure.GPIO_Pin = FLASH_SPI_SCK_PIN;
  15.   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  
  16.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  17.   GPIO_Init(FLASH_SPI_SCK_PORT, &GPIO_InitStructure);
  18.   /* 配置SPI功能引脚:MISO 主机输入从机输出引脚 */       
  19.   GPIO_InitStructure.GPIO_Pin = FLASH_SPI_MISO_PIN;
  20.   GPIO_Init(FLASH_SPI_MISO_PORT, &GPIO_InitStructure);
  21.   /* 配置SPI功能引脚:MISO 主机输出从机输入引脚 */       
  22.   GPIO_InitStructure.GPIO_Pin = FLASH_SPI_MOSI_PIN;
  23.   GPIO_Init(FLASH_SPI_MOSI_PORT, &GPIO_InitStructure);
  24.   
  25.   /* 配置SPI功能引脚:CS 串行Flash片选引脚 */       
  26.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  27.   GPIO_InitStructure.GPIO_Pin = FLASH_SPI_CS_PIN;       
  28.   GPIO_Init(FLASH_SPI_CS_PORT, &GPIO_InitStructure);
  29.   /* 首先禁用串行Flash,等需要操作串行Flash时再使能即可 */
  30.   FLASH_SPI_CS_DISABLE();
  31.   /* SPI外设配置 */
  32.   /*
  33.    * FLASH芯片:
  34.          * 在CLK上升沿时到DIO数据采样输入.
  35.    * 在CLK下降沿时在DIO进行数据输出。
  36.          * 据此设置CPOL CPHA
  37.    */
  38.   SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
  39.   SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
  40.   SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
  41.   SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
  42.   SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
  43.   SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
  44.   SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;
  45.   SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
  46.   SPI_InitStructure.SPI_CRCPolynomial = 7;
  47.   SPI_Init(FLASH_SPIx , &SPI_InitStructure);
  48.   /* 使能SPI外设  */
  49.   SPI_Cmd(FLASH_SPIx , ENABLE);       
  50. }/**  * 函数功能: 擦除扇区  * 输入参数: SectorAddr:待擦除扇区地址,要求为4096倍数  * 返 回 值: 无  * 说    明:串行Flash最小擦除块巨细为4KB(4096字节),即一个扇区巨细,要求输入参数  *           为4096倍数。在往串行Flash芯片写入数据之前要求先擦除空间。  */void SPI_FLASH_SectorErase(u32 SectorAddr){  /* 发送FLASH写使能下令 */  SPI_FLASH_WriteEnable();  SPI_FLASH_WaitForWriteEnd();  /* 擦除扇区 */  /* 选择串行FLASH: CS低电平 */  FLASH_SPI_CS_ENABLE();  /* 发送扇区擦除指令*/  SPI_FLASH_SendByte(W25X_SectorErase);  /*发送擦除扇区地址的高位*/  SPI_FLASH_SendByte((SectorAddr & 0xFF0000) >> 16);  /* 发送擦除扇区地址的中位 */  SPI_FLASH_SendByte((SectorAddr & 0xFF00) >> 8);  /* 发送擦除扇区地址的低位 */  SPI_FLASH_SendByte(SectorAddr & 0xFF);  /* 禁用串行FLASH: CS 高电平 */  FLASH_SPI_CS_DISABLE();  /* 等待擦除完毕*/  SPI_FLASH_WaitForWriteEnd();}/**  * 函数功能: 擦除整片  * 输入参数: 无  * 返 回 值: 无  * 说    明:擦除串行Flash整片空间  */void SPI_FLASH_BulkErase(void)
  51. {
  52. /* 发送FLASH写使能命令 */
  53.   SPI_FLASH_WriteEnable();
  54.   /* 整片擦除 Erase */
  55.   /* 选择串行FLASH: CS低电平 */
  56.   FLASH_SPI_CS_ENABLE();
  57.   /* 发送整片擦除指令*/
  58.   SPI_FLASH_SendByte(W25X_ChipErase);
  59.   /* 禁用串行FLASH: CS高电平 */
  60.   FLASH_SPI_CS_DISABLE();
  61.   /* 等待擦除完毕*/
  62.   SPI_FLASH_WaitForWriteEnd();
  63. }/**  * 函数功能: 往串行FLASH按页写入数据,调用本函数写入数据前必要先擦除扇区  * 输入参数: pBuffer:待写入数据的指针  *           WriteAddr:写入地址  *           NumByteToWrite:写入数据长度,必须小于即是SPI_FLASH_PerWritePageSize  * 返 回 值: 无  * 说    明:串行Flash每页巨细为256个字节  */void SPI_FLASH_PageWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite){  /* 发送FLASH写使能下令 */  SPI_FLASH_WriteEnable();   /* 探求串行FLASH: CS低电平 */  FLASH_SPI_CS_ENABLE();  /* 写送写指令*/  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;     //printf("Err: SPI_FLASH_PageWrite too large!\n");  }  /* 写入数据*/  while (NumByteToWrite--)  {     /* 发送当前要写入的字节数据 */    SPI_FLASH_SendByte(*pBuffer);     /* 指向下一字节数据 */    pBuffer++;  }  /* 禁用串行FLASH: CS 高电平 */  FLASH_SPI_CS_DISABLE();  /* 等待写入完毕*/  SPI_FLASH_WaitForWriteEnd();}/**  * 函数功能: 往串行FLASH写入数据,调用本函数写入数据前必要先擦除扇区  * 输入参数: pBuffer:待写入数据的指针  *           WriteAddr:写入地址  *           NumByteToWrite:写入数据长度  * 返 回 值: 无  * 说    明:该函数可以设置任意写入数据长度  */void SPI_FLASH_BufferWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite)
  64. {
  65.   u8 NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0, temp = 0;
  66.   Addr = WriteAddr % SPI_FLASH_PageSize;
  67.   count = SPI_FLASH_PageSize - Addr;
  68.   NumOfPage =  NumByteToWrite / SPI_FLASH_PageSize;
  69.   NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;
  70.   if (Addr == 0) /* 若地址与 SPI_FLASH_PageSize 对齐  */
  71.   {
  72.     if (NumOfPage == 0) /* NumByteToWrite < SPI_FLASH_PageSize */
  73.     {
  74.       SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);
  75.     }
  76.     else /* NumByteToWrite > SPI_FLASH_PageSize */
  77.     {
  78.       while (NumOfPage--)
  79.       {
  80.         SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize);
  81.         WriteAddr +=  SPI_FLASH_PageSize;
  82.         pBuffer += SPI_FLASH_PageSize;
  83.       }
  84.       SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);
  85.     }
  86.   }
  87.   else /* 若地址与 SPI_FLASH_PageSize 不对齐 */
  88.   {
  89.     if (NumOfPage == 0) /* NumByteToWrite < SPI_FLASH_PageSize */
  90.     {
  91.       if (NumOfSingle > count) /* (NumByteToWrite + WriteAddr) > SPI_FLASH_PageSize */
  92.       {
  93.         temp = NumOfSingle - count;
  94.         SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);
  95.         WriteAddr +=  count;
  96.         pBuffer += count;
  97.         SPI_FLASH_PageWrite(pBuffer, WriteAddr, temp);
  98.       }
  99.       else
  100.       {
  101.         SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);
  102.       }
  103.     }
  104.     else /* NumByteToWrite > SPI_FLASH_PageSize */
  105.     {
  106.       NumByteToWrite -= count;
  107.       NumOfPage =  NumByteToWrite / SPI_FLASH_PageSize;
  108.       NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;
  109.       SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);
  110.       WriteAddr +=  count;
  111.       pBuffer += count;
  112.       while (NumOfPage--)
  113.       {
  114.         SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize);
  115.         WriteAddr +=  SPI_FLASH_PageSize;
  116.         pBuffer += SPI_FLASH_PageSize;
  117.       }
  118.       if (NumOfSingle != 0)
  119.       {
  120.         SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);
  121.       }
  122.     }
  123.   }
  124. }/**  * 函数功能: 从串行Flash读取数据  * 输入参数: pBuffer:存放读取到数据的指针  *           ReadAddr:读取数据目标地址  *           NumByteToRead:读取数据长度  * 返 回 值: 无  * 说    明:该函数可以设置任意读取数据长度  */void SPI_FLASH_BufferRead(u8* pBuffer, u32 ReadAddr, u16 NumByteToRead)
  125. {
  126.   /* 选择串行FLASH: CS低电平 */
  127.   FLASH_SPI_CS_ENABLE();
  128.   /* 发送 读 指令 */
  129.   SPI_FLASH_SendByte(W25X_ReadData);
  130.   /* 发送 读 地址高位 */
  131.   SPI_FLASH_SendByte((ReadAddr & 0xFF0000) >> 16);
  132.   /* 发送 读 地址中位 */
  133.   SPI_FLASH_SendByte((ReadAddr& 0xFF00) >> 8);
  134.   /* 发送 读 地址低位 */
  135.   SPI_FLASH_SendByte(ReadAddr & 0xFF);
  136.   while (NumByteToRead--) /* 读取数据 */
  137.   {
  138.      /* 读取一个字节*/
  139.     *pBuffer = SPI_FLASH_SendByte(Dummy_Byte);
  140.     /* 指向下一个字节缓冲区 */
  141.     pBuffer++;
  142.   }
  143.   /* 禁用串行FLASH: CS 高电平 */
  144.   FLASH_SPI_CS_DISABLE();
  145. }/**  * 函数功能: 读取串行Flash型号的ID  * 输入参数: 无  * 返 回 值: u32:串行Flash的型号ID  * 说    明:  FLASH_ID      IC型号      存储空间巨细                         0xEF3015      W25X16        2M byte                0xEF4015            W25Q16        4M byte                0XEF4017      W25Q64        8M byte                0XEF4018      W25Q128       16M byte  (YS-F1Pro开发板默认配置)  */u32 SPI_FLASH_ReadID(void){  u32 Temp = 0, Temp0 = 0, Temp1 = 0, Temp2 = 0;  /* 选择串行FLASH: CS低电平 */  FLASH_SPI_CS_ENABLE();  /* 发送命令:读取芯片型号ID */  SPI_FLASH_SendByte(W25X_JedecDeviceID);  /* 从串行Flash读取一个字节数据 */  Temp0 = SPI_FLASH_SendByte(Dummy_Byte);  /* 从串行Flash读取一个字节数据 */  Temp1 = SPI_FLASH_SendByte(Dummy_Byte);  /* 从串行Flash读取一个字节数据 */  Temp2 = SPI_FLASH_SendByte(Dummy_Byte);  /* 禁用串行Flash:CS高电平 */  FLASH_SPI_CS_DISABLE();    Temp = (Temp0 << 16) | (Temp1 << 8) | Temp2;  return Temp;}/**  * 函数功能: 读取串行Flash装备ID  * 输入参数: 无  * 返 回 值: u32:串行Flash的装备ID  * 说    明:  */u32 SPI_FLASH_ReadDeviceID(void){  u32 Temp = 0;  /* 选择串行FLASH: CS低电平 */  FLASH_SPI_CS_ENABLE();  /* 发送命令:读取芯片装备ID * */  SPI_FLASH_SendByte(W25X_DeviceID);  SPI_FLASH_SendByte(Dummy_Byte);  SPI_FLASH_SendByte(Dummy_Byte);  SPI_FLASH_SendByte(Dummy_Byte);    /* 从串行Flash读取一个字节数据 */  Temp = SPI_FLASH_SendByte(Dummy_Byte);  /* 禁用串行Flash:CS高电平 */  FLASH_SPI_CS_DISABLE();  return Temp;}/**  * 函数功能: 启动连续读取数据串  * 输入参数: ReadAddr:读取地址  * 返 回 值: 无  * 说    明:Initiates a read data byte (READ) sequence from the Flash.  *           This is done by driving the /CS line low to select the device,  *           then the READ instruction is transmitted followed by 3 bytes  *           address. This function exit and keep the /CS line low, so the  *           Flash still being selected. With this technique the whole  *           content of the Flash is read with a single READ instruction.  */void SPI_FLASH_StartReadSequence(u32 ReadAddr){  /* Select the FLASH: Chip Select low */  FLASH_SPI_CS_ENABLE();  /* Send "Read from Memory " instruction */  SPI_FLASH_SendByte(W25X_ReadData);  /* Send the 24-bit address of the address to read from -----------------------*/  /* Send ReadAddr high nibble address byte */  SPI_FLASH_SendByte((ReadAddr & 0xFF0000) >> 16);  /* Send ReadAddr medium nibble address byte */  SPI_FLASH_SendByte((ReadAddr& 0xFF00) >> 8);  /* Send ReadAddr low nibble address byte */  SPI_FLASH_SendByte(ReadAddr & 0xFF);}/**  * 函数功能: 从串行Flash读取一个字节数据  * 输入参数: 无  * 返 回 值: u8:读取到的数据  * 说    明:This function must be used only if the Start_Read_Sequence  *           function has been previously called.  */u8 SPI_FLASH_ReadByte(void){  return (SPI_FLASH_SendByte(Dummy_Byte));}/**  * 函数功能: 往串行Flash读取写入一个字节数据并接收一个字节数据  * 输入参数: byte:待发送数据  * 返 回 值: u8:接收到的数据  * 说    明:无  */u8 SPI_FLASH_SendByte(u8 byte){  /* 循环等待直到SPI 数据寄存器DR为空,即当DR寄存器不为空时连续等待 */  while (SPI_I2S_GetFlagStatus(FLASH_SPIx , SPI_I2S_FLAG_TXE) == RESET);  /* 通过SPI外设发送一个字节数据 */  SPI_I2S_SendData(FLASH_SPIx , byte);  /* 等待接收到数据 */  while (SPI_I2S_GetFlagStatus(FLASH_SPIx , SPI_I2S_FLAG_RXNE) == RESET);  /* 读取SPI总线接收到一个字节数据并返回 */  return SPI_I2S_ReceiveData(FLASH_SPIx );}/**  * 函数功能: 往串行Flash读取写入半字(16bit)数据并接收半字数据  * 输入参数: byte:待发送数据  * 返 回 值: u16:接收到的数据  * 说    明:无  */u16 SPI_FLASH_SendHalfWord(u16 HalfWord){  /* 循环等待直到SPI 数据寄存器DR为空,即当DR寄存器不为空时连续等待 */  while (SPI_I2S_GetFlagStatus(FLASH_SPIx , SPI_I2S_FLAG_TXE) == RESET);  /* 通过SPI外设发送半字数据 */  SPI_I2S_SendData(FLASH_SPIx , HalfWord);  /* 等待接收到数据 */  while (SPI_I2S_GetFlagStatus(FLASH_SPIx , SPI_I2S_FLAG_RXNE) == RESET);  /* 读取SPI总线接收到半字数据并返回 */  return SPI_I2S_ReceiveData(FLASH_SPIx );}/**  * 函数功能: 使能串行Flash写操作  * 输入参数: 无  * 返 回 值: 无  * 说    明:无  */void SPI_FLASH_WriteEnable(void)
  146. {
  147.   /* 选择串行FLASH: CS低电平 */
  148.   FLASH_SPI_CS_ENABLE();
  149.   /* 发送命令:写使能 */
  150.   SPI_FLASH_SendByte(W25X_WriteEnable);
  151.   /* 禁用串行Flash:CS高电平 */
  152.   FLASH_SPI_CS_DISABLE();
  153. }/**  * 函数功能: 等待数据写入完成  * 输入参数: 无  * 返 回 值: 无  * 说    明:Polls the status of the Write In Progress (WIP) flag in the  *           FLASH's status  register  and  loop  until write  opertaion  *           has completed.  */void SPI_FLASH_WaitForWriteEnd(void){  u8 FLASH_Status = 0;  /* Select the FLASH: Chip Select low */  FLASH_SPI_CS_ENABLE();  /* Send "Read Status Register" instruction */  SPI_FLASH_SendByte(W25X_ReadStatusReg);  /* Loop as long as the memory is busy with a write cycle */  do  {    /* Send a dummy byte to generate the clock needed by the FLASH    and put the value of the status register in FLASH_Status variable */    FLASH_Status = SPI_FLASH_SendByte(Dummy_Byte);           }  while ((FLASH_Status & WIP_Flag) == SET); /* Write in progress */  /* Deselect the FLASH: Chip Select high */  FLASH_SPI_CS_DISABLE();}/**  * 函数功能: 进入掉电模式  * 输入参数: 无  * 返 回 值: 无  * 说    明:无  */void SPI_Flash_PowerDown(void)   {   /* Select the FLASH: Chip Select low */  FLASH_SPI_CS_ENABLE();  /* Send "Power Down" instruction */  SPI_FLASH_SendByte(W25X_PowerDown);  /* Deselect the FLASH: Chip Select high */  FLASH_SPI_CS_DISABLE();}   /**  * 函数功能: 唤醒串行Flash  * 输入参数: 无  * 返 回 值: 无  * 说    明:无  */void SPI_Flash_WAKEUP(void)   {  /* Select the FLASH: Chip Select low */  FLASH_SPI_CS_ENABLE();  /* Send "Power Down" instruction */  SPI_FLASH_SendByte(W25X_ReleasePowerDown);  /* Deselect the FLASH: Chip Select high */  FLASH_SPI_CS_DISABLE(); }      
复制代码
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.   /* 调用格式化输出函数打印输出数据 */
  43.   printf("这是一个16M byte串行flash(W25Q128)读写测试实验\n");  
  44.   
  45.   /* 16M串行flash W25Q128初始化 */
  46.         SPI_FLASH_Init();
  47.        
  48.         /* Get SPI Flash Device ID */
  49.         DeviceID = SPI_FLASH_ReadDeviceID();
  50.        
  51.         Delay( 1 );
  52.        
  53.         /* Get SPI Flash ID */
  54.         FlashID = SPI_FLASH_ReadID();
  55.        
  56.         printf("FlashID is 0x%X,  Manufacturer Device ID is 0x%X\n", FlashID, DeviceID);
  57.        
  58.         /* Check the SPI Flash ID */
  59.         if (FlashID == SPI_FLASH_ID)  /* #define  sFLASH_ID  0XEF4018 */
  60.         {       
  61.                 printf("检测到华邦串行flash W25Q128 !\n");
  62.                
  63.                 /* 擦除SPI的扇区以写入 */
  64.                 SPI_FLASH_SectorErase(FLASH_SectorToErase);                  
  65.                
  66.                 /* 将发送缓冲区的数据写到flash中 */        
  67.                 SPI_FLASH_BufferWrite(Tx_Buffer, FLASH_WriteAddress, BufferSize);
  68.                 SPI_FLASH_BufferWrite(Tx_Buffer, 252, BufferSize);
  69.                 printf("写入的数据为:\n%s \n", Tx_Buffer);
  70.                
  71.                 /* 将刚刚写入的数据读出来放到接收缓冲区中 */
  72.                 SPI_FLASH_BufferRead(Rx_Buffer, FLASH_ReadAddress, BufferSize);
  73.                 printf("读出的数据为:\n %s\n", Rx_Buffer);
  74.                
  75.                 /* 检查写入的数据与读出的数据是否相等 */
  76.                 TransferStatus1 = Buffercmp(Tx_Buffer, Rx_Buffer, BufferSize);
  77.                
  78.                 if( PASSED == TransferStatus1 )
  79.                 {   
  80.                         printf("16M串行flash(W25Q128)测试成功!\r");
  81.                         LED1_ON;
  82.                 }
  83.                 else
  84.                 {        
  85.                         printf("16M串行flash(W25Q128)测试失败!\r");
  86.                         LED2_ON;
  87.                 }
  88.         }
  89.         else
  90.         {   
  91.                 printf("获取不到 W25Q128 ID!\n");
  92.                 LED3_ON;
  93.         }
  94.   
  95.   /* 无限循环 */
  96.   while (1)
  97.   {   
  98.   }
  99. }
  100. /*
  101. * 函数名:Buffercmp
  102. * 描述  :比较两个缓冲区中的数据是否相等
  103. * 输入  :-pBuffer1     src缓冲区指针
  104. *         -pBuffer2     dst缓冲区指针
  105. *         -BufferLength 缓冲区长度
  106. * 输出  :无
  107. * 返回  :-PASSED pBuffer1 等于   pBuffer2
  108. *         -FAILED pBuffer1 不同于 pBuffer2
  109. */
  110. static TestStatus Buffercmp(uint8_t* pBuffer1, uint8_t* pBuffer2, uint16_t BufferLength)
  111. {
  112.   while(BufferLength--)
  113.   {
  114.     if(*pBuffer1 != *pBuffer2)
  115.     {
  116.       return FAILED;
  117.     }
  118.     pBuffer1++;
  119.     pBuffer2++;
  120.   }
  121.   return PASSED;
  122. }
复制代码
四、实验效果

   这边展示的是用SPI点亮WS2812b,展示了红色和青色两种效果,假如对WS2812b有兴趣的同砚可以自行去了解一下,点亮方式还可以利用PWM波,可以看下这个。    WS2812B彩灯 STM32HAL库开发:PWM+DMA(stm32f103c8t6)_ws2812编程实例-CSDN博客     
  


参考博客:

代码用的是硬石嵌入式开发团队
竣事语

本文以STM32VET6为例解说了如何用STM32单片机中SPI接口来实现LED的闪亮并玩转WS2812B灯带,并指出其中的易坑点。盼望对大家有所帮助!假如还有什么问题,欢迎品评区留言,谢谢!

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

杀鸡焉用牛刀

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表