#include "qspi_flash.h" #include "stdio.h" QSPI_HandleTypeDef QSPIHandle; /** * @brief This function polling for release busy status. * @param hqspi: QSPI handle * @retval error code */ static uint32_t QSPI_WaitforBusyRelease(QSPI_HandleTypeDef *hqspi) { QSPI_CommandTypeDef s_command; QSPI_AutoPollingTypeDef s_config; /* Configure automatic polling mode to wait for write enabling */ s_config.Match = 0x0; s_config.Mask = (1 << 0); // BUSY s_config.MatchMode = QSPI_MATCH_MODE_AND; s_config.StatusBytesSize = 1; s_config.Interval = 0x10; s_config.AutomaticStop = QSPI_AUTOMATIC_STOP_ENABLE; s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE; s_command.Instruction = READ_STATUS_REG_CMD; s_command.AddressMode = QSPI_ADDRESS_NONE; s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; s_command.DataMode = QSPI_DATA_1_LINE; s_command.DummyCycles = 0; s_command.DdrMode = QSPI_DDR_MODE_DISABLE; s_command.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; s_command.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; if (HAL_QSPI_AutoPolling(hqspi, &s_command, &s_config, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { printf("%s: function %s, line %d, command \"qspi auto polling for release busy\" failed.\r\n", __FILE__, __FUNCTION__, __LINE__); return ((2 << 28) | s_config.Mask); } return 0; } /** * @brief This function send a Write Enable and wait it is effective. * @param hqspi: QSPI handle * @retval None */ static uint32_t QSPI_WriteEnable(QSPI_HandleTypeDef *hqspi) { QSPI_CommandTypeDef s_command; QSPI_AutoPollingTypeDef s_config; /* Enable write operations */ s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE; s_command.Instruction = WRITE_ENABLE_CMD; s_command.AddressMode = QSPI_ADDRESS_NONE; s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; s_command.DataMode = QSPI_DATA_NONE; s_command.DummyCycles = 0; s_command.DdrMode = QSPI_DDR_MODE_DISABLE; s_command.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; s_command.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; if (HAL_QSPI_Command(hqspi, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { printf("%s: function %s, line %d, command \"write enable\" failed.\r\n", __FILE__, __FUNCTION__, __LINE__); return ((1 << 28) | s_command.Instruction); } /* Configure automatic polling mode to wait for write enabling */ s_config.Match = 0x2; s_config.Mask = (1 << 1) | (1 << 0); // WEL | BUSY s_config.MatchMode = QSPI_MATCH_MODE_AND; s_config.StatusBytesSize = 1; s_config.Interval = 0x10; s_config.AutomaticStop = QSPI_AUTOMATIC_STOP_ENABLE; s_command.Instruction = READ_STATUS_REG_CMD; s_command.DataMode = QSPI_DATA_1_LINE; if (HAL_QSPI_AutoPolling(hqspi, &s_command, &s_config, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { printf("%s: function %s, line %d, command \"qspi auto polling for write enable\" failed.\r\n", __FILE__, __FUNCTION__, __LINE__); return ((2 << 28) | s_config.Mask); } return 0; } void qspi_flash_init(uint32_t div) { GPIO_InitTypeDef gpio_init_structure; /*##-1- Enable peripherals and GPIO Clocks #################################*/ /* Enable the QuadSPI memory interface clock */ QSPI_CLK_ENABLE(); /* Reset the QuadSPI memory interface */ QSPI_FORCE_RESET(); QSPI_RELEASE_RESET(); /* Enable GPIO clocks */ QSPI_CLK_GPIO_CLK_ENABLE(); QSPI_BK1_CS_GPIO_CLK_ENABLE(); QSPI_BK1_D0_GPIO_CLK_ENABLE(); QSPI_BK1_D1_GPIO_CLK_ENABLE(); QSPI_BK1_D2_GPIO_CLK_ENABLE(); QSPI_BK1_D3_GPIO_CLK_ENABLE(); /* Enable DMA clock */ QSPI_MDMA_CLK_ENABLE(); /*##-2- Configure peripheral GPIO ##########################################*/ /* QSPI CLK GPIO pin configuration */ gpio_init_structure.Pin = QSPI_CLK_PIN; gpio_init_structure.Mode = GPIO_MODE_AF_PP; gpio_init_structure.Speed = GPIO_SPEED_FREQ_VERY_HIGH; gpio_init_structure.Pull = GPIO_NOPULL; gpio_init_structure.Alternate = GPIO_AF9_QUADSPI; HAL_GPIO_Init(QSPI_CLK_GPIO_PORT, &gpio_init_structure); /* QSPI CS GPIO pin configuration */ gpio_init_structure.Pin = QSPI_BK1_CS_PIN; gpio_init_structure.Pull = GPIO_PULLUP; gpio_init_structure.Alternate = GPIO_AF10_QUADSPI; HAL_GPIO_Init(QSPI_BK1_CS_GPIO_PORT, &gpio_init_structure); /* QSPI D0 GPIO pin configuration */ gpio_init_structure.Pin = QSPI_BK1_D0_PIN; gpio_init_structure.Pull = GPIO_NOPULL; gpio_init_structure.Alternate = GPIO_AF9_QUADSPI; HAL_GPIO_Init(QSPI_BK1_D0_GPIO_PORT, &gpio_init_structure); /* QSPI D1 GPIO pin configuration */ gpio_init_structure.Pin = QSPI_BK1_D1_PIN; gpio_init_structure.Pull = GPIO_NOPULL; gpio_init_structure.Alternate = GPIO_AF9_QUADSPI; HAL_GPIO_Init(QSPI_BK1_D1_GPIO_PORT, &gpio_init_structure); /* QSPI D2 GPIO pin configuration */ gpio_init_structure.Pin = QSPI_BK1_D2_PIN; gpio_init_structure.Pull = GPIO_NOPULL; gpio_init_structure.Alternate = GPIO_AF9_QUADSPI; HAL_GPIO_Init(QSPI_BK1_D2_GPIO_PORT, &gpio_init_structure); /* QSPI D3 GPIO pin configuration */ gpio_init_structure.Pin = QSPI_BK1_D3_PIN; gpio_init_structure.Pull = GPIO_NOPULL; gpio_init_structure.Alternate = GPIO_AF9_QUADSPI; HAL_GPIO_Init(QSPI_BK1_D3_GPIO_PORT, &gpio_init_structure); /*##-3- Configure the NVIC for QSPI #########################################*/ /* NVIC configuration for QSPI interrupt */ // HAL_NVIC_SetPriority(QUADSPI_IRQn, 0x0F, 0); // HAL_NVIC_EnableIRQ(QUADSPI_IRQn); /* Initialize QuadSPI structures ------------------------------------------- */ QSPIHandle.Instance = QUADSPI; /* QSPI initialization */ /* ClockPrescaler set to 1, so QSPI clock = 200MHz / (1+1) = 100MHz */ QSPIHandle.Init.ClockPrescaler = div; QSPIHandle.Init.FifoThreshold = 32; QSPIHandle.Init.SampleShifting = QSPI_SAMPLE_SHIFTING_HALFCYCLE; QSPIHandle.Init.FlashSize = QSPI_FLASH_SIZE; QSPIHandle.Init.ChipSelectHighTime = QSPI_CS_HIGH_TIME_3_CYCLE; QSPIHandle.Init.ClockMode = QSPI_CLOCK_MODE_0; QSPIHandle.Init.FlashID = QSPI_FLASH_ID_1; QSPIHandle.Init.DualFlash = QSPI_DUALFLASH_DISABLE; /* Initialize QuadSPI ------------------------------------------------ */ HAL_QSPI_DeInit(&QSPIHandle); if (HAL_QSPI_Init(&QSPIHandle) != HAL_OK) { printf("%s: function %s, line %d, qspi init failed.\r\n", __FILE__, __FUNCTION__, __LINE__); while (1); } else { printf("qspi init succeed.\r\n"); } } void qspi_flash_deinit(void) { /*##-1- Disable the NVIC for QSPI ###########################################*/ HAL_NVIC_DisableIRQ(QUADSPI_IRQn); /*##-1- Disable peripherals ################################################*/ /* De-Configure QSPI pins */ HAL_GPIO_DeInit(QSPI_CLK_GPIO_PORT, QSPI_CLK_PIN); HAL_GPIO_DeInit(QSPI_BK1_CS_GPIO_PORT, QSPI_BK1_CS_PIN); HAL_GPIO_DeInit(QSPI_BK1_D0_GPIO_PORT, QSPI_BK1_D0_PIN); HAL_GPIO_DeInit(QSPI_BK1_D1_GPIO_PORT, QSPI_BK1_D1_PIN); HAL_GPIO_DeInit(QSPI_BK1_D2_GPIO_PORT, QSPI_BK1_D2_PIN); HAL_GPIO_DeInit(QSPI_BK1_D3_GPIO_PORT, QSPI_BK1_D3_PIN); /*##-3- Reset peripherals ##################################################*/ /* Reset the QuadSPI memory interface */ QSPI_FORCE_RESET(); QSPI_RELEASE_RESET(); /* Disable the QuadSPI memory interface clock */ QSPI_CLK_DISABLE(); } uint32_t qspi_flash_write(uint32_t addr, uint8_t *data, uint32_t length) { uint32_t ret; QSPI_CommandTypeDef s_command; ret = QSPI_WriteEnable(&QSPIHandle); if (ret) { return ret; } /* Initialize the read volatile configuration register command */ s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE; s_command.Instruction = QUAD_IN_FAST_PROG_CMD; s_command.AddressMode = QSPI_ADDRESS_1_LINE; s_command.AddressSize = QSPI_ADDRESS_24_BITS; s_command.Address = addr; s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; s_command.DataMode = QSPI_DATA_4_LINES; s_command.DummyCycles = 0; s_command.NbData = length; s_command.DdrMode = QSPI_DDR_MODE_DISABLE; s_command.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; s_command.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; /* Configure the command */ if (HAL_QSPI_Command(&QSPIHandle, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { printf("%s: function %s, line %d, command \"QUAD_IN_FAST_PROG_CMD\" failed.\r\n", __FILE__, __FUNCTION__, __LINE__); return ((1 << 28) | s_command.Instruction); } /* Writing of the data */ if (HAL_QSPI_Transmit(&QSPIHandle, data, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { printf("%s: function %s, line %d, command \"HAL_QSPI_Transmit\" failed.\r\n", __FILE__, __FUNCTION__, __LINE__); return ((3 << 28) | s_command.Instruction); } ret = QSPI_WaitforBusyRelease(&QSPIHandle); if (ret) { printf("%s: function %s, line %d, command \"busy after write\" failed.\r\n", __FILE__, __FUNCTION__, __LINE__); return ret; } return 0; } uint32_t qspi_flash_read(uint32_t addr, uint8_t *data, uint32_t length) { QSPI_CommandTypeDef s_command; /* Initialize the read volatile configuration register command */ s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE; s_command.Instruction = QUAD_OUT_FAST_READ_CMD; s_command.AddressMode = QSPI_ADDRESS_1_LINE; s_command.AddressSize = QSPI_ADDRESS_24_BITS; s_command.Address = addr; s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; s_command.DataMode = QSPI_DATA_4_LINES; s_command.DummyCycles = 8; s_command.NbData = length; s_command.DdrMode = QSPI_DDR_MODE_DISABLE; s_command.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; s_command.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; /* Configure the command */ if (HAL_QSPI_Command(&QSPIHandle, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { printf("%s: function %s, line %d, command \"QUAD_OUT_FAST_READ_CMD\" failed.\r\n", __FILE__, __FUNCTION__, __LINE__); return ((1 << 28) | s_command.Instruction); } /* Reception of the data */ if (HAL_QSPI_Receive(&QSPIHandle, data, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { printf("%s: function %s, line %d, command \"HAL_QSPI_Receive\" failed.\r\n", __FILE__, __FUNCTION__, __LINE__); return ((3 << 28) | s_command.Instruction); } return 0; } uint32_t qspi_flash_erase_4kbytes(uint32_t addr) { uint32_t ret; QSPI_CommandTypeDef s_command; ret = QSPI_WriteEnable(&QSPIHandle); if (ret) { return ret; } /* Initialize the read volatile configuration register command */ s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE; s_command.Instruction = SECTOR_ERASE_CMD; s_command.AddressMode = QSPI_ADDRESS_1_LINE; s_command.AddressSize = QSPI_ADDRESS_24_BITS; s_command.Address = addr; s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; s_command.DataMode = QSPI_DATA_NONE; s_command.DummyCycles = 0; s_command.DdrMode = QSPI_DDR_MODE_DISABLE; s_command.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; s_command.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; /* Configure the command */ if (HAL_QSPI_Command(&QSPIHandle, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { printf("%s: function %s, line %d, command \"SECTOR_ERASE_CMD\" failed.\r\n", __FILE__, __FUNCTION__, __LINE__); return ((1 << 28) | s_command.Instruction); } ret = QSPI_WaitforBusyRelease(&QSPIHandle); if (ret) { printf("%s: function %s, line %d, command \"busy after erasen 4K\" failed.\r\n", __FILE__, __FUNCTION__, __LINE__); return ret; } return 0; } uint32_t qspi_flash_erase_32kbytes(uint32_t addr) { uint32_t ret; QSPI_CommandTypeDef s_command; ret = QSPI_WriteEnable(&QSPIHandle); if (ret) { return ret; } /* Initialize the read volatile configuration register command */ s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE; s_command.Instruction = SUBBLOCK_ERASE_CMD; s_command.AddressMode = QSPI_ADDRESS_1_LINE; s_command.AddressSize = QSPI_ADDRESS_24_BITS; s_command.Address = addr; s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; s_command.DataMode = QSPI_DATA_NONE; s_command.DummyCycles = 0; s_command.DdrMode = QSPI_DDR_MODE_DISABLE; s_command.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; s_command.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; /* Configure the command */ if (HAL_QSPI_Command(&QSPIHandle, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { printf("%s: function %s, line %d, command \"SUBBLOCK_ERASE_CMD\" failed.\r\n", __FILE__, __FUNCTION__, __LINE__); return ((1 << 28) | s_command.Instruction); } ret = QSPI_WaitforBusyRelease(&QSPIHandle); if (ret) { printf("%s: function %s, line %d, command \"busy after erasen 32K\" failed.\r\n", __FILE__, __FUNCTION__, __LINE__); return ret; } return 0; } uint32_t qspi_flash_erase_64kbytes(uint32_t addr) { uint32_t ret; QSPI_CommandTypeDef s_command; ret = QSPI_WriteEnable(&QSPIHandle); if (ret) { return ret; } /* Initialize the read volatile configuration register command */ s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE; s_command.Instruction = BLOCK_ERASE_CMD; s_command.AddressMode = QSPI_ADDRESS_1_LINE; s_command.AddressSize = QSPI_ADDRESS_24_BITS; s_command.Address = addr; s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; s_command.DataMode = QSPI_DATA_NONE; s_command.DummyCycles = 0; s_command.DdrMode = QSPI_DDR_MODE_DISABLE; s_command.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; s_command.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; /* Configure the command */ if (HAL_QSPI_Command(&QSPIHandle, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { printf("%s: function %s, line %d, command \"SUBBLOCK_ERASE_CMD\" failed.\r\n", __FILE__, __FUNCTION__, __LINE__); return ((1 << 28) | s_command.Instruction); } ret = QSPI_WaitforBusyRelease(&QSPIHandle); if (ret) { printf("%s: function %s, line %d, command \"busy after erasen 64K\" failed.\r\n", __FILE__, __FUNCTION__, __LINE__); return ret; } return 0; }