微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

单片机学习笔记————STM32使用SPI读写串行Flash二

目录

 

第一步:STM32与Flash的硬件连接

第二步:配置相关的宏

第三步:SPI初始化

1.SPI相关GPIO初始化

2.SPI模式初始化

第四步:使用SPI实现发送及接收一个字节的数据

1.发送一个字节

2.接收一个字节

第五步:读取Flash ID的函数

第六步:读取Flash ID的代码实现

1.编程要点

2.代码实现

3.测试结果​


第一步:STM32与Flash的硬件连接

单片机型号:STM32F103ZET6

Flash型号:W25Q64

第二步:配置相关的宏

/**************************SPI接口定义********************************/
#define             FLASH_SPIx                                SPI1
#define             FLASH_SPI_APBxClock_FUN                   RCC_APB2PeriphClockCmd
#define             FLASH_SPI_CLK                             RCC_APB2Periph_SPI1

/* CS 片选选普通GPIO即可 */
#define             FLASH_SPI_CS_APBxClock_FUN                RCC_APB2PeriphClockCmd
#define             FLASH_SPI_CS_CLK                          RCC_APB2Periph_GPIOA     
#define             FLASH_SPI_CS_PORT                         GPIOA   
#define             FLASH_SPI_CS_PIN                          GPIO_Pin_4
/* SCK  */
#define             FLASH_SPI_SCK_APBxClock_FUN               RCC_APB2PeriphClockCmd
#define             FLASH_SPI_SCK_CLK                         RCC_APB2Periph_GPIOA     
#define             FLASH_SPI_SCK_PORT                        GPIOA   
#define             FLASH_SPI_SCK_PIN                         GPIO_Pin_5
/* MOSI  */
#define             FLASH_SPI_MOSI_APBxClock_FUN              RCC_APB2PeriphClockCmd
#define             FLASH_SPI_MOSI_CLK                        RCC_APB2Periph_GPIOA     
#define             FLASH_SPI_MOSI_PORT                       GPIOA   
#define             FLASH_SPI_MOSI_PIN                        GPIO_Pin_7
/* MISO  */
#define             FLASH_SPI_MISO_APBxClock_FUN              RCC_APB2PeriphClockCmd
#define             FLASH_SPI_MISO_CLK                        RCC_APB2Periph_GPIOA     
#define             FLASH_SPI_MISO_PORT                       GPIOA   
#define             FLASH_SPI_MISO_PIN                        GPIO_Pin_6

#define             FLASH_SPI_CS_LOW()                        (GPIO_ResetBits(FLASH_SPI_CS_PORT,FLASH_SPI_CS_PIN))
#define             FLASH_SPI_CS_HIGH()                       (GPIO_SetBits(FLASH_SPI_CS_PORT,FLASH_SPI_CS_PIN))

注意:在定义MOSI及MISO的时候,如果反了,会造成后续数据错误

第三步:SPI初始化

1.SPI相关GPIO初始化

/**
  * @brief  SPI GPIO初始化
  * @param  无
  * @retval 无
  */
static void SPI_GPIO_Config(void)
{
  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_MOSI_CLK | FLASH_SPI_MISO_CLK, ENABLE);
  /* 配置SPI的CS引脚,普通IO */
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_InitStructure.GPIO_Pin = FLASH_SPI_CS_PIN;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(FLASH_SPI_CS_PORT, &GPIO_InitStructure);
  /* 配置SPI的SCK引脚 复用推挽输出*/
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  GPIO_InitStructure.GPIO_Pin = FLASH_SPI_SCK_PIN;
  GPIO_Init(FLASH_SPI_SCK_PORT, &GPIO_InitStructure);
  /* 配置SPI的MOSI引脚 复用推挽输出*/
  GPIO_InitStructure.GPIO_Pin = FLASH_SPI_MOSI_PIN;
  GPIO_Init(FLASH_SPI_MOSI_PORT, &GPIO_InitStructure);
  /* 配置SPI的MISO引脚 浮空输入*/
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  GPIO_InitStructure.GPIO_Pin = FLASH_SPI_MISO_PIN;
  GPIO_Init(FLASH_SPI_MISO_PORT, &GPIO_InitStructure);
	
  /* 停止信号 CS高电平 */
  FLASH_SPI_CS_HIGH();
}

2.SPI模式初始化

/**
  * @brief  SPI 模式初始化
  * @param  无
  * @retval 无
  */
static void SPI_MODE_Config(void)
{
  SPI_InitTypeDef    SPI_InitStructure;
	
  /* SPI模式配置 */
	SPI_InitStructure.SPI_BaudratePrescaler = SPI_BaudratePrescaler_2;    /* 2分频 36MHz */
	SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;                          /* 模式3 CPOL = 1 CPHA = 1 */
	SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
	SPI_InitStructure.SPI_CRCpolynomial = 7;                              /* 未使用,任意值 */
	SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;                     /* 数据帧长度为8位 */
	SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;    /* 双线全双工模式 */
	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;                    /* MSB先行 */
	SPI_InitStructure.SPI_Mode = SPI_Mode_Master;                         /* 主机模式 */
	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;                             /* 软件片选 */	
	SPI_Init(FLASH_SPIx, &SPI_InitStructure);
	
  /* 使能SPI */
	SPI_Cmd(FLASH_SPIx, ENABLE);
}

第四步:使用SPI实现发送及接收一个字节的数据

由于SPI为同步通信,在实现发送的时候,也是同步接收的,所以发送及接收本质上是一个函数

1.发送一个字节

/**
  * @brief  使用SPI发送一个字节的数据
  * @param  byte:要发送的数据
  * @retval 返回接收到的数据
  */
u8 SPI_FLASH_SendByte(u8 byte)
{
  SPITimeout = SPIT_FLAG_TIMEOUT;

  /* 等待发送缓冲区为空 TXE事件 */
  while(SPI_I2S_GetFlagStatus(FLASH_SPIx, SPI_I2S_FLAG_TXE) == RESET)
  {
    if((SPITimeout--) == 0)
    {
      return SPI_TIMEOUT_UserCallback(0);
    }
  }
  /* 写入数据寄存器,把要写入的数据写入发送缓冲区 */
  SPI_I2S_SendData(FLASH_SPIx, byte);

  SPITimeout = SPIT_FLAG_TIMEOUT;

  /* 等待接收缓冲区非空,RXNE事件 */
  while(SPI_I2S_GetFlagStatus(FLASH_SPIx, SPI_I2S_FLAG_RXNE) == RESET)
  {
    if((SPITimeout--) == 0)
    {
      return SPI_TIMEOUT_UserCallback(1);
    }
  } 
  return SPI_I2S_ReceiveData(FLASH_SPIx);
}

在通信过程增加超时的报错。

2.接收一个字节

/**
  * @brief  使用SPI读取一个字节的数据
  * @param  无
  * @retval 无
  */
u8 SPI_FLASH_ReadByte(void)
{
  return SPI_FLASH_SendByte(Dummy_Byte);
}

第五步:读取Flash ID的函数

#define W25X_Jedecdeviceid            0x9F
#define Dummy_Byte                    0xFF

/**
  * @brief  读取FLASH ID
  * @param  无
  * @retval FLASH ID
  */
u32 SPI_FLASH_ReadID(void)
{
  u32 Temp = 0, Temp0 = 0, Temp1 = 0, Temp2 = 0;

  /* 开始通信:CS拉低 */
  FLASH_SPI_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拉高 */
  FLASH_SPI_CS_HIGH();

  Temp = ((Temp0 << 16) | (Temp1 << 8) | Temp2);
  return Temp;
}

发送指定的指令,可以获取相应的数据,发送0x9F即可获取Flash ID。

第六步:读取Flash ID的代码实现

1.编程要点

(1) 初始化通讯使用的目标引脚及端口时钟;

(2) 使能 SPI 外设的时钟;

(3) 配置 SPI 外设的模式、地址、速率等参数并使能 SPI 外设;

(4) 编写基本 SPI 按字节收发的函数

(5) 编写对 FLASH 的Flash ID读取的函数

(6) 测试

2.代码实现

#ifndef __SPI_FLASH_H
#define	__SPI_FLASH_H


#include "stm32f10x.h"

//#define  sFLASH_ID              0xEF3015   //W25X16
//#define  sFLASH_ID              0xEF4015	 //W25Q16
//#define  sFLASH_ID              0XEF4018   //W25Q128
#define  sFLASH_ID              0XEF4017    //W25Q64

/* FLASH 常用命令 */
#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
#define Dummy_Byte                    0xFF


/**************************SPI接口定义********************************/
#define             FLASH_SPIx                                SPI1
#define             FLASH_SPI_APBxClock_FUN                   RCC_APB2PeriphClockCmd
#define             FLASH_SPI_CLK                             RCC_APB2Periph_SPI1

/* CS 片选选普通GPIO即可 */
#define             FLASH_SPI_CS_APBxClock_FUN                RCC_APB2PeriphClockCmd
#define             FLASH_SPI_CS_CLK                          RCC_APB2Periph_GPIOA     
#define             FLASH_SPI_CS_PORT                         GPIOA   
#define             FLASH_SPI_CS_PIN                          GPIO_Pin_4
/* SCK  */
#define             FLASH_SPI_SCK_APBxClock_FUN               RCC_APB2PeriphClockCmd
#define             FLASH_SPI_SCK_CLK                         RCC_APB2Periph_GPIOA     
#define             FLASH_SPI_SCK_PORT                        GPIOA   
#define             FLASH_SPI_SCK_PIN                         GPIO_Pin_5
/* MOSI  */
#define             FLASH_SPI_MOSI_APBxClock_FUN              RCC_APB2PeriphClockCmd
#define             FLASH_SPI_MOSI_CLK                        RCC_APB2Periph_GPIOA     
#define             FLASH_SPI_MOSI_PORT                       GPIOA   
#define             FLASH_SPI_MOSI_PIN                        GPIO_Pin_7
/* MISO  */
#define             FLASH_SPI_MISO_APBxClock_FUN              RCC_APB2PeriphClockCmd
#define             FLASH_SPI_MISO_CLK                        RCC_APB2Periph_GPIOA     
#define             FLASH_SPI_MISO_PORT                       GPIOA   
#define             FLASH_SPI_MISO_PIN                        GPIO_Pin_6

#define             FLASH_SPI_CS_LOW()                        (GPIO_ResetBits(FLASH_SPI_CS_PORT,FLASH_SPI_CS_PIN))
#define             FLASH_SPI_CS_HIGH()                       (GPIO_SetBits(FLASH_SPI_CS_PORT,FLASH_SPI_CS_PIN))

/*等待超时时间*/
#define SPIT_FLAG_TIMEOUT         ((uint32_t)0x1000)
#define SPIT_LONG_TIMEOUT         ((uint32_t)(10 * SPIT_FLAG_TIMEOUT))


/*信息输出*/
#define FLASH_DEBUG_ON         0

#define FLASH_INFO(fmt,arg...)           printf("<<-FLASH-INFO->> "fmt"\n",##arg)
#define FLASH_ERROR(fmt,arg...)          printf("<<-FLASH-ERROR->> "fmt"\n",##arg)
#define FLASH_DEBUG(fmt,arg...)          do{\
                                          if(FLASH_DEBUG_ON)\
                                          printf("<<-FLASH-DEBUG->> [%d]"fmt"\n",__LINE__, ##arg);\
                                          }while(0)


void SPI_FLASH_Init(void);
u8 SPI_FLASH_SendByte(u8 byte);
u8 SPI_FLASH_ReadByte(void);
u32 SPI_FLASH_ReadID(void);

#endif /* __SPI_FLASH_H */
/**
  ******************************************************************************
  * @file    bsp_spi_flash.c
  * @author  ZHQ
  * @version V1.0
  * @date    2021-02-15
  * @brief   spi flash应用函数bsp
  ******************************************************************************
  */ 

#include "./spi/bsp_spi_flash.h"
#include "./usart/bsp_usart.h"		



static __IO uint32_t  SPITimeout = SPIT_LONG_TIMEOUT;   


static uint32_t SPI_TIMEOUT_UserCallback(uint8_t errorCode);


/**
  * @brief  SPI GPIO初始化
  * @param  无
  * @retval 无
  */
static void SPI_GPIO_Config(void)
{
  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_MOSI_CLK | FLASH_SPI_MISO_CLK, ENABLE);
  /* 配置SPI的CS引脚,普通IO */
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Pin = FLASH_SPI_CS_PIN;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(FLASH_SPI_CS_PORT, &GPIO_InitStructure);
  /* 配置SPI的SCK引脚 复用推挽输出*/
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = FLASH_SPI_SCK_PIN;
	GPIO_Init(FLASH_SPI_SCK_PORT, &GPIO_InitStructure);
  /* 配置SPI的MOSI引脚 复用推挽输出*/
	GPIO_InitStructure.GPIO_Pin = FLASH_SPI_MOSI_PIN;
	GPIO_Init(FLASH_SPI_MOSI_PORT, &GPIO_InitStructure);
  /* 配置SPI的MISO引脚 */
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_InitStructure.GPIO_Pin = FLASH_SPI_MISO_PIN;
	GPIO_Init(FLASH_SPI_MISO_PORT, &GPIO_InitStructure);
	
  /* 停止信号 CS高电平 */
  FLASH_SPI_CS_HIGH();
}

/**
  * @brief  SPI 模式初始化
  * @param  无
  * @retval 无
  */
static void SPI_MODE_Config(void)
{
  SPI_InitTypeDef    SPI_InitStructure;
	
  /* SPI模式配置 */
	SPI_InitStructure.SPI_BaudratePrescaler = SPI_BaudratePrescaler_2;    /* 2分频 36MHz */
	SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;                          /* 模式3 CPOL = 1 CPHA = 1 */
	SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
	SPI_InitStructure.SPI_CRCpolynomial = 7;                              /* 未使用,任意值 */
	SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;                     /* 数据帧长度为8位 */
	SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;    /* 双线全双工模式 */
	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;                    /* MSB先行 */
	SPI_InitStructure.SPI_Mode = SPI_Mode_Master;                         /* 主机模式 */
	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;                             /* 软件片选 */	
	SPI_Init(FLASH_SPIx, &SPI_InitStructure);
	
  /* 使能SPI */
	SPI_Cmd(FLASH_SPIx, ENABLE);
}

/**
  * @brief  SPI 初始化
  * @param  无
  * @retval 无
  */
void SPI_FLASH_Init(void)
{
  SPI_GPIO_Config();
  SPI_MODE_Config();
}

/**
  * @brief  使用SPI发送一个字节的数据
  * @param  byte:要发送的数据
  * @retval 返回接收到的数据
  */
u8 SPI_FLASH_SendByte(u8 byte)
{
  SPITimeout = SPIT_FLAG_TIMEOUT;

  /* 等待发送缓冲区为空 TXE事件 */
  while(SPI_I2S_GetFlagStatus(FLASH_SPIx, SPI_I2S_FLAG_TXE) == RESET)
  {
    if((SPITimeout--) == 0)
    {
      return SPI_TIMEOUT_UserCallback(0);
    }
  }
  /* 写入数据寄存器,把要写入的数据写入发送缓冲区 */
  SPI_I2S_SendData(FLASH_SPIx, byte);

  SPITimeout = SPIT_FLAG_TIMEOUT;

  /* 等待接收缓冲区非空,RXNE事件 */
  while(SPI_I2S_GetFlagStatus(FLASH_SPIx, SPI_I2S_FLAG_RXNE) == RESET)
  {
    if((SPITimeout--) == 0)
    {
      return SPI_TIMEOUT_UserCallback(1);
    }
  } 
  return SPI_I2S_ReceiveData(FLASH_SPIx);
}


/**
  * @brief  使用SPI读取一个字节的数据
  * @param  无
  * @retval 无
  */
u8 SPI_FLASH_ReadByte(void)
{
  return SPI_FLASH_SendByte(Dummy_Byte);
}

/**
  * @brief  读取FLASH ID
  * @param  无
  * @retval FLASH ID
  */
u32 SPI_FLASH_ReadID(void)
{
  u32 Temp = 0, Temp0 = 0, Temp1 = 0, Temp2 = 0;

  /* 开始通信:CS拉低 */
  FLASH_SPI_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拉高 */
  FLASH_SPI_CS_HIGH();

  Temp = ((Temp0 << 16) | (Temp1 << 8) | Temp2);
  return Temp;
}

/**
  * @brief  Basic management of the timeout situation.
  * @param  errorCode:错误代码,可以用来定位是哪个环节出错.
  * @retval 返回0,表示SPI读取失败.
  */
static  uint32_t SPI_TIMEOUT_UserCallback(uint8_t errorCode)
{
  /* Block communication and all processes */
  FLASH_ERROR("SPI 等待超时!errorCode = %d",errorCode);
  
  return 0;
}
/*********************************************END OF FILE**********************/

/**
  ******************************************************************************
  * @file    main.c
  * @author  ZHQ
  * @version V1.0
  * @date    2021-02-15
  * @brief   SPI flash测试,测试信息通过USART1打印在电脑的超级终端
  ******************************************************************************
  */
  
#include "stm32f10x.h"
#include "./usart/bsp_usart.h"
#include "./spi/bsp_spi_flash.h"
#include <string.h>


/**
  * @brief  主函数
  * @param  无
  * @retval 无
  */
int main(void)
{ 
  u32 Flash_ID;
  /* 串口初始化 */
  USART_Config();
	
  printf("\r\n 这是一个SPI外设(FLASH)读写测试例程 \r\n");

  SPI_FLASH_Init();
  Flash_ID = SPI_FLASH_ReadID();  
  printf("\r\n Flash_ID : 0x%X \r\n", Flash_ID);
  while (1)
  {  
    ;    
  }
}


/*********************************************END OF FILE**********************/

3.测试结果

得到的结果0xEF4017与该芯片的规格书描述相符合。

 

 

 

 

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。

相关推荐