| /** | |
| ****************************************************************************** | |
| * File Name : QUADSPI.c | |
| * Description : This file provides code for the configuration | |
| * of the QUADSPI instances. | |
| ****************************************************************************** | |
| * @attention | |
| * | |
| * <h2><center>© Copyright (c) 2020 STMicroelectronics. | |
| * All rights reserved.</center></h2> | |
| * | |
| * This software component is licensed by ST under Ultimate Liberty license | |
| * SLA0044, the "License"; You may not use this file except in compliance with | |
| * the License. You may obtain a copy of the License at: | |
| * www.st.com/SLA0044 | |
| * | |
| ****************************************************************************** | |
| */ | |
| /* Includes ------------------------------------------------------------------*/ | |
| #include "quadspi.h" | |
| /* USER CODE BEGIN 0 */ | |
| /* USER CODE END 0 */ | |
| QSPI_HandleTypeDef hqspi; | |
| /* QUADSPI init function */ | |
| void MX_QUADSPI_Init(void) | |
| { | |
| hqspi.Instance = QUADSPI; | |
| hqspi.Init.ClockPrescaler = 2; | |
| hqspi.Init.FifoThreshold = 1; | |
| hqspi.Init.SampleShifting = QSPI_SAMPLE_SHIFTING_NONE; | |
| hqspi.Init.FlashSize = QSPI_FLASH_SIZE; | |
| hqspi.Init.ChipSelectHighTime = QSPI_CS_HIGH_TIME_1_CYCLE; | |
| hqspi.Init.ClockMode = QSPI_CLOCK_MODE_3; | |
| hqspi.Init.FlashID = QSPI_FLASH_ID_2; | |
| hqspi.Init.DualFlash = QSPI_DUALFLASH_DISABLE; | |
| if (HAL_QSPI_Init(&hqspi) != HAL_OK) | |
| { | |
| Error_Handler(); | |
| } | |
| } | |
| void HAL_QSPI_MspInit(QSPI_HandleTypeDef* qspiHandle) | |
| { | |
| if(qspiHandle->Instance==QUADSPI) | |
| { | |
| /* USER CODE BEGIN QUADSPI_MspInit 0 */ | |
| /* USER CODE END QUADSPI_MspInit 0 */ | |
| /* QUADSPI clock enable */ | |
| __HAL_RCC_QSPI_CLK_ENABLE(); | |
| __HAL_RCC_QSPI_FORCE_RESET(); | |
| __HAL_RCC_QSPI_RELEASE_RESET(); | |
| /* USER CODE BEGIN QUADSPI_MspInit 1 */ | |
| /* USER CODE END QUADSPI_MspInit 1 */ | |
| } | |
| } | |
| void HAL_QSPI_MspDeInit(QSPI_HandleTypeDef* qspiHandle) | |
| { | |
| if(qspiHandle->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 */ | |
| /** | |
| * @brief This function send a Write Enable and wait it is effective. | |
| * @param hqspi: QSPI handle | |
| * @retval None | |
| */ | |
| void QSPI_WriteEnable(QSPI_HandleTypeDef *hqspi) | |
| { | |
| QSPI_CommandTypeDef sCommand; | |
| QSPI_AutoPollingTypeDef sConfig; | |
| /* Enable write operations ------------------------------------------ */ | |
| sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE; | |
| sCommand.Instruction = WRITE_ENABLE_CMD; | |
| sCommand.AddressMode = QSPI_ADDRESS_NONE; | |
| sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; | |
| sCommand.DataMode = QSPI_DATA_NONE; | |
| sCommand.DummyCycles = 0; | |
| sCommand.DdrMode = QSPI_DDR_MODE_DISABLE; | |
| sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; | |
| sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; | |
| volatile HAL_StatusTypeDef status = HAL_QSPI_Command(hqspi, &sCommand, HAL_QPSI_TIMEOUT_DEFAULT_VALUE); | |
| if (status != HAL_OK) | |
| { | |
| Error_Handler(); | |
| } | |
| /* Configure automatic polling mode to wait for write enabling ---- */ | |
| sConfig.Match = 0x02; | |
| sConfig.Mask = 0x02; | |
| sConfig.MatchMode = QSPI_MATCH_MODE_AND; | |
| sConfig.StatusBytesSize = 1; | |
| sConfig.Interval = 0x10; | |
| sConfig.AutomaticStop = QSPI_AUTOMATIC_STOP_ENABLE; | |
| sCommand.Instruction = READ_STATUS_REG1_CMD; | |
| sCommand.DataMode = QSPI_DATA_1_LINE; | |
| status = HAL_QSPI_AutoPolling(hqspi, &sCommand, &sConfig, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) ; | |
| if (status != HAL_OK) | |
| { | |
| Error_Handler(); | |
| } | |
| } | |
| static HAL_StatusTypeDef QSPI_AutoPollingMemReady(QSPI_HandleTypeDef *hqspi, uint32_t Timeout) | |
| { | |
| QSPI_CommandTypeDef s_command; | |
| QSPI_AutoPollingTypeDef s_config; | |
| /* Configure automatic polling mode to wait for memory ready */ | |
| s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE; | |
| s_command.Instruction = READ_STATUS_REG1_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; | |
| s_config.Match = 0; | |
| s_config.MatchMode = QSPI_MATCH_MODE_AND; | |
| s_config.Interval = 0x10; | |
| s_config.AutomaticStop = QSPI_AUTOMATIC_STOP_ENABLE; | |
| s_config.Mask = 0x01; | |
| s_config.StatusBytesSize = 1; | |
| HAL_StatusTypeDef status = HAL_QSPI_AutoPolling(hqspi, &s_command, &s_config, Timeout); | |
| return status; | |
| } | |
| /** | |
| * @brief This function configure the dummy cycles on memory side. | |
| * @param hqspi: QSPI handle | |
| * @retval None | |
| */ | |
| static void QSPI_DummyCyclesCfg(QSPI_HandleTypeDef *hqspi) | |
| { | |
| QSPI_CommandTypeDef sCommand = {0}; | |
| uint8_t reg; | |
| /* Read Volatile Configuration register --------------------------- */ | |
| sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE; | |
| sCommand.Instruction = READ_VOL_CFG_REG_CMD; | |
| sCommand.AddressMode = QSPI_ADDRESS_NONE; | |
| sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; | |
| sCommand.DataMode = QSPI_DATA_1_LINE; | |
| sCommand.DummyCycles = 0; | |
| sCommand.DdrMode = QSPI_DDR_MODE_DISABLE; | |
| sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; | |
| sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; | |
| sCommand.NbData = 1; | |
| if (HAL_QSPI_Command(hqspi, &sCommand, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) | |
| { | |
| Error_Handler(); | |
| } | |
| if (HAL_QSPI_Receive(hqspi, ®, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) | |
| { | |
| Error_Handler(); | |
| } | |
| /* Enable write operations ---------------------------------------- */ | |
| QSPI_WriteEnable(hqspi); | |
| /* Write Volatile Configuration register (with new dummy cycles) -- */ | |
| sCommand.Instruction = WRITE_VOL_CFG_REG_CMD; | |
| MODIFY_REG(reg, 0xF0, (DUMMY_CLOCK_CYCLES_READ_QUAD << POSITION_VAL(0xF0))); | |
| if (HAL_QSPI_Command(hqspi, &sCommand, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) | |
| { | |
| Error_Handler(); | |
| } | |
| if (HAL_QSPI_Transmit(hqspi, ®, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) | |
| { | |
| Error_Handler(); | |
| } | |
| } | |
| static uint8_t qspi_reset(QSPI_HandleTypeDef *hqspi) | |
| { | |
| QSPI_CommandTypeDef s_command = {0}; | |
| /* Initialize the reset enable command */ | |
| s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE; | |
| s_command.Instruction = RESET_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; | |
| /* Send the command */ | |
| if (HAL_QSPI_Command(hqspi, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) | |
| { | |
| Error_Handler(); | |
| } | |
| /* Send the reset memory command */ | |
| s_command.Instruction = RESET_MEMORY_CMD; | |
| if (HAL_QSPI_Command(hqspi, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) | |
| { | |
| Error_Handler(); | |
| } | |
| /* Configure automatic polling mode to wait the memory is ready */ | |
| if (QSPI_AutoPollingMemReady(hqspi, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != QSPI_OK) | |
| { | |
| Error_Handler(); | |
| } | |
| return HAL_OK; | |
| } | |
| void qspi_chip_erase() | |
| { | |
| QSPI_WriteEnable(&hqspi); | |
| QSPI_CommandTypeDef s_command = {0}; | |
| s_command.Instruction = BULK_ERASE_CMD; | |
| s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE; | |
| 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) | |
| { | |
| Error_Handler(); | |
| } | |
| QSPI_AutoPollingMemReady(&hqspi, FLASH_CHIP_ERASE_MAX_TIME); | |
| QSPI_WriteEnable(&hqspi); | |
| } | |
| HAL_StatusTypeDef qspi_sector_erase(uint32_t BlockAddress) | |
| { | |
| QSPI_WriteEnable(&hqspi); | |
| QSPI_CommandTypeDef s_command = {0}; | |
| HAL_StatusTypeDef status = QSPI_OK; | |
| /* Initialize the erase command */ | |
| s_command.Instruction = SECTOR_ERASE_CMD; | |
| s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE; | |
| s_command.AddressMode = QSPI_ADDRESS_1_LINE; | |
| s_command.AddressSize = QSPI_ADDRESS_32_BITS; | |
| s_command.Address = BlockAddress; | |
| 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; | |
| /* Send the command */ | |
| if ((status = HAL_QSPI_Command(&hqspi, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE)) != HAL_OK) | |
| { | |
| Error_Handler(); | |
| } | |
| QSPI_AutoPollingMemReady(&hqspi, FLASH_SECTOR_ERASE_MAX_TIME); | |
| return status; | |
| } | |
| HAL_StatusTypeDef qspi_write_buffer(size_t offset, const uint8_t* buffer, size_t length) | |
| { | |
| QSPI_CommandTypeDef s_command = {0}; | |
| HAL_StatusTypeDef status = QSPI_OK; | |
| s_command.Instruction = QUAD_IN_FAST_PROG_CMD; | |
| s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE; | |
| s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_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; | |
| s_command.AddressMode = QSPI_ADDRESS_1_LINE; | |
| s_command.DataMode = QSPI_DATA_4_LINES; | |
| s_command.AddressSize = QSPI_ADDRESS_32_BITS; | |
| // qspi can only be written a page (256 bytes) at a time so we need to break it into chunks | |
| while(length > 0) | |
| { | |
| uint32_t bytes_to_write = length < 256 ? length : 256; | |
| s_command.Address = offset; | |
| s_command.NbData = bytes_to_write; | |
| QSPI_WriteEnable(&hqspi); | |
| if((status = HAL_QSPI_Command(&hqspi, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE)) == HAL_OK) | |
| { | |
| if((status = HAL_QSPI_Transmit(&hqspi, (uint8_t *)buffer, HAL_QPSI_TIMEOUT_DEFAULT_VALUE )) == HAL_OK) | |
| { | |
| status = QSPI_AutoPollingMemReady(&hqspi, HAL_QPSI_TIMEOUT_DEFAULT_VALUE); | |
| if(status != HAL_OK) | |
| return status; | |
| } | |
| } | |
| offset += bytes_to_write; | |
| buffer += bytes_to_write; | |
| length -= bytes_to_write; | |
| } | |
| return status; | |
| } | |
| HAL_StatusTypeDef qspi_read_buffer(size_t address, const uint8_t* buffer, size_t length) { | |
| QSPI_CommandTypeDef s_command = {0}; | |
| HAL_StatusTypeDef status = QSPI_OK; | |
| s_command.Instruction = QUAD_OUT_FAST_READ_CMD; | |
| s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE; | |
| s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_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; | |
| s_command.DummyCycles = DUMMY_CLOCK_CYCLES_READ_QUAD; | |
| s_command.AddressMode = QSPI_ADDRESS_1_LINE; | |
| s_command.DataMode = QSPI_DATA_4_LINES; | |
| s_command.AddressSize = QSPI_ADDRESS_32_BITS; | |
| s_command.Address = address; | |
| s_command.NbData = length; | |
| if ((status = HAL_QSPI_Command(&hqspi, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE)) == HAL_OK) | |
| { | |
| status = HAL_QSPI_Receive(&hqspi, (uint8_t *)buffer, HAL_QPSI_TIMEOUT_DEFAULT_VALUE); | |
| } | |
| return status; | |
| } | |
| static HAL_StatusTypeDef qspi_enable_quad(QSPI_HandleTypeDef *hqspi) | |
| { | |
| QSPI_CommandTypeDef s_command = {0}; | |
| uint8_t status[1] = {0x02}; | |
| s_command.Instruction = WRITE_STATUS_REG2; | |
| s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE; | |
| 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; | |
| s_command.NbData = sizeof(status); | |
| if (HAL_QSPI_Command(hqspi, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) | |
| { | |
| Error_Handler(); | |
| } | |
| if (HAL_QSPI_Transmit(hqspi, status, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { | |
| Error_Handler(); | |
| } | |
| return QSPI_OK; | |
| } | |
| HAL_StatusTypeDef qspi_enable_memorymapped_mode(void) | |
| { | |
| QSPI_CommandTypeDef s_command = {0}; | |
| QSPI_MemoryMappedTypeDef s_mem_mapped_cfg = {0}; | |
| s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE; | |
| s_command.AddressMode = QSPI_ADDRESS_1_LINE; | |
| s_command.AddressSize = QSPI_ADDRESS_32_BITS; | |
| s_command.DataMode = QSPI_DATA_4_LINES; | |
| s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; | |
| s_command.DummyCycles = 6; | |
| s_command.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; | |
| s_command.Instruction = QUAD_OUT_FAST_READ_DTR_CMD; | |
| s_command.DdrMode = QSPI_DDR_MODE_ENABLE; | |
| s_command.DdrHoldHalfCycle = QSPI_DDR_HHC_HALF_CLK_DELAY; | |
| /* Configure the memory mapped mode */ | |
| s_mem_mapped_cfg.TimeOutActivation = QSPI_TIMEOUT_COUNTER_DISABLE; | |
| s_mem_mapped_cfg.TimeOutPeriod = 0; | |
| if (HAL_QSPI_MemoryMapped(&hqspi, &s_command, &s_mem_mapped_cfg) != HAL_OK) | |
| { | |
| Error_Handler(); | |
| } | |
| return QSPI_OK; | |
| } | |
| int is_qspi_memorymapped() | |
| { | |
| return hqspi.State == HAL_QSPI_STATE_BUSY_MEM_MAPPED; | |
| } | |
| void qspi_disable_memorymapped_mode(void) | |
| { | |
| HAL_QSPI_Abort(&hqspi); | |
| qspi_init(); | |
| } | |
| static HAL_StatusTypeDef qspi_enable_4byte_addr(QSPI_HandleTypeDef *hqspi) | |
| { | |
| QSPI_CommandTypeDef s_command = {0}; | |
| s_command.Instruction = ENTER_4_BYTE_ADDR_MODE_CMD; | |
| s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE; | |
| 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) | |
| { | |
| Error_Handler(); | |
| } | |
| return QSPI_OK; | |
| } | |
| // for some godforsaken reason if this is optimised in any way when returning from the function | |
| // it jumps off into the normal SPI code! | |
| #pragma GCC push_options | |
| #pragma GCC optimize ("O0") | |
| int qspi_init(void) { | |
| qspi_reset(&hqspi); | |
| qspi_enable_quad(&hqspi); | |
| qspi_enable_4byte_addr(&hqspi); | |
| return(0); | |
| } | |
| #pragma GCC pop_options | |
| /* USER CODE END 1 */ | |
| /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ |