一、芯片管脚和MCU管脚、时钟



时钟配置:

二、生成QSPI的代码

代码里面挺多备注了,不懂得再去刷一刷正点原子的视频吧,结合手册来看,来源都是正点原子的代码。
头文件qspi.h

#ifndef __QSPI_H
#define __QSPI_H
#include "stm32f7xx_hal.h"
//QSPI功能配置
u8 QSPI_Init(void);
//QSPI 底层驱动 引脚配置 时钟使能
void HAL_QSPI_MspInit(QSPI_HandleTypeDef* hqspi);
//QSPI发送命令
void QSPI_Send_CMD(u32 Instruction,u32 Address,u32 DummyCycles,u32 InstructionMode ,
u32 AddressMode , u32 AddressSize ,u32 DataMode);
//发送数据给W25Q256
u8 QSPI_Transmit(u8*buf,u32 datalen);
//接受W25Q256数据到数组中
u8 QSPI_Receive(u8*buf,u32 datalen);
//关闭QSPI通讯
void HAL_QSPI_MspDeInit(QSPI_HandleTypeDef* hqspi);
#endif
#include "qspi.h"
QSPI_HandleTypeDef hqspi;//QSPI句柄/*** @brief QSPI功能配置
QSPI_BK2_NCS    PC11
QSPI_BK2_IO0    PE7
QSPI_BK2_IO1    PE8
QSPI_BK2_IO2    PE9
QSPI_BK2_IO3    PE10
QSPI_BK2_CLK    PB2* @param None* @retval None*/
u8 QSPI_Init(void)
{hqspi.Instance = QUADSPI;//QSPI//时钟预分频系数,在QUADSPI_CR控制寄存器的PRESCALER位[31:24]//FCLK=FAHB/(ClockPrescaler+1) QSPI挂载在AHB总线上//范围0~255 QSPI分频比,W25Q256的最大频率为104M,这里的AHB频率为216Mhqspi.Init.ClockPrescaler = 2;//QSPI频率=216/(2+1)=72Mhqspi.Init.FifoThreshold = 4;//FIFO阈值 CR中FTHRES[4:0] FIFO中写入4+1个字节后,FTF标志置1hqspi.Init.SampleShifting = QSPI_SAMPLE_SHIFTING_HALFCYCLE;   //采样移位CR4 1=半个周期之后再采样  (DDR模式下,必须设置为0)//POSITION_VAL (0X2000000)获取VAL值的最高位位数//Flash字节数=2^(FlashSize+1)//POSITION_VAL(0X2000000)=26  2^26=67108864 字节=64MB   计算结果错误,难道是POSITION_VAL(0X2000000)=25???hqspi.Init.FlashSize = POSITION_VAL(0X2000000)-1; //DCR[20:16] Flash大小32MB=256Mb  //片选高电平时间为四个时钟(13.8*4=55.2ns) 手册里面的tSHSL参数//发送Flash命令之间必须保持高电平的最少CLK周期数hqspi.Init.ChipSelectHighTime = QSPI_CS_HIGH_TIME_4_CYCLE;//片选高电平时间 DCR 10:8
//模式设置 DCR0 //nCS为高电平(片选释放)时,CLK必须保持低电平,这称为模式0 QSPI_CLOCK_MODE_3//nCS为高电平(片选释放)时,CLK必须保持高电平,这称为模式3 QSPI_CLOCK_MODE_0hqspi.Init.ClockMode = QSPI_CLOCK_MODE_0;//模式0 //选择FLASH CR7   FSEL//0选择FLASH1 1选择FLASH2  DFM=1时忽略该位,因为DFM时双闪存模式hqspi.Init.FlashID = QSPI_FLASH_ID_2;//第一片Flash//双闪存模式 CR6 DFM   0禁止 1使能hqspi.Init.DualFlash = QSPI_DUALFLASH_DISABLE;//禁止双闪存模式if (HAL_QSPI_Init(&hqspi) != HAL_OK)//QSI初始化失败{return 1;//初始化失败}else {HAL_QSPI_MspInit(&hqspi);return 0;//初始化成功}
}/**
* @brief QSPI 底层驱动 引脚配置 时钟使能
*此函数会被QSPI_Init(void)函数调用
* @param hqspi: QSPI handle pointer
* @retval None
*/
void HAL_QSPI_MspInit(QSPI_HandleTypeDef* hqspi)
{GPIO_InitTypeDef GPIO_InitStruct = {0};if(hqspi->Instance==QUADSPI){/* USER CODE BEGIN QUADSPI_MspInit 0 *//* USER CODE END QUADSPI_MspInit 0 *//* Peripheral clock enable */__HAL_RCC_QSPI_CLK_ENABLE();//使能时钟__HAL_RCC_GPIOB_CLK_ENABLE();__HAL_RCC_GPIOE_CLK_ENABLE();__HAL_RCC_GPIOC_CLK_ENABLE();/**QUADSPI GPIO ConfigurationPB2     ------> QUADSPI_CLKPE7     ------> QUADSPI_BK2_IO0PE8     ------> QUADSPI_BK2_IO1PE9     ------> QUADSPI_BK2_IO2PE10     ------> QUADSPI_BK2_IO3PC11     ------> QUADSPI_BK2_NCS*///PB2 时钟管脚GPIO_InitStruct.Pin = GPIO_PIN_2;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;GPIO_InitStruct.Alternate = GPIO_AF9_QUADSPI;HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);//PE7、8、9、10  IO0、1、2、3//数据管脚GPIO_InitStruct.Pin = GPIO_PIN_7|GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;GPIO_InitStruct.Alternate = GPIO_AF10_QUADSPI;HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);//片选管脚GPIO_InitStruct.Pin = GPIO_PIN_11;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;GPIO_InitStruct.Alternate = GPIO_AF9_QUADSPI;HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);/* USER CODE BEGIN QUADSPI_MspInit 1 *//* USER CODE END QUADSPI_MspInit 1 */}}//QSPI发送命令
void QSPI_Send_CMD(u32 Instruction,u32 Address,u32 DummyCycles,u32 InstructionMode ,u32 AddressMode , u32 AddressSize ,u32 DataMode)
{QSPI_CommandTypeDef CmdHandler;CmdHandler.Instruction=Instruction;//指令CmdHandler.Address=Address;//地址CmdHandler.DummyCycles=DummyCycles;//设置空指令周期数CmdHandler.InstructionMode=InstructionMode;//指令模式CmdHandler.AddressMode=AddressMode;//地址模式CmdHandler.AddressSize=AddressSize;//地址长度CmdHandler.DataMode=DataMode;//数据模式CmdHandler.SIOOMode=QSPI_SIOO_INST_EVERY_CMD;//每次都发指令CmdHandler.AlternateByteMode=QSPI_ALTERNATE_BYTES_NONE;//无交替字节CmdHandler.DdrMode=QSPI_DDR_MODE_DISABLE;//关闭DDR模式CmdHandler.DdrHoldHalfCycle=QSPI_DDR_HHC_ANALOG_DELAY;// QSPI_HandleTypeDef  QSPI_CommandTypeDef   TimeoutHAL_QSPI_Command(&hqspi,&CmdHandler,5000);
}//QSPI发送指定长度的数据
//buf:发送数据缓冲区首地址
//datalen:数据长度
//返回值0:正常
u8 QSPI_Transmit(u8*buf,u32 datalen)
{//DLR数据长度寄存器hqspi.Instance->DLR=datalen-1; //配置数据长度if(HAL_QSPI_Transmit(&hqspi,buf,5000)==HAL_OK){return 0;//发送数据,发送buf数组中的数据。}else{return 1;}
}//QSPI接收指定长度的数据
//buf:接受数据缓冲区首地址
//datalen:数据长度
//返回值0:正常
u8 QSPI_Receive(u8*buf,u32 datalen)
{//DLR数据长度寄存器hqspi.Instance->DLR=datalen-1; //配置数据长度if(HAL_QSPI_Receive(&hqspi,buf,5000)==HAL_OK){return 0;//接收到数据,存到了buf数组中了。}else{return 1;}
}/**
* @brief QSPI MSP De-Initialization
* This function freeze the hardware resources used in this example
* @param hqspi: QSPI handle pointer
* @retval None
*/
void HAL_QSPI_MspDeInit(QSPI_HandleTypeDef* hqspi)
{if(hqspi->Instance==QUADSPI){/* USER CODE BEGIN QUADSPI_MspDeInit 0 *//* USER CODE END QUADSPI_MspDeInit 0 *//* Peripheral clock disable */__HAL_RCC_QSPI_CLK_DISABLE();/**QUADSPI GPIO ConfigurationPB2     ------> QUADSPI_CLKPE7     ------> QUADSPI_BK2_IO0PE8     ------> QUADSPI_BK2_IO1PE9     ------> QUADSPI_BK2_IO2PE10     ------> QUADSPI_BK2_IO3PC11     ------> QUADSPI_BK2_NCS*/HAL_GPIO_DeInit(GPIOB, GPIO_PIN_2);HAL_GPIO_DeInit(GPIOE, GPIO_PIN_7|GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10);HAL_GPIO_DeInit(GPIOC, GPIO_PIN_11);/* USER CODE BEGIN QUADSPI_MspDeInit 1 *//* USER CODE END QUADSPI_MspDeInit 1 */}}/* USER CODE BEGIN 1 *//* USER CODE END 1 */

三、W25Q256代码

都是正点原子的代码,不想重新慢慢写了,也不会写,看懂会用先,以后在慢慢自己写。
头文件w25q256.h

#ifndef __W25Q256_H
#define __W25Q256_H
#include "stm32f7xx_hal.h"//W25X系列/Q系列芯片列表
//W25Q80  ID  0XEF13
//W25Q16  ID  0XEF14
//W25Q32  ID  0XEF15
//W25Q64  ID  0XEF16
//W25Q128 ID  0XEF17
//W25Q256 ID  0XEF18
#define W25Q80  0XEF13
#define W25Q16  0XEF14
#define W25Q32  0XEF15
#define W25Q64  0XEF16
#define W25Q128 0XEF17
#define W25Q256 0XEF18//3个读状态寄存器指令
#define W25X_ReadStatusReg1     0x05
#define W25X_ReadStatusReg2     0x35
#define W25X_ReadStatusReg3     0x15
//3个写状态寄存器指令
#define W25X_WriteStatusReg1    0x01
#define W25X_WriteStatusReg2    0x31
#define W25X_WriteStatusReg3    0x11
//写使能指令
#define W25X_WriteEnable        0x06
#define W25X_WriteDisable       0x04
//进入QSPI模式和退出QSPI模式指令
#define W25X_EnterQPIMode       0x38
#define W25X_ExitQPIMode        0xFF
//FLASH芯片ID
#define W25X_ManufactDeviceID   0x90
//四字节地址模式
#define W25X_Enable4ByteAddr    0xB7
#define W25X_Exit4ByteAddr      0xE9
//读参数指令
#define W25X_SetReadParam       0xC0
//快速读取数据指令
#define W25X_FastReadData       0x0B
#define W25X_FastReadDual       0x3B
//芯片擦除指令
#define W25X_PageProgram        0x02
#define W25X_BlockErase         0xD8
#define W25X_SectorErase        0x20
#define W25X_ChipErase          0xC7 void W25QXX_Init(void);//初始化W25QXX
void W25QXX_Qspi_Enable(void);//使能QSPI模式
void W25QXX_Qspi_Disable(void);//关闭QSPI模式
u8 W25QXX_ReadSR(u8 regno); //读取状态寄存器
void W25QXX_WriteSR(u8 regno,u8 sr);//写状态寄存器
void W25QXX_Write_Enable(void);
void W25QXX_Write_Disable(void);
u16 W25QXX_ReadID(void);//读取FLASH ID
void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead) ;
void W25QXX_Write_Page(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);
void W25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite) ;
void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)  ;
void W25QXX_Erase_Chip(void)  ;
void W25QXX_Erase_Sector(u32 Dst_Addr) ;
void W25QXX_Wait_Busy(void);
#endif

w25q256.c

#include "w25q256.h"
#include "qspi.h"u16 W25QXX_TYPE=W25Q256;    //默认是W25Q256
u8 W25QXX_QPI_MODE=0;      //QSPI模式标志:0,SPI模式;1,QPI模式.//初始化SPI FLASH的IO口
void W25QXX_Init(void)
{QSPI_Init();//初始化QSPIW25QXX_Qspi_Enable();//使能QSPI模式W25QXX_TYPE=W25QXX_ReadID();//读取FLASH ID.if(W25QXX_TYPE==W25Q256)        //SPI FLASH为W25Q256{temp=W25QXX_ReadSR(3);      //读取状态寄存器3,判断地址模式if((temp&0X01)==0)//SR3 第一位 ADS //如果不是4字节地址模式,则进入4字节地址模式{W25QXX_Write_Enable(); //写使能//QPI,使能4字节地址指令,地址为0,无数据_8位地址_无地址_4线传输指令,无空周期,0个字节数据 QSPI_Send_CMD(W25X_Enable4ByteAddr,0,0,QSPI_INSTRUCTION_4_LINES,QSPI_ADDRESS_NONE,QSPI_ADDRESS_8_BITS,QSPI_DATA_NONE);}W25QXX_Write_Enable();     //写使能//QPI,设置读参数指令,地址为0,4线传数据_8位地址_无地址_4线传输指令,无空周期,1个字节数据QSPI_Send_CMD(W25X_SetReadParam,0,0,QSPI_INSTRUCTION_4_LINES,QSPI_ADDRESS_NONE,QSPI_ADDRESS_8_BITS,QSPI_DATA_4_LINES);         //设置P4&P5=11,8个dummy clocks,104Mtemp=3<<4;//110000(Bin) QSPI_Transmit(&temp,1);     //发送1个字节    }
}//W25QXX进入QSPI模式
void W25QXX_Qspi_Enable(void)
{u8 stareg2=0;stareg2=W25QXX_ReadSR(2);//先读出状态寄存器2的原始值if((stareg2&0x02)==0)//取出来的状态寄存器2的第二位QE,为0未使能。{//没有使能就先写使能W25QXX_Write_Enable();       //写使能 stareg2|=1<<1;//使能QE位W25QXX_WriteSR(2,stareg2);}//使能完成//单线传输指令,没有数据//写command指令,地址为0,无空周期,单线传输指令,无地址,8位地址,无数据,0个字节数据QSPI_Send_CMD(W25X_EnterQPIMode,0,0,QSPI_INSTRUCTION_1_LINES,QSPI_ADDRESS_NONE,QSPI_ADDRESS_8_BITS,QSPI_DATA_NONE);W25QXX_QPI_MODE=1;//标记QSPI模式;1,QPI模式.
}//W25QXX退出QSPI模式
void W25QXX_Qspi_Disable(void)
{ QSPI_Send_CMD(W25X_ExitQPIMode,0,0,QSPI_INSTRUCTION_4_LINES,QSPI_ADDRESS_NONE,QSPI_ADDRESS_8_BITS,QSPI_DATA_NONE);//写command指令,地址为0,无数据_8位地址_无地址_4线传输指令,无空周期,0个字节数据W25QXX_QPI_MODE=0;                //标记SPI模式
}//读取W25QXX的状态寄存器,W25QXX一共有3个状态寄存器//状态寄存器1:
//SPR   RV  TB BP2 BP1 BP0 WEL BUSY
//BIT7  6   5   4   3   2   1   0
//SPR:默认0,状态寄存器保护位,配合WP使用
//TB,BP2,BP1,BP0:FLASH区域写保护设置
//WEL:写使能锁定
//BUSY:忙标记位(1,忙;0,空闲)
//默认:0x00//状态寄存器2:
//SUS   CMP LB3 LB2 LB1 (R) QE  SRP1
//BIT7  6   5   4   3   2   1   0//状态寄存器3:
//HOLD/RST  DRV1 DRV0 (R) (R) WPS ADP ADS
//BIT7      6    5    4   3   2   1   0//regno:状态寄存器号,范:1~3
//返回值:状态寄存器值u8 W25QXX_ReadSR(u8 regno)
{u8 byte=0;//存接收到的状态寄存器值u8 command=0;//存指令switch(regno){case 1:command=W25X_ReadStatusReg1; //读状态寄存器1指令break;case 2:command=W25X_ReadStatusReg2; //读状态寄存器2指令break;case 3:command=W25X_ReadStatusReg3; //读状态寄存器3指令break;default:command=W25X_ReadStatusReg1;    break;}if(W25QXX_QPI_MODE)//如果为QSPI模式 4线传输数据{//传一个字节数据,4根线一起传。//QPI,写command指令,地址为0,无空周期,4线传输指令,无地址,8位地址,4线传数据QSPI_Send_CMD(command,0,0,QSPI_INSTRUCTION_4_LINES,QSPI_ADDRESS_NONE,QSPI_ADDRESS_8_BITS,QSPI_DATA_4_LINES);}else{    //传一个字节数据,单线传输//SPI,写command指令,地址为0,无空周期,1线传输指令,无地址,8位地址,1线传数据QSPI_Send_CMD(command,0,0,QSPI_INSTRUCTION_1_LINE,QSPI_ADDRESS_NONE,QSPI_ADDRESS_8_BITS,QSPI_DATA_1_LINE);}QSPI_Receive(&byte,1);//读取到一字节的状态寄存器数据存到byte变量中return byte;//返回状态寄存器的值
}//写W25QXX状态寄存器
//regno:状态寄存器1~3
//sr:写入的值
void W25QXX_WriteSR(u8 regno,u8 sr)
{u8 command=0;switch(regno){case 1:command=W25X_WriteStatusReg1; //写状态寄存器1指令break;case 2:command=W25X_WriteStatusReg2; //读状态寄存器2指令break;case 3:command=W25X_WriteStatusReg3; //读状态寄存器3指令break;default:command=W25X_WriteStatusReg1;    break;}if(W25QXX_QPI_MODE)//如果为QSPI模式 4线传输数据{//QPI,写command指令,地址为0,无空周期,4线传输指令,无线传输地址,8位地址(一个字节数据),4线传数据 QSPI_Send_CMD(command,0,0,QSPI_INSTRUCTION_4_LINES,QSPI_ADDRESS_NONE,QSPI_ADDRESS_8_BITS,QSPI_DATA_4_LINES);}else{   //SPI,写command指令,地址为0,单线传数据_8位地址_无地址_单线传输指令,无空周期,1个字节数据QSPI_Send_CMD(command,0,0,QSPI_INSTRUCTION_1_LINE,QSPI_ADDRESS_NONE,QSPI_ADDRESS_8_BITS,QSPI_DATA_1_LINE);}QSPI_Transmit(&sr,1);//sr值写入到状态寄存器数据中
}//W25QXX写使能
//将状态寄存器1的WEL置位
void W25QXX_Write_Enable(void)
{if(W25QXX_QPI_MODE)//如果为QSPI模式 4线传输指令{//QPI,写command指令,地址为0,无空周期,4线传输指令,无线传输地址,8位地址,无传数据QSPI_Send_CMD(W25X_WriteEnable,0,0,QSPI_INSTRUCTION_4_LINES,QSPI_ADDRESS_NONE,QSPI_ADDRESS_8_BITS,QSPI_DATA_NONE);}else{ //SPI,写command指令,地址为0,无空周期,单线传输指令,无线传输地址,8位地址,无传数据QSPI_Send_CMD(command,0,0,QSPI_INSTRUCTION_1_LINE,QSPI_ADDRESS_NONE,QSPI_ADDRESS_8_BITS,QSPI_DATA_NONE);}
}//W25QXX写禁止
//将WEL清零
void W25QXX_Write_Disable(void)
{  if(W25QXX_QPI_MODE)QSPI_Send_CMD(W25X_WriteDisable,0,0,QSPI_INSTRUCTION_4_LINES,QSPI_ADDRESS_NONE,QSPI_ADDRESS_8_BITS,QSPI_DATA_NONE);//QPI,写禁止指令,地址为0,无数据_8位地址_无地址_4线传输指令,无空周期,0个字节数据else QSPI_Send_CMD(W25X_WriteDisable,0,0,QSPI_INSTRUCTION_1_LINE,QSPI_ADDRESS_NONE,QSPI_ADDRESS_8_BITS,QSPI_DATA_NONE);                //SPI,写禁止指令,地址为0,无数据_8位地址_无地址_单线传输指令,无空周期,0个字节数据
} //返回值如下:
//0XEF13,表示芯片型号为W25Q80
//0XEF14,表示芯片型号为W25Q16
//0XEF15,表示芯片型号为W25Q32
//0XEF16,表示芯片型号为W25Q64
//0XEF17,表示芯片型号为W25Q128
//0XEF18,表示芯片型号为W25Q256
u16 W25QXX_ReadID(void)
{u8 temp[2];u16 id=0;if(W25QXX_QPI_MODE){//不是很懂这个参数设置????//QPI,写command指令,地址为0,无空周期,4线传输指令,无地址,16位地址(2个字节),4线传数据//QSPI_Send_CMD(W25X_ManufactDeviceID,0,0,QSPI_INSTRUCTION_4_LINE,QSPI_ADDRESS_NONE,QSPI_ADDRESS_16_BITS,QSPI_DATA_4_LINES);//QPI,写command指令,地址为0,无空周期,4线传输指令,4线传输地址,24位地址,4线传数据QSPI_Send_CMD(W25X_ManufactDeviceID,0,0,QSPI_INSTRUCTION_4_LINE,QSPI_ADDRESS_4_LINES,QSPI_ADDRESS_24_BITS,QSPI_DATA_4_LINES);}else{        //SPI,写command指令,地址为0,无空周期,单线传输指令,无地址,24位地址(两个字节),4线传数据QSPI_Send_CMD(W25X_ManufactDeviceID,0,0,QSPI_INSTRUCTION_1_LINE,QSPI_ADDRESS_1_LINES,QSPI_ADDRESS_24_BITS,QSPI_DATA_1_LINES);}QSPI_Receive(&temp,2);id=((temp[0]<<8)|temp[1]);//QSPI_Receive(&id,2);//id为两个字节return id;
}//读取SPI FLASH,仅支持QPI模式
//在指定地址开始读取指定长度的数据
//pBuffer:数据存储区
//ReadAddr:开始读取的地址(最大32bit)
//NumByteToRead:要读取的字节数(最大65535)
void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead)
{ //QPI,快速读数据,地址为ReadAddr,4线传输数据_32位地址_4线传输地址_4线传输指令,8空周期,NumByteToRead个数据QSPI_Send_CMD(W25X_FastReadData,ReadAddr,8,QSPI_INSTRUCTION_4_LINES,QSPI_ADDRESS_4_LINES,QSPI_ADDRESS_32_BITS,QSPI_DATA_4_LINES); QSPI_Receive(pBuffer,NumByteToRead);
}  //SPI在一页(0~65535)内写入少于256个字节的数据
//在指定地址开始写入最大256字节的数据
//pBuffer:数据存储区
//WriteAddr:开始写入的地址(最大32bit)
//NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!!
void W25QXX_Write_Page(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
{W25QXX_Write_Enable();                 //写使能QSPI_Send_CMD(W25X_PageProgram,WriteAddr,0,QSPI_INSTRUCTION_4_LINES,QSPI_ADDRESS_4_LINES,QSPI_ADDRESS_32_BITS,QSPI_DATA_4_LINES);  //QPI,页写指令,地址为WriteAddr,4线传输数据_32位地址_4线传输地址_4线传输指令,无空周期,NumByteToWrite个数据QSPI_Transmit(pBuffer,NumByteToWrite);                   W25QXX_Wait_Busy();                      //等待写入结束
} //无检验写SPI FLASH
//必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!
//具有自动换页功能
//在指定地址开始写入指定长度的数据,但是要确保地址不越界!
//pBuffer:数据存储区
//WriteAddr:开始写入的地址(最大32bit)
//NumByteToWrite:要写入的字节数(最大65535)
//CHECK OK
void W25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
{                    u16 pageremain;       pageremain=256-WriteAddr%256; //单页剩余的字节数                if(NumByteToWrite<=pageremain)pageremain=NumByteToWrite;//不大于256个字节while(1){      W25QXX_Write_Page(pBuffer,WriteAddr,pageremain);if(NumByteToWrite==pageremain)break;//写入结束了else //NumByteToWrite>pageremain{pBuffer+=pageremain;WriteAddr+=pageremain;  NumByteToWrite-=pageremain;              //减去已经写入了的字节数if(NumByteToWrite>256)pageremain=256; //一次可以写入256个字节else pageremain=NumByteToWrite;       //不够256个字节了}}
} //写SPI FLASH
//在指定地址开始写入指定长度的数据
//该函数带擦除操作!
//pBuffer:数据存储区
//WriteAddr:开始写入的地址(最大32bit)
//NumByteToWrite:要写入的字节数(最大65535)
u8 W25QXX_BUFFER[4096];
void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
{ u32 secpos;u16 secoff;u16 secremain;     u16 i;    u8 * W25QXX_BUF;     W25QXX_BUF=W25QXX_BUFFER;         secpos=WriteAddr/4096;//扇区地址  secoff=WriteAddr%4096;//在扇区内的偏移secremain=4096-secoff;//扇区剩余空间大小   //printf("ad:%X,nb:%X\r\n",WriteAddr,NumByteToWrite);//测试用if(NumByteToWrite<=secremain)secremain=NumByteToWrite;//不大于4096个字节while(1) {   W25QXX_Read(W25QXX_BUF,secpos*4096,4096);//读出整个扇区的内容for(i=0;i<secremain;i++)//校验数据{if(W25QXX_BUF[secoff+i]!=0XFF)break;//需要擦除     }if(i<secremain)//需要擦除{W25QXX_Erase_Sector(secpos);//擦除这个扇区for(i=0;i<secremain;i++)     //复制{W25QXX_BUF[i+secoff]=pBuffer[i];    }W25QXX_Write_NoCheck(W25QXX_BUF,secpos*4096,4096);//写入整个扇区  }else W25QXX_Write_NoCheck(pBuffer,WriteAddr,secremain);//写已经擦除了的,直接写入扇区剩余区间.                   if(NumByteToWrite==secremain)break;//写入结束了else//写入未结束{secpos++;//扇区地址增1secoff=0;//偏移位置为0     pBuffer+=secremain;  //指针偏移WriteAddr+=secremain;//写地址偏移       NumByteToWrite-=secremain;              //字节数递减if(NumByteToWrite>4096)secremain=4096;   //下一个扇区还是写不完else secremain=NumByteToWrite;         //下一个扇区可以写完了}    };
}//擦除整个芯片
//等待时间超长...
void W25QXX_Erase_Chip(void)
{                                   W25QXX_Write_Enable();                  //SET WEL W25QXX_Wait_Busy();   //QPI,写全片擦除指令,地址为0,无数据_8位地址_无地址_4线传输指令,无空周期,0个字节数据QSPI_Send_CMD(W25X_ChipErase,0,0,QSPI_INSTRUCTION_4_LINES,QSPI_ADDRESS_NONE,QSPI_ADDRESS_8_BITS,QSPI_DATA_NONE);  W25QXX_Wait_Busy();                     //等待芯片擦除结束
} //擦除一个扇区
//Dst_Addr:扇区地址 根据实际容量设置
//擦除一个扇区的最少时间:150ms
void W25QXX_Erase_Sector(u32 Dst_Addr)
{  //printf("fe:%x\r\n",Dst_Addr);            //监视falsh擦除情况,测试用     Dst_Addr*=4096;W25QXX_Write_Enable();                  //SET WEL      W25QXX_Wait_Busy();  QSPI_Send_CMD(W25X_SectorErase,Dst_Addr,0,QSPI_INSTRUCTION_4_LINES,QSPI_ADDRESS_4_LINES,QSPI_ADDRESS_32_BITS,QSPI_DATA_NONE);//QPI,写扇区擦除指令,地址为0,无数据_32位地址_4线传输地址_4线传输指令,无空周期,0个字节数据W25QXX_Wait_Busy();                    //等待擦除完成
}//等待空闲
void W25QXX_Wait_Busy(void)
{   while((W25QXX_ReadSR(1)&0x01)==0x01);   // 等待BUSY位清空
}

四、内存单位

学习链接:https://www.eet-china.com/mp/a91743.html
专业名词:块(Block、Bank、簇)、扇区(sector)、页(page) 、Row是行、 Column是列

W25Q256:256Mb=256/8=32MB,注意小写的b和大写的B,b:bit,B:byte。
页是最小存储单元,一页有256字节(Byte)。
一个扇区有16页,所以一个扇区有:16256=4096字节(Byte) = 4096/1024=4KB。
一个块有16个扇区,所以一个块有:16
4=64KB
这个芯片有512个块,所以内存有:512*64=32768KB=32768/1024=32MB
芯片一共有512(块)*16(扇区)*16(页)=131072页

学习链接:https://blog.csdn.net/qq_44726883/article/details/105716126
https://blog.csdn.net/qq_24312945/article/details/117756829
https://blog.csdn.net/weixin_44453694/article/details/123955074
https://blog.csdn.net/ningjianwen/article/details/96477565
https://blog.csdn.net/oXiaoLingTong/article/details/123161211
QSPI:https://www.cnblogs.com/firege/p/9435349.html
https://blog.csdn.net/wangguchao/article/details/105593303
https://blog.csdn.net/wangguchao/article/details/105593303

说白了全是别人的东西,只会用别人的,自己创新不出来,这种感觉很难受。
后面需要做写一个W25Q256内存映射模式下存储MCU的代码,不知道写不写的出来。

SMT32F767通过STM32CUBE HAL库配置QSPI和W25Q256驱动相关推荐

  1. HAL库配置STM32F1系列定时器驱动步进电机(三)

    之前的电机成功地转了起来,但其噪音非常大,因为之前尝试过自带细分功能的优质驱动器,关于其具体原理我没有系统学习,在使用L298N驱动电机时就感觉到有些吃力,于是在这里补一下步进电机微步细分原理的功课, ...

  2. HAL库配置STM32F1系列PWM驱动步进电机(一)

    步进电机在3D打印机.机床CNC等很多领域有广泛的应用,这个系列用于记录笔者在学习步进电机过程中遇到的问题以及解决思路 1 电机原理 电机的分类有下述几种 具体原理可以参考这篇文章,介绍的很详细 (7 ...

  3. HAL库配置STM32F1系列定时器驱动步进电机(四)(梯形加减速)

    前言 经过之前的一些学习我们已经成功地让电机成功地转了起来,但是在实际应用中这样的电机是很难满足工业上的一些需求的,因为电机在启动和停止时都很难在一瞬间达到目标速度,我们可以从波形图的角度来看,如果我 ...

  4. HAL库配置STM32F1系列PWM驱动步进电机(二)

    接着上一篇,我要先补完利用PWM对步进电机进行调速的实验 PWM主要有两大要素:频率&占空比 频率 简单来说就是信号在1s内由高电平跳转到低电平再跳转回高电平的次数,一高一低再一高,这便是一个 ...

  5. stm32cube,hal库来实现PS2手柄数据发送

    stm32cube,hal库来实现PS2手柄数据发送 很久前买了个PS2的手柄,如下,之前 以前不会使用cube来配置工程,导致写程序很麻烦,对我这样的新手很不友好,看卖家提供的程序也很麻烦,拉高拉低 ...

  6. STM32F1系列HAL库配置系统时钟

    STM32F1系列HAL库配置系统时钟 其实一开始对于时钟我也是知之甚少,在MSP432中我就一直忽视时钟配置,其实也是在STM32学习时落下的病根,现在趁有空补一下. 时钟简单讲解 对于时钟系统,在 ...

  7. STM32F4系列HAL库配置定时器实验——输入捕获

    STM32F4系列HAL库配置定时器实验--输入捕获 输入捕获简单讲解 输入捕获模式可以用来测量脉冲宽度或者测量频率.我们以测量周期和频率为例,用一个简图来说明输入捕获的原理 假定定时器工作在向上计数 ...

  8. HAL库配置FreeRTOS

    HAL库配置 配置时钟源 1:修改HAL库定时器时钟源. 由于HAL库内部会使用systick定时器用于系统延时功能,而FreeRTOS也需要一个定时器用于操作系统内核调度的使用,顾需修改HAL库的时 ...

  9. HAL库配置GPIO

    HAL库配置GPIO HAL库与标准库不同的特点: 使用`CubeMX`配置GPIO底层参数: 总结HAL库中GPIO的相关功能: GPIO的寄存器: 总结 HAL库与标准库不同的特点: 标准库中初始 ...

  10. STM32F4xx系列使用HAL库配置SPI-读写FLASH

    SPI协议简介 SPI物理层特点    SPI协议层 QSPI协议简介 SPI框图 Flash写入与EEPROM有点相似,不同的是FLASH写入需要软件手动擦除,而EEPROM不用 SPI配置流程 1 ...

最新文章

  1. C#测试程序运行时间
  2. 3.Web项目中使用Log4j实例
  3. python爬虫beautifulsoup4系列4-子节点
  4. Spark1.0 安装
  5. 我所遭遇过的游戏中间件---HumanIK
  6. python导入函数模块 为什么会打印两次_5.1.2Python从模块导入函数
  7. 【文末彩蛋】数据仓库服务 GaussDB(DWS)单点性能案例集锦
  8. Deepin-文件目录介绍
  9. Android开发——通过扫描二维码,打开或者下载Android应用
  10. es6.2.4-ansible部署
  11. js base64图片太大_手把手教你常用JS方法封装(一) [ 大杂烩 ]
  12. 《中国科学》中文论文模板使用CCTTEX编译
  13. 8051单片机Proteus仿真与开发实例-RS485协议通信仿真
  14. jqGrid设置表格列的背景色
  15. 3GPP 各Release 版本(Rel-15、Rel-16、Rel-17)下载地址
  16. Centos7升级OpenSSL 1.0.2k-fips
  17. wxWidgets(1) :mac下搭建wxWidgets 3.0 环境
  18. Windows API程序设计入门(新手的第一个Windows程序)
  19. 银河麒麟arm64 qt打包
  20. leetcode 561

热门文章

  1. 液晶电视TCON板原理讲解<转>
  2. javashop 发布开源放大镜HiZoom插件
  3. 实验楼挑战:备份日志
  4. 屏幕录像软件camtasia2022汉化版好用的录屏软件
  5. 计算机毕业设计python基于django租房系统-房屋租赁系统
  6. 捷联惯导系统学习4.3(静基座误差)
  7. 小米手机图标应用图标不改变的问题
  8. 宽凳公司关于无人驾驶高精地图的看法(2018.8)
  9. VSCode下载安装和修改插件下载位置(配置右键菜单)和更换终端
  10. R语言相关性分析与偏相关分析