/** * @brief This function configure the dummy cycles on memory side. * @param hqspi: QSPI handle * @retval None */ static uint8_t QSPI_DummyCyclesCfg(QSPI_HandleTypeDef *hqspi) { QSPI_CommandTypeDef s_command; uint8_t reg; /* Initialize the read volatile configuration register command */ s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE; s_command.Instruction = READ_VOL_CFG_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.NbData = 1; 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(hqspi, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { return QSPI_ERROR; } /* Reception of the data */ if (HAL_QSPI_Receive(hqspi, ®, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { return QSPI_ERROR; } /* Enable write operations */ if (QSPI_WriteEnable(hqspi) != QSPI_OK) { return QSPI_ERROR; } /* Update volatile configuration register (with new dummy cycles) */ s_command.Instruction = WRITE_VOL_CFG_REG_CMD; MODIFY_REG(reg, N25Q512A_VCR_NB_DUMMY, (N25Q512A_DUMMY_CYCLES_READ_QUAD << POSITION_VAL(N25Q512A_VCR_NB_DUMMY))); /* Configure the write volatile configuration register command */ if (HAL_QSPI_Command(hqspi, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { return QSPI_ERROR; } /* Transmission of the data */ if (HAL_QSPI_Transmit(hqspi, ®, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { return QSPI_ERROR; } return QSPI_OK; }
/** * @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; 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(&QSPIHandle, &sCommand, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { Error_Handler(); } if (HAL_QSPI_Receive(&QSPIHandle, ®, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { Error_Handler(); } /* Enable write operations ---------------------------------------- */ QSPI_WriteEnable(&QSPIHandle); /* 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(&QSPIHandle, &sCommand, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { Error_Handler(); } if (HAL_QSPI_Transmit(&QSPIHandle, ®, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { Error_Handler(); } }
/** * @brief Writes an amount of data to the QSPI memory. * @param pData: Pointer to data to be written * @param WriteAddr: Write start address * @param Size: Size of data to write * @retval QSPI memory status */ uint8_t BSP_QSPI_Write(uint8_t* pData, uint32_t WriteAddr, uint32_t Size) { QSPI_CommandTypeDef s_command; uint32_t end_addr, current_size, current_addr; /* Calculation of the size between the write address and the end of the page */ current_addr = 0; while (current_addr <= WriteAddr) { current_addr += N25Q512A_PAGE_SIZE; } current_size = current_addr - WriteAddr; /* Check if the size of the data is less than the remaining place in the page */ if (current_size > Size) { current_size = Size; } /* Initialize the address variables */ current_addr = WriteAddr; end_addr = WriteAddr + Size; /* Initialize the program 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_32_BITS; s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; s_command.DataMode = QSPI_DATA_4_LINES; 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; /* Perform the write page by page */ do { s_command.Address = current_addr; s_command.NbData = current_size; /* Enable write operations */ if (QSPI_WriteEnable(&QSPIHandle) != QSPI_OK) { return QSPI_ERROR; } /* Configure the command */ if (HAL_QSPI_Command(&QSPIHandle, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { return QSPI_ERROR; } /* Transmission of the data */ if (HAL_QSPI_Transmit(&QSPIHandle, pData, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { return QSPI_ERROR; } /* Configure automatic polling mode to wait for end of program */ if (QSPI_AutoPollingMemReady(&QSPIHandle, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != QSPI_OK) { return QSPI_ERROR; } /* Update the address and size variables for next page programming */ current_addr += current_size; pData += current_size; current_size = ((current_addr + N25Q512A_PAGE_SIZE) > end_addr) ? (end_addr - current_addr) : N25Q512A_PAGE_SIZE; } while (current_addr < end_addr); return QSPI_OK; }
/** * @brief This function configure the dummy cycles on memory side. * @param hqspi: QSPI handle * @retval None */ static uint8_t QSPI_DummyCyclesCfg(QSPI_HandleTypeDef *hqspi) { QSPI_CommandTypeDef s_command; uint8_t reg[2]; /* Command ID differs between N25Q512A and S25FL512S memories */ if (QspiInfo.ManufID == QSPI_N25Q512A) { /* Initialize the read volatile configuration register command */ s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE; s_command.Instruction = READ_VOL_CFG_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.NbData = 1; 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(hqspi, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { return QSPI_ERROR; } /* Reception of the data */ if (HAL_QSPI_Receive(hqspi, ®[0], HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { return QSPI_ERROR; } /* Enable write operations */ if (QSPI_WriteEnable(hqspi) != QSPI_OK) { return QSPI_ERROR; } /* Update volatile configuration register (with new dummy cycles) */ s_command.Instruction = WRITE_VOL_CFG_REG_CMD; MODIFY_REG(reg[0], N25Q512A_VCR_NB_DUMMY, (N25Q512A_DUMMY_CYCLES_READ_QUAD << POSITION_VAL(N25Q512A_VCR_NB_DUMMY))); /* Configure the write volatile configuration register command */ if (HAL_QSPI_Command(hqspi, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { return QSPI_ERROR; } /* Transmission of the data */ if (HAL_QSPI_Transmit(hqspi, ®[0], HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { return QSPI_ERROR; } } else { /* Initialize the read configuration register command */ s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE; s_command.Instruction = S25FL512S_READ_CONFIGURATION_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.NbData = 1; 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) { return QSPI_ERROR; } /* Reception of the data */ if (HAL_QSPI_Receive(&QSPIHandle, ®[1], HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { return QSPI_ERROR; } /* Initialize the read status register1 command */ s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE; s_command.Instruction = S25FL512S_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.NbData = 1; 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) { return QSPI_ERROR; } /* Reception of the data */ if (HAL_QSPI_Receive(&QSPIHandle, ®[0], HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { return QSPI_ERROR; } /* Enable write operations */ if (QSPI_WriteEnable(&QSPIHandle) != QSPI_OK) { return QSPI_ERROR; } /* Update configuration register (with new Latency Code) */ s_command.Instruction = S25FL512S_WRITE_STATUS_CMD_REG_CMD; s_command.NbData = 2; MODIFY_REG(reg[1], S25FL512S_CR1_LC_MASK, S25FL512S_CR1_LC1); /* Configure the write volatile configuration register command */ if (HAL_QSPI_Command(&QSPIHandle, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { return QSPI_ERROR; } /* Transmission of the data Status Register 1 */ if (HAL_QSPI_Transmit(&QSPIHandle, reg, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { return QSPI_ERROR; } } return QSPI_OK; }
/** * @brief This function set the QSPI memory in 4-byte address mode * @param hqspi: QSPI handle * @retval None */ static uint8_t QSPI_EnterFourBytesAddress(QSPI_HandleTypeDef *hqspi) { QSPI_CommandTypeDef s_command; uint8_t reg1; /* Command ID differs between N25Q512A and S25FL512S memories */ if (QspiInfo.ManufID == QSPI_N25Q512A) { /* Initialize the command */ s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE; s_command.Instruction = ENTER_4_BYTE_ADDR_MODE_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; /* Enable write operations */ if (QSPI_WriteEnable(hqspi) != QSPI_OK) { return QSPI_ERROR; } /* Send the command */ if (HAL_QSPI_Command(hqspi, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { return QSPI_ERROR; } /* Configure automatic polling mode to wait the memory is ready */ if (QSPI_AutoPollingMemReady(hqspi, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != QSPI_OK) { return QSPI_ERROR; } return QSPI_OK; } else { /* Initialize the read bank register command */ s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE; s_command.Instruction = S25FL512S_READ_BANK_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.NbData = 1; 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) { return QSPI_ERROR; } /* Reception of the data */ if (HAL_QSPI_Receive(&QSPIHandle, ®1, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { return QSPI_ERROR; } /* Enable write operations */ if (QSPI_WriteEnable(&QSPIHandle) != QSPI_OK) { return QSPI_ERROR; } /* Update Bank address register (with 4byte addressing bit) */ s_command.Instruction = S25FL512S_WRITE_BANK_REG_CMD; MODIFY_REG(reg1, S25FL512S_BA_EXTADD, S25FL512S_BA_EXTADD); /* Configure the write volatile configuration register command */ if (HAL_QSPI_Command(&QSPIHandle, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { return QSPI_ERROR; } /* Transmission of the data Status Register 1 */ if (HAL_QSPI_Transmit(&QSPIHandle, ®1, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { return QSPI_ERROR; } return QSPI_OK; } }
static rt_uint32_t qspixfer(struct rt_spi_device *device, struct rt_spi_message *message) { rt_size_t len = 0; RT_ASSERT(device != RT_NULL); RT_ASSERT(device->bus != RT_NULL); struct rt_qspi_message *qspi_message = (struct rt_qspi_message *)message; struct stm32_qspi_bus *qspi_bus = device->bus->parent.user_data; #ifdef BSP_QSPI_USING_SOFTCS struct stm32_hw_spi_cs *cs = device->parent.user_data; #endif const rt_uint8_t *sndb = message->send_buf; rt_uint8_t *rcvb = message->recv_buf; rt_int32_t length = message->length; #ifdef BSP_QSPI_USING_SOFTCS if (message->cs_take) { rt_pin_write(cs->pin, 0); } #endif /* send data */ if (sndb) { qspi_send_cmd(qspi_bus, qspi_message); if (qspi_message->parent.length != 0) { if (HAL_QSPI_Transmit(&qspi_bus->QSPI_Handler, (rt_uint8_t *)sndb, 5000) == HAL_OK) { len = length; } else { LOG_E("QSPI send data failed(%d)!", qspi_bus->QSPI_Handler.ErrorCode); qspi_bus->QSPI_Handler.State = HAL_QSPI_STATE_READY; goto __exit; } } else { len = 1; } } else if (rcvb)/* recv data */ { qspi_send_cmd(qspi_bus, qspi_message); #ifdef BSP_QSPI_USING_DMA if (HAL_QSPI_Receive_DMA(&qspi_bus->QSPI_Handler, rcvb) == HAL_OK) #else if (HAL_QSPI_Receive(&qspi_bus->QSPI_Handler, rcvb, 5000) == HAL_OK) #endif { len = length; #ifdef BSP_QSPI_USING_DMA while (qspi_bus->QSPI_Handler.RxXferCount != 0); #endif } else { LOG_E("QSPI recv data failed(%d)!", qspi_bus->QSPI_Handler.ErrorCode); qspi_bus->QSPI_Handler.State = HAL_QSPI_STATE_READY; goto __exit; } } __exit: #ifdef BSP_QSPI_USING_SOFTCS if (message->cs_release) { rt_pin_write(cs->pin, 1); } #endif return len; }
//-------------------------------------------------------------- // schreiben von einem 8bit Datenblock ins QFlash // start_adr : start adresse in die geschrieben wird // size : anzahl der daten die geschrieben werden // data_buf : pointer zu einem Puffer der Daten // // return : QSPI_OK, wenn alles ok //-------------------------------------------------------------- uint8_t UB_QFlash_Write_Block8b(uint32_t start_adr, uint32_t size, uint8_t *data_buf) { QSPI_CommandTypeDef s_command; uint32_t end_addr, current_size, current_addr; if (start_adr >= N25Q128A_FLASH_SIZE) return QSPI_ERROR; if ((start_adr + size) >= N25Q128A_FLASH_SIZE) return QSPI_ERROR; if (size == 0) return QSPI_ERROR; // calc size current_addr = 0; while (current_addr <= start_adr) { current_addr += N25Q128A_PAGE_SIZE; } current_size = current_addr - start_adr; // set variables if (current_size > size) current_size = size; current_addr = start_adr; end_addr = start_adr + size; // Initialize the program command s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE; s_command.Instruction = EXT_QUAD_IN_FAST_PROG_CMD; s_command.AddressMode = QSPI_ADDRESS_4_LINES; s_command.AddressSize = QSPI_ADDRESS_24_BITS; s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; s_command.DataMode = QSPI_DATA_4_LINES; 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; // write all pages do { s_command.Address = current_addr; s_command.NbData = current_size; // Enable write operations if (P_QSPI_WriteEnable(&QSPIHandle) != QSPI_OK) return QSPI_ERROR; // Configure the command if (HAL_QSPI_Command(&QSPIHandle, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) return QSPI_ERROR; // Transmission of the data if (HAL_QSPI_Transmit(&QSPIHandle, data_buf, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) return QSPI_ERROR; // Configure automatic polling mode to wait for end of program if (P_QSPI_AutoPollingMemReady(&QSPIHandle, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != QSPI_OK) return QSPI_ERROR; // Update the address and size variables for next page programming current_addr += current_size; data_buf += current_size; current_size = ((current_addr + N25Q128A_PAGE_SIZE) > end_addr) ? (end_addr - current_addr) : N25Q128A_PAGE_SIZE; } while (current_addr < end_addr); return QSPI_OK; }