/* Private variables ---------------------------------------------------------*/
QSPI_HandleTypeDef hqspi;
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_QUADSPI_Init(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
#define DATA_SIZE 10000
#define DATA_ADDR 320
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_QUADSPI_Init();
/* USER CODE BEGIN 2 */
HAL_GPIO_WritePin(GPIOF, GPIO_PIN_6|GPIO_PIN_7, GPIO_PIN_SET);
delay_init(216);
delay_ms(500);
static uint16_t W25qxx_Id;
W25qxx_Id = W25qxx_Read_Id(hqspi);
W25qxx_Erase_Sector(hqspi, 0);
static uint8_t pData_wr[DATA_SIZE] = {22,33,44};
uint32_t i;
for(i = 0; i < DATA_SIZE; i++)
{
pData_wr[i] = i;
}
W25QXX_Write(hqspi, pData_wr, DATA_ADDR, DATA_SIZE);
static uint8_t pData_rd[DATA_SIZE];
W25qxx_Read_Data(hqspi, DATA_ADDR, (uint8_t*)pData_rd, DATA_SIZE);
for(i = 0; i < DATA_SIZE; i++)
{
if((pData_wr[i] & 0xff) != (pData_rd[i] & 0xff)) break;
}
if(i < DATA_SIZE)
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET);
}
else
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_SET);
}
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief QUADSPI Initialization Function
* @param None
* @retval None
*/
static void MX_QUADSPI_Init(void)
{
/* USER CODE BEGIN QUADSPI_Init 0 */
/* USER CODE END QUADSPI_Init 0 */
/* USER CODE BEGIN QUADSPI_Init 1 */
/* USER CODE END QUADSPI_Init 1 */
/* QUADSPI parameter configuration*/
hqspi.Instance = QUADSPI;
hqspi.Init.ClockPrescaler = 2;
hqspi.Init.FifoThreshold = 4;
hqspi.Init.SampleShifting = QSPI_SAMPLE_SHIFTING_HALFCYCLE;
hqspi.Init.FlashSize = 24;
hqspi.Init.ChipSelectHighTime = QSPI_CS_HIGH_TIME_5_CYCLE;
hqspi.Init.ClockMode = QSPI_CLOCK_MODE_0;
hqspi.Init.FlashID = QSPI_FLASH_ID_1;
hqspi.Init.DualFlash = QSPI_DUALFLASH_DISABLE;
if (HAL_QSPI_Init(&hqspi) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN QUADSPI_Init 2 */
/* USER CODE END QUADSPI_Init 2 */
}
/**
* @brief GPIO Initialization Function
* @param None
* @retval None
*/
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOF_CLK_ENABLE();
__HAL_RCC_GPIOH_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOF, GPIO_PIN_6|GPIO_PIN_7, GPIO_PIN_RESET);
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET);
/*Configure GPIO pins : PF6 PF7 */
GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);
/*Configure GPIO pin : PB1 */
GPIO_InitStruct.Pin = GPIO_PIN_1;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
#ifndef __BSP_W25QXX_H
#define __BSP_W25QXX_H
#include "main.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
extern uint16_t W25QXX_TYPE; //定义W25QXX芯片型号
//
//指令表
#define W25X_WriteEnable 0x06
#define W25X_WriteDisable 0x04
#define W25X_ReadStatusReg1 0x05
#define W25X_ReadStatusReg2 0x35
#define W25X_ReadStatusReg3 0x15
#define W25X_WriteStatusReg1 0x01
#define W25X_WriteStatusReg2 0x31
#define W25X_WriteStatusReg3 0x11
#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 W25X_Enable4ByteAddr 0xB7
#define W25X_Exit4ByteAddr 0xE9
#define W25X_SetReadParam 0xC0
#define W25X_EnterQPIMode 0x38
#define W25X_ExitQPIMode 0xFF
/**
* @brief HAL Status structures definition
*/
typedef enum
{
Status_Busy = 0x00U,
Status_Free = 0x01U
} Busy_StatusTypeDef;
HAL_StatusTypeDef W25qxx_Cmd(QSPI_HandleTypeDef hqspi, uint32_t Instruction);
uint16_t W25qxx_Read_Id(QSPI_HandleTypeDef hqspi);
HAL_StatusTypeDef W25qxx_Read_Data(QSPI_HandleTypeDef hqspi, uint32_t Addr, uint8_t *pData, uint32_t Data_Count);
HAL_StatusTypeDef W25qxx_Erase_Sector(QSPI_HandleTypeDef hqspi, uint32_t Sector);
Busy_StatusTypeDef W25qxx_Is_Busy(QSPI_HandleTypeDef hqspi);
void W25QXX_Write_Page(QSPI_HandleTypeDef hqspi, uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite);
void W25QXX_Write_NoCheck(QSPI_HandleTypeDef hqspi, uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite) ;
void W25QXX_Write(QSPI_HandleTypeDef hqspi, uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite);
#endif //__BSP_W25QXX_H
#include "bsp_w25qxx.h"
uint16_t W25QXX_TYPE; //定义W25QXX芯片型号
HAL_StatusTypeDef W25qxx_Cmd(QSPI_HandleTypeDef hqspi, uint32_t Instruction)
{
QSPI_CommandTypeDef cmd;
HAL_StatusTypeDef status;
cmd.Instruction = Instruction;
cmd.Address = 0;
cmd.AlternateBytes = 0;
cmd.AddressSize = 0;
cmd.AlternateBytesSize = 0;
cmd.DummyCycles = 0;
cmd.InstructionMode = QSPI_INSTRUCTION_1_LINE;
cmd.AddressMode = QSPI_ADDRESS_NONE;
cmd.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
cmd.DataMode = QSPI_DATA_NONE;
cmd.NbData = 0;
cmd.DdrMode = QSPI_DDR_MODE_DISABLE;
cmd.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
cmd.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
status = HAL_QSPI_Command(&hqspi, &cmd, 1000);
return status;
}
uint16_t W25qxx_Read_Id(QSPI_HandleTypeDef hqspi)
{
uint16_t id;
QSPI_CommandTypeDef cmd;
HAL_StatusTypeDef status;
uint8_t pData[2];
W25qxx_Cmd(hqspi, W25X_WriteEnable);
cmd.Instruction = W25X_ManufactDeviceID;
cmd.Address = 0;
cmd.AlternateBytes = 0;
cmd.AddressSize = QSPI_ADDRESS_24_BITS;
cmd.AlternateBytesSize = 0;
cmd.DummyCycles = 0;
cmd.InstructionMode = QSPI_INSTRUCTION_1_LINE;
cmd.AddressMode = QSPI_ADDRESS_1_LINE;
cmd.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
cmd.DataMode = QSPI_DATA_1_LINE;
cmd.NbData = 2;
cmd.DdrMode = QSPI_DDR_MODE_DISABLE;
cmd.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
cmd.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
status = HAL_QSPI_Command(&hqspi, &cmd, 1000);
status = HAL_QSPI_Receive(&hqspi, pData, 1000);
id = (pData[0] << 8) | pData[1];
while(W25qxx_Is_Busy(hqspi) == Status_Busy)
{
}
return id;
}
void W25QXX_Write_Page(QSPI_HandleTypeDef hqspi, uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite)
{
QSPI_CommandTypeDef cmd;
W25qxx_Cmd(hqspi, W25X_WriteEnable);
cmd.Instruction = W25X_PageProgram;
cmd.Address = WriteAddr;
cmd.AlternateBytes = 0;
cmd.AddressSize = QSPI_ADDRESS_24_BITS;
cmd.AlternateBytesSize = 0;
cmd.DummyCycles = 0;
cmd.InstructionMode = QSPI_INSTRUCTION_1_LINE;
cmd.AddressMode = QSPI_ADDRESS_1_LINE;
cmd.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
cmd.DataMode = QSPI_DATA_1_LINE;
cmd.NbData = NumByteToWrite;
cmd.DdrMode = QSPI_DDR_MODE_DISABLE;
cmd.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
cmd.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
HAL_QSPI_Command(&hqspi, &cmd, 1000);
HAL_QSPI_Transmit(&hqspi, pBuffer, 1000);
while(W25qxx_Is_Busy(hqspi) == Status_Busy)
{
}
}
void W25QXX_Write_NoCheck(QSPI_HandleTypeDef hqspi, uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite)
{
uint16_t pageremain;
pageremain=256-WriteAddr%256; //单页剩余的字节数
if(NumByteToWrite<=pageremain)pageremain=NumByteToWrite;//不大于256个字节
while(1)
{
W25QXX_Write_Page(hqspi, 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个字节了
}
}
}
void W25QXX_Write(QSPI_HandleTypeDef hqspi, uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite)
{
uint32_t secpos;
uint16_t secoff;
uint16_t secremain;
uint16_t i;
uint8_t * W25QXX_BUF;
static uint8_t W25QXX_BUFFER[4096];
W25QXX_BUF=W25QXX_BUFFER;
secpos=WriteAddr/4096;//扇区地址
secoff=WriteAddr%4096;//在扇区内的偏移
secremain=4096-secoff;//扇区剩余空间大小
if(NumByteToWrite<=secremain)secremain=NumByteToWrite;//不大于4096个字节
while(1)
{
W25qxx_Read_Data(hqspi, secpos*4096, W25QXX_BUF, 4096);//读出整个扇区的内容
while(W25qxx_Is_Busy(hqspi) == Status_Busy)
{
}
for(i=0;i<secremain;i++)//校验数据
{
if(W25QXX_BUF[secoff+i]!=0XFF)break;//需要擦除
}
if(i<secremain)//需要擦除
{
W25qxx_Erase_Sector(hqspi, secpos);//擦除这个扇区
while(W25qxx_Is_Busy(hqspi) == Status_Busy)
{
}
for(i=0;i<secremain;i++) //复制
{
W25QXX_BUF[i+secoff]=pBuffer[i];
}
W25QXX_Write_NoCheck(hqspi, W25QXX_BUF,secpos*4096,4096);//写入整个扇区
}else W25QXX_Write_NoCheck(hqspi, pBuffer,WriteAddr,secremain);//写已经擦除了的,直接写入扇区剩余区间.
while(W25qxx_Is_Busy(hqspi) == Status_Busy)
{
}
if(NumByteToWrite==secremain)break;//写入结束了
else//写入未结束
{
secpos++;//扇区地址增1
secoff=0;//偏移位置为0
pBuffer+=secremain; //指针偏移
WriteAddr+=secremain;//写地址偏移
NumByteToWrite-=secremain; //字节数递减
if(NumByteToWrite>4096)secremain=4096; //下一个扇区还是写不完
else secremain=NumByteToWrite; //下一个扇区可以写完了
}
};
}
HAL_StatusTypeDef W25qxx_Read_Data(QSPI_HandleTypeDef hqspi, uint32_t Addr, uint8_t *pData, uint32_t Data_Count)
{
QSPI_CommandTypeDef cmd;
HAL_StatusTypeDef status;
W25qxx_Cmd(hqspi, W25X_WriteEnable);
cmd.Instruction = W25X_ReadData;
cmd.Address = Addr;
cmd.AlternateBytes = 0;
cmd.AddressSize = QSPI_ADDRESS_24_BITS;
cmd.AlternateBytesSize = 0;
cmd.DummyCycles = 0;
cmd.InstructionMode = QSPI_INSTRUCTION_1_LINE;
cmd.AddressMode = QSPI_ADDRESS_1_LINE;
cmd.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
cmd.DataMode = QSPI_DATA_1_LINE;
cmd.NbData = Data_Count;
cmd.DdrMode = QSPI_DDR_MODE_DISABLE;
cmd.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
cmd.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
status = HAL_QSPI_Command(&hqspi, &cmd, 1000);
status = HAL_QSPI_Receive(&hqspi, pData, 1000);
while(W25qxx_Is_Busy(hqspi) == Status_Busy)
{
}
return status;
}
HAL_StatusTypeDef W25qxx_Erase_Sector(QSPI_HandleTypeDef hqspi, uint32_t Sector)
{
QSPI_CommandTypeDef cmd;
HAL_StatusTypeDef status;
W25qxx_Cmd(hqspi, W25X_WriteEnable);
cmd.Instruction = W25X_SectorErase;
cmd.Address = Sector * 4096;
cmd.AlternateBytes = 0;
cmd.AddressSize = QSPI_ADDRESS_24_BITS;
cmd.AlternateBytesSize = 0;
cmd.DummyCycles = 0;
cmd.InstructionMode = QSPI_INSTRUCTION_1_LINE;
cmd.AddressMode = QSPI_ADDRESS_1_LINE;
cmd.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
cmd.DataMode = QSPI_DATA_NONE;
cmd.NbData = 0;
cmd.DdrMode = QSPI_DDR_MODE_DISABLE;
cmd.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
cmd.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
status = HAL_QSPI_Command(&hqspi, &cmd, 1000);
while(W25qxx_Is_Busy(hqspi) == Status_Busy)
{
}
return status;
}
Busy_StatusTypeDef W25qxx_Is_Busy(QSPI_HandleTypeDef hqspi)
{
QSPI_CommandTypeDef cmd;
HAL_StatusTypeDef status;
uint8_t Data;
cmd.Instruction = W25X_ReadStatusReg1;
cmd.Address = 0;
cmd.AlternateBytes = 0;
cmd.AddressSize = QSPI_ADDRESS_24_BITS;
cmd.AlternateBytesSize = 0;
cmd.DummyCycles = 0;
cmd.InstructionMode = QSPI_INSTRUCTION_1_LINE;
cmd.AddressMode = QSPI_ADDRESS_1_LINE;
cmd.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
cmd.DataMode = QSPI_DATA_1_LINE;
cmd.NbData = 1;
cmd.DdrMode = QSPI_DDR_MODE_DISABLE;
cmd.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
cmd.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
status = HAL_QSPI_Command(&hqspi, &cmd, 1000);
status = HAL_QSPI_Receive(&hqspi, &Data, 1000);
if(Data & 0x01) return Status_Busy;
else return Status_Free;
}
总结:
1、从地址DATA_ADDR开始写DATA_SIZE个字节数据,然后从地址DATA_ADDR开始读DATA_SIZE个字节数据,并进行比较,结果均相同
2、此程序指令、地址、数据等都采用单线SPI通信
3、
4、W25Q256最高104MHz时钟,所以分频设置为2,采用时钟216MHz/(2+1);
5、FLASH SIZE设为24,W25Q256为32MB大小,2的(24+1)次方
6、Clock Mode为Low,CLK空闲时为低电平
7、Chip Select High Time设为5,最低50ns,13.8ns*5 > 50ns
8、每条W25Q256指令发送前都要使能W25qxx_Cmd(hqspi, W25X_WriteEnable);发送后都要等待忙标志复位while(W25qxx_Is_Busy(hqspi) == Status_Busy)
9、将QuadSPI Mode改为Quad SPI lines,屏蔽//HAL_GPIO_WritePin(GPIOF, GPIO_PIN_6|GPIO_PIN_7, GPIO_PIN_SET);因为这两个引脚已复用,程序一样正确执行