STM32CubeMX配置SPI访问W25QXX

一、W25QXX芯片介绍

本次以W25Q32为主介绍,其他如W25Q64,W25Q16等只是在存储大小上有区别。

W25Q32有16384个可编程页,每页有256个字节;在页擦除时可以按照16页为一组擦除(即4KB扇区擦除)、128页为一组擦除(即32KB块擦除)、256页为一组擦除(即64KB块擦除)或者整个芯片擦除。

块、扇区、页的大小:

块(block)64KB
扇区(sector)4KB
页(page)256byte

W25Q32有64block = 64*16sector = 64*16*(4*1024/256)page = 16384page

二、状态寄存器

BUSY(忙位):BUSY位是一个只读位,位于状态寄存器中的S0,当器件在执行“页编程”、“扇区擦除”、“跨擦除”、“芯片擦除”、“写状态寄存器或擦除/程序安全寄存器”指令时,该位自动置为1,这是除了“读状态寄存器”指令,其他指令都忽略。当指令执行完毕后,该位自动置为0,表示芯片接收其他指令。

WEL位:写使能锁存(WEL)是状态寄存器(S1)中的一个只读位,在执行写使能指令后被设置为1。当设备禁止写时,WEL状态位清零。写禁用状态发生在上电或以下任何指令之后:写禁用、页程序、四页程序、扇区擦除、块擦除、芯片擦除、写状态寄存器、擦除安全寄存器和程序安全寄存器。

三、指令介绍

3.1 Manufacturer/Device ID

读取制造厂商/设备ID。不同的芯片制造厂商/设备ID是不同的,具体是多少要查看对应的手册。

指令最后得到的是MF7-MF0  ID7-ID0,所以得到的是EF15

代码编写过程:

1、先将CS拉低

2、发送8位指令

3、发送24位的地址

4、接收8位的制造商ID和8位的设备ID

5、CS拉高

uint16_t  readID()
{

  unsigned char command = W25X_ManufactDeviceID;
  W25QXX_CS_L;
  HAL_SPI_Transmit(&hspi1,&command,1,0xff);
  unsigned char data = 0x00;
  
  HAL_SPI_Transmit(&hspi1,&data,1,0xff);
  HAL_SPI_Transmit(&hspi1,&data,1,0xff);
  HAL_SPI_Transmit(&hspi1,&data,1,0xff);

  uint16_t ID = 0x00;
  uint8_t temp = 0xff;
  HAL_SPI_Receive(&hspi1,&temp,1,0xff);
  ID = temp;
 // printf("0x%2x",temp);
  ID = ID<<8;
  HAL_SPI_Receive(&hspi1,&temp,1,0xff);
  ID = ID|temp;
  //printf("0x%2x",temp);
  
  W25QXX_CS_H;
  return ID;
}

3.2 JEDEC ID

返回的正确结果是EF4016

//读取JEDEC ID =0xEF4016
uint32_t JEDEC_ID()
{
  unsigned char command = W25X_JedecDeviceID;
  
  W25QXX_CS_L;
  HAL_SPI_Transmit(&hspi1,&command,1,0xff);
 
  
  uint32_t JEDEC_ID = 0x00;
  uint8_t temp1 = 0xff;
  uint8_t temp2 = 0xff;
  uint8_t temp3 = 0xff;
  HAL_SPI_Receive(&hspi1,&temp1,1,0xff);
  HAL_SPI_Receive(&hspi1,&temp2,1,0xff);
  HAL_SPI_Receive(&hspi1,&temp3,1,0xff);
  
  JEDEC_ID = temp1<<16 | temp2<<8 | temp3;
  
  W25QXX_CS_H;
  
  return JEDEC_ID;
  
}

3.3 Read Status Register-1

返回S7-S0

//查看寄存器1,查看最低位是否为0,0:表示空闲,1:表示在将进行写,在写之前要判断是否空闲
void readStatueRegister1()
{
    W25QXX_CS_L;
    uint8_t command = W25X_ReadStatusReg1;
    uint8_t statue = 0xff;
    HAL_SPI_Transmit(&hspi1,&command,1,0xff);
    do{
       
        HAL_SPI_Receive(&hspi1,&statue,1,0xff);
        //printf("0x%02X\r\n",statue);
        HAL_Delay(1000);
    }while(statue & 0x01);
    
    W25QXX_CS_H;
}

3.4 Write Enable

void WriteEnable()
{
  uint8_t command = W25X_WriteEnable;
  
  W25QXX_CS_L;
  HAL_SPI_Transmit(&hspi1,&command,1,0xff);
  W25QXX_CS_H;
}

3.5 Sector Erase (4KB)

void EraseSector(uint32_t SectorAddr)
{
  uint8_t command = W25X_SectorErase;
   //等待写结束
  readStatueRegister1();
  
  //写使能
  WriteEnable();
  
  W25QXX_CS_L;
  HAL_SPI_Transmit(&hspi1,&command,1,0xff);
 
  //分三次发送地址
  uint8_t addr = 0x00;
  
  addr = (  SectorAddr& 0xFF0000) >>16;
  HAL_SPI_Transmit(&hspi1,&addr,1,0xff);
  addr = ( SectorAddr & 0xFF00) >>8;
  HAL_SPI_Transmit(&hspi1,&addr,1,0xff);
  addr = ( SectorAddr& 0xFF);
  HAL_SPI_Transmit(&hspi1,&addr,1,0xff);
  
  W25QXX_CS_H;
  readStatueRegister1();
  

}

3.6 Page Program

在页面成开始之前。需要进行如下操作:

1、擦除页所在的扇区。因为在写入时只能将内存中的1变为0,而不能将0变为1,而擦除指令则可以将0置为1。

                所以页编程:1---置为0

                扇区擦除:0----置为1

2、等待写结束。如果上次的写操作还没完成,则不能进行本次页编程。

3、写使能。

4、执行页编程指令。

//页编程
//参数:数组,地址,字节
void PagePrograe(uint8_t* pBuffer,uint32_t SectorAddr, uint32_t WriteAddr, uint16_t NumByteToWrite)
{
  printf("从%06Xh地址开始写入%d个数据\r\n",WriteAddr,NumByteToWrite);
  uint8_t command = W25X_PageProgram;
  
  //先擦除扇区
  EraseSector(SectorAddr);
    
  //等待写结束
  readStatueRegister1();
  
  //写使能
  WriteEnable();
  
  W25QXX_CS_L;

  HAL_SPI_Transmit(&hspi1,&command,1,0xff);
  
  //分三次发送地址
  uint8_t addr = 0x00;
  
  addr = (WriteAddr & 0xFF0000) >>16;
  HAL_SPI_Transmit(&hspi1,&addr,1,0xff);
  addr = (WriteAddr & 0xFF00) >>8;
  HAL_SPI_Transmit(&hspi1,&addr,1,0xff);
  addr = (WriteAddr & 0xFF);
  HAL_SPI_Transmit(&hspi1,&addr,1,0xff);
  
  //写
  while(NumByteToWrite--)
  {
    HAL_SPI_Transmit(&hspi1,pBuffer,1,0xff);
    printf("0X%02X ",*pBuffer);
    pBuffer++;
  }
  
  W25QXX_CS_H;

}

3.7 Read Data

void ReadBuffer(uint8_t* pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead)
{
  
  printf("从%06Xh地址开始读%d个数据\r\n",ReadAddr,NumByteToRead);
  uint8_t command= W25X_ReadData;
  
  //等待空闲
   readStatueRegister1();
 
  W25QXX_CS_L;
  HAL_SPI_Transmit(&hspi1,&command,1,0xff);
  
  //分三次发送地址
  uint8_t addr = 0x00;
  
  addr = (ReadAddr & 0xFF0000) >>16;
 // printf("%0x ",addr);
  HAL_SPI_Transmit(&hspi1,&addr,1,0xff);
  addr = (ReadAddr & 0xFF00) >>8;
   //printf("%0x ",addr);
  HAL_SPI_Transmit(&hspi1,&addr,1,0xff);
  addr = (ReadAddr & 0xFF);
   //printf("%0x ",addr);
  HAL_SPI_Transmit(&hspi1,&addr,1,0xff);
  
  //读
  while(NumByteToRead--)
  {
    HAL_SPI_Receive(&hspi1,pBuffer,1,0xff);
     // printf("0X%02X ",*pBuffer);
    pBuffer++;
  }
//  for(int i =0;i < NumByteToRead;i++)
//    printf("0X%02X ",*pBuffer);
  
  
  W25QXX_CS_H;
  
}

四、配置

芯片:STM32F103CBT6

这里我用了其他引脚作为片选信号。

五、添加W25QXX相关文件

w25qxx.c

#include "w25qxx.h"
#include "w25qxx_config.h"
#include "main.h"
#include "stdio.h"



#define W25QXX_CS_H 			HAL_GPIO_WritePin(_W25QXX_CS_GPIO, _W25QXX_CS_PIN, GPIO_PIN_SET);
#define W25QXX_CS_L 			HAL_GPIO_WritePin(_W25QXX_CS_GPIO, _W25QXX_CS_PIN, GPIO_PIN_RESET);

w25qxx_t	w25qxx;

#if (_W25QXX_USE_FREERTOS == 1)
#define	W25qxx_Delay(delay)		osDelay(delay)
#include "cmsis_os.h"
#else
#define	W25qxx_Delay(delay) for(uint8_t i=0;i<255;i++);;
#endif

//##############################################################################################################
//EF15
uint16_t  readID()
{
/*
1、片选拉低
2、发送指令
3、发送三个8位数据
4、接收数据8位
5、接收数据8位
6、片选拉高
*/
  unsigned char command = W25X_ManufactDeviceID;
  W25QXX_CS_L;
  HAL_SPI_Transmit(&hspi1,&command,1,0xff);
  unsigned char data = 0x00;
  
  HAL_SPI_Transmit(&hspi1,&data,1,0xff);
  HAL_SPI_Transmit(&hspi1,&data,1,0xff);
  HAL_SPI_Transmit(&hspi1,&data,1,0xff);

  uint16_t ID = 0x00;
  uint8_t temp = 0xff;
  HAL_SPI_Receive(&hspi1,&temp,1,0xff);
  ID = temp;
 // printf("0x%2x",temp);
  ID = ID<<8;
  HAL_SPI_Receive(&hspi1,&temp,1,0xff);
  ID = ID|temp;
  //printf("0x%2x",temp);
  
  W25QXX_CS_H;
  return ID;
}
//##############################################################################################################
//读取JEDEC ID =0xEF4016
uint32_t JEDEC_ID()
{
  unsigned char command = W25X_JedecDeviceID;
  
  W25QXX_CS_L;
  HAL_SPI_Transmit(&hspi1,&command,1,0xff);
 
  
  uint32_t JEDEC_ID = 0x00;
  uint8_t temp1 = 0xff;
  uint8_t temp2 = 0xff;
  uint8_t temp3 = 0xff;
  HAL_SPI_Receive(&hspi1,&temp1,1,0xff);
  HAL_SPI_Receive(&hspi1,&temp2,1,0xff);
  HAL_SPI_Receive(&hspi1,&temp3,1,0xff);
  
  JEDEC_ID = temp1<<16 | temp2<<8 | temp3;
  
  W25QXX_CS_H;
  
  return JEDEC_ID;
  
}

//##############################################################################################################

//查看寄存器1,查看最低位是否为0,0:表示空闲,1:表示在将进行写,在写之前要判断是否空闲
void readStatueRegister1()
{
    W25QXX_CS_L;
    uint8_t command = W25X_ReadStatusReg1;
    uint8_t statue = 0xff;
    HAL_SPI_Transmit(&hspi1,&command,1,0xff);
    do{
       
        HAL_SPI_Receive(&hspi1,&statue,1,0xff);
        //printf("0x%02X\r\n",statue);
        HAL_Delay(1000);
    }while(statue & 0x01);
    
    W25QXX_CS_H;
}

//###########################################################################################################
//写使能

void WriteEnable()
{
  uint8_t command = W25X_WriteEnable;
  
  W25QXX_CS_L;
  HAL_SPI_Transmit(&hspi1,&command,1,0xff);
  W25QXX_CS_H;
}
//###########################################################################################################

void EraseSector(uint32_t SectorAddr)
{
  uint8_t command = W25X_SectorErase;
   //等待写结束
  readStatueRegister1();
  
  //写使能
  WriteEnable();
  
  W25QXX_CS_L;
  HAL_SPI_Transmit(&hspi1,&command,1,0xff);
 
  //分三次发送地址
  uint8_t addr = 0x00;
  
  addr = (  SectorAddr& 0xFF0000) >>16;
  HAL_SPI_Transmit(&hspi1,&addr,1,0xff);
  addr = ( SectorAddr & 0xFF00) >>8;
  HAL_SPI_Transmit(&hspi1,&addr,1,0xff);
  addr = ( SectorAddr& 0xFF);
  HAL_SPI_Transmit(&hspi1,&addr,1,0xff);
  
  W25QXX_CS_H;
  readStatueRegister1();
  

}
//###########################################################################################################
//页编程
//参数:数组,地址,字节
void PagePrograe(uint8_t* pBuffer,uint32_t SectorAddr, uint32_t WriteAddr, uint16_t NumByteToWrite)
{
  printf("从%06Xh地址开始写入%d个数据\r\n",WriteAddr,NumByteToWrite);
  uint8_t command = W25X_PageProgram;
  
  //先擦除扇区
  EraseSector(SectorAddr);
    
  //等待写结束
  readStatueRegister1();
  
  //写使能
  WriteEnable();
  
  W25QXX_CS_L;

  HAL_SPI_Transmit(&hspi1,&command,1,0xff);
  
  //分三次发送地址
  uint8_t addr = 0x00;
  
  addr = (WriteAddr & 0xFF0000) >>16;
  HAL_SPI_Transmit(&hspi1,&addr,1,0xff);
  addr = (WriteAddr & 0xFF00) >>8;
  HAL_SPI_Transmit(&hspi1,&addr,1,0xff);
  addr = (WriteAddr & 0xFF);
  HAL_SPI_Transmit(&hspi1,&addr,1,0xff);
  
  //写
  while(NumByteToWrite--)
  {
    HAL_SPI_Transmit(&hspi1,pBuffer,1,0xff);
    printf("0X%02X ",*pBuffer);
    pBuffer++;
  }
  
  W25QXX_CS_H;

}
//###########################################################################################################
void ReadBuffer(uint8_t* pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead)
{
  
  printf("从%06Xh地址开始读%d个数据\r\n",ReadAddr,NumByteToRead);
  uint8_t command= W25X_ReadData;
  
  //等待空闲
   readStatueRegister1();
 
  W25QXX_CS_L;
  HAL_SPI_Transmit(&hspi1,&command,1,0xff);
  
  //分三次发送地址
  uint8_t addr = 0x00;
  
  addr = (ReadAddr & 0xFF0000) >>16;
 // printf("%0x ",addr);
  HAL_SPI_Transmit(&hspi1,&addr,1,0xff);
  addr = (ReadAddr & 0xFF00) >>8;
   //printf("%0x ",addr);
  HAL_SPI_Transmit(&hspi1,&addr,1,0xff);
  addr = (ReadAddr & 0xFF);
   //printf("%0x ",addr);
  HAL_SPI_Transmit(&hspi1,&addr,1,0xff);
  
  //读
  while(NumByteToRead--)
  {
    HAL_SPI_Receive(&hspi1,pBuffer,1,0xff);
     // printf("0X%02X ",*pBuffer);
    pBuffer++;
  }
//  for(int i =0;i < NumByteToRead;i++)
//    printf("0X%02X ",*pBuffer);
  
  
  W25QXX_CS_H;
  
}

w25qxx_config.h

这里根据个人引脚定义需要修改_W25QXX_CS_GPIO 和_W25QXX_CS_PIN

#ifndef __W25QXX_CONFIG_H__
#define __W25QXX_CONFIG_H__

#define _W25QXX_SPI                   hspi1
#define _W25QXX_CS_GPIO               GPIOB
#define _W25QXX_CS_PIN                GPIO_PIN_0
#define _W25QXX_USE_FREERTOS          0
#define _W25QXX_DEBUG                 1

#endif

w25qxx.h

#ifndef __W25QXX_H__
#define __W25QXX_H__

#include <stdbool.h>
#include "spi.h"

#define SPI_FLASH_PageSize                  	256
#define SPI_FLASH_PerWritePageSize          	256

#define W25X_WriteEnable		              	0x06 
#define W25X_WriteDisable		              	0x04 
#define W25X_ReadStatusReg1		              	0x05 
#define W25X_WriteStatusReg1	              	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 W25QXX_DUMMY_BYTE					  	0xFF

#pragma pack(1)

typedef enum
{
	W25Q10=1,
	W25Q20,
	W25Q40,
	W25Q80,
	W25Q16,
	W25Q32,
	W25Q64,
	W25Q128,
	W25Q256,
	W25Q512,
	
}W25QXX_ID_t;

typedef struct
{
	W25QXX_ID_t	ID;
	uint8_t		UniqID[8];
	uint16_t	PageSize;
	uint32_t	PageCount;
	uint32_t	SectorSize;
	uint32_t	SectorCount;
	uint32_t	BlockSize;
	uint32_t	BlockCount;
	uint32_t	CapacityInKiloByte;
	uint8_t		StatusRegister1;
	uint8_t		StatusRegister2;
	uint8_t		StatusRegister3;	
	uint8_t		Lock;
	
}w25qxx_t;

#pragma pack()

extern w25qxx_t	w25qxx;

/************************************************用户API*******************************************/

uint16_t  readID();
uint32_t JEDEC_ID();
void readStatueRegister1();
void PagePrograe(uint8_t* pBuffer,uint32_t SectorAddr, uint32_t WriteAddr, uint16_t NumByteToWrite);
void ReadBuffer(uint8_t* pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead);
void EraseSector(uint32_t SectorAddr);
#endif

main.c中添加

 /* USER CODE BEGIN 2 */
  printf("串口测试成功\r\n");
  
  uint16_t ID = 0x00;
  ID = readID();
  printf("---------------------------------------\r\n");
  printf("Manufacturer/Device ID =0X%X\r\n",ID);

  printf("---------------------------------------\r\n");
  uint32_t JEDECID = JEDEC_ID();
  printf("JEDEC ID=0X%6X\r\n",JEDECID);
  
  
  printf("---------------------------------------\r\n");  
  unsigned char buff[256]={0};
  for(int i =0;i<256;i++)
  {
    buff[i]=i;
  }
  //write
  PagePrograe(buff,0x00, 0x100, 256);
  HAL_Delay(2000);
  
  printf("---------------------------------------\r\n");
//  //read
    unsigned char read[256]={0};
  ReadBuffer(read, 0x100, 256);
  for(int i=0;i<256;i++)
  {
    printf("0X%02X ", read[i]);
  }
  

六、测试

  • 22
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值