• SMT32F767通过STM32CUBE HAL库配置QSPI和W25Q256驱动


    一、芯片管脚和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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    #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频率为216M
      hqspi.Init.ClockPrescaler = 2;//QSPI频率=216/(2+1)=72M
      hqspi.Init.FifoThreshold = 4;//FIFO阈值 CR中FTHRES[4:0] FIFO中写入4+1个字节后,FTF标志置1
      hqspi.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_0
      hqspi.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 Configuration
        PB2     ------> QUADSPI_CLK
        PE7     ------> QUADSPI_BK2_IO0
        PE8     ------> QUADSPI_BK2_IO1
        PE9     ------> QUADSPI_BK2_IO2
        PE10     ------> QUADSPI_BK2_IO3
        PC11     ------> 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   Timeout
    	HAL_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 Configuration
        PB2     ------> QUADSPI_CLK
        PE7     ------> QUADSPI_BK2_IO0
        PE8     ------> QUADSPI_BK2_IO1
        PE9     ------> QUADSPI_BK2_IO2
        PE10     ------> QUADSPI_BK2_IO3
        PC11     ------> 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 */
    
    
    
    
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222

    三、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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64

    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();//初始化QSPI
    	W25QXX_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,104M
    		temp=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++;//扇区地址增1
    			secoff=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位清空
    }   
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304
    • 305
    • 306
    • 307
    • 308
    • 309
    • 310
    • 311
    • 312
    • 313
    • 314
    • 315
    • 316
    • 317
    • 318
    • 319
    • 320
    • 321
    • 322
    • 323
    • 324
    • 325
    • 326
    • 327
    • 328
    • 329
    • 330
    • 331
    • 332
    • 333
    • 334
    • 335
    • 336
    • 337
    • 338
    • 339

    四、内存单位

    学习链接: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的代码,不知道写不写的出来。

  • 相关阅读:
    【C++基础】内存泄漏检测——Valgrind、VLD、RTC
    [网鼎杯 2018]Comment
    关于你所不知道的双机互备,确定不了解一下?
    高压 36V 四通道 DMOS 全桥驱动MS3988/3988N
    threejs全景图片展示
    identity4 系列————案例篇[三]
    网络安全(黑客)自学
    随机规划——报童模型
    【每日一题】2558. 从数量最多的堆取走礼物-2023.10.28
    基于SpringBoot的仿京东商城系统
  • 原文地址:https://blog.csdn.net/chenyouledashen/article/details/124902488