【HM】STM32F407 HAL库 SPI读写FLASH W25Q128

本文详细介绍了如何在STM32F407微控制器上使用HAL库进行W25Q128闪存的SPI读写操作,包括SPI的不同工作模式设置、时钟配置以及硬件电路的配合,同时提供了相关的初始化、擦除、读写和状态检查函数。
摘要由CSDN通过智能技术生成

【HM】STM32F407 HAL库 SPI读写FLASH W25Q128

W25Q128芯片简介

查看数据手册W25Q128芯片的SPI总线工作模式0支持(0,0)和3(1,1)

在这里插入图片描述

SPI不同工作模式CPOL和CPHA

Mode 0 : Clock Polarity (CPOL) = 0 and, Clock Phase (CPHA) = 0

Mode 1 : CPOL = 0 and, CPHA = 1

Mode 2 : CPOL = 1 and, CPHA = 0

Mode 3 : CPOL = 1 and, CPHA = 1

配置SYS和时钟

https://blog.csdn.net/hmxm6/article/details/136636793

配置SPI

Mode:全双工主机模式

NSS:不使用片选

Prescaler (for Baud Rate):波特率分配因子

CPOL:时钟极性

CPHA:时钟相位

image-20240315153020384
根据硬件电路配置SPI引脚
image-20240315153647090 image-20240315153629461 ##### 根据硬件电路配置SPI 片选引脚

测试程序

头文件
#ifndef __BSP_SPI_FLASH_H
#define __BSP_SPI_FLASH_H

#include "stm32f4xx.h"
#include "stdio.h"

/**************命令定义-开头*************/
#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
/**************命令定义-结尾*************/

#define SPI_FLASH_PageSize              256
#define SPI_FLASH_PerWritePageSize      256


// 	FLASH初始化
void SPI_FLASH_Init(SPI_HandleTypeDef hspi1);
// 	读写数据
uint8_t SPI_FLASH_ReadWrite(uint8_t txdata);

// 	读取ID
uint16_t SPI_FLASH_ReadManufactDeviceID(void);
uint16_t SPI_FLASH_ReadDeviceID(void);
uint32_t SPI_FLASH_ReadJedecDeviceID(void);

// 	写使能
void SPI_FLASH_WriteEnable(void);
//	进入掉电模式
void SPI_Flash_PowerDown(void);
//	唤醒
void SPI_Flash_WAKEUP(void);
// 	等待WIP(BUSY)标志被置0,即等待到FLASH内部数据写入完毕
void SPI_FLASH_WaitForWriteEnd(void);
//	对FLASH写入数据,调用本函数写入数据前需要先擦除扇区
void SPI_FLASH_BufferWrite(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite);
//  对FLASH按页写入数据,调用本函数写入数据前需要先擦除扇区
void SPI_FLASH_PageWrite(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite);
// 	读取FLASH数据
void SPI_FLASH_BufferRead(uint8_t* pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead);


// 	擦除FLASH扇区
void SPI_FLASH_SectorErase(uint32_t SectorAddr);
//  擦除FLASH扇区,整片擦除
void SPI_FLASH_BulkErase(void);


// 片选拉低
void SPI_FLASH_CS_LOW(void);
// 片选拉高
void SPI_FLASH_CS_HIGH(void);

#endif
片选信号函数&初始化函数
#include "bsp_spi_flash.h"

SPI_HandleTypeDef spiFlashHandle;

void SPI_FLASH_Init(SPI_HandleTypeDef hspi1)
{
	spiFlashHandle = hspi1;
	SPI_FLASH_CS_HIGH();
}

void SPI_FLASH_CS_LOW(void)
{
	HAL_GPIO_WritePin(GPIOG, GPIO_PIN_6, GPIO_PIN_RESET);
}

void SPI_FLASH_CS_HIGH(void)
{
	HAL_GPIO_WritePin(GPIOG, GPIO_PIN_6, GPIO_PIN_SET);
}
SPI最底层读写函数
uint8_t SPI_FLASH_ReadWrite(uint8_t txdata)
{
    uint8_t rxdata;
    HAL_SPI_TransmitReceive(&spiFlashHandle, &txdata, &rxdata, 1, 1000);
    return rxdata; /* 返回收到的数据 */
}
读取芯片各种ID函数
uint16_t SPI_FLASH_ReadManufactDeviceID(void)
{
    uint16_t deviceid;
 
    SPI_FLASH_CS_LOW();
    SPI_FLASH_ReadWrite(W25X_ManufactDeviceID);   /* 发送读 ID 命令 */
    SPI_FLASH_ReadWrite(0);                        /* 写入一个字节 */
    SPI_FLASH_ReadWrite(0);
    SPI_FLASH_ReadWrite(0);
    deviceid = SPI_FLASH_ReadWrite(0xFF) << 8;     /* 读取高8位字节 */
    deviceid |= SPI_FLASH_ReadWrite(0xFF);         /* 读取低8位字节 */
    SPI_FLASH_CS_HIGH();
 
    return deviceid;
}

uint16_t SPI_FLASH_ReadDeviceID(void)
{
	uint32_t Temp = 0;
	SPI_FLASH_CS_LOW();
  SPI_FLASH_ReadWrite(W25X_DeviceID);
  SPI_FLASH_ReadWrite(0);
  SPI_FLASH_ReadWrite(0);
  SPI_FLASH_ReadWrite(0);
	
	Temp = SPI_FLASH_ReadWrite(0);
	SPI_FLASH_CS_HIGH();
	
	return Temp;
}


uint32_t SPI_FLASH_ReadJedecDeviceID(void)
{
	uint32_t Temp = 0, Temp0 = 0, Temp1 = 0, Temp2 = 0;
	
	SPI_FLASH_CS_LOW();
	
	SPI_FLASH_ReadWrite(W25X_JedecDeviceID);
	
	Temp0 = SPI_FLASH_ReadWrite(0);
	Temp1 = SPI_FLASH_ReadWrite(0);
	Temp2 = SPI_FLASH_ReadWrite(0);
	
	Temp = (Temp0 << 16) | (Temp1 << 8) | Temp2;
	
	return Temp;
}
写使能&掉电&唤醒
void SPI_FLASH_WriteEnable(void)
{
  SPI_FLASH_CS_LOW();
  /* 发送写使能命令*/
  SPI_FLASH_ReadWrite(W25X_WriteEnable);
  SPI_FLASH_CS_HIGH();
}

void SPI_Flash_PowerDown(void)   
{ 
  SPI_FLASH_CS_LOW();
  /* 发送 掉电 命令 */
  SPI_FLASH_ReadWrite(W25X_PowerDown);
  SPI_FLASH_CS_HIGH();
}   

void SPI_Flash_WAKEUP(void)   
{
  SPI_FLASH_CS_LOW();
  /* 发上 上电 命令 */
  SPI_FLASH_ReadWrite(W25X_ReleasePowerDown);
  SPI_FLASH_CS_HIGH();
}  
Flash擦除函数
void SPI_FLASH_SectorErase(uint32_t SectorAddr)
{
  /* 发送FLASH写使能命令 */
  SPI_FLASH_WriteEnable();
  SPI_FLASH_WaitForWriteEnd();
  
  SPI_FLASH_CS_LOW();
  
	/* 发送扇区擦除指令*/
  SPI_FLASH_ReadWrite(W25X_SectorErase);
  /*发送擦除扇区地址的高位*/
  SPI_FLASH_ReadWrite((SectorAddr & 0xFF0000) >> 16);
  /* 发送擦除扇区地址的中位 */
  SPI_FLASH_ReadWrite((SectorAddr & 0xFF00) >> 8);
  /* 发送擦除扇区地址的低位 */
  SPI_FLASH_ReadWrite(SectorAddr & 0xFF);
  
  SPI_FLASH_CS_HIGH();
	
  /* 等待擦除完毕*/
  SPI_FLASH_WaitForWriteEnd();
}


void SPI_FLASH_BulkErase(void)
{
  /* 发送FLASH写使能命令 */
  SPI_FLASH_WriteEnable();

  SPI_FLASH_CS_LOW();
	
  /* 发送整块擦除指令*/
  SPI_FLASH_ReadWrite(W25X_ChipErase);
  
  SPI_FLASH_CS_HIGH();

  /* 等待擦除完毕*/
  SPI_FLASH_WaitForWriteEnd();
}
等待到FLASH内部数据写入完毕
void SPI_FLASH_WaitForWriteEnd(void)
{
  uint8_t FLASH_Status = 0;

  SPI_FLASH_CS_LOW();

  /* 发送 读状态寄存器 命令 */
  SPI_FLASH_ReadWrite(W25X_ReadStatusReg);

  int SPITimeout = 1000;
  /* 若FLASH忙碌,则等待 */
  do
  {
    /* 读取FLASH芯片的状态寄存器 */
    FLASH_Status = SPI_FLASH_ReadWrite(Dummy_Byte);	 

    {
      if((SPITimeout--) == 0) 
      {
        //SPI_TIMEOUT_UserCallback(4);
        return;
      }
    } 
  }
  while ((FLASH_Status & WIP_Flag) == SET); /* 正在写入标志 */

  SPI_FLASH_CS_HIGH();
}
按页写数据
void SPI_FLASH_PageWrite(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
{
  /* 发送FLASH写使能命令 */
  SPI_FLASH_WriteEnable();

  /* 选择FLASH: CS低电平 */
  SPI_FLASH_CS_LOW();
  /* 写页写指令*/
  SPI_FLASH_ReadWrite(W25X_PageProgram);
  /*发送写地址的高位*/
  SPI_FLASH_ReadWrite((WriteAddr & 0xFF0000) >> 16);
  /*发送写地址的中位*/
  SPI_FLASH_ReadWrite((WriteAddr & 0xFF00) >> 8);
  /*发送写地址的低位*/
  SPI_FLASH_ReadWrite(WriteAddr & 0xFF);

  if(NumByteToWrite > SPI_FLASH_PerWritePageSize)
  {
    NumByteToWrite = SPI_FLASH_PerWritePageSize;
		printf("SPI:SPI_FLASH_PageWrite too large!\r\n");
  }

  /* 写入数据*/
  while (NumByteToWrite--)
  {
    /* 发送当前要写入的字节数据 */
    SPI_FLASH_ReadWrite(*pBuffer);
    /* 指向下一字节数据 */
    pBuffer++;
  }

  /* 停止信号 FLASH: CS 高电平 */
  SPI_FLASH_CS_HIGH();

  /* 等待写入完毕*/
  SPI_FLASH_WaitForWriteEnd();
}
写数据
void SPI_FLASH_BufferWrite(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
{
  uint8_t 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;

      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);
      }
    }
  }
}
读取数据
void SPI_FLASH_BufferRead(uint8_t* pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead)
{
  SPI_FLASH_CS_LOW();

  /* 发送 读 指令 */
  SPI_FLASH_ReadWrite(W25X_ReadData);

  /* 发送 读 地址高位 */
  SPI_FLASH_ReadWrite((ReadAddr & 0xFF0000) >> 16);
  /* 发送 读 地址中位 */
  SPI_FLASH_ReadWrite((ReadAddr& 0xFF00) >> 8);
  /* 发送 读 地址低位 */
  SPI_FLASH_ReadWrite(ReadAddr & 0xFF);
  
	/* 读取数据 */
  while (NumByteToRead--)
  {
    /* 读取一个字节*/
    *pBuffer = SPI_FLASH_ReadWrite(Dummy_Byte);
    /* 指向下一个字节缓冲区 */
    pBuffer++;
  }

  SPI_FLASH_CS_HIGH();
}
  • 11
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是使用STM32F427 HAL进行SPI3W25Q32的基本步骤: 1. 配置SPI3 ```c hspi3.Instance = SPI3; hspi3.Init.Mode = SPI_MODE_MASTER; hspi3.Init.Direction = SPI_DIRECTION_2LINES; hspi3.Init.DataSize = SPI_DATASIZE_8BIT; hspi3.Init.CLKPolarity = SPI_POLARITY_LOW; hspi3.Init.CLKPhase = SPI_PHASE_1EDGE; hspi3.Init.NSS = SPI_NSS_SOFT; hspi3.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2; hspi3.Init.FirstBit = SPI_FIRSTBIT_MSB; hspi3.Init.TIMode = SPI_TIMODE_DISABLE; hspi3.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; hspi3.Init.CRCPolynomial = 10; HAL_SPI_Init(&hspi3); ``` 2. 配置GPIO引脚 ```c GPIO_InitStruct.Pin = GPIO_PIN_4; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; HAL_GPIO_Init(GPIOE, &GPIO_InitStruct); ``` 3. 取ID ```c uint8_t rxData[2]; uint8_t txData[2]; txData[0] = 0x9F; // 发送取ID命令 txData[1] = 0x00; HAL_GPIO_WritePin(GPIOE, GPIO_PIN_4, GPIO_PIN_RESET); // 使能W25Q32 HAL_SPI_TransmitReceive(&hspi3, txData, rxData, 2, 100); // 发送命令并接收数据 HAL_GPIO_WritePin(GPIOE, GPIO_PIN_4, GPIO_PIN_SET); // 禁用W25Q32 ``` 4. 取数据 ```c uint8_t rxData[256]; uint8_t txData[256]; txData[0] = 0x03; // 发送取数据命令 txData[1] = 0x00; txData[2] = 0x00; txData[3] = 0x00; HAL_GPIO_WritePin(GPIOE, GPIO_PIN_4, GPIO_PIN_RESET); // 使能W25Q32 HAL_SPI_Transmit(&hspi3, txData, 4, 1000); // 发送命令 HAL_SPI_Receive(&hspi3, rxData, 256, 1000); // 接收数据 HAL_GPIO_WritePin(GPIOE, GPIO_PIN_4, GPIO_PIN_SET); // 禁用W25Q32 ``` 5. 入数据 ```c uint8_t txData[256]; txData[0] = 0x06; // 发送使能命令 HAL_GPIO_WritePin(GPIOE, GPIO_PIN_4, GPIO_PIN_RESET); // 使能W25Q32 HAL_SPI_Transmit(&hspi3, txData, 1, 1000); // 发送命令 HAL_GPIO_WritePin(GPIOE, GPIO_PIN_4, GPIO_PIN_SET); // 禁用W25Q32 txData[0] = 0x02; // 发送数据命令 txData[1] = 0x00; txData[2] = 0x00; txData[3] = 0x00; for (int i = 0; i < 256; i++) { txData[4 + i] = i; // 入数据 } HAL_GPIO_WritePin(GPIOE, GPIO_PIN_4, GPIO_PIN_RESET); // 使能W25Q32 HAL_SPI_Transmit(&hspi3, txData, 260, 1000); // 发送命令和数据 HAL_GPIO_WritePin(GPIOE, GPIO_PIN_4, GPIO_PIN_SET); // 禁用W25Q32 ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值